Written and maintained by CASRAI Editorial Board
Last updated
The short-time Fourier transform (STFT) shows how the frequency content of a signal whose spectrum changes over time evolves, by computing a Fourier transform on short, overlapping, windowed segments. This page is for researchers who need to choose the window and hop settings, read the resulting spectrogram, and report the settings so the analysis can be reproduced.
Definition: what the short-time Fourier transform does
An ordinary Fourier transform describes the whole signal at once and says nothing about when a frequency occurred. The STFT slides a window along the signal, takes the discrete Fourier transform of each windowed segment, and stacks the results into a time-frequency matrix. MathWorks describes it as a tool to analyze how the frequency content of a nonstationary signal changes over time, computed by taking the DFT of each segment of windowed data. The squared magnitude of that matrix is the spectrogram. For the wider field, see what is signal processing.
Short-time Fourier transform formula
In the notation of J.O. Smith’s Spectral Audio Signal Processing, frame m of the STFT is
Xm(ω) = Σn x(n) w(n − mR) e−jωn
- x(n): the input signal, sample index n.
- w(n): the window function, of length M samples.
- R: the hop size in samples, the step by which the window advances between frames.
- m: the frame index; w(n − mR) is the window shifted to start at sample mR.
- ω: frequency in radians per sample; e−jωn is the Fourier kernel.
Equivalently, Xm(ω) is the discrete-time Fourier transform of the signal multiplied by the shifted window. In practice the transform is evaluated on an FFT grid of mfft points. SciPy’s ShortTimeFFT documents the resulting grid: time step Δt = hop × T (T = 1/fs), frequency step Δf = 1/(mfft × T), and the default mfft equals the window length.
Worked example with the arithmetic shown
Take a signal sampled at fs = 1000 Hz and lasting 10 s, so 10,000 samples. Use a Hann window of M = 256 samples, hop R = 128 samples (50% overlap) and mfft = 256.
- Window duration: 256 / 1000 = 0.256 s.
- Hop duration Δt: 128 / 1000 = 0.128 s.
- Frequency spacing Δf: 1000 / 256 = 3.90625 Hz.
- Frames that fit fully inside the signal: ⌊(10,000 − 256) / 128⌋ + 1 = ⌊76.125⌋ + 1 = 77.
Now halve the window to M = 128 with the same fs. The window lasts 0.128 s, so each frame localises events twice as sharply, but Δf = 1000 / 128 = 7.8125 Hz, so frequency bins are twice as wide. This is the time-frequency trade-off. It is an interpretation drawn from the formulas above, not a quotation: one window length sets both the time span each frame covers and the spacing of independent frequency information.
Zero-padding changes the grid but not this trade-off. With M = 256 and mfft = 1024, Δf = 1000 / 1024 = 0.9765625 Hz, so the spectrum is sampled more densely, but the window still spans 0.256 s. SciPy’s documentation presents zero-padding (for example, mfft = 200 against a 50-sample window) as a finer grid, not as extra resolution.
Running the SciPy snippet below on a 100 Hz sine with these settings gave a complex matrix of 129 frequency rows (256/2 + 1, one-sided) by 80 time slices, Δf = 3.90625 and Δt = 0.128. The 80 slices exceed the 77 interior frames because SciPy also returns border slices where the window extends beyond the signal.
Interpreting a spectrogram
| What you see | Reading | Basis |
|---|---|---|
| Horizontal ridge at a constant frequency | A stationary component present over those frames | Each column is the windowed spectrum of one frame |
| Ridge whose frequency moves across columns | Frequency content changing over time, the case the STFT is meant for | MathWorks: frequency content of a nonstationary signal over time |
| Wider ridge after shortening the window | Coarser frequency resolution, finer time localisation | Δf = fs / mfft, window span = M / fs (worked example) |
| Smearing or artefacts in the first and last columns | Border slices where the window extends past the signal | SciPy ShortTimeFFT documentation |
If you plot the magnitude with a colour scale, choose it deliberately; the guide to MATLAB colormap colors covers perceptual choices, and Praat is one tool that draws spectrograms of speech.
Window, overlap and invertibility: assumptions and common mistakes
The STFT assumes the signal is approximately stationary within one window. Windowing suppresses side lobes at the cost of widening the main lobe (SciPy signal tutorial). For reconstruction, Smith defines the constant-overlap-add condition COLA(R): Σm w(n − mR) = 1 for all n. A rectangular window is COLA(M); Bartlett and generalised Hamming windows are COLA(M/2). A Blackman window at 50% overlap does not satisfy the condition exactly, though Smith notes the result looks fine and edge data has a slightly muted impact.
| Mistake | Correction |
|---|---|
| Treating zero-padding as better resolution | It only interpolates the frequency grid; resolution follows the window |
| Choosing lengths so the last segment is partial | MATLAB requires (length(x) − noverlap) / (length(window) − noverlap) to be an integer for whole segments; its defaults are a Hann window of 128 and overlap of 96, so a 1024-sample signal gives (1024 − 96) / 32 = 29 segments |
| Ignoring border slices | Trim or flag the padded edge frames before quantitative claims |
| Assuming the legacy functions are current | The SciPy signals guide notes that scipy.signal.stft, spectrogram, istft and check_COLA are legacy; new code should use ShortTimeFFT |
How to report an STFT analysis
There is no test statistic to report, so report the parameters that determine the result. A template: “Spectrograms were computed with a short-time Fourier transform (Hann window, 256 samples [0.256 s], hop 128 samples [50% overlap], 256-point FFT, sampling rate 1000 Hz) using SciPy ShortTimeFFT, version X.” State the window type and length, hop or overlap, FFT length, sampling rate, scaling and software version.
Short-time Fourier transform in MATLAB and Python
MATLAB
[s, f, t] = stft(x, fs); % defaults: Hann window length 128, overlap 96
Python (SciPy)
import numpy as np
from scipy.signal import ShortTimeFFT
from scipy.signal.windows import hann
fs = 1000
t = np.arange(10000) / fs
x = np.sin(2 * np.pi * 100 * t)
SFT = ShortTimeFFT(hann(256, sym=False), hop=128, fs=fs, mfft=256, scale_to='magnitude')
Sx = SFT.stft(x) # complex matrix, shape (129, 80)
print(SFT.delta_f, SFT.delta_t) # 3.90625 0.128
x_back = SFT.istft(Sx, k1=len(x)) # inverse
print(np.allclose(x, x_back)) # True
Excel has no built-in STFT; use MATLAB or Python for this method.
Frequently asked questions
What is the short-time Fourier transform in simple terms?
It is a Fourier transform applied repeatedly to short, windowed, overlapping pieces of a signal, giving frequency content as a function of time.
What is the STFT formula?
Xm(ω) = Σ x(n) w(n − mR) e−jωn, where w is the window, R the hop size and m the frame index (Smith, SASP).
How do I choose the window length?
A shorter window localises events better in time but gives wider frequency bins (Δf = fs / mfft); a longer window does the reverse. Choose from the time scale of the events and the frequency separation you need to resolve.
Is a spectrogram the same as the STFT?
The spectrogram is the squared magnitude of the STFT; the STFT itself is complex and retains phase.
Can the STFT be inverted?
Yes, with a window and hop that satisfy the overlap-add condition; the SciPy example above inverts it with ShortTimeFFT.istft.
Sources
- J.O. Smith, Spectral Audio Signal Processing, Mathematical Definition of the STFT: ccrma.stanford.edu/~jos/sasp/Mathematical_Definition_STFT.html (read 2026-11-07).
- SciPy documentation,
scipy.signal.ShortTimeFFT: docs.scipy.org and the signal-processing tutorial (read 2026-11-07). - MathWorks,
stftreference page: mathworks.com/help/signal/ref/stft.html (read 2026-11-07).







