Script handlers
A script reacts to events. Each event has its own handler, written as
on <eventname> opening a block that closes with end. In Scoped
mode the editor handles the on ... end wrapper for you; this page
shows the assembled form.
Two handlers, note and release, run per voice. The rest run
globally: there is one instance of the handler, not one per voice.
Per-voice handlers see the voice's own voice var slots; global
handlers do not.
Every handler can read the always-available clock variables bpm,
time, and playing. The event-specific variables are listed under
each handler.
For the language itself, the parameter primitives, and the editor, see the scripting overview.
on init
Fires once, on the first audio block after the script is applied (or the patch is loaded). Use it to log a startup line, prime a global counter from a tuning constant, or warm up state.
Bound variables: bpm, time, playing.
on init
log("script loaded")
end
There is no matching shutdown handler. State that needs resetting is reset by re-applying the script.
on note
Fires per voice, at note-on, after the voice has been claimed but before audio for that note has rendered. Use it to make decisions that depend on which note was played and how hard.
Bound variables: pitch (0..127), velocity (0..127), channel
(MIDI channel of the source note), plus the clock variables.
on note is the only handler where a voice var is initialised
fresh. Inside its body, the voice variables are at their declared
defaults; they keep their values across later on tick-style events
only for this voice's lifetime.
on note
if velocity > 100
ramp("filt_cutoff", 4000, 80)
end
end
A script-fired play(...) does not trigger another on note. The
handler runs for notes received from the keyboard or the host only,
not for notes the script itself starts.
on release
Fires per voice, at note-off. The voice is still alive at this point; it is in its release tail. Use it to schedule echoes, drop a parting modulation, or count releases per pitch.
Bound variables: pitch, channel, plus the clock variables. There
is no velocity here because note-off does not carry release
velocity in this version.
on release
after 200
play(pitch + 12, 50, 120)
end
end
pitch is the note that was released, which is the same note the
matching on note saw.
on tick
Fires globally, once per audio block. This is the tightest cadence a script can run at. The block length depends on the host's buffer size; a typical project hits this handler a few hundred times per second.
Bound variables: phase (0..1, the script clock's position within the
current beat), index (block counter since script load), time
(seconds since load), plus the clock variables.
Use on tick to shape modulation that the LFOs cannot, or to advance
a step sequencer that the arpeggiator does not cover.
on tick
# Asymmetric pulse: 70% high, 30% low.
if phase < 0.7
modulate("filt_cutoff", 2000)
else
modulate("filt_cutoff", -1000)
end
end
on tick is global. To do per-voice work on every block, work from
on note plus voice var plus after.
on beat
Fires globally on each musical beat boundary. The host's tempo and position drive the timing, so the handler is bar-locked across loops and bounces.
Bound variables: phase (0..1 within the beat at the moment the beat
fires; usually zero, but non-zero if a block crosses a beat boundary),
index (beat counter since load), plus the clock variables.
Use on beat for sequencers and step-counters. The handler is the
right home for play calls that should land on the grid.
on beat
# Two rhythms at once.
if index % 4 == 0
play(36, 110, 200)
end
if index % 6 == 0
play(43, 80, 150)
end
end
When the host is stopped, on beat does not fire. Beat counting is
tied to host transport.
on cc(N)
Fires globally when a MIDI CC with number N arrives on any channel. N
is part of the header: on cc(1) is the mod wheel, on cc(7) is
channel volume, on cc(74) is the filter-cutoff CC. A script can
have several on cc handlers, one per CC number.
Bound variables: value (0..1, the CC value normalised), channel,
plus the clock variables.
Use on cc to turn a CC into something the mod matrix cannot do:
fire a chord on a threshold, switch a discrete parameter, draw a
non-linear curve.
on cc(1)
if value > 0.5
play(60, 100, 200)
play(64, 100, 200)
play(67, 100, 200)
end
end
The CC also reaches the mod matrix in the normal way. The script handler runs in addition.
on pitchbend
Fires globally when the pitch wheel moves. Use it to draw a non-linear bend curve on top of the synth's built-in linear bend.
Bound variables: value (-1..+1, where 0 is centre), channel, plus
the clock variables.
on pitchbend
modulate("master_tune", value * value * value * 1200)
end
The synth's own pitch-bend handling still applies. The script's contribution adds on top, so a cube curve here gives a centre-soft, edge-aggressive bend without disabling the built-in one.
on aftertouch
Fires globally on channel aftertouch (the pressure value from many keyboards after a key is held). Polyphonic aftertouch is a separate modulation source in the mod matrix; this handler is the channel-pressure one.
Bound variables: value (0..1), channel, plus the clock variables.
on aftertouch
if value > 0.85
play(72, 60, 80)
end
end
Aftertouch is a modulator in the mod matrix; turning it into a note trigger or a discrete switch is script territory.
on transport
Fires globally on the edges of the host transport: when the host starts playing or stops. Use it to reset script state at the start of a section, or to log when the host moved.
Bound variables: playing (true on a start edge, false on a stop
edge), bpm.
on transport
if playing
note_count = 0
log("transport started")
end
end
on transport does not fire continuously while the host runs. It
fires at the edges only. For "every block while the host runs", use
on tick and read playing.