PluginUI

Spectrogram

spectrogramVisualizers

Live mode keeps a scrolling history of FFT energy. Static mode analyzes supplied PCM. Prepared mode renders a bounded host-produced magnitude grid with explicit frame times and frequency bins, so a module face needs no audio buffer or FFT work. PitchRead uses prepared mode beneath its MIDI roll with the same seconds and logarithmic pitch bounds. The mapping helpers are pure and downloadable; color indicates source energy, not model confidence.

Bone / Studio
Loading interactive preview…
Planar / Flat
Loading interactive preview…
Midnight / Minimal
Loading interactive preview…
Ember / Vintage
Loading interactive preview…
Ink / Studio
Loading interactive preview…
Mint / Flat
Loading interactive preview…

Install

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

$ pnpm dlx shadcn@latest add @pluginui/core-spectrogram

Pulls in

use-audio-sourcecontrol-themeprepared-spectrogramstft

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

components.json
{
  "registries": {
    "@pluginui": "https://pluginui.com/r/{name}.json"
  }
}

Usage

Connect the documented props to your application or host.

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

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

// The host prepares this grid outside the face and realtime callback.
<Spectrogram mode="prepared" prepared={host.spectrogram}
  durationSeconds={clipDuration} minFrequency={lowHz} maxFrequency={highHz}
  showAxis={false} height={150} />

Props

PropTypeDefaultDescription
mode / prepared"realtime" | "static" | "prepared" / PreparedSpectrogramData"realtime"Prepared data contains frame-major Uint8 magnitudes, frames, bins, explicit frameTimesSeconds, binFrequencyHz, durationSeconds and the quantization dB range. It never invokes PCM analysis.
durationSecondsnumber—Prepared mode's horizontal timeline domain. Defaults to the source duration; a longer domain leaves the remainder empty instead of stretching the image.
analyserAnalyserNode—A live analyser to read frequency data from.
getData(target: Float32Array) => boolean | void—Supply 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.
sampleRatenumber—Needed 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.