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:

  1. Website background color - Calculates brightness to determine light/dark theme
  2. Theme classes - Checks for common patterns like .dark, data-theme="dark"
  3. System preference - Falls back to prefers-color-scheme media query
  4. 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-200 for 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