Signal and Sensation

The Moog Bass

The first of twelve basslines built a step at a time, and the step that matters turns out to be the filter you can't get from Web Audio. A Moog's ladder is four one pole stages with feedback around all four, and that feedback subtracts the input, which is what eats the bass: for an 8.5 dB resonant peak it pays 10.4 dB of fundamental while the peak itself rises 12.1 dB. Two cascaded BiquadFilterNodes put up the same peak within 0.1 dB and pay nothing at all, so at matched peaks the ladder sits 12.5 dB further down. First of twelve.

Open fullscreen →

What it is

A Moog bass, and the first of a new run of twelve.

The last series built twelve club sounds and measured what makes each one that sound. This one goes a level down: not the sound, the route to it. Every part takes one bassline and shows the five steps it was built from, one step at a time, with the number that step changed. You can step through them with the buttons or the keys 1 to 5, and you can play any step on the keyboard while you do it.

Five steps here. One saw. Three of them, twelve cents apart. A ladder lowpass. An envelope on the cutoff. Drive.

How it works

The last step is the finished bass rather than a separate patch, which matters more than it sounds like it does: there’s one graph definition with a cut point, so the sound at step five can’t drift away from the sound the page opens with.

The ladder is an audio worklet, because Web Audio doesn’t have one. Four one pole stages, and the resonance is feedback around all four:

const u = input - k * fourth;   // this line is the whole finding
first  += g * (u - first);
second += g * (first - second);
third  += g * (second - third);
fourth += g * (third - fourth);

The page will also switch that for two cascaded BiquadFilterNodes, which is what you’d reach for first, and their Q is set so the two filters put up a peak of the same height. That’s what makes the comparison fair: with the peaks matched, the only thing left different between them is what happened to the bass.

Every number comes off a rendered stage rather than out of the patch code, and each one gets measured twice, once from a pure renderer under node and once from the real audio graph rendered offline in the browser. The beat rate came out at 0.375 Hz one way and 0.375 the other, against 0.383 predicted from twelve cents. The finding came out at 12.5 dB both ways, to the decimal.

What surprised me

The finding was right about Moogs and wrong about my code. I wrote the plan expecting a resonant lowpass to cost you low end, built it out of two normalised biquads because that’s what Web Audio gives you, and measured the fundamental going up 1.1 dB as I turned the resonance from 0.71 to 12. That’s correct behaviour for a normalised biquad. It has no feedback path around it, so its resonance is free. A ladder’s resonance is a subtraction of the input, and that subtraction is the cost. For the same 8.5 dB peak the ladder pays 10.4 dB of fundamental and the biquad pays nothing, and at matched peaks the gap is 12.5 dB.

The obvious objection is that a ladder just gets quieter, since its gain at DC is one over one plus the feedback. It doesn’t: over that same range the fundamental falls 10.4 dB while the resonant peak rises 12.1 dB and harmonic ten doesn’t move at all. Opposite directions, so it’s a tilt in the sound rather than a level.

The drive is the weakest step and I am reporting it as one. Push it from 1 to 4 and the harmonics up to the eighth move by 0.4 dB total. An octave above the cutoff it puts about 5 dB back, and two octaves up almost nothing, because a four pole ladder has already removed what a tanh would have shaped. It also did literally nothing until I fed it at unity: the ladder arrives a quarter of the size, and a tanh doesn’t care about a signal that small.

Five of the six things that went wrong were my instruments, not the physics. The one I’d most like back is the ghost trace. Each step declares its own window, one cycle for a shape step and two seconds for a beat step, and I drew the previous step at its window behind the current step at this window. So 36 milliseconds of the drive step sat behind 600 milliseconds of the envelope step, which compares nothing to nothing, and it looked plausible.

The others: the graph summed its three oscillators where the renderer averaged them, so every absolute level after step two disagreed. The offline probe let go of the note at 85% of the render, leaving the tail in near silence where every harmonic is equally tiny and so every harmonic counts as audible, and a brightness tracker read full bandwidth at the end of every step. Measuring the sweep off a spectral centroid gave 281 milliseconds for a 180 millisecond setting, consistently, because a power weighted centroid is a sublinear function of the cutoff and its time constant simply isn’t the cutoff’s. And the picture was built from the animation loop, which doesn’t run in a background tab, so a page nobody was looking at rendered nothing at all.

What I would do next

The ladder here is the linear one. A real Moog saturates inside the loop, which is where the compression and the character live, and it would make the drive step redundant by doing that job four times over. That’s a part of its own rather than a footnote to this one.

The pitch of the resonance is also worth a day. At feedback 3.8 the ladder is a hair from oscillating, and what it does then is play a note of its own at the cutoff, which is a completely different instrument from a filter.