Skip to main content

Overview

The MediaElementPlaylistProvider is a simplified provider for single-track audio playback using the browser’s native HTMLAudioElement. It provides pitch-preserving playback rate control (0.5x - 2.0x speed) without requiring audio decoding or the Tone.js library. Use this provider for:
  • Language learning apps (speed control for practice)
  • Podcast players
  • Single-track audio viewers
  • Simple annotation playback
  • Use cases where you need speed control without pitch shift
For multi-track editing, use WaveformPlaylistProvider instead.

Key Differences from WaveformPlaylistProvider

FeatureMediaElementPlaylistProviderWaveformPlaylistProvider
Audio EngineHTMLAudioElementTone.js (Web Audio API)
Playback Speed0.5x - 2.0x (pitch-preserving)1.0x only
TracksSingle track onlyMultiple tracks
Audio EffectsNot supportedFull Tone.js effects
Waveform DataPre-computed (required)Generated from AudioBuffer
Track EditingView onlyFull editing (move, trim, split)
RecordingNot supportedSupported
Bundle SizeSmaller (no Tone.js)Larger

Import

import { MediaElementPlaylistProvider } from '@waveform-playlist/browser';

Props

track
MediaElementTrackConfig
required
Single track configuration with source URL and pre-computed waveform data.
track={{
  source: 'https://example.com/audio.mp3',
  waveformData: peaksData,  // From BBC audiowaveform or similar
  name: 'My Audio'
}}
samplesPerPixel
number
default:"1024"
Zoom level in samples per pixel. Lower values = more zoomed in.Note: Unlike WaveformPlaylistProvider, zoom is fixed (no zoom in/out controls).
waveHeight
number
default:"100"
Height in pixels of the waveform track.
timescale
boolean
default:"false"
Show timescale ruler above waveform.
playbackRate
number
default:"1.0"
Initial playback speed. Valid range: 0.5 to 2.0.
  • 0.5 = half speed (50%)
  • 1.0 = normal speed
  • 2.0 = double speed (200%)
Speed changes preserve pitch (no “chipmunk effect”).
automaticScroll
boolean
default:"false"
Automatically scroll to keep the playhead centered during playback.
theme
Partial<WaveformPlaylistTheme>
Custom theme to override default colors and styling.See Theming documentation for available options.
controls
{ show: boolean; width: number }
default:"{ show: false, width: 0 }"
Configuration for track control panel area.
controls={{ show: true, width: 100 }}
annotationList
object
Configuration for annotations display.
barWidth
number
default:"1"
Width in pixels of waveform bars.
barGap
number
default:"0"
Spacing in pixels between waveform bars.
progressBarWidth
number
default:"barWidth + barGap"
Width in pixels of progress bars during playback.
onAnnotationsChange
(annotations: AnnotationData[]) => void
Callback fired when annotations are edited.
onAnnotationsChange={(updated) => setAnnotations(updated)}
onReady
() => void
Callback fired when the audio element is ready for playback.

Context Hooks

The provider exposes four separate contexts optimized for performance.

useMediaElementAnimation

Access playback state and timing.
import { useMediaElementAnimation } from '@waveform-playlist/browser';

function MyComponent() {
  const { 
    isPlaying,      // boolean
    currentTime,    // number (seconds)
    currentTimeRef  // RefObject<number>
  } = useMediaElementAnimation();
}

useMediaElementState

Access playlist state (annotations, playback rate).
import { useMediaElementState } from '@waveform-playlist/browser';

function MyComponent() {
  const {
    continuousPlay,
    annotations,
    activeAnnotationId,
    playbackRate,
    isAutomaticScroll
  } = useMediaElementState();
}

useMediaElementControls

Access control functions for playback and settings.
import { useMediaElementControls } from '@waveform-playlist/browser';

function PlaybackControls() {
  const { 
    play, 
    pause, 
    stop, 
    seekTo,
    setPlaybackRate 
  } = useMediaElementControls();
  
  return (
    <div>
      <button onClick={() => play()}>Play</button>
      <button onClick={pause}>Pause</button>
      <button onClick={stop}>Stop</button>
      <button onClick={() => setPlaybackRate(0.75)}>0.75x</button>
      <button onClick={() => setPlaybackRate(1.0)}>1.0x</button>
      <button onClick={() => setPlaybackRate(1.5)}>1.5x</button>
    </div>
  );
}

useMediaElementData

