← All tutorials

Aliasing and Anti-Aliasing Filters

Why fast tones look slow.

On this page

1 Aliasing on film 2 The Nyquist limit 3 Sampling copies the spectrum 4 Filter before you sample 5 Changing the sampling rate 6 In practice 7 When sampling goes wrong 8 References

Sampling keeps one value at fixed intervals and throws away everything in between. A tone faster than half the sampling rate then leaves exactly the same samples as a slower tone, so it shows up at the wrong frequency, and nothing after the analog-to-digital converter (ADC) can undo that. The cure is to remove those fast tones before they are sampled, with an analog low-pass filter, or to sample fast and filter digitally before slowing the rate down. The analog filter acts continuously on the input; the digital one acts once per kept sample.

1 Aliasing on film

Film takes 24 pictures per second and shows you nothing in between, so your eye joins each spoke to the nearest spoke in the next picture. The spokes in Figure 1 are 45° apart, and the wheel turns 40° per picture, a little less than one gap. The nearest spoke then sits 5° behind, so the wheel seems to creep backward.

frame 1 frame 2 frame 3 frame 4 the red spoke turns 40° forward per frame the nearest spoke seems to slip 5° back
Figure 1. 1The real turn, 40° forward. 2The nearest spoke seems to slip 5° back.

A sampled sine has the same problem. Between two samples a fast sine can turn almost a full cycle, and the dots then trace a slow wave instead. That false, slower tone is called an alias. The line marked Nyquist in Figure 2 sits at half the sampling rate, the limit Section 2 explains.

Nyquist05001000 Hzsampling ratefrequencysignal600 Hzalias400 Hz
Figure 2. Sampled at 1 kHz, 1a 600 Hz tone shows up as 2a 400 Hz alias.

2 The Nyquist limit

The wheel looked right as long as it turned less than half a spoke gap per picture. For a sine the limit is half a cycle per sample, which puts the highest safe frequency at half the sampling rate fs.

Three common sampling rates and the highest frequency each can keep.

RecordingSampling rate fsHighest frequency, fs/2
Phone call8 kHz4 kHz
Speech recognizer input16 kHz8 kHz
Music CD44.1 kHz22.05 kHz
The highest frequency the samples can describe.
fNyquist = fs2
The Nyquist limit fNyquist is half the sampling rate fs, the number of samples per second, because a tone needs more than two samples per cycle to be told apart from a slower one. Doubling fs doubles this limit; anything faster folds back below it.
fmax < fs2
The limit is strict. A sine exactly at fs2 can be sampled at its zero crossings and read as all zeros, so fmax, the highest frequency in the signal, must sit strictly below half the rate. It builds on the wheel of Section 1 and is used by the fold rule in Section 3 and by every rate change in Section 5.

Shannon proved this sampling theorem in 1949, building on Nyquist (1928): a signal with nothing above fs/2 is fully pinned down by its samples.

3 Sampling copies the spectrum

frequency how much of each frequency
Figure 3. Bar height is how strong each frequency is (Fourier Transform and the FFT).

Why does a fast tone land at a specific slower one? Sampling copies the whole spectrum to every multiple of fs. In Figure 4 each triangle stands for the band of frequencies a signal holds.

Fast enough, the copies sit apart0fs2fsToo slow, the copies overlap and mix0fs2fsoverlapoverlap
Figure 4. 1Sampled fast, the copies sit apart. 2Too slow, they overlap. 3The overlap is aliasing.
The spectrum after sampling.
Xs(f) = fs Σk X(f − k fs)
Xs(f) is the spectrum after sampling and X(f) the continuous signal's spectrum. Sampling multiplies the signal by a row of spikes one sample period apart, which in frequency makes copies: each term X(f − k fs) is X shifted by k fs, and Σk adds one for every whole number k, positive or negative. The factor fs only scales the height. Where a copy overlaps the original band, the two can no longer be pulled apart, and that overlap is aliasing. It builds on the spectrum and is used by the fold rule and by upsampling in Section 5.2.

The copy that lands between 0 and fs/2 is the one you see. That gives the fold rule, and at 16 kHz it moves two tones from above Nyquist to well below it.

ToneFrequency fLands at fs − f
A high test tone10 kHz6 kHz
An old TV’s whine15.73 kHz0.27 kHz

