Player options
Full inherited option surface — Player → Options.
The @arraypress waveform family ships three Vue 3 wrappers, each a thin, typed component over its vanilla core:
| Package | Component(s) | Wraps |
|---|---|---|
@arraypress/waveform-player-vue |
<WaveformPlayer> |
@arraypress/waveform-player |
@arraypress/waveform-bar-vue |
<WaveformBar>, <WaveformBarTrigger> |
@arraypress/waveform-bar |
@arraypress/waveform-playlist-vue |
<WaveformPlaylist> |
@arraypress/waveform-playlist |
Every wrapper follows the same design:
defineComponent with a render function — no .vue compile step, dual ESM/CJS builds, and a hand-written .d.ts. Core options surface as props, lifecycle events as emits, and the instance as a template ref.WaveformPlayerOptions, not re-declared — so they never drift as the core evolves.import()ed inside onMounted, so the audio + canvas + fetch surface never runs during SSR / Nuxt.{ type: …, default: undefined }. An absent prop is never forwarded to the core, so the core’s own default applies — you only pass the props you want to override.<WaveformPlayer>vue and @arraypress/waveform-player are peer dependencies (Vue ^3.5.0; core player ^1.8.0).
npm install @arraypress/waveform-player-vue @arraypress/waveform-player vuepnpm add @arraypress/waveform-player-vue @arraypress/waveform-player vueyarn add @arraypress/waveform-player-vue @arraypress/waveform-player vuebun add @arraypress/waveform-player-vue @arraypress/waveform-player vueImport the core stylesheet once at your app entry:
// main.ts / app entryimport '@arraypress/waveform-player/dist/waveform-player.css';<script setup lang="ts">import { WaveformPlayer } from '@arraypress/waveform-player-vue';</script>
<template> <WaveformPlayer url="/audio/track.mp3" title="My Track" waveform-style="mirror" :waveform-color="['#fafafa', '#71717a']" show-bpm @timeupdate="(currentTime, duration) => { /* live progress */ }" /></template>src is the core’s shorthand alias for url (url wins if both are set). Provide one of the two.
Every core player option is accepted as a typed prop — url, src, audioMode, height, samples, barWidth, barSpacing, barRadius, colorPreset, gradient-array colours (waveformColor, progressColor), accessibleSeek, markers, showBPM, and so on. The prop type WaveformPlayerProps is Omit<WaveformPlayerOptions, …callbacks>, so it tracks the core automatically. See Player → Options for the full inherited surface.
Because every optional prop defaults to undefined, props you don’t pass are not forwarded and the core’s default wins.
The six core lifecycle callbacks surface as emits (not callback props) — each forwards the live WaveformPlayer instance:
<WaveformPlayer url="/audio/track.mp3" @load="(player) => console.log('loaded', player)" @play="() => console.log('playing')" @pause="() => console.log('paused')" @timeupdate="(currentTime, duration) => console.log(`${currentTime} / ${duration}`)" @end="() => console.log('finished')" @error="(err) => console.error('audio failed:', err)"/>| Emit | Payload | Notes |
|---|---|---|
@load |
(instance) |
Fired after the waveform is drawn. |
@play |
(instance) |
Playback started (both audio modes). |
@pause |
(instance) |
Playback paused (both modes). |
@end |
(instance) |
Track ended (external mode synthesizes it at progress ≥ 1). |
@timeupdate |
(currentTime, duration, instance) |
Progress tick. Same argument order in both modes. |
@error |
(error, instance) |
Load / decode / audio error. |
emit is stable, so listeners always reach the latest handler without tearing the player down. Only construction-time props (url, audioMode, visualization options, …) trigger a re-mount.
Grab the instance with a template ref typed as WaveformPlayerExpose. Methods are reached through defineExpose:
<script setup lang="ts">import { ref } from 'vue';import { WaveformPlayer, type WaveformPlayerExpose,} from '@arraypress/waveform-player-vue';
const player = ref<WaveformPlayerExpose>();</script>
<template> <WaveformPlayer ref="player" url="/audio/track.mp3" /> <button @click="player?.togglePlay()">Play / Pause</button> <button @click="player?.seekTo(60)">Jump to 1:00</button> <button @click="player?.loadTrack('/audio/next.mp3', 'Next Track', 'Artist')"> Load next </button></template>| 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.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. |
instance |
readonly WaveformPlayer | null |
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), Vue ^3.5.0.
npm install @arraypress/waveform-bar-vue @arraypress/waveform-bar @arraypress/waveform-player vuepnpm add @arraypress/waveform-bar-vue @arraypress/waveform-bar @arraypress/waveform-player vueyarn add @arraypress/waveform-bar-vue @arraypress/waveform-bar @arraypress/waveform-player vuebun add @arraypress/waveform-bar-vue @arraypress/waveform-bar @arraypress/waveform-player vue<script setup lang="ts">import { WaveformBar } from '@arraypress/waveform-bar-vue';</script>
<template> <RouterView /> <WaveformBar :config="{ persist: true, continuous: true, showQueue: true, actions: { favorite: { endpoint: '/api/favorites' }, cart: { endpoint: '/api/cart' }, }, }" /></template>On mount the component dynamically imports the core and calls window.WaveformBar.init(config). When the config’s structural shape changes it re-runs init() (idempotent — the library destroys and recreates internally); on unmount it calls destroy() so route changes and HMR reloads 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. |
class and style fall through to the persist host <div> (base class wb-host).
WaveformBarConfigBar-vue 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'), shuffle |
| UI toggles | showQueue, showPrevNext, showRepeat, showShuffle, showVolume, showMute, showTime, showTrackLink, showMeta, maxMeta (number) |
| Layout | mode ('waveform' | 'classic'), wide, position ('bottom' | 'top'), collapsible |
| 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? }) |
mode defaults to 'waveform' (the standard layout with a width-adjustable waveform). Set mode: 'classic' for the Spotify-style centre layout with a full-width seek bar. showShuffle adds a shuffle toggle to the transport cluster; shuffle starts with random queue advance on.
<WaveformBarTrigger> emits the data-wb-* attribute contract the bar scans for. It is polymorphic — a <button> by default — and any unlisted DOM attribute or native listener (@click, data-testid, role, …) falls through to the rendered element.
<script setup lang="ts">import { WaveformBarTrigger } from '@arraypress/waveform-bar-vue';</script>
<template> <!-- Default: a <button> with auto play/pause icons --> <WaveformBarTrigger url="/audio/track.mp3" id="track-42" title="Midnight Dreams" artist="The Wavelength" artwork="/img/cover.jpg" />
<!-- Append to the queue with custom content --> <WaveformBarTrigger mode="queue" url="/audio/track.mp3" title="Midnight Dreams"> + Queue </WaveformBarTrigger>
<!-- Wrap a whole card as the trigger --> <WaveformBarTrigger as="div" :url="track.url" no-default-icons> <article class="product-card">…</article> </WaveformBarTrigger></template>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 |
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". |
noDefaultIcons |
boolean |
false |
Suppress the injected play/pause SVGs. |
aria-label |
string |
auto | Falls through to the element; auto-generated from title when absent. |
| default slot | — | — | Custom content; overrides the default icons. |
class is merged with the base class wb-icon-swap.
<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), Vue ^3.5.0.
npm install @arraypress/waveform-playlist-vue @arraypress/waveform-playlist @arraypress/waveform-player vuepnpm add @arraypress/waveform-playlist-vue @arraypress/waveform-playlist @arraypress/waveform-player vueyarn add @arraypress/waveform-playlist-vue @arraypress/waveform-playlist @arraypress/waveform-player vuebun add @arraypress/waveform-playlist-vue @arraypress/waveform-playlist @arraypress/waveform-player vueImport 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.WaveformPlayer<script setup lang="ts">import { WaveformPlaylist } from '@arraypress/waveform-playlist-vue';
const 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' }, ], },];</script>
<template> <WaveformPlaylist continuous waveform-style="bars" :tracks="tracks" /></template>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 falling 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. |
Common pass-through player props include waveformStyle, height, colorPreset, waveformColor, progressColor, showBPM, accessibleSeek, and autoplay — see Player → Options for the complete list.
Grab the instance with a template ref typed as WaveformPlaylistExpose:
<script setup lang="ts">import { ref } from 'vue';import { WaveformPlaylist, type WaveformPlaylistExpose,} from '@arraypress/waveform-playlist-vue';
const playlist = ref<WaveformPlaylistExpose>();</script>
<template> <WaveformPlaylist ref="playlist" :tracks="tracks" continuous /> <button @click="playlist?.previousTrack()">Prev</button> <button @click="playlist?.nextTrack()">Next</button> <button @click="playlist?.selectTrack(0)">First</button></template>| 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.
All three components are SSR / Nuxt 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 onMounted — which runs only on the client, never during server render. The interactive UI hydrates in once the library loads.
No extra guarding is needed for the imperative path: keep instantiation inside onMounted (the wrappers already do), never at module top level. Reserve layout space with style (e.g. :style="{ minHeight: '96px' }") to avoid a content shift before the waveform draws.
For Nuxt, you can render the components directly. To skip server rendering entirely and sidestep any hydration mismatch, wrap them in <ClientOnly>:
<template> <ClientOnly> <WaveformPlayer url="/audio/track.mp3" title="Midnight Drive" /> <template #fallback> <div class="waveform-placeholder" style="min-height: 96px" /> </template> </ClientOnly></template>
<script setup lang="ts">import { WaveformPlayer } from '@arraypress/waveform-player-vue';</script>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.