Making Music with The WebAudio API Part 2

In Part 1 of this series, we created a page that lets a user click a button to play a tone and then click another button to stop playing the tone as follows…

Now, let’s make it possible to play and stop the note over and over again. First, we’ll try doing it the obvious way that does not actually work, modifying the playTone() and stopTone() functions to read as follows…

If you try to use this, you will find that nothing happens when you try to play the note after previously playing it and stopping it. If you have a browser console open, you’ll see an error something like “InvalidStateError: An attempt was made to use an object that is not, or is no longer, usable”.

What this is telling us is that the oscillator object can only be used once. After it has been stopped, it can never be started again. What we need, then, is to create a new oscillator instance for each time that we want to start playing a note. We can do that by changing our JavaScript to read as follows.

Now you can successfully start and stop the tone as many times as you want.

So next, how can we play other notes besides Middle A? Each note has a particular frequency, so we could just have a big map of note values to frequencies. We ca do the math for that directly in JavaScript instead though.

In Western music, we are usually working with a Chromatic scale having twelve-tones per octave. The frequency of a note in one octave is always exactly double that of the same note in the next lower octave. For instance, A in octave 4 (Middle A) is 440 Hz and A in octave 5 is 880 Hz. A Chromatic scale means that the ratio between any 2 successive note frequencies is always the same. In order to get from one note to the one an octave above it by applying the same ratio 12 times, we need each ratio to be the twelfth root of 2 which can also be described as 2 to the power of 1/12.

Knowing that, we can number our notes starting at A4 = 440Hz being note number 0, adding 1 to that for each semitone higher or subtracting 1 for each semitone lower that we want to produce.

To incorporate this knowledge into our demo, we first add a note number input to our page right before the buttons.

Next, we modify the playTone() function to read as follows.

Now, if you enter a number into the “Note number” input before clicking the “Play Tone” button, you will hear a tone with a pitch that number of semitones higher (for positive numbers) or lower (for negative numbers) than Middle A.

The next article in this series will discuss how to control the volume. Later in the series, we’ll discuss how to eliminate the annoying click that you might have noticed when stopping tones.