Installation
NPM
npm install @arraypress/waveform-player
Yarn
yarn add @arraypress/waveform-player
CDN
<link rel="stylesheet" href="https://unpkg.com/@arraypress/waveform-player@latest/dist/waveform-player.css">
<script src="https://unpkg.com/@arraypress/waveform-player@latest/dist/waveform-player.js"></script>
Download
Download the latest release from GitHub Releases and include the files:
<link rel="stylesheet" href="path/to/waveform-player.css">
<script src="path/to/waveform-player.js"></script>
Quick Start
Zero Configuration (HTML Only)
The simplest way to use WaveformPlayer - just add data attributes:
<div data-waveform-player data-url="audio.mp3"></div>
JavaScript API
For programmatic control, use the JavaScript API:
const player = new WaveformPlayer('#player', {
url: 'audio.mp3',
title: 'My Song',
waveformStyle: 'mirror'
});
Basic Usage
HTML with Data Attributes
<div data-waveform-player
data-url="audio.mp3"
data-title="Song Title"
data-subtitle="Artist Name"
data-waveform-style="mirror"
data-height="80">
</div>
JavaScript Initialization
// Using element selector
const player = new WaveformPlayer('#my-player', {
url: 'audio.mp3',
title: 'Song Title'
});
// Using DOM element
const element = document.getElementById('my-player');
const player = new WaveformPlayer(element, {
url: 'audio.mp3'
});
// With callbacks
const player = new WaveformPlayer('#player', {
url: 'audio.mp3',
onPlay: () => console.log('Playing'),
onPause: () => console.log('Paused'),
onEnd: () => console.log('Ended')
});
Configuration Options
| Option | Type | Default | Description |
|---|---|---|---|
url |
string | '' | Audio file URL (required) |
waveformStyle |
string | 'mirror' | Visual style: bars, mirror, line, blocks, dots, seekbar |
height |
number | 60 | Waveform height in pixels |
barWidth |
number | varies | Width of waveform bars (style-specific) |
barSpacing |
number | varies | Space between bars (style-specific) |
samples |
number | 200 | Number of waveform samples |
colorPreset |
string | null | Color theme: null (auto-detect), 'dark', or 'light' |
waveformColor |
string | Auto | Waveform color (overrides preset) |
progressColor |
string | Auto | Progress color (overrides preset) |
buttonColor |
string | Auto | Play button color (overrides preset) |
buttonHoverColor |
string | Auto | Button hover color (overrides preset) |
textColor |
string | Auto | Text color (overrides preset) |
textSecondaryColor |
string | Auto | Secondary text color (overrides preset) |
showTime |
boolean | true | Show time display |
showBPM |
boolean | false | Enable BPM detection |
showPlaybackSpeed |
boolean | false | Show speed control menu |
playbackRate |
number | 1 | Initial playback speed (0.5-2) |
autoplay |
boolean | false | Autoplay on load |
title |
string | '' | Track title |
subtitle |
string | '' | Track subtitle |
artwork |
string | '' | Album artwork URL |
album |
string | '' | Album name for Media Session |
markers |
array | [] | Chapter markers array |
enableMediaSession |
boolean | true | Enable system media controls |
waveform |
array | null | Pre-generated waveform data |
Data Attributes
All configuration options can be set via HTML data attributes by prefixing with data- and
using kebab-case:
<div data-waveform-player
data-url="audio.mp3"
data-waveform-style="mirror"
data-bar-width="2"
data-bar-spacing="1"
data-color-preset="light"
data-show-bpm="true"
data-show-playback-speed="true"
data-enable-media-session="true">
</div>
Color Themes & Auto-Detection
WaveformPlayer automatically detects your website's color scheme and adapts its appearance. No configuration needed!
Automatic Detection
By default, the player analyzes:
- Website background color - Calculates brightness to determine light/dark theme
- Theme classes - Checks for common patterns like
.dark,data-theme="dark" - System preference - Falls back to
prefers-color-schememedia query - Default - Uses dark theme as ultimate fallback
Examples
Auto-detect (Recommended)
<!-- No color configuration needed! -->
<div data-waveform-player data-url="audio.mp3"></div>
Force Dark Theme
<div data-waveform-player
data-url="audio.mp3"
data-color-preset="dark">
</div>
Force Light Theme
<div data-waveform-player
data-url="audio.mp3"
data-color-preset="light">
</div>
Custom Colors (Override Preset)
<div data-waveform-player
data-url="audio.mp3"
data-color-preset="dark"
data-waveform-color="rgba(100, 150, 255, 0.3)"
data-progress-color="rgba(100, 150, 255, 0.9)"
data-button-color="#6496ff">
</div>
JavaScript API
// Auto-detect (default)
new WaveformPlayer('#player', {
url: 'audio.mp3'
});
// Force theme
new WaveformPlayer('#player', {
url: 'audio.mp3',
colorPreset: 'light'
});
// Custom colors
new WaveformPlayer('#player', {
url: 'audio.mp3',
colorPreset: 'dark',
waveformColor: '#ff6b6b',
progressColor: '#ff0000',
buttonColor: '#ff0000'
});
Color Presets
Two built-in presets are available:
Dark Preset (Default)
- Waveform:
rgba(255, 255, 255, 0.3) - Progress:
rgba(255, 255, 255, 0.9) - Button:
rgba(255, 255, 255, 0.9) - Text:
#ffffff - Secondary Text:
rgba(255, 255, 255, 0.6)
Light Preset
- Waveform:
rgba(0, 0, 0, 0.2) - Progress:
rgba(0, 0, 0, 0.8) - Button:
rgba(0, 0, 0, 0.8) - Text:
#333333 - Secondary Text:
rgba(0, 0, 0, 0.6)
Detection Priority
The auto-detection follows this priority order:
1. Explicit colorPreset attribute → Use specified preset
2. Body background brightness → Light (>128) or Dark (<128)
3. Theme class/attribute → Check for .dark, data-theme="dark", etc.
4. System preference → prefers-color-scheme media query
5. Default → Dark theme fallback
Platform Compatibility
Auto-detection works on:
- ✅ WordPress (all themes)
- ✅ Shopify (Dawn and most themes)
- ✅ Wix, Squarespace, Webflow
- ✅ React, Vue, Angular, Next.js apps
- ✅ Tailwind CSS (dark mode class)
- ✅ Custom static sites
Live Demo
Try it yourself! This player auto-detected your page's theme:
Custom Styling
Override default styles with CSS:
.waveform-player {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 12px;
}
.waveform-btn {
border-color: #fff;
}
.waveform-title {
font-weight: 700;
}
API Methods
Playback Control
play()
Start playback
player.play();
pause()
Pause playback
player.pause();
togglePlay()
Toggle between play and pause
player.togglePlay();
Seeking
seekTo(seconds)
Seek to specific time in seconds
player.seekTo(30); // Seek to 30 seconds
seekToPercent(percent)
Seek to percentage (0-1)
player.seekToPercent(0.5); // Seek to 50%
Volume & Speed
setVolume(volume)
Set volume (0-1)
player.setVolume(0.8); // 80% volume
setPlaybackRate(rate)
Set playback speed (0.5-2)
player.setPlaybackRate(1.5); // 1.5x speed
Dynamic Loading
loadTrack(url, title, subtitle, options)
Load a new track dynamically
await player.loadTrack(
'new-song.mp3',
'Song Title',
'Artist Name',
{
markers: [{time: 30, label: 'Chorus'}],
artwork: 'cover.jpg'
}
);
Lifecycle
destroy()
Destroy the player instance and clean up
player.destroy();
Static Methods
WaveformPlayer.getInstance(id)
Get player instance by ID or element
const player = WaveformPlayer.getInstance('my-player');
WaveformPlayer.getAllInstances()
Get all player instances
const players = WaveformPlayer.getAllInstances();
WaveformPlayer.destroyAll()
Destroy all player instances
WaveformPlayer.destroyAll();
WaveformPlayer.generateWaveformData(url, samples)
Generate waveform data from audio URL
const data = await WaveformPlayer.generateWaveformData('audio.mp3', 200);
Events
DOM Events
The player emits custom DOM events on the container element:
const container = document.getElementById('player');
container.addEventListener('waveformplayer:ready', (e) => {
console.log('Player ready', e.detail);
});
container.addEventListener('waveformplayer:play', (e) => {
console.log('Playing', e.detail);
});
container.addEventListener('waveformplayer:pause', (e) => {
console.log('Paused', e.detail);
});
container.addEventListener('waveformplayer:ended', (e) => {
console.log('Ended', e.detail);
});
container.addEventListener('waveformplayer:timeupdate', (e) => {
console.log('Time:', e.detail.currentTime, '/', e.detail.duration);
});
Callbacks
You can also use callback functions in the options:
new WaveformPlayer('#player', {
url: 'audio.mp3',
onLoad: (player) => {
console.log('Audio loaded');
},
onPlay: (player) => {
console.log('Playback started');
},
onPause: (player) => {
console.log('Playback paused');
},
onEnd: (player) => {
console.log('Playback ended');
},
onTimeUpdate: (currentTime, duration, player) => {
console.log(`Progress: ${currentTime}/${duration}`);
},
onError: (error, player) => {
console.error('Error:', error);
}
});
Framework Integration
WaveformPlayer works with any JavaScript framework. Here are examples for popular frameworks:
React Integration
Function Component with Hooks
import { useEffect, useRef } from 'react';
import WaveformPlayer from '@arraypress/waveform-player';
import '@arraypress/waveform-player/dist/waveform-player.css';
function AudioPlayer({ url, title }) {
const playerRef = useRef();
const instanceRef = useRef();
useEffect(() => {
// Create player
instanceRef.current = new WaveformPlayer(playerRef.current, {
url,
title,
waveformStyle: 'mirror'
});
// Cleanup
return () => {
instanceRef.current?.destroy();
};
}, [url, title]);
return (
<div>
<div ref={playerRef} />
<button onClick={() => instanceRef.current?.play()}>
Play
</button>
</div>
);
}
Custom Hook
import { useEffect, useRef } from 'react';
import WaveformPlayer from '@arraypress/waveform-player';
export function useWaveformPlayer(options) {
const containerRef = useRef();
const playerRef = useRef();
useEffect(() => {
if (containerRef.current && options.url) {
playerRef.current = new WaveformPlayer(
containerRef.current,
options
);
return () => playerRef.current?.destroy();
}
}, [options.url]);
return {
containerRef,
player: playerRef.current,
play: () => playerRef.current?.play(),
pause: () => playerRef.current?.pause(),
seekTo: (time) => playerRef.current?.seekTo(time)
};
}
Vue Integration
Vue 3 Composition API
<template>
<div>
<div ref="playerEl"></div>
<button @click="play">Play</button>
<button @click="pause">Pause</button>
</div>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue';
import WaveformPlayer from '@arraypress/waveform-player';
import '@arraypress/waveform-player/dist/waveform-player.css';
const props = defineProps({
url: String,
title: String
});
const playerEl = ref();
let player = null;
onMounted(() => {
player = new WaveformPlayer(playerEl.value, {
url: props.url,
title: props.title
});
});
onUnmounted(() => {
player?.destroy();
});
const play = () => player?.play();
const pause = () => player?.pause();
</script>
Angular Integration
import { Component, OnInit, OnDestroy, ViewChild, ElementRef } from '@angular/core';
import WaveformPlayer from '@arraypress/waveform-player';
@Component({
selector: 'app-audio-player',
template: `
<div #playerContainer></div>
<button (click)="play()">Play</button>
<button (click)="pause()">Pause</button>
`,
styleUrls: [
'node_modules/@arraypress/waveform-player/dist/waveform-player.css'
]
})
export class AudioPlayerComponent implements OnInit, OnDestroy {
@ViewChild('playerContainer', { static: true })
playerContainer: ElementRef;
private player: any;
ngOnInit() {
this.player = new WaveformPlayer(
this.playerContainer.nativeElement,
{
url: 'audio.mp3',
title: 'My Song'
}
);
}
ngOnDestroy() {
this.player?.destroy();
}
play() {
this.player?.play();
}
pause() {
this.player?.pause();
}
}
WordPress Integration
Theme Integration
// In functions.php
function enqueue_waveform_player() {
wp_enqueue_style(
'waveform-player',
'https://unpkg.com/@arraypress/waveform-player@latest/dist/waveform-player.css'
);
wp_enqueue_script(
'waveform-player',
'https://unpkg.com/@arraypress/waveform-player@latest/dist/waveform-player.js',
array(),
null,
true
);
}
add_action('wp_enqueue_scripts', 'enqueue_waveform_player');
Shortcode
// Create shortcode
function waveform_player_shortcode($atts) {
$atts = shortcode_atts(array(
'url' => '',
'title' => '',
'style' => 'mirror',
'height' => '60'
), $atts);
return sprintf(
'<div data-waveform-player
data-url="%s"
data-title="%s"
data-waveform-style="%s"
data-height="%s"></div>',
esc_attr($atts['url']),
esc_attr($atts['title']),
esc_attr($atts['style']),
esc_attr($atts['height'])
);
}
add_shortcode('waveform', 'waveform_player_shortcode');
// Usage in posts/pages
[waveform url="audio.mp3" title="My Song" style="mirror"]
Gutenberg Block
// Basic Gutenberg block
registerBlockType('my-plugin/waveform-player', {
title: 'Waveform Player',
icon: 'format-audio',
category: 'media',
attributes: {
url: { type: 'string' },
title: { type: 'string' },
style: { type: 'string', default: 'mirror' }
},
edit: (props) => {
// Editor interface
},
save: (props) => {
return el('div', {
'data-waveform-player': true,
'data-url': props.attributes.url,
'data-title': props.attributes.title,
'data-waveform-style': props.attributes.style
});
}
});
Keyboard Controls
When a player is focused (click on it), these keyboard controls are available:
| Key | Action |
|---|---|
| Space | Play/Pause |
| ← / → | Seek backward/forward 5 seconds |
| ↑ / ↓ | Volume up/down 10% |
| M | Mute/Unmute |
| 0 - 9 | Jump to 0% - 90% of track |
Media Session API
WaveformPlayer integrates with the Media Session API for system-level media controls:
- Lock screen controls on mobile
- Media key support (play/pause/prev/next)
- Bluetooth device controls
- Browser media hub integration
- Picture-in-picture support (where available)
new WaveformPlayer('#player', {
url: 'audio.mp3',
title: 'Song Title',
subtitle: 'Artist Name',
album: 'Album Name',
artwork: 'cover.jpg',
enableMediaSession: true // Enabled by default
});
Performance Optimization
Pre-generated Waveforms
For best performance, generate waveform data on the server:
// 1. Generate waveform data once (server-side or build time)
const waveformData = await WaveformPlayer.generateWaveformData('audio.mp3', 200);
// 2. Store in database/JSON file
saveToDatabase(audioId, waveformData);
// 3. Use pre-generated data for instant display
new WaveformPlayer('#player', {
url: 'audio.mp3',
waveform: waveformData // No client-side processing!
});
Lazy Loading
For pages with many players, use intersection observer:
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
// Initialize player when visible
new WaveformPlayer(entry.target);
observer.unobserve(entry.target);
}
});
});
// Observe all player containers
document.querySelectorAll('[data-waveform-player]').forEach(el => {
observer.observe(el);
});
Best Practices
- Use
samples: 100-200for optimal balance - Pre-generate waveforms for known audio files
- Use
preload="none"for many players on one page - Destroy unused instances with
player.destroy() - Use simpler styles (seekbar) for better mobile performance