The Parametric Equaliser

From the bilinear transform to a live, hand-written biquad cascade you can drag with your fingers. The RBJ Audio-EQ Cookbook, rendered honestly in the browser.

RBJ CookbookBiquad · Direct Form IWeb Audio · JUCE
Begin ↓

01What an equaliser really is

An equaliser is a bank of linear, time-invariant filters placed in series across an audio signal. Each filter reshapes the spectrum in one region — a boost here, a cut there, a slope rolled off at the extremes — without inventing anything that was not already present. Everything an EQ does can be written as a single transfer function, and almost every knob you have ever turned on a channel strip is, underneath, one second-order section: a biquad.

The word "equalise" is a historical accident. Early telephone and disc-cutting engineers used compensating networks to equalise the frequency-dependent losses of long cable runs and the RIAA recording curve — to make the output flat again. Musicians inherited the tool and inverted the intent: we now reach for an EQ precisely to make things un-flat, to carve space in a mix, to tame a resonant room mode, or to flatter a voice. The mathematics did not change; only the aesthetics did.

This page builds a full parametric equaliser — one where frequency, gain and bandwidth are all continuously adjustable per band — from first principles. We derive the biquad from the analogue prototype via the bilinear transform, quote Robert Bristow-Johnson's celebrated cookbook coefficient formulas, and then run those exact formulas in a hand-written AudioWorklet so that the equation printed on the page is quite literally the code processing the sound. You can drag six bands across a live spectrum and hear every move.

Every filter curve, spectrum and animation on this page is computed live from the same coefficient formulas — there are no images and no pre-baked data. What you read is what you hear.

02The biquad and the bilinear transform

A biquad ("bi-quadratic") is a filter whose transfer function is the ratio of two quadratics in the delay operator $z^{-1}$. In the $z$-domain,

$$H(z)=\frac{b_0+b_1 z^{-1}+b_2 z^{-2}}{a_0+a_1 z^{-1}+a_2 z^{-2}}.$$

Realised as a Direct Form I difference equation — the most transparent arrangement, with the two feed-forward delays and two feedback delays kept separate — a normalised biquad computes each output sample as

$$y[n]=\frac{b_0}{a_0}x[n]+\frac{b_1}{a_0}x[n-1]+\frac{b_2}{a_0}x[n-2]-\frac{a_1}{a_0}y[n-1]-\frac{a_2}{a_0}y[n-2].$$

Two multiply-accumulates on past inputs, two on past outputs, five coefficients, four state registers. That is the entire cost of one EQ band, and it is why a modern console can afford dozens of them per channel.

Where the coefficients come from

The recipe starts in the continuous-time world, where a second-order section has a Laplace transfer function $H(s)$ built from an analogue prototype — a resonant RLC network, or its op-amp equivalent. To move it into a sampled system we need a map from the $s$-plane to the $z$-plane that (a) is algebraically simple and (b) keeps a stable analogue filter stable. The bilinear transform does exactly this:

$$s \;\longleftarrow\; \frac{2}{T}\,\frac{1-z^{-1}}{1+z^{-1}},\qquad T=\frac{1}{f_s}.$$

It maps the entire imaginary $s$-axis (all analogue frequencies $0\!\to\!\infty$) onto the unit circle in the $z$-plane exactly once, and the whole open left half-plane to the interior of the unit circle — so stability is preserved. Substituting this into $H(s)$ and clearing denominators always yields a ratio of quadratics in $z^{-1}$: a biquad. The price is frequency warping: the analogue frequency $\Omega$ and the digital frequency $\omega$ are related by $\Omega=\frac{2}{T}\tan(\omega/2)$. The cookbook formulas absorb this warp by pre-warping the design frequency, which is why they are written directly in terms of the digital centre frequency rather than an analogue one.

03The RBJ Audio-EQ Cookbook

In the late 1990s Robert Bristow-Johnson posted a single plain-text document to the music-dsp mailing list that has since become the most-cited page in practical audio DSP. It gives, for each classic response, the five biquad coefficients as closed-form expressions of three shared intermediate quantities. Define

$$\omega_0=\frac{2\pi f_0}{f_s},\qquad \alpha=\frac{\sin\omega_0}{2Q},\qquad A=10^{\,G/40},$$

