Playing notes with the Web Audio API Part 1 - Monophonic Synthesis

June 5, 2013

The Web Audio API turns your browser into a powerful synthesizer. One question I’ve been asked about the Web Audio API is how to play single, one-shot sounds using the oscillators, in other words how to play notes or tunes. I’ll show one approach in this post and along the way we’ll look at how some early analogue monophonic synths worked.

If you’re new to the Web Audio API there’s a good introduction over on HTML5 Rocks. The first couple of sections should be all you need to follow this tutorial.

Playing a sound

We start by creating some sound. This could be considered the “hello world” of Web Audio applications - we’ll generate music’s purest tone, the sine wave, and make it play.

context = new AudioContext;
oscillator = context.createOscillator();
oscillator.frequency.value = 200;

oscillator.connect(context.destination);

oscillator.start(0);

If you open a javascript console on this page and enter the code above, assuming your browser supports the Web Audio API1, you should hear a tone at 200Hz. We’ve created an AudioContext, added an oscillator and told it to start playing immediately. If you don’t set the frequency the default is 440Hz, or an A above middle C. Why 440Hz? I thought you’d never ask!

We can stop the tone with

oscillator.stop(0);

At this point, you might be tempted to start the oscillator again

oscillator.start(0);

but you’ll find, if you try, that this doesn’t work. For performance reasons the Web Audio API is designed so that oscillator, and other nodes such as AudioBufferSourceNode can only be “used” once. So if we want to play a sequence of notes, or attach notes to the keys of a virtual keyboard, how do we proceed? To answer this question, let’s consider how analogue synth designers tackled the problem.

Monophonic Synthesis

One classic monophonic synthesizer is Robert Moog’s “Minimoog”. This was a monophonic synthesizer - that is, it could only play a single note at a time. Here’s the control panel from the Minimoog:

Minimoog Control Panel

The sections we are interested in here are the “oscillator bank”2 and the “loudness contour” controls, also known as the VCO (Voltage Controlled Oscillator) and VCA (Voltage Controlled Amplifier) respectively3.

The VCO, as it’s name suggests, is an oscillator whose frequency can be set by sending a specific voltage - typically from the keyboard of the instrument. In our code above, this is equivalent to the oscillator.frequency.value =.

The VCA is an amplifier which varies its gain depending on a supplied voltage. For simplicity, let’s assume that when it receives a positive voltage it sets its gain to 1, and when it receives a negative voltage it sets its gain to zero.

When a musician presses a key on the keyboard, two things happen. Firstly a voltage is sent to the VCO to set its frequency. Secondly, the same key press sets the gain of the VCA. The oscillator is connected to the amplifier so pressing the key makes a sound. When the key is released, the VCA is set to zero and the sound stops.

Armed with this knowledge, we can simulate this using the Web Audio API.

A simple Web Audio monosynth

To recreate the simple Minimoog described above we need two nodes from the Web Audio API, connected together like so:

Monosynth block diagram

We also need a mechanism to send messages to the nodes using key-presses. We’ll use Stuart Memo’s very useful Qwerty Hancock library for this. It turns a given DOM element into an interactive vector keyboard!

A little bit of glue code, and we have

var keyboard = qwertyHancock({id: 'keyboard'});

var context = new AudioContext();

/* VCO */
var vco = context.createOscillator();
vco.type = vco.SINE;
vco.frequency.value = this.frequency;
vco.start(0);

/* VCA */
var vca = context.createGain();
vca.gain.value = 0;

/* Connections */
vco.connect(vca);
vca.connect(context.destination);

keyboard.keyDown(function (_, frequency) {
  vco.frequency.value = frequency;
  vca.gain.value = 1;
});

keyboard.keyUp(function (_, _) {
  vca.gain.value = 0;
});

When a key on our virtual keyboard is pressed, the keyDown event is fired. We set the frequency of the oscillator and turn the gain of the amplifier up to 1. When the key is lifted we set the gain to zero.

Here’s the simple synthesiser in action:

There’s a few tweaks we should make here, as I’ve deliberately kept the code very simple. For example, if you press two keys at the same time and then release one, the gain will be set to zero. This can be avoided by keeping track of the keys that are pressed using an object

var isEmpty = function(obj) {
  return Object.keys(obj).length === 0;
}

depressed_keys = {};

keyboard.keyDown(function (note, frequency) {
  vco.frequency.value = frequency;
  vca.gain.value = 1;
  depressed_keys[note] = true;
});

keyboard.keyUp(function (note, _) {
  delete depressed_keys[note];
  if (isEmpty(depressed_keys)) {
    vca.gain.value = 0;
  }
});

The completed code is in this gist.

The other thing you might hear when playing with this demo is the “slide” between each note. When setting the value of some parameters directly in the API, as we do here with frequency, the spec recommends that changes are made smoothly to avoid clicks or glitches. We’ll see how to control this effect in a future post when we look at parameter automation and envelopes.

Next

We’ve seen how to play notes using the Web Audio API using code influenced by the design of classic monophonic synthesizers. Next time we’ll have a look at techniques for playing multiple notes at the same time: polyphonic synthesis.

Footnotes


  1. I’m using Chris Wilson’s AudioContext monkeypatch on this page so that I can use AudioContext instead of the vendor-prefixed webkitAudioContext on Chrome. The code examples here match the Web Audio spec and can work in non-webkit browsers, such as Firefox, as well. If you’re trying these examples in a page that doesn’t have the patch loaded, you’ll need to replace AudioContext with webkitAudioContext.

  2. If you look closely at the oscillator bank you’ll see three oscillators - the Minimoog could mix together the sound of three oscillators, but only to combine them into a single “note” - they couldn’t be triggered by independent keys on the keyboard.

  3. We’ll come back to some of the other sections in future posts.

Discuss

blog comments powered by Disqus

Share