Illustrative tones at fs = 16 kHz, Nyquist 8 kHz. The whine sits just below fs, so it folds to near 0.

Where a tone at frequency f shows up after sampling.
falias = |f − k fs|
falias is the frequency the samples show. A tone at f also sits in every copy, at f − k fs, and the samples show whichever copy lands between 0 and fs2. The whole number k picks that copy, and the absolute value folds a negative frequency back to positive.
falias = fs − f
This is the case k = 1, for f between fs2 and fs. As f rises past Nyquist the alias falls by the same amount, so a tone just above fs2 shows up just below it. It builds on the copies card and is used by the tool in Figure 5 and by the filter in Section 4.

Twenty samples of a 600 Hz tone and its 400 Hz alias at fs = 1 kHz; the last line prints True because the samples match.

import numpy as np

FS, F_TONE = 1000, 600
F_ALIAS = abs(F_TONE - FS)          # 400 Hz
n = np.arange(20)                   # sample index n
tone = np.cos(2 * np.pi * F_TONE * n / FS)
alias = np.cos(2 * np.pi * F_ALIAS * n / FS)
print(np.allclose(tone, alias))
  1. 1Puts the 600 Hz tone's alias at fs − f = 400 Hz (eq card in Section 3, line 2).
  2. 2Samples both tones 20 times and checks that the dots are the same.

Playback rebuilds the one smooth wave below Nyquist through the dots, so a tone past Nyquist plays back as its alias even though every sample was right.

Nyquist fs/2 = 500 Hz
ALIASED
Alias frequency = 400 Hz

Figure 5. 1The alias hits the same sample dots as the gray true tone.

4 Filter before you sample

Take an illustrative recording at fs = 1 kHz with a wanted signal at 150 Hz and a 600 Hz interferer. Once the interferer has folded to 400 Hz, no digital filter can tell it from a real 400 Hz signal. So an analog low-pass anti-aliasing filter in front of the ADC removes what lies above Nyquist before sampling.

|H(f)| is how much of frequency f a filter lets through; How FIR and IIR Filters Shape Magnitude and Phase builds it from scratch. The Butterworth low-pass stays as flat as possible below its cutoff fc. Its order p counts its poles, the factors in the bottom of its response, and each pole adds 20 dB per decade (ten times the frequency) of fall past the cutoff.

How much power a Butterworth low-pass of order p lets through.
|H(f)|2 = 11 + (ffc)2p
The ratio ffc measures frequency in cutoffs. At f = fc it is 1, so the bottom is 2 and half the power passes. Below the cutoff the ratio raised to 2p stays near 0 and almost everything passes; above it the term grows fast, and a higher order p makes the fall steeper, 20p dB per decade. It builds on the decibel and on |H(f)|, and is used by Figure 7, Figure 8 and the digital low-pass in Section 5.
0 dB−20 dB−40 dBcutoff10 × cutoff−3 dBpasses
Figure 6. 1Order 1 falls 20 dB per decade. 2Higher orders fall faster. 3The cutoff, 3 dB down.

At fs = 1 kHz, a 500 Hz cutoff is only 3 dB down at Nyquist, so a 600 Hz interferer still gets through and becomes a 400 Hz alias.

The Butterworth card in decibels for four cutoff and order pairs, plus the wanted band; a lower cutoff and a higher order both push the 600 Hz interferer down.

import numpy as np

def butter_db(f, f_c, p):
    return -10 * np.log10(1 + (f / f_c) ** (2 * p))

wanted = butter_db(150, 400, 4)                      # the wanted band
CASES = [(500, 4), (400, 4), (400, 8), (400, 12)]    # (cutoff, order)
interferer = [butter_db(600, f_c, p) for f_c, p in CASES]
  1. 1Gain in dB of a Butterworth low-pass (eq card in Section 4, line 1).
  2. 2The wanted 150 Hz band passes almost untouched.
  3. 3The 600 Hz interferer for each cutoff and order pair, the points of Figure 7.
148120 dB−20−40Butterworth order pgain at 600 Hzcutoff 500 Hzcutoff 400 Hz
Figure 7. Gain at 600 Hz. At order 4, 1−7.2 dB and 2−14.3 dB. 3Order 12 gets below −40 dB.

