Audio Player
A player built on the real <audio> element, so buffering, codecs, media keys, the OS lock screen and Bluetooth controls all keep working. State is read back off the element rather than mirrored, so anything else touching playback stays in sync.
src/components/ui/audio-player.tsxAbout
From the component's own source, where it was written.
An audio element with a scrubber, built on the real <audio> element.
The element does the work — buffering, codecs, media keys, the OS lock screen, Bluetooth controls — and this draws the surface over it. A player built on AudioContext for the sake of a nicer waveform loses all of that, and the waveform is the least important part.
**State is read from the element, not mirrored beside it.** timeupdate, play and pause all fire whether the change came from this UI, a media key, or the OS — so mirroring into React state means the two disagree the moment anything else touches playback.
**The scrubber is the kit's Slider.** It is a range input underneath, so keyboard seeking, page-up jumps, screen-reader announcement and touch drag are all native — and it looks like every other slider in the product, which a hand-styled one with its own vendor pseudo-element rules did not. It is scrubbing *while dragging* that needs care: dragging suspends the timeupdate handler, or every frame of playback yanks the thumb back out of your hand.
peaks draws a waveform behind the track when you have one, and the slider's rail and fill go transparent so the peaks *are* the track rather than being covered by a second progress bar. Computing it here would mean decoding the whole file in the main thread before the first frame — the caller either has it precomputed or does not want it.
Install
npx astralyx-ui add audio-player
Usage
import { AudioPlayer } from '@/components/ui/audio-player'
<AudioPlayer src="/theme.mp3" title="Theme" artist="Astralyx" peaks={peaks} />Composer
Change the props and the source below follows.
For bigger joyrides
Sample audio
<AudioPlayer
src="/theme.mp3"
title="Theme"
artist="Astralyx"
showSkip={true}
peaks={peaks}
/>Examples
With a waveform
<AudioPlayer src={src} title="Theme" peaks={peaks} />Bare
<AudioPlayer src={src} showSkip={false} />API
Generated from the source, so it cannot fall behind the code. One table per exported component, then hooks, functions and types.
AudioPlayer
Extends Omit<ComponentProps<'div'>, 'title' | 'onEnded'>.
| Prop | Type | Default | Description |
|---|---|---|---|
| src | string | required | The element does the playing; this draws the surface over it. |
| title | ReactNode | — | The element does the playing; this draws the surface over it. |
| artist | ReactNode | — | The element does the playing; this draws the surface over it. |
| peaks | number[] | — | Precomputed amplitudes 0–1, drawn behind the scrubber. Computing them here would mean decoding the whole file on the main thread before the first frame. |
| defaultMuted | boolean | false | Start muted — required for anything that also autoplays. |
| skipBy | number | 15 | Seconds a skip button moves, and whether they render at all. |
| showSkip | boolean | true | Seconds a skip button moves, and whether they render at all. |
| artwork | ReactNode | — | Cover art. |
| onEnded | () => void | — | — |
| playLabel | string | 'Play' | — |
| pauseLabel | string | 'Pause' | — |
| muteLabel | string | 'Mute' | — |
| unmuteLabel | string | 'Unmute' | — |
| seekLabel | string | 'Seek' | — |
| backLabel | string | 'Back' | — |
| forwardLabel | string | 'Forward' | — |
| formatTime | (seconds: number) => string | defaultTime | Formats the clock. Defaults to m:ss, and h:mm:ss past an hour. |
Functions
| Function | Signature | Description |
|---|---|---|
| formatAudioTime | (seconds: number) => string | — |
Behaviour
| Topic | Detail | Description |
|---|---|---|
| scrubbing | input[type=range] | Keyboard seeking, page-up jumps and screen-reader announcement are native. timeupdate is suspended while dragging, or playback yanks the thumb out of your hand. |
| state | read from the element | Every listener reads back off <audio>, so a media key or the OS lock screen keeps this UI correct without going through it. |