Skip to main content

WaveformPlaylistProvider

The WaveformPlaylistProvider is the core component that manages all playlist state and provides context to child components. It uses the Web Audio API (via Tone.js) for multi-track playback, mixing, and effects processing.

Single Instance Per Page

Only one WaveformPlaylistProvider should be mounted at a time. It uses a shared Tone.js Transport singleton for scheduling — multiple instances will conflict with each other's playback, timing, and effects. For multiple independent players on the same page, use MediaElementPlaylistProvider which creates independent HTMLAudioElement instances.

Import

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

Basic Usage

<WaveformPlaylistProvider
tracks={tracks}
samplesPerPixel={1024}
waveHeight={128}
timescale
>
<Waveform />
</WaveformPlaylistProvider>

Props

Required Props

tracks

Type: ClipTrack[]

Array of track objects to display. Use useAudioTracks to create tracks from audio files.

const { tracks, loading } = useAudioTracks([
{ src: '/audio/track.mp3', name: 'Track' },
]);

<WaveformPlaylistProvider tracks={tracks}>

Display Props

samplesPerPixel

Type: number Default: 1024

Zoom level. Higher values show more audio, lower values show more detail.

ValueUse Case
256Detailed editing
512Close view
1024Default view
2048Overview
4096+Long files

waveHeight

Type: number Default: 128

Height of each track in pixels.

timescale

Type: boolean Default: false

Show the time ruler at the top of the playlist.

<WaveformPlaylistProvider tracks={tracks} timescale>

controls

Type: { show: boolean; width: number } Default: { show: false, width: 200 }

Track controls panel configuration.

<WaveformPlaylistProvider
tracks={tracks}
controls={{ show: true, width: 180 }}
>

Theming

theme

Type: Partial<WaveformPlaylistTheme> Default: defaultTheme

Custom theme object. See Theming Guide.

<WaveformPlaylistProvider
tracks={tracks}
theme={{
waveColor: '#00ff00',
cursorColor: '#ff0000',
}}
>

Audio Effects

effects

Type: EffectsFunction

Master effects chain function from useDynamicEffects().

Annotations

annotationList

Type: { annotations?: any[]; editable?: boolean; isContinuousPlay?: boolean; linkEndpoints?: boolean; controls?: any[] }

Annotation configuration. When using editable annotations, pair with onAnnotationsChange.

onAnnotationsChange

Type: (annotations: AnnotationData[]) => void

Callback when annotations change. Required for editable annotations to persist.

Callbacks

onTracksChange

Type: (tracks: ClipTrack[]) => void

Called when engine clip operations (move, trim, split) update the tracks. Use this to keep your parent state in sync with engine mutations:

const [tracks, setTracks] = useState<ClipTrack[]>(initialTracks);

<WaveformPlaylistProvider tracks={tracks} onTracksChange={setTracks}>

onReady

Type: () => void

Called when all tracks finish loading.

Waveform Rendering

barWidth

Type: number Default: 1

Width in pixels of waveform bars.

barGap

Type: number Default: 0

Spacing in pixels between waveform bars.

progressBarWidth

Type: number Default: barWidth + barGap

Width in pixels of progress bars.

mono

Type: boolean Default: false

Render mono waveforms.

zoomLevels

Type: number[]

Array of zoom levels in samples per pixel.

automaticScroll

Type: boolean Default: false

Auto-scroll to keep playhead visible.

deferEngineRebuild

Type: boolean Default: false

When true, the provider skips rebuilding the audio engine when tracks change. Set this to the loading value from useAudioTracks when using { immediate: true } mode — placeholder tracks render instantly while audio decodes, then the engine builds once when all tracks are ready.

const { tracks, loading } = useAudioTracks(configs, { immediate: true });

<WaveformPlaylistProvider tracks={tracks} deferEngineRebuild={loading}>
<Waveform />
</WaveformPlaylistProvider>

Context Values

The provider exposes state and controls through React Context. Access them using the provided hooks.

Playback Animation (usePlaybackAnimation)

interface PlaybackAnimationContextValue {
isPlaying: boolean;
currentTime: number;
currentTimeRef: RefObject<number>;
playbackStartTimeRef: RefObject<number>;
audioStartPositionRef: RefObject<number>;
/** Returns current playback time from engine (auto-wraps at loop boundaries). */
getPlaybackTime: () => number;
}

State (usePlaylistState)

