npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

supersonic-scsynth

v0.66.0

Published

SuperCollider scsynth WebAssembly port for AudioWorklet - Run SuperCollider synthesis in the browser

Readme

SuperSonic 0.66.0

SuperCollider's scsynth in the browser as an AudioWorklet.

Auto-generated from project docs — see GitHub for full documentation.

Table of Contents


Quick Start

You've installed SuperSonic - now let's make some sound.

We'll create a simple page with two buttons: one to boot the audio engine, and one to trigger a synth.

<button id="boot-btn">boot</button>
<button id="trig-btn">trigger</button>
import { SuperSonic } from "https://unpkg.com/supersonic-scsynth@latest";

const supersonic = new SuperSonic({
  baseURL: "https://unpkg.com/supersonic-scsynth@latest/dist/",
  synthdefBaseURL: "https://unpkg.com/supersonic-scsynth-synthdefs@latest/synthdefs/",
});

const bootBtn = document.getElementById("boot-btn");
const trigBtn = document.getElementById("trig-btn");

bootBtn.onclick = async () => {
  await supersonic.init();
  await supersonic.loadSynthDef("sonic-pi-prophet");
};

trigBtn.onclick = () => {
  supersonic.send("/s_new", "sonic-pi-prophet", -1, 0, 0, "note", 28, "release", 8, "cutoff", 70);
};

Let's break down what's happening here.

User Interaction

Web browsers have an autoplay policy that prevents websites from making sound without user consent. Audio can only start after a user interaction like a click, tap, or keypress.

This is why we use a boot button - calling init() from a button handler satisfies the browser's autoplay policy and allows audio to begin.

Creating a SuperSonic Instance

const supersonic = new SuperSonic({
  baseURL: "https://unpkg.com/supersonic-scsynth@latest/dist/",
  synthdefBaseURL: "https://unpkg.com/supersonic-scsynth-synthdefs@latest/synthdefs/",
});

This creates a new SuperSonic instance configured to load assets from CDN. The baseURL tells SuperSonic where to find the WASM engine and workers. The synthdefBaseURL tells it where to find synthdef files when you call loadSynthDef(). The instance doesn't start the audio engine yet - it just sets up the configuration. You can pass additional options to configure transport mode, debug output, and scsynth engine settings (see API Reference).

Booting the Engine

await supersonic.init();

Calling init() boots the scsynth audio engine. Behind the scenes this:

  1. Creates an AudioContext and AudioWorklet
  2. Loads the WebAssembly module containing scsynth
  3. Starts scsynth running in a dedicated high-priority audio thread

This is an async operation - the await ensures we don't proceed until the engine is ready.

Loading a Synth Definition

await supersonic.loadSynthDef("sonic-pi-prophet");

Before you can play a synth, you need to send its design to scsynth. This design is called a synth definition (or "synthdef") and is a recipe that describes a synth's audio graph - what oscillators, filters, and effects it uses and how they're connected.

SuperSonic comes with 127 ready-to-use synthdefs from Sonic Pi. Here we're loading sonic-pi-prophet, a warm polyphonic synth inspired by the Prophet-5.

Note: you can also use SuperCollider's Desktop app to design your own synthdefs and directly import them live at runtime into your SuperSonic session.

Triggering a Synth

supersonic.send("/s_new", "sonic-pi-prophet", -1, 0, 0, "note", 28, "release", 8, "cutoff", 70);

Now for the fun part - making sound! The send() method sends OSC (Open Sound Control) messages to scsynth. The /s_new command creates a new synth instance.

The arguments are:

| | | | |---------------|----------------------|----------------------------------------| | synthdef name | "sonic-pi-prophet" | Which synth to create | | node ID | -1 | Let scsynth assign an ID automatically | | add action | 0 | Add to the head of the target group | | target | 0 | The root group | | params... | "note", 28, ... | Name/value pairs for synth parameters |

The synth parameters control the sound. Here we set note to 28 (a low E), release to 8 seconds, and cutoff to 70 (filter brightness). Each synthdef has its own parameters - see the synthdef documentation for available options.

Working Example

See example/simple.html for a complete working example you can run locally, or example/simple_metrics.html for the same example with a live metrics dashboard.

Adding a Metrics Dashboard

SuperSonic includes a web component that renders a full metrics dashboard from the schema:

<link rel="stylesheet" href="https://unpkg.com/supersonic-scsynth@latest/dist/metrics-dark.css" />
<script type="module" src="https://unpkg.com/supersonic-scsynth@latest/dist/metrics_component.js"></script>

<supersonic-metrics id="metrics"></supersonic-metrics>

Connect it after boot to start live updates:

bootBtn.onclick = async () => {
  await supersonic.init();
  await supersonic.loadSynthDef("sonic-pi-prophet");
  document.getElementById("metrics").connect(supersonic, { refreshRate: 10 });
};

See Metrics Component for theming, layout control, and customisation.


API Overview

Coordinates WASM, AudioWorklet, SharedArrayBuffer, and IO Workers to run scsynth with low latency inside a web page.

