Audio Effects
The audio effects example demonstrates the full effects processing capabilities with 20 different Tone.js effects, real-time parameter controls, and a live frequency visualizer.
View Live Demo →
What It Demonstrates
- Master effects chain - Global effects applied to the mix
- Per-track effects - Independent effect chains per track
- 20 Tone.js effects - Reverb, delay, filters, distortion, and more
- Real-time parameter control - Adjust effect parameters during playback
- Frequency visualizer - Live FFT analysis with industrial Berlin aesthetic
- Drag-and-drop tracks - Add your own audio files
- Export with effects - Render effects offline for WAV export
Complete Example
import React, { useState } from 'react';
import {
WaveformPlaylistProvider,
Waveform,
PlayButton,
PauseButton,
useDynamicEffects,
useTrackDynamicEffects,
useAudioTracks,
} from '@waveform-playlist/browser';
const audioConfigs = [
{ src: '/audio/kick.opus', name: 'Kick' },
{ src: '/audio/bass.opus', name: 'Bass' },
{ src: '/audio/synth.opus', name: 'Synth' },
];
export function EffectsExample() {
const [tracks, setTracks] = useState([]);
// Master effects manager
const effectsManager = useDynamicEffects(256); // FFT size for analyzer
const { analyserRef, masterEffects, addEffect, createOfflineEffectsFunction } =
effectsManager;
// Track effects manager
const trackEffectsManager = useTrackDynamicEffects();
const { addEffectToTrack, getTrackEffectsFunction } = trackEffectsManager;
// Load audio tracks
const { tracks: loadedTracks, loading } = useAudioTracks(audioConfigs, {
progressive: true,
});
// Add default effects on mount
React.useEffect(() => {
if (!loading && loadedTracks.length > 0) {
// Master reverb
addEffect('reverb');
// Per-track effects
addEffectToTrack(loadedTracks[0].id, 'compressor'); // Kick
addEffectToTrack(loadedTracks[1].id, 'distortion'); // Bass
addEffectToTrack(loadedTracks[2].id, 'pingPongDelay'); // Synth
}
}, [loading, loadedTracks]);
// Attach effects functions to tracks
const tracksWithEffects = React.useMemo(() => {
return loadedTracks.map(track => ({
...track,
effects: getTrackEffectsFunction(track.id),
}));
}, [loadedTracks, getTrackEffectsFunction]);
return (
<>
{/* Effect rack UI (add/remove effects, adjust parameters) */}
<EffectRack effectsManager={effectsManager} />
{/* Frequency visualizer */}
<FrequencyVisualizer analyserRef={analyserRef} />
<WaveformPlaylistProvider
tracks={tracksWithEffects}
effects={masterEffects} // Master effects chain
samplesPerPixel={512}
waveHeight={100}
>
<PlayButton />
<PauseButton />
<Waveform />
</WaveformPlaylistProvider>
</>
);
}
Key Features
Master Effects Chain
Apply effects to the entire mix using useDynamicEffects:
import { useDynamicEffects } from '@waveform-playlist/browser';
const { masterEffects, addEffect, removeEffect, updateEffectParam } =
useDynamicEffects(256); // FFT size for analyzer
// Add effects
addEffect('reverb');
addEffect('delay');
// Update parameters
updateEffectParam('reverb-0', 'decay', 5.0);
updateEffectParam('reverb-0', 'wet', 0.3);
// Pass to provider
<WaveformPlaylistProvider effects={masterEffects} ...>
The master effects chain processes audio after all tracks are mixed together. Effects run in the order they were added.
Per-Track Effects
Apply independent effects to each track:
import { useTrackDynamicEffects } from '@waveform-playlist/browser';
const { addEffectToTrack, getTrackEffectsFunction, updateTrackEffectParam } =
useTrackDynamicEffects();
// Add effects to specific tracks
addEffectToTrack('track-1', 'compressor');
addEffectToTrack('track-2', 'distortion');
addEffectToTrack('track-2', 'reverb'); // Multiple effects per track
// Update track effect parameters
updateTrackEffectParam('track-2', 'distortion-0', 'distortion', 0.8);
// Attach to tracks
const tracksWithEffects = tracks.map(track => ({
...track,
effects: getTrackEffectsFunction(track.id),
}));
<WaveformPlaylistProvider tracks={tracksWithEffects} ...>
Available Effects
20 Tone.js effects are available:
Reverb & Delay:
reverb - Algorithmic reverb
convolverReverb - Convolution reverb
delay - Simple delay
pingPongDelay - Stereo ping-pong delay
feedbackDelay - Feedback delay
Filters:
lowpass - Low-pass filter
highpass - High-pass filter
bandpass - Band-pass filter
notch - Notch filter
allpass - All-pass filter
eq3 - 3-band EQ
Distortion & Dynamics:
distortion - Waveshaping distortion
overdrive - Soft clipping overdrive
bitcrusher - Lo-fi bit reduction
compressor - Dynamic range compression
limiter - Peak limiting
Modulation:
chorus - Chorus effect
phaser - Phaser
tremolo - Amplitude modulation
vibrato - Pitch modulation
Frequency Visualizer
Display real-time frequency analysis:
import { useDynamicEffects } from '@waveform-playlist/browser';
const { analyserRef } = useDynamicEffects(256);
function FrequencyVisualizer({ analyserRef }) {
const canvasRef = useRef<HTMLCanvasElement>(null);
useEffect(() => {
if (!canvasRef.current || !analyserRef.current) return;
const canvas = canvasRef.current;
const canvasCtx = canvas.getContext('2d');
let animationId: number;
const draw = () => {
animationId = requestAnimationFrame(draw);
// Get FFT data from Tone.js Analyser
const dataArray = analyserRef.current.getValue();
const bufferLength = dataArray.length;
// Clear canvas
canvasCtx.fillStyle = '#0d0d0d';
canvasCtx.fillRect(0, 0, canvas.width, canvas.height);
// Draw frequency bars
const barWidth = (canvas.width / bufferLength) * 2.5;
let x = 0;
for (let i = 0; i < bufferLength; i++) {
// Normalize dB values (-100 to 0) to 0-1 range
const normalized = Math.max(0, (dataArray[i] + 100) / 100);
const barHeight = normalized * canvas.height;
canvasCtx.fillStyle = '#63C75F'; // Berlin green
canvasCtx.fillRect(x, canvas.height - barHeight, barWidth, barHeight);
x += barWidth + 1;
}
};
draw();
return () => cancelAnimationFrame(animationId);
}, [analyserRef]);
return <canvas ref={canvasRef} width={1000} height={120} />;
}
Export with Effects
Render effects offline when exporting WAV files:
import { ExportWavButton } from '@waveform-playlist/browser';
const { createOfflineEffectsFunction } = useDynamicEffects();
const offlineEffects = createOfflineEffectsFunction();
<ExportWavButton
filename="mix-with-effects"
mode="master"
applyEffects={true}
effectsFunction={offlineEffects}
/>
Offline rendering uses OfflineAudioContext to process effects without real-time constraints, ensuring perfect quality exports.
Effect Parameters
Each effect has adjustable parameters. Example for reverb:
updateEffectParam('reverb-0', 'decay', 5.0); // Decay time in seconds
updateEffectParam('reverb-0', 'preDelay', 0.01); // Pre-delay in seconds
updateEffectParam('reverb-0', 'wet', 0.3); // Dry/wet mix (0-1)
See the EffectRack component in the source for all available parameters per effect type.
Drag-and-Drop Tracks
Add your own audio files by dragging them into the drop zone:
const handleDrop = async (e: React.DragEvent) => {
const files = Array.from(e.dataTransfer.files).filter(f =>
f.type.startsWith('audio/')
);
const audioContext = new AudioContext();
for (const file of files) {
const arrayBuffer = await file.arrayBuffer();
const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);
const track = createTrack({
name: file.name,
clips: [createClipFromSeconds({ audioBuffer, startTime: 0 })],
});
setTracks(prev => [...prev, track]);
}
};
Source Code
View the complete source code:
- Example component:
website/src/components/examples/EffectsExample.tsx
- Effect rack UI:
website/src/components/effects/EffectRack.tsx
- E2E tests:
e2e/effects.spec.ts