# Waveform Playlist A multi-track audio editor and player built with React, Tone.js, and the Web Audio API. Features canvas-based waveform visualization, drag-and-drop clip editing, and professional audio effects.

Waveform Playlist Screenshot

Join the dawcore Discord

## Features - **Multi-track editing** - Multiple clips per track with drag-to-move and trim - **Waveform visualization** - Canvas-based rendering with zoom controls - **20+ audio effects** - Reverb, delay, filters, distortion, and more via Tone.js - **Recording** - AudioWorklet-based recording with live waveform preview - **Export** - WAV export with effects, individual tracks or full mix - **Annotations** - Time-synced text annotations with keyboard navigation - **Theming** - Full theme customization with dark/light mode support - **MIDI playback** - MIDI file parsing with piano roll visualization and SoundFont sample playback - **TypeScript** - Full type definitions included ## Quick Start ```bash npm install @waveform-playlist/browser tone @dnd-kit/react ``` > **Note**: `tone` and `@dnd-kit/react` are peer dependencies and must be installed separately. `@dnd-kit/dom` and `@dnd-kit/abstract` are also declared peers (they install automatically as dependencies of `@dnd-kit/react`). > **v14:** the playout engines are optional peers. Install the one(s) you use: > - WebAudio/Tone path: `npm install @waveform-playlist/browser @waveform-playlist/playout tone` > - MediaElement path: `npm install @waveform-playlist/browser @waveform-playlist/media-element-playout` > - Custom adapter: `npm install @waveform-playlist/browser` and pass `createAdapter` (no `tone`). > > The `Tone` convenience re-export was removed in v14 — `import * as Tone from 'tone'` directly. > > **v14:** effects, WAV export, output metering, and the `useAudioTracks`/`useDynamicTracks` loaders import from `@waveform-playlist/browser/tone`. > > **v16:** `@dnd-kit/*` `^0.5.0` is required (`^0.3` is no longer supported). `noDropAnimationPlugins` was removed — `Clip`, `AnnotationBox`, and `SortableTrackControls` configure their own `@dnd-kit` Feedback behavior, so a custom `DragDropProvider` needs no `plugins` prop. `@dnd-kit/dom` is now a declared peer of `ui-components` and `annotations`. ```tsx import { WaveformPlaylistProvider, Waveform, PlayButton, PauseButton, StopButton } from '@waveform-playlist/browser'; import { createTrack, createClipFromSeconds } from '@waveform-playlist/core'; function App() { const [tracks, setTracks] = useState([]); // Load audio and create tracks useEffect(() => { async function loadAudio() { const response = await fetch('/audio/song.mp3'); const arrayBuffer = await response.arrayBuffer(); const audioContext = new AudioContext(); const audioBuffer = await audioContext.decodeAudioData(arrayBuffer); const track = createTrack({ name: 'My Track', clips: [createClipFromSeconds({ audioBuffer, startTime: 0 })], }); setTracks([track]); } loadAudio(); }, []); return (
); } ``` ## Documentation - [**Live Examples**](https://naomiaro.github.io/waveform-playlist/examples/stem-tracks) - Interactive demos - [**Getting Started**](https://naomiaro.github.io/waveform-playlist/docs/getting-started/installation) - Installation and basic usage - [**Guides**](https://naomiaro.github.io/waveform-playlist/docs/guides/loading-audio) - In-depth tutorials - [**API Reference**](https://naomiaro.github.io/waveform-playlist/docs/api/provider) - Component and hook documentation ## Community - [**Discord**](https://discord.gg/dsZZBpmD7k) - Chat, get help, and share what you're building on the dawcore server - [**Issues**](https://github.com/naomiaro/waveform-playlist/issues) - Bug reports and feature requests - [**Code of Conduct**](CODE_OF_CONDUCT.md) - The standards we hold this community to, on GitHub and Discord ## Examples | Example | Description | |---------|-------------| | [Stem Tracks](https://naomiaro.github.io/waveform-playlist/examples/stem-tracks) | Multi-track playback with mute/solo/volume controls | | [Effects](https://naomiaro.github.io/waveform-playlist/examples/effects) | 20 Tone.js effects with real-time parameter control | | [WAM! Kick It Up a Notch](https://naomiaro.github.io/waveform-playlist/examples/wam-effects) | Host WAM 2.0 plugins with community library, native GUIs, and mixed Tone+WAM chains | | [Recording](https://naomiaro.github.io/waveform-playlist/examples/recording) | Live recording with VU meter and waveform preview | | [Multi-Clip](https://naomiaro.github.io/waveform-playlist/examples/multi-clip) | Drag-and-drop clip editing with trim handles | | [Annotations](https://naomiaro.github.io/waveform-playlist/examples/annotations) | Time-synced text with keyboard navigation | | [Waveform Data](https://naomiaro.github.io/waveform-playlist/examples/waveform-data) | Pre-computed peaks for fast loading | | [MIDI](https://naomiaro.github.io/waveform-playlist/examples/midi) | MIDI file playback with piano roll and SoundFont samples | | [Beats & Bars](https://naomiaro.github.io/waveform-playlist/examples/beats-and-bars) | Tempo-synced timescale with beats and bars ruler | | [Fades](https://naomiaro.github.io/waveform-playlist/examples/fades) | Fade in/out with configurable curves | | [Stereo](https://naomiaro.github.io/waveform-playlist/examples/stereo) | Stereo waveform rendering and pan controls | | [Spectrogram](https://naomiaro.github.io/waveform-playlist/examples/mir-spectrogram) | FFT-based spectrogram visualization | | [Media Element](https://naomiaro.github.io/waveform-playlist/examples/media-element) | HTMLMediaElement playout with playback rate | | [Styling](https://naomiaro.github.io/waveform-playlist/examples/styling) | Bar width, gaps, rounded bars, gradients, and theme colors | | [Flexible API](https://naomiaro.github.io/waveform-playlist/examples/flexible-api) | Custom playheads, timestamps, and full UI customization | ## Packages | Package | Description | |---------|-------------| | `@waveform-playlist/browser` | Main React components, hooks, and context | | `@waveform-playlist/core` | Types, utilities, and clip/track creation | | `@waveform-playlist/engine` | Framework-agnostic timeline engine with pure operations | | `@waveform-playlist/ui-components` | Styled UI components (buttons, sliders, etc.) | | `@waveform-playlist/playout` | Tone.js audio engine | | `@waveform-playlist/webaudio-peaks` | Peak extraction from AudioBuffer or sample arrays | **Optional packages:** | Package | Description | |---------|-------------| | `@waveform-playlist/midi` | React `useMidiTracks` hook + piano roll visualization with SoundFont/PolySynth playback. Re-exports the parser from `@dawcore/midi`. | | `@waveform-playlist/annotations` | Time-synced text annotations with drag editing | | `@waveform-playlist/recording` | AudioWorklet recording with live waveform preview (requires [setup](https://naomiaro.github.io/waveform-playlist/docs/guides/recording#audioworklet-setup)) | | `@waveform-playlist/worklets` | Shared AudioWorklet processors for metering and recording (auto-installed with recording) | | `@waveform-playlist/spectrogram` | React `SpectrogramProvider` + UI (menu items, settings modal). Computation/worker/orchestrator are imported from `@dawcore/spectrogram`. | | `@waveform-playlist/media-element-playout` | HTMLMediaElement-based playout with pitch-preserving playback rate | ## Key Hooks ```tsx // Load audio files into tracks const { tracks, loading, error } = useAudioTracks([ { src: '/audio/vocals.mp3', name: 'Vocals' }, { src: '/audio/drums.mp3', name: 'Drums' }, ]); // Playback controls const { play, pause, stop, seekTo } = usePlaylistControls(); // Playback animation (60fps updates) const { currentTime, isPlaying } = usePlaybackAnimation(); // Zoom controls const { zoomIn, zoomOut, samplesPerPixel } = useZoomControls(); // Master effects chain const { masterEffects, toggleBypass, updateParameter } = useDynamicEffects(); // WAV export const { exportWav, isExporting, progress } = useExportWav(); // Recording const { startRecording, stopRecording, isRecording } = useIntegratedRecording( tracks, setTracks, selectedTrackId ); ``` ## Web Components (Experimental) `@dawcore/components` provides framework-agnostic Web Components for multi-track audio editing — no React required. Built with Lit, adapter-pluggable: choose between `@dawcore/transport` (native Web Audio) or `@waveform-playlist/playout` (Tone.js). ```bash npm install @dawcore/components @waveform-playlist/core @waveform-playlist/engine ``` Audio backend (choose one): ```bash npm install @dawcore/transport # Native Web Audio (multi-tempo, multi-meter, metronome) # or npm install @waveform-playlist/playout tone # Tone.js (effects, MIDI synths) ``` ```html ``` **Features:** - Declarative `` and `` elements with auto-loading - Imperative API for programmatic mutation — `editor.addTrack/removeTrack/updateTrack/addClip/removeClip/updateClip` and `editor.ready()` for engine bootstrap before any track loads - Adapter-pluggable — choose Native Web Audio or Tone.js backend - Clip move, trim, and split with collision detection - Undo/redo with transaction-based grouping - Keyboard shortcuts (Space=play/pause, S=split, Cmd/Ctrl+Z=undo) - File drop for adding tracks - Frozen-panes layout — height-constrained editors scroll vertically with a pinned ruler and row-locked track controls; controls adapt to short rows - Recording with overdub, punch-in replace (takes land at the playhead and replace overlapped content), automatic armed-track mute (the recorded-over material is silenced during the take), named takes with live clip headers, and latency compensation - Metronome with mixed meters and tempo changes - Tempo automation — linear ramps and Möbius-Ease curves with exact integration - Pre-computed peaks for fast initial render - Player-style end-of-timeline auto-stop by default; `indefinite-playback` rolls DAW-style until an explicit stop, `fill-viewport` fills the empty viewport so the ruler renders before any track (recording sessions suppress the auto-stop automatically) **Packages:** | Package | Description | |---------|-------------| | `@dawcore/components` | Lit Web Components for multi-track editing | | `@dawcore/transport` | Native Web Audio transport — scheduling, looping, tempo automation, time signatures, metronome | | `@dawcore/spectrogram` | Framework-agnostic spectrogram orchestrator, FFT worker pool, and color maps — used by `render-mode="spectrogram"` | | `@dawcore/midi` | Framework-agnostic MIDI parser (`parseMidiFile`, `parseMidiUrl`) — used by `editor.loadMidi()` and re-exported by `@waveform-playlist/midi` | | `@dawcore/wam` | Web Audio Modules (WAM 2.0) plugin hosting and generic parameter GUIs — used by `editor.addWamPlugin()` and the effects chain (optional peer, loaded on demand) | | `@dawcore/faust` | In-browser Faust DSP → WAM 2.0 compilation — used by `editor.addFaustEffect()` (optional peer, loaded on demand) | Run the examples locally: ```bash pnpm example:dawcore-native # Native Web Audio — localhost:5173 pnpm example:dawcore-tone # Tone.js backend — localhost:5174 pnpm example:dawcore-wam # WAM 2.0 plugins + Faust — localhost:5175 pnpm example:media-element # MediaElement-only React starter (no Tone) — localhost:5176 pnpm example:react-starter # Full multitrack React starter (Tone engine) — localhost:5177 ``` The **[react-starter](examples/react-starter/)** is the smallest correct setup for the full multitrack editor — `WaveformPlaylistProvider` on the default Tone.js engine with mixer controls, plus a README section explaining the "AudioContext was prevented from starting" browser warning and how the provider's gesture-safe startup avoids it (see its [README](examples/react-starter/README.md)). The **[media-element-player](examples/media-element-player/)** starter is a minimal React single-track player using only `@waveform-playlist/browser` + `@waveform-playlist/media-element-playout` (no `@waveform-playlist/playout`, no `tone`) — see its [README](examples/media-element-player/README.md). **dawcore-native** example pages: - [`basic.html`](examples/dawcore-native/basic.html) — Basic playback with timescale and file drop - [`multiclip.html`](examples/dawcore-native/multiclip.html) — Multi-clip editing with move, trim, and split, plus drag-to-reorder tracks with a live slot-snap drop preview - [`programmatic.html`](examples/dawcore-native/programmatic.html) — Imperative `editor.addTrack` / `addClip` / `updateClip` / `removeClip` plus declarative DOM mutation, side-by-side - [`beats-grid.html`](examples/dawcore-native/beats-grid.html) — Beats & bars grid mode with snap-to-grid, plus bar-aligned tick-based annotation regions (`start-tick`/`end-tick`) - [`beat-map-grid.html`](examples/dawcore-native/beat-map-grid.html) — Variable tempo from beat maps with metronome - [`record.html`](examples/dawcore-native/record.html) — Recording with overdub - [`metronome.html`](examples/dawcore-native/metronome.html) — Metronome with mixed meters, tempo presets, and looping sequences - [`automation.html`](examples/dawcore-native/automation.html) — Tempo automation with step, linear, and curve presets - [`analyser.html`](examples/dawcore-native/analyser.html) — Spectrum analyser connected to master output - [`spectrogram.html`](examples/dawcore-native/spectrogram.html) — Per-track FFT spectrograms via `render-mode="spectrogram"` with color-map and frequency-scale controls - [`effects.html`](examples/dawcore-native/effects.html) — Per-track and master insert effects (`native-*` registry) with live parameter sliders, bypass, and `daw-effect-*` event log - [`player.html`](examples/dawcore-native/player.html) — Lightweight `` single-track HTMLMediaElement player: waveform with `peaks-src` + timescale ruler, and a scrubber-only fallback (no `peaks-src`), with `daw-ready`/`daw-timeupdate`/`daw-play`/`daw-stop` event log - [`annotations.html`](examples/dawcore-native/annotations.html) — `` lyric/section markers with dual-view sync between the timeline lane and ``, drag-to-resize boundaries, and keyboard navigation/playback - [`sonnet.html`](examples/dawcore-native/sonnet.html) — Sonnet 1 parity demo: `box-label="id"` bars with the full text in `` **dawcore-tone** example pages: - [`basic.html`](examples/dawcore-tone/basic.html) — Basic playback with Tone.js adapter - [`multiclip.html`](examples/dawcore-tone/multiclip.html) — Multi-clip editing with Tone.js, plus drag-to-reorder tracks with a live slot-snap drop preview - [`programmatic.html`](examples/dawcore-tone/programmatic.html) — Imperative `editor.addTrack` / `addClip` / `updateClip` / `removeClip` plus declarative DOM mutation, with the Tone.js adapter - [`beats-grid.html`](examples/dawcore-tone/beats-grid.html) — Beats & bars grid with Tone.js - [`record.html`](examples/dawcore-tone/record.html) — Mic recording with overdub - [`analyser.html`](examples/dawcore-tone/analyser.html) — Spectrum analyser connected to master output - [`spectrogram.html`](examples/dawcore-tone/spectrogram.html) — Per-track FFT spectrograms with the Tone.js adapter - [`midi.html`](examples/dawcore-tone/midi.html) — Programmatic MIDI clips with piano-roll render mode and Tone.js PolySynth - [`midi-load.html`](examples/dawcore-tone/midi-load.html) — Load `.mid` files (URL or file picker) via `editor.loadMidi()` and play them through Tone.js PolySynth - [`soundfont.html`](examples/dawcore-tone/soundfont.html) — MIDI through SoundFont samples: `SoundFontCache.fromUrl()` + `createToneAdapter({ soundFontCache })`, with PolySynth fallback when the `.sf2` fails to load - [`annotations.html`](examples/dawcore-tone/annotations.html) — `` lyric/section markers with dual-view sync between the timeline lane and ``, drag-to-resize boundaries, and keyboard navigation/playback with the Tone.js adapter **dawcore-wam** example pages: - [`index.html`](examples/dawcore-wam/index.html) — WAM 2.0 plugin hosting end to end: insert plugins on **any track or the master bus** (one rack per track + master), with the target chosen from a dropdown that tracks every track — including stems **dragged-and-dropped** onto the editor (`file-drop`). Load community plugins by URL or from the [webaudiomodules.com](https://www.webaudiomodules.com/) library, open plugin GUIs, reorder/bypass chains, save and restore chains (including state) via localStorage, export the mix to WAV through `exportAudio()`, and compile Faust DSP to a live effect in the browser (`@dawcore/faust`) — plus pre-compiled Faust effects served from this repo **Spec & roadmap:** [`docs/specs/web-components-migration.md`](docs/specs/web-components-migration.md) — full element catalogue, attribute/property/event tables, programmatic API contracts, theming tokens, and migration phases. ## Browser Support Requires Web Audio API support: Chrome, Firefox, Safari, Edge (modern versions). See [Can I Use: Web Audio API](https://caniuse.com/audio-api) ## Development ```bash # Install dependencies pnpm install # Start dev server pnpm website # Run tests pnpm test # Build all packages pnpm build ``` Visit http://localhost:3000/waveform-playlist to see the examples. ## Books Currently writing: [Mastering Tone.js](https://leanpub.com/masteringtonejs)

Mastering Tone.js

## Credits Originally created for the [Airtime](https://www.sourcefabric.org/software/airtime/) project at [Sourcefabric](https://www.sourcefabric.org/). ## License [MIT License](http://doge.mit-license.org)