@waveform-playlist/media-element-playout
v12.3.2
Published
HTMLMediaElement-based playout engine for waveform-playlist with pitch-preserving playback rate
Maintainers
Readme
@waveform-playlist/media-element-playout
A lightweight, HTMLMediaElement-based playout engine for waveform-playlist with pitch-preserving playback rate control.
Features
- Pitch-preserving playback rate (0.25x - 4.0x) - uses browser's built-in time-stretching
- Pre-computed peaks - no AudioBuffer decoding required, instant visualization
- Lightweight - no Tone.js dependency
- Simple API - designed for single-track playback use cases
When to Use
Use MediaElementPlayout when you need:
- Playback speed control for language learning, podcasts, etc.
- Single-track playback with minimal overhead
- Quick load times with pre-computed peaks
Use TonePlayout from @waveform-playlist/playout when you need:
- Multi-track mixing and editing
- Clip-level effects and fades
- Precise sample-accurate timing
Installation
npm install @waveform-playlist/media-element-playoutUsage
import { MediaElementPlayout } from '@waveform-playlist/media-element-playout';
import WaveformData from 'waveform-data';
// Load pre-computed peaks
const response = await fetch('/audio/podcast.dat');
const arrayBuffer = await response.arrayBuffer();
const peaks = WaveformData.create(arrayBuffer);
// Create playout
const playout = new MediaElementPlayout({
masterVolume: 1.0,
playbackRate: 1.0,
});
// Add a track
playout.addTrack({
source: '/audio/podcast.mp3', // URL or Blob URL
peaks: peaks,
name: 'Podcast Episode 1',
});
// Control playback
playout.play(0); // Play from beginning
playout.setPlaybackRate(0.75); // Slow down to 75% speed (pitch preserved)
playout.pause();
playout.seekTo(30); // Seek to 30 seconds
playout.resume(); // Resume from the current position (does NOT reset to 0)
// Clean up
playout.dispose();API
MediaElementPlayout
interface MediaElementPlayoutOptions {
masterVolume?: number; // 0.0 to 1.0 (default: 1.0)
playbackRate?: number; // 0.25 to 4.0 (default: 1.0)
}
class MediaElementPlayout {
// Lifecycle
init(): Promise<void>; // No-op for media element
dispose(): void;
// Track management
addTrack(options: MediaElementTrackOptions): MediaElementTrack;
setSource(options: MediaElementTrackOptions): MediaElementTrack; // silent in-place replace
removeTrack(trackId: string): void;
getTrack(trackId: string): MediaElementTrack | undefined;
// Playback
play(when?: number, offset?: number, duration?: number): void;
resume(): void; // resume from current position (no reset to 0)
pause(): void;
stop(): void;
seekTo(time: number): void;
getCurrentTime(): number;
// Lifecycle events (typed via MediaElementTrackEvents)
on<K extends keyof MediaElementTrackEvents>(event: K, listener: MediaElementTrackEvents[K]): void;
off<K extends keyof MediaElementTrackEvents>(event: K, listener: MediaElementTrackEvents[K]): void;
// Volume & Rate
setMasterVolume(volume: number): void;
setPlaybackRate(rate: number): void; // 0.25 to 4.0, pitch preserved
// State
readonly isPlaying: boolean;
readonly duration: number;
readonly playbackRate: number;
}MediaElementTrack
interface MediaElementTrackOptions {
source: string | HTMLAudioElement; // URL or audio element
peaks?: WaveformDataObject; // Pre-computed peaks (optional — omit for scrubber-only / headless players)
id?: string;
name?: string;
volume?: number;
playbackRate?: number;
}Player Mode
Beyond the timeline/editor API, three affordances make this engine pleasant to
reuse as a single-track player (podcast/audiobook players, <daw-player>):
// Resume from the current position (play() with no offset resets to 0)
playout.resume();
// Swap to the next source in place — no "Only one track is supported" warning,
// and any Web Audio routing/effects are preserved across the swap
playout.setSource({ source: '/audio/episode-2.mp3', name: 'Episode 2' });
// Observe media lifecycle without reaching into the audio element
playout.on('loadedmetadata', () => console.log('duration:', playout.duration));
playout.on('play', () => updateTransportUI('playing'));
playout.on('pause', () => updateTransportUI('paused'));
playout.on('error', (err) => surfaceError(err));
playout.off('play', handler); // unsubscribeon() listeners are retained across setSource() swaps — register them once.
The same on()/off() and resume()/load() methods exist on MediaElementTrack
for power users. Event names and payloads are typed via MediaElementTrackEvents.
Generating Peaks
Use audiowaveform or waveform-data.js to pre-compute peaks:
# Generate peaks file with audiowaveform
audiowaveform -i audio.mp3 -o peaks.dat -b 16Browser Support
Pitch-preserving playback rate is supported in:
- Chrome 77+
- Firefox 20+
- Safari 14.1+
- Edge 79+
Older browsers will still work but may change pitch with speed.
License
MIT
