Skip to main content

Stem Tracks

The stem tracks example demonstrates multi-track playback with full per-track controls. It loads 12 individual stems from Albert Kader’s “Whiptails” minimal techno track and provides independent control over each. View Live Demo →

What It Demonstrates

  • Progressive track loading - Tracks appear as they load, no waiting for all files
  • Per-track controls - Independent mute, solo, volume, and pan for each track
  • Keyboard shortcuts - Space (play/pause), Escape (stop), 0 (rewind)
  • Master volume - Global volume control affecting all tracks
  • Zoom controls - Adjust waveform detail level
  • Automatic scrolling - Playhead stays visible during playback

Complete Example

import React from 'react';
import {
  WaveformPlaylistProvider,
  Waveform,
  PlayButton,
  PauseButton,
  StopButton,
  AudioPosition,
  ZoomInButton,
  ZoomOutButton,
  AutomaticScrollCheckbox,
  MasterVolumeControl,
  useAudioTracks,
  usePlaybackShortcuts,
} from '@waveform-playlist/browser';

// Stem tracks configuration - Albert Kader "Whiptails" minimal techno
const audioConfigs = [
  {
    src: '/media/audio/AlbertKader_Whiptails/01_Loop1.opus',
    name: 'Loop 1',
  },
  {
    src: '/media/audio/AlbertKader_Whiptails/02_Loop2.opus',
    name: 'Loop 2',
  },
  {
    src: '/media/audio/AlbertKader_Whiptails/03_Kick.opus',
    name: 'Kick',
  },
  {
    src: '/media/audio/AlbertKader_Whiptails/04_Snare.opus',
    name: 'Snare',
  },
  // ... more stems
];

// Component to enable keyboard shortcuts (must be inside provider)
function PlaybackShortcuts() {
  usePlaybackShortcuts();
  return null;
}

export function StemTracksExample() {
  // Load audio tracks PROGRESSIVELY - tracks appear as they load!
  const { tracks, loading, error, loadedCount, totalCount } = useAudioTracks(
    audioConfigs,
    { progressive: true }
  );

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

  return (
    <WaveformPlaylistProvider
      tracks={tracks}
      samplesPerPixel={512}
      mono
      waveHeight={100}
      automaticScroll={true}
      controls={{ show: true, width: 200 }}
      timescale
      barWidth={4}
      barGap={0}
    >
      <PlaybackShortcuts />
      
      <div className="controls">
        <PlayButton />
        <PauseButton />
        <StopButton />
        {loading && <span>Loading: {loadedCount}/{totalCount}</span>}
        
        <ZoomInButton />
        <ZoomOutButton />
        
        <AudioPosition />
        <MasterVolumeControl />
        <AutomaticScrollCheckbox />
      </div>

      <Waveform />
    </WaveformPlaylistProvider>
  );
}

Key Features

Progressive Loading

Tracks load independently and appear one-by-one. No waiting for all 12 stems to download:
const { tracks, loading, loadedCount, totalCount } = useAudioTracks(
  audioConfigs,
  { progressive: true } // Each track appears as it loads!
);
The progressive: true option enables tracks to render as soon as they finish loading, rather than waiting for all tracks. This provides a much better user experience for multi-track projects.

Track Controls

Each track has independent controls rendered in the sidebar:
  • Mute/Solo - Isolate or silence individual tracks
  • Volume slider - 0 to 1 range (linear gain)
  • Pan slider - -1 (left) to 1 (right)
  • Track name - Editable label
<WaveformPlaylistProvider
  controls={{ show: true, width: 200 }}
  // ... other props
>

Keyboard Shortcuts

Enable playback keyboard shortcuts using the usePlaybackShortcuts hook:
function PlaybackShortcuts() {
  usePlaybackShortcuts();
  return null;
}

// Use inside provider:
<WaveformPlaylistProvider ...>
  <PlaybackShortcuts />
  {/* ... rest of UI */}
</WaveformPlaylistProvider>
Default shortcuts:
  • Space - Toggle play/pause
  • Escape - Stop playback
  • 0 - Rewind to start
The usePlaybackShortcuts hook must be rendered inside the WaveformPlaylistProvider to access playback controls via context.

Waveform Rendering

Configure waveform appearance:
<WaveformPlaylistProvider
  samplesPerPixel={512}    // Zoom level (lower = more detail)
  mono                      // Render as single channel
  waveHeight={100}          // Track height in pixels
  timescale                 // Show time markers
  barWidth={4}              // Waveform bar width
  barGap={0}                // Gap between bars
>

Source Code

View the complete source code:
  • Example component: website/src/components/examples/StemTracksExample.tsx
  • E2E tests: e2e/stem-tracks.spec.ts

Build docs developers (and LLMs) love