Higher order has a price. The anti-aliasing Butterworth is an analog circuit, and its group delay, how long each frequency is held back, changes with frequency, more so at higher order. Its digital copy is an IIR filter, one that feeds its own output back, and it also delays frequencies unevenly. How FIR and IIR Filters Shape Magnitude and Phase shows why that bends a waveform.

The dB axis grows in 60 dB steps.

A lower cutoff or higher order shrinks the 400 Hz alias.

Figure 8. Bars show the amplitude left. 1The wanted 150 Hz. 2600 Hz interferer.

5 Changing the sampling rate

Section 4 filtered the signal before the converter. Once audio is digital, its rate often still has to change, to store fewer samples or to feed a model built for one rate. Two recordings must also share a rate before they can be compared or matched (Anushiravani 2016).

5.1 Sample fast, then slow down in software

A steep analog filter is expensive, and its group delay grows more uneven as it gets steeper. Sampling much faster than needed lets a gentle analog filter do the job; a digital low-pass then removes the rest, and the downsampling factor Q says how many samples go in for each one kept. That low-pass followed by keeping one sample in Q is called decimation.

sample fast, gentle analog filter analog old Nyquist new Nyquist steep digital low-pass digital new Nyquist keep one sample in Q Nyquist nothing above, nothing folds frequency
Figure 9. 1Gentle analog roll-off. 2Steep digital cut at the new Nyquist. 3Keep one in Q.

For example, keeping one sample in Q = 4 takes 8 kHz audio to 2 kHz, the case Figure 11 runs on a rising tone.

The sampling rate after keeping one sample in Q.
fs,new = fsQ
fs,new is the rate after. Keeping one sample in Q divides the rate by Q, and with it the Nyquist limit, which drops to fs,new2. Anything between the new and the old limit would fold, so the digital low-pass removes it first. It builds on the Nyquist limit of Section 2 and the low-pass of Section 4, and is used by the any-ratio card in Section 5.3.

A spectrogram stacks short spectra side by side over time, with frequency going up and brightness for strength.

time high low
Figure 10. Frequency runs up. A rising tone is a rising line (The STFT and Spectrograms).

Figure 11. 8 kHz down to 2 kHz. 1The true tone, dashed. 2The folded tone.

5.2 Speeding up inserts zeros, then fills them

Whisper, OpenAI’s speech recognizer, resamples all audio to 16,000 Hz before it computes its spectrogram features (Radford et al. 2022, section 2.2). An 8 kHz phone recording therefore has to be upsampled by a factor L = 2. Upsampling puts L − 1 zeros between neighboring samples, so [1, 2, 3] becomes [1, 0, 2, 0, 3, 0]. The zeros create an image, a mirror copy of each tone, because the spectrum copy centered on the old rate (Section 3) now lies inside the new band.

low-pass at the old Nyquist3 kHz tone5 kHz image04 kHz8 kHzfrequency, sampled at 16 kHz
Figure 12. Zeros inserted. 1A 3 kHz tone. 2Its image at 5 kHz. 3The low-pass removes it.

Nothing folds, so upsampling causes no aliasing; only the image has to be removed.

Upsampling by a whole number L, then filling the zeros.
xup[L n] = x[n]
x is the original signal and xup the signal at the new rate L fs. Each old sample x[n] lands at position L n, and the L − 1 positions between hold 0. In frequency this leaves the spectrum plus its image.
y[n] = L · (h ∗ xup)[n]
y is the upsampled signal, h a low-pass with its cutoff at the old Nyquist fs2, and ∗ is convolution, where each output sample adds up the nearby input samples, each weighted by h (taught in full in LTI Systems and Convolution, section 5). Removing the image fills each zero with a value between its neighbors, and the factor L restores the level, since only one sample in L carried signal. It builds on the copies of Section 3 and is used by Section 5.3.

SciPy’s resample_poly does both steps; with its default filter the 5 kHz image comes out about 57 dB below the tone.

5.3 Resampling by any ratio

Music at 44.1 kHz going into that 16 kHz recognizer is not a whole-number step either way. The way through is to upsample by one whole number, low-pass once, and keep one sample in another.

