BMates is an open-source, embeddable multitrack audio editor for the web. Build a custom audio workflow with project state, Canvas waveform editing, Web Audio playback, and optional React composition.
| Package | Responsibility | Use it when |
|---|---|---|
@bmates/core |
Framework-independent project state, validation, commands, and history | You need headless editing state or another UI framework. |
@bmates/renderer |
Canvas scene graph, rendering loop, hit testing, and pointer events | You are building a custom Canvas surface. |
@bmates/editor |
Timeline, waveform clips, interactions, playback, import, and export | You need the browser audio editor without a prescribed UI. |
@bmates/studio |
React provider, hooks, and declarative editor composition | You are integrating BMates in a React application. |
The dependency direction is renderer -> editor -> studio. Core is shared project state and can be used on its own.
Choose the highest-level package that fits your integration.
# React integration
pnpm add @bmates/studio
# Browser Canvas and Web Audio editor without React UI bindings
pnpm add @bmates/editor
# Headless project state
pnpm add @bmates/coreCore keeps project data and history independent from Canvas, React, or browser audio APIs.
import { createProjectStore } from '@bmates/core';
const project = createProjectStore(initialTracks);
project.setTrackMute('drums', true);
project.moveClip('kick', { trackId: 'drums', start: 2 });
project.undo();
const exportedProject = project.getProject();Editor mounts an interactive timeline on a canvas. Wait for ready before audio-dependent commands and destroy it when the
owner unmounts.
import { Editor } from '@bmates/editor';
const editor = new Editor(canvas, tracks);
await editor.ready;
await editor.play();
await editor.pause();
editor.destroy();Studio is the convenient React entry point. The default component lets you supply your own track controls while BMates owns the Canvas and audio lifecycle.
import { BMates } from '@bmates/studio';
<BMates
data={tracks}
trackEl={({ muted, toggleMute, track }) => (
<button onClick={toggleMute}>{muted ? `Unmute ${track.name}` : `Mute ${track.name}`}</button>
)}
/>;For complete UI control, compose one shared store around your own transport and track list.
<BMates.Root defaultData={tracks} onDataChange={saveProject}>
<div style={{ display: 'flex', height: 600 }}>
<BMates.TrackList>
{({ muted, toggleMute, track }) => (
<button onClick={toggleMute}>{muted ? `Unmute ${track.name}` : `Mute ${track.name}`}</button>
)}
</BMates.TrackList>
<BMates.Canvas style={{ flex: 1 }} />
</div>
</BMates.Root>A project is an ordered array of tracks. Each track contains clips with a source URL, timeline start position, and group index. Keep track and clip IDs unique. See the documentation for the full type reference, import/export, playback, and extension guides.
This repository is a pnpm workspace. Use Node.js 20.19+ or 22.12+.
pnpm install
pnpm build
pnpm --filter bmates-docs buildThe documentation source is in apps/www/posts and is built with Docgo.