Player options
Full inherited option surface — Player → Options.
The @arraypress waveform family ships four React wrappers, each a thin, typed component over its vanilla core:
| Package | Component(s) | Wraps |
|---|---|---|
@arraypress/waveform-player-react |
<WaveformPlayer> |
@arraypress/waveform-player |
@arraypress/waveform-bar-react |
<WaveformBar>, <WaveformBarTrigger> |
@arraypress/waveform-bar |
@arraypress/waveform-playlist-react |
<WaveformPlaylist> |
@arraypress/waveform-playlist |
@arraypress/waveform-sounds-react |
<WaveformSounds> |
@arraypress/waveform-sounds |
Every wrapper follows the same design:
WaveformPlayerOptions; WaveformSoundsOptions for the sound list), not re-declared — so they never drift as the core evolves.import()ed inside useEffect, so the audio + canvas + fetch surface never runs during SSR / RSC.forwardRef as a typed imperative handle for loadTrack, seekTo, playlist navigation, and the rest.<WaveformPlayer>react and @arraypress/waveform-player are peer dependencies (React ^18 || ^19; core player ^1.8.0).
npm install @arraypress/waveform-player-react @arraypress/waveform-playerpnpm add @arraypress/waveform-player-react @arraypress/waveform-playeryarn add @arraypress/waveform-player-react @arraypress/waveform-playerbun add @arraypress/waveform-player-react @arraypress/waveform-playerImport the core stylesheet once at your app entry:
// main.tsx / app entryimport '@arraypress/waveform-player/dist/waveform-player.css';import { WaveformPlayer } from '@arraypress/waveform-player-react';
export default function Track() { return ( <WaveformPlayer url="/audio/track.mp3" title="My Track" waveformStyle="mirror" waveformColor={['#fafafa', '#71717a']} // gradient stops showBPM onTimeUpdate={(currentTime, duration) => { /* live progress */ }} /> );}WaveformPlayerProps extends the core’s WaveformPlayerOptions so every player option is accepted as a typed prop — height, samples, barRadius, colorPreset, accessibleSeek, markers, gradient-array colours, and so on. See Player → Options for the full inherited surface.
The core’s url, style, and the six callbacks are omitted from the inherited set and re-declared with React shapes:
| Prop | Type | Notes |
|---|---|---|
url |
string |
Audio file URL. Optional, because the core’s src shorthand alias is also accepted — provide one of url or src. |
onLoad |
(instance) => void |
Fired after the waveform is drawn. Receives the typed WaveformPlayer instance. |
onPlay |
(instance) => void |
Playback started (both audio modes). |
onPause |
(instance) => void |
Playback paused (both modes). |
onEnd |
(instance) => void |
Track ended (external mode synthesizes it at progress ≥ 1). |
onTimeUpdate |
(currentTime, duration, instance) => void |
Progress tick. Same argument order in both modes. |
onError |
(error, instance) => void |
Load / decode / audio error. |
id |
string |
Forwarded to the container <div>. |
className |
string |
Appended to the always-present base class wfp-host. |
style |
React.CSSProperties |
Inline style on the container — e.g. min-height to reserve space before draw. |
Grab the instance with a ref typed as WaveformPlayerHandle:
import { useRef } from 'react';import { WaveformPlayer, type WaveformPlayerHandle,} from '@arraypress/waveform-player-react';
function Controlled() { const ref = useRef<WaveformPlayerHandle>(null);
return ( <> <WaveformPlayer ref={ref} url="/audio/track.mp3" /> <button onClick={() => ref.current?.togglePlay()}>Play / Pause</button> <button onClick={() => ref.current?.seekTo(60)}>Jump to 1:00</button> <button onClick={() => ref.current?.loadTrack('/audio/next.mp3', 'Next Track', 'Artist') } > Load next </button> </> );}| Method | Signature | Notes |
|---|---|---|
play |
() => Promise<void> | undefined |
Returns the native play() promise in self mode; undefined in external mode. |
pause |
() => void |
|
togglePlay |
() => void |
|
seekTo |
(seconds: number) => void |
Self mode only. |
seekToPercent |
(percent: number) => void |
Fraction 0..1. Self mode only. |
setVolume |
(volume: number) => void |
0..1. Self mode only. |
setPlaybackRate |
(rate: number) => void |
Clamped 0.25..4. Self mode only. |
setPlayingState |
(playing: boolean) => void |
External mode — push play/pause visual state. |
setProgress |
(currentTime, duration) => void |
External mode — push the progress overlay from your own clock. |
loadTrack |
(url, title?, artist?, options?) => Promise<void> |
Swap track at runtime; auto-plays unless options.autoplay === false. |
instance |
readonly WaveformPlayer |
Escape hatch — full core API (load, setWaveformData, refreshTheme, container, statics, …). |
Calls before the async import resolves are safe no-ops.
<WaveformBar> + <WaveformBarTrigger>The bar is a singleton: render <WaveformBar> once in your root layout, then scatter <WaveformBarTrigger> elements anywhere to play or queue tracks.
Peer deps: @arraypress/waveform-bar (^1.3.1), @arraypress/waveform-player (^1.7.2), React ^18 || ^19.
npm install @arraypress/waveform-bar-react @arraypress/waveform-bar @arraypress/waveform-playerpnpm add @arraypress/waveform-bar-react @arraypress/waveform-bar @arraypress/waveform-playeryarn add @arraypress/waveform-bar-react @arraypress/waveform-bar @arraypress/waveform-playerbun add @arraypress/waveform-bar-react @arraypress/waveform-bar @arraypress/waveform-playerimport { WaveformBar } from '@arraypress/waveform-bar-react';
export default function RootLayout({ children }) { return ( <> {children} <WaveformBar config={{ persist: true, continuous: true, showQueue: true, actions: { favorite: { endpoint: '/api/favorites' }, cart: { endpoint: '/api/cart' }, }, }} /> </> );}On mount the component dynamically imports the core and calls window.WaveformBar.init(config). When config changes it re-runs init() (idempotent — the library destroys and recreates internally); on unmount it calls destroy() so StrictMode double-mounts and route changes don’t leak listeners.
<WaveformBar> prop |
Type | Default | Notes |
|---|---|---|---|
config |
WaveformBarConfig |
undefined |
Passed verbatim to init(). See table below. |
persist |
boolean |
true |
Relocate the .waveform-bar element under the host <div> so it survives route changes. |
hostId |
string |
'waveform-bar-host' |
DOM id of the persist host. |
className |
string |
— | Appended to the base class wb-host. |
style |
React.CSSProperties |
— | Inline style on the host. |
WaveformBarConfigBar-react hand-declares this type (the bar core ships no .d.ts), so this is the source of truth for the typed config surface. Every field is optional.
| Group | Fields |
|---|---|
| Behaviour | persist, autoResume, continuous, repeat ('off' | 'all' | 'one') |
| UI toggles | showQueue, showPrevNext, showRepeat, showVolume, showMute, showTime, showTrackLink, showMeta, maxMeta (number) |
| Layout | mode ('waveform' | 'classic'), wide, position ('bottom' | 'top'), collapsible, showShuffle, shuffle |
| Theming | defaultArtwork (string | null), theme ('dark' | 'light' | null) |
| Waveform | waveform (boolean), waveformStyle, waveformHeight, barWidth, barSpacing, waveformColor, progressColor, markerColor |
| Sharing / errors | share, shareParam, errorText |
| Volume / storage | volume, storageKey |
| Server actions | actions ({ favorite?, cart? }, each { endpoint, method?, headers? }) |
<WaveformBarTrigger> emits the data-wb-* attribute contract the bar scans for. It is polymorphic — a <button> by default — and forwards every standard DOM prop (onClick, data-testid, role, ref, …) to the rendered element.
import { WaveformBarTrigger } from '@arraypress/waveform-bar-react';
function ProductCard({ track }) { return ( <article> <h3>{track.title}</h3>
{/* Default: a <button> with auto play/pause icons */} <WaveformBarTrigger url={track.url} id={track.id} title={track.title} artist={track.artist} artwork={track.cover} />
{/* Append to the queue with custom content */} <WaveformBarTrigger mode="queue" url={track.url} title={track.title}> + Queue </WaveformBarTrigger>
{/* Wrap a whole card as the trigger */} <WaveformBarTrigger as="div" url={track.url} noDefaultIcons> <div className="card-body">{/* … */}</div> </WaveformBarTrigger> </article> );}Track data props (WaveformBarTrackData):
| Prop | Type | Notes |
|---|---|---|
url |
string |
Required — play target + identity. |
id |
string |
Defaults to url. |
title, artist, album, artwork, link |
string |
Display metadata. |
duration, bpm |
string | number |
|
musicalKey |
string |
→ data-wb-key. |
meta |
string[] |
Extra metadata chips. |
waveform |
number[] | string |
Pre-computed peaks or .json URL. |
markers |
WaveformBarMarker[] |
DJ-mode cue markers ({ time, label, title?, artist?, artwork?, bpm?, key?, color? }). |
favorited, inCart |
boolean |
Initial action state. |
Trigger-specific props:
| Prop | Type | Default | Notes |
|---|---|---|---|
mode |
'play' | 'queue' |
'play' |
Immediate play vs. append to queue. |
as |
'button' | 'a' | 'div' | 'span' |
'button' |
Rendered tag. |
href |
string |
— | Used when as="a". |
aria-label |
string |
auto | Auto-generated from title when absent. |
className |
string |
— | Appended to the base class wb-icon-swap. |
noDefaultIcons |
boolean |
false |
Suppress the injected play/pause SVGs. |
children |
ReactNode |
— | Custom content; overrides the default icons. |
<WaveformPlaylist>A declarative tracks array with optional chapters, an embedded player, and imperative track / chapter navigation.
Peer deps: @arraypress/waveform-playlist (^1.3.0), @arraypress/waveform-player (^1.8.0), React ^18 || ^19.
npm install @arraypress/waveform-playlist-react @arraypress/waveform-playlist @arraypress/waveform-playerpnpm add @arraypress/waveform-playlist-react @arraypress/waveform-playlist @arraypress/waveform-playeryarn add @arraypress/waveform-playlist-react @arraypress/waveform-playlist @arraypress/waveform-playerbun add @arraypress/waveform-playlist-react @arraypress/waveform-playlist @arraypress/waveform-playerImport both stylesheets, and import the player core for its global side effect — the playlist constructs new window.WaveformPlayer(...) for the active track:
import '@arraypress/waveform-player/dist/waveform-player.css';import '@arraypress/waveform-playlist/dist/waveform-playlist.css';import '@arraypress/waveform-player'; // registers window.WaveformPlayerimport { WaveformPlaylist } from '@arraypress/waveform-playlist-react';
export default function Album() { return ( <WaveformPlaylist continuous waveformStyle="bars" tracks={[ { url: '/audio/a.mp3', title: 'Track A', artist: 'Artist' }, { url: '/audio/b.mp3', title: 'Track B', chapters: [ { time: 0, label: 'Intro' }, { time: '1:30', label: 'Verse' }, { time: 180, label: 'Chorus', color: '#22d3ee' }, ], }, ]} /> );}Each track renders into the [data-track] / [data-chapter] markup the playlist constructor parses on mount. The host carries the base class wfp-host.
tracks (required)WaveformPlaylistTrackInput[]:
| Field | Type | Notes |
|---|---|---|
url |
string |
Required → data-url. |
title |
string |
Falls back to the filename if omitted. |
artist |
string |
Artist row. |
artwork |
string |
Artwork URL. |
album |
string |
Media Session metadata. |
duration |
string |
Display duration, e.g. '3:45'. |
markers |
WaveformPlaylistMarker[] |
JSON-encoded into data-markers. |
chapters |
WaveformPlaylistChapterInput[] |
{ time: number | string, label, color? } — time accepts seconds or 'M:SS'. |
WaveformPlaylistProps combines three groups:
WaveformPlaylistOptions: continuous, expandChapters, showDuration, showChapterMarkers, chapterMarkerColor, showPlayState.WaveformPlayerOptions visualization / colour / behaviour surface, minus per-track content (url, src, title, artist, artwork, album, markers, waveform), the style alias, the player’s own layout, and the lifecycle callbacks.tracks, plus id, className, style.| Prop | Type | Default | Notes |
|---|---|---|---|
tracks |
WaveformPlaylistTrackInput[] |
— | Required. An empty array renders an empty playlist (the core skips init). |
layout |
'list' | 'minimal' |
'list' |
Full track list vs. compact button switcher. Overrides the player’s own layout. |
id |
string |
— | Forwarded to the host <div>. |
className |
string |
— | Appended to wfp-host. |
style |
React.CSSProperties |
— | Inline style on the host. |
Common pass-through player props include waveformStyle, height, colorPreset, waveformColor, progressColor, showBPM, accessibleSeek, and autoplay — see Player → Options for the complete list.
import { useRef } from 'react';import { WaveformPlaylist, type WaveformPlaylistHandle,} from '@arraypress/waveform-playlist-react';
function NavExample({ tracks }) { const ref = useRef<WaveformPlaylistHandle>(null);
return ( <> <WaveformPlaylist ref={ref} tracks={tracks} continuous /> <button onClick={() => ref.current?.previousTrack()}>Prev</button> <button onClick={() => ref.current?.nextTrack()}>Next</button> <button onClick={() => ref.current?.selectTrack(0)}>First</button> </> );}| Method | Signature | Notes |
|---|---|---|
selectTrack |
(index: number) => void |
Select + load a track by index. |
seekToChapter |
(trackIndex, time) => void |
Loads the target track first if needed, then seeks. |
nextTrack |
() => void |
|
previousTrack |
() => void |
|
getPlayer |
() => unknown | null |
The embedded WaveformPlayer instance (full player API). |
getCurrentTrackIndex |
() => number |
|
getTracks |
() => WaveformPlaylistTrack[] |
Parsed tracks (with resolved element / index). |
instance |
readonly WaveformPlaylist | null |
Escape hatch for anything else. |
Calls before the async import resolves are no-ops.
<WaveformSounds>A searchable, filterable list of sound previews — a sample pack, a preset bank — with a mini waveform per row and one shared audio engine. Unlike the components above it renders its markup in React: with sounds, the whole list is in the first render (server HTML included) and the runtime adopts it on the client instead of rebuilding it. See WaveformSounds for what the list does.
Peer deps: @arraypress/waveform-sounds (^0.1.0), @arraypress/waveform-player (^1.24.5), React ^18 || ^19.
npm install @arraypress/waveform-sounds-react @arraypress/waveform-sounds @arraypress/waveform-playerpnpm add @arraypress/waveform-sounds-react @arraypress/waveform-sounds @arraypress/waveform-playeryarn add @arraypress/waveform-sounds-react @arraypress/waveform-sounds @arraypress/waveform-playerbun add @arraypress/waveform-sounds-react @arraypress/waveform-sounds @arraypress/waveform-playerImport both stylesheets, and import the player for its global — the list plays through window.WaveformPlayer, and the wrapper doesn’t import it for you (or pass the class as playerClass). With the player in place, the list builds its engine as soon as it’s ready; no audio loads until something plays.
import '@arraypress/waveform-player'; // registers window.WaveformPlayer, the engineimport '@arraypress/waveform-player/styles.css';import '@arraypress/waveform-sounds/styles.css';import { WaveformSounds } from '@arraypress/waveform-sounds-react';
export default function Pack() { return ( <WaveformSounds sounds={[ { url: '/s/bass-01.mp3', title: 'Bass Loop 01', type: 'Bass', bpm: 128, key: 'Fm' }, { url: '/s/drum-01.mp3', title: 'Drum Loop 01', type: 'Drums', bpm: 140 }, ]} onPlay={(sound) => console.log(sound.title)} /> );}Or from a waveform-gen --manifest file, fetched and built in the browser: <WaveformSounds manifest="/sounds.json" />. To server-render a manifest, read it at build or request time and pass its sounds array.
Every WaveformSoundsOptions key is a prop, typed from the core’s own index.d.ts — sounds / manifest, player, search, filters, sorts, showCount, menuSearch, idPrefix, urlState, loopToggle, maxTypeChips, pageSize, columns, waveformStyle, waveformColor, progressColor, barWidth, barGap, loop, autoAdvance, arrowAudition, playerOptions, playerClass, strings — plus the callbacks onReady / onPlay / onPause / onEnd / onFilter / onError, and id / className / style for the host. See Options for each one.
sounds, the host’s inner HTML is the core’s own renderSounds() output. With only manifest, the host is empty and the list is fetched and built client-side. The host carries wfp-host waveform-sounds waveform-sounds--<player> from the first render, so it’s styled before hydration, and never data-waveform-sounds (the global auto-init must not mount it twice).idPrefix prop, else the id prop, else one from React’s useId().urlState is runtime-only: the server renders the unfiltered list and the runtime applies the address on load.sounds, filters, sorts, columns, strings, playerOptions) compare by value, so inline literals are fine. A changed callback never re-creates it — the latest one is always called, and the ref is live by the time onReady and the first onFilter fire.Theme it through style — set --ws-surface (your page background) so the server-rendered first paint is right:
<WaveformSounds sounds={sounds} style={{ '--ws-surface': '#0a0a0a' } as React.CSSProperties} />import { useRef } from 'react';import { WaveformSounds, type WaveformSoundsHandle } from '@arraypress/waveform-sounds-react';
function Pack({ sounds }) { const ref = useRef<WaveformSoundsHandle>(null);
return ( <> <WaveformSounds ref={ref} sounds={sounds} /> <button onClick={() => ref.current?.setFilter({ type: 'Bass' })}>Bass</button> <button onClick={() => ref.current?.setSort('bpm')}>By BPM</button> </> );}| Method | Signature | Notes |
|---|---|---|
play |
(target?, opts?: { at?: number }) => void |
By index, id or Sound; no target resumes. |
pause |
() => void |
|
toggle |
(target?) => void |
|
next / previous |
() => void |
The next / previous visible sound. |
setFilter |
(patch: Partial<SoundsFilter>) => void |
Merged into the current filter. |
clearFilters |
() => void |
The sort stays. |
setSort |
(by: SoundsSort) => void |
|
setLoop |
(on: boolean) => void |
Live — changing the loop prop re-creates the list instead. |
showMore |
() => void |
|
instance |
readonly WaveformSounds | null |
The core instance (visible, current, sounds, engine, ready, …). |
Calls before the instance mounts are no-ops. The core’s bubbling waveformsounds:* events fire from the host as well.
All four components are SSR / RSC safe by construction: the host markup renders on the server, and the browser-only core is dynamically import()ed inside useEffect so it only evaluates client-side. <WaveformSounds> goes further and renders the whole list on the server (from the core’s DOM-free /render entry) when given sounds. The interactive UI hydrates in once the library loads. Reserve layout space with the style prop (e.g. style={{ minHeight: 96 }}) to avoid a content shift before the waveform draws.
Player options
Full inherited option surface — Player → Options.
Events
The waveformplayer:* DOM events — Player → Events.
Astro
The declarative data-* wrappers — Astro.
Installation
Bundler, CDN, and CSS setup — Getting started.