synth() -- Web Audio Synth Cue

Oscilla "Native WebAudio Utility Synth"

Oscilla Synth

The synth() cue creates a lightweight Web Audio sound source inside Oscilla. It is intended for reference tones, drones, textures, chords, and simple patterned sound processes, integrated directly into the score timeline.

The design prioritises deterministic, cue-scoped behaviour; readable parameter syntax; low default amplitudes and click-free envelopes; optional pattern sequencing; optional OSC mirroring; and real-time parameter binding from animation and control sources.

The synth is not intended as a full synthesiser environment. For full electroacoustic work, Oscilla is designed to operate in conjunction with external audio systems via OSC (e.g. SuperCollider, Pure Data, Max). The built-in synth provides a bounded, score-aligned sound source intended for rehearsal contexts away from a complete setup, as well as for simple tone cues, drones, and basic animation- or pattern-driven sound sequences embedded directly in the score.


Basic Usage

synth(uid:refA, wave:sine, freq:440, amp:0.1)

Pitch may be specified as Hz or note name:

synth(uid:tuning, wave:sine, freq:A4, amp:0.08)

Parameters

Key Default Description
uid auto Unique voice identity. Auto-generated from element id if omitted
wave sine Waveform type
freq 440 Frequency: Hz, note name, array (chord), pattern, rand(), or signal ref
amp 0.08 Amplitude 0--0.25. Accepts rand() or signal ref
pan 0 Stereo position -1 (left) to 1 (right). Accepts rand() or signal ref
env {a:0.02, d:0, s:1, r:0.1} ADSR envelope object
envPerStep false Retrigger envelope on each sequence step (articulated notes)
dur -- Step duration for patterns, or voice lifetime if no step engine. Omit for gate mode
filter -- Filter object {type, freq, q}
delay -- Delay object {time, fb, mix}
reverb -- Reverb object {mix, time, damp}
glide 0.02 Frequency interpolation time in seconds
interp smooth Interpolation mode: smooth or step
poly 0 Polyphony: 0=mono (retrigger updates), N=overlapping voices
lifetime region region (stop on playhead exit) or process (until explicit stop)
waveform self Scope display target: self, none, or element id
overlay 2 Overlay detail: 0/off, 1/brief, 2/full
osc 0 Enable OSC mirroring (1=on)
oscAddr -- Custom OSC address
rel env.r Override release time for synthStop

Wave Types

sine
square
saw
triangle
noise

Oscillator waveforms map directly to standard Web Audio oscillator types. noise produces a broadband noise source generated from a looping audio buffer.


Frequency and Pitch

Frequency accepts several formats:

Format Example Description
Hz freq:440 Direct frequency in Hertz
Note name freq:A4 Standard pitch notation (A0--C8)
MIDI number freq:69 MIDI note number (auto-detected above 127 as Hz)
Chord array freq:[440, 550, 660] Multiple oscillators summed
Random freq:rand(200, 800) Random Hz per trigger
Pattern freq:Pseq(220, 330, 440) Sequenced values
Signal ref freq:fader.t[200,2000] Real-time binding from control source

Chords

When freq is an array, multiple oscillators are created and summed. All voices share the same envelope and effects chain. A mixer gain node scales amplitude by 1 / sqrt(N) to prevent clipping.

synth(
  uid:pad3,
  wave:sine,
  freq:[440, 477, 644, 777],
  env:{a:1.5},
  amp:0.12
)

Chord arrays can contain rand() expressions. Each element is evaluated independently at voice creation time:

synth(
  uid:cluster,
  wave:triangle,
  freq:[rand(200, 400), rand(300, 600), rand(400, 800)],
  env:{a:3, r:2},
  amp:0.1
)

Retriggering from the live console re-evaluates all rand() calls, producing a new chord voicing each time.

Chord frequencies can be updated live. Existing oscillators ramp to new pitches, excess oscillators stop, and new oscillators start as needed.


Amplitude Envelope (ADSR)

synth(
  uid:env1,
  wave:saw,
  freq:220,
  amp:0.12,
  env:{a:0.5, d:0.2, s:0.7, r:1.2}
)

Envelope parameters:

