- Accordion
- Alert
- Alert Dialog
- Aspect Ratio
- Attachment
- Avatar
- Badge
- Breadcrumb
- Bubble
- Button
- Button Group
- Calendar
- Card
- Carousel
- Chart
- Checkbox
- Collapsible
- Combobox
- Command
- Context Menu
- Desktop Window
- Dialog
- Drawer
- Dropdown Menu
- Empty
- Field
- Frame
- Heading
- Hover Card
- Input
- Input Group
- Input OTP
- Item
- Kbd
- Label
- Live Waveform
- Marker
- Menubar
- Message
- Message Scroller
- Meter
- Meter Ring
- Native Select
- Navigation Menu
- NumberCount
- Pagination
- Popover
- Progress
- Progress Ring
- Radio Group
- Resizable
- Scroll Area
- Select
- Separator
- Sheet
- Sidebar
- Skeleton
- Slider
- Spinner
- Switch
- Table
- Tabs
- Textarea
- Timeline
- Toast
- Toggle
- Toggle Group
- Tooltip
A canvas-based real-time audio waveform visualizer with microphone input and customizable rendering modes.
"use client"
import * as React from "react"LiveWaveform requests microphone access when active is true. Only enable
it in response to a user gesture, and provide a clear way to stop listening.
Installation#
bunx --bun shadcn@latest add @ondo-ui/live-waveform
Usage#
import { LiveWaveform } from "@/components/ui/live-waveform"<LiveWaveform active={true} />The bars default to the primary color. Override with any text-* utility via
className, or set barColor for an arbitrary value:
<LiveWaveform active={true} className="text-emerald-500" />
<LiveWaveform active={true} barColor="#3b82f6" />Examples#
Static mode#
Symmetric bars across the frequency bands, updated in place. This is the default.
import { LiveWaveform } from "@/components/ui/live-waveform"
export default function LiveWaveformStatic() {Scrolling mode#
Historical average volume scrolling from right to left, so the waveform reads as a timeline. History fills in gradually as audio arrives.
import { LiveWaveform } from "@/components/ui/live-waveform"
export default function LiveWaveformScrolling() {Processing state#
Set processing to show an animated wave while waiting for input — for example
between the moment a user stops speaking and the model's reply. It respects
prefers-reduced-motion and settles to a calm static frame instead of looping.
"use client"
import * as React from "react"Bar geometry#
barWidth, barGap, barRadius, and barHeight (the minimum bar height)
shape the bars — widen them with more gap for a chunky look, set barRadius to
half the width for pills or 0 for hard edges. fadeEdges controls whether the
ends soften or run to the border.
Start listening to drive every preset from one shared microphone (passed via
stream). The processing shape is only the pre-recording state — once
listening, every bar stays active and follows the live signal, silence
included.
"use client"
import * as React from "react"Custom styling#
Bar geometry, color, height, and edge fading are all configurable.
import { LiveWaveform } from "@/components/ui/live-waveform"
export default function LiveWaveformCustom() {Bring your own stream#
Pass a stream to visualize audio you already have — a shared microphone track,
or an agent's response audio routed through a MediaStreamAudioDestinationNode.
When stream is set, LiveWaveform uses it instead of calling getUserMedia,
and leaves the stream's lifecycle to you (it never stops tracks it did not open).
<LiveWaveform active stream={myStream} />API reference#
LiveWaveform#
A canvas-based real-time audio visualizer with microphone input support.
| Prop | Type | Default | Description |
|---|---|---|---|
active | boolean | false | Whether to actively listen to audio input. |
processing | boolean | false | Show a processing animation when not active. |
stream | MediaStream | null | null | Use an existing stream instead of the microphone. |
deviceId | string | — | Specific input device to request from getUserMedia. |
barWidth | number | 4 | Width of each bar in pixels. |
barHeight | number | 4 | Minimum bar height in pixels. |
barGap | number | 2 | Gap between bars in pixels. |
barRadius | number | 2 | Corner radius of bars. |
barColor | string | — | Bar color. Defaults to the current text color. |
fadeEdges | boolean | true | Fade the left and right edges of the waveform. |
fadeWidth | number | 24 | Width of the fade in pixels. |
height | string | number | 64 | Height of the waveform. |
sensitivity | number | 1 | Audio sensitivity multiplier. |
smoothingTimeConstant | number | 0.8 | Analyser smoothing, 0–1. |
fftSize | number | 256 | FFT size for frequency analysis. |
historySize | number | 60 | Bars kept in history (scrolling mode). |
updateRate | number | 30 | Audio update interval in milliseconds. |
mode | "scrolling" | "static" | "static" | Visualization mode. |
onError | (error: Error) => void | — | Called when microphone access fails. |
onStreamReady | (stream: MediaStream) => void | — | Called when the audio stream is ready. |
onStreamEnd | () => void | — | Called when a microphone stream this component owns ends. |
className | string | — | Merged onto the wrapper element. |
...props | HTMLDivElement | — | All standard div element props. |
Notes#
- Uses the Web Audio API for real-time frequency analysis, rendered to a canvas with HiDPI support.
- Microphone permission is requested when
activebecomestrue; streams and audio contexts are cleaned up on unmount and whenactiveturns off. - The render loop parks itself when idle and wakes only for live audio, processing, resizes, and transitions — it does not burn a frame budget at rest.
- Static mode shows symmetric bars across the frequency bands; scrolling mode shows historical average volume scrolling from right to left.
- Callbacks are read from a ref, so passing new inline
onError/onStreamReady/onStreamEndfunctions on each render does not re-acquire the microphone.