Skip to content

Spectrograms & the STFT

Cut a recording into windowed frames, take an FFT of each, and read the picture: tones as lines, clicks as stripes, glides as slopes.

Before thisThe DFT (13.2), Fast convolution (14.3)

1 more before it

Properties of the Fourier transform (8.2)

Before this13.2 · 14.3 · 1 more
Chapter 15 · Lesson 5 of 6

First, the picture

Watch each short spectrum stand on end and drop into the picture as one column: a spectrum with a clock.

A row of short spectra

f_s = 8 kHz: 1000 Hz for 0.25 s, then 2000 Hz. Hann window N = 256 (32 ms), hop 128 (16 ms).

frame m
–
centre t_m
–
0.00 / 16.00 s
Describe this picture

A recording at fs=8f_s = 8 kHz plays 1000 Hz for 0.25 s, then 2000 Hz; the window is a Hann window of N=256N = 256 (32 ms), with a hop of 128 (16 ms). Three panels stack. The first holds the signal, xx against time from 0 to 512 ms, with a dashed Hann outline over 32 ms, labelled “frame m”, marking the current frame. The second holds that frame’s spectrum, in dB re 1 from −60 to 0, against frequency from 0 to 4000 Hz. The third is the spectrogram, frequency against time, with one column per frame, 16 ms wide and centred on tmt_m. Level is ink: 0 dB is full ink and −60 dB bare paper, with a scale bar reading “0 dB … −60 dB” and “re 1”, so the picture does not depend on hue; in the dark theme the ink is light. The readouts are the frame mm and its centre tmt_m in whole milliseconds.

The clip runs for 16 s and plays once. Each frame’s window slides onto the signal, its spectrum appears, and then the spectrum stands on end and drops into the spectrogram as one column. A caption is shown only while the picture shows what it says. At 1 s (frame 0, 0 ms): “Frame 0 is centred at 0 ms, so half its window hangs before the start, where x is 0. Its spectrum shows 1000 Hz at −5.9 dB: half a window, about half the size.” At 6.75 s (frame 5, 80 ms): “Frame 5, centred at 80 ms: a full window of the 1000 Hz tone, reading 0 dB. Stood on end, its spectrum is one column of the picture.” At 11.75 s (frame 16, 256 ms): “Frame 16, centred at 256 ms, straddles the change at 250 ms: 2000 Hz at −1.6 dB and 1000 Hz at −15.6 dB, both in one column.” At the end, 16 s (readouts “0 to 32” and “every 16 ms”): “33 frames, one column each: the spectrogram. Time runs across, frequency up, and level is ink: 0 dB full, −60 dB none. It shows what one spectrum of the whole record cannot: when each tone played.” While the window slides on, the caption reads “The window slides on, 16 ms at a time.”; during the faster stretches it is blank. With reduced motion the clip rests on the end frame, with steps at 1, 6.75, 11.75 and 16 s.

After the clip ends, a control named “Frame m” chooses a frame; the hint reads “Tap a column, or use the arrow keys, to see that frame.” Left and Right move it on the focused spectrogram, Home and End go to the first and last, and its value reads, for example, “frame 16, 256 ms”. The window outline and the frame spectrum return for that frame, with the captions above for frames 0, 5 and 16; for any other frame the caption describes it live, such as “Frame 24, centred at 384 ms: its peak is at 2000 Hz, 0.0 dB.” The second tone is named only if it is within 20 dB of the peak.

One spectrum, no clock

The spectrum of a whole recording, from The DFT (13.2), says which frequencies were present and how strong they were. It does not say when. The time of an event is stored in the phases of the spectrum, and there it is hard to read. A tone that played for the first second and a tone that played for the last one look alike in a plot of sizes.

The remedy is to look at the recording through a short window, one piece at a time. I cut the recording into short frames, multiply each by a window such as the Hann window of Window functions compared (15.2), take the FFT of each, and stand the spectra side by side. The result is the short-time Fourier transform, the STFT. Drawn with time across, frequency up and level as the strength of the ink, it is a spectrogram.

A frame has NN samples. The next frame starts NhopN_\text{hop} samples after this one, and NhopN_\text{hop} is the hop. Frame mm is centred at sample mNhopmN_\text{hop}, and the samples of xx outside the record count as 0:

X[m,k]=∑n=0N−1x[mNhop−N/2+n] w[n] e−j2πkn/N.X[m,k]=\sum_{n=0}^{N-1}x[mN_\text{hop}-N/2+n]\,w[n]\,e^{-j2\pi kn/N}.

This is the DFT of one windowed frame, with w[n]w[n] the window. The index mm numbers the frames and kk numbers the bins, as in The DFT (13.2). The centre time of frame mm is

tm=mNhopfs,t_m=\frac{mN_\text{hop}}{f_s},

and the overlap between neighbouring frames is N−NhopN-N_\text{hop} samples. SciPy’s stft(..., boundary='zeros') makes the same frames, and it divides by ∑w\sum w.

I draw the level in dB re amplitude 1, using the amplitude scaling of Reading a spectrum: scaling and units (15.4): divide the size by half the window’s sum, so that a steady tone of amplitude 1 reads 0 dB.

level[m,k]=20log⁡10∣X[m,k]∣12∑nw[n].\text{level}[m,k]=20\log_{10}\frac{\lvert X[m,k]\rvert}{\tfrac12\sum_{n}w[n]}.

A row of short spectra

The picture at the top of this page builds a spectrogram of a recording that plays 1000 Hz for 0.25 s and then 2000 Hz, at fs=8f_s=8 kHz, with a Hann window of N=256N=256 (32 ms) and a hop of 128 (16 ms). Frame 0 shows 1000 Hz at −5.9 dB. Frame 5 holds a full window of the tone and reads 0 dB. Frame 16 straddles the change at 250 ms and holds 2000 Hz at −1.6 dB and 1000 Hz at −15.6 dB. The 33 frames, one column each, are the spectrogram: it shows what one spectrum of the whole record cannot, when each tone played.

Frame 0 is the easiest to check. Half its window lies before the record, where xx is 0, so the frame holds only half of the window’s weight. It sees about half the tone, and half an amplitude is 20log⁡100.5=−6.0220\log_{10}0.5=-6.02 dB, close to the −5.9-5.9 dB the instrument shows. Frame 16 is centred at 256 ms, so its samples are numbers 1920 to 2175. The change happens at sample 2000, so 80 of them belong to the 1000 Hz tone and 176 to the 2000 Hz tone. Both tones are in the column, and the 2000 Hz one is louder because more of the window holds it.

After the clip ends, tap a column to see that frame. Notice frame 16. One column holds both tones, because one window held both. A frame that contains a change has the spectrum of the change, not of either side alone.

Hop and overlap

Frames that do not overlap waste the samples near their ends, where the window is small. Anything that happens at a join between two frames is seen by neither at full weight. The figure shows N=128N=128 Hann windows placed every 128, 64 and 32 samples, with their sum as a thick dashed line.

hop N = 1281hop N/2 = 641hop N/4 = 32120128256384512640sample n
Fig. With hop N, each window falls to 0 where the next begins: anything at the joins is barely seen. With hop N/2 (50 % overlap) the windows add to exactly 1, and with N/4 to exactly 2: every moment is weighed equally. 15.6 uses this to turn a spectrogram back into sound.

The Hann window is w[n]=0.5−0.5cos⁡(2πn/N)w[n]=0.5-0.5\cos(2\pi n/N), and the sums are exact for a reason you can see in the cosines. At hop N/2N/2 the second window is the first one shifted by half a period, so its cosine is the first one with the sign flipped. The two cosines cancel and the two constants add to 1. At hop N/4N/4 four windows overlap, and their four cosines are spaced a quarter period apart, so they add to 0 and the four halves make 2.

So a hop of N/2N/2 or N/4N/4 is a good choice for a Hann window, and no moment falls between frames. The cost is the number of FFTs: a hop of N/4N/4 makes twice as many frames as N/2N/2, so it takes twice as many FFTs for the same record.

Short window or long window

Watch two clicks and two tones as the window grows: no length shows both pairs sharp.

Short window or long window

Two steady tones, 1000 and 1100 Hz, and two clicks 10 ms apart, at 300 and 310 ms. f_s = 8 kHz, Hann window, hop N/4.

N = 64 (8 ms): the two clicks stand apart, at 300 and 310 ms. But bins are 125 Hz apart, and the two tones merge into one band.

