Charts/Matrix charts
Spectrogram
<Spectrogram /> · 10 examples
A signal's frequencies over time: time across, frequency up, power as colour.
Overview
A waveform tells you how loud something was and when. It tells you almost nothing about what it was. A flute and a trombone playing the same note at the same volume draw similar-looking squiggles, and a human can tell them apart in a tenth of a second. The information is in the frequencies, and a spectrogram is the picture of those.
Spectrograms have been around since the 1940s, when Bell Labs built a machine that turned speech into a visible pattern, and a book titled Visible Speech was published on the premise that people could be trained to read it. (Few were. It turns out that listening is quicker.) Today the same picture is the front end of speech recognition, bird-song identification, vibration analysis in machines, and the sonar of almost anything.
The reading is straightforward once you have seen a few. A steady note is a horizontal line. A siren is a diagonal. A drum hit is a vertical smear from bottom to top, because a click contains every frequency at once. The difficulty is not the reading but the one setting that changes what you see: the window.
Props
API
<Spectrogram data={samples} x={|s: &Sample| s.time} value={|s: &Sample| s.value} window_size={512} />| Prop | Type | Default |
|---|---|---|
data | Vec<T>, Signal<Vec<T>>, or your own Dataset | required |
x | Fn(&T) -> f64: time, in seconds, evenly spaced | required |
value | Fn(&T) -> f64: the signal | required |
window_size | usize: samples per FFT, rounded up to a power of two | 256 |
overlap | f64: the share of each window that the next one repeats, 0 to 0.95 | 0.5 |
scheme | HeatmapScheme | Magma |
class |
The signal is cut into windows, each window gets a Hann taper and a fast Fourier transform, and each cell is the power at that time and frequency in dB. The sample rate is taken from the median gap between x values; the y axis runs from 0 Hz to half the sample rate.
Run them
10 examples
Press Run to load an example into the live render at the top of the page. Open one to read its code.
How to read it
- 01Time runs left to right in seconds, frequency runs bottom to top in Hz, and the top of the axis is half the sample rate.
- 02The default colour scheme is magma: dark is quiet, bright is loud. Values are in dB, so a step of 10 is ten times the power.
- 03A horizontal line is a steady tone. A stack of evenly spaced lines is a note with its harmonics.
- 04A diagonal is a sweep (rising or falling pitch). A vertical stripe is a click or an onset.
- 05Hover a cell to read its time, frequency and level in dB.
When to use it, and when not to
Reach for it when
- You have a signal sampled at a steady rate and the question is which frequencies, when: audio, vibration, EEG, radio, a heartbeat, a turbine.
- You are looking for events that are hard to see in the waveform: a bearing starting to whine, a bird call under traffic noise.
- You want to see a trend in the frequency content: pitch gliding, a harmonic fading, a hum that comes and goes.
- You need to compare two recordings by structure rather than by loudness.
Skip it when
- The signal is not evenly sampled. The chart takes the median step and assumes it holds. Resample first or the frequency axis is fiction.
- You only need the overall spectrum. A single spectrum, not a function of time, is clearer as a line of power against frequency.
- You care about phase. A spectrogram keeps the magnitude and throws the phase away.
- The signal is very short (fewer samples than the window). There are no full windows, so nothing is drawn.
The math behind it
The short-time Fourier transform
The signal is , sampled at a steady rate. The chart reads the rate from your x column: it takes the median of the positive gaps between consecutive times, calls that the sample step , and sets . Pass time in seconds and the frequency axis is in Hz. Non-finite samples count as zero.
The signal is cut into windows of samples (window_size, default 256, rounded up to a power of two, at least 8). Each window is multiplied by a Hann taper, which fades its edges to zero so that the cut does not leak energy across the spectrum, and then transformed with a fast Fourier transform.
Power in decibels
Each window gives the bins , the lower half of the spectrum (the upper half is a mirror image for a real signal, and the top bin is the Nyquist limit ). Bin is centered on and spans one bin width .
The cell's colour is the power in decibels. A tiny constant keeps the logarithm finite where the power is zero.
The window trade-off and the hop
A window of samples lasts seconds and separates frequencies that are about Hz apart, so one number sets both resolutions: a long window sees close pitches clearly and smears events in time, and a short window pins the moment and blurs the pitch. You cannot have both, which is the Heisenberg-style uncertainty of signal analysis.
Windows advance by a hop of samples (overlap is clamped to 0 to 0.95, default 0.5, and the hop is at least one sample). Only full windows are used, so a tail shorter than samples is dropped. Each cell is drawn as wide as one hop, starting at the window's first sample. Overlap gives smoother columns without changing the trade-off.
Real life usage
The hum at 2.4 kilohertz
Amira was a reliability engineer at a small company that made industrial fans, and she had a customer who was convinced that one of the fans "sounded different on Thursdays". The customer had said this on three calls. Amira had the manner of a person who has decided to believe a statement only when it comes with data.
The fan had a vibration sensor sampling at 10 kHz, and it had been logging for weeks. She pulled a 30-second capture from a Wednesday and one from a Thursday and plotted both waveforms. They looked identical: a fuzzy band of noise of the same height. The customer was either delusional or hearing something the waveform could not show.
What the waveform could not show was frequency. She needed a picture in which a fault that lived at one specific pitch would show as one specific line, and in which a Thursday-only behaviour would show as a line that started partway through.
She loaded the Thursday capture into a <Spectrogram> from zopra-charts with a window of 1024 samples, so that each bin was about 10 Hz wide, and left everything else alone. Time and value, two accessors, and a magma map she did not need to choose.
It was there at once: a thin bright line at 2.4 kHz that began about eight seconds in and stayed. Wednesday's capture had no such line. A bright line that starts in the middle of a recording is a mechanical event, and the shaft that drove the fan had a single bearing at that rotation rate.
They replaced the bearing the following week. The line was gone, and the average vibration energy in the 2 to 3 kHz band dropped by about 14 dB. The customer said it sounded the same, which Amira took as the best possible review.
She keeps a screenshot of the line on her desktop. She calls it the Thursday, which is not an exact translation of what it is but has stuck.
Pitfalls
- Window size is a choice, not a default to ignore. A 256-sample window at 8 kHz resolves about 31 Hz and lasts 32 ms. If your tones are closer than that, they will fuse into one, if your events are shorter, they will smear. Try both before you conclude anything.
- Wrong time units. The frequency axis is only in Hz if your
xis in seconds. Pass milliseconds and every frequency is 1000 times too small. - Aliasing. Anything above half the sample rate folds back into the picture as a lower frequency. The spectrogram cannot tell you it did. Filter before sampling.
- Reading dB as linear. A cell that looks twice as bright is not twice the energy. Ten decibels is a factor of ten in power, so small colour changes can be large physical ones.
From around the web