Audio Waveform
betaCanvas audio visualiser — live bars, mirrored history or oscilloscope line from a mic or media element, or recorded peaks with a seek scrubber.
Simulated live input
minimal
Player with scrub, and every variant
Recorded peaks with play/pause, drag-to-seek and a timer; below, bars/spectrum, mirrored/history and line.
full
0:20
Voice note in a chat bubble
A compact muted waveform with an icon-button play control.
composition
0:14
Voice note · transcribedInstallation
npx shadcn@latest add https://kuro.abstergo.fyi/r/audio-waveform.jsonCopies the source into components/kura/audio-waveform with its dependencies (kura-theme, kura-merge-refs, use-audio-analyser, use-reduced-motion, utils). Tokens come from the kura-theme item.
When to use it
Use it for
- Show that the mic is live while dictating or talking to a voice agent (mirrored history).
- Visualise playback of a model's spoken reply or an <audio> element (spectrum bars or line).
- A recorded voice note / audio attachment with a played/unplayed split and drag-to-seek.
Reach for something else when
- A complete dictation control with permissions, transcript and states — use voice-dictation (it renders this).
- An ambient “the agent is speaking” blob — use agent-orb.
- A non-audio progress indicator — use a progress bar or context-meter.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| analyser | AudioAnalyser | — | Live analyser from useAudioAnalyser. Share one between several visuals. |
| source | MediaStream | HTMLMediaElement | null | — | Mic stream or media element to analyse internally when no analyser is passed. |
| simulate | boolean | "speech" | "music" | "silence" | — | Synthetic data for docs, previews and pre-permission states. |
| peaks | readonly number[] | — | Static mode: amplitudes 0..1 of any length (resampled to the bar count). Build them with peaksFromSamples(). |
| progress | number | — | Playback position 0..1 in static mode; bars after it are faded. |
| onSeek | (progress: number) => void | — | Turns the waveform into a seek slider (drag, click, arrow keys). Receives 0..1. |
| duration | number | — | Duration in seconds for the slider's spoken value (“0:12 of 1:04”). |
| variant | "bars" | "mirrored" | "line" | "mirrored" | bars = bottom-aligned, mirrored = symmetric around the centre line, line = oscilloscope (live) or filled envelope (static). |
| display | "history" | "spectrum" | — | Live data for bars/mirrored: history scrolls recent loudness right→left, spectrum shows frequency bands. Default: history for mirrored, spectrum for bars. |
| barWidth | number | 3 | Bar width in CSS px. |
| barGap | number | 2 | Gap between bars in CSS px. |
| minBarHeight | number | 2 | Minimum bar height so silence reads as a dotted baseline. |
| tone | "default" | "muted" | "accent" | "default" | Colour from tokens: ink, subtle ink, or the jade signal (only for live AI listening — once per view). |
| active | boolean | true | Pause the live animation; the last frame stays on screen. |
| label | string | — | Accessible name. Default "Audio waveform", or "Seek" when onSeek is set. |
All native attributes of the root element, className and ref pass through.
Anatomy
- AudioWaveformRoot sized by CSS (default h-10 w-full). data-mode = live | static, data-variant, data-status (analyser status), data-scrubbing.
data-slot="audio-waveform"
Accessibility
- Role:
img (display) or slider (with onSeek) - Display-only waveforms are role=img with a label; the canvas itself is aria-hidden.
- With onSeek it is a focusable slider with aria-valuenow (0–100) and aria-valuetext (“0:12 of 1:04” when duration is set).
- Colours come from tokens via currentColor, so contrast follows light/dark themes automatically.
- Pair live waveforms with a text status (“Listening”, “Muted”) — the visual alone is not an accessible state.
- Reduced motion: Live data becomes a calm level meter redrawn ~6×/s without sub-frame scrolling; static waveforms are unaffected.
| Keys | Action |
|---|---|
| ArrowRight / ArrowUp | Seek forward 5% (Shift: 10%) |
| ArrowLeft / ArrowDown | Seek back 5% (Shift: 10%) |
| Home / End | Start / end |
Notes for agents
- Size it with CSS (className="h-8 w-48"); the canvas tracks the element size and device pixel ratio automatically.
- For a mic: const rec = useMediaRecorder(); <AudioWaveform source={rec.stream} />. For playback pass the <audio> element as source.
- Pass one `analyser` to several visuals instead of creating an analyser per component.
- peaksFromSamples(audioBuffer.getChannelData(0), 200) turns decoded audio into peaks.
- Use tone="accent" only while the AI is actively listening, and only once per view.