window N
64 samples, 8 ms
Δf = f_s/N
125.0 Hz
0.00 / 13.00 s
Describe this picture

A signal that needs both kinds of detail at once: two steady tones, 1000 and 1100 Hz, and two clicks 10 ms apart, at 300 and 310 ms, with fs=8f_s = 8 kHz, a Hann window and a hop of N/4N/4. One panel, the spectrogram, with time from 200 to 400 ms and frequency from 500 to 1600 Hz; its level runs from 0 dB to −60 dB relative to the loudest cell, and the scale bar says so with “re loudest”. Left-pointing triangles at the right edge, labelled “tones”, sit at 1000 and 1100 Hz, and downward triangles above the plot, labelled “clicks”, sit at 300 and 310 ms. The readouts are the window NN, for example “256 samples, 32 ms”, and Δf=fs/N\Delta f = f_s/N with one decimal, for example “31.3 Hz”.

The clip runs for 13 s and cross-fades between three windows, with the caption blank during each cross-fade. From 0 to 3 s, N=64N=64 (“64 samples, 8 ms”, “125.0 Hz”): “N = 64 (8 ms): the two clicks stand apart, at 300 and 310 ms. But bins are 125 Hz apart, and the two tones merge into one band.” From 4 to 7 s, N=256N=256 (“256 samples, 32 ms”, “31.3 Hz”): “N = 256 (32 ms): bins 31.3 Hz apart, and the tones split into two lines. The clicks, 10 ms apart inside a 32 ms window, blur into one stripe.” From 8 to 13 s, N=1024N=1024 (“1024 samples, 128 ms”, “7.8 Hz”): “N = 1024 (128 ms): sharp tones, bins 7.8 Hz apart, and the clicks smeared over 128 ms. One length sets both: time detail N/f_s, frequency detail f_s/N, and their product is 1.” With reduced motion the clip rests on the end frame, with steps at 1.5, 5.5 and 13 s.

After the clip ends, an overlay slider named “Window length N” appears, its value reading, for example, “256 samples, 32 ms”, with the hint “Drag across the picture, or use the arrow keys, to set N (32 to 2048).” The arrow keys double or halve NN, and Home and End jump to 32 and 2048. At 64, 256 and 1024 the captions above are shown; at the other lengths the caption is a short verdict, for example “N = 128 (16 ms): tones merged; clicks merged.” The hop stays N/4N/4.

One window length sets two things at once. A frame of NN samples spans N/fsN/f_s seconds, which I call the time detail, Δt\Delta t: events closer together than that fall in one frame. Its bins are fs/Nf_s/N apart, the frequency detail, Δf\Delta f, as in Zero-padding and resolution (15.3): tones closer than that merge. The product is

Δt Δf=Nfs⋅fsN=1.\Delta t\,\Delta f=\frac{N}{f_s}\cdot\frac{f_s}{N}=1.

Making one finer makes the other coarser. This is the pulse-width rule of Properties of the Fourier transform (8.2), applied to frames, and I use it as a rule of thumb, not as an exact bound.

The picture’s signal needs both kinds of detail at once: two steady tones, 1000 and 1100 Hz, and two clicks 10 ms apart, at 300 and 310 ms, sampled at 8 kHz. With N=64N=64 (8 ms) the clicks stand apart, but the tones merge into one band. With N=256N=256 (32 ms) the tones split into two lines, but the clicks blur into one stripe. With N=1024N=1024 (128 ms) the tones are sharp and the clicks are smeared over 128 ms.

The numbers follow from the two formulas. At N=64N=64 a frame lasts 64/8000=864/8000=8 ms, shorter than the 10 ms gap, and the bins are 8000/64=1258000/64=125 Hz apart. The tones are 100 Hz apart, which is 0.8 of a bin, so they cannot be told apart. At N=256N=256 a frame lasts 32 ms and the bins are 31.25 Hz apart. The tones are 3.2 bins apart and split, but one window now holds both clicks.

After the clip ends, drag across the picture to set NN yourself, from 32 to 2048. I count two clicks as apart only when the picture dips by at least 3 dB between them. At 32 the tones merge and the clicks stand apart. At 128 both pairs merge, and at 512 and 2048 the tones split while the clicks merge. The hop stays N/4N/4.

