Skip to content

Features

mode picks the bar’s layout:

  • 'waveform' (default) — a full-width waveform scrubber spanning the bar. wide: true lifts the 1400px content cap for an edge-to-edge waveform.
  • 'classic' — a Spotify-style three-column layout: now-playing (artwork + title / artist) on the left, transport controls centred above a compact full-width seek bar, and secondary controls (volume, queue, share) on the right. Always full-width, so wide doesn’t apply.

Both modes share the same queue, transport, persistence and event surface — only the presentation differs. See Configuration for the full option list.

When showQueue is true, the bar renders a queue button and a .wb-queue-panel (appended to document.body). The panel groups entries into Now Playing / Up Next / Previously Played, with per-row remove buttons and a clear-all action. Build it from data attributes (data-wb-queue) or the API (addToQueue, removeFromQueue, clearQueue, skipTo). The current track can never be removed; clearQueue() keeps it.

Each row’s number and title/artist sit inside a <button class="wb-queue-skip">, so the queue is keyboard-operable: Tab to a row and press Enter to skip to it (focus stays in the panel), the remove button appears on keyboard focus, and the current row carries aria-current (1.12.0+).

The volume wrapper renders when showMute or showVolume is true. The mute button toggles, and the popup exposes a vertical slider. Volume and mute persist to localStorage (<storageKey>-vol) when persist is on, so they survive navigation; with persist: false nothing is written (1.12.0+). setVolume() clamps to 0..1, updates the mute flag, persists (when persist is on), and emits volumechange + onVolumeChange.

repeat: 'off' | 'all' | 'one'. 'all' loops the queue (next() wraps to index 0, previous() wraps to the last track); 'one' loops the current track. Cycle with the repeat button or cycleRepeat(), or set directly with setRepeat(). Every change emits repeatchange. The repeat config applies even with showRepeat: false (1.12.0+). The button sets aria-pressed (on for 'all'/'one') and an aria-label naming the mode (Repeat: Off/All/One); the favorite and shuffle buttons also expose aria-pressed.

Set showShuffle: true to add a shuffle toggle beside the repeat control. With shuffle on, next() and continuous auto-advance pick a random track not yet played in the current shuffle pass instead of the next in order; shuffle: true starts it enabled. Once every track has played, shuffle stops with repeat 'off', or starts a new pass under repeat 'all' — and the Next button’s disabled state follows the same rule (1.12.0+; earlier versions drew from the whole queue, played tracks included, and never stopped). Turning shuffle on starts a fresh pass from the current track. Toggle at runtime with toggleShuffle() / setShuffle() — each change emits shufflechange + onShuffleChange, and the button reflects state and tucks away with the collapsed pill.

Favorites and cart power the heart and cart buttons (which render only when actions.favorite / actions.cart are configured), plus the .wb-favorited / .wb-in-cart page classes.

  • Favorites persist to localStorage (<storageKey>-favs, an id array) when persist is on. Seed server state with data-wb-favorited="true" — it is authoritative over localStorage on init.
  • Cart is an in-memory Set, never persisted. Seed it from data-wb-in-cart="true"; your server remains the source of truth. There is no removal API.

Both call actions.* (if configured) and sync page attributes/classes across all matching trigger elements. The methods work even with the buttons hidden.

Attach a JSON array of markers to a track to drive DJ mode — as playback crosses each marker boundary, the bar updates its displayed title/artist/artwork and emits markerchange. Each marker’s time is coerced to a finite number; non-numeric entries are dropped.

<button data-wb-play
data-url="mix.mp3"
data-title="Live Mix"
data-wb-markers='[
{ "time": 0, "label": "Intro", "title": "Opener", "artist": "DJ A", "bpm": 124 },
{ "time": 184, "label": "Drop", "title": "Banger", "artist": "DJ B", "color": "#f43f5e" },
{ "time": 372, "label": "Outro", "title": "Closer", "artist": "DJ A" }
]'>
Play Mix
</button>

Marker fields: time (required), label, title, artist, artwork, bpm, key, color. Markers without a color use the markerColor config default. Navigate from script with seekToMarker(index) or seekToMarkerByLabel(label). The active section is reflected on the player’s marker elements via .wb-marker-active.

With share: true, the bar renders a copy-link button. Clicking it copies the current page URL with the playback timestamp and track identity appended, then emits waveformbar:share. On init, the bar reads these same params and restores the shared track + position.

Param Meaning
?<shareParam> (default wt) Timestamp in seconds, e.g. ?wt=42.
wid Track id.
wu Track URL (scheme-guarded by isSafeHref).
wtitle Title.
wartist Artist.

A bare ?wt= with no track identity seeks whatever track loads first. A wid/wu cold-loads that specific track, paused at the timestamp — applied after queue restore, so a shared track always wins.

When persist is true (the default), state is split across two stores under storageKey (default 'waveform-bar'):

Store Key Holds
sessionStorage <key> { queue, currentIndex, position, isPlaying }
sessionStorage <key>-collapsed Collapsed pill state.
localStorage <key>-vol { v, m, b } — volume, mute, base volume.
localStorage <key>-favs Favorited id array.

The exact position is saved on beforeunload, pagehide and whenever the page becomes hidden (1.12.0+) — iOS Safari doesn’t reliably fire beforeunload. With persist: false, nothing is written to either store (1.12.0+; earlier versions still saved volume, mute and favorites).

On restore, autoResume resumes playback if the saved state was playing (falling back to paused if autoplay is blocked). Use distinct storageKey values to run independent bars across different site sections.

WaveformBar.init({
persist: true,
storageKey: 'shop-player',
autoResume: true
});