Core

| Member | Description | | ----------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- | | init() | Initialise the engine. | | shutdown() | Shut down the engine. | | destroy() | Destroy the engine completely. | | recover() | Smart recovery — tries a quick resume first, falls back to full reload. | | suspend() | Suspend the AudioContext and stop the drift timer. | | resume() | Quick resume — calls purge to flush stale messages, resumes AudioContext, and resyncs timing. | | reload() | Full reload — destroys and recreates the worklet and WASM, then restores all previously loaded synthdefs and audio buffers. | | reset() | Shutdown and immediately re-initialise. | | send() | Send any OSC message. | | sendOSC() | Send pre-encoded OSC bytes to scsynth. | | sync() | Wait for scsynth to process all pending commands. | | purge() | Cancel all pending scheduled messages everywhere in the pipeline. | | cancelAll() | Cancel all scheduled messages in the JS prescheduler. | | cancelSession() | Cancel all scheduled messages for a session. | | cancelSessionTag() | Cancel scheduled messages matching both a session and run tag. | | cancelTag() | Cancel all scheduled messages with the given run tag. |

Asset Loading

| Member | Description | | ----------------------------------- | ------------------------------------------------------------------------- | | loadSynthDef() | Load a SynthDef into scsynth. | | loadSynthDefs() | Load multiple SynthDefs by name in parallel. | | loadSample() | Load an audio sample into a scsynth buffer slot. | | sampleInfo() | Get sample metadata (including content hash) without allocating a buffer. |

Events

| Member | Description | | --------------------------------------------- | ------------------------------------------------------------- | | on() | Subscribe to an event. | | off() | Unsubscribe from an event. | | once() | Subscribe to an event once. | | removeAllListeners() | Remove all listeners for an event, or all listeners entirely. |

Node Tree

| Member | Description | | ------------------------------- | ------------------------------------------------------------------- | | getTree() | Get the node tree in hierarchical format. | | getRawTree() | Get the node tree in flat format with linkage pointers. | | getSnapshot() | Get a diagnostic snapshot with metrics, node tree, and memory info. |

Metrics

| Member | Description | | ----------------------------------------- | -------------------------------------------------------------- | | getMetrics() | Get current metrics as a named object. | | getMetricsArray() | Get metrics as a flat Uint32Array for zero-allocation reading. | | getMetricsSchema() | Get the metrics schema describing all available metrics. |

Properties

| Member | Description | | ------------------------------- | -------------------------------------------------- | | initialized | Whether the engine has completed initialisation. | | initializing | Whether init is currently in progress. | | audioContext | The underlying AudioContext. | | node | AudioWorkletNode wrapper for custom audio routing. |

Advanced

| Member | Description | | --------------------------------------------------- | ----------------------------------------------------------------------- | | getInfo() | Get engine info: sample rate, memory layout, capabilities, and version. | | createOscChannel() | Create an OscChannel for direct worker-to-worklet communication. | | startCapture() | Start capturing audio output to a buffer. | | stopCapture() | Stop capturing and return the captured audio data. | | getCaptureFrames() | Get number of audio frames captured so far. | | isCaptureEnabled() | Check if audio capture is currently enabled. | | getMaxCaptureDuration() | Get maximum capture duration in seconds. | | setClockOffset() | Set clock offset for multi-system sync (e.g. Ableton Link, NTP server). |

Advanced

| Member | Description | | ----------------------------------------- | ------------------------------------------------------------ | | bufferConstants | Buffer layout constants from the WASM build. | | initTime | NTP time (seconds since 1900) when the AudioContext started. | | mode | Active transport mode ('sab' or 'postMessage'). | | ringBufferBase | Ring buffer base offset in SharedArrayBuffer. | | sharedBuffer | The SharedArrayBuffer (SAB mode) or null (postMessage mode). | | getLoadedBuffers() | Get info about all loaded audio buffers. | | getSystemReport() | Get a comprehensive system performance report. | | nextNodeId() | Get the next unique node ID. | | getRawTreeSchema() | Get schema describing the raw flat node tree structure. | | getTreeSchema() | Get schema describing the hierarchical node tree structure. |

OscChannel

OscChannel — unified dispatch for sending OSC to the AudioWorklet.

Obtain a channel via SuperSonic.createOscChannel on the main thread, then transfer it to a Web Worker for direct communication with the AudioWorklet.

| Member | Description | | --------------------------------------------- | ------------------------------------------------------------------------------- | | getCurrentNTP | Set the NTP time source for classification (used in AudioWorklet context). | | mode | Transport mode this channel is using. | | transferable | Serializable config for transferring this channel to a worker via postMessage. | | transferList | Array of transferable objects (MessagePorts) for the postMessage transfer list. | | classify() | Classify an OSC message to determine its routing. | | close() | Close the channel and release its ports. | | getAndResetMetrics() | Get and reset local metrics (for periodic reporting). | | getMetrics() | Get current metrics snapshot. | | nextNodeId() | Get the next unique node ID. | | send() | Send an OSC message with automatic routing. | | sendDirect() | Send directly to worklet without classification or metrics tracking. | | sendToPrescheduler() | Send to prescheduler without classification. | | fromTransferable() | Reconstruct an OscChannel from data received via postMessage in a worker. |

Example

// Main thread: create and transfer to worker
const channel = sonic.createOscChannel();
myWorker.postMessage(
  { channel: channel.transferable },
  channel.transferList,
);

// Inside worker: reconstruct and send
import { OscChannel } from 'supersonic-scsynth/osc-channel';
const channel = OscChannel.fromTransferable(event.data.channel);
channel.send(oscBytes);

Constructors

Constructor

new OscChannel(): OscChannel

Returns

OscChannel


Accessors

getCurrentNTP
Set Signature

set getCurrentNTP(fn): void

Set the NTP time source for classification (used in AudioWorklet context).

Parameters

| Parameter | Type | | --------- | -------------- | | fn | () => number |

Returns

void

mode
Get Signature

get mode(): TransportMode

Transport mode this channel is using.

Returns

TransportMode

transferable
Get Signature

get transferable(): OscChannelTransferable

Serializable config for transferring this channel to a worker via postMessage.

Example
worker.postMessage({ ch: channel.transferable }, channel.transferList);
Returns

OscChannelTransferable

transferList
Get Signature

get transferList(): Transferable[]

Array of transferable objects (MessagePorts) for the postMessage transfer list.

Example
worker.postMessage({ ch: channel.transferable }, channel.transferList);
Returns

Transferable[]


Methods

classify()

classify(oscData): OscCategory

Classify an OSC message to determine its routing.

Parameters

| Parameter | Type | Description | | --------- | ------------ | ----------------- | | oscData | Uint8Array | Encoded OSC bytes |

Returns

OscCategory

close()

close(): void

Close the channel and release its ports.

Returns

void

getAndResetMetrics()

getAndResetMetrics(): OscChannelMetrics

Get and reset local metrics (for periodic reporting).

Returns

OscChannelMetrics

getMetrics()

getMetrics(): OscChannelMetrics

Get current metrics snapshot.

Returns

OscChannelMetrics

nextNodeId()

nextNodeId(): number

Get the next unique node ID.

Thread-safe — can be called concurrently from multiple workers and no two callers will ever receive the same ID. IDs start at 1000 (0 is the root group, 1 is the default group, 2–999 are reserved for manual use).

Returns

number

A unique node ID (>= 1000)

send()

send(oscData): boolean

Send an OSC message with automatic routing.

Classifies the message and routes it:

  • bypass categories → sent directly to the AudioWorklet
  • far-future bundles → routed to the prescheduler for timed dispatch
Parameters

| Parameter | Type | Description | | --------- | ------------ | ----------------- | | oscData | Uint8Array | Encoded OSC bytes |

Returns

boolean

true if sent successfully

sendDirect()

sendDirect(oscData): boolean

Send directly to worklet without classification or metrics tracking.

Parameters

| Parameter | Type | Description | | --------- | ------------ | ----------------- | | oscData | Uint8Array | Encoded OSC bytes |

Returns

boolean

true if sent successfully

sendToPrescheduler()

sendToPrescheduler(oscData): boolean

Send to prescheduler without classification.

Parameters

| Parameter | Type | Description | | --------- | ------------ | ----------------- | | oscData | Uint8Array | Encoded OSC bytes |

Returns

boolean

true if sent successfully

fromTransferable()

static fromTransferable(data): OscChannel

Reconstruct an OscChannel from data received via postMessage in a worker.

Parameters

| Parameter | Type | Description | | --------- | ----------------------------------------------------- | --------------------------------------------------- | | data | OscChannelTransferable | The transferable config from channel.transferable |

Returns

OscChannel

Example
// In a Web Worker:
self.onmessage = (e) => {
  const channel = OscChannel.fromTransferable(e.data.ch);
  channel.send(oscBytes);
};

Variables


Patterns

Scheduling with OSC Bundles

Full-Precision NTP Timestamps

When scheduling bundles, a plain JavaScript number loses sub-microsecond precision because IEEE 754 float64 only has 52 mantissa bits — not enough for NTP's full 64-bit range. Use a [seconds, fraction] uint32 pair for lossless timestamps:

// Float — easy but loses precision at large NTP values
const ntpTime = SuperSonic.osc.ntpNow();
SuperSonic.osc.encodeBundle(ntpTime + 0.5, [
  ["/s_new", "sonic-pi-beep", 1001, 0, 0],
]);

// Uint32 pair — full 64-bit precision, no float loss
SuperSonic.osc.encodeBundle([3913056000, 2147483648], [
  ["/s_new", "sonic-pi-beep", 1001, 0, 0],
  ["/n_set", 1001, "amp", 0.5],
]);

TimeTag formats accepted by encodeBundle():

| Value | Meaning | |---|---| | 1, null, undefined | Execute immediately | | [seconds, fraction] | NTP uint32 pair — preserves full 64-bit precision | | Number (e.g. 3913056000.5) | NTP float — seconds since 1900 (fractional part encoded) |

Sending OSC from Workers

Avoiding the Main Thread

When you send OSC from the main thread, it has to compete with everything else happening there - DOM updates, event handlers, animations, your application logic. For simple cases this is fine, but if you're running a sequencer, processing MIDI input, or doing other timing-sensitive work, you can end up with jitter or UI stuttering.

Web Workers let you move work off the main thread. The challenge is getting OSC from a worker to the AudioWorklet efficiently. You could send messages back to the main thread and have it forward them - but that adds latency and defeats the purpose of using a worker in the first place.

SuperSonic solves this with OscChannel - a transferable object that gives workers a direct line to the AudioWorklet.

OscChannel

Normally you'd just call supersonic.send() - but workers are separate threads with no access to your main thread's objects. The supersonic instance doesn't exist in the worker's world.

You could have the worker send messages back to the main thread via postMessage, and have the main thread forward them to scsynth. But that puts the main thread back in the middle of every message - defeating the purpose of using a worker.

The solution is to create a direct channel from the worker to scsynth running in the AudioWorklet. SuperSonic gives you an OscChannel for exactly this.

You create an OscChannel with createOscChannel and then you need to transfer it to the worker.

Note: Transferring is critical because the core internal comms mechanisms cannot be copied to the worker with a standard postMessage, they must be explicitly transferred. This enables the worker to have full and unique ownership of the newly created OscChannel.

Here's how you create an OscChannel and transfer it to a worker:

// Main thread - create and transfer
const channel = supersonic.createOscChannel();
worker.postMessage(
  { type: "init", channel: channel.transferable },
  channel.transferList
);

The second argument to postMessage is optional - when provided, it lists which objects to transfer rather than copy.

In your worker, handle this as an init message and reconstruct the channel:

// Worker thread
import { OscChannel } from "supersonic-scsynth";

let channel = null;

self.onmessage = (event) => {
  if (event.data.type === "init") {
    channel = OscChannel.fromTransferable(event.data.channel);
  }
};

// Now you can send OSC directly to the AudioWorklet
channel.send(oscBytes);

Using a type field lets you handle different message types cleanly - for example init, start, stop (see the sequencer example below).

Multiple Workers

Each call to createOscChannel() returns a new channel with a unique source ID. An optional blocking parameter (SAB mode only) controls whether Atomics.wait() is used for guaranteed ring buffer delivery (default: true for workers where sourceId !== 0, false for main thread). Set to false for AudioWorklet use. This means you can have multiple workers all sending OSC independently - they don't need to coordinate with each other, and their messages can be traced back to their source in metrics and logs.

const sequencerChannel = supersonic.createOscChannel(); // sourceId: 1
const lfoChannel = supersonic.createOscChannel(); // sourceId: 2
const midiChannel = supersonic.createOscChannel(); // sourceId: 3

sequencerWorker.postMessage({ channel: sequencerChannel.transferable }, sequencerChannel.transferList);
lfoWorker.postMessage({ channel: lfoChannel.transferable }, lfoChannel.transferList);
midiWorker.postMessage({ channel: midiChannel.transferable }, midiChannel.transferList);

SAB vs PM Mode

OscChannel works identically in both modes - it detects the active mode and uses the appropriate communication method automatically. Your worker code doesn't need to know or care which mode is active.

See Communication Modes for details on choosing between SAB and postMessage modes.

Message Routing

OscChannel.send() automatically classifies messages and routes them appropriately:

| Message Type | Where It Goes | Why | | ---------------------------------- | ---------------------- | ----------------------------------- | | Regular messages (not bundles) | Direct to AudioWorklet | No timing requirements | | Immediate bundles (timetag 0 or 1) | Direct to AudioWorklet | Execute now | | Near-future bundles (within 500ms) | Direct to AudioWorklet | Close enough to buffer | | Late bundles (in the past) | Direct to AudioWorklet | Execute immediately | | Far-future bundles (>500ms ahead) | Prescheduler | Hold until closer to execution time |

The 500ms threshold is configurable via bypassLookaheadMs in the SuperSonic constructor.

OscChannel API

Methods

| Method | Description | | ---------------------- | ------------------------------------------ | | send(oscBytes) | Send with automatic routing | | sendDirect(oscBytes) | Force direct send (bypass prescheduler) | | classify(oscBytes) | Get routing classification without sending | | close() | Release resources |

Properties

| Property | Description | | --------------- | ------------------------------------------------------------- | | mode | 'sab' or 'postMessage' | | transferable | Data for postMessage transfer | | transferList | Transferable objects array | | getCurrentNTP | (setter) Set NTP time source function for timestamp classification |

Static Methods

| Method | Description | | ----------------------------------- | ----------------------------- | | OscChannel.fromTransferable(data) | Reconstruct channel in worker |

Example: Sequencer Worker

Here's a worker that runs a simple step sequencer:

// sequencer-worker.js
import { OscChannel, osc } from "supersonic-scsynth";

let channel = null;
let running = false;
let step = 0;
let bpm = 120;