Try this: step from N=64N=64 to 128 and then to 256. The clicks merge first, at 128, and the tones split only at 256, so at 128 neither pair is resolved. Between 32 and 2048, no length gives both a sharp pair of clicks and a sharp pair of tones.

Reading a chirp

Watch the strongest cell climb as the tone glides, then the probe land on the line.

Reading a chirp

A tone that glides from 200 to 3800 Hz in 2 s (f_s = 8 kHz). Hann window N = 256 (32 ms), hop 64 (8 ms).

Each frame adds one column, and the strongest cell climbs as the tone glides.

time
not yet
brightest at
not yet
0.00 / 12.00 s
Describe this picture

A tone that glides from 200 to 3800 Hz in 2 s (fs=8f_s = 8 kHz), with a Hann window of N=256N = 256 (32 ms) and a hop of 64 (8 ms). One panel, the spectrogram, with time from 0 to 2 s and frequency from 0 to 4000 Hz; the level runs from 0 dB to −60 dB re amplitude 1, with a scale bar marked “re 1”. A dashed line labelled “200 + 1800t Hz” marks the formula, and a ring with a thin crosshair marks the probe. The readouts are the time, with three decimals, and the frequency of the brightest cell, with two; both read “not yet” before the probe lands. A button, “Hear the chirp”, plays the 2 s chirp.

The clip runs for 12 s. From 0 to 6 s the spectrogram builds one column per frame, 2 s of signal over 6 s: “Each frame adds one column, and the strongest cell climbs as the tone glides.” At 7 s: “A sloping line: the frequency rises steadily, by 1800 Hz every second.” From 7.5 to 10 s the dashed line draws and the probe ring lands, at 1.000 s and 2000 Hz, with the caption blank. At the end, 12 s, the readouts are “1.000 s” and “2000.00 Hz”, and the caption says “The line is 200 + 1800t. At 1.000 s the brightest cell is at 2000 Hz, on it. In one 32 ms window the tone glides 58 Hz, about two bins, which is why the line has width.” With reduced motion the clip rests on the end frame, with steps at 7 and 12 s.

After the clip ends, a control named “Probe time” moves the probe: tap the picture, or use Left and Right to move by one frame, Page Up and Page Down to move by 0.25 s, and Home and End. The hint reads “Tap the picture, or use the arrow keys, to read the brightest frequency at any moment.” The probe snaps to a frame centre, a multiple of 8 ms, and to that frame’s brightest bin; its value reads, for example, “1.000 s, brightest at 2000.00 Hz”. After a move the caption is live, for example “At 1.496 s the brightest cell is at 2906.25 Hz; 200 + 1800t gives 2892.8 Hz.”

A tone whose frequency changes draws a sloping line. A siren rising past you is a pitch that climbs, a line and not a dot. The chirp in this instrument glides at a steady rate, so its phase is

φ(t)=2π(200t+900t2),\varphi(t)=2\pi\left(200t+900t^2\right),

and its frequency at time tt is the derivative of the phase divided by 2π2\pi:

f(t)=12πdφdt=200+1800t Hz.f(t)=\frac{1}{2\pi}\frac{d\varphi}{dt}=200+1800t\ \text{Hz}.

It starts at 200 Hz and gains 1800 Hz every second, so at t=2t=2 s it reaches 3800 Hz.

The picture draws that line, with the formula dashed over it. At 1.000 s the brightest cell is at 2000 Hz, on the line. After the clip ends, tap the picture to move the probe and read the brightest frequency at any moment.

The brightest cell is within half a bin of the formula, because the bins are 31.25 Hz apart and the probe picks one of them. At 1.496 s the formula gives 200+1800⋅1.496=2892.8200+1800\cdot1.496=2892.8 Hz, and the nearest bin is number 93, at 93⋅31.25=2906.2593\cdot31.25=2906.25 Hz. The thickness of the line is the window’s blur. In one 32 ms window the tone moves 1800⋅0.032=57.61800\cdot0.032=57.6 Hz, which is 57.6/31.25≈1.857.6/31.25\approx1.8 bins, so one frame cannot say exactly which frequency the tone had.

For speech the usual choice is a 25 ms window every 10 ms. At fs=16f_s=16 kHz that is N=400N=400, so Δf=16000/400=40\Delta f=16000/400=40 Hz, and a hop of Nhop=160N_\text{hop}=160 samples, which gives 100 frames per second. A 100 ms window would give 10 Hz bins but blur syllables. A 32 ms window gives bins 31.25 Hz apart at any sampling rate, because Δf=1/(N/fs)\Delta f=1/(N/f_s). Frames are often zero-padded to the next power of two, as in Zero-padding and resolution (15.3). That makes a finer grid of the same detail.

The maths behind it · frames, not bases

The STFT is a tall matrix acting on the signal: one row per (frame, bin), each a windowed probe. With overlap it has more rows than the signal has samples, which makes it a frame (a redundant spanning set) and not a basis. That redundancy is what lets Inverting the STFT (15.6) edit a spectrogram and still invert it.

The maths behind it · time series of spectra

A spectrogram is a time series of spectra. Averaging its columns is the Welch estimate of Averaged periodograms: Bartlett and Welch (25.2), and watching the columns change is how non-stationarity is detected.

Worked example

  1. Frames. A record of 0.5 s at 8 kHz has 4000 samples. With N=256N=256 and Nhop=128N_\text{hop}=128 the frames are centred at 0, 128, 256, and so on up to 4096, which makes 33 frames, one every 16 ms.
  2. A frame at the change. Frame 16 is centred at 256 ms and holds samples 1920 to 2175. The change is at sample 2000, so 80 samples are the 1000 Hz tone and 176 are the 2000 Hz tone. It reads 2000 Hz at −1.6-1.6 dB and 1000 Hz at −15.6-15.6 dB.
  3. The trade. At N=64N=64 the bins are 125 Hz apart and the window is 8 ms. At N=256N=256 they are 31.25 Hz apart and 32 ms. At N=1024N=1024 they are 7.8125 Hz apart and 128 ms. Each product is 1.
  4. A chirp. At 1.000 s the formula gives 200+1800=2000200+1800=2000 Hz and the brightest bin is at 2000.00 Hz. At 1.496 s the formula gives 2892.8 Hz and the bin is at 2906.25 Hz.
  5. Speech. A 25 ms window at 16 kHz has 400 samples and 40 Hz bins. A hop of 10 ms is 160 samples, so there are 100 frames per second.
  6. Hop. A Hann window with a hop of N/2N/2 sums to exactly 1, and with N/4N/4 to exactly 2.

Where you’ll meet this

Spectrogram displays in audio software are STFTs, and many speech features start from one. Inverting the STFT (15.6) turns the picture back into sound, using the window sums of the figure on this page, so that you can edit the spectrogram and listen to the result. Filter banks (23.1) and Wavelets (23.2) come back to the trade of this page. The averaged periodograms of Averaged periodograms: Bartlett and Welch (25.2) average the columns of the spectrogram to get a steadier estimate.

Reference card

QuantityFormulaNotes
STFTX[m,k]=∑n=0N−1x[mNhop−N/2+n] w[n] e−j2πkn/NX[m,k]=\sum_{n=0}^{N-1}x[mN_\text{hop}-N/2+n]\,w[n]\,e^{-j2\pi kn/N}frame mm centred at mNhopmN_\text{hop}
Frame timetm=mNhop/fst_m=mN_\text{hop}/f_s
OverlapN−NhopN-N_\text{hop} samplesHann: 50 % or 75 %
Time detailΔt≈N/fs\Delta t\approx N/f_sthe window’s length
Frequency detailΔf=fs/N\Delta f=f_s/Npadding does not change it
TradeΔt Δf≈1\Delta t\,\Delta f\approx18.2 for frames
Spectrogram20log⁡10∣X[m,k]∣20\log_{10}\lvert X[m,k]\rvert, time across, frequency upname the dB reference
Hann sumshop N/2N/2: 1; hop N/4N/4: 2periodic Hann
Chirpf(t)=200+1800tf(t)=200+1800t Hzphase 2π(200t+900t2)2\pi(200t+900t^2)
SciPystft(x, fs, window, nperseg=N, noverlap=N-N_hop)divides by ∑w\sum w

End of lesson 15.5

Where to go next.

Phasorium
LibraryEvery lesson, in order

Parts

About Phasorium
Look