Lately I've been toying around with learning Morse Code, and like any good computer programmer, I decided to write a program to do it for me. The whole program was under 100 lines of code. This was my first time using the new Web Audio API and I must say I'm impressed with how easy it is to use.

If you're using Chrome, feel free to try it out:

Unfortunately, it seems that Firefox doesn't yet support the APIs I used, although rumor has it that this should work in the nightlies. I haven't tested Safari, but I wouldn't be surprised if it works there because it also uses WebKit.

The Web Audio APIs are a new way of generating audio and applying rich effects. The basic idea is to create a set of nodes and connect them together to form an audio processing network. Some of these nodes are audio sources, such as from a file you loaded off the web, while others are generated in place, like the OscillatorNode.

I was happy to find the OscillatorNode, as I expected I was going to have to directly generate audio data. Instead, I just have to set the frequency I wanted and tell it to start generating tones. Basically, like this:

var context = new (window.AudioContext || window.webkitAudioContext());
var oscillator = context.createOscillator();

oscillator.frequency.value = 750;
oscillator.connect(context.destination);

oscillator.start(0);

This code gives us a continually playing tone, which quickly gets annoying. We need a way to generate dots and dashes so we can actually generate Morse Code. Unfortunately, I couldn't see an easy way to do this using start and stop. I thought about using setTimeout, but I didn't trust it to give me reliable enough timing.

I was afraid I was going to have to generate the audio data directly after all, but then I found out that the APIs include parameter automation. This let's us schedule changes in various audio parameters at specific points in time, and the standard even seems to require these to take effect at the per-sample granularity. This seems to be exactly what we need.

It seemed as though it'd be as simple as calling oscillator.volume.setValueAtTime, but oscillator.volume turned out not to be a thing. Instead, we need to add a GainNode, which allows us to adjust the volume. Wiring the whole thing up looks something like this:

var context = new (window.AudioContext || window.webkitAudioContext());
var oscillator = context.createOscillator();

var gain = context.createGain();

oscillator.frequency.value = 750;

oscillator.connect(context);
gain.connect(context.destination);

oscillator.start(0);

Now we can generate Morse Code by simply toggling the gain between 0 and 1. I did this by creating a table mapping all the letters to a string of dots and dashes (for example, "A" maps to .-, "B" to -..., etc.). Then it was just a matter of creating a couple for loops to translate strings of letters into dots and dashes and then dots and dashes into sequences of volume changes. For the full details, I've shared my code in a gist.

So there you have it: generating Morse Code using the Web Audio APIs in less that 100 lines of code. One thing I like about the design is that it mimics an actual telegraph. You have a signal generator (the OscillatorNode) connected to a key (the GainNode) which is connected to a transmitter (the AudioContext).