āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā š shadcn/directory/elevenlabs/ui/components/waveform ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā
<ComponentPreview name="waveform-demo" description="A live scrolling waveform visualization with smooth animations." />
npx @elevenlabs/cli@latest components add waveform
</TabsContent>
<TabsContent value="manual">
<Steps>
<Step>Copy and paste the following code into your project.</Step>
<ComponentSource name="waveform" title="components/ui/waveform.tsx" /><Step>Update the import paths to match your project setup.</Step>
</Steps> </TabsContent> </CodeTabs>import {
AudioScrubber,
LiveMicrophoneWaveform,
MicrophoneWaveform,
RecordingWaveform,
ScrollingWaveform,
StaticWaveform,
Waveform,
} from "@/components/ui/waveform"
const data = Array.from({ length: 50 }, () => Math.random())
<Waveform data={data} height={100} barWidth={4} barGap={2} />
<ScrollingWaveform
height={80}
speed={50}
barCount={60}
barColor="hsl(var(--primary))"
fadeEdges={true}
/>
const [isRecording, setIsRecording] = useState(false)
<MicrophoneWaveform
active={isRecording}
height={100}
sensitivity={1.5}
onError={(error) => console.error("Microphone error:", error)}
/>
The base waveform component that displays audio data as bars.
<Waveform data={audioData} />
| Prop | Type | Description |
| ---------- | ---------------------------------------- | -------------------------------------------------- |
| data | number[] | Array of values between 0 and 1 for each bar |
| barWidth | number | Width of each bar in pixels. Default: 4 |
| barGap | number | Gap between bars in pixels. Default: 2 |
| barRadius | number | Border radius of bars. Default: 2 |
| barColor | string | Custom bar color. Uses foreground color by default |
| fadeEdges | boolean | Apply fade effect to edges. Default: true |
| fadeWidth | number | Width of fade effect in pixels. Default: 24 |
| height | string \| number | Height of the waveform. Default: 128 |
| onBarClick | (index: number, value: number) => void | Callback when a bar is clicked |
Continuously scrolling waveform with auto-generated bars.
<ScrollingWaveform speed={50} />
| Prop | Type | Description |
| -------- | --------------- | ------------------------------------------------- |
| speed | number | Scroll speed in pixels per second. Default: 50 |
| barCount | number | Number of bars to display. Default: 60 |
| ...props | WaveformProps | All Waveform props except data and onBarClick |
Interactive waveform for audio playback with seek functionality.
<AudioScrubber
data={waveformData}
currentTime={playbackTime}
duration={totalDuration}
onSeek={handleSeek}
/>
| Prop | Type | Description |
| ----------- | ------------------------ | --------------------------------------- |
| currentTime | number | Current playback time in seconds |
| duration | number | Total duration in seconds. Default: 100 |
| onSeek | (time: number) => void | Callback when user seeks to a new time |
| showHandle | boolean | Show draggable handle. Default: true |
| ...props | WaveformProps | All standard Waveform props |
Real-time microphone input visualization.
<MicrophoneWaveform active={isListening} sensitivity={1.5} />
| Prop | Type | Description |
| --------------------- | ------------------------ | ----------------------------------------------- |
| active | boolean | Enable/disable microphone input. Default: false |
| fftSize | number | FFT size for frequency analysis. Default: 256 |
| smoothingTimeConstant | number | Smoothing factor (0-1). Default: 0.8 |
| sensitivity | number | Amplitude sensitivity. Default: 1 |
| onError | (error: Error) => void | Error callback |
| ...props | WaveformProps | All standard Waveform props |
Waveform with deterministic random data based on seed.
<StaticWaveform bars={40} seed={42} />
| Prop | Type | Description |
| -------- | --------------- | --------------------------------------- |
| bars | number | Number of bars to generate. Default: 40 |
| seed | number | Random seed for consistent data |
| ...props | WaveformProps | All standard Waveform props |
Advanced microphone visualization with recording history and playback scrubbing.
<LiveMicrophoneWaveform
active={isRecording}
enableAudioPlayback={true}
playbackRate={1}
/>
| Prop | Type | Description |
| ------------------- | ---------------------------------- | -------------------------------------------------- |
| active | boolean | Enable/disable recording |
| historySize | number | Max bars to keep in history. Default: 150 |
| updateRate | number | Update interval in ms. Default: 50 |
| enableAudioPlayback | boolean | Enable audio scrubbing when stopped. Default: true |
| playbackRate | number | Audio playback speed. Default: 1 |
| savedHistoryRef | React.MutableRefObject<number[]> | External ref to persist history |
| dragOffset | number | External drag offset control |
| setDragOffset | (offset: number) => void | External drag offset setter |
| ...props | ScrollingWaveformProps | All ScrollingWaveform props |
Recording interface with scrubbing through recorded audio.
<RecordingWaveform
recording={isRecording}
onRecordingComplete={(data) => console.log("Recording data:", data)}
/>
| Prop | Type | Description |
| ------------------- | -------------------------- | ------------------------------------ |
| recording | boolean | Recording state. Default: false |
| onRecordingComplete | (data: number[]) => void | Callback with recorded waveform data |
| showHandle | boolean | Show scrubbing handle. Default: true |
| updateRate | number | Update interval in ms. Default: 50 |
| ...props | WaveformProps | All standard Waveform props |
function MusicPlayer() {
const [audioData] = useState(() =>
Array.from({ length: 100 }, () => 0.2 + Math.random() * 0.6)
)
return (
<AudioScrubber
data={audioData}
currentTime={currentTime}
duration={duration}
onSeek={handleSeek}
height={60}
barWidth={3}
barGap={1}
barColor="hsl(var(--primary))"
/>
)
}
function VoiceRecorder() {
const [recording, setRecording] = useState(false)
return (
<div className="space-y-4">
<RecordingWaveform
recording={recording}
height={100}
onRecordingComplete={(data) => {
console.log("Recording complete", data)
}}
/>
<Button onClick={() => setRecording(!recording)}>
{recording ? "Stop" : "Start"} Recording
</Button>
</div>
)
}
function AudioMonitor() {
const [active, setActive] = useState(false)
return (
<MicrophoneWaveform
active={active}
height={80}
sensitivity={2}
barWidth={2}
barGap={1}
onError={(error) => {
console.error("Microphone error:", error)
setActive(false)
}}
/>
)
}
requestAnimationFrame for smooth 60fps updates--foreground but can be customizedā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā