# Buzzsaw > Sound effects described as data and synthesized in the browser with the Web Audio API. Nothing to download at runtime, and the synthesis engine ships no dependencies. Two published packages plus a Studio for designing sounds by hand or by description. A sound is a plain JSON object, not an audio file. The same object plays in real time, renders offline to WAV, serializes to JSON, and round-trips through storage unchanged. - `@rjvr/buzzsaw`: synthesis engine, sound registry, parameter envelopes. Zero dependencies. - `@rjvr/buzzsaw/sounds`: 113 built-in presets, each a named export so you bundle only what you use. - `@rjvr/buzzsaw-wav`: RIFF/WAVE encoder, offline renderer, file exporter. `@rjvr/buzzsaw` is a peer dependency. Both packages are ESM-only and side-effect free. Their published types declare only the structural subset of Web Audio they use, so they typecheck under a Node-only `tsconfig` with no `lib.dom`. ## Documentation Fetch these for the authoritative API reference. GitHub serves them as plain text from `main`, so they describe the current source, which may be ahead of the published version. - [Project overview](https://raw.githubusercontent.com/ruandre/buzzsaw/main/README.md): what Buzzsaw is, install commands, the shortest working example of playback and WAV export. - [@rjvr/buzzsaw reference](https://raw.githubusercontent.com/ruandre/buzzsaw/main/packages/core/README.md): `SoundDefinition` fields and defaults, how `duration`/`attack`/`decay` interact, envelope interpolation, `Sound` and `SoundManager` APIs, playback handles, validation, inspection utilities, and the full preset list by category. - [@rjvr/buzzsaw-wav reference](https://raw.githubusercontent.com/ruandre/buzzsaw/main/packages/wav/README.md): `WavExporter` and `WavEncoder` APIs, export options, bit depths, the Node.js `offlineAudioContextClass` recipe, and RIFF header decoding. - [@rjvr/buzzsaw changelog](https://raw.githubusercontent.com/ruandre/buzzsaw/main/packages/core/CHANGELOG.md): breaking changes and migration notes per version. - [@rjvr/buzzsaw-wav changelog](https://raw.githubusercontent.com/ruandre/buzzsaw/main/packages/wav/CHANGELOG.md): the same, for the export package. ## Links - Studio: https://ruandre.github.io/buzzsaw/ - Source: https://github.com/ruandre/buzzsaw - npm, core: https://www.npmjs.com/package/@rjvr/buzzsaw - npm, wav: https://www.npmjs.com/package/@rjvr/buzzsaw-wav - License: MIT ## Behavior worth knowing before writing code The READMEs cover these in full. Repeated here because they are the details most often guessed wrong: - Annotate object literals as `SoundDefinition` (or use `satisfies`). Without it TypeScript widens `waveType: 'sawtooth'` to `string` and the object stops matching. - `attack` and `decay` are carved out of `duration`; neither extends it. Only an envelope step scheduled past `duration` extends a sound, and then only to that step plus a 10 ms tail. - The attack ramps up linearly from a 0.0001 silence floor; the decay ramps back down to it exponentially. Both apply to an envelope gain, where the attack ramps in to the envelope's `start`. `gain: 0` therefore renders as that floor, not true zero. - Frequency envelopes ramp linearly between steps; gain envelopes hold each value until the next. Set `interpolation` to `'linear'` or `'step'` to override either default. - `register` and `registerAll` return the manager, widened over the names just added, so `play` rejects typos at compile time. Chain them rather than calling them as separate statements. Use `new SoundManager()` for dynamic names. - `play()` resolves when the voice is scheduled, not when it ends. Await `handle.promise` for completion. It rejects when no `AudioContext` can be created or resumed, so guard with `isAudioContextSupported()` or catch the rejection. - Invalid definitions throw `SoundValidationError` at registration. Out-of-range option values throw `RangeError` rather than clamping. Definition values are equally strict: `gain` above 1, `frequency` above 20000 Hz, `duration` below 0.01, and a negative step `time` are all errors, and so is any property outside the seven `SoundDefinition` fields, which catches `waveform` written for `waveType`. - There is no noise source. Every voice is one oscillator, so approximate noisy textures with `waveType: 'custom'` and dense `partials`. - Rendering WAV needs an `OfflineAudioContext`. Node has no global one, so pass `offlineAudioContextClass`. Real-time playback in Node needs the same treatment: `setAudioContextInstance(new AudioContext())` from a polyfill. - `SoundManager` limits its master bus by default; WAV export does not, so a hot definition exports louder than it plays. ## Sound pack format The Studio's import/export schema, matching the exported `SoundPack` type: ```json { "version": 1, "exportedAt": "2026-09-02T00:00:00.000Z", "sounds": { "laser": { "waveType": "sawtooth", "frequency": 440, "duration": 0.2 } } } ``` A flat `{ [name: string]: SoundDefinition }` map is also accepted on import. ## Studio tools for in-browser agents In a WebMCP-capable browser, the Studio registers tools on `document.modelContext` and `navigator.modelContext`: - `list_sound_presets`: lists built-in presets. Arguments: `{ category?: string, search?: string }`. - `get_sound_definition`: returns the JSON definition of a sound. Arguments: `{ name: string }`. - `play_sound_preset`: plays a sound in the browser tab. Arguments: `{ name: string }`. - `synthesize_sound`: synthesizes a tone from oscillator parameters. Arguments: `{ name: string, frequency: number, duration: number, waveType?: string, attack?: number, decay?: number, gain?: number, saveToLibrary?: boolean }`. - `generate_ai_sound`: creates a sound from a text description. Arguments: `{ prompt: string, saveToLibrary?: boolean }`. - `edit_sound_definition`: modifies an existing definition from natural language. Arguments: `{ name: string, instruction: string, definition?: object, saveToLibrary?: boolean }`. - `get_studio_state`: returns active view, voice count, output meter, and library counts. Sound design from text uses the browser built-in Prompt API when available. Without it, a deterministic local heuristic takes over, needing neither a model nor a network. ## Privacy and storage All synthesis and export runs on-device. No telemetry, no third-party tracking scripts, and no external audio network requests. User-saved sounds are stored in the browser's `localStorage` and can be cleared from the Library view.