const pattern = [60, 62, 64, 65, 67, 65, 64, 62]; // Notes to play

self.onmessage = (event) => {
  const { type, data } = event.data;

  if (type === "init") {
    channel = OscChannel.fromTransferable(data.channel);
  } else if (type === "start") {
    running = true;
    step = 0;
    tick();
  } else if (type === "stop") {
    running = false;
  } else if (type === "bpm") {
    bpm = data.bpm;
  }
};

function tick() {
  if (!running || !channel) return;

  const note = pattern[step % pattern.length];
  const msg = osc.encodeMessage("/s_new", [
    "sonic-pi-beep", -1, 0, 0,
    "note", note,
    "amp", 0.5
  ]);
  channel.send(msg);

  step++;
  const msPerBeat = 60000 / bpm;
  setTimeout(tick, msPerBeat / 4); // 16th notes
}

Lifecycle & Recovery

Setup vs Ready

The setup event fires after init/recover completes, before ready. Async handlers are awaited. Use it for any persistent audio infrastructure that needs to exist on both initial boot and after recovery:

  • Groups — node tree organization
  • FX chains — reverb, filters, compressors
  • Bus routing — synths that read/write to audio buses
  • Persistent synths — always-on nodes like analyzers or mixers
supersonic.on("setup", async () => {
  // Create group structure
  supersonic.send("/g_new", 100, 0, 0); // synths group
  supersonic.send("/g_new", 101, 1, 0); // fx group (after synths)

  // Create FX chain
  supersonic.send("/s_new", "sonic-pi-fx_reverb", 2000, 0, 101,
    "in_bus", 20, "out_bus", 0, "mix", 0.3);

  await supersonic.sync();
});

Why setup instead of ready? When recover() falls through to a full reload(), WASM memory is destroyed and recreated, so all nodes are lost. The setup event lets you rebuild consistently on both initial boot and after recovery, regardless of transport mode. See Communication Modes for more on SAB vs postMessage.

Tab Visibility & Recovery

Use visibilitychange to recover when the user switches back to your tab:

document.addEventListener("visibilitychange", async () => {
  if (!document.hidden) {
    await supersonic.recover();
  }
});

Resume vs Reload

resume() is fast but only works if the worklet is still alive — it calls purge() to flush stale scheduled messages, resumes the AudioContext, and resyncs timing. reload() is a full restart. When you don't know which is needed, use recover() — or handle it manually:

if (await supersonic.resume()) {
  console.log("Quick resume worked, nodes preserved");
} else {
  console.log("Worklet was killed, need full reload");
  await supersonic.reload();
}

Audio Routing

Connecting Microphone Input

const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
const micSource = audioContext.createMediaStreamSource(stream);

// Connect mic to SuperSonic input
micSource.connect(supersonic.node.input);

// Audio flows through scsynth's input buses (bus 2+ by default)
// Use In.ar(2) in a synthdef to read the mic signal

Connecting to an Analyser

const analyser = supersonic.audioContext.createAnalyser();

// Connect SuperSonic output to analyser (in addition to speakers)
supersonic.node.connect(analyser);

// Read frequency data
const data = new Uint8Array(analyser.frequencyBinCount);
analyser.getByteFrequencyData(data);

Samples

Loading from Different Sources

// By name (uses sampleBaseURL)
await supersonic.loadSample(0, "loop_amen.flac");

// By full path/URL
await supersonic.loadSample(0, "./custom/my-sample.wav");

// From user-selected file
const file = document.querySelector('input[type="file"]').files[0];
await supersonic.loadSample(0, file);

// From ArrayBuffer (e.g., fetched manually)
const response = await fetch("./audio/sample.wav");
const arrayBuffer = await response.arrayBuffer();
await supersonic.loadSample(0, arrayBuffer);

// Load partial sample (frames 1000-2000)
await supersonic.loadSample(0, "long-sample.flac", 1000, 1000);

Deduplication with sampleInfo()

Use sampleInfo() to get a SHA-256 content hash without allocating a buffer, then check before loading:

const info = await supersonic.sampleInfo("kick.wav");
console.log(info.duration, info.numChannels, info.sampleRate);

// Check for duplicates
const loaded = supersonic.getLoadedBuffers();
if (loaded.some(b => b.hash === info.hash)) {
  console.log("Already loaded, skipping");
} else {
  await supersonic.loadSample(nextBufnum, "kick.wav");
}


Constructor Options