where $f_0$ is the centre or corner frequency, $Q$ the quality factor (bandwidth), $G$ the gain in decibels for peaking and shelving types, and $A$ therefore the amplitude gain. Note the divide-by-40, not 20: peaking and shelf filters place $A$ on both the numerator and denominator, so the realised gain is $A^2$ in power terms, i.e. $G$ dB. Below, $a_0$ is the normalising denominator — divide every coefficient by it before use.

Peaking (bell) filter

$$\begin{aligned} b_0&=1+\alpha A, & b_1&=-2\cos\omega_0, & b_2&=1-\alpha A,\\ a_0&=1+\alpha/A, & a_1&=-2\cos\omega_0, & a_2&=1-\alpha/A. \end{aligned}$$

The symmetry between numerator and denominator is what makes a bell boost and its matching cut exact mirror images: swap $A\leftrightarrow 1/A$ and the roles of the two quadratics exchange.

Low shelf

$$\begin{aligned} b_0&=A\big[(A{+}1)-(A{-}1)\cos\omega_0+2\sqrt{A}\,\alpha\big],\\ b_1&=2A\big[(A{-}1)-(A{+}1)\cos\omega_0\big],\\ b_2&=A\big[(A{+}1)-(A{-}1)\cos\omega_0-2\sqrt{A}\,\alpha\big],\\ a_0&=(A{+}1)+(A{-}1)\cos\omega_0+2\sqrt{A}\,\alpha,\\ a_1&=-2\big[(A{-}1)+(A{+}1)\cos\omega_0\big],\\ a_2&=(A{+}1)+(A{-}1)\cos\omega_0-2\sqrt{A}\,\alpha. \end{aligned}$$

High shelf

$$\begin{aligned} b_0&=A\big[(A{+}1)+(A{-}1)\cos\omega_0+2\sqrt{A}\,\alpha\big],\\ b_1&=-2A\big[(A{-}1)+(A{+}1)\cos\omega_0\big],\\ b_2&=A\big[(A{+}1)+(A{-}1)\cos\omega_0-2\sqrt{A}\,\alpha\big],\\ a_0&=(A{+}1)-(A{-}1)\cos\omega_0+2\sqrt{A}\,\alpha,\\ a_1&=2\big[(A{-}1)-(A{+}1)\cos\omega_0\big],\\ a_2&=(A{+}1)-(A{-}1)\cos\omega_0-2\sqrt{A}\,\alpha. \end{aligned}$$

Low-pass, high-pass and notch

These have no gain parameter — they either pass or reject — so $A$ drops out and the numerator carries the whole shape:

$$\text{LPF: } b=\Big[\tfrac{1-\cos\omega_0}{2},\;1-\cos\omega_0,\;\tfrac{1-\cos\omega_0}{2}\Big],$$
$$\text{HPF: } b=\Big[\tfrac{1+\cos\omega_0}{2},\;-(1+\cos\omega_0),\;\tfrac{1+\cos\omega_0}{2}\Big],$$
$$\text{Notch: } b=\big[\,1,\;-2\cos\omega_0,\;1\,\big],$$

with the common denominator $a=[\,1+\alpha,\;-2\cos\omega_0,\;1-\alpha\,]$ for all three. The high-pass is the workhorse of every mix: a gentle $Q\approx0.7$ (Butterworth) roll-off from 20–40 Hz removes subsonic rumble and inter-track mud that a single-band section handles for almost no CPU.

TypeParams usedTypical use
Peakingf, gain, QSurgical cut or musical boost of a resonance
Low shelff, gain, QOverall weight / warmth below a corner
High shelff, gain, Q"Air", brightness above a corner
High-passf, QRumble / DC removal, low-end clean-up
Low-passf, QDe-essing send, taming harshness, effects
Notchf, QKilling a single tone: mains hum, feedback

04Reading the response off the unit circle

Coefficients are not a spectrum. To see what a biquad does we evaluate its transfer function on the unit circle, where $z=e^{j\omega}$ and $\omega=2\pi f/f_s$ runs from $0$ (DC) to $\pi$ (Nyquist). The complex frequency response is

$$H(e^{j\omega})=\frac{b_0+b_1 e^{-j\omega}+b_2 e^{-2j\omega}}{a_0+a_1 e^{-j\omega}+a_2 e^{-2j\omega}}.$$