Key Default Description
a 0.02 Attack time in seconds (ramp from 0 to peak)
d 0 Decay time in seconds (ramp from peak to sustain)
s 1 Sustain level as fraction of peak amplitude (0--1)
r 0.1 Release time in seconds (ramp to silence on stop)

The envelope uses linearRampToValueAtTime for the attack and decay stages. Sustain holds indefinitely until the voice is stopped (by explicit synthStop, dur timeout, or region exit), at which point the release ramp begins from the current gain value.

When both env and dur are specified, dur controls voice lifetime: the voice auto-stops after dur seconds, triggering the release envelope. This allows self-terminating voices with shaped envelopes:

synth(uid:swell, wave:sine, freq:220, env:{a:6, r:4}, amp:0.12, dur:12)

This voice fades in over 6 seconds, sustains, then stops at 12 seconds and fades out over 4 seconds.


Per-Step Envelope (envPerStep)

By default, pitch sequences glide smoothly between notes with the envelope applied only at the start and end of the entire sequence. For articulated sequences where each note should have its own attack-decay-sustain-release cycle, enable envPerStep:

synth(
  uid:melody,
  wave:triangle,
  freq:Pseq(C4, E4, G4, C5),
  dur:0.5,
  env:{a:0.05, d:0.1, s:0.6, r:0.15},
  envPerStep:true,
  amp:0.1
)

With envPerStep:true, each pitch step triggers a complete ADSR cycle:

  1. Attack ramps gain from 0 to peak
  2. Decay ramps from peak to sustain level
  3. Sustain holds until release begins
  4. Release ramps to silence, timed to complete as the next step starts

Envelope Compression

If the step duration is shorter than the total envelope time (attack + decay + release), the envelope phases are compressed proportionally to fit within the step. This ensures smooth transitions even at fast tempos.

Comparison

Mode Behaviour
envPerStep:false (default) Legato: pitches glide smoothly, envelope only at start/end
envPerStep:true Articulate: each pitch gets full ADSR, distinct note attacks

Polyphony

The poly parameter controls how retriggers with the same uid behave.

Value Behaviour
poly:0 (default) Mono mode. Retrigger updates the running voice in place
poly:N Up to N overlapping voices. Oldest voice is stopped when count exceeds N

In mono mode, retriggering with the same uid calls updateSynthVoice -- parameters change smoothly without restarting playback or envelope. This is the default and is suitable for live console tweaking.

In poly mode, each trigger creates an independent sub-voice with its own oscillator(s), envelope, and effects chain. Sub-voices are internally named uid__0, uid__1, etc. The dur parameter and rand() expressions are especially useful in poly mode, since each voice evaluates its own random values and manages its own lifetime.

Example: Overlapping Swells

synth(
  uid:swell,
  wave:triangle,
  freq:[rand(33, 333), rand(33, 533), rand(33, 1333)],
  env:{a:6, r:4},
  amp:0.12,
  dur:12,
  poly:12
)

Each trigger creates a new 3-oscillator chord with random frequencies, fading in over 6 seconds. Up to 12 can overlap. The 13th trigger stops the oldest voice (with its release envelope). All poly voices share a single scope display rect in the GUI.

Stopping Poly Voices

synthStop(uid:swell) stops all sub-voices in the group. Each voice fades out independently using its own release time.


Patterned Parameters

The following parameters accept static values, arrays, or pattern functions. When patterns are present, a step engine advances through values at the rate specified by dur.

Parameter Description
freq Pitch sequence
amp Amplitude sequence
dur Step duration sequence
filter.freq Filter cutoff sequence
filter.q Filter resonance sequence
pan Stereo position sequence

Frequency Sequence

synth(
  uid:seq1,
  wave:triangle,
  freq:Pseq(220, 330, 440),
  dur:Pseq(1, 1, 2),
  amp:0.08
)

Patterned Chord Progression

synth(
  uid:chordSeq,
  wave:saw,
  freq:Pseq(
    [220, 330, 440],
    [247, 370, 494],
    [196, 294, 392]
  ),
  dur:1.5,
  amp:0.1
)

Random Pitch Selection

synth(
  uid:randFreq,
  wave:square,
  freq:Prand(200, 400, 600),
  dur:0.8,
  amp:0.09
)

Available Patterns

Pattern Behaviour
Pseq(...) Sequential, loops forever
Prand(...) Random selection per step
Pxrand(...) Random without consecutive repeats
Pshuf(...) Shuffle, then repeat the shuffled order

Random Expressions

rand(min, max) and irand(min, max) can be used for any numeric parameter. They are evaluated at voice creation time for static params.

synth(uid:x, wave:sine, freq:rand(200, 2000), amp:rand(0.02, 0.12))

rand() returns a continuous float. irand() returns an integer. Both work inside chord arrays, filter configs, and anywhere a numeric value is expected.

For patterned parameters, random values evaluate once when the pattern is built. For per-step randomness, use Prand(...).


Random Expressions in Chords

rand() expressions inside freq arrays are evaluated individually per oscillator per trigger:

synth(uid:x, wave:sine, freq:[rand(100, 500), rand(200, 800), rand(300, 1200)])

Each trigger produces three oscillators at different random frequencies. Retriggering from the live console rolls new values each time.


Filters

synth(
  uid:filterSeq,
  wave:saw,
  freq:330,
  filter:{type:lp, freq:Pseq(400, 800, 1600, 800), q:0.7},
  dur:0.5,
  amp:0.08
)

Filter parameters:

Key Default Description
type lp Filter type: lp, hp, bp, notch
freq / cutoff 1200 Cutoff frequency. Accepts rand() or signal ref
q / resonance 1 Resonance. Accepts rand() or signal ref

Delay

synth(
  uid:delayLead,
  wave:square,
  freq:550,
  delay:{time:0.25, fb:0.35, mix:0.2},
  amp:0.12
)
Key Default Description
time 0.3 Delay time in seconds
fb 0.3 Feedback amount 0--0.95
mix 0.3 Wet/dry mix 0--1

Reverb

synth(
  uid:revDrone,
  wave:sine,
  freq:110,
  reverb:{mix:0.3, time:2, damp:3000},
  amp:0.07
)
Key Default Description
mix 0.3 Wet/dry mix 0--1
time 1.5 Reverb time (controls delay line lengths)
damp 4000 Damping lowpass frequency in Hz

Glide

synth(
  uid:glide1,
  wave:sine,
  freq:Pseq(220, 330, 440),
  glide:0.1,
  dur:1,
  amp:0.1
)

glide specifies the time in seconds used to interpolate frequency changes between steps. It applies only to pitch. Default is 0.02 (near-instant). Set higher values for audible portamento.


Interpolation Mode

synth(
  uid:stepSeq,
  wave:triangle,
  freq:Pshuf(300, 450, 600),
  interp:step,
  dur:1,
  amp:0.08
)
Value Behaviour
smooth (default) Ramps between values using setTargetAtTime
step Immediate value changes using setValueAtTime

Pan

synth(uid:panTest, wave:sine, freq:440, pan:-0.6, amp:0.09)

Pan accepts static values, rand() expressions, patterns, or signal references. Range is -1 (left) to 1 (right).


Lifetime and Duration

Gate Mode (default)

When no dur is specified, the synth operates in gate mode: it plays while the playhead is inside the cue element's region and releases when the playhead exits. This is ideal for sustained tones that follow the score timeline.

synth(uid:regionTone, wave:sine, freq:220, amp:0.06)

The synth starts on playhead entry, sustains indefinitely, and triggers its release envelope on playhead exit. If the cue element has an extent handle (for waveform-line or handle display modes), the region boundary extends to that handle position.

Explicit duration

synth(uid:fixedDur, wave:sine, freq:220, dur:5, amp:0.07)

The synth auto-stops after 5 seconds. If an envelope is specified, the release ramp begins at the stop point. This switches lifetime to process mode internally.

Process lifetime

synth(uid:persist, wave:saw, freq:110, lifetime:process, amp:0.05)

The synth continues until explicitly stopped with synthStop(uid:persist).

Extent Handle Support

When using waveform-line or handle display modes, the cue element has a draggable extent handle that defines the right boundary of the region. Synths with region-based lifetime (gate mode) respect this extended boundary -- they continue playing until the playhead exits past the extent handle, not just the original element bounding box.

Parent Group Extent (SVG-Authored Cues)

For SVG-authored cues where the synth DSL is placed directly in an element's id attribute (authored in Inkscape or another SVG editor), the extent region can be defined by a parent group. If the synth element is placed inside a group (<g>) that contains an extent path or line, the playhead uses the group's bounding box rather than just the synth element's box.

This enables workflows where the synth rect and an extent line are grouped together in the SVG editor, with the group defining the active region:

<g id="g20">
  <rect id="synth(wave:sine, freq:440, amp:0.1)" ... />
  <path d="m 100,50 h 500" />  <!-- extent line -->
</g>

The synth remains active until the playhead exits the group's combined bounding box.

Duration vs. Step Engine

When pattern parameters are present (making _hasStepEngine true), dur controls step timing rather than voice lifetime. Use stopAfter or lifeDur to set an explicit voice lifetime independently of step duration:

synth(uid:seq, wave:saw, freq:Pseq(220, 330, 440), dur:0.5, stopAfter:10, amp:0.08)

This steps through frequencies every 0.5 seconds and stops the entire voice after 10 seconds.


Signal Binding (Control Plane)

Synth parameters can be bound to real-time signal sources such as controlXY faders, o2p animations, rotate, and scale cues. Bound parameters update continuously at audio rate.

Syntax

param:source.channel[min,max]

The source is the uid of a publishing cue, and the channel specifies which signal dimension to follow. The optional [min,max] range maps the 0--1 signal to the specified output range.

Bindable Parameters

Parameter Default Range Description
freq 20--20000 Oscillator frequency
amp 0--0.5 Output amplitude
pan -1 to 1 Stereo position
cutoff / filterFreq 20--20000 Filter cutoff frequency
q / resonance 0.1--30 Filter resonance

Examples

synth(uid:pad, freq:myFader.t[200,800], amp:0.15)
synth(uid:drone, freq:220, amp:slider.y[0,0.25], pan:slider.x[-1,1])
synth(uid:filter1, wave:saw, freq:440, filter:{type:lp, freq:knob.t[200,8000], q:2})

When a signal ref is used for freq, chord mode is disabled (binding applies to a single oscillator). Static values work alongside bound values: you can bind freq while keeping amp static, or vice versa.

Bindings are automatically cleaned up when the voice stops.


Oscilloscope Display

Each synth voice renders a real-time oscilloscope trace driven by a Web Audio AnalyserNode. The scope shows the time-domain waveform shape updating at display framerate.

Display Modes

waveform value Behaviour
self (default) Render scope inside the cue element
none Suppress scope display
<element_id> Render inside a different SVG element by id

Scope Stacking

Multiple synths can share a single display area. When waveform points to another synth's uid, the scope renders into that synth's rect with a distinct colour:

synth(uid:pad1, wave:sine, freq:[740, 477, 644], amp:0.12)
synth(uid:pad2, wave:square, freq:880, waveform:pad1, amp:0.1)

pad2's scope stacks into pad1's display element with a different colour from the 12-colour palette.

Poly Scope Display

All poly voices for a given uid share a single display rect. The rect is created when the first voice triggers and persists across group recreation. Each voice gets its own scope trace in a distinct colour, and traces are cleaned up when their voice stops.

Info Text

A text line above the scope shows the voice's current parameters: uid, waveform type, frequency/chord voicing, amplitude, and any active effects. This text updates when parameters change via live console.

Live Console Scopes

When synth cues are triggered from the live console without a score element, an ephemeral SVG rect is created in the viewport as a scope anchor. These rects stack vertically and can be dragged and resized. They persist until the global stop button clears them.


OSC Mirroring

synth(
  uid:oscLead,
  osc:1,
  oscAddr:/synth/lead,
  wave:saw,
  freq:440,
  amp:0.1
)

When osc:1 is enabled, synth state changes (start, stop, parameter updates) are sent as OSC messages to external software via the Oscilla server.


Stopping

synthStop(uid:seq1, rel:0.5)

The rel parameter overrides the envelope release time. Without it, the voice's env.r value is used.

Voices can also be stopped by:


Click-to-Trigger

Synth cues are click-triggerable. Clicking the synth element in the score starts the synth immediately, regardless of playhead position.

By default, clicking an already-playing synth stops it (toggle behaviour). This can be controlled with the toggle parameter:

toggle value Behaviour
(omitted) Toggle on click, retrigger on playhead
toggle:true Always toggle (click and playhead)
toggle:false Never toggle, always retrigger
synth(wave:sine, freq:440, amp:0.1, toggle:false)

This synth will layer multiple instances on repeated clicks instead of toggling.

For poly voices, synthStop(uid:X) stops all sub-voices in the group. Each voice fades out independently using its own release time.


Live Console

Hot-Update (Mono Mode)

In mono mode (default, poly:0), retriggering a synth from the live console with the same uid updates the running voice without restarting it. The following parameters update immediately:

Parameter Behaviour
amp Gain ramp via setTargetAtTime
pan Pan ramp on StereoPannerNode
wave Oscillator type change (instant)
freq (single) Frequency ramp with glide
freq (chord) Ramp existing oscs, stop excess, create new
filter.freq Cutoff ramp
filter.q Resonance ramp
lifetime Switch between region/process

Pattern parameters (Pseq, Prand, etc.) are rebuilt on update. Duration and stop-after timers are reset.

Poly Mode from Console

With poly:N, each execution from the live console creates a new overlapping voice. The rand() expressions re-evaluate on each trigger, so repeated execution of the same line produces different results:

synth(uid:cloud, wave:sine, freq:[rand(100,2000), rand(100,2000)], env:{a:4, r:6}, dur:8, poly:20)

Execute this line repeatedly to build up a cloud of overlapping two-note chords, each with random pitches, each fading in over 4 seconds and releasing over 6 seconds after the 8-second lifetime.


Signal Chain

The internal audio graph for each voice is:

source (oscillator or noise buffer)
  -> filter (optional, BiquadFilterNode)
  -> gain (GainNode, envelope target)
  -> panner (optional, StereoPannerNode)
  -> delay (optional, feedback delay)
  -> reverb (optional, multi-tap reverb)
  -> analyser (AnalyserNode for scope display)
  -> destination

For chords, all oscillators feed through a shared mixer gain node (scaled by 1/sqrt(N)) before the filter stage.


Examples

// Simple reference tone
synth(uid:ref, wave:sine, freq:A4, amp:0.08)

// Drone with slow attack
synth(uid:drone, wave:saw, freq:55, env:{a:3, s:0.6, r:2}, amp:0.1)

// Chord with envelope
synth(uid:pad, wave:sine, freq:[440, 550, 660], env:{a:2, d:0.5, s:0.7, r:3}, amp:0.12)

// Filtered sequence
synth(uid:bass, wave:square, freq:Pseq(55, 82, 110), dur:0.5,
  filter:{type:lp, freq:Pseq(200, 800, 1600), q:2}, amp:0.1)

// Random chord cloud (poly, from live console)
synth(uid:cloud, wave:triangle,
  freq:[rand(100, 1000), rand(200, 1500), rand(300, 2000)],
  env:{a:5, r:3}, dur:10, amp:0.08, poly:16)

// Signal-bound drone (controlled by fader)
synth(uid:ctrlDrone, wave:saw, freq:fader1.t[80,800],
  amp:fader1.y[0,0.2], filter:{type:lp, freq:knob1.t[200,4000]})

// Noise burst with delay
synth(uid:noiseBurst, wave:noise, dur:0.1,
  delay:{time:0.15, fb:0.6, mix:0.4}, amp:0.15)

// Patterned pan movement
synth(uid:ping, wave:sine, freq:1200, dur:0.3,
  pan:Pseq(-1, -0.5, 0, 0.5, 1), amp:0.06)

// Self-terminating swell with long envelope
synth(uid:swell, wave:sine, freq:rand(100, 400),
  env:{a:8, r:6}, dur:15, amp:0.1)

Summary

The synth() cue provides a bounded, score-aligned sound source suitable for reference tones, drones, chords, patterned textures, and signal-controlled processes. All behaviour is deterministic and cue-scoped. Polyphony enables overlapping voices with independent envelopes and random parameters. The oscilloscope display provides real-time visual feedback. Signal binding connects synth parameters to Oscilla's control plane for continuous modulation from faders, animations, and other score elements.


See Also

Tip: use ← → or ↑ ↓ to navigate the docs