Player options
Full inherited option surface — Player → Options.
The @arraypress waveform family ships four 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 |
@arraypress/waveform-sounds-svelte |
<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. (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.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. |
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.
<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. With sounds, the list is rendered on the server by the core’s own DOM-free renderer, hydration keeps those nodes, and the runtime adopts them instead of rebuilding. See WaveformSounds for what the list does.
Peer deps: @arraypress/waveform-sounds (^0.1.0), @arraypress/waveform-player (^1.24.5), svelte (^5.20.0 — the component uses $props.id(), which 5.20 introduced).
npm install @arraypress/waveform-sounds-svelte @arraypress/waveform-sounds @arraypress/waveform-player sveltepnpm add @arraypress/waveform-sounds-svelte @arraypress/waveform-sounds @arraypress/waveform-player svelteyarn add @arraypress/waveform-sounds-svelte @arraypress/waveform-sounds @arraypress/waveform-player sveltebun add @arraypress/waveform-sounds-svelte @arraypress/waveform-sounds @arraypress/waveform-player svelteImport 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):
<script> import '@arraypress/waveform-player'; // registers window.WaveformPlayer, the engine import '@arraypress/waveform-player/styles.css'; import '@arraypress/waveform-sounds/styles.css';</script>
<slot /><script lang="ts"> import { WaveformSounds } from '@arraypress/waveform-sounds-svelte'; import pack from '$lib/sounds.json'; // written by waveform-gen --manifest</script>
<WaveformSounds sounds={pack.sounds} onplay={(sound) => console.log(sound.title)} />
<!-- or, fetched and built in the browser: --><WaveformSounds manifest="/sounds.json" />Write the JSON with waveform-gen --manifest — mind its --base-url, which has to match where the audio is served.
Every WaveformSoundsOptions key except the callbacks is a prop, typed from the core’s index.d.ts (WaveformSoundsProps) — 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. See Options for each one. class, id, style and any other attribute land on the host <div>.
The callbacks are lowercase props — onready, onplay, onpause, onend, onfilter, onerror — with the core callbacks’ arguments. They reach the live instance through closures, so a new handler never rebuilds the list. The core’s bubbling waveformsounds:* events fire from the host too.
idPrefix, else the host’s id, else a per-component id from $props.id() — unique, and identical on the server and during hydration.urlState is runtime-only: the server renders the unfiltered list and the runtime applies the address after hydration.sounds or an option rebuilds the list over a fresh render, so the new instance never adopts rows the old one re-sorted or hid. A class-only change doesn’t rebuild.Theme it through style — set --ws-surface (your page background) so the server-rendered first paint is right:
<WaveformSounds {sounds} style="--ws-surface: #0b0b0c" />bind:this<script lang="ts"> import { WaveformSounds } from '@arraypress/waveform-sounds-svelte'; let list: WaveformSounds; let { sounds } = $props();</script>
<WaveformSounds bind:this={list} {sounds} /><button onclick={() => list.setFilter({ type: 'Drum loops' })}>Drum loops</button><button onclick={() => list.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. |
setLoop |
(on: boolean) => void |
|
setFilter |
(patch: Partial<SoundsFilter>) => void |
Merged into the current filter. |
clearFilters |
() => void |
The sort stays. |
setSort |
(by: SoundsSort) => void |
|
showMore |
() => void |
|
getInstance |
() => WaveformSounds | null |
The core instance. |
Calls before the instance mounts are safe no-ops. The handle’s type is exported as WaveformSoundsExpose. bind:instance gives you the core instance itself, reactively (null until mounted).
All four components are SSR / SvelteKit-prerender safe by construction: the host markup (for the playlist, the [data-track] children; for the sound list, the whole list when given sounds) 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.