Skip to content

AudioSynth

Software audio synthesizer that plays short tones, musical notes, and melodies. Useful for lab-side audible notifications, robot/CNC feedback tones, and experiment-complete bell sounds — the kind of "done!" cue that lets you know an experiment is finished from across the room.

AudioSynth supports three output paths, picked via the output config field:

  • browser (default) — streams PCM frames to every connected dashboard via the WebSocket. Cross-platform: works on Windows, Linux, and macOS hosts. All clients connected to the server hear the same audio simultaneously.
  • server — plays locally on the host's default audio output via NAudio. Windows-only — useful when the lab machine itself has speakers attached and you want sound there even when no browser is open.
  • both — runs both paths in parallel.

AudioSynth is a free tier-3 extension driver. It ships as a .muxdriver package and installs into workspace/drivers/ alongside other extension drivers. In server mode, audio plays through whichever output device the OS has set as default; AudioSynth does not expose output-device selection.

Every note is shaped by an ADSR envelope (attack → decay → sustain → release) and optionally layered with additive harmonic partials, so you can get bell-like shimmer or pluck-like transients without leaving the 1980s-beep comfort zone if you don't want to.

Properties

PropertyTypeAccessDescription
volumedoubleR/WOutput volume, 0.0 to 1.0. Applies to the next playback.
waveformstringR/WOscillator waveform: sine, square, triangle, or sawtooth.
presetstringR/WTimbre preset — sets ADSR + harmonics in one go. See Presets.
attackint (ms)R/WADSR attack time. Linear ramp from 0 to full amplitude.
decayint (ms)R/WADSR decay time. Exponential fall from full amplitude to sustain.
sustaindoubleR/WADSR sustain level, 0.0 to 1.0.
releaseint (ms)R/WADSR release time. Exponential fall from sustain to 0.
harmonicsarrayR/WAdditive partials as [[multiplier, amplitude], ...].
playingboolRWhether a tone or melody is currently rendering.

Actions

ActionArgsDescription
playTonefrequency, durationPlay a single tone at frequency Hz for duration ms.
playNotenote, durationPlay a scientific-pitch note (A4, C#5, Bb3) for duration ms.
playMelodynotesPlay a sequence — shorthand string or array of {note, duration} objects.
beep880 Hz, 120 ms confirmation tone at the current volume, waveform, and envelope.
stopCancel any in-flight playback. Idempotent.

Config Options

OptionTypeDefaultDescription
outputstringbrowserOutput path — browser, server, or both. See Output Modes.
volumedouble0.5Initial volume, 0.0 to 1.0.
waveformstringsineInitial waveform.
presetstringbeepInitial preset — applied before any explicit ADSR/harmonics overrides.
attackintOverride the preset's attack time (ms).
decayintOverride the preset's decay time (ms).
sustaindoubleOverride the preset's sustain level (0..1).
releaseintOverride the preset's release time (ms).
harmonicsarrayOverride the preset's partials.

Output Modes

output decides where the synthesised audio actually plays.

browser (default)

Each note is rendered server-side at 48 kHz mono, chunked into 20 ms Opus packets (32 kbit/s VBR), base64-encoded, and emitted on the driver's audio stream. Every browser dashboard subscribed to that stream decodes the chunks via the WebCodecs AudioDecoder and plays them through the Web Audio API. All connected clients hear the same audio simultaneously — useful for lab notifications that should be audible regardless of which machine someone is sitting at.

Requires a user gesture once per session (browser policy): the first click/keydown/touch unlocks audio. Until then, chunks are dropped silently. There is no special UI for this — clicking anywhere in the dashboard primes it.

server

Original v1.2 behaviour: NAudio's WaveOutEvent plays each note on the host machine's default audio device. Windows-only; on Linux/Mac the call throws at first playback. Use this mode when the host machine has speakers attached and you want sound there even when no browser is open. No subscriber needed.

both

Runs server and browser in parallel. NAudio plays locally; Opus packets are also streamed to dashboards. On non-Windows hosts the local-output half fails silently and the streaming half still works.

Presets

Each preset bundles an ADSR envelope and a partial set into a single name. Setting preset overwrites the five ADSR/harmonics properties at once — you can still tweak individual fields afterwards for fine control.

PresetADSRPartialsFeel
beep5 ms01.010 msfundamentalLegacy 1980s confirmation beep.
bell5 ms1500 ms0200 ms1, 2.76, 5.4, 8.93, 13.34Tubular-bell shimmer — long exponential decay.
pluck2 ms400 ms050 ms1, 2, 3, 4Short plucked transient.
pad300 ms100 ms0.8500 ms1, 2, 3Slow mellow swell for non-urgent cues.
marimba3 ms500 ms0100 ms1, 4, 9.2Stiff-bar strike, wooden feel.

Setting an unknown preset throws ArgumentException.

ADSR Envelope

The envelope fits inside the note's total duration:

  1.0 ┤   ╱╲
      │  ╱  ╲___________
      │ ╱      sustain  ╲__
  0.0 ┤╱                    ╲___
      └─a─┼─d─┼────hold────┼─r──┘
      0                    duration
  • Attack — linear ramp from 0 to full amplitude.
  • Decay — exponential fall from full amplitude to sustain level.
  • Sustain hold — flat at sustain until release starts.
  • Release — exponential fall from sustain to 0.

If attack + decay + release exceeds the note's total duration, all three are scaled down proportionally so the full curve still fits — you never lose the release tail.

Additive Harmonics

Each entry in harmonics is a [multiplier, amplitude] pair. The generator sums the base waveform at every partial frequency, weighted by its amplitude. Amplitudes are normalised so their sum is 1.0, which prevents clipping regardless of how many partials you stack.

javascript
// Bell-like partials — mostly inharmonic, which is what makes a real bell
// sound like a bell instead of a sawtooth.
audio.harmonics = [[1, 1], [2.76, 0.6], [5.4, 0.3], [8.93, 0.2], [13.34, 0.1]];

// Object form also accepted
audio.harmonics = [{ mult: 1, amp: 1 }, { mult: 2, amp: 0.5 }];

Playback Semantics

  • Replace-not-queue. Every new playTone, playNote, or playMelody cancels whatever was already playing. You do not need to call stop() between calls.
  • stop() is cooperative. Cancellation happens at sample-chunk boundaries, so there is no click.
  • volume, waveform, preset, and ADSR/harmonics apply to the next playback. Changing them mid-note does not affect the note in progress.

Note Names

Notes use scientific-pitch notation: a letter AG, an optional accidental # or b, and an octave number. A4 is standard concert pitch (440 Hz). Supported range is C-1 through B9.

ExampleFrequency
A4440.00 Hz
C4261.63 Hz (middle C)
C#5554.37 Hz
Bb3233.08 Hz

Melody Shorthand

javascript
// Shorthand — "NOTE:DURATION_MS, NOTE:DURATION_MS, ..."
audio.playMelody({ notes: "C4:200,E4:200,G4:400" });

// Array form
audio.playMelody({ notes: [
  { note: "C4", duration: 200 },
  { note: "E4", duration: 200 },
  { note: "G4", duration: 400 },
]});

Example Connector

javascript
// workspace/connectors/audio.js
export default {
    driver: "AudioSynth",

    ai: {
        instructions: "Use beep() for quick cues and playMelody() for distinct alerts. Change `preset` for different timbres — 'bell' for chimes, 'pluck' for pings.",
    },

    volume: 0.5,
    waveform: "sine",
    preset: "bell",

    methods: {
        chime: {
            fn: async () => {
                driver.preset = "bell";
                await driver.playMelody({ notes: "C5:400,E5:400,G5:1500" });
            },
            description: "Three-note bell chime — useful for 'done' cues",
        },
        alert: {
            fn: async () => {
                driver.preset = "pluck";
                await driver.playMelody({ notes: "A5:120,A5:120,A5:120" });
            },
            description: "Triple-pluck alert — useful for warnings",
        },
    },
};

Example Script

javascript
const audio = connector("audio");

// Quick feedback
audio.beep();

// A nice bell instead of a 1980s beep
audio.preset = "bell";
await audio.playNote({ note: "C5", duration: 2000 });

// Fully custom envelope
audio.attack = 10;
audio.decay = 300;
audio.sustain = 0.4;
audio.release = 800;
audio.harmonics = [[1, 1], [2, 0.4], [3, 0.2]];
await audio.playNote({ note: "A4", duration: 1500 });

// Cancel a long-running tone
audio.playTone({ frequency: 440, duration: 5000 });
await delay(1000);
audio.stop();

Out of Scope

AudioSynth deliberately keeps the surface small. The following are not supported:

  • WAV/MP3/OGG file playback
  • Microphone input or recording
  • MIDI input / output
  • Output-device selection or routing (uses the OS default in server mode)
  • Polyphony (multiple simultaneous notes within one connector)
  • Effects (reverb, delay, EQ)
  • Visualisation data (the audio stream carries encoded playback audio, not a waveform/FFT view for widgets)

If you need any of those, a different driver is the right place for them.

Platform Support

  • browser mode (default) — works on Windows, Linux, and macOS. Audio plays through every connected dashboard's Web Audio API; no native audio dependency on the host.
  • server mode — uses NAudio, which targets Windows (WASAPI / WaveOut). On Linux/Mac the first playback throws — switch to browser or both.
  • both modeserver half fails silently on non-Windows hosts; browser half always works. Useful on Windows when you want both host-speaker and dashboard playback at the same time.

The audio stream uses 48 kHz mono Opus (32 kbit/s VBR, 20 ms frames), base64-encoded inside a JSON wrapper. See WebSocket API → audio streams for the wire format.

Muxit — Hardware Orchestration Platform