GitHub 0

Waveform

Previous

Canvas-based audio waveform visualization components with recording, playback scrubbing, and microphone input support.

Waveform
Real-time audio visualization with smooth scrolling animation

Installation

npx shadcn-svelte@latest add https://sv11.ui.twango.dev/r/waveform.json

Usage

<script lang="ts">
	import { Waveform } from "$lib/registry/ui/waveform";
</script>
 
<Waveform />

Examples

Basic Usage

Pass an array of values in [0, 1] to render a static bar chart.

<script lang="ts">
	import { Waveform } from "$lib/registry/ui/waveform";
 
	const data = Array.from({ length: 50 }, () => Math.random());
</script>
 
<Waveform {data} height={100} barWidth={4} barGap={2} />

Scrolling Animation

ScrollingWaveform continuously scrolls auto-generated bars from right to left.

<script lang="ts">
	import { ScrollingWaveform } from "$lib/registry/ui/waveform";
</script>
 
<ScrollingWaveform height={80} speed={50} barCount={60} fadeEdges />

Microphone Input

MicrophoneWaveform captures live input and renders a symmetric frequency visualization.

<script lang="ts">
	import { MicrophoneWaveform } from "$lib/registry/ui/waveform";
 
	let isRecording = $state(false);
</script>
 
<MicrophoneWaveform
	active={isRecording}
	height={100}
	sensitivity={1.5}
	onError={(error) => console.error("Microphone error:", error)}
/>

Static Waveform

StaticWaveform generates deterministic bars from a seed — useful for placeholders.

<script lang="ts">
	import { StaticWaveform } from "$lib/registry/ui/waveform";
</script>
 
<StaticWaveform bars={40} seed={42} />

Audio Scrubber

AudioScrubber turns a waveform into an interactive seek control.

<script lang="ts">
	import { AudioScrubber } from "$lib/registry/ui/waveform";
 
	const data = Array.from({ length: 100 }, () => 0.2 + Math.random() * 0.6);
	let currentTime = $state(0);
	const duration = 100;
 
	function handleSeek(time: number) {
		currentTime = time;
	}
</script>
 
<AudioScrubber
	{data}
	{currentTime}
	{duration}
	onSeek={handleSeek}
	height={60}
	barWidth={3}
	barGap={1}
/>

Voice Recorder

RecordingWaveform captures mic input, stores the full history, and lets the user scrub through the recording once stopped.

<script lang="ts">
	import { RecordingWaveform } from "$lib/registry/ui/waveform";
	import { Button } from "$lib/registry/ui/button";
 
	let recording = $state(false);
</script>
 
<div class="space-y-4">
	<RecordingWaveform
		{recording}
		height={100}
		onRecordingComplete={(data) => {
			console.log("Recording complete", data);
		}}
	/>
	<Button onclick={() => (recording = !recording)}>
		{recording ? "Stop" : "Start"} Recording
	</Button>
</div>

Live Microphone with Playback

LiveMicrophoneWaveform pairs a scrolling live feed with post-recording scrub-and-playback.

<script lang="ts">
	import { LiveMicrophoneWaveform } from "$lib/registry/ui/waveform";
 
	let active = $state(false);
</script>
 
<LiveMicrophoneWaveform {active} enableAudioPlayback playbackRate={1} />

API Reference

Prop Type Default Description
data? number[] [] Array of normalized bar values in [0, 1]. The component samples from this array to fill the available width.
barWidth? number 4 Width of each bar in pixels.
barHeight? number 4 Minimum bar height in pixels. Bars are drawn at least this tall even when their value is near zero.
barGap? number 2 Gap between bars in pixels.
barRadius? number 2 Corner radius applied to each bar. Set to 0 for square bars.
barColor? string Custom bar color. Falls back to the canvas's computed --foreground CSS variable when unset.
fadeEdges? boolean true Fade the left and right edges of the waveform via a destination-out gradient mask.
fadeWidth? number 24 Width of the edge fade region in pixels.
height? string | number 128 Height of the waveform container. Numbers are treated as pixels; strings are passed through as a CSS length.
active? boolean Marks the waveform as actively capturing or rendering audio. Rendered as data-active on the root element for CSS styling hooks.
onBarClick? (index: number, value: number) => void Called when a bar is clicked with the data index and its value.

Notes

  • All variants render to HTML5 Canvas and drive animations with requestAnimationFrame for smooth 60fps updates.
  • Bar color defaults to the computed --foreground CSS variable — override via barColor to break out of the theme.
  • Canvas sizing is handled internally via ResizeObserver and accounts for devicePixelRatio on high-DPI displays.
  • Microphone-backed variants (MicrophoneWaveform, RecordingWaveform, LiveMicrophoneWaveform) request user permission when their active / recording prop becomes true and release the stream when it flips back to false.
  • AudioScrubber only updates its internal position from currentTime while the user is not dragging — local state takes over during interaction to avoid feedback loops.
  • ScrollingWaveform synthesizes data when data is empty; pass values to drive it from your own source.