Skip to content
VoiceAudio playerMenu

Audio player

Read-aloud and audio output player: play and pause, a seek bar you can drag or step with keys, time in tabular figures, speed, and a compact mini player for message actions.

Speech is prepared, then plays with a seek bar and a speed menu.

Installation

$ pnpm dlx shadcn@latest add https://chatcn.dev/r/audio-player.json

Usage

tsx
import { AudioPlayer } from "@/components/ui/chatcn/audio-player"

<AudioPlayer src="/speech/answer-142.mp3" title="Why the deploy failed" meta="Marlo" />

Speech engines that stream audio without a file drive the player instead: pass playing, currentTime and duration with their callbacks, and status while speech is generated.

tsx
<AudioPlayer
  status={tts.ready ? "ready" : "loading"}
  playing={tts.playing}
  onPlayingChange={(play) => (play ? tts.resume() : tts.pause())}
  currentTime={tts.position}
  duration={tts.duration}
  onSeek={tts.seek}
  playbackRate={tts.rate}
  onPlaybackRateChange={tts.setRate}
/>

Examples

Variants and states

compact is the read-aloud button grown into a mini player, sized to sit in a row of message actions. peaks draws the track as a waveform with the played part in ink.

Compact mini player, waveform track, preparing, failed and ended.

States

StateShows
loadingSpinner in the play button, Preparing audio, seek disabled
readyPlay or pause
bufferingA thin spinner ring around the pause button while playback stalls
EndedPlay again, which restarts from the beginning
errorThe reason with a fix, and Retry when onRetry is set

Props

PropTypeNotes
srcstringPlays through an internal audio element
title, metaReactNodeOne line above the track in default
variant"default" | "compact"Self-contained, or a mini player
playing, defaultPlaying, onPlayingChangebooleanControlled or uncontrolled
currentTime, onSeeknumber (s)Position, and seeks from the bar, keys and skip
durationnumber (s)Read from the file when src is set
playbackRate, defaultPlaybackRate, onPlaybackRateChangenumberDefault 1
ratesnumber[]Default 0.75, 1, 1.25, 1.5, 2
status"loading" | "ready" | "buffering" | "error"Derived from the element with src
errorText, onRetryReactNode, () => voidError copy should say how to fix it
peaksnumber[]0 to 1. Draws a waveform track
skipnumber | falseSkip back seconds. Default 10
onEnded, labelEnd callback; accessible name, default Audio player

Keyboard

KeyAction
Space, K on the seek barPlay or pause
Left and right arrowsBack or forward 5 seconds (mirrored in RTL)
J, LBack or forward 10 seconds
Page Down, Page UpBack or forward 30 seconds
Home, EndStart or end
Arrow keys in the speed menuMove between speeds; Enter picks

Accessibility

  • The player is a named group. The seek bar is a real slider whose value is spoken in words: 1 minute 12 seconds of 3 minutes.
  • The play button's name follows its state: Play, Pause, Play again, Preparing audio. While preparing it is aria-disabled, not removed from the tab order.
  • Errors are a role=alert with a red icon and words, never color alone. Touch targets grow to 44 px on coarse pointers.

Motion

  • Play, pause and replay swap with a 150 ms blur crossfade when clicked; toggled with the keyboard they swap instantly.
  • Progress moves with transform and clip-path only, following the playhead every frame instead of the element's four updates a second.
  • The thumb fades in on hover, focus and drag over 150 ms. Under reduced motion the swap is instant.