Skip to content
WANCRAFT

A.156Buttons & Controls

Waveform Player

A compact audio player with a bar waveform you can click or drag to scrub, a morphing play button and a real slider for keyboards.

NewReactMotionTailwindadvanced

Overview

Waveform Player is the small audio row you find in voice notes and podcast snippets, done properly. The waveform is drawn from a peaks array, with the played part highlighted in the accent and a faint preview (plus a time label) following the pointer. Click anywhere to seek or drag to scrub. The play button morphs between its two shapes point by point. For keyboards and assistive tech the waveform is a real slider, announced as “1:23 of 3:45”: the arrow keys seek by a configurable step, Page Up / Down by a tenth, Home / End to the ends, and Space plays or pauses. It plays any audio URL, or in synth mode generates a gentle Web Audio arpeggio so the demo needs no files. It never autoplays and stops when it unmounts.

01Installation

Method 01 · Registry

CLI verified

Uses the shadcn CLI in a project initialised with shadcn init. Installs the file to components/ui/waveform-player.tsx and adds its dependencies.

npx shadcn@latest add https://wancraft.my/r/waveform-player.json

Method 02 · Copy and paste

  1. 1. Install the dependencies below.
  2. 2. Copy the source from the Code tab into components/ui/waveform-player.tsx.
  3. 3. Import it and adjust props.
npm install motion
Dependencies
motion
Peer
react ≥ 18, tailwindcss ≥ 4
Added
13 Oct 2026
Updated
—

02Usage

The default configuration. Change properties above to generate a tailored example in the Usage tab.

example.tsx
import { WaveformPlayer } from "@/components/ui/waveform-player";

export function WaveformPlayerExample() {
  const peaks = Array.from({ length: 72 }, (_, i) => 0.35 + 0.65 * Math.abs(Math.sin(i * 0.37) * Math.cos(i * 0.11)));

  return (
    <WaveformPlayer
      peaks={peaks}
      synth
      title="Night drive"
      subtitle="Demo synth"
    />
  );
}

03Props

PropTypeDefaultDescription
peaksnumber[]—Required. One bar per value, any positive scale (normalised to the largest).
srcstring—Audio file URL (played with an <audio> element). Ignored when synth is on.
synthbooleanfalsePlay a generated Web Audio arpeggio instead of a file.
synthDurationnumber32Length of the synth melody in seconds.
titlestring—Track title, shown above the waveform and used in the accessible names.
subtitlestring—Secondary line, such as the artist.
seekStepnumber5Seconds moved by the arrow keys.
heightnumber48Waveform height in px.
barGapnumber2Gap between bars in px.
showTimebooleantrueShow elapsed / total time.
variant"card" | "bare""card"Bordered card or no chrome.
accentstring"#FF7448"Played bars, play button and focus rings.
onPlayChange(playing: boolean) => void—Called when playback starts or stops (including at the end).
...propsHTMLAttributes<HTMLDivElement>—Forwarded to the root group.

04Features

  • 01Bar waveform from a peaks array with the played portion highlighted; extra peaks merge so narrow layouts never overflow
  • 02Click to seek, drag to scrub, hover to preview the position and time
  • 03Play / pause icon that morphs point by point
  • 04Plays an audio URL, or a generated Web Audio melody in synth mode (no files needed)
  • 05Progress runs on a CSS variable per frame (no re-render per frame) and pauses while off screen
  • 06Never autoplays; stops and releases the audio when unmounted or when the source changes
motion · springmotion · continuousinput · clickinput · draginput · keyboardinput · pointer

05Accessibility

  • The waveform is role="slider" with aria-valuemin / max / now in seconds and aria-valuetext such as “1:23 of 3:45”.
  • Keyboard on the slider: Left / Down and Right / Up seek by seekStep, Page Up / Down by a tenth, Home / End jump to the ends, and Space (or K) plays and pauses.
  • The play button is a native button whose label switches between “Play …” and “Pause …” with the track title.
  • Bars, the hover label and the visual time readout are aria-hidden duplicates of the slider's values; focus rings are visible in the accent colour.
  • With prefers-reduced-motion the icon switches instantly and the button no longer scales on press.

06Limitations

  • Peaks are not computed from the file; generate them ahead of time (for example with audiowaveform or the Web Audio API).
  • Browsers only allow audio after a user gesture, which the play button provides.