Skip to main content

Components

Pre-built React components for building audio applications.

Import​

import {
Waveform,
PlayButton,
PauseButton,
StopButton,
// ... other components
} from '@waveform-playlist/browser';

Waveform​

The main visualization component that renders tracks and handles interactions.

<Waveform />

Features​

  • Canvas-based waveform rendering
  • Click to seek
  • Drag to select
  • Drag clips to move/trim
  • Keyboard shortcuts
  • Custom playhead rendering

Props​

PropTypeDefaultDescription
showClipHeadersbooleanfalseShow clip name headers above waveforms
gainScaledWaveformsbooleanfalseDraw each clip's waveform scaled by its gain (clamped to the channel height), so clips levelled with clip gain look the way they sound. Draw-time only — peaks are not regenerated
interactiveClipsbooleanfalseEnable dragging/trimming interactions on clips (requires @dnd-kit/react DragDropProvider setup)
renderPlayheadRenderPlayheadFunction-Custom playhead render function
renderTrackControls(trackIndex: number) => ReactNode-Custom track controls renderer
trackReorderingbooleanfalseShow a drag grip + move up/down buttons on the default track control panel for vertical track reordering. Drag requires ClipInteractionProvider; the buttons work regardless

Note: The timescale prop is set on WaveformPlaylistProvider, not on Waveform.

Usage​

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

The Waveform component reads configuration from the provider context.


Custom Playhead​

Replace the default playhead with a custom component using the renderPlayhead prop.

PlayheadProps​

The render function receives these props:

interface PlayheadProps {
/** Position in pixels from left edge */
position: number;
/** Playhead color from theme (default: #ff0000) */
color?: string;
}

Built-in Playhead Components​

Import pre-built playhead variants:

import { Playhead, PlayheadWithMarker } from '@waveform-playlist/ui-components';

Default Playhead​

A simple vertical line (used by default):

<Waveform />
// or explicitly:
<Waveform renderPlayhead={(props) => <Playhead {...props} />} />

PlayheadWithMarker​

A playhead with a triangle marker at the top, positioned to sit in the timescale area:

<Waveform
timescale
renderPlayhead={(props) => <PlayheadWithMarker {...props} />}
/>

Custom Playhead Example​

Create your own playhead component:

import styled from 'styled-components';

const CustomPlayheadLine = styled.div<{ $position: number; $color: string }>`
position: absolute;
top: 0;
left: 0;
width: 3px;
height: 100%;
background: ${(props) => props.$color};
transform: translate3d(${(props) => props.$position}px, 0, 0);
z-index: 150;
pointer-events: none;
will-change: transform;
border-radius: 2px;
box-shadow: 0 0 4px ${(props) => props.$color};
`;

const GlowingPlayhead = ({ position, color = '#ff0000' }) => (
<CustomPlayheadLine $position={position} $color={color} />
);

// Usage
<Waveform renderPlayhead={(props) => <GlowingPlayhead {...props} />} />

Performance Tips​

  • Use transform: translate3d() for GPU-accelerated positioning
  • Add will-change: transform for smooth animation
  • Set pointer-events: none to prevent interference with track interactions

Playback Buttons​

PlayButton​

Starts playback from the current cursor position.

<PlayButton />

Behavior:

  • Disabled during playback
  • Starts from selection start if selection exists

PauseButton​

Pauses playback, maintaining cursor position.

<PauseButton />

Behavior:

  • Disabled when not playing

StopButton​

Stops playback and resets cursor to start.

<StopButton />

Behavior:

  • Always enabled

RewindButton​

Jumps backward by a fixed amount.

<RewindButton />

Default: 5 seconds

FastForwardButton​

Jumps forward by a fixed amount.

<FastForwardButton />

Default: 5 seconds

SetLoopRegionButton​

Creates a loop region from the current selection, or clears an existing loop region.

<SetLoopRegionButton />

Behavior:

  • Disabled when no selection exists and no loop region is set
  • Shows "Set Loop" when no loop region exists
  • Shows "Clear Loop" when a loop region is set
  • Click to toggle between setting and clearing the loop region

LoopButton​