The magnitude response $|H(e^{j\omega})|$ — how much each frequency is boosted or cut — is the modulus of that ratio, usually plotted in decibels as $20\log_{10}|H|$. The phase response $\arg H(e^{j\omega})$ is its argument, and its negative derivative is the group delay. A minimum-phase biquad like these couples the two: you cannot change the magnitude without incurring the phase shift that a causal, stable filter obliges. Only a linear-phase (FIR) EQ escapes it, at the cost of latency and pre-ring.

Evaluating the modulus is just book-keeping with a complex exponential. Writing $c=\cos\omega$, $s=\sin\omega$ and stepping to the second harmonic with $\cos 2\omega$, $\sin 2\omega$, the numerator's real and imaginary parts are $\Re=b_0+b_1 c+b_2\cos 2\omega$ and $\Im=-(b_1 s+b_2\sin 2\omega)$, and similarly for the denominator; the magnitude is the ratio of the two hypotenuses. This is precisely the function evalBiquad() that draws the green curve in the demo below, and cascading bands means multiplying their complex responses (adding their dB curves and their phases).

Cascade = multiply in magnitude, add in dB, add in phase. Six bands in series give one product response — which is exactly what the plugin computes, point by point, every animation frame.

05Live demo — a six-band parametric EQ

Everything here runs on hand-written biquads. Six bands — a high-pass, a low shelf, three bells and a high shelf — are cascaded inside an AudioWorklet executing the Direct Form I difference equation sample by sample. The green curve is the product of the six transfer functions evaluated on the unit circle; the ghostly fill behind it is a live FFT of the output. Drag the numbered nodes, or use the per-band strips, and listen.

◈ Parametric EQ · 6 Band DSP & MUSIC
Output
0.0 dB
Mix
100%
Out
Idle — pick a source and press Play.
CLIP

Drag a node horizontally to sweep frequency, vertically to change gain (for bells and shelves) or Q (for the high-pass, low-pass and notch). The mouse wheel over a node adjusts its Q; double-clicking resets that band. On a phone, drag with one finger. The built-in loop synthesises a kick, a snare, a bass line and a sustained chord pad so that moves in every region are audible.

06Extra views — phase and the single biquad

The main display sums the magnitude of all six bands. Two further canvases isolate the pieces. First, the phase response of the same cascade, wrapped to $\pm180^\circ$ — watch how a bell boost drags the phase one way on its low side and the other on its high side, the unavoidable signature of a minimum-phase filter:

Cascade phase $\arg H(e^{j\omega})$, computed live from the same coefficients as the demo above.

Second, a self-contained single-biquad explorer. This one makes no sound; it simply draws $20\log_{10}|H|$ for one filter so you can see, in isolation, how frequency, gain and $Q$ deform a single second-order section. Raising $Q$ narrows a bell and steepens a shelf's overshoot; the coefficient formulas of §3 are doing all of it.

One RBJ biquad, magnitude only — the atomic unit every EQ is built from.

07The same filter in JUCE

The browser demo hand-rolls the difference equation for pedagogy. In a shipping plug-in you would normally lean on juce::dsp::IIR, whose Coefficients factory methods implement these very cookbook formulas. Here is a compact but idiomatic sketch: an AudioProcessorValueTreeState parameter layout for one band, a ProcessorDuplicator so a mono coefficient set runs on a stereo (or N-channel) signal, and the coefficient update in parameterChanged.

