WebAudio: oscillator in JS

October 27th, 2012. Tagged: JavaScript, Music, WebAudio

How about generating some noise in JavaScript?

Demo is here: oscillator.

How does this work?

Using HTML Web Audio you can synthesize audio with a given frequency. E.g. 440Hz is A ("la" in solfège)

This means you don't need an <audio> element or any mp3, ogg, wav, etc, no external files.

Let's see how.

Capable browser?

You need a browser that supports AudioContext. No such (major) browser at the time of writing, afaik. But there's webkitAudioContext supported in stable iOS Safari, Safari, Chrome. Also there could be browsers that support AudioContext but not the oscillator part. So starting off:

  // globals
  var audio_context, oscillator;
 
  // hello audio world
  (function init(g){
    try {
      audio_context = new (g.AudioContext || g.webkitAudioContext);
      oscillator = audio_context.createOscillator();
    } catch (e) {
      alert('No web audio oscillator support in this browser');
    }
  }(window));

Start/stop playing

Alright, next is a play(frequency /*number*/) function which makes noise with a given frequency.

  function play(freq) {
    oscillator = audio_context.createOscillator();
    oscillator.frequency.value = freq;
    oscillator.connect(audio_context.destination);
    oscillator.noteOn(0);
    fire('play', oscillator.frequency.value);
  }

(Don't mind fire(), it's just a poor man's event utility for logging what's going on)

The audio context provides a createOscillator(). You assign the frequency you need and connect this oscillator node to the audio destination (speaker).

There is a nice analogy going on in the Web Audio: you start with some input noise, say coming from microphone or an audio file, or, in this case, you generate the noise yourself. Then you connect that initial input to the output (destination) which is the system speaker/phones. In between though you can pass the noise through a bunch of nodes that can modify the noise.

In this simple example I only have an oscillator node which is connected directly to the audio destination.

noteOn(0) starts playing the noise we just generated.

Implementing stop() to silence the noise is just a question of calling noteOff(0) on the same oscillator node.

  function stop() {
    oscillator.noteOff(0);
    fire('stop');
  }

That's it, go play with the demo.

The demo plays 440Hz (A on 4th octave of the piano) and 880Hz (A on 5th octave) and also lets you punch in a number and see what happens. Probably nice to play with your dog and with sounds at frequencies you cannot hear.

A chord

Finally, an attempt to play a chord: three frequencies at the same time. C major is C, E and G tones. We have an array of the three frequencies, so loop over the array and create and noteOn three oscillator nodes.

  var cmajor = {};
  cmajor.yo = function () {
    var oscs = [], o, i, freqs = [261.63, 329.63, 392];
    freqs.forEach(function(freq) {
      o = audio_context.createOscillator();
      o.frequency.value = freq;
      o.connect(audio_context.destination);
      o.noteOn(0);
      oscs.push(o);
    });
    this.oscs = oscs;
    fire('play', '\n - ' + freqs.join('Hz\n - '));
  };
  
  cmajor.no = function () {
    this.oscs.forEach(function(o) {
      o.noteOff(0);
    });
    fire('stop');
  };

Thanks

Some links for learning more

Once again the demo is here: oscillator.

Intro: html5rocks.com

Educational demos: webaudiodemos.appspot.com/

Comments? Feedback? Find me on Twitter, Mastodon, Bluesky, LinkedIn, Threads