Toggles loop mode for the loop region.

<LoopButton />

Behavior:

  • Shows "Loop Off" when disabled, "Loop On" when enabled
  • If no loop region exists when enabling, creates a default loop region (first 10s or 25% of duration)
  • When enabled, playback loops when the cursor enters and reaches the end of the loop region
  • Loop region is displayed in the timescale area and can be dragged to adjust

Workflow:

  1. Create a selection by clicking and dragging on the waveform
  2. Click SetLoopRegionButton to create a loop region from the selection (or drag in the timescale)
  3. Click LoopButton to enable looping
  4. Play - when the cursor reaches the loop region, it will loop

Zoom Controls​

ZoomInButton​

Decreases samplesPerPixel for more detail.

<ZoomInButton />

Behavior:

  • Disabled at minimum zoom (128 spp)

ZoomOutButton​

Increases samplesPerPixel for wider view.

<ZoomOutButton />

Behavior:

  • Disabled at maximum zoom (8192 spp)

Volume Controls​

MasterVolumeControl​

Slider for overall output volume.

<MasterVolumeControl />

Range: 0 (silent) to 1 (full)

TrackVolumeControl​

Volume slider for a specific track.

<TrackVolumeControl trackIndex={0} />

Props:

  • trackIndex (required): Index of the track

Position Display​

AudioPosition​

Shows current time and duration.

<AudioPosition />
// Output: "0:00.000 / 3:45.123"

The format respects the current time format setting.


Time Format​

TimeFormatSelect​

Dropdown for selecting time display format.

<TimeFormatSelect />

Options:

  • seconds - 0.000
  • thousandths - 0:00.000
  • hh:mm:ss - 0:00:00
  • hh:mm:ss.u - 0:00:00.0
  • hh:mm:ss.uu - 0:00:00.00
  • hh:mm:ss.uuu - 0:00:00.000

Checkboxes​

ContinuousPlayCheckbox​

Toggle for continuous play, which governs annotation playback:

  • Off: playing an annotation stops at that annotation's end.
  • On: playback continues through the following annotations.

Switching it on while an annotation is playing lets playback continue past that annotation. Playing the selection (the Play button or Space with a selection highlighted) is not affected by the toggle: the selection plays from its start to its end either way. An annotation you click follows the toggle even while a selection is highlighted elsewhere, and the selection stays highlighted. It is not the loop control; see LoopButton.

<ContinuousPlayCheckbox />

AutomaticScrollCheckbox​

Toggle for auto-scrolling during playback.

<AutomaticScrollCheckbox />

Track Controls​

TrackControls​

Built-in control panel for a track.

<TrackControls trackIndex={0} />

Includes:

  • Track name
  • Mute button
  • Solo button
  • Volume slider
  • Pan slider

SortableTrackControls​

Registers a custom track-controls row as a vertical drag source for reordering. Use this when rendering your own track control panel (via renderTrackControls) instead of the default one — the default panel already wires this up internally when trackReordering is set on Waveform.

<SortableTrackControls trackId={track.id} index={trackIndex}>
{({ ref, handleRef, isDragSource }) => (
<div ref={ref} style={{ opacity: isDragSource ? 0.5 : 1 }}>
<button ref={handleRef}>Drag</button>
{/* rest of your custom track controls */}
</div>
)}
</SortableTrackControls>
PropTypeDefaultDescription
trackIdstring-The track's id
indexnumber-The track's current position
disabledbooleanfalseDisable the drag source
children(props: SortableTrackControlsRenderProps) => ReactNode-Render prop receiving ref (sortable item wrapper), handleRef (drag grip), and isDragSource

Requires ClipInteractionProvider (or another ambient @dnd-kit/react DragDropProvider) — drags are committed by calling usePlaylistControls().reorderTrack(trackId, toIndex) from the shared onDragEnd handler.


Export Controls​

ExportWavButton​

Export the playlist to a WAV file.

<ExportWavButton />

Props:

PropTypeDefaultDescription
labelstring'Export WAV'Button label
filenamestring'export'Downloaded file name (without extension)
mode'master' | 'individual''master'Export all tracks mixed or single track
trackIndexnumber-Track index for individual export
bitDepth16 | 3216WAV bit depth (16-bit PCM or 32-bit float)
applyEffectsbooleantrueApply fades and other clip effects
effectsFunctionEffectsFunction-Tone.js effects chain for export with effects
classNamestring-CSS class name
onExportComplete(blob: Blob) => void-Callback when export succeeds
onExportError(error: Error) => void-Callback when export fails

Behavior:

  • Shows progress percentage during export
  • Disabled when no tracks loaded
  • Automatically triggers download when complete

Example:

<ExportWavButton
filename="my-mix"
mode="master"
bitDepth={16}
onExportComplete={(blob) => console.log('Exported:', blob.size, 'bytes')}
/>

Individual Track Export:

<ExportWavButton
label="Export Track 1"
filename="track-1"
mode="individual"
trackIndex={0}
/>

Export Without Effects (Raw Audio):

<ExportWavButton
label="Export Raw"
filename="raw-export"
applyEffects={false}
/>

Export With Tone.js Effects:

import { Reverb } from 'tone';

// Define effects chain
const createEffectsChain = (masterVolume, destination, isOffline) => {
const reverb = new Reverb({ decay: 2.5, wet: 0.3 });
masterVolume.connect(reverb);
reverb.connect(destination);

// Return cleanup function
return () => {
reverb.dispose();
};
};

// Use in export button
<ExportWavButton
label="Export with Reverb"
filename="mix-with-effects"
effectsFunction={createEffectsChain}
/>

Complete Example​

import {
WaveformPlaylistProvider,
Waveform,
PlayButton,
PauseButton,
StopButton,
RewindButton,
FastForwardButton,
SetLoopRegionButton,
LoopButton,
ZoomInButton,
ZoomOutButton,
MasterVolumeControl,
AudioPosition,
TimeFormatSelect,
ContinuousPlayCheckbox,
AutomaticScrollCheckbox,
ExportWavButton,
useAudioTracks,
} from '@waveform-playlist/browser';

function FullFeaturedPlaylist() {
const { tracks, loading, error } = useAudioTracks([
{ src: '/audio/track1.mp3', name: 'Track 1' },
{ src: '/audio/track2.mp3', name: 'Track 2' },
]);

if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error}</div>;

return (
<WaveformPlaylistProvider
tracks={tracks}
samplesPerPixel={1024}
waveHeight={100}
timescale
controls={{ show: true, width: 180 }}
>
{/* Transport Controls */}
<div style={{ display: 'flex', gap: '0.5rem', marginBottom: '1rem' }}>
<RewindButton />
<PlayButton />
<PauseButton />
<StopButton />
<FastForwardButton />
<SetLoopRegionButton />
<LoopButton />
</div>

{/* Zoom Controls */}
<div style={{ display: 'flex', gap: '0.5rem', marginBottom: '1rem' }}>
<ZoomInButton />
<ZoomOutButton />
</div>

{/* Volume and Position */}
<div style={{ display: 'flex', gap: '1rem', alignItems: 'center', marginBottom: '1rem' }}>
<MasterVolumeControl />
<AudioPosition />
<TimeFormatSelect />
</div>

{/* Options */}
<div style={{ display: 'flex', gap: '1rem', marginBottom: '1rem' }}>
<ContinuousPlayCheckbox />
<AutomaticScrollCheckbox />
</div>

{/* Export */}
<div style={{ marginBottom: '1rem' }}>
<ExportWavButton filename="my-mix" />
</div>

{/* Waveform */}
<Waveform />
</WaveformPlaylistProvider>
);
}

Styling Components​

All components accept standard React props including className and style:

<PlayButton className="my-play-button" />
<PlayButton style={{ backgroundColor: 'green' }} />

For consistent styling, use the theme system or wrap components:

const StyledPlayButton = styled(PlayButton)`
background: #0066cc;
color: white;
border: none;
padding: 0.5rem 1rem;
border-radius: 4px;
cursor: pointer;

&:hover {
background: #0055aa;
}

&:disabled {
background: #cccccc;
cursor: not-allowed;
}
`;

See Also​