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
Show timescale ruler above waveforms.
Render waveforms in mono mode (combine stereo channels).
Height in pixels of each waveform track.
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.
Automatically scroll to keep the playhead centered during playback.
theme
Partial<WaveformPlaylistTheme>
controls
{ show: boolean; width: number }
default: "{ show: false, width: 0 }"
Configuration for track control panels (mute, solo, volume, pan). controls = {{ show : true , width : 150 }}
Configuration for annotations display and editing. Show Annotation configuration properties
Array of annotation segments. Must be pre-parsed with numeric start and end values. Use parseAeneas() from @waveform-playlist/annotations to parse raw annotation data.
Allow dragging annotation boundaries to edit timing.
When enabled, playback continues across annotation boundaries. When disabled, playback stops at the end of the current annotation.
Link adjacent annotation endpoints. When editing one annotation’s end, the next annotation’s start moves to match.
Array of available annotation controls (e.g., delete, merge, split).
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 );
}}
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.
Width in pixels of waveform bars.
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 ();
}
Show PlaybackAnimation values
Whether audio is currently playing.
Current playback position in seconds. Updated on discrete events (play/pause/seek).
Ref to current time for animation loops. Access via currentTimeRef.current in requestAnimationFrame callbacks.
Audio context time when playback started. Used for calculating elapsed time.
Audio position when playback started. Used with elapsed time to calculate current position.
Returns current playback time from the engine. Auto-wraps at loop boundaries. Use this in animation loops for accurate timing instead of reading state.
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 ();
}
Show PlaylistState values
Whether continuous play is enabled for annotations.
Whether adjacent annotation endpoints are linked.
Whether annotations can be edited by dragging.
Whether automatic scroll is enabled to keep playhead centered.
Whether looping is enabled.
Current annotations array.
ID of the currently active annotation during playback.
Start time of current selection in seconds.
End time of current selection in seconds.
ID of currently selected track for editing operations.
Start time of loop region in seconds.
End time of loop region in seconds.
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
};
}
play
(startTime?: number, playDuration?: number) => Promise<void>
Start playback. Optionally specify start time and duration.
Pause playback at current position.
Stop playback and return cursor to start position.
Seek to a specific time in seconds.
Set current time without seeking (updates UI only).
setTrackMute
(trackIndex: number, muted: boolean) => void
Mute or unmute a track.
setTrackSolo
(trackIndex: number, soloed: boolean) => void
Solo or unsolo a track.
setTrackVolume
(trackIndex: number, volume: number) => void
Set track volume (0.0 to 1.0).
setTrackPan
(trackIndex: number, pan: number) => void
Set track pan (-1.0 to 1.0).
Show Selection & editing controls
setSelection
(start: number, end: number) => void
Set time selection range in seconds.
setSelectedTrackId
(trackId: string | null) => void
Select a track for editing operations.
Zoom in to the next zoom level.
Zoom out to the previous zoom level.
Show Time format controls
setTimeFormat
(format: TimeFormat) => void
Set time display format (‘seconds’, ‘hh:mm:ss’, etc.).
formatTime
(seconds: number) => string
Format a time value according to current time format.
Show Volume & scroll controls
Set master volume (0.0 to 1.0).
setAutomaticScroll
(enabled: boolean) => void
Enable or disable automatic scrolling.
setScrollContainer
(element: HTMLDivElement | null) => void
Set the scroll container element for automatic scrolling.
scrollContainerRef
RefObject<HTMLDivElement | null>
Ref to the scroll container element.
setContinuousPlay
(enabled: boolean) => void
Enable or disable continuous play for annotations.
setLinkEndpoints
(enabled: boolean) => void
Enable or disable linked annotation endpoints.
setAnnotationsEditable
(enabled: boolean) => void
Enable or disable annotation editing.
setAnnotations
Dispatch<SetStateAction<AnnotationData[]>>
Update annotations. Calls onAnnotationsChange callback.
setActiveAnnotationId
(id: string | null) => void
Set the active annotation ID.
setLoopEnabled
(enabled: boolean) => void
Enable or disable looping.
setLoopRegion
(start: number, end: number) => void
Set loop region start and end times in seconds.
setLoopRegionFromSelection
Set loop region to match current selection.
Clear loop region (sets both to 0).
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 ();
}
Total timeline duration in seconds.
Array of decoded audio buffers for all tracks.
Array of waveform peak data for rendering. Each track contains an array of clip peaks.
Array of track UI states (muted, soloed, volume, pan).
Original tracks array with IDs and clip data.
Audio sample rate (typically 44100 or 48000 Hz).
Height in pixels of each waveform track.
Height in pixels of timescale ruler (0 if disabled).
Minimum height in pixels for the entire playlist.
controls
{ show: boolean; width: number }
Track controls configuration.
playoutRef
RefObject<PlaylistEngine | null>
Ref to the underlying playlist engine instance.
Current zoom level in samples per pixel.
Current time display format.
Master volume (0.0 to 1.0).
Whether zooming in further is possible.
Whether zooming out further is possible.
Width in pixels of waveform bars.
Gap in pixels between waveform bars.
Width in pixels of progress bars during playback.
Whether the playlist has finished loading all tracks.
Whether waveforms are rendered in mono mode.
isDraggingRef
MutableRefObject<boolean>
Ref tracking whether a clip boundary trim drag is in progress.
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 >
);
}