Player options
Full inherited option surface — Player → Options.
The @arraypress waveform family ships three Svelte 5 wrappers, each a thin, typed component over its vanilla core — built with runes ($props, $effect, $derived):
| Package | Component(s) | Wraps |
|---|---|---|
@arraypress/waveform-player-svelte |
<WaveformPlayer> |
@arraypress/waveform-player |
@arraypress/waveform-bar-svelte |
<WaveformBar>, <WaveformBarTrigger> |
@arraypress/waveform-bar |
@arraypress/waveform-playlist-svelte |
<WaveformPlaylist> |
@arraypress/waveform-playlist |
Every wrapper follows the same design:
WaveformPlayerOptions, not re-declared — so they never drift as the core evolves. (The bar is the exception: its core ships no .d.ts, so waveform-bar-svelte hand-declares WaveformBarConfig.)import()ed inside $effect, which only runs in the browser — so the audio + canvas + fetch surface never runs during SSR / SvelteKit prerendering.export functions, reached through bind:this — loadTrack, seekTo, playlist navigation, and the rest.+layout.svelte).<WaveformPlayer>svelte (^5.0.0) and @arraypress/waveform-player (^1.8.0) are peer dependencies — you bring them so you control the versions.
npm install @arraypress/waveform-player-svelte @arraypress/waveform-player sveltepnpm add @arraypress/waveform-player-svelte @arraypress/waveform-player svelteyarn add @arraypress/waveform-player-svelte @arraypress/waveform-player sveltebun add @arraypress/waveform-player-svelte @arraypress/waveform-player svelteImport the core stylesheet once at your app entry. The wrapper does not import it for you — your bundler should own that decision:
<script> // also works in main.ts for a plain Vite app import '@arraypress/waveform-player/dist/waveform-player.css';</script>
<slot /><script lang="ts"> import { WaveformPlayer } from '@arraypress/waveform-player-svelte';</script>
<WaveformPlayer url="/audio/track.mp3" title="My Track" waveformStyle="mirror" waveformColor={['#fafafa', '#71717a']} showBPM ontimeupdate={(currentTime, duration) => { /* live progress */ }}/>WaveformPlayerProps is Omit<WaveformPlayerOptions, …callbacks>, 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. Absent props are not forwarded, so the core’s own defaults apply.
src is a shorthand alias for url (provide one; url wins if both are set). class, style, id, and any other element attribute fall through to the host <div> via HTMLAttributes<HTMLDivElement> — the base class wfp-host is always applied.
| Prop | Type | Notes |
|---|---|---|
url |
string |
Audio file URL. |
src |
string |
Shorthand alias for url. |
class |
string |
Appended to the always-present base class wfp-host. |
style |
string |
Inline CSS on the host — e.g. min-height to reserve space before draw. |
id |
string |
Forwarded to the host <div>. |
Every lifecycle event the core exposes is a lowercase callback prop (matching Svelte’s native onclick / oninput convention), each forwarding the live WaveformPlayer instance:
<WaveformPlayer url="/audio/track.mp3" onload={(i) => console.log('loaded', i)} onplay={() => console.log('playing')} onpause={() => console.log('paused')} ontimeupdate={(currentTime, duration) => console.log(`${currentTime}s / ${duration}s`)} onend={() => console.log('finished')} onerror={(err) => console.error('audio failed:', err)}/>| Prop | Signature | Notes |
|---|---|---|
onload |
(instance) => void |
Fired once after the waveform is drawn. |
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. |
bind:thisThe component exports its API as Svelte 5 export functions. Grab the instance with bind:this and call them directly:
<script lang="ts"> import { WaveformPlayer } from '@arraypress/waveform-player-svelte'; let player: WaveformPlayer;</script>
<WaveformPlayer bind:this={player} url="/audio/track.mp3" /><button onclick={() => player.togglePlay()}>Play / Pause</button><button onclick={() => player.seekTo(60)}>Jump to 1:00</button><button onclick={() => player.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 |
0.5..2. 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. |
getInstance |
() => WaveformPlayer | null |
Escape hatch — the full core API (load, setWaveformData, refreshTheme, container, statics, …). |
Calls before the async import resolves are safe no-ops. The typed handle interface is exported as WaveformPlayerExpose for explicit typing.
When pairing with @arraypress/waveform-bar (or any audio controller you own), the player can render visualization only and surrender playback. Drive it via setProgress() / setPlayingState():
<WaveformPlayer url={track.url} audioMode="external" waveformStyle="seekbar" showInfo={false} height={32}/><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), svelte (^5.0.0).
npm install @arraypress/waveform-bar-svelte @arraypress/waveform-bar @arraypress/waveform-player sveltepnpm add @arraypress/waveform-bar-svelte @arraypress/waveform-bar @arraypress/waveform-player svelteyarn add @arraypress/waveform-bar-svelte @arraypress/waveform-bar @arraypress/waveform-player sveltebun add @arraypress/waveform-bar-svelte @arraypress/waveform-bar @arraypress/waveform-player svelteRender exactly once in your root layout. On mount the component renders a persist host, dynamically imports the core, and calls window.WaveformBar.init(config); on unmount it calls destroy() so route changes don’t leak listeners.
<script lang="ts"> import { WaveformBar } from '@arraypress/waveform-bar-svelte';</script>
<slot />
<WaveformBar config={{ persist: true, continuous: true, showQueue: true, actions: { favorite: { endpoint: '/api/favorites' }, cart: { endpoint: '/api/cart' }, }, }}/><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. |
class and any other attribute fall through to the host <div> (base class wb-host).
WaveformBarConfigwaveform-bar-svelte 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 | wide, position ('bottom' | 'top'), collapsible, mode ('waveform' | 'classic'), 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; the core library handles click delegation. It is polymorphic via as — a <button> by default — and forwards every standard DOM attribute (onclick, data-testid, role, …) to the rendered element via ...rest.
<script lang="ts"> import { WaveformBarTrigger } from '@arraypress/waveform-bar-svelte'; export let track;</script>
<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} title={track.title} noDefaultIcons> <div class="card-body"><!-- … --></div> </WaveformBarTrigger></article>Track data props (WaveformBarTrackData) — each maps 1:1 to a data-wb-* attribute; arrays are JSON-encoded, and absent props emit no attribute:
| Prop | Type | Notes |
|---|---|---|
url |
string |
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". |
noDefaultIcons |
boolean |
false |
Suppress the injected play/pause SVGs. |
aria-label |
string |
auto | Auto-generated from title when absent. |
class |
string |
— | Appended to the base class wb-icon-swap. |
Slotted children replace the default icons. class, native listeners (onclick), and any other DOM attribute fall through to the rendered element.
<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), svelte (^5.0.0).
npm install @arraypress/waveform-playlist-svelte @arraypress/waveform-playlist @arraypress/waveform-player sveltepnpm add @arraypress/waveform-playlist-svelte @arraypress/waveform-playlist @arraypress/waveform-player svelteyarn add @arraypress/waveform-playlist-svelte @arraypress/waveform-playlist @arraypress/waveform-player sveltebun add @arraypress/waveform-playlist-svelte @arraypress/waveform-playlist @arraypress/waveform-player svelteImport both stylesheets, and import the player core for its global side effect — the playlist constructs new window.WaveformPlayer(...) for the active track:
<script> import '@arraypress/waveform-player'; // registers window.WaveformPlayer import '@arraypress/waveform-player/dist/waveform-player.css'; import '@arraypress/waveform-playlist/dist/waveform-playlist.css';</script>
<slot /><script lang="ts"> import { WaveformPlaylist } from '@arraypress/waveform-playlist-svelte';</script>
<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 class, style, id and other element attributes (fall through to the host).| 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. |
class |
string |
— | Appended to wfp-host. |
style |
string |
— | Inline CSS on the host. |
id |
string |
— | Forwarded to the host <div>. |
Common pass-through player props include waveformStyle, height, colorPreset, waveformColor, progressColor, showBPM, accessibleSeek, and autoplay — see Player → Options for the complete list.
The same lowercase lifecycle callback props as the player (onload, onplay, onpause, onend, ontimeupdate, onerror) are accepted and forwarded to the embedded player; like the player’s, they reach the live instance through reactive closures and never trigger a re-mount. For richer control, reach the embedded player directly through getPlayer().
bind:this<script lang="ts"> import { WaveformPlaylist } from '@arraypress/waveform-playlist-svelte'; let playlist: WaveformPlaylist; export let tracks;</script>
<WaveformPlaylist bind:this={playlist} {tracks} continuous /><button onclick={() => playlist.previousTrack()}>Prev</button><button onclick={() => playlist.nextTrack()}>Next</button><button onclick={() => playlist.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). |
getInstance |
() => WaveformPlaylist | null |
Escape hatch for anything else. |
Calls before the async import resolves are safe no-ops. The typed handle interface is exported as WaveformPlaylistExpose.
All three components are SSR / SvelteKit-prerender safe by construction: the host markup (and, for the playlist, the [data-track] children) renders on the server, and the browser-only core is dynamically import()ed inside $effect. Because effects never run during SSR, the audio + canvas surface only evaluates after hydration on the client — no import.meta.env.SSR guard or {#if browser} wrapper is needed.
Reserve layout space with the style attribute (e.g. style="min-height: 96px") to avoid a content shift before the waveform draws, and import each core’s CSS once in your root +layout.svelte (for the bar, remember the player-before-bar load order above).
Player options
Full inherited option surface — Player → Options.
Events
The waveformplayer:* DOM events — Player → Events.
React
The same wrappers, typed for React — React.
Installation
Bundler, CDN, and CSS setup — Getting started.