During a recent hackathon, I decided to build a multiplayer 8 bits sequencer using sounds generated programatically with the Web Audio API. I didn’t want to only use the HTML 5 audio
tag because I found it too limiting… but the first thing I discovered is that getting the right kind of sound is not straightforward at all, especially if you only have a very basic musical background like myself. So how exactly do you create a clear, ringing and nice sounding note?
In this article I’ll give some pointers with usable code. I’ve also added examples that you can actually run if your browser supports it.
Produce a Simple Beep
First let’s create a very basic beep using a sinusoid. We’ll initiate an audio context, which is the central object for generating sound. Then we’ll create an oscillator producing the sine wave. Finally we connect the oscillator to the context and start.
You’ll notice that the sound produced here is not great. It seems like you let a phone off the hook and when you stop it, you hear a “click” and it’s not pleasant at all. This is because the human hear reacts this way as explained in this great article. Basically when you stop the sound anywhere else than the zero crossing point, you’ll hear this clicking sound.
Getting Rid Of The Clicking Sound
The best solution to get rid of this click is to ramp the sine wave down with an exponentional function, using AudioParam.exponentialRampToValueAtTime()
as documented here.
This time we need to add a gain node to our oscillator. A gain node allows us to change the volume of a signal as explained in this schema from the documentation:
The code to start the sound now looks like this:
In order to stop the sound we change the gain value, effectively reducing the volume. Note that we don’t ramp down to 0 since there is a limitation in this function where the value has to be positive.
As you can hear, the clicking sound is gone! But that’s not the only interesting thing that you can do with this exponential ramp down.
Set A Ringing Effect
In the example above, we decided to stop the sound really quickly, in 0.04
seconds. But what happens when we change this X
value?
Play Stop (X=0.1) Stop (X=1) Stop (X=5)
Hitting Notes
Giving more time to the sound to fade out gives it a totally different feel. It gets more visible when we start and stop the signal right away:
Start and stop quickly Start and stop slowly
The first one sounds like a ticking noise when the other sounds like an actual note played on an instrument.
Various Oscilators
So far we’ve been using a sine wave for our main signal, but we have other options:
It’s enven more interesting when we start playing around with the type of oscilators by setting o.type = type
.
Playing Actual Notes
With the previous code, it becomes fairly simple to have a nice sounding note, but what exactly were we playing? That’s when you have to take frequency into account. For instance, the one people know is that A4 is 440Hz, but there are others.
Note Frequencies in Hz
With this table, you can easily create a mapping in your code to play any given note using its . For the hackathon I used a simple hash mapping that is available in this gist.
C | C# | D | Eb | E | F | F# | G | G# | A | Bb | B | |
---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 16.35 | 17.32 | 18.35 | 19.45 | 20.60 | 21.83 | 23.12 | 24.50 | 25.96 | 27.50 | 29.14 | 30.87 |
1 | 32.70 | 34.65 | 36.71 | 38.89 | 41.20 | 43.65 | 46.25 | 49.00 | 51.91 | 55.00 | 58.27 | 61.74 |
2 | 65.41 | 69.30 | 73.42 | 77.78 | 82.41 | 87.31 | 92.50 | 98.00 | 103.8 | 110.0 | 116.5 | 123.5 |
3 | 130.8 | 138.6 | 146.8 | 155.6 | 164.8 | 174.6 | 185.0 | 196.0 | 207.7 | 220.0 | 233.1 | 246.9 |
4 | 261.6 | 277.2 | 293.7 | 311.1 | 329.6 | 349.2 | 370.0 | 392.0 | 415.3 | 440.0 | 466.2 | 493.9 |
5 | 523.3 | 554.4 | 587.3 | 622.3 | 659.3 | 698.5 | 740.0 | 784.0 | 830.6 | 880.0 | 932.3 | 987.8 |
6 | 1047 | 1109 | 1175 | 1245 | 1319 | 1397 | 1480 | 1568 | 1661 | 1760 | 1865 | 1976 |
7 | 2093 | 2217 | 2349 | 2489 | 2637 | 2794 | 2960 | 3136 | 3322 | 3520 | 3729 | 3951 |
8 | 4186 | 4435 | 4699 | 4978 | 5274 | 5588 | 5920 | 6272 | 6645 | 7040 | 7459 | 7902 |
To implement this, we just need to add a frequency to our oscilator:
If we change with the value of frequency
, we can play any note. For instance:
261.6Hz (C4) 440Hz (A4) 830.6Hz (G#5)
Mix this with the ramp down timings and different signals, and you start to be able to create more interesting sounds.
174.6Hz (F3) - Square 1109Hz (C#6) - Sawtooth 87.31 Hz (F2) - Triangle
Since you scrolled this far, you might be interested in some other things I wrote:
- Building A Multiplayer 8 Bits Sequencer
- Trailing Slashes, Github Pages, Jekyll 3 & 404s
- How CSS Animations Can Break Your Tests
- Some Respect For Legacy Code
- Tips on Creating a Website From When I Was 12
- Enough With The Trolls
- Please Keep a Changelog For Your Open Source Lib
- Startup & Tech Book Reviews