// --- PluginProcessor.h (essentials) ---
class EqAudioProcessor : public juce::AudioProcessor,
                         private juce::AudioProcessorValueTreeState::Listener
{
public:
    using Filter     = juce::dsp::IIR::Filter<float>;
    using Coeffs     = juce::dsp::IIR::Coefficients<float>;
    using Duplicator = juce::dsp::ProcessorDuplicator<Filter, Coeffs>;

    EqAudioProcessor();
    void prepareToPlay (double sr, int blockSize) override;
    void processBlock (juce::AudioBuffer<float>&, juce::MidiBuffer&) override;

    juce::AudioProcessorValueTreeState apvts;

private:
    static juce::AudioProcessorValueTreeState::ParameterLayout makeLayout();
    void parameterChanged (const juce::String& id, float value) override;
    void updateBand (int i);

    double sampleRate = 44100.0;
    juce::dsp::ProcessorChain<Duplicator, Duplicator, Duplicator,
                              Duplicator, Duplicator, Duplicator> chain;
};
Six duplicators in a ProcessorChain — one per band, exactly mirroring the JS cascade.
// --- parameter layout: three params per band ---
juce::AudioProcessorValueTreeState::ParameterLayout
EqAudioProcessor::makeLayout()
{
    juce::AudioProcessorValueTreeState::ParameterLayout layout;
    const char* names[] = { "hp","ls","p1","p2","p3","hs" };

    for (int b = 0; b < 6; ++b)
    {
        auto id = juce::String (names[b]);
        layout.add (std::make_unique<juce::AudioParameterFloat>(
            id + "_freq", "Freq " + id,
            juce::NormalisableRange<float>(20.f, 20000.f, 1.f, 0.25f), // skewed = log
            (float) juce::jmap (b, 0, 5, 30, 9000)));
        layout.add (std::make_unique<juce::AudioParameterFloat>(
            id + "_gain", "Gain " + id,
            juce::NormalisableRange<float>(-24.f, 24.f, 0.1f), 0.f));
        layout.add (std::make_unique<juce::AudioParameterFloat>(
            id + "_q", "Q " + id,
            juce::NormalisableRange<float>(0.1f, 12.f, 0.001f, 0.3f), 0.707f));
    }
    return layout;
}

// --- turning parameters into coefficients ---
void EqAudioProcessor::updateBand (int i)
{
    const char* names[] = { "hp","ls","p1","p2","p3","hs" };
    auto id = juce::String (names[i]);
    const float f = apvts.getRawParameterValue (id + "_freq")->load();
    const float g = apvts.getRawParameterValue (id + "_gain")->load();
    const float q = apvts.getRawParameterValue (id + "_q")   ->load();
    const float A = juce::Decibels::decibelsToGain (g);

    juce::dsp::IIR::Coefficients<float>::Ptr c;
    switch (i)
    {
        case 0: c = Coeffs::makeHighPass  (sampleRate, f, q);       break;
        case 1: c = Coeffs::makeLowShelf  (sampleRate, f, q, A);    break;
        case 5: c = Coeffs::makeHighShelf (sampleRate, f, q, A);    break;
        default:c = Coeffs::makePeakFilter(sampleRate, f, q, A);    break;
    }
    // route to the i-th duplicator in the chain
    switch (i)
    {
        case 0: *chain.get<0>().state = *c; break;
        case 1: *chain.get<1>().state = *c; break;
        case 2: *chain.get<2>().state = *c; break;
        case 3: *chain.get<3>().state = *c; break;
        case 4: *chain.get<4>().state = *c; break;
        case 5: *chain.get<5>().state = *c; break;
    }
}

void EqAudioProcessor::processBlock (juce::AudioBuffer<float>& buffer,
                                     juce::MidiBuffer&)
{
    juce::dsp::AudioBlock<float> block (buffer);
    juce::dsp::ProcessContextReplacing<float> ctx (block);
    chain.process (ctx);
}
makePeakFilter / makeLowShelf / makeHighShelf are JUCE's names for the cookbook formulas of §3.

For readers who want to see that the library is doing nothing magical, here is the raw hand-coded biquad struct — the exact C++ analogue of the JavaScript that runs in the worklet. This maps one-to-one onto the Direct Form I equation of §2:

// --- a bare biquad, Direct Form I, matching the JS worklet ---
struct Biquad
{
    float b0=1, b1=0, b2=0, a1=0, a2=0;     // normalised (a0 == 1)
    float x1=0, x2=0, y1=0, y2=0;           // state

    inline float process (float x) noexcept
    {
        const float y = b0*x + b1*x1 + b2*x2 - a1*y1 - a2*y2;
        x2 = x1; x1 = x;                    // shift input history
        y2 = y1; y1 = y;                    // shift output history
        return y;
    }

    void setPeaking (double fs, double f0, double dBgain, double Q)
    {
        const double A  = std::pow (10.0, dBgain / 40.0);
        const double w0 = 2.0 * juce::MathConstants<double>::pi * f0 / fs;
        const double cw = std::cos (w0), sw = std::sin (w0);
        const double alpha = sw / (2.0 * Q);
        const double a0 = 1.0 + alpha / A;
        b0 = (float) ((1.0 + alpha * A) / a0);
        b1 = (float) ((-2.0 * cw)       / a0);
        b2 = (float) ((1.0 - alpha * A) / a0);
        a1 = (float) ((-2.0 * cw)       / a0);
        a2 = (float) ((1.0 - alpha / A) / a0);
    }
};
Compare this struct's setPeaking with the biquadCoeffs() function driving the live demo — they are line-for-line the same arithmetic.

