Waveform

Real-time audio visualization with a smooth scrolling bar “wave”. Uses Web Audio (AudioContext + AnalyserNode) and draws to a <canvas>.

Preview

"use client";

import { Waveform } from "@/registry/waveform";

export function WaveformDemo() {
  return (
    <div className="flex w-full max-w-md flex-col gap-3">
      <Waveform className="h-14 rounded-lg" />
    </div>
  );
}

Installation

pnpm dlx @arrowui/cli@latest add waveform

Usage

import { Waveform } from "@/registry/waveform";
<Waveform />

By default, Waveform will request microphone access while it is mounted and active is true.

Gate active behind a user gesture (button click). This prevents surprise permission prompts on page load.

"use client";

import * as React from "react";
import { Waveform } from "@/registry/waveform";

export function MicWaveform() {
const [active, setActive] = React.useState(false);
return (
  <div className="flex flex-col gap-3">
    <button type="button" onClick={() => setActive((v) => !v)}>
      {active ? "Stop" : "Start mic"}
    </button>
    <Waveform active={active} />
  </div>
);
}

External audio streams

If you already have a MediaStream (WebRTC call audio, a recorder, etc.), pass it via stream:

<Waveform stream={myMediaStream} active />
  • stream={undefined}: Waveform will create its own microphone stream (default).
  • stream={MediaStream}: Waveform uses your stream and will not call getUserMedia.
  • stream={null}: disables audio (renders nothing / clears the canvas).

Examples

Strength

Use the strength prop to tune how reactive the bars feel.

Soft (strength=0.6)
Default (strength=1)
Hot (strength=1.6)
"use client";

import { Waveform } from "@/registry/waveform";

export function WaveformStrengthDemo() {
  return (
    <div className="flex w-full max-w-md flex-col gap-4">
      <div className="flex flex-col gap-1">
        <div className="text-xs font-medium text-muted-foreground">
          Soft (strength=0.6)
        </div>
        <Waveform className="h-10 rounded-md" strength={0.6} />
      </div>
      <div className="flex flex-col gap-1">
        <div className="text-xs font-medium text-muted-foreground">
          Default (strength=1)
        </div>
        <Waveform className="h-10 rounded-md" />
      </div>
      <div className="flex flex-col gap-1">
        <div className="text-xs font-medium text-muted-foreground">
          Hot (strength=1.6)
        </div>
        <Waveform className="h-10 rounded-md" strength={1.6} />
      </div>
    </div>
  );
}

Density

Tune bar density with barWidthPx, gapPx, and bars (cap).

Dense (1px bars, 1px gap)
Default (2px bars, 2px gap)
Chunky (3px bars, 3px gap)
"use client";

import { Waveform } from "@/registry/waveform";

export function WaveformDensityDemo() {
  return (
    <div className="flex w-full max-w-md flex-col gap-4">
      <div className="flex flex-col gap-1">
        <div className="text-xs font-medium text-muted-foreground">
          Dense (1px bars, 1px gap)
        </div>
        <Waveform
          className="h-10 rounded-md"
          barWidthPx={1}
          gapPx={1}
          bars={160}
        />
      </div>
      <div className="flex flex-col gap-1">
        <div className="text-xs font-medium text-muted-foreground">
          Default (2px bars, 2px gap)
        </div>
        <Waveform className="h-10 rounded-md" />
      </div>
      <div className="flex flex-col gap-1">
        <div className="text-xs font-medium text-muted-foreground">
          Chunky (3px bars, 3px gap)
        </div>
        <Waveform
          className="h-10 rounded-md"
          barWidthPx={3}
          gapPx={3}
          bars={72}
        />
      </div>
    </div>
  );
}

Track + color

Pass backgroundColor to render a solid “track” behind the bars.

Dark track + light bars
Subtle track + charcoal bars
"use client";

import { Waveform } from "@/registry/waveform";

export function WaveformTrackDemo() {
  return (
    <div className="flex w-full max-w-md flex-col gap-4">
      <div className="flex flex-col gap-1">
        <div className="text-xs font-medium text-muted-foreground">
          Dark track + light bars
        </div>
        <Waveform
          className="h-10 rounded-md"
          backgroundColor="#121212"
          barColor="rgba(255,255,255,0.85)"
        />
      </div>
      <div className="flex flex-col gap-1">
        <div className="text-xs font-medium text-muted-foreground">
          Subtle track + charcoal bars
        </div>
        <Waveform
          className="h-10 rounded-md"
          backgroundColor="rgba(0,0,0,0.06)"
          barColor="rgba(48,48,48,0.75)"
        />
      </div>
    </div>
  );
}

Props

PropTypeDefault
activebooleantrue
streamMediaStream | null | undefinedundefined
barsnumber96
backgroundColorstring (CSS color)undefined
barColorstring (CSS color)"rgba(224,224,224,0.92)"
gapPxnumber2
barWidthPxnumber2
smoothingnumber (0–1)0.24
strengthnumber1
onError(error: Error) => void
classNamestring