interface PlaylistStateContextValue {
continuousPlay: boolean;
linkEndpoints: boolean;
annotationsEditable: boolean;
isAutomaticScroll: boolean;
isLoopEnabled: boolean;
annotations: AnnotationData[];
activeAnnotationId: string | null;
selectionStart: number;
selectionEnd: number;
selectedTrackId: string | null;
loopStart: number;
loopEnd: number;
/** Whether playback continues past the end of loaded audio */
indefinitePlayback: boolean;
}

Controls (usePlaylistControls)

interface PlaylistControlsContextValue {
play: (startTime?: number, playDuration?: number) => Promise<void>;
pause: () => void;
stop: () => void;
seekTo: (time: number) => void;
setCurrentTime: (time: number) => void;
setTrackMute: (trackIndex: number, muted: boolean) => void;
setTrackSolo: (trackIndex: number, soloed: boolean) => void;
setTrackVolume: (trackIndex: number, volume: number) => void;
setTrackPan: (trackIndex: number, pan: number) => void;
setSelection: (start: number, end: number) => void;
setSelectedTrackId: (trackId: string | null) => void;
setTimeFormat: (format: TimeFormat) => void;
formatTime: (seconds: number) => string;
zoomIn: () => void;
zoomOut: () => void;
setMasterVolume: (volume: number) => void;
setAutomaticScroll: (enabled: boolean) => void;
setScrollContainer: (element: HTMLDivElement | null) => void;
scrollContainerRef: RefObject<HTMLDivElement | null>;
setContinuousPlay: (enabled: boolean) => void;
setLinkEndpoints: (enabled: boolean) => void;
setAnnotationsEditable: (enabled: boolean) => void;
setAnnotations: Dispatch<SetStateAction<AnnotationData[]>>;
setActiveAnnotationId: (id: string | null) => void;
setLoopEnabled: (enabled: boolean) => void;
setLoopRegion: (start: number, end: number) => void;
setLoopRegionFromSelection: () => void;
clearLoopRegion: () => void;
}

Data (usePlaylistData)

interface PlaylistDataContextValue {
duration: number;
audioBuffers: AudioBuffer[];
peaksDataArray: TrackClipPeaks[];
trackStates: TrackState[];
tracks: ClipTrack[];
sampleRate: number;
waveHeight: number;
timeScaleHeight: number;
minimumPlaylistHeight: number;
controls: { show: boolean; width: number };
samplesPerPixel: number;
timeFormat: TimeFormat;
masterVolume: number;
canZoomIn: boolean;
canZoomOut: boolean;
barWidth: number;
barGap: number;
progressBarWidth: number;
isReady: boolean;
mono: boolean;
playoutRef: RefObject<PlaylistEngine | null>; // from @waveform-playlist/engine
isDraggingRef: MutableRefObject<boolean>; // true during boundary trim drags
onTracksChange: ((tracks: ClipTrack[]) => void) | undefined;
}

Example: Custom Wrapper

Create a custom wrapper with your application's defaults:

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

interface MyPlaylistProps extends Partial<WaveformPlaylistProviderProps> {
tracks: ClipTrack[];
children: React.ReactNode;
}

function MyPlaylistProvider({ tracks, children, ...props }: MyPlaylistProps) {
return (
<WaveformPlaylistProvider
tracks={tracks}
samplesPerPixel={1024}
waveHeight={100}
timescale
controls={{ show: true, width: 180 }}
theme={myTheme}
{...props}
>
{children}
</WaveformPlaylistProvider>
);
}

TypeScript

Full type definition:

interface WaveformPlaylistProviderProps {
// Required
tracks: ClipTrack[];
children: React.ReactNode;

// Display
samplesPerPixel?: number;
waveHeight?: number;
timescale?: boolean;
mono?: boolean;
zoomLevels?: number[];
automaticScroll?: boolean;
controls?: { show: boolean; width: number };

// Theming
theme?: Partial<WaveformPlaylistTheme>;

// Audio
effects?: EffectsFunction;

// Annotations
annotationList?: {
annotations?: any[];
editable?: boolean;
isContinuousPlay?: boolean;
linkEndpoints?: boolean;
controls?: any[];
};

// Callbacks
onReady?: () => void;
onAnnotationsChange?: (annotations: AnnotationData[]) => void;
/** Called when engine clip operations (move/trim/split) update tracks */
onTracksChange?: (tracks: ClipTrack[]) => void;

// Waveform rendering
barWidth?: number;
barGap?: number;
progressBarWidth?: number;

// Advanced
/** SoundFont cache for sample-based MIDI playback */
soundFontCache?: SoundFontCache;
/** Defer engine build during progressive loading */
deferEngineRebuild?: boolean;
/** Disable automatic stop when cursor reaches end of longest track */
indefinitePlayback?: boolean;
}

See Also