Access playlist data (duration, waveform peaks).
import { useMediaElementData } from '@waveform-playlist/browser';

function MyComponent() {
  const {
    duration,
    peaksDataArray,
    sampleRate,
    waveHeight,
    samplesPerPixel
  } = useMediaElementData();
}

Complete Example

import { useState } from 'react';
import { 
  MediaElementPlaylistProvider,
  useMediaElementControls,
  useMediaElementState,
  useMediaElementAnimation
} from '@waveform-playlist/browser';
import { parseAeneas } from '@waveform-playlist/annotations';

function SpeedControl() {
  const { setPlaybackRate } = useMediaElementControls();
  const { playbackRate } = useMediaElementState();
  
  return (
    <div>
      <label>Speed: {playbackRate}x</label>
      <input
        type="range"
        min="0.5"
        max="2.0"
        step="0.1"
        value={playbackRate}
        onChange={(e) => setPlaybackRate(parseFloat(e.target.value))}
      />
    </div>
  );
}

function PlaybackControls() {
  const { play, pause, stop } = useMediaElementControls();
  const { isPlaying } = useMediaElementAnimation();
  
  return (
    <div>
      <button onClick={() => play()}>
        {isPlaying ? 'Pause' : 'Play'}
      </button>
      <button onClick={stop}>Stop</button>
    </div>
  );
}

function App() {
  const [annotations, setAnnotations] = useState([
    parseAeneas({ begin: "0.000", end: "2.500", id: "1", lines: ["First segment"] }),
    parseAeneas({ begin: "2.500", end: "5.000", id: "2", lines: ["Second segment"] })
  ]);
  
  // Waveform data from BBC audiowaveform or similar tool
  const waveformData = {
    sample_rate: 44100,
    samples_per_pixel: 512,
    bits: 8,
    length: 2000,
    data: [...], // Int8Array of peak values
    duration: 45.2
  };
  
  return (
    <MediaElementPlaylistProvider
      track={{
        source: 'https://example.com/podcast-episode.mp3',
        waveformData: waveformData,
        name: 'Episode 42'
      }}
      waveHeight={120}
      samplesPerPixel={1024}
      playbackRate={1.0}
      automaticScroll
      timescale
      annotationList={{
        annotations,
        isContinuousPlay: true
      }}
      onAnnotationsChange={setAnnotations}
      onReady={() => console.log('Audio ready!')}
    >
      <PlaybackControls />
      <SpeedControl />
      {/* Your waveform visualization components */}
    </MediaElementPlaylistProvider>
  );
}

Generating Waveform Data

Since this provider doesn’t decode audio, you must provide pre-computed waveform data. Use tools like:
# Install
brew install audiowaveform

# Generate waveform data
audiowaveform -i input.mp3 -o output.json -z 512

Server-side with audiowaveform

const { exec } = require('child_process');

function generateWaveform(inputPath, outputPath) {
  return new Promise((resolve, reject) => {
    exec(
      `audiowaveform -i ${inputPath} -o ${outputPath} -z 512 --pixels-per-second 50`,
      (error, stdout, stderr) => {
        if (error) reject(error);
        else resolve(outputPath);
      }
    );
  });
}
The JSON output from audiowaveform is compatible with the waveformData prop.

Use Cases

Language Learning App

<MediaElementPlaylistProvider
  track={{ source: lessonAudioUrl, waveformData, name: 'Lesson 5' }}
  playbackRate={0.75}  // Slow down for beginners
  annotationList={{
    annotations: sentenceAnnotations,
    isContinuousPlay: false  // Stop after each sentence
  }}
>
  {/* UI for sentence-by-sentence playback */}
</MediaElementPlaylistProvider>

Podcast Player

<MediaElementPlaylistProvider
  track={{ source: episodeUrl, waveformData, name: episode.title }}
  playbackRate={1.5}  // Faster playback for regular listeners
  automaticScroll
  annotationList={{
    annotations: chapterMarkers,
    isContinuousPlay: true
  }}
>
  {/* UI with chapter navigation */}
</MediaElementPlaylistProvider>

Interview Transcription

<MediaElementPlaylistProvider
  track={{ source: interviewUrl, waveformData, name: 'Interview' }}
  playbackRate={0.8}  // Slightly slower for transcription
  annotationList={{
    annotations: transcriptSegments,
    isContinuousPlay: false
  }}
>
  {/* UI for reviewing and editing transcript timing */}
</MediaElementPlaylistProvider>

Build docs developers (and LLMs) love