media

AudioVisualizer

Renders animated bars (frequency-domain) or a waveform (time-domain) for an audio file. Does **not** play audio — pair with a parallel `AudioClip` for playback. Uses `useAudioData` from `@remotion/media-utils` (cached per `src`).

$ npx ondajs add audio-visualizer

AudioVisualizer

Animated visualization of an audio file — four variants, all driven by the same dB-normalized FFT pipeline and rendered in SVG with Onda's accent treatment (single color prop + soft glow).

Does not play audio. Pair with a parallel AudioClip pointing at the same src for audible playback.

Variants

VariantWhat it shows
'bars'Frequency-domain bars (spectrum analyzer). Each bar is one FFT bin.
'wave'Parametric sine wave whose amplitude is driven by the audio's RMS energy. Always smooth and flowing — the audio drives one scalar, not the shape.
'hills'Filled smooth-curve "mountains" mirrored around a centerline. Mid-frequency band drives the bump heights; per-bump variation is seed-deterministic.
'radial'Bars arranged in a circle, each driven by a high-frequency FFT bin. Mirrored so the circle reads symmetrically.

All four use the same dB-normalization (minDb..maxDb) so the visual response is perceptually correct — raw FFT magnitudes sit below 0.1 and look dead without this.

Why it ships as one component (not four)

All four share the same useAudioData cache, the same processFftValue pipeline, the same Onda accent treatment, and most of the same props. Switching variants is a single prop change. The per-variant props (bar*, wave*, hills*, radial*) are namespaced so they don't collide.

Props

Universal

NameTypeDefaultNotes
srcstringsample URLURL or path to the audio file.
variant'bars' | 'wave' | 'hills' | 'radial''bars'Visualization style.
widthnumber640Width in px (ignored by 'radial' — use radialDiameter).
heightnumber160Height in px (ignored by 'radial').
colorstring | string[]["#D96B82", "#E89AAB"]Single color OR an array. Bars / radial render a multi-stop vertical gradient. Wave cycles colors per stacked line. Hills cycle per stacked copy. Default is Onda's two-tone accent ramp (rose → soft rose).
glowbooleantrueSoft accent glow via drop-shadow.
minDbnumber-100Lower dB bound for FFT normalization.
maxDbnumber-30Upper dB bound.
numberOfSamplesinteger (power of two)256FFT bin count.
placementPlacement?Canvas placement.

'bars' variant

NameTypeDefault
barWidthnumber4
barGapnumber4
barRadiusnumber2
barAlign'top' | 'middle' | 'bottom''middle'

'wave' variant

NameTypeDefault
waveSectionsinteger12
waveLinesinteger2
waveLineGapnumber16
waveStrokeWidthnumber2
waveScrollSpeednumber-160

'hills' variant

NameTypeDefault
hillsBumpsinteger8
hillsCopiesinteger2
hillsAlign'top' | 'middle' | 'bottom''middle'
hillsFillOpacitynumber 0..10.4
hillsStrokeWidthnumber0
hillsSeednumber | string42

'radial' variant

NameTypeDefault
radialDiameternumber360
radialInnerRadiusnumber80
radialBarWidthnumber4
radialBarGapnumber4
radialBarRadiusnumber2
radialBarOrigin'outer' | 'inner' | 'middle''inner'

Usage

TSX
import { Composition } from 'remotion';
import { AudioVisualizer, audioVisualizerSchema } from './components/onda/audio-visualizer/AudioVisualizer';

<Composition
  id="Visualizer"
  component={AudioVisualizer}
  durationInFrames={300}
  fps={30}
  width={1920}
  height={1080}
  schema={audioVisualizerSchema}
  defaultProps={audioVisualizerSchema.parse({
    src: '/music.mp3',
    variant: 'wave',
  })}
/>

Paired with playback (the typical pattern):

TSX
<AbsoluteFill>
  <AudioClip src="/music.mp3" />
  <AudioVisualizer src="/music.mp3" variant="hills" placement="bottom" />
</AbsoluteFill>

Implementation notes

  • dB normalization is the secret. processFftValue(v, minDb, maxDb) converts raw FFT magnitudes → decibels → 0..1. Without it, almost all bars sit between 0 and 0.1 of canvas height and look dead. Adjust minDb / maxDb to tune responsiveness for different content.
  • useAudioData caches by src. Multiple visualizers on the same audio file share one decode.
  • 'wave' doesn't render raw audio data. It's a parametric sine wave; the audio only drives amplitude via RMS. Rendering raw amplitude data as a continuous shape pinches to zero at silent samples and looks broken.
  • 'hills' per-bump variation is deterministic. Driven by hillsSeed via Remotion's random().
  • 'radial' mirrors the FFT data. Takes the upper half of the spectrum, mirrors it, so the circle reads symmetrically.

Color examples

TSX
// Default — two-tone Onda accent
<AudioVisualizer src="..." variant="wave" />

// Single color, fully tinted
<AudioVisualizer src="..." variant="bars" color="#D96B82" />

// Multi-color gradient on bars (top-to-bottom rainbow)
<AudioVisualizer src="..." variant="bars" color={['#D96B82', '#E89AAB', '#F2F2F4']} />

// Multi-color wave — each line gets its own hue
<AudioVisualizer
  src="..."
  variant="wave"
  waveLines={3}
  color={['#D96B82', '#E89AAB', '#8E8E98']}
/>

// Multi-color hills — each copy gets its own fill
<AudioVisualizer
  src="..."
  variant="hills"
  hillsCopies={3}
  color={['#D96B82', '#E89AAB', '#F2F2F4']}
/>

Inspiration

Visual design ported (technique, not code) from marcusstenbeck/remotion-audio-visualizers. The signal-processing primitives (processFftValue, getRms) are standard WebAudio / W3C patterns reimplemented from scratch for our MIT-licensed catalog.