wavesurf 1.0.0 → 1.2.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/README.md CHANGED
@@ -1,38 +1,38 @@
1
1
  # wavesurf
2
2
 
3
- A React audio player with WaveSurfer.js waveform visualization, global state management, and mini-player support.
3
+ A production-ready React audio player built on [WaveSurfer.js](https://wavesurfer.xyz/). Features waveform visualization, global state management, a persistent mini-player, and social sharing—everything you need for a music streaming experience.
4
4
 
5
- ## Features
5
+ ## Why wavesurf?
6
6
 
7
- - **WaveSurfer.js Integration** - Beautiful waveform visualization for audio playback
8
- - **Global Audio State** - React Context for managing playback across your app
9
- - **Mini Player** - A persistent bottom/top bar for controlling playback
10
- - **Pre-computed Peaks Support** - Fast loading with pre-generated waveform data
11
- - **Volume Fade-in Effect** - Smooth 3-second volume fade when starting playback
12
- - **Volume Persistence** - Remember user's volume preference via localStorage
13
- - **Lazy Loading** - Load waveforms only when visible using IntersectionObserver
14
- - **Mobile Responsive** - Adapts to different screen sizes
15
- - **CSS Variables** - Easy theming with CSS custom properties
16
- - **TypeScript** - Full TypeScript support with exported types
7
+ Building a good audio player is harder than it looks. You need:
8
+
9
+ - **Global state** so only one song plays at a time (like Spotify)
10
+ - **Waveform visualization** that's performant and interactive
11
+ - **A persistent mini-player** that stays visible while users browse
12
+ - **Volume fade-in** so playback doesn't blast at full volume
13
+ - **Mobile responsiveness** across all screen sizes
14
+ - **Lazy loading** so pages with many tracks don't lag
15
+
16
+ wavesurf handles all of this out of the box, so you can focus on your actual product.
17
17
 
18
18
  ## Installation
19
19
 
20
20
  ```bash
21
21
  npm install wavesurf wavesurfer.js
22
- # or
23
- yarn add wavesurf wavesurfer.js
24
- # or
25
- pnpm add wavesurf wavesurfer.js
26
22
  ```
27
23
 
24
+ > **Why wavesurfer.js is a peer dependency:** You might already have it in your project, or want to control the version. Making it a peer dependency prevents duplicate bundles and version conflicts.
25
+
28
26
  ## Quick Start
29
27
 
30
- ### 1. Wrap your app with the provider
28
+ ### 1. Add the Provider
29
+
30
+ Wrap your app (or the part that needs audio) with `AudioPlayerProvider`:
31
31
 
32
32
  ```tsx
33
33
  import { AudioPlayerProvider } from 'wavesurf';
34
34
 
35
- function App() {
35
+ export default function App() {
36
36
  return (
37
37
  <AudioPlayerProvider>
38
38
  <YourApp />
@@ -41,12 +41,15 @@ function App() {
41
41
  }
42
42
  ```
43
43
 
44
- ### 2. Add the MiniPlayer component
44
+ ### 2. Add the Mini Player
45
+
46
+ Place `MiniPlayer` in your layout—it appears automatically when a song plays:
45
47
 
46
48
  ```tsx
47
49
  import { MiniPlayer } from 'wavesurf';
50
+ import 'wavesurf/styles.css';
48
51
 
49
- function Layout({ children }) {
52
+ export default function Layout({ children }) {
50
53
  return (
51
54
  <div>
52
55
  {children}
@@ -56,25 +59,24 @@ function Layout({ children }) {
56
59
  }
57
60
  ```
58
61
 
59
- ### 3. Use the WaveformPlayer for songs
62
+ ### 3. Display Songs with WaveformPlayer
60
63
 
61
64
  ```tsx
62
65
  import { WaveformPlayer } from 'wavesurf';
63
- import 'wavesurf/styles.css';
64
66
 
65
- function SongList({ songs }) {
67
+ function TrackList({ tracks }) {
66
68
  return (
67
69
  <div>
68
- {songs.map((song) => (
70
+ {tracks.map((track) => (
69
71
  <WaveformPlayer
70
- key={song.id}
72
+ key={track.id}
71
73
  song={{
72
- id: song.id,
73
- title: song.title,
74
- artist: song.artist,
75
- audioUrl: song.url,
76
- duration: song.duration,
77
- peaks: song.peaks, // Optional: pre-computed waveform data
74
+ id: track.id,
75
+ title: track.title,
76
+ artist: track.artist,
77
+ audioUrl: track.url,
78
+ duration: track.duration,
79
+ peaks: track.peaks, // Optional but recommended
78
80
  }}
79
81
  />
80
82
  ))}
@@ -83,164 +85,358 @@ function SongList({ songs }) {
83
85
  }
84
86
  ```
85
87
 
86
- ## API Reference
88
+ That's it. Click play on any track, and the mini-player appears. Click another track, and it seamlessly switches.
89
+
90
+ ---
91
+
92
+ ## Architecture & Design Decisions
93
+
94
+ ### Global Audio Context
95
+
96
+ **Problem:** In a typical music app, you have multiple track listings, album pages, and a persistent player bar. Without global state, you'd have multiple `<audio>` elements fighting each other.
97
+
98
+ **Solution:** wavesurf uses React Context to maintain a single audio source. When you call `play()` from anywhere in your app, it:
99
+
100
+ 1. Pauses any currently playing audio
101
+ 2. Loads the new track
102
+ 3. Starts playback with a volume fade-in
103
+ 4. Notifies all `WaveformPlayer` components to update their UI
104
+
105
+ ```tsx
106
+ // Any component can control playback
107
+ const { play, pause, currentSong, isPlaying } = useAudioPlayer();
108
+ ```
109
+
110
+ ### Waveform Visualization (Why WaveSurfer.js?)
111
+
112
+ **Problem:** Audio waveforms require decoding audio data and rendering thousands of bars. Doing this poorly kills performance.
113
+
114
+ **Solution:** WaveSurfer.js is the industry standard for web audio visualization. It handles:
115
+
116
+ - Efficient canvas rendering
117
+ - Audio decoding
118
+ - Responsive resize handling
119
+ - Click-to-seek interactions
120
+
121
+ wavesurf wraps WaveSurfer.js with React lifecycle management, so you don't deal with manual cleanup or memory leaks.
122
+
123
+ ### Pre-computed Peaks (Performance)
124
+
125
+ **Problem:** Decoding audio to generate waveforms is slow—especially for longer tracks or pages with many songs. Users see loading spinners everywhere.
126
+
127
+ **Solution:** Generate peaks once (server-side), store them, and pass them to wavesurf:
128
+
129
+ ```tsx
130
+ <WaveformPlayer
131
+ song={{
132
+ id: '1',
133
+ title: 'My Song',
134
+ audioUrl: '/audio/song.mp3',
135
+ duration: 245,
136
+ peaks: [0.1, 0.3, 0.5, 0.8, ...], // Pre-computed!
137
+ }}
138
+ />
139
+ ```
140
+
141
+ When peaks are provided:
142
+ - **No audio decoding needed** — waveform renders instantly
143
+ - **No network request for audio** — until the user clicks play
144
+ - **Pages load faster** — even with 50+ tracks
145
+
146
+ #### How to Generate Peaks
147
+
148
+ Using [audiowaveform](https://github.com/bbc/audiowaveform) (recommended):
149
+
150
+ ```bash
151
+ # Install
152
+ brew install audiowaveform # macOS
153
+ apt install audiowaveform # Ubuntu
154
+
155
+ # Generate peaks
156
+ audiowaveform -i song.mp3 -o peaks.json --pixels-per-second 10 -b 8
157
+ ```
158
+
159
+ Or server-side with FFmpeg/Node.js—compute once when uploading audio, store in your database.
160
+
161
+ ### Volume Fade-in (UX)
162
+
163
+ **Problem:** Clicking play and getting blasted with sudden audio is jarring. Users instinctively reach for the volume.
164
+
165
+ **Solution:** wavesurf fades volume from 0 to the user's set level over 3 seconds (configurable). This:
166
+
167
+ - Creates a professional, polished feel
168
+ - Prevents startling users
169
+ - Matches how streaming services behave
170
+
171
+ ```tsx
172
+ <AudioPlayerProvider config={{
173
+ fadeInEnabled: true, // default: true
174
+ fadeInDuration: 3000, // default: 3000ms
175
+ }}>
176
+ ```
177
+
178
+ ### Volume Persistence (UX)
179
+
180
+ **Problem:** Users set their volume, navigate to another page, and it resets.
181
+
182
+ **Solution:** Volume is automatically saved to localStorage and restored on page load.
183
+
184
+ ```tsx
185
+ <AudioPlayerProvider config={{
186
+ persistVolume: true, // default: true
187
+ storageKey: 'myAppVolume', // default: 'audioPlayerVolume'
188
+ defaultVolume: 0.8, // default: 1
189
+ }}>
190
+ ```
191
+
192
+ ### Lazy Loading (Performance)
193
+
194
+ **Problem:** A page with 20 tracks means 20 WaveSurfer instances initializing at once, causing jank.
195
+
196
+ **Solution:** wavesurf uses IntersectionObserver to only initialize waveforms when they scroll into view:
197
+
198
+ ```tsx
199
+ <WaveformPlayer
200
+ song={song}
201
+ lazyLoad={true} // default: true
202
+ />
203
+ ```
204
+
205
+ Tracks off-screen are just empty containers until needed.
206
+
207
+ ### Mini Player (UX Pattern)
208
+
209
+ **Problem:** Users want to browse your site while listening. A player embedded in the track list disappears when they navigate.
210
+
211
+ **Solution:** The `MiniPlayer` component is a fixed bar (bottom or top) that:
212
+
213
+ - Appears when playback starts
214
+ - Shows current track, progress, volume controls
215
+ - Has its own mini waveform for seeking
216
+ - Stays visible during navigation
217
+ - Can be closed by the user
218
+
219
+ ```tsx
220
+ <MiniPlayer
221
+ position="bottom" // or "top"
222
+ showVolume={true} // auto-hidden on mobile
223
+ showClose={true}
224
+ onClose={() => console.log('Player closed')}
225
+ />
226
+ ```
227
+
228
+ ---
229
+
230
+ ## Components
87
231
 
88
232
  ### AudioPlayerProvider
89
233
 
90
- Wrap your application with this provider to enable global audio state management.
234
+ Wraps your app to provide global audio state.
91
235
 
92
236
  ```tsx
93
- <AudioPlayerProvider config={config}>
237
+ <AudioPlayerProvider config={{
238
+ fadeInEnabled: true,
239
+ fadeInDuration: 3000,
240
+ persistVolume: true,
241
+ storageKey: 'audioPlayerVolume',
242
+ defaultVolume: 1,
243
+ onPlay: (song) => analytics.track('play', song),
244
+ onPause: () => analytics.track('pause'),
245
+ onEnd: () => analytics.track('songEnded'),
246
+ onTimeUpdate: (time) => {},
247
+ }}>
94
248
  {children}
95
249
  </AudioPlayerProvider>
96
250
  ```
97
251
 
98
- #### Config Options
99
-
100
- | Property | Type | Default | Description |
101
- |----------|------|---------|-------------|
102
- | `fadeInEnabled` | `boolean` | `true` | Enable volume fade-in effect on play |
103
- | `fadeInDuration` | `number` | `3000` | Duration of fade-in in milliseconds |
104
- | `persistVolume` | `boolean` | `true` | Persist volume to localStorage |
105
- | `storageKey` | `string` | `'audioPlayerVolume'` | localStorage key for volume |
106
- | `defaultVolume` | `number` | `1` | Default volume level (0-1) |
107
- | `onPlay` | `(song: Song) => void` | - | Callback when playback starts |
108
- | `onPause` | `() => void` | - | Callback when playback pauses |
109
- | `onEnd` | `() => void` | - | Callback when song ends |
110
- | `onTimeUpdate` | `(time: number) => void` | - | Callback on time update |
111
-
112
252
  ### useAudioPlayer Hook
113
253
 
114
- Access the audio player state and controls from any component.
254
+ Access state and controls from any component:
115
255
 
116
256
  ```tsx
117
- import { useAudioPlayer } from 'wavesurf';
118
-
119
- function CustomControls() {
120
- const {
121
- // State
122
- currentSong,
123
- isPlaying,
124
- currentTime,
125
- duration,
126
- volume,
127
- displayVolume,
128
- isFadingIn,
129
- // Actions
130
- play,
131
- pause,
132
- togglePlay,
133
- seek,
134
- setVolume,
135
- stop,
136
- } = useAudioPlayer();
137
-
138
- return (
139
- <button onClick={togglePlay}>
140
- {isPlaying ? 'Pause' : 'Play'}
141
- </button>
142
- );
143
- }
257
+ const {
258
+ // State
259
+ currentSong, // Song | null
260
+ isPlaying, // boolean
261
+ currentTime, // number (seconds)
262
+ duration, // number (seconds)
263
+ volume, // number (0-1, user's saved volume)
264
+ displayVolume, // number (0-1, actual volume during fade)
265
+ isFadingIn, // boolean
266
+
267
+ // Actions
268
+ play, // (song: Song) => void
269
+ pause, // () => void
270
+ togglePlay, // () => void
271
+ seek, // (time: number) => void
272
+ setVolume, // (volume: number) => void
273
+ stop, // () => void
274
+ } = useAudioPlayer();
144
275
  ```
145
276
 
146
277
  ### WaveformPlayer
147
278
 
148
- Displays a waveform visualization with play controls for a single song.
279
+ Displays a track with waveform visualization:
149
280
 
150
281
  ```tsx
151
282
  <WaveformPlayer
152
- song={song}
283
+ song={{
284
+ id: string,
285
+ title: string,
286
+ artist?: string,
287
+ album?: string,
288
+ audioUrl: string,
289
+ duration?: number,
290
+ peaks?: number[],
291
+ }}
153
292
  waveformConfig={{
154
293
  waveColor: '#666666',
155
294
  progressColor: '#D4AF37',
295
+ cursorColor: '#D4AF37',
296
+ barWidth: 2,
297
+ barGap: 1,
298
+ barRadius: 2,
156
299
  height: 60,
157
300
  }}
158
301
  lazyLoad={true}
159
302
  showTime={true}
160
- className="my-player"
161
- renderHeader={(song, isPlaying) => (
162
- <div>{song.title} {isPlaying && '▶'}</div>
163
- )}
164
- renderControls={(song, isPlaying) => (
165
- <button>Share</button>
166
- )}
303
+ standalone={false} // Use local audio instead of global context
304
+ className=""
305
+ renderHeader={(song, isPlaying) => <CustomHeader />}
306
+ renderControls={(song, isPlaying) => <CustomControls />}
167
307
  />
168
308
  ```
169
309
 
170
- #### Props
310
+ #### Standalone Mode
171
311
 
172
- | Prop | Type | Default | Description |
173
- |------|------|---------|-------------|
174
- | `song` | `Song` | required | The song object to play |
175
- | `waveformConfig` | `WaveformConfig` | - | Waveform styling options |
176
- | `lazyLoad` | `boolean` | `true` | Enable lazy loading via IntersectionObserver |
177
- | `showTime` | `boolean` | `true` | Show time display below waveform |
178
- | `className` | `string` | `''` | Additional CSS class |
179
- | `renderHeader` | `(song, isPlaying) => ReactNode` | - | Custom header renderer |
180
- | `renderControls` | `(song, isPlaying) => ReactNode` | - | Custom controls renderer |
312
+ By default, `WaveformPlayer` uses the global `AudioPlayerProvider` context and works with the `MiniPlayer`. If you want a simpler setup—individual players that don't share state and don't show the mini player bar—use standalone mode:
313
+
314
+ ```tsx
315
+ // No AudioPlayerProvider needed
316
+ <WaveformPlayer
317
+ song={song}
318
+ standalone={true}
319
+ />
320
+ ```
321
+
322
+ **When to use standalone mode:**
323
+ - Simple pages with just one or two tracks
324
+ - Embedded players that shouldn't affect the rest of your site
325
+ - When you don't want the persistent mini player bar
326
+
327
+ **Standalone mode behavior:**
328
+ - Each player manages its own audio element
329
+ - Clicking play on one song automatically pauses others (even in standalone mode)
330
+ - No MiniPlayer appears
331
+ - Volume fade-in and persistence are not applied
181
332
 
182
333
  ### MiniPlayer
183
334
 
184
- A fixed position player bar for persistent playback control.
335
+ Persistent playback bar:
185
336
 
186
337
  ```tsx
187
338
  <MiniPlayer
188
- position="bottom"
339
+ position="bottom" // 'top' | 'bottom'
189
340
  showVolume={true}
190
341
  showClose={true}
191
- onClose={() => console.log('Player closed')}
192
- className="my-mini-player"
193
- waveformConfig={{
194
- progressColor: '#FF0000',
195
- }}
342
+ onClose={() => {}}
343
+ className=""
344
+ waveformConfig={{...}}
196
345
  />
197
346
  ```
198
347
 
199
- #### Props
348
+ #### Persisting Across Route Changes
200
349
 
201
- | Prop | Type | Default | Description |
202
- |------|------|---------|-------------|
203
- | `position` | `'top' \| 'bottom'` | `'bottom'` | Position on screen |
204
- | `showVolume` | `boolean` | `true` | Show volume control (auto-hidden on mobile) |
205
- | `showClose` | `boolean` | `true` | Show close button |
206
- | `onClose` | `() => void` | - | Callback when close is clicked |
207
- | `className` | `string` | `''` | Additional CSS class |
208
- | `waveformConfig` | `WaveformConfig` | - | Waveform styling options |
350
+ To keep the MiniPlayer visible and audio playing while users navigate between pages, place both `AudioPlayerProvider` and `MiniPlayer` in your **root layout**—not in individual pages.
209
351
 
210
- ### Song Interface
352
+ **Next.js App Router:**
211
353
 
212
- ```typescript
213
- interface Song {
214
- id: string; // Unique identifier
215
- title: string; // Display title
216
- artist?: string; // Artist name (optional)
217
- album?: string; // Album name (optional)
218
- audioUrl: string; // URL to the audio file
219
- duration?: number; // Duration in seconds (optional)
220
- peaks?: number[]; // Pre-computed waveform peaks (optional)
354
+ ```tsx
355
+ // app/layout.tsx
356
+ import { AudioPlayerProvider, MiniPlayer } from 'wavesurf';
357
+ import 'wavesurf/styles.css';
358
+
359
+ export default function RootLayout({ children }) {
360
+ return (
361
+ <html>
362
+ <body>
363
+ <AudioPlayerProvider>
364
+ <Header />
365
+ <main>{children}</main>
366
+ <Footer />
367
+ <MiniPlayer />
368
+ </AudioPlayerProvider>
369
+ </body>
370
+ </html>
371
+ );
221
372
  }
222
373
  ```
223
374
 
224
- ### WaveformConfig Interface
375
+ **Next.js Pages Router:**
225
376
 
226
- ```typescript
227
- interface WaveformConfig {
228
- waveColor?: string; // Color of the waveform (default: '#666666')
229
- progressColor?: string; // Color of played portion (default: '#D4AF37')
230
- cursorColor?: string; // Color of playhead (default: '#D4AF37')
231
- barWidth?: number; // Width of bars in px (default: 2)
232
- barGap?: number; // Gap between bars in px (default: 1)
233
- barRadius?: number; // Border radius of bars (default: 2)
234
- height?: number; // Height in pixels (default: 60)
235
- normalize?: boolean; // Normalize waveform (default: true)
377
+ ```tsx
378
+ // pages/_app.tsx
379
+ import { AudioPlayerProvider, MiniPlayer } from 'wavesurf';
380
+ import 'wavesurf/styles.css';
381
+
382
+ export default function MyApp({ Component, pageProps }) {
383
+ return (
384
+ <AudioPlayerProvider>
385
+ <Component {...pageProps} />
386
+ <MiniPlayer />
387
+ </AudioPlayerProvider>
388
+ );
236
389
  }
237
390
  ```
238
391
 
239
- ## Styling & Theming
392
+ **React Router:**
393
+
394
+ ```tsx
395
+ // App.tsx
396
+ import { AudioPlayerProvider, MiniPlayer } from 'wavesurf';
397
+ import { BrowserRouter, Routes, Route } from 'react-router-dom';
398
+ import 'wavesurf/styles.css';
399
+
400
+ function App() {
401
+ return (
402
+ <AudioPlayerProvider>
403
+ <BrowserRouter>
404
+ <Routes>
405
+ <Route path="/" element={<Home />} />
406
+ <Route path="/album/:id" element={<Album />} />
407
+ </Routes>
408
+ </BrowserRouter>
409
+ <MiniPlayer />
410
+ </AudioPlayerProvider>
411
+ );
412
+ }
413
+ ```
414
+
415
+ **Why this works:** React Context state persists as long as the provider component stays mounted. By placing it in the root layout, the audio state survives page transitions. If you put the provider inside a page component, it unmounts on navigation and loses the current song.
416
+
417
+ ### ShareButtons
418
+
419
+ Social sharing for tracks:
420
+
421
+ ```tsx
422
+ import { ShareButtons } from 'wavesurf';
423
+
424
+ <ShareButtons
425
+ url="https://mysite.com/track/123"
426
+ text="Check out this song!"
427
+ platforms={['facebook', 'twitter', 'whatsapp', 'copy']}
428
+ onShare={(platform, url) => analytics.track('share', { platform })}
429
+ showLabels={false}
430
+ />
431
+ ```
432
+
433
+ **Available platforms:** `facebook`, `twitter`, `whatsapp`, `linkedin`, `reddit`, `telegram`, `email`, `copy`
240
434
 
241
- ### Using the default styles
435
+ ---
242
436
 
243
- Import the CSS file in your app:
437
+ ## Styling
438
+
439
+ ### Using Default Styles
244
440
 
245
441
  ```tsx
246
442
  import 'wavesurf/styles.css';
@@ -248,24 +444,27 @@ import 'wavesurf/styles.css';
248
444
 
249
445
  ### Customizing with CSS Variables
250
446
 
251
- Override the CSS variables to customize the appearance:
447
+ Override any of these in your CSS:
252
448
 
253
449
  ```css
254
450
  :root {
255
- /* Colors */
256
- --wsp-wave-color: #888888;
257
- --wsp-progress-color: #FF5500;
258
- --wsp-cursor-color: #FF5500;
451
+ /* Waveform */
452
+ --wsp-wave-color: #666666;
453
+ --wsp-progress-color: #D4AF37;
454
+ --wsp-cursor-color: #D4AF37;
455
+
456
+ /* Backgrounds */
259
457
  --wsp-background: transparent;
458
+ --wsp-background-secondary: rgba(255, 255, 255, 0.05);
260
459
 
261
- /* Button Colors */
262
- --wsp-button-bg: #FF5500;
263
- --wsp-button-bg-hover: #FF7733;
264
- --wsp-button-text: #ffffff;
460
+ /* Buttons */
461
+ --wsp-button-bg: #D4AF37;
462
+ --wsp-button-bg-hover: #e5c04a;
463
+ --wsp-button-text: #000000;
265
464
 
266
- /* Text Colors */
465
+ /* Text */
267
466
  --wsp-text: #ffffff;
268
- --wsp-text-muted: #999999;
467
+ --wsp-text-muted: #a3a3a3;
269
468
 
270
469
  /* Sizing */
271
470
  --wsp-height: 60px;
@@ -273,54 +472,32 @@ Override the CSS variables to customize the appearance:
273
472
  --wsp-button-size: 56px;
274
473
 
275
474
  /* Mini Player */
276
- --wsp-mini-bg: #1a1a1a;
277
- --wsp-mini-border-color: #FF5500;
475
+ --wsp-mini-bg: #0a0a0a;
476
+ --wsp-mini-border-color: #D4AF37;
477
+ --wsp-mini-shadow: 0 -4px 20px rgba(0, 0, 0, 0.5);
478
+
479
+ /* Transitions */
480
+ --wsp-transition: 150ms ease;
278
481
  }
279
482
  ```
280
483
 
281
- ### Using your own styles
484
+ ### Custom Styling
282
485
 
283
- You can also write completely custom CSS targeting the class names:
486
+ All components use BEM-style class names you can target:
284
487
 
285
- - `.wsp-player` - Main player container
488
+ - `.wsp-player` - WaveformPlayer container
286
489
  - `.wsp-play-button` - Play/pause button
287
490
  - `.wsp-waveform` - Waveform container
288
- - `.wsp-time-display` - Time display
289
- - `.wsp-mini-player` - Mini player container
290
- - `.wsp-mini-play-button` - Mini player play button
291
- - `.wsp-mini-waveform` - Mini player waveform
491
+ - `.wsp-time-display` - Time labels
492
+ - `.wsp-mini-player` - MiniPlayer container
493
+ - `.wsp-share-buttons` - ShareButtons container
494
+ - `.wsp-share-button` - Individual share button
292
495
 
293
- ## Pre-computed Peaks
294
-
295
- For optimal performance, especially with large audio files, you can pre-compute waveform peaks on your server. This eliminates the need to decode audio on the client.
296
-
297
- ### Generating peaks with audiowaveform
298
-
299
- ```bash
300
- # Install audiowaveform (on Ubuntu/Debian)
301
- sudo apt install audiowaveform
302
-
303
- # Generate peaks JSON
304
- audiowaveform -i audio.mp3 -o peaks.json --pixels-per-second 10
305
- ```
306
-
307
- ### Using peaks in your app
308
-
309
- ```tsx
310
- const song = {
311
- id: '1',
312
- title: 'My Song',
313
- audioUrl: '/audio/song.mp3',
314
- duration: 180, // 3 minutes
315
- peaks: peaksData, // Array of numbers from audiowaveform
316
- };
317
-
318
- <WaveformPlayer song={song} />
319
- ```
496
+ ---
320
497
 
321
498
  ## TypeScript
322
499
 
323
- All types are exported from the package:
500
+ All types are exported:
324
501
 
325
502
  ```typescript
326
503
  import type {
@@ -331,18 +508,79 @@ import type {
331
508
  WaveformConfig,
332
509
  WaveformPlayerProps,
333
510
  MiniPlayerProps,
511
+ SharePlatform,
512
+ ShareButtonsProps,
334
513
  } from 'wavesurf';
335
514
  ```
336
515
 
516
+ ---
517
+
518
+ ## Examples
519
+
520
+ ### Custom Play Button (Headless Usage)
521
+
522
+ ```tsx
523
+ function CustomPlayButton({ song }) {
524
+ const { play, pause, currentSong, isPlaying } = useAudioPlayer();
525
+ const isThisSong = currentSong?.id === song.id;
526
+ const playing = isThisSong && isPlaying;
527
+
528
+ return (
529
+ <button onClick={() => playing ? pause() : play(song)}>
530
+ {playing ? 'Pause' : 'Play'}
531
+ </button>
532
+ );
533
+ }
534
+ ```
535
+
536
+ ### Track Card with Share
537
+
538
+ ```tsx
539
+ function TrackCard({ track }) {
540
+ const shareUrl = `https://mysite.com/track/${track.id}`;
541
+
542
+ return (
543
+ <div className="track-card">
544
+ <WaveformPlayer song={track} />
545
+ <ShareButtons
546
+ url={shareUrl}
547
+ text={`Listen to ${track.title}`}
548
+ platforms={['twitter', 'whatsapp', 'copy']}
549
+ />
550
+ </div>
551
+ );
552
+ }
553
+ ```
554
+
555
+ ### Analytics Integration
556
+
557
+ ```tsx
558
+ <AudioPlayerProvider config={{
559
+ onPlay: (song) => {
560
+ analytics.track('song_play', {
561
+ songId: song.id,
562
+ title: song.title,
563
+ });
564
+ },
565
+ onEnd: () => {
566
+ analytics.track('song_completed');
567
+ },
568
+ }}>
569
+ ```
570
+
571
+ ---
572
+
337
573
  ## Browser Support
338
574
 
339
- This package requires browsers that support:
575
+ Requires browsers with:
340
576
  - Web Audio API
341
577
  - CSS Custom Properties
342
- - IntersectionObserver (for lazy loading)
578
+ - IntersectionObserver
343
579
 
344
580
  All modern browsers (Chrome, Firefox, Safari, Edge) are supported.
345
581
 
582
+ ---
583
+
346
584
  ## License
347
585
 
348
- MIT
586
+ MIT © [TheDecipherist](https://github.com/TheDecipherist)