Skip to main content

Overview

The WaveformPlaylistProvider is the primary provider for multi-track audio editing. It provides full audio playback, editing capabilities, effects processing, and waveform visualization using Tone.js as the audio engine. Use this provider for:
  • Multi-track audio editing
  • Complex audio effects chains
  • Recording and mixing
  • Full playlist manipulation (move, trim, split clips)
For single-track playback with speed control, use MediaElementPlaylistProvider instead.

Import

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

Props

tracks
ClipTrack[]
required
Array of tracks with clips. Each track contains audio clips positioned on the timeline.See ClipTrack type documentation for details.
timescale
boolean
default:"false"
Show timescale ruler above waveforms.
mono
boolean
default:"false"
Render waveforms in mono mode (combine stereo channels).
waveHeight
number
default:"80"
Height in pixels of each waveform track.
samplesPerPixel
number
default:"1024"
Initial zoom level. Lower values = more zoomed in. Must match one of the zoom levels.
zoomLevels
number[]
default:"[256, 512, 1024, 2048, 4096, 8192]"
Array of available zoom levels in samples per pixel. Users can zoom between these levels.
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 panels (mute, solo, volume, pan).
controls={{ show: true, width: 150 }}
annotationList
object
Configuration for annotations display and editing.
effects
EffectsFunction
Master effects chain function. Processes all tracks through the specified effects.See Audio Effects guide for usage.
effects={(input, output) => {
  const reverb = new Reverb();
  input.connect(reverb).connect(output);
}}
onReady
() => void
Callback fired when all tracks have loaded and the playlist is ready for playback.
onAnnotationsChange
(annotations: AnnotationData[]) => void
Callback fired when annotations are edited (drag, split, merge, delete).Required if annotationList.editable is true. Without this callback, edits will not persist.
onAnnotationsChange={(updated) => setAnnotations(updated)}
onAnnotationUpdate
(annotations: AnnotationData[]) => void
deprecated
Deprecated. Use onAnnotationsChange instead.
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. Defaults to fill gaps between bars.
onTracksChange
(tracks: ClipTrack[]) => void
Callback when engine operations (move, trim, split) modify tracks.Important: You must pass the received tracks reference back as the tracks prop (i.e., setState(tracks)). The provider uses reference identity to detect engine-originated updates and skip expensive rebuilds.
const [tracks, setTracks] = useState(initialTracks);

<WaveformPlaylistProvider
  tracks={tracks}
  onTracksChange={setTracks}  // Pass reference directly
>

Context Hooks

The provider exposes four separate contexts for optimal performance. Components only re-render when their specific context data changes.

usePlaybackAnimation

Access playback timing and animation state. This context updates on discrete events (play/pause/stop/seek), not during the animation loop itself.
import { usePlaybackAnimation } from '@waveform-playlist/browser';

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

usePlaylistState

Access playlist state (selection, loop, annotations).
import { usePlaylistState } from '@waveform-playlist/browser';

function MyComponent() {
  const {
    continuousPlay,
    linkEndpoints,
    annotationsEditable,
    isAutomaticScroll,
    isLoopEnabled,
    annotations,
    activeAnnotationId,
    selectionStart,
    selectionEnd,
    selectedTrackId,
    loopStart,
    loopEnd
  } = usePlaylistState();
}

usePlaylistControls

Access control functions for playback, tracks, selection, zoom, and more.
import { usePlaylistControls } from '@waveform-playlist/browser';

function MyComponent() {
  const { play, pause, stop, seekTo, setSelection } = usePlaylistControls();
  
  const handlePlay = () => {
    play(); // Play from current position
    // or
    play(5.0); // Play from 5 seconds
    // or
    play(5.0, 10.0); // Play 10 seconds starting at 5s
  };
}

usePlaylistData

Access playlist data (duration, audio buffers, waveform peaks, track states).
import { usePlaylistData } from '@waveform-playlist/browser';

function MyComponent() {
  const {
    duration,
    tracks,
    trackStates,
    audioBuffers,
    peaksDataArray,
    sampleRate,
    isReady
  } = usePlaylistData();
}

Complete Example

import { useState } from 'react';
import { 
  WaveformPlaylistProvider,
  usePlaylistControls,
  usePlaylistState,
  usePlaylistData
} from '@waveform-playlist/browser';
import { parseAeneas } from '@waveform-playlist/annotations';

// Load your audio and annotations
const initialTracks = [
  {
    id: 'track-1',
    name: 'Vocals',
    clips: [
      {
        id: 'clip-1',
        audioBuffer: vocalsBuffer,  // AudioBuffer from fetch + decodeAudioData
        startSample: 0,
        offsetSamples: 0,
        durationSamples: vocalsBuffer.length,
        sampleRate: vocalsBuffer.sampleRate,
      }
    ],
    volume: 1.0,
    muted: false,
    soloed: false,
    pan: 0,
  },
  {
    id: 'track-2',
    name: 'Instrumental',
    clips: [
      {
        id: 'clip-2',
        audioBuffer: instrumentalBuffer,
        startSample: 0,
        offsetSamples: 0,
        durationSamples: instrumentalBuffer.length,
        sampleRate: instrumentalBuffer.sampleRate,
      }
    ],
    volume: 0.8,
    muted: false,
    soloed: false,
    pan: 0,
  }
];

function PlaybackControls() {
  const { play, pause, stop, seekTo } = usePlaylistControls();
  const { isPlaying, currentTime } = usePlaybackAnimation();
  
  return (
    <div>
      <button onClick={() => play()}>
        {isPlaying ? 'Pause' : 'Play'}
      </button>
      <button onClick={stop}>Stop</button>
      <div>Time: {currentTime.toFixed(2)}s</div>
    </div>
  );
}

function App() {
  const [tracks, setTracks] = useState(initialTracks);
  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"] })
  ]);
  
  return (
    <WaveformPlaylistProvider
      tracks={tracks}
      onTracksChange={setTracks}
      waveHeight={100}
      samplesPerPixel={1024}
      zoomLevels={[256, 512, 1024, 2048, 4096]}
      automaticScroll
      timescale
      controls={{ show: true, width: 150 }}
      annotationList={{
        annotations,
        editable: true,
        isContinuousPlay: false,
        linkEndpoints: true
      }}
      onAnnotationsChange={setAnnotations}
      onReady={() => console.log('Playlist ready!')}
    >
      <PlaybackControls />
      {/* Your waveform visualization components */}
    </WaveformPlaylistProvider>
  );
}

Build docs developers (and LLMs) love