| Property | Type | Description | Required | | ----------------------------------------------------------- | ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------- | | activityEvent? | ActivityLineConfig | Line length limits for activity events emitted to listeners. | | | audioContext? | AudioContext | Provide your own AudioContext instead of letting SuperSonic create one. | | | audioContextOptions? | AudioContextOptions | Options passed to new AudioContext(). Ignored if audioContext is provided. | | | autoConnect? | boolean | Auto-connect the AudioWorkletNode to the AudioContext destination. Default: true. | | | baseURL? | string | Convenience shorthand when all assets (WASM, workers, synthdefs, samples) are co-located. | Yes* | | bufferGrowIncrement? | number | Bytes to grow the buffer pool per growth event. Default: 32MB. | | | bypassLookaheadMs? | number | Bundles scheduled within this many ms of now are dispatched immediately for lowest latency. Bundles further in the future are held and dispatched closer to their scheduled time. Default: 500. | | | coreBaseURL? | string | Base URL for GPL assets: WASM and AudioWorklet (supersonic-scsynth-core package). Defaults to baseURL. | | | debug? | boolean | Enable all debug console logging. Default: false. | | | debugOscIn? | boolean | Log incoming OSC messages to console. Default: false. | | | debugOscOut? | boolean | Log outgoing OSC messages to console. Default: false. | | | debugScsynth? | boolean | Log scsynth debug output to console. Default: false. | | | fetchMaxRetries? | number | Max fetch retries when loading assets. Default: 3. | | | fetchRetryDelay? | number | Base delay between retries in ms (exponential backoff). Default: 1000. | | | maxBufferMemory? | number | Maximum buffer pool capacity in bytes. Pool grows on demand up to this limit. Default: 256MB. | | | mode? | TransportMode | Transport mode. - 'postMessage' (default) — works everywhere, no special headers needed - 'sab' — lowest latency, requires Cross-Origin-Opener-Policy and Cross-Origin-Embedder-Policy headers See docs/MODES.md for a full comparison of communication modes. | | | preschedulerCapacity? | number | Max pending events in the JS prescheduler. Default: 65536. | | | sampleBaseURL? | string | Base URL for audio sample files (used by SuperSonic.loadSample). | | | scsynthOptions? | ScsynthOptions | Engine options passed to scsynth World_New(). | | | snapshotIntervalMs? | number | How often to snapshot metrics/tree in postMessage mode (ms). | | | synthdefBaseURL? | string | Base URL for synthdef files (used by SuperSonic.loadSynthDef). | | | wasmBaseURL? | string | Base URL for WASM files. Defaults to coreBaseURL + 'wasm/'. | | | wasmUrl? | string | Full URL to the WASM binary. Overrides wasmBaseURL. | | | workerBaseURL? | string | Base URL for MIT worker scripts. Defaults to baseURL + 'workers/'. | | | workletUrl? | string | Full URL to the AudioWorklet script. Overrides coreBaseURL. | |

Required unless both coreBaseURL/workerBaseURL and wasmBaseURL are provided.


Server Options

| Property | Type | Description | Default | Range | | ----------------------------------------------------------- | ---------- | ------------------------------------------------------------------------------ | ------- | -------------- | | bufLength? | 128 | Audio buffer length — must be 128 (WebAudio API constraint). | 128 | 128 (fixed) | | loadGraphDefs? | 0 | 1 | Auto-load synthdefs from disk: 0 or 1. Default: 0. | 0 | 0–1 | | maxGraphDefs? | number | Max synth definitions. Default: 1024. | 1024 | 1+ | | maxNodes? | number | Max synthesis nodes — synths + groups. Default: 1024. | 1024 | 1+ | | maxWireBufs? | number | Max wire buffers for internal UGen routing. Default: 64. | 64 | 1+ | | memoryLocking? | boolean | Memory locking — not applicable in browser. Default: false. | false | — | | numAudioBusChannels? | number | Audio bus channels for routing between synths. Default: 128. | 128 | 1+ | | numBuffers? | number | Max audio buffers (1–65535). Default: 1024. | 1024 | 1–65535 | | numControlBusChannels? | number | Control bus channels for control-rate data. Default: 4096. | 4096 | 1+ | | numInputBusChannels? | number | Hardware input channels. Default: 2 (stereo). | 2 | 0+ | | numOutputBusChannels? | number | Hardware output channels (1–128). Default: 2 (stereo). | 2 | 1–128 | | numRGens? | number | Random number generators per synth. Default: 64. | 64 | 1+ | | preferredSampleRate? | number | Preferred sample rate. 0 = use AudioContext default (typically 48000). | 0 | 0, 8000–384000 | | realTime? | boolean | Clock source. Always false in SuperSonic (externally clocked by AudioWorklet). | false | — | | realTimeMemorySize? | number | Real-time memory pool in KB for synthesis allocations. Default: 8192 (8MB). | 8192 | 1+ | | verbosity? | number | Debug verbosity: 0 = quiet, 1 = errors, 2 = warnings, 3 = info, 4 = debug. | 0 | 0–4 |


scsynth Command Quick Reference

Send commands using send() which auto-detects types:

supersonic.send("/s_new", "sonic-pi-beep", 1000, 0, 0, "note", 60);

Or directly send OSC bytes that you have already pre-encoded via sendOSC():

supersonic.sendOSC(oscBytes);

