Spectrogram

spectrogramVisualizers

Where the spectrum shows one instant of the FFT, this keeps the history: each frame paints a single column at the right edge and shifts the whole image one pixel left, so the display reads right-to-left from now back to a few seconds ago. Frequency runs up the vertical axis on a log scale, magnitude is colour, and six ramps are built in. The scroll is a self-blit rather than a repaint of a stored ring buffer, which is what makes a full-width spectrogram affordable at animation rate. The bin-to-pixel mapping is an exported pure function, so the data path can be asserted on in a test or reused by a WebGL renderer.

Synthetic spectrum for display only — this demo produces no audio.

Install

Copies the source into your project. Anything it depends on comes with it.

$ npx shadcn add @syntho-ui/spectrogram

Pulls in

use-audio-sourcecontrol-theme

First time? Add the registry to your components.json once:

components.json
{
  "registries": {
    "@syntho-ui": "https://engine.syntho.app/r/{name}.json"
  }
}

Usage

Controlled or uncontrolled, whichever suits the surrounding state.

example.tsx
import { Spectrogram } from "@/components/ui/spectrogram"

<Spectrogram analyser={analyserNode} height={220} colorScale="viridis" />

Props

PropTypeDefaultDescription
analyserAnalyserNodeA live analyser to read frequency data from.
getData(target: Float32Array) => boolean | voidSupply dB magnitudes yourself instead of using an analyser.
colorScale"viridis" | "plasma" | "inferno" | "magma" | "cividis" | "turbo" | "grayscale" | "heat" | (value: number) => [r, g, b]"viridis"Built-in colour ramp, or your own magnitude → RGB function.
scale"log" | "linear""log"Frequency axis mapping. Linear spends most of the height above 10 kHz; log is the musically useful one.
minFrequency / maxFrequencynumber20 / NyquistThe frequency range shown, bottom to top.
minDecibels / maxDecibelsnumber-90 / -10The magnitude range mapped onto the colour ramp.
gainnumber1Magnitude multiplier applied after normalisation, before clamping.
scrollSpeednumber1Pixels the image scrolls per frame. Higher is faster and coarser.
sampleRatenumberNeeded for the frequency axis when driving from getData.
showAxisbooleantrueDraw the decade frequency labels down the left edge.
heightnumber200Display height in pixels; width defaults to 100%.
pausedbooleanfalseStop the animation loop and hold the last frame.

Interaction

  • Drag with pointer capture, so the gesture keeps tracking past the edge of the control.
  • Shift while dragging for a fine pass; release it mid-gesture and coarse tracking resumes.
  • Double-click resets to defaultValue.
  • Arrow keys step normally, Shift+arrow steps finely, PageUp/PageDown jump by ten, Home/End go to the extremes.
  • Proper slider and switch roles with live aria-valuetext, so a screen reader announces the formatted value rather than the raw number.
  • The scroll wheel adjusts the value while the pointer is over the control; stepped ranges move at least one declared step per notch by default.