Designing FIR filters
A finite impulse response (FIR) filter makes each output sample a weighted sum of the most recent input samples:
The weights are the filter’s taps (or coefficients), and designing an FIR filter means choosing them. This page starts with the most naive choice imaginable, works out what’s wrong with it, and builds up to the methods used in practice.
Frequencies on this page are normalised: measured in cycles per sample, so they run from 0 (DC) to 0.5 (the Nyquist frequency, half the sample rate). Multiply by the sample rate to get Hz. At 44.1 kHz, 0.1 × fs is 4.41 kHz.
1 · The simplest filter: a moving average
Section titled “1 · The simplest filter: a moving average”Set all taps to and each output is just the average of the last inputs. Averaging smooths out fast wiggles and keeps slow trends, so it’s already a crude low-pass filter:
Each output sample is the average of the shaded input samples. Press Slide the window to watch it move. Longer averages smooth more, but notice two other things. The output lags the input by samples, because the average is centred on the middle of the window. And the square wave’s sharp edges turn into ramps samples long.
The impulse response
Section titled “The impulse response”Before going further we need one idea that the rest of the page leans on. Feed the filter the simplest possible input: a single sample of 1, with zeros before and after. This is called an impulse, written . In the sum , only the term with survives, so : the taps come straight out, one per sample, in order. That output is the filter’s impulse response.
Top: the impulse. Middle: the taps, lined up under the samples they multiply, reversed, since multiplies the newest input. Bottom: the output. Step through time (or press Play): as the window slides past the lone 1, each tap meets it exactly once, so the output reproduces the taps. Pick decaying taps to see that they come out in order.
For an FIR filter the impulse response is just the list of taps. It’s finite: after samples the impulse has passed through and the output returns to zero. That’s where the name finite impulse response comes from. (The IIR filters on the next page feed their output back into themselves, so their impulse response goes on forever.)
The impulse response also tells you everything about the filter. Any input signal is a sum of scaled, shifted impulses: times an impulse at 0, plus times an impulse at 1, and so on. The filter is linear (scale or add inputs, and the outputs scale and add) and time-invariant (delay the input, and the output is delayed the same way). So its output is the same sum of scaled, shifted impulse responses. That sum is exactly the convolution formula at the top of the page.
2 · Frequency response: what a filter does to each sinusoid
Section titled “2 · Frequency response: what a filter does to each sinusoid”“Smooths out fast wiggles” is vague. We want to know exactly how much of each frequency gets through. Here’s the key property of any FIR filter (any linear, time-invariant system, in fact): feed in a sinusoid and you get out a sinusoid of the same frequency, just scaled and delayed. The scale factor at each frequency is the filter’s frequency response.
So one way to measure it is to do exactly that: send in sinusoids one frequency at a time and measure what comes out. Try it below by dragging the test frequency.
The other way is quicker. Feed in a sinusoid, written as a complex exponential , and the filter’s sum becomes
The bracket is , the frequency response, and it is exactly the Fourier transform of the impulse response. An FFT of the taps gives you at evenly spaced frequencies. Pad the taps with zeros before the FFT and you get the same curve sampled more finely. Zero-padding adds no information, it just draws the curve with more points.
Top: the moving average’s impulse response. Middle: a test sinusoid going in (grey) and coming out (blue). Bottom: the curve is the zero-padded FFT of the impulse response; the dot is the gain measured from the sinusoid in the middle panel. They always agree. Notice the nulls at multiples of : a sinusoid with exactly 1, 2, 3… whole cycles inside the -sample window averages to zero. Turn on the dB scale to see how weak the stopband is. The first “bump” past the main lobe only gets down to about −13 dB, so about a fifth of that frequency’s amplitude leaks through.
For the moving average the curve has a closed form, the Dirichlet kernel:
3 · Turning a low-pass into a high-pass
Section titled “3 · Turning a low-pass into a high-pass”Two neat tricks turn any low-pass into a high-pass.
Spectral inversion. A high-pass keeps what a low-pass throws away, so subtract: the input minus its smoothed version. As taps, that’s a single 1 at the centre (an impulse , which passes everything unchanged, delayed to line up with the low-pass) minus the low-pass taps:
This needs an odd number of taps, so the centre lands on a whole sample.
Modulation. Multiply the taps by . Since is a complex exponential at exactly half the sample rate, this shifts the whole frequency response up by . What was at DC moves to Nyquist, and the response is mirrored. (If you’ve read the MP3 page, this is the same trick that turns the polyphase filterbank’s lowest band into its highest.)
Top: the taps. Middle: the response (the original low-pass dashed). Bottom: a mix of a slow and a fast sinusoid, filtered. Spectral inversion gives , so the low-pass’s leaky sidelobes become ripples in the high-pass’s passband. Modulation gives a mirror image of the low-pass. Either way, the high-pass inherits all of the low-pass’s flaws.
4 · What’s wrong with the moving average
Section titled “4 · What’s wrong with the moving average”It works, after a fashion, but it falls short of a real filter specification in every way:
- You can’t choose the cutoff independently. The only knob is , which sets the cutoff (about ), the roll-off and the stopband nulls all at once.
- The roll-off is gentle. There’s no sharp line between frequencies that pass and frequencies that stop. The transition band spans the whole main lobe.
- The stopband is leaky. Sidelobes at −13 dB, −18 dB, −21 dB… barely count as stopping anything. A usable audio filter needs more like 60–100 dB.
- The passband droops: the gain starts falling immediately above DC.
A real design starts from a specification: pass everything below (the passband edge) with at most so much ripple, reject everything above (the stopband edge) by at least dB, and use as few taps as possible. The gap between and is the transition band. So instead of guessing taps, we should work backwards from the response we want.
5 · Start from the response you want: the IFFT method
Section titled “5 · Start from the response you want: the IFFT method”If the FFT of the taps is the frequency response, then the inverse FFT of a frequency response should give the taps. So: write down the ideal “brick-wall” low-pass (gain 1 up to the cutoff , 0 above), sample it at evenly spaced frequencies, and inverse-FFT. This is called frequency-sampling design.
Top: the brick wall we asked for (dashed), the points we actually specified (orange), and the response of the resulting filter (blue). It passes exactly through every specified point, but ripples in between, worst next to the cutoff. Middle: the same in dB. Bottom: the taps turn out to be (almost exactly) samples of a sinc function. Drag up: the ripples crowd closer to the edge and the transition gets sharper, but the overshoot stays stubbornly around 13 % and the stopband never gets below about −17 dB. Switch Taps from to the ideal sinc, truncated to compare with the next section.
Why the ripple won’t go away: the Gibbs phenomenon
Section titled “Why the ripple won’t go away: the Gibbs phenomenon”The ideal brick-wall low-pass has a perfectly good impulse response, the sinc:
But it extends forever in both directions, so no finite filter can have it. Keeping only taps is the same as multiplying the ideal taps by a rectangular window: 1 for the taps we keep, 0 for the rest. And multiplying in time is convolving in frequency: the response we get is the ideal brick wall, smeared by the spectrum of the rectangle. That spectrum is the Dirichlet kernel again, the same shape as the moving average’s response with its −13 dB sidelobes. Its sidelobes become the ripples.
Adding taps makes the rectangle longer, which makes its spectrum narrower, but its sidelobes keep the same relative height. So the ripples get squeezed towards the cutoff without getting smaller. That’s the Gibbs phenomenon, and it’s why simply using more taps doesn’t fix it. Switch the figure above to the ideal sinc, truncated. The overshoot settles at about 9 % (the classic Gibbs figure) and the stopband at about −21 dB, whatever you choose.
That’s a little better than the IFFT design, and not by accident. Plain truncation is the least-squares best fit to the brick wall: no other -tap filter has less total squared error. The IFFT design gives that up to hit its specified points exactly. Its taps are the sinc plus copies of it wrapped around from neighbouring periods (time-domain aliasing), and that costs it 4 dB. But least-squares is the wrong goal anyway. The squared error is dominated by the transition band, and the ripples we care about are left as they fall.
6 · Windows: tapering instead of chopping
Section titled “6 · Windows: tapering instead of chopping”The fix is to stop chopping the sinc off abruptly. Multiply it by a window that tapers smoothly to zero at both ends instead. The response is then the ideal brick wall convolved with that window’s spectrum. Here is the convolution, visualised directly:
Top: the ideal passband (grey) and the window’s spectrum (blue), centred on frequency . The filter’s response at is the shaded area: the part of the window spectrum inside the passband. Bottom: the response this traces out as sweeps. Press Sweep f and watch three things. Deep in the passband the whole main lobe is inside, so the area is about 1. At the cutoff the main lobe slides out across the edge, so the transition band is about as wide as the main lobe. In the stopband only sidelobes overlap the passband, so they set how much leaks through. Compare the rectangular window, whose sidelobes are big, with Hann and Blackman.
That’s the whole story of window design. Every window is a trade between those two properties: main-lobe width (which sets the transition width) and sidelobe level (which sets the stopband attenuation). Smoother windows have much lower sidelobes, but a wider main lobe.
Top: five classic windows (51 points). Bottom: their spectra in dB. The horizontal axis is in “bins” (frequency × N), so the main-lobe widths can be compared directly. The rectangle has the narrowest main lobe but sidelobes at −13 dB. Hann and Hamming trade a doubled main lobe for −31 dB and −43 dB sidelobes. Blackman triples it for about −58 dB. The Kaiser window has a parameter, β, that slides continuously along this trade-off: drag it.
Now design with them. The windowed filter is just :
Top: the truncated ideal sinc (dashed), the window (orange, scaled), and their product, the filter (blue stems). Bottom: the response in dB, with the plain truncation dashed for comparison. The tiles measure the result. Try changing the window at fixed : the attenuation changes a lot, while the transition width changes in the opposite direction. Then change with a fixed window: the transition narrows (roughly as ), but the attenuation hardly moves. The window sets the attenuation; the length sets the transition width. Use Listen to hear white noise through the filter (at 44.1 kHz).
Measured from each window, and from a 101-tap filter designed with it:
All numbers are measured live from the code on this page, not copied from a textbook. Broadly, more attenuation costs a wider transition, but not strictly. Hamming beats Hann on both counts. Hann’s sidelobes start higher and then fall away quickly with frequency, while Hamming is designed to make its highest sidelobe as low as possible, and a filter spec only cares about the worst one. The filter’s attenuation is also 8–23 dB better than the window’s own highest sidelobe. The response is an integral over the window spectrum, and neighbouring positive and negative sidelobes partly cancel.
7 · Designing to a spec: the Kaiser window
Section titled “7 · Designing to a spec: the Kaiser window”With a fixed window, you’re stuck with that window’s attenuation. James Kaiser measured how his window’s β and length relate to the result and fitted simple formulas, so you can go straight from a spec to a design. For a stopband attenuation of dB (which also fixes the passband ripple ) and a transition width :
The cutoff goes halfway between the band edges.
Set the spec and the formulas pick β and . The red regions are what the spec forbids, and the response must stay out of them. Halving the transition width doubles the length. Every extra 20 dB of attenuation adds roughly 1.4/Δf taps. The formulas are empirical fits, so the result sometimes lands a fraction of a dB short of the spec (as it does for the default settings). In practice you check the design and add a couple of taps if needed.
8 · Optimal filters: Parks–McClellan
Section titled “8 · Optimal filters: Parks–McClellan”Look closely at any window design and you’ll see wasted effort. The ripples are biggest next to the transition band and die away further out. The spec only cares about the worst ripple, so all the small ripples are over-achieving. Taps spent making them small could have been used to narrow the transition or shorten the filter.
The best possible filter of a given length spreads the error evenly: every ripple the same height. This is the equiripple or minimax design, which minimises the maximum error. The algorithm that finds it is the Parks–McClellan algorithm (1972), built around Remez’s exchange algorithm from approximation theory.
A symmetric filter with taps has a real amplitude response , which is a polynomial of degree in . The alternation theorem says this polynomial minimises the maximum weighted error exactly when the error reaches its maximum magnitude , with alternating sign, at no fewer than frequencies. Here is the desired response and a weight. The Remez exchange searches for those frequencies:
- Guess “extremal” frequencies.
- Solve for the polynomial whose error is exactly at the guessed frequencies. That’s equations in unknowns: the coefficients plus .
- Look at the error everywhere. Its real peaks are not where we guessed, and some exceed .
- Exchange: move the guesses to the frequencies of the actual peaks, and go back to step 2.
Each round, grows and the peaks even out. When every peak is within a hair of , that’s the optimum.
The exchange broke down: with this many taps the optimal ripple would be smaller than double-precision arithmetic can resolve (well below −200 dB). Use fewer taps or a narrower transition band.
Top: the response (blue), with a Kaiser-window design of the same length for comparison (orange dashes), and the current extremal frequencies (green). Bottom: the weighted error. The algorithm forces it to exactly at the green points; everywhere else it’s whatever it turns out to be. Press Start over, then Step to watch the exchange: the green points jump to the peaks, and within a handful of iterations every ripple is the same height. The weight trades passband against stopband. Weight 10 makes the stopband ripple 10× smaller and the passband ripple 10× larger. Fewest taps finds the shortest equiripple filter for a given attenuation and compares it with Kaiser’s estimate.
Equiripple designs typically meet a spec with noticeably fewer taps than a window design, which matters when every tap is a multiply per sample. The price is a less predictable design process. Very wide or very narrow transition bands can make the exchange misbehave, and some specs can’t be met at a given length at all.
9 · The trade-offs, summarised
Section titled “9 · The trade-offs, summarised”Whatever the method, the same four quantities pull against each other:
| Get more of it by… | Costs… | |
|---|---|---|
| Sharper transition | more taps | computation, delay |
| More stopband attenuation | smoother window / higher stopband weight / more taps | wider transition, or more taps |
| Less passband ripple | same as attenuation (window) or higher passband weight (equiripple) | the other band’s ripple, or more taps |
| Less delay | fewer taps | all of the above |
And two things come with FIR filters regardless of how you design them:
- Cost: multiply-adds per output sample.
- Delay: a symmetric filter delays every frequency by exactly samples. That’s linear phase: the waveform’s shape is preserved, only shifted. It’s the big advantage FIR filters have over IIR filters, which reach a sharp response with far fewer coefficients but delay different frequencies by different amounts. A sharp FIR filter can be long, though: a 501-tap filter at 44.1 kHz delays everything by 5.7 ms.
| Method | Strengths | Weaknesses |
|---|---|---|
| Moving average | Trivial, very cheap (can be computed with a running sum) | No control; poor stopband |
| IFFT / frequency sampling | Any response shape, directly | Gibbs ripple between the samples |
| Window (Hann, Hamming, Blackman) | Simple, robust, predictable | Fixed attenuation per window; wastes taps |
| Kaiser window | Designs straight from a spec | Still wastes taps on over-achieving ripples |
| Parks–McClellan | Fewest taps for a spec; controllable band weights | Iterative; can fail to converge for awkward specs |
Further reading
Section titled “Further reading”- J. F. Kaiser, “Nonrecursive digital filter design using the I₀-sinh window function” (1974): the Kaiser window and its design formulas.
- T. W. Parks and J. H. McClellan, “Chebyshev approximation for nonrecursive digital filters with linear phase” (1972): the equiripple method.
- A. V. Oppenheim and R. W. Schafer, Discrete-Time Signal Processing, chapter 7: the standard textbook treatment.
- F. J. Harris, “On the use of windows for harmonic analysis with the discrete Fourier transform” (1978): the classic survey of windows.