| Command | Description | | -------------------------------------------- | -------------------------------------------------- | | Top-Level | | | /notify | Register for node event notifications | | /status | Query server status (UGens, synths, CPU) | | /sync | Wait for async commands to complete | | /version | Query server version info | | /rtMemoryStatus | Query realtime memory usage | | Synth Definitions | | | /d_recv | Load a synthdef from binary data | | /d_free | Free loaded synthdefs by name | | Nodes | | | /n_free | Delete nodes | | /n_run | Turn nodes on or off | | /n_set | Set node control values | | /n_setn | Set sequential control values | | /n_fill | Fill controls with a single value | | /n_map | Map controls to control buses | | /n_mapn | Map sequential controls to control buses | | /n_mapa | Map controls to audio buses | | /n_mapan | Map sequential controls to audio buses | | /n_before | Move node before another | | /n_after | Move node after another | | /n_query | Query node info | | /n_trace | Debug trace node execution | | /n_order | Reorder nodes within a group | | Synths | | | /s_new | Create a new synth | | /s_get | Get synth control values | | /s_getn | Get sequential synth control values | | /s_noid | Remove synth ID tracking | | Groups | | | /g_new | Create a new group | | /p_new | Create a parallel group | | /g_head | Move node to head of group | | /g_tail | Move node to tail of group | | /g_freeAll | Free all nodes in group | | /g_deepFree | Recursively free all synths in group | | /g_dumpTree | Print group tree (debug) | | /g_queryTree | Query group tree structure | | Buffers | | | /b_alloc | Allocate an empty buffer | | /b_allocRead | Allocate buffer and load audio file | | /b_allocReadChannel | Allocate buffer and load specific channels | | /b_free | Free a buffer | | /b_zero | Zero buffer contents | | /b_set | Set individual samples | | /b_setn | Set sequential samples | | /b_fill | Fill samples with a value | | /b_gen | Generate buffer contents (sine, cheby, etc.) | | /b_query | Query buffer info | | /b_get | Get sample values | | /b_getn | Get sequential sample values | | Control Buses | | | /c_set | Set control bus values | | /c_setn | Set sequential bus values | | /c_fill | Fill buses with a value | | /c_get | Get bus values | | /c_getn | Get sequential bus values | | SuperSonic Extensions | | | /b_allocFile | Load audio from inline file data (SuperSonic only) |


Parameter Types

| Notation | Type | Description | | -------- | ------- | ------------------------ | | int | Integer | 32-bit signed integer | | float | Float | 32-bit floating point | | double | Double | 64-bit floating point | | string | String | Null-terminated string | | bytes | Blob | Binary data (byte array) |

Repetition

N × indicates the parameter can be repeated N times. For example:

// /n_free takes N node IDs
supersonic.send("/n_free", 1000, 1001, 1002);

Node IDs

  • Use -1 to have the server auto-generate a unique node ID
  • Node ID 0 is the root group (always exists)
  • Positive integers are user-assigned IDs

Add Actions

Used when creating or moving nodes:

| Value | Action | Description | | ----- | ------- | --------------------------- | | 0 | head | Add to head of target group | | 1 | tail | Add to tail of target group | | 2 | before | Add before target node | | 3 | after | Add after target node | | 4 | replace | Replace target node |

Control Values

Controls can be set by index (integer) or name (string). Values can be:

  • float or int - Direct value
  • "cN" - Map to control bus N (e.g., "c0")
  • "aN" - Map to audio bus N (e.g., "a0")

Asynchronous Commands

Commands marked Async execute on a background thread. They reply with /done on success or /fail on error. Use /sync to wait for all async commands to complete.


These commands don't work in SuperSonic due to browser/AudioWorklet constraints.

For a complete guide to all differences between SuperSonic and scsynth—including unsupported UGens, architectural differences, and error handling—see SCSYNTH_DIFFERENCES.md.

Scheduling and Debug Commands

| Command | Reason | | ------------- | --------------------------------------------------------------------------------------------------------------------- | | /clearSched | Use cancelAll() or the fine-grained cancelTag(), cancelSession(), cancelSessionTag() methods instead | | /error | SuperSonic always enables error notifications so you never miss a /fail message | | /quit | Use destroy() to shut down the SuperSonic instance |

Plugin Commands

| Command | Status | | -------- | -------------------------------------------------------------------- | | /cmd | No commands currently registered | | /u_cmd | No UGens currently define commands |

These commands allow plugins to register custom functionality beyond the standard OSC API. None of the built-in UGens use them, but the mechanism exists if compelling use cases emerge. If you have a need for custom plugin commands, open an issue describing your use case.

Filesystem Commands

No filesystem in browser/WASM, so file-based commands aren't available:

| Command | Alternative | | -------------------- | ------------------------------------------------------------------------- | | /d_load | loadSynthDef() or /d_recv with bytes | | /d_loadDir | loadSynthDefs() | | /b_read | loadSample() | | /b_readChannel | loadSample() | | /b_write | Not available | | /b_close | Not available |

Buffer Commands

| Command | Reason | | ----------------- | -------------------------------------------------------------------------------- | | /b_setSampleRate| Not implemented - WebAudio automatically resamples buffers to context sample rate |

Use the JavaScript API to load assets - it fetches via HTTP and sends the data to scsynth:

await supersonic.loadSynthDef("sonic-pi-beep");
await supersonic.loadSample(0, "loop_amen.flac");

For full details on every command (parameters, replies, examples), see the scsynth Command Reference.


Full Method Reference

Methods

cancelAll()

cancelAll(): void

Cancel all scheduled messages in the JS prescheduler.

Returns

void

cancelSession()

cancelSession(sessionId): void

Cancel all scheduled messages for a session.

Parameters

| Parameter | Type | Description | | ----------- | -------- | ----------------- | | sessionId | string | Session to cancel |

Returns

void

cancelSessionTag()

cancelSessionTag(sessionId, runTag): void

Cancel scheduled messages matching both a session and run tag.

Parameters

| Parameter | Type | Description | | ----------- | -------- | -------------------------------- | | sessionId | string | Session to match | | runTag | string | Tag to match within that session |

Returns

void

cancelTag()

cancelTag(runTag): void

Cancel all scheduled messages with the given run tag. Only affects messages in the JS prescheduler (not yet dispatched to WASM).

Parameters

| Parameter | Type | Description | | --------- | -------- | ------------- | | runTag | string | Tag to cancel |

Returns

void

createOscChannel()

createOscChannel(options?): OscChannel

Create an OscChannel for direct worker-to-worklet communication.

The returned channel can be transferred to a Web Worker, allowing that worker to send OSC directly to the AudioWorklet without going through the main thread. Works in both SAB and postMessage modes.

The blocking option defaults to true for worker channels (sourceId !== 0) and false for main thread. Set to false for AudioWorkletProcessor use. In postMessage mode this has no effect.

For AudioWorkletProcessor use, import from 'supersonic-scsynth/osc-channel' which avoids DOM APIs unavailable in the worklet scope.

See docs/WORKERS.md for the full workers guide.

Parameters

| Parameter | Type | Description | | ------------------- | -------------------------------------------------- | ------------------------------------------------------- | | options? | { blocking?: boolean; sourceId?: number; } | Channel options | | options.blocking? | boolean | Whether sends block until the worklet reads the message | | options.sourceId? | number | Numeric source ID (0 = main thread, 1+ = workers) |

Returns

OscChannel

Example
const channel = sonic.createOscChannel();
myWorker.postMessage(
  { channel: channel.transferable },
  channel.transferList,
);
destroy()

destroy(): Promise<void>

Destroy the engine completely. The instance cannot be re-used.

Calls shutdown then clears the WASM cache and all event listeners. Emits 'destroy'.

Returns

Promise<void>

getCaptureFrames()

getCaptureFrames(): number

Get number of audio frames captured so far.

Returns

number

getInfo()

getInfo(): SuperSonicInfo

Get engine info: sample rate, memory layout, capabilities, and version.

Returns

SuperSonicInfo

Example
const info = sonic.getInfo();
console.log(`Sample rate: ${info.sampleRate}Hz`);
console.log(`Boot time: ${info.bootTimeMs}ms`);
console.log(`Version: ${info.version}`);
getLoadedBuffers()

getLoadedBuffers(): LoadedBufferInfo[]

Get info about all loaded audio buffers.

Returns

LoadedBufferInfo[]

Example
const buffers = sonic.getLoadedBuffers();
for (const buf of buffers) {
  console.log(`Buffer ${buf.bufnum}: ${buf.duration.toFixed(1)}s, ${buf.source}`);
}
getMaxCaptureDuration()

getMaxCaptureDuration(): number

Get maximum capture duration in seconds.

Returns

number

getMetrics()

getMetrics(): SuperSonicMetrics

Get current metrics as a named object.

This is a cheap local memory read in both SAB and postMessage modes — no IPC or copying. Safe to call from requestAnimationFrame.

See docs/METRICS.md for the full metrics guide.

Returns

SuperSonicMetrics

Example
const m = sonic.getMetrics();
console.log(`Messages sent: ${m.oscOutMessagesSent}`);
console.log(`Scheduler depth: ${m.scsynthSchedulerDepth}`);
getMetricsArray()

getMetricsArray(): Uint32Array

Get metrics as a flat Uint32Array for zero-allocation reading.

Returns the same array reference every call — values are updated in-place. Use SuperSonic.getMetricsSchema for offset mappings.

Returns

Uint32Array

Example
const schema = SuperSonic.getMetricsSchema();
const arr = sonic.getMetricsArray();
const sent = arr[schema.metrics.oscOutMessagesSent.offset];
getRawTree()

getRawTree(): RawTree

Get the node tree in flat format with linkage pointers.

More efficient than getTree for serialization or custom rendering.

Returns

RawTree

getSnapshot()

getSnapshot(): Snapshot

Get a diagnostic snapshot with metrics, node tree, and memory info.

Useful for capturing state for bug reports or debugging timing issues.

Returns

Snapshot

getSystemReport()

getSystemReport(): SystemReport

Get a comprehensive system performance report.

Includes hardware info, audio configuration, Chrome playbackStats (if available), a cross-browser audio health percentage, and a human-r