44,100 Hz to 16,000 Hzup 160low-passdown 4417,056,000 Hzcutoff 8 kHz16,000 Hz48,000 Hz to 16,000 Hzup 1low-passdown 348,000 Hzcutoff 8 kHz16,000 Hz
Figure 13. 1Insert zeros. 2One low-pass at the lower Nyquist. 3Keep one sample in 441 (or 3).
The rate after upsampling by up and keeping one sample in down.
fs,new = fs · updown
up and down are whole numbers with no common factor, so 16,00044,100, divided top and bottom by 100, becomes 160441. The rate in between is fs · up, 7,056,000 Hz here. One low-pass at the lower of the two Nyquist limits, 8 kHz, removes the images of the zeros and anything that would fold when one sample in down is kept. It builds on Section 5.1 and Section 5.2 and is used by equalization matching (Power Spectral Density, section 5).

6 In practice

Anti-aliasing sits in two places, an analog low-pass right before the ADC and a digital low-pass before every downsampling step in software.

StepHow often, on what dataState it keepsCost
Anti-aliasing filtercontinuously, before the sampler; designed once, on the analog inputthe charge on its capacitorsmore parts for a steeper slope
Decimationonce per kept output sample, on the recent input samplesthe filter’s past inputsone low-pass per kept sample
Resampling by a ratioonce per output sample, on the recent input samplesthe filter’s past inputsone low-pass per output sample; the inserted zeros are skipped

Rates, state and cost come from the sources in References.

A rising tone downsampled by 4 with SciPy, once by slicing (bad) and twice with a low-pass first (good, good2), then a sweep taken from 44.1 kHz to 16 kHz.

import numpy as np
from scipy import signal

FS, FACTOR, DUR_S = 8000, 4, 2
t = np.arange(DUR_S * FS) / FS
sweep = signal.chirp(t, f0=0, t1=DUR_S, f1=3800)

bad = sweep[::FACTOR]                     # no filter, folds
good = signal.decimate(sweep, FACTOR)
good2 = signal.resample_poly(sweep, up=1, down=FACTOR)

x44 = signal.chirp(np.arange(DUR_S * 44100) / 44100, f0=0, t1=DUR_S, f1=20000)
x16 = signal.resample_poly(x44, 160, 441)  # one low-pass at 8 kHz
  1. 1Makes a 2-second sweep from 0 to 3.8 kHz at 8 kHz.
  2. 2Keeps every 4th sample with no filter, so the sweep folds (eq card in Section 5.1).
  3. 3Low-passes first, then keeps every 4th sample, so nothing folds.
  4. 4Goes from 44.1 kHz to 16 kHz with up 160, one low-pass and down 441, so 88,200 samples become 32,000 (eq card in Section 5.3).
Knob, where it entersRaise itLower itDefault
Sample rate fs, which sets the Nyquist frequency fs/2The Nyquist frequency moves up, so more of the band is safeMore tones fold; a tone at exactly fs/2 can read as all zeros16 kHz for speech (torchaudio kaldi.mfcc)
Cutoff fc in the Butterworth cardKeeps more of the top band, lets more fold through the slopeSafer against folding, dulls the top of the band; a cutoff far below the band removes the signal tooNo default; Wn is required and sits at the −3 dB point (scipy.signal.butter)
Order p, the exponent 2p in the Butterworth cardSteeper slope, 20 dB per decade per pole, more cost and ringingGentler slope; p = 1 lets much of the fold throughNo default; N is required (scipy.signal.butter)
Downsampling factor Q in the decimation cardFewer samples to store and process; the new Nyquist limit drops Q times, so the digital low-pass must cut lowerMore samples kept; Q = 1 keeps every sample and changes nothingNo default; q is required. decimate low-passes with an order 8 IIR filter run forward and back (zero_phase=True), and SciPy advises several calls for factors above 13 (scipy.signal.decimate)

SciPy documents scipy.signal.decimate; resample_poly does the same with a polyphase filter, one that computes only the samples it keeps.

6.1 Gotchas

7 When sampling goes wrong

What you seeLikely causeWhat to do
Tones at frequencies you never hadAliasingPut a low-pass in front of the ADC, below Nyquist
A tone that falls while the real one risesIt crossed Nyquist and foldedSame fix, or sample faster
Downsampled data full of strange tonesSlicing with x[::Q] and no filterUse SciPy’s decimate or its polyphase resampler
A tone mirrored above the old Nyquist after upsamplingZeros were inserted and the interpolation low-pass was skippedLow-pass at the old Nyquist with gain L, or use resample_poly

What is a spectrum, exactly? That is Fourier Transform and the FFT.

8 References