Drawing the response in the editor is the same unit-circle evaluation the demo performs: for a path across the component's width, map each pixel to a log frequency, accumulate the magnitude of every band with Coefficients::getMagnitudeForFrequency, convert to decibels, and lineTo.

08A short history of tone control

1930s–1950s
Passive equalisation is born of necessity: disc-cutting pre-emphasis, telephone line equalisers, and cinema loudspeaker compensation. Fixed LC networks correct known losses — the literal equalising of a channel.
1951 · Pultec EQP-1A
The Pulse Techniques program equaliser makes passive shelving musical. Its quirk of allowing simultaneous boost and cut at the same low frequency — producing a resonant dip above the boost — becomes a mixing legend still emulated today.
1967–1970 · Neve 1073 · API 550
Discrete-transistor console EQs define the sound of a decade. The Neve 1073's inductor-based bands and the API 550's stepped "Proportional-Q" curves establish opposing philosophies of colour that engineers still choose between deliberately.
1972 · Massenburg
George Massenburg presents the fully parametric equaliser to the AES: continuous, independent control of frequency, bandwidth and gain in one band. It is the conceptual template for every EQ on this page.
1980s
Digital EQ arrives in early workstations and the first plug-ins. The graphic EQ (fixed bands, faders) and the parametric coexist; the mathematics moves from op-amps and inductors to sampled biquads.
late 1990s · The Cookbook
Robert Bristow-Johnson's Audio-EQ Cookbook circulates on the music-dsp list, giving every developer the same battle-tested coefficient formulas. It quietly standardises how the world's software EQs are built.
2000s
Linear-phase EQ becomes practical: FFT-based designs trade latency and pre-ring for zero phase distortion, prized in mastering where phase coherence across a stereo bus matters.
2010s · Pro-Q
FabFilter's Pro-Q popularises the interactive, drag-the-node interface with a live analyser behind the curve — the very interaction model reproduced in this demo — and dynamic per-band behaviour blurring the line between EQ and multiband compression.

09Kinds of equaliser, compared

TypeControlStrengthsTrade-offs
GraphicFixed bands, one fader eachFast, visual, live-sound stapleCoarse; overlapping bands interact
ParametricContinuous f, gain, Q per bandSurgical and musical; the modern defaultNeeds skill; easy to over-process
ShelvingCorner + gain (broad)Gentle tonal tilt, natural soundingNot for isolating a single frequency
Minimum-phase (IIR)Biquads — this pageCheap, zero latency, "analogue" feelMagnitude change drags phase with it
Linear-phase (FIR)FFT / long convolutionNo phase distortion; mastering-gradeLatency and pre-ring on transients

The demo above is squarely a minimum-phase parametric EQ combining shelving and bell sections — the configuration of a classic mixing channel strip. The high-pass and shelves anchor the extremes; the three bells do the detailed carving in between.

10References & further reading

Sources

  1. R. Bristow-Johnson, Cookbook formulae for audio EQ biquad filter coefficients, music-dsp mailing list / Analog Devices application note (c. 1998–2005). The canonical source for every coefficient in §3.
  2. U. Zölzer (ed.), DAFX: Digital Audio Effects, 2nd ed., Wiley, 2011 — filter design, the bilinear transform and equaliser structures.
  3. G. Massenburg, Parametric Equalization, presented at the 42nd AES Convention, 1972 — the origin of continuously variable, independent band control.
  4. J. O. Smith III, Introduction to Digital Filters, with Audio Applications, W3K Publishing / CCRMA (online) — transfer functions, the unit circle and biquad realisations.
  5. A. V. Oppenheim & R. W. Schafer, Discrete-Time Signal Processing, 3rd ed., Pearson, 2010 — the bilinear transform and $z$-plane fundamentals.
  6. U. Zölzer, Digital Audio Signal Processing, 2nd ed., Wiley, 2008 — practical shelving and peaking filter derivations.
  7. W3C, Web Audio API specification — AudioWorklet, AnalyserNode, and the processing model used here.