@arraypress/waveform-bar 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +732 -0
- package/dist/waveform-bar.css +943 -0
- package/dist/waveform-bar.esm.js +1389 -0
- package/dist/waveform-bar.js +1387 -0
- package/dist/waveform-bar.min.css +1 -0
- package/dist/waveform-bar.min.js +51 -0
- package/package.json +60 -0
- package/src/css/waveform-bar.css +943 -0
- package/src/js/actions.js +35 -0
- package/src/js/core.js +1271 -0
- package/src/js/dom.js +84 -0
- package/src/js/icons.js +24 -0
- package/src/js/index.js +20 -0
- package/src/js/queue.js +113 -0
- package/src/js/storage.js +92 -0
- package/src/js/utils.js +74 -0
package/README.md
ADDED
|
@@ -0,0 +1,732 @@
|
|
|
1
|
+
# WaveformBar
|
|
2
|
+
|
|
3
|
+
A persistent bottom audio player bar for websites. Built
|
|
4
|
+
on [WaveformPlayer](https://github.com/arraypress/waveform-player), it provides site-wide playback with queue
|
|
5
|
+
management, volume control, favorites, cart integration, DJ mode with markers, repeat modes, session persistence, and
|
|
6
|
+
page state syncing.
|
|
7
|
+
|
|
8
|
+
Think Spotify's bottom player โ but lightweight, zero-config, and works on any site.
|
|
9
|
+
|
|
10
|
+
**[Live Demo](https://waveformplayer.com/bar)** | *
|
|
11
|
+
*[NPM Package](https://www.npmjs.com/package/@arraypress/waveform-bar)**
|
|
12
|
+
|
|
13
|
+

|
|
14
|
+

|
|
15
|
+
|
|
16
|
+
## Features
|
|
17
|
+
|
|
18
|
+
- ๐ต **Persistent Player** โ Fixed bottom bar that stays while users browse
|
|
19
|
+
- ๐ **Queue Management** โ Add, remove, skip, replay, clear. Now Playing/Up Next/Previously Played sections
|
|
20
|
+
- ๐ **Volume Control** โ Popup vertical slider + mute toggle, persisted in localStorage
|
|
21
|
+
- ๐ **Repeat Modes** โ Off, repeat all (loops queue), repeat one (loops current track)
|
|
22
|
+
- ๐ท๏ธ **Metadata Tags** โ Display BPM, key, or custom data inline
|
|
23
|
+
- โค๏ธ **Favorites** โ Toggle favorites with server-side seeding via `data-wb-favorited`
|
|
24
|
+
- ๐ **Cart Integration** โ Add to cart with REST callbacks and DOM events
|
|
25
|
+
- ๐ง **DJ Mode** โ Markers with title/artist fields that update the bar as a mix plays
|
|
26
|
+
- ๐ **Marker Navigation** โ Seek to markers by index or label via JavaScript API
|
|
27
|
+
- ๐พ **Session Persistence** โ Queue, position, and playback state survive page navigations
|
|
28
|
+
- ๐ **Product Links** โ Click track info to navigate to a product page
|
|
29
|
+
- ๐ **Page State Sync** โ Trigger elements get CSS classes reflecting play state
|
|
30
|
+
- ๐๏ธ **Data Attribute API** โ Just add `data-wb-play` to any element
|
|
31
|
+
- ๐จ **Helper CSS** โ Icon swaps, equalizer bars, card highlights, favorite/cart visibility
|
|
32
|
+
- ๐ผ๏ธ **Default Artwork** โ Configurable fallback artwork when tracks have no cover image
|
|
33
|
+
- ๐ **Auto-Scroll Text** โ Long titles/artists bounce-scroll smoothly
|
|
34
|
+
- ๐ **Theme Support** โ Dark/light via CSS custom properties
|
|
35
|
+
- ๐ฑ **Responsive** โ Stacked layout on mobile, full-width queue panel
|
|
36
|
+
- ๐ชถ **Lightweight** โ ~6KB gzipped (JS + CSS)
|
|
37
|
+
|
|
38
|
+
## Installation
|
|
39
|
+
|
|
40
|
+
### CDN
|
|
41
|
+
|
|
42
|
+
```html
|
|
43
|
+
<!-- WaveformPlayer (required dependency) -->
|
|
44
|
+
<link rel="stylesheet" href="https://unpkg.com/@arraypress/waveform-player@latest/dist/waveform-player.css">
|
|
45
|
+
<script src="https://unpkg.com/@arraypress/waveform-player@latest/dist/waveform-player.js"></script>
|
|
46
|
+
|
|
47
|
+
<!-- WaveformBar -->
|
|
48
|
+
<link rel="stylesheet" href="https://unpkg.com/@arraypress/waveform-bar@latest/dist/waveform-bar.css">
|
|
49
|
+
<script src="https://unpkg.com/@arraypress/waveform-bar@latest/dist/waveform-bar.js"></script>
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### NPM
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
npm install @arraypress/waveform-player @arraypress/waveform-bar
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Quick Start
|
|
59
|
+
|
|
60
|
+
```html
|
|
61
|
+
<!-- 1. A play button โ that's all you need -->
|
|
62
|
+
<button data-wb-play
|
|
63
|
+
data-url="song.mp3"
|
|
64
|
+
data-title="My Song"
|
|
65
|
+
data-artist="Artist Name">
|
|
66
|
+
โถ Play
|
|
67
|
+
</button>
|
|
68
|
+
|
|
69
|
+
<!-- 2. Initialize -->
|
|
70
|
+
<script>
|
|
71
|
+
WaveformBar.init();
|
|
72
|
+
</script>
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Click the button โ the bar slides up from the bottom โ music plays. Add more buttons anywhere on the page and they all
|
|
76
|
+
share the same player bar.
|
|
77
|
+
|
|
78
|
+
## Data Attributes
|
|
79
|
+
|
|
80
|
+
### Play Trigger (`data-wb-play`)
|
|
81
|
+
|
|
82
|
+
Add to any HTML element to make it play a track when clicked.
|
|
83
|
+
|
|
84
|
+
```html
|
|
85
|
+
<!-- Simple button -->
|
|
86
|
+
<button data-wb-play
|
|
87
|
+
data-url="audio/track.mp3"
|
|
88
|
+
data-title="Track Title"
|
|
89
|
+
data-artist="Artist Name">
|
|
90
|
+
Play
|
|
91
|
+
</button>
|
|
92
|
+
|
|
93
|
+
<!-- Card with full metadata -->
|
|
94
|
+
<div data-wb-play
|
|
95
|
+
data-url="audio/beat.mp3"
|
|
96
|
+
data-id="product-42"
|
|
97
|
+
data-title="Trap Beat"
|
|
98
|
+
data-artist="Producer"
|
|
99
|
+
data-bpm="140"
|
|
100
|
+
data-key="Cm"
|
|
101
|
+
data-artwork="covers/beat.jpg"
|
|
102
|
+
data-link="/beats/trap-beat"
|
|
103
|
+
data-wb-favorited="true">
|
|
104
|
+
<img src="covers/beat.jpg">
|
|
105
|
+
<h3>Trap Beat</h3>
|
|
106
|
+
</div>
|
|
107
|
+
|
|
108
|
+
<!-- Table row -->
|
|
109
|
+
<tr data-wb-play
|
|
110
|
+
data-url="samples/kick.wav"
|
|
111
|
+
data-title="808 Kick"
|
|
112
|
+
data-artist="Drum Kit Vol. 3"
|
|
113
|
+
data-bpm="128">
|
|
114
|
+
<td>808 Kick</td>
|
|
115
|
+
<td>128 BPM</td>
|
|
116
|
+
</tr>
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Queue Trigger (`data-wb-queue`)
|
|
120
|
+
|
|
121
|
+
Add to any element to queue a track without immediately playing it.
|
|
122
|
+
|
|
123
|
+
```html
|
|
124
|
+
|
|
125
|
+
<button data-wb-queue
|
|
126
|
+
data-url="audio/track.mp3"
|
|
127
|
+
data-title="Queued Track"
|
|
128
|
+
data-artist="Artist">
|
|
129
|
+
+ Add to Queue
|
|
130
|
+
</button>
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Pre-Generated Waveform Data
|
|
134
|
+
|
|
135
|
+
Skip client-side audio analysis by providing pre-generated waveform peaks.
|
|
136
|
+
Use [waveform-gen](https://github.com/arraypress/waveform-gen) to generate the data.
|
|
137
|
+
|
|
138
|
+
```html
|
|
139
|
+
|
|
140
|
+
<div data-wb-play
|
|
141
|
+
data-url="song.mp3"
|
|
142
|
+
data-title="My Song"
|
|
143
|
+
data-wb-waveform='[0.12,0.45,0.89,0.34,0.67,0.92,0.15,0.78]'>
|
|
144
|
+
</div>
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
The waveform displays instantly on load without downloading and decoding the audio file.
|
|
148
|
+
|
|
149
|
+
### DJ Mode Markers
|
|
150
|
+
|
|
151
|
+
Add time-stamped markers with title/artist fields. As the mix plays, the bar updates the displayed track name, artist,
|
|
152
|
+
and metadata at each marker boundary.
|
|
153
|
+
|
|
154
|
+
```html
|
|
155
|
+
|
|
156
|
+
<div data-wb-play
|
|
157
|
+
data-url="audio/guestmix.mp3"
|
|
158
|
+
data-title="Guest Mix"
|
|
159
|
+
data-artist="Various Artists"
|
|
160
|
+
data-wb-markers='[
|
|
161
|
+
{"time": 0, "label": "Intro", "title": "Opening Track", "artist": "DJ One"},
|
|
162
|
+
{"time": 180, "label": "Drop", "title": "Big Tune", "artist": "Producer X", "bpm": "174", "key": "Am"},
|
|
163
|
+
{"time": 360, "label": "Chill", "title": "Downtempo", "artist": "Ambient Artist"}
|
|
164
|
+
]'>
|
|
165
|
+
Play Mix
|
|
166
|
+
</div>
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Markers can include: `time` (seconds, required), `label` (required), `title`, `artist`, `artwork`, `bpm`, `key`,
|
|
170
|
+
`color`.
|
|
171
|
+
|
|
172
|
+
The active marker on the waveform gently pulses to indicate the current section.
|
|
173
|
+
|
|
174
|
+
### Attribute Reference
|
|
175
|
+
|
|
176
|
+
| Attribute | Description |
|
|
177
|
+
|---------------------|------------------------------------------------------------|
|
|
178
|
+
| `data-wb-play` | Makes element a play trigger (click to play) |
|
|
179
|
+
| `data-wb-queue` | Makes element a queue trigger (click to add to queue) |
|
|
180
|
+
| `data-url` | Audio file URL (required) |
|
|
181
|
+
| `data-id` | Unique track identifier (defaults to URL) |
|
|
182
|
+
| `data-title` | Track title |
|
|
183
|
+
| `data-artist` | Artist or subtitle |
|
|
184
|
+
| `data-artwork` | Album artwork URL |
|
|
185
|
+
| `data-album` | Album name (for Media Session API) |
|
|
186
|
+
| `data-link` | URL to navigate when clicking track info in the bar |
|
|
187
|
+
| `data-duration` | Display duration string |
|
|
188
|
+
| `data-bpm` | BPM value โ displayed as a tag in the bar |
|
|
189
|
+
| `data-key` | Musical key โ displayed as a tag in the bar |
|
|
190
|
+
| `data-meta` | JSON object of custom metadata (e.g. `'{"genre":"Trap"}'`) |
|
|
191
|
+
| `data-wb-waveform` | Pre-generated waveform peaks JSON array |
|
|
192
|
+
| `data-wb-markers` | JSON array of marker objects for DJ mode |
|
|
193
|
+
| `data-wb-favorited` | `"true"` to pre-set favorite state (server-side seeding) |
|
|
194
|
+
| `data-wb-in-cart` | `"true"` to pre-set cart state (server-side seeding) |
|
|
195
|
+
|
|
196
|
+
All attributes also accept `data-wb-` prefixed versions (`data-wb-url`, `data-wb-title`, etc.) to avoid conflicts with
|
|
197
|
+
other libraries.
|
|
198
|
+
|
|
199
|
+
## Configuration
|
|
200
|
+
|
|
201
|
+
```javascript
|
|
202
|
+
WaveformBar.init({
|
|
203
|
+
// Persistence
|
|
204
|
+
persist: true, // Save queue/position to sessionStorage
|
|
205
|
+
autoResume: true, // Auto-resume playback after page navigation
|
|
206
|
+
|
|
207
|
+
// Playback
|
|
208
|
+
continuous: true, // Auto-advance to next track in queue
|
|
209
|
+
repeat: 'off', // Repeat mode: 'off', 'all', 'one'
|
|
210
|
+
volume: 1, // Initial volume (0-1)
|
|
211
|
+
|
|
212
|
+
// UI visibility
|
|
213
|
+
showQueue: true, // Show queue toggle button
|
|
214
|
+
showPrevNext: true, // Show prev/next skip buttons
|
|
215
|
+
showRepeat: true, // Show repeat mode button
|
|
216
|
+
showVolume: true, // Show volume popup slider
|
|
217
|
+
showMute: true, // Show mute button
|
|
218
|
+
showTime: true, // Show elapsed/total time display
|
|
219
|
+
showTrackLink: true, // Make track info clickable (navigates to data-link)
|
|
220
|
+
showMeta: true, // Show metadata tags (BPM, key, custom)
|
|
221
|
+
maxMeta: 3, // Max number of metadata tags to display
|
|
222
|
+
|
|
223
|
+
// Artwork
|
|
224
|
+
defaultArtwork: null, // URL to fallback artwork (shown when track has no artwork)
|
|
225
|
+
|
|
226
|
+
// Waveform display (passed to WaveformPlayer)
|
|
227
|
+
waveformStyle: 'mirror', // 'bars', 'mirror', 'line', 'blocks', 'dots', 'seekbar'
|
|
228
|
+
waveformHeight: 32, // Waveform height in pixels
|
|
229
|
+
barWidth: 2, // Width of waveform bars
|
|
230
|
+
barSpacing: 0, // Space between waveform bars
|
|
231
|
+
waveformColor: null, // Waveform color (null = auto-detect from theme)
|
|
232
|
+
progressColor: null, // Progress color (null = auto-detect from theme)
|
|
233
|
+
markerColor: 'rgba(255, 255, 255, 0.25)', // Default marker line color
|
|
234
|
+
|
|
235
|
+
// Storage
|
|
236
|
+
storageKey: 'waveform-bar', // Key prefix for sessionStorage/localStorage
|
|
237
|
+
|
|
238
|
+
// Server-side actions (REST callbacks)
|
|
239
|
+
actions: {
|
|
240
|
+
favorite: {
|
|
241
|
+
endpoint: '/api/favorites', // POST URL
|
|
242
|
+
method: 'POST'
|
|
243
|
+
},
|
|
244
|
+
cart: {
|
|
245
|
+
endpoint: '/api/cart', // POST URL
|
|
246
|
+
method: 'POST'
|
|
247
|
+
}
|
|
248
|
+
},
|
|
249
|
+
|
|
250
|
+
// Callbacks
|
|
251
|
+
onPlay: (track) => {
|
|
252
|
+
},
|
|
253
|
+
onPause: (track) => {
|
|
254
|
+
},
|
|
255
|
+
onTrackChange: (track, index) => {
|
|
256
|
+
},
|
|
257
|
+
onQueueChange: (queue, currentIndex) => {
|
|
258
|
+
},
|
|
259
|
+
onVolumeChange: (volume) => {
|
|
260
|
+
},
|
|
261
|
+
onFavorite: (track, favorited) => {
|
|
262
|
+
},
|
|
263
|
+
onCart: (track) => {
|
|
264
|
+
}
|
|
265
|
+
});
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
## JavaScript API
|
|
269
|
+
|
|
270
|
+
### Playback
|
|
271
|
+
|
|
272
|
+
```javascript
|
|
273
|
+
// Play a track object
|
|
274
|
+
WaveformBar.play({
|
|
275
|
+
url: 'audio/song.mp3',
|
|
276
|
+
title: 'My Song',
|
|
277
|
+
artist: 'Artist',
|
|
278
|
+
bpm: '128',
|
|
279
|
+
key: 'Am',
|
|
280
|
+
artwork: 'cover.jpg',
|
|
281
|
+
link: '/products/my-song'
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
// Play by URL shorthand
|
|
285
|
+
WaveformBar.play('audio/song.mp3');
|
|
286
|
+
|
|
287
|
+
// Playback controls
|
|
288
|
+
WaveformBar.togglePlay();
|
|
289
|
+
WaveformBar.pause();
|
|
290
|
+
WaveformBar.next(); // Next track (wraps if repeat: 'all')
|
|
291
|
+
WaveformBar.previous(); // Previous track (restarts if >3s in)
|
|
292
|
+
WaveformBar.skipTo(3); // Jump to queue index 3
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
### Repeat
|
|
296
|
+
|
|
297
|
+
```javascript
|
|
298
|
+
WaveformBar.cycleRepeat(); // Cycles: off โ all โ one โ off
|
|
299
|
+
WaveformBar.setRepeat('all'); // Set directly: 'off', 'all', 'one'
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
- **off** โ Stops at end of queue
|
|
303
|
+
- **all** โ Loops back to first track after last; prev/next wrap around
|
|
304
|
+
- **one** โ Replays the current track indefinitely
|
|
305
|
+
|
|
306
|
+
### Markers / DJ Mode
|
|
307
|
+
|
|
308
|
+
```javascript
|
|
309
|
+
// Seek to marker by index (0-based) on current track
|
|
310
|
+
WaveformBar.seekToMarker(3);
|
|
311
|
+
|
|
312
|
+
// Seek to marker by label name on current track
|
|
313
|
+
WaveformBar.seekToMarkerByLabel('Horizon');
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
Both methods auto-play if the player is paused. They only work on the currently loaded track's markers โ if the track
|
|
317
|
+
with markers isn't loaded, they silently return.
|
|
318
|
+
|
|
319
|
+
### Volume
|
|
320
|
+
|
|
321
|
+
```javascript
|
|
322
|
+
WaveformBar.setVolume(0.5); // Set to 50%
|
|
323
|
+
WaveformBar.getVolume(); // Returns 0.5
|
|
324
|
+
WaveformBar.toggleMute();
|
|
325
|
+
WaveformBar.isMutedState(); // true/false
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
Volume and mute state persist in localStorage across sessions.
|
|
329
|
+
|
|
330
|
+
### Queue
|
|
331
|
+
|
|
332
|
+
```javascript
|
|
333
|
+
// Add to end of queue
|
|
334
|
+
WaveformBar.addToQueue({url: 'track.mp3', title: 'Next Up', artist: 'Someone'});
|
|
335
|
+
|
|
336
|
+
// Remove by index
|
|
337
|
+
WaveformBar.removeFromQueue(2);
|
|
338
|
+
|
|
339
|
+
// Replay a previously played track (by URL)
|
|
340
|
+
WaveformBar.replay('audio/old-track.mp3');
|
|
341
|
+
|
|
342
|
+
// Clear upcoming tracks
|
|
343
|
+
WaveformBar.clearQueue();
|
|
344
|
+
|
|
345
|
+
// Clear play history
|
|
346
|
+
WaveformBar.clearHistory();
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
The queue panel shows three sections: **Now Playing** (click to toggle play/pause), **Up Next**, and **Previously Played
|
|
350
|
+
**. Tracks can be removed individually from the queue via the ร button on hover.
|
|
351
|
+
|
|
352
|
+
### Favorites
|
|
353
|
+
|
|
354
|
+
```javascript
|
|
355
|
+
WaveformBar.toggleFavorite(); // Toggle current track's favorite state
|
|
356
|
+
WaveformBar.isFavorited('beat-001'); // Check by track ID
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
Favorites persist in localStorage. Server-side state can be seeded via `data-wb-favorited="true"` on trigger elements โ
|
|
360
|
+
this is authoritative and overrides localStorage on page load.
|
|
361
|
+
|
|
362
|
+
When a track is favorited/unfavorited, the `.wb-favorited` class is toggled on all matching trigger elements on the
|
|
363
|
+
page.
|
|
364
|
+
|
|
365
|
+
### Cart
|
|
366
|
+
|
|
367
|
+
```javascript
|
|
368
|
+
WaveformBar.addToCart(); // Add current track to cart
|
|
369
|
+
WaveformBar.isInCart('beat-001'); // Check by track ID
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
Cart state is NOT persisted to localStorage โ it's server-authoritative. Seed from your server via
|
|
373
|
+
`data-wb-in-cart="true"`. The `.wb-in-cart` class is toggled on matching trigger elements.
|
|
374
|
+
|
|
375
|
+
If an `actions.cart.endpoint` is configured, a POST request fires automatically with the track data.
|
|
376
|
+
|
|
377
|
+
### State
|
|
378
|
+
|
|
379
|
+
```javascript
|
|
380
|
+
WaveformBar.getCurrentTrack(); // { url, title, artist, bpm, key, ... }
|
|
381
|
+
WaveformBar.isCurrentlyPlaying('song.mp3'); // true if this URL is actively playing
|
|
382
|
+
WaveformBar.isCurrentTrack('song.mp3'); // true if current (playing or paused)
|
|
383
|
+
WaveformBar.getQueue(); // Full queue array
|
|
384
|
+
WaveformBar.getHistory(); // Previously played tracks
|
|
385
|
+
WaveformBar.getPlayer(); // Underlying WaveformPlayer instance
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
### UI
|
|
389
|
+
|
|
390
|
+
```javascript
|
|
391
|
+
WaveformBar.show(); // Show the bar
|
|
392
|
+
WaveformBar.hide(); // Hide the bar
|
|
393
|
+
WaveformBar.toggleQueuePanel(); // Toggle queue panel visibility
|
|
394
|
+
WaveformBar.toggleVolumePopup(); // Toggle volume popup
|
|
395
|
+
WaveformBar.closeQueuePanel();
|
|
396
|
+
WaveformBar.closeVolumePopup();
|
|
397
|
+
WaveformBar.destroy(); // Remove bar and clean up
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
## DOM Events
|
|
401
|
+
|
|
402
|
+
All events bubble from the bar element and are prefixed with `waveformbar:`.
|
|
403
|
+
|
|
404
|
+
```javascript
|
|
405
|
+
document.addEventListener('waveformbar:play', (e) => {
|
|
406
|
+
console.log('Playing:', e.detail.track);
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
document.addEventListener('waveformbar:trackchange', (e) => {
|
|
410
|
+
console.log('Track changed:', e.detail.track, 'Index:', e.detail.index);
|
|
411
|
+
});
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
| Event | Detail |
|
|
415
|
+
|----------------------------|----------------------------|
|
|
416
|
+
| `waveformbar:play` | `{ track }` |
|
|
417
|
+
| `waveformbar:pause` | `{ track }` |
|
|
418
|
+
| `waveformbar:trackchange` | `{ track, index }` |
|
|
419
|
+
| `waveformbar:markerchange` | `{ marker, index, track }` |
|
|
420
|
+
| `waveformbar:favorite` | `{ track, favorited }` |
|
|
421
|
+
| `waveformbar:cart` | `{ track }` |
|
|
422
|
+
| `waveformbar:queuechange` | `{ queue, currentIndex }` |
|
|
423
|
+
| `waveformbar:volumechange` | `{ volume }` |
|
|
424
|
+
| `waveformbar:repeatchange` | `{ mode }` |
|
|
425
|
+
|
|
426
|
+
## Page State Sync
|
|
427
|
+
|
|
428
|
+
WaveformBar automatically adds CSS classes to trigger elements on the page:
|
|
429
|
+
|
|
430
|
+
| Class | Applied When |
|
|
431
|
+
|-----------------|---------------------------------------------------|
|
|
432
|
+
| `.wb-current` | The element's track URL matches the current track |
|
|
433
|
+
| `.wb-playing` | The element's track is actively playing |
|
|
434
|
+
| `.wb-favorited` | The element's track is favorited |
|
|
435
|
+
| `.wb-in-cart` | The element's track is in the cart |
|
|
436
|
+
|
|
437
|
+
These update in real-time as the user interacts with the bar.
|
|
438
|
+
|
|
439
|
+
## Helper CSS Classes
|
|
440
|
+
|
|
441
|
+
Ready-made utility classes for common UI patterns. Add them to your trigger elements.
|
|
442
|
+
|
|
443
|
+
### Icon Swap (`wb-icon-swap`)
|
|
444
|
+
|
|
445
|
+
Swaps play/pause content automatically based on playback state.
|
|
446
|
+
|
|
447
|
+
```html
|
|
448
|
+
|
|
449
|
+
<button data-wb-play data-url="song.mp3" class="wb-icon-swap">
|
|
450
|
+
<span class="wb-show-play">โถ Play</span>
|
|
451
|
+
<span class="wb-show-pause">โธ Playing</span>
|
|
452
|
+
</button>
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
### Equalizer Bars (`wb-eq-bars`)
|
|
456
|
+
|
|
457
|
+
Animated equalizer bars that activate when the track is playing.
|
|
458
|
+
|
|
459
|
+
```html
|
|
460
|
+
|
|
461
|
+
<tr data-wb-play data-url="song.mp3">
|
|
462
|
+
<td>
|
|
463
|
+
<span class="wb-eq-bars">
|
|
464
|
+
<span></span><span></span><span></span><span></span>
|
|
465
|
+
</span>
|
|
466
|
+
</td>
|
|
467
|
+
<td>Track Title</td>
|
|
468
|
+
</tr>
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
### Card Highlight (`wb-card-highlight`)
|
|
472
|
+
|
|
473
|
+
Adds accent border and glow when the element's track is current.
|
|
474
|
+
|
|
475
|
+
```html
|
|
476
|
+
|
|
477
|
+
<div data-wb-play data-url="song.mp3"
|
|
478
|
+
class="wb-card-highlight"
|
|
479
|
+
style="border: 1px solid transparent; border-radius: 8px;">
|
|
480
|
+
<h3>Track Title</h3>
|
|
481
|
+
</div>
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
### Accent Text (`wb-accent-current`)
|
|
485
|
+
|
|
486
|
+
Colors text with the accent color when the parent track is current.
|
|
487
|
+
|
|
488
|
+
```html
|
|
489
|
+
|
|
490
|
+
<div data-wb-play data-url="song.mp3">
|
|
491
|
+
<span class="wb-accent-current">Track Title</span>
|
|
492
|
+
</div>
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
### Pulse Animation (`wb-pulse-playing`)
|
|
496
|
+
|
|
497
|
+
Subtle opacity pulse on playing elements.
|
|
498
|
+
|
|
499
|
+
```html
|
|
500
|
+
|
|
501
|
+
<div data-wb-play data-url="song.mp3" class="wb-pulse-playing">
|
|
502
|
+
<img src="cover.jpg">
|
|
503
|
+
</div>
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
### Favorite/Cart Visibility
|
|
507
|
+
|
|
508
|
+
Show or hide content based on favorite/cart state.
|
|
509
|
+
|
|
510
|
+
```html
|
|
511
|
+
|
|
512
|
+
<div data-wb-play data-url="song.mp3" data-id="beat-001">
|
|
513
|
+
<button>
|
|
514
|
+
<span class="wb-hide-if-fav">โก Save</span>
|
|
515
|
+
<span class="wb-show-if-fav">โค Saved</span>
|
|
516
|
+
</button>
|
|
517
|
+
<button>
|
|
518
|
+
<span class="wb-hide-if-cart">๐ Add to Cart</span>
|
|
519
|
+
<span class="wb-show-if-cart">โ In Cart</span>
|
|
520
|
+
</button>
|
|
521
|
+
</div>
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
## Persistence
|
|
525
|
+
|
|
526
|
+
WaveformBar uses two storage mechanisms:
|
|
527
|
+
|
|
528
|
+
**sessionStorage** (cleared when browser closes):
|
|
529
|
+
|
|
530
|
+
- Queue contents and order
|
|
531
|
+
- Current track index
|
|
532
|
+
- Playback position
|
|
533
|
+
- Playing state
|
|
534
|
+
|
|
535
|
+
**localStorage** (persists across sessions):
|
|
536
|
+
|
|
537
|
+
- Volume level
|
|
538
|
+
- Mute state
|
|
539
|
+
- Favorite track IDs
|
|
540
|
+
|
|
541
|
+
When a user navigates between pages, the bar restores the queue, seeks to the saved position, and optionally resumes
|
|
542
|
+
playback (if `autoResume: true`). Position is saved on every page unload for accuracy.
|
|
543
|
+
|
|
544
|
+
Cart state is intentionally NOT persisted โ it seeds from `data-wb-in-cart` attributes on page load, making the server
|
|
545
|
+
the source of truth.
|
|
546
|
+
|
|
547
|
+
## Custom Styling
|
|
548
|
+
|
|
549
|
+
Override CSS custom properties to theme the bar:
|
|
550
|
+
|
|
551
|
+
```css
|
|
552
|
+
.waveform-bar,
|
|
553
|
+
.wb-queue-panel {
|
|
554
|
+
--wb-bg: rgba(20, 20, 20, 0.98);
|
|
555
|
+
--wb-border: rgba(255, 255, 255, 0.1);
|
|
556
|
+
--wb-text: #ffffff;
|
|
557
|
+
--wb-text-muted: rgba(255, 255, 255, 0.5);
|
|
558
|
+
--wb-accent: #1db954; /* e.g. Spotify green */
|
|
559
|
+
--wb-accent-light: #1ed760;
|
|
560
|
+
--wb-hover: rgba(255, 255, 255, 0.08);
|
|
561
|
+
--wb-tag-bg: rgba(29, 185, 84, 0.12);
|
|
562
|
+
--wb-tag-text: #1db954;
|
|
563
|
+
--wb-fav-color: #ef4444;
|
|
564
|
+
--wb-cart-color: #4ade80;
|
|
565
|
+
}
|
|
566
|
+
```
|
|
567
|
+
|
|
568
|
+
A `.wb-light` class is available for light backgrounds:
|
|
569
|
+
|
|
570
|
+
```css
|
|
571
|
+
.waveform-bar.wb-light {
|
|
572
|
+
--wb-bg: rgba(255, 255, 255, 0.95);
|
|
573
|
+
--wb-text: #1a1a1a;
|
|
574
|
+
--wb-text-muted: rgba(0, 0, 0, 0.5);
|
|
575
|
+
}
|
|
576
|
+
```
|
|
577
|
+
|
|
578
|
+
## Layout
|
|
579
|
+
|
|
580
|
+
The bar uses a three-zone flex layout:
|
|
581
|
+
|
|
582
|
+
```
|
|
583
|
+
[Left: controls + track info] โ [Centre: waveform + time] โ [Right: meta + actions + volume + queue]
|
|
584
|
+
```
|
|
585
|
+
|
|
586
|
+
- **Left zone** โ Prev, Play/Pause, Next, Repeat buttons + artwork + title/artist
|
|
587
|
+
- **Centre zone** โ Full-width waveform visualization + elapsed/total time
|
|
588
|
+
- **Right zone** โ BPM/key tags, favorite/cart buttons, volume popup, queue toggle
|
|
589
|
+
|
|
590
|
+
On mobile (โค768px):
|
|
591
|
+
|
|
592
|
+
- Left + Right share the top row
|
|
593
|
+
- Waveform drops to a full-width second row
|
|
594
|
+
- Meta tags, actions, and time display are hidden
|
|
595
|
+
- Queue panel becomes full-width
|
|
596
|
+
|
|
597
|
+
On small mobile (โค480px):
|
|
598
|
+
|
|
599
|
+
- Prev/Next and Repeat buttons are hidden
|
|
600
|
+
- Volume is hidden
|
|
601
|
+
- Only play button, track info, and waveform remain
|
|
602
|
+
|
|
603
|
+
## Use Cases
|
|
604
|
+
|
|
605
|
+
### Beat Store
|
|
606
|
+
|
|
607
|
+
```html
|
|
608
|
+
|
|
609
|
+
<div class="beat-card wb-card-highlight"
|
|
610
|
+
data-wb-play
|
|
611
|
+
data-url="beats/trap-42.mp3"
|
|
612
|
+
data-id="beat-42"
|
|
613
|
+
data-title="Trap Beat #42"
|
|
614
|
+
data-artist="ProducerName"
|
|
615
|
+
data-bpm="140"
|
|
616
|
+
data-key="Cm"
|
|
617
|
+
data-artwork="covers/trap-42.jpg"
|
|
618
|
+
data-link="/beats/trap-42"
|
|
619
|
+
data-wb-favorited="true">
|
|
620
|
+
<img src="covers/trap-42.jpg">
|
|
621
|
+
<h3 class="wb-accent-current">Trap Beat #42</h3>
|
|
622
|
+
<p>140 BPM ยท Cm ยท $29.99</p>
|
|
623
|
+
<span class="wb-eq-bars"><span></span><span></span><span></span><span></span></span>
|
|
624
|
+
<button>
|
|
625
|
+
<span class="wb-hide-if-fav">โก Save</span>
|
|
626
|
+
<span class="wb-show-if-fav">โค Saved</span>
|
|
627
|
+
</button>
|
|
628
|
+
</div>
|
|
629
|
+
<button data-wb-queue data-url="beats/trap-42.mp3" data-title="Trap Beat #42">+ Queue</button>
|
|
630
|
+
```
|
|
631
|
+
|
|
632
|
+
### Sample Library Table
|
|
633
|
+
|
|
634
|
+
```html
|
|
635
|
+
|
|
636
|
+
<table>
|
|
637
|
+
<tr data-wb-play
|
|
638
|
+
data-url="samples/kick.wav"
|
|
639
|
+
data-title="808 Kick"
|
|
640
|
+
data-artist="Drum Kit Vol. 3"
|
|
641
|
+
data-bpm="128"
|
|
642
|
+
data-meta='{"format":"WAV"}'>
|
|
643
|
+
<td><span class="wb-eq-bars"><span></span><span></span><span></span><span></span></span></td>
|
|
644
|
+
<td class="wb-accent-current">808 Kick</td>
|
|
645
|
+
<td>128 BPM</td>
|
|
646
|
+
<td>WAV</td>
|
|
647
|
+
</tr>
|
|
648
|
+
</table>
|
|
649
|
+
```
|
|
650
|
+
|
|
651
|
+
### DJ Mix with Clickable Tracklist
|
|
652
|
+
|
|
653
|
+
```html
|
|
654
|
+
<!-- Mix trigger with markers -->
|
|
655
|
+
<div data-wb-play
|
|
656
|
+
data-url="mixes/guestmix.mp3"
|
|
657
|
+
data-title="Guest Mix"
|
|
658
|
+
data-artist="Various Artists"
|
|
659
|
+
data-wb-waveform='[0.1,0.3,0.5,...]'
|
|
660
|
+
data-wb-markers='[
|
|
661
|
+
{"time":0,"label":"Intro","title":"Opening","artist":"DJ One"},
|
|
662
|
+
{"time":180,"label":"Drop","title":"Big Tune","artist":"Producer X"},
|
|
663
|
+
{"time":360,"label":"Chill","title":"Wind Down","artist":"Ambient Co"}
|
|
664
|
+
]'>
|
|
665
|
+
๐ง Play Guest Mix
|
|
666
|
+
</div>
|
|
667
|
+
|
|
668
|
+
<!-- Clickable tracklist buttons -->
|
|
669
|
+
<button onclick="WaveformBar.seekToMarkerByLabel('Intro')">00:00 โ DJ One โ Opening</button>
|
|
670
|
+
<button onclick="WaveformBar.seekToMarkerByLabel('Drop')">03:00 โ Producer X โ Big Tune</button>
|
|
671
|
+
<button onclick="WaveformBar.seekToMarkerByLabel('Chill')">06:00 โ Ambient Co โ Wind Down</button>
|
|
672
|
+
```
|
|
673
|
+
|
|
674
|
+
### Podcast
|
|
675
|
+
|
|
676
|
+
```html
|
|
677
|
+
|
|
678
|
+
<button data-wb-play
|
|
679
|
+
data-url="episodes/ep42.mp3"
|
|
680
|
+
data-title="Ep 42: The Future of AI"
|
|
681
|
+
data-artist="with Dr. Sarah Chen"
|
|
682
|
+
data-artwork="ep42-cover.jpg"
|
|
683
|
+
data-link="/episodes/42"
|
|
684
|
+
class="wb-icon-swap">
|
|
685
|
+
<span class="wb-show-play">โถ Listen</span>
|
|
686
|
+
<span class="wb-show-pause">โธ Playing</span>
|
|
687
|
+
</button>
|
|
688
|
+
```
|
|
689
|
+
|
|
690
|
+
## Pre-Generating Waveforms
|
|
691
|
+
|
|
692
|
+
Use [@arraypress/waveform-gen](https://github.com/arraypress/waveform-gen) to batch-generate waveform data:
|
|
693
|
+
|
|
694
|
+
```bash
|
|
695
|
+
# Generate JSON files for all audio
|
|
696
|
+
npx @arraypress/waveform-gen ./audio/*.mp3 --output ./waveforms/
|
|
697
|
+
|
|
698
|
+
# Generate ready-to-paste HTML markup
|
|
699
|
+
npx @arraypress/waveform-gen ./audio/*.mp3 --format html --output ./
|
|
700
|
+
```
|
|
701
|
+
|
|
702
|
+
Then reference the data in your HTML:
|
|
703
|
+
|
|
704
|
+
```html
|
|
705
|
+
|
|
706
|
+
<div data-wb-play
|
|
707
|
+
data-url="song.mp3"
|
|
708
|
+
data-title="My Song"
|
|
709
|
+
data-wb-waveform='[0.12,0.45,0.89,0.34,0.67]'>
|
|
710
|
+
</div>
|
|
711
|
+
```
|
|
712
|
+
|
|
713
|
+
## Browser Support
|
|
714
|
+
|
|
715
|
+
- Chrome/Edge 90+
|
|
716
|
+
- Firefox 88+
|
|
717
|
+
- Safari 14+
|
|
718
|
+
- Mobile browsers (iOS Safari, Chrome Android)
|
|
719
|
+
|
|
720
|
+
## Dependencies
|
|
721
|
+
|
|
722
|
+
- [WaveformPlayer](https://github.com/arraypress/waveform-player) โฅ1.3.5 โ must be loaded before WaveformBar
|
|
723
|
+
|
|
724
|
+
## License
|
|
725
|
+
|
|
726
|
+
MIT ยฉ [ArrayPress](https://github.com/arraypress)
|
|
727
|
+
|
|
728
|
+
## Related
|
|
729
|
+
|
|
730
|
+
- [WaveformPlayer](https://github.com/arraypress/waveform-player) โ Core audio player with waveform visualization
|
|
731
|
+
- [WaveformPlaylist](https://github.com/arraypress/waveform-playlist) โ On-page playlist and chapter navigation
|
|
732
|
+
- [WaveformGen](https://github.com/arraypress/waveform-generator) โ CLI tool for batch waveform data generation
|