I'd like to point out two things beforehand:
- this is a complex question, and
- as soon as you have figured out how to do this algorithmically, you'll have to write a lot of signal processing functionality in JS that other languages would already have access to (e.g. python through scipy/numpy, C/C++/Fortran through LAPACK/BLAS/FFTW etc)
so this will become a very complex project, and if you have the chance to influence the expectations for fast results, I'd recommend not promising a working prototype too soon.
Now, for a start, this looks like a linear algebra problem. That's because LA is kind of the sonic screw driver of DSP; give a man a sonic screwdriver, and everything looks like... wait, that metaphor doesn't work.
So, anyways, what you want to do is find a vector representation of your signal under test and map it to coordinates in a space spanned by the possible musical notes. Mathematically, you want to
- Find a set of base vectors representing your notes,
- Find a suitable base in which you can represent both these vectors and your signal
- Project the signal vector onto your note vector; the magnitude of the coefficients gives you the "amount" of the respective notes being represented in the signal.
As an example, the discrete Fourier transform takes signals that are represented in a time sampling base (every base vector is $(0,\ldots,0,1,0,\ldots,0)$, with the 1 representing the the sample time) and transforms it to a base system of orthogonal oscillations (The nth base vector of that base is $( e^{j2\pi nt})_{t\in (0,\ldots, N)}$). That is such a base transform.
You could simply record fixed lengths of your tones, normalize them in power (important!) and do a dot product with each of these, and find the ones that are above a certain threshold. Do that again with the piece of your signal starting one sample later, and find the notes that have been present with enough energy for enough time. What you end up with is a FIR filter bank.
Now, with a proper language and libraries, that would be a few lines of code, and one could do everything in frequency domain rather than time domain, reducing the effort of the convolution above to a multiplication and two FFTs, but since there is (I don't think there can be, due to architectural restrictions) no FFT in JavaScript, this is probably not easy to optimize. It might be that for reliable results, your algorithm might become too slow to process the audio signal in real time.