KODA Scripting Reference
Lists every function available to scripts along with code examples.
Introduction
This reference complements the KODA Scripting Manual. If you're new to KODA scripting, read the manual first. Then use this reference as a lookup when writing instrument behavior. Find the type you're working with in the sidebar, scroll to its function list, and copy what you need.
KODA is in pre-release and the scripting language might change between versions.
Conventions used in this reference
| Function | Returns | Description |
|---|---|---|
| functionName(argType argName) | ReturnType | A description of what the function does, with range or unit information for numeric arguments and midpoints, or defaults where relevant.TipOptional note about parameter-accepting overloads or related functions. |
Getting by name vs. by index
Most getter functions that fetch a part of the instrument — getArticulation, getVariation, getGroup, getMic, getBus, getParameter, and others — accept either a name (string) or an index (int). Both forms return the same kind of object.
- By name — More robust. If you reorder articulations, variations, or other parts of the instrument, scripts keep working because they reference the parts by what they're called, not where they sit.
- By index — Less typing, and natural inside
forloops where you'd iterate over every articulation, mic, etc. Index access skips the string lookup, but breaks if you reorder.
// By name — survives reordering
Articulation soft = getArticulation("Soft");
Mic close = getMic("Close");
event initialize() { setLogEnabled(true); } // Without this, debug.log() output is silent
// By index — concise in loops
for(int i = 0; i < getNumArticulations(); i++)
{
Articulation art = getArticulation(i);
debug.log(art.getName());
}
Note vs NoteData
Two related but distinct types describe a note at different points in its life:
NoteData— a template for a note before it plays. Carries pitch, velocity, envelope, timing, and routing. KODA hands one to yournoteOnhandler describing each incoming MIDI note. You can modify it freely, build new ones viacreateNoteData(), and trigger them withstart()orstartNote().Note— a handle to a note that's currently playing. You get one back fromstartNote(). Use it to modulate or stop the playing note: change its tuning, gain, pan, or trigger its release.
The typical flow is configure → start → modify:
event noteOn(NoteData note) // receive a NoteData template…
{
note.setAttack(0.05f); // pre-configure before it plays
Articulation art = getCurrentArticulation();
Note playing = art.startNote(note); // …trigger it, get back a Note handle…
playing.setGain(0.8f); // …modify the playing note
}
Once startNote() has fired, changes to the original NoteData have no effect on the playing note — they only describe the template. To affect a sounding note, always use its Note handle.
Two ways to call a function: with a dot or without
artic.startNote(note); can also be written as startNote(artic, note);
artic.setGain(0.5f); can also be written as setGain(artic, 0.5f);
Although written differently, they are identical.
Examples in this reference use dot syntax because it's the more common style in the Cmajor standard library and KODA codebase. If you prefer a procedural style — passing the object as the first argument — that form works everywhere too.
Using Parameter to skip using .getValue() (Overload Convenience)
Many setter functions that take a primitive (float, int, or bool) also accept a Parameter directly, saving you a .getValue() / .getIntValue() / .getBoolValue() call. The Parameter overload exists wherever you see a Tip line in a function's description.
// Both of these set the articulation's gain from a Parameter:
art.setGain(myGainParam.getValue()); // Explicit unwrap
art.setGain(myGainParam); // Parameter overload — same result, less typing
For non-setter contexts (arithmetic, comparisons, stream output, passing to functions without a Parameter overload) you'll always need an explicit .getValue():
// ✗ Won't compile
if(myParam > 0.5f)
console <- myParam;
// ✓ Works
if(myParam.getValue() > 0.5f)
console <- myParam.getValue();
Built-in Parameters
Every KODA instrument always carries these eight Parameters. They're created automatically — you don't need to add them in Figma — and you can react to them from a script with the standard event <ParameterName>(Parameter parameter) handler pattern without any extra UI setup. The default CC mappings follow the MIDI standard, and the user can remap any of them to a different CC in KODA's CONTROL tab.
All share a range of 0.0–1.0; default 0.0, except where noted.
| Parameter | Default CC | Description |
|---|---|---|
| Dynamics | CC 1 | Continuous expressive level — typically used to blend dynamic-layer Articulations or to control a Variation crossfader. Standard mod-wheel destination. |
| Vibrato | CC 2 | Vibrato depth or rate. Sometimes wired to a Modulator's LFO depth for scripted vibrato. |
| CurrentArticulation | — | Integer-valued index of the currently active Articulation. Practical range: 0 the number of Articulations in the instrument minus one; default 0. Controlled by articulation buttons in the UI or by script via setCurrentArticulation(...). Has no default CC mapping; the user can assign one if desired. |
| Expression | CC 11 | Continuous expression / volume-after-fader. |
| InstrumentVolume | CC 7 | Master output level. |
| SustainPedal | CC 64 | Sustain pedal state. Conventionally treated as a switch above 0.5. |
| SostenutoPedal | CC 66 | Sostenuto pedal state. Conventionally treated as a switch above 0.5. |
| PitchBend | Pitch-bend msg | Pitch-bend wheel position. Range −1.0 (full down) to 1.0 (full up); default 0.0 (centred). Driven by the dedicated MIDI pitch-bend message rather than a CC. (Now an intrinsic parameter — react with event PitchBend(Parameter parameter); the old pitchBend(float) event has been removed.) |
Default CCs are just that — defaults. If the user maps Dynamics to CC 11 in the CONTROL tab, the Parameter will respond to CC 11 instead of CC 1 going forward; nothing in your script needs to change. From a script perspective, you always address these by name — getParameter("Dynamics"), event Dynamics(Parameter parameter) — and the CC number is just an input-routing detail handled outside your code.
Built-in helpers
Generic utility functions that don't belong to any specific type. Always in scope.
| Function | Returns | Description |
|---|---|---|
| limit<Type>(Type value, Type min, Type max) | Type | Returns value clamped to the inclusive range [min, max]. Works with any numeric type. |
Cmajor patterns you'll see
A few Cmajor language conventions that aren't part of the KODA API but show up regularly in scripts.
Safe array indexing with wrap<N>
When you index a fixed-size array with a plain int, the Cmajor compiler rejects the access unless the index is guaranteed to be in bounds. The standard fix is to cast the index via wrap<N>, which wraps the value modulo N so it's always valid for an array of size N.
float[8] table = (0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.7f);
int i = someIndex;
float v = table[wrap<8>(i)]; // safe: i wraps into [0, 8)
You'll see this most often in event process() when stepping through a precomputed table — for example, looking up an entry in a vibrato LFO waveform.
Function calls on a returned value
Dot syntax (obj.foo()) is shorthand for the free function call foo(obj). When obj is the value returned by another call (a temporary), the Cmajor compiler can sometimes fail to resolve the dot-syntax call. The fix is to assign the temporary to a named variable first.
// ✗ May fail to compile
Note n = getArticulation("Straight").startNote(note);
// ✓ Assign first, then call
Articulation art = getArticulation("Straight");
Note n = art.startNote(note);
Instrument (Global)
This is your script's top-level entry point into the instrument. Everything you've built in the KODA app — the Articulations, Variations, Groups, Parameters, Modulators, Mics, and Buses — is reachable from here, alongside instrument-wide controls (volume, tuning, MIDI input filters) and host-supplied info (BPM, play state, time signature, sample rate, elapsed time).
When calling setInstrumentGainDecibels(-6.0f) or setInstrumentTuning(2.0f), you're setting the same values as the instrument-wide volume and tuning controls in the KODA app's GUI.
These are always in scope. Call them from anywhere in your script — no setup, no object to fetch.
When logging, you'll need to write debug.log(...) to avoid a name collision with the built-in math function for logarithms.
Logging is disabled by default. Enable it once in event initialize() so messages reach the debug console.
Example: Log notes only when the transport is playing
event initialize() { setLogEnabled(true); } // Without this, debug.log() output is silent
// Triggered every time the user plays a note
event noteOn(NoteData note)
{
// Skip if the host transport isn't running (no DAW playback)
if(getIsPlaying())
{
// PPQ = pulses per quarter note — the host's current playback position
debug.log("Note played at PPQ:", getppqPosition());
}
}
Logging
| Function | Returns | Description |
|---|---|---|
| setLogEnabled(bool enabled) | void | Enables or disables debug.log() output. Call once in event initialize() to enable. |
| debug.log(string message) | void | Logs a message to the debug console (requires setLogEnabled(true)). |
| debug.log<Type>(string message, Type value) | void | Logs a message followed by a value (any printable type) to the debug console. |
AVG Hierarchy Section
This is where the AVG (Articulation / Variation / Group) hierarchy you've built in the KODA app becomes reachable from your script. Use these to grab a handle to whichever Articulation, Variation, or Group you want to act on.
| Function | Returns | Description |
|---|---|---|
|
getArticulation(int index) getArticulation(string name) |
Articulation | Gets an Articulation by 0-based index or by name (names are defined in the instrument's structure). |
| getArticulations() | Articulation[] | Returns an array of all articulations in the instrument. |
| getNumArticulations() | int | Returns the total number of articulations in the instrument. |
| getCurrentArticulation() | Articulation | Returns the currently active articulation (controlled by the CurrentArticulation parameter). |
|
setCurrentArticulation(int index) setCurrentArticulation(string name) setCurrentArticulation(Articulation articulation) |
void | Sets the current articulation by 0-based index, name, or an existing Articulation object. |
Parameters & Modulators
The Parameters you've defined in Figma and the Modulators wired up in the KODA app's Modulators panel become reachable from your script here. Use these to grab a handle to the Parameter or Modulator you want to read, set, or react to.
| Function | Returns | Description |
|---|---|---|
| getParameter(string name) | Parameter | Gets a Parameter by name. The name matches what was defined in Figma. |
| getParameters() | Parameter[] | Array of every Parameter currently registered in the engine; iterate to scan them all. |
|
getModulator(int index) getModulator(string name) |
Modulator | Gets a Modulator by 0-based index or by name. |
Mics
The mic mixer you've configured in the KODA app becomes reachable from your script here. Use these to grab a handle to a specific Mic so you can adjust its gain, pan, solo, or mute from your script.
| Function | Returns | Description |
|---|---|---|
|
getMic(int index) getMic(string name) |
Mic | Gets a Mic by 0-based index or by name. |
Convolution IRs
The impulse-response .wav files in the library's IR Samples folder — the same list the Convolution effect shows in its own dropdown — are reachable from your script here. Use these to autopopulate a front-end menu of IRs: the index matches Convolution::File, so a menu item's value can route straight to setInsertParameter(slot, Convolution::File, index).
| Function | Returns | Description |
|---|---|---|
| getNumConvolutionFiles() | int | Returns the number of IR .wav files found in the library's IR Samples folder (0 if the folder is absent or empty). Useful for for-loops that build a menu of every available IR. |
| getConvolutionFileName(int index) | string | Returns the display name (extension stripped) of the IR at the given 0-based index (0 … getNumConvolutionFiles() - 1), in the same order as the Convolution effect's dropdown. Returns "" if the index is out of range. |
Example: Autopopulate a menu with the library's IRs
// Build a front-end dropdown from the library's "IR Samples" folder.
Component createConvolutionMenu(string name)
{
Component menu = getComponent(name);
menu.clearItems();
menu.addItem("None", -1); // -1 clears the IR (pass-through)
for (int i = 0; i < getNumConvolutionFiles(); i++)
menu.addItem(getConvolutionFileName(i), i);
menu.setMenuSelection(-1); // start on "None"
return menu;
}
// The menu value maps directly to the Convolution insert's File parameter.
// (assumes a Convolution insert at slot 0 of the master bus)
event mnuConvolution(Parameter parameter)
{
Bus master = getMasterBus();
master.setInsertParameter(0, Convolution::File, parameter.getValue());
}
Instrument output
The instrument-wide volume and tuning controls in the KODA app are exposed here. Use these to adjust the global gain or tuning from a script.
| Function | Returns | Description |
|---|---|---|
| setInstrumentGain(float gain) | void | Sets the global instrument gain (linear). Range: 0.0 (silence) to 4.0 (~+12 dB); 1.0 is unity. |
| setInstrumentGainDecibels(float gainDecibels) | void | Sets the global instrument gain in decibels. Effective range: -inf to ~+12 dB (clamped to 0.0–4.0 linear); 0 dB is unity. |
| setInstrumentTuning(float semitones) | void | Sets the global instrument tuning in semitones. 0.0 = no change. No hard clamp; typical range -24 to +24. |
MIDI input control
Filter or block specific kinds of incoming MIDI messages at the instrument level.
| Function | Returns | Description |
|---|---|---|
| setIgnorePitchBend(bool ignore) | void | Sets whether to ignore incoming pitch-bend messages. true = ignore, false = process normally. |
| setIgnoreSustainPedal(bool ignore) | void | Sets whether to ignore sustain pedal messages. true = ignore, false = process normally. |
| setIgnoreKeyRange(bool ignore) | void | Sets whether to ignore the playable key range filter. true = play notes outside the playable range; false = filter them out. |
| setIgnoreNoteOns(bool ignoreNoteOns) | void | Sets whether the instrument ignores incoming note-on events. |
| setIgnoreNoteOffs(bool ignoreNoteOffs) | void | Sets whether the instrument ignores incoming note-off events. |
| setIgnoreMidiNotes(bool ignore) | void | Sets whether the instrument ignores both note-on and note-off events. Shortcut for calling setIgnoreNoteOns + setIgnoreNoteOffs together. |
| setNoteOffCancelsDelayedNotes(bool cancel) | void | Sets whether a note-off cancels notes that are still in their delay phase (haven't started yet). |
When your script routes notes itself — picking an articulation per note, applying script-side modulation, or generating notes programmatically — call setIgnoreNoteOns(true) in event initialize(). Without it, every incoming MIDI note triggers twice: once through the instrument's default routing and again through your startNote() call.
setIgnoreNoteOffs works the same way for releases. Most scripted instruments want both — setIgnoreMidiNotes(true) turns them on together in one call.
Instrument runtime state
Read-only queries about what your instrument is doing right now.
| Function | Returns | Description |
|---|---|---|
| getNumActiveNotes() | int | Returns the number of currently active notes (across all articulations). |
| isNoteHeld(int noteNumber) | bool | Returns true if the given MIDI note number is currently held (key down). Tracks host MIDI only — script-triggered notes are not counted. |
| getHeldNotes() | HeldNotes | Returns the current set of held MIDI notes as a HeldNotes value. Tracks host MIDI only — script-triggered notes are not counted. |
Transport state
Read-only queries about the host's current transport state and audio configuration. Note: although you access this from the instrument, the data is being passed in from the host DAW.
| Function | Returns | Description |
|---|---|---|
| getBPM() | float64 | Returns the host transport's current BPM (beats per minute). |
| getppqPosition() | float64 | Returns the host transport's current position in PPQ (pulses per quarter note). |
| getTimeSignature() | TimeSignature | Returns the host transport's current time signature as a TimeSignature struct (numerator + denominator). |
| getTimeSignatureNumerator() | float64 | Returns the time signature numerator only (e.g. 4.0 in 4/4 time). |
| getIsPlaying() | bool | Returns true if the host transport is currently playing. |
| getIsRecording() | bool | Returns true if the host transport is currently recording. Not every DAW reports this — hosts that don't will return false even while recording. |
| getIsStandalone() | bool | Returns true if KODA is running as a standalone application (not loaded as a plugin in a DAW). |
| getBlockSize() | int | Returns the audio processing block size for the current block, in samples. Note: this can vary block-to-block depending on the host; don't assume it's constant. |
| getSampleRate() | float64 | Returns the internal sample rate in Hz (e.g. 48000.0). |
System & environment
Read-only info about the host environment and machine.
| Function | Returns | Description |
|---|---|---|
| getIsRealTime() | bool | True in a real-time audio context; false during offline/bounce render. |
| getWrapperType() | string | Plugin format/wrapper (e.g. "Standalone", "AU", "VST3"). |
| getHostType() | string | Host/DAW name. |
| getOperatingSystem() | string | OS name. |
| getCpuArchitecture() | string | CPU architecture (e.g. "arm64", "x86_64"). |
| getCoreCount() | int | Logical CPU cores. |
| getPhysicalCoreCount() | int | Physical CPU cores. |
| getMemorySizeMB() | int | Total system RAM in MB. |
| getUserLanguage() | string | User's language. |
| getUserRegion() | string | User's region. |
| getBlockTimeSeconds() | float64 | Duration of one audio block in seconds (block size ÷ sample rate). |
Time elapsed & beats
Time since the script started running, and helpers for sample-counts until the next beat or bar. Reach for the samples version for sample-accurate scheduling (it combines naturally with getBeat(), getNextBeat(), and getBlockSize()); reach for the seconds version for logging or human-readable duration checks.
| Function | Returns | Description |
|---|---|---|
| getTimeElapsedSamples() | int64 | Returns the total time elapsed since the script started, in samples. |
| getTimeElapsedSeconds() | float64 | Returns the total time elapsed since the script started, in seconds. |
| getBeat() | float64 | Returns the length of one beat in samples (at the current host BPM and sample rate). |
| getNextBeat() | int | Returns the number of samples remaining until the next beat boundary. |
| getNextBar() | int | Returns the number of samples remaining until the next bar boundary. |
Note length helpers
Length of musical note durations in samples, based on the host BPM (and time signature for bars). Useful for scheduling delays and lengths in rhythmic units.
| Function | Returns | Description |
|---|---|---|
| getBar() | float64 | Length of one bar in samples (depends on time signature and BPM). |
| getWholeNote() | float64 | Length of a whole note in samples. |
| getHalfNote() | float64 | Length of a half note in samples. |
| getHalfNoteTriplet() | float64 | Length of a half-note triplet in samples. |
| getQuarterNote() | float64 | Length of a quarter note in samples. |
| getQuarterNoteTriplet() | float64 | Length of a quarter-note triplet in samples. |
| get8thNote() | float64 | Length of an 8th note in samples. |
| get8thNoteTriplet() | float64 | Length of an 8th-note triplet in samples. |
| get16thNote() | float64 | Length of a 16th note in samples. |
| get16thNoteTriplet() | float64 | Length of a 16th-note triplet in samples. |
| get32ndNote() | float64 | Length of a 32nd note in samples. |
| get32ndNoteTriplet() | float64 | Length of a 32nd-note triplet in samples. |
Example: Enable logging on initialize
// Runs once when the script first compiles
event initialize()
{
// Logging is disabled by default — enable it for the rest of the session
setLogEnabled(true);
// Print useful host info to the debug console
debug.log("Sample rate:", getSampleRate());
debug.log("BPM:", getBPM());
}
Parameter
The Parameters you've created in Figma — knobs, sliders, buttons, etc. — become reachable from your script here. Use these to grab a handle to whichever Parameter you want to read, set, or react to.
- Access a parameter inside its specific callback:
event <ParameterName>(Parameter parameter) - Or a generic callback that fires when any parameter changes:
event parameterUpdated(Parameter parameter) - Or, for use outside the callback, create a global Parameter via
getParameter("Name").
Parameter p_gain = getParameter("Gain");
float currentGain = p_gain.getValue();
Reading values
When two function names are stacked in a single row, they're equivalent — they call the same underlying code. Pick whichever name reads more naturally in your script.
| Function | Returns | Description |
|---|---|---|
|
getValue() get() |
float | Returns the Parameter's current value as a float. |
|
getIntValue() toInt() |
int | Returns the Parameter's current value rounded to the nearest integer. |
|
getBoolValue() isTrue() |
bool | Returns true if the Parameter's current value is greater than 0.5, otherwise false. |
| getValueNormalized() | float | Returns the Parameter's current value normalized to the 0.0–1.0 range, based on its min and max. |
| getValueInverted(float& value) | void | Writes the inverted-normalized value (max − normalized × range) into the supplied reference. |
Range & identity
| Function | Returns | Description |
|---|---|---|
| getId() | Identifier | Returns the Parameter's unique identifier. Use to compare against another Parameter's ID (e.g. inside event parameterUpdated to identify which Parameter changed — though the named-event form event <ParameterName>(...) usually avoids the need for this check). |
| getName() | string | Returns the Parameter's name as defined in Figma. Returns "Invalid Parameter" if the ID is out of range. |
| getMinValue() | float | Returns the Parameter's minimum value as set in Figma. Returns 0.0 if the ID is invalid. |
| getMaxValue() | float | Returns the Parameter's maximum value as set in Figma. Returns 1.0 if the ID is invalid. |
| getDefaultValue() | float | Returns the Parameter's default value as set in Figma. Returns 0.0 if the ID is invalid. |
| getRange() | float | Returns the Parameter's range (max − min). |
| idIsValid() | bool | Returns true if the Parameter's ID falls within the valid range. |
| isEqualTo(Parameter other) | bool | Returns true if this Parameter has the same ID as other. Useful inside event parameterUpdated when you need to dispatch on which Parameter changed. |
Setting values
| Function | Returns | Description |
|---|---|---|
|
setValue(float newValue) setValue(int newValue) setValue(bool newValue) |
void | Sets the Parameter's value, clamped to its min/max range. Bool true = 1.0, false = 0.0.NoteCalling setValue() from within the Parameter's own parameterUpdated handler is not allowed (logs a warning and is ignored). |
Reacting to parameter changes
To run code when a Parameter's value changes, write a named event handler — the handler's name matches the Parameter's name (the string passed to getParameter). KODA dispatches each change to its own handler automatically; there's no central dispatch function to write.
The event runs once per change, with the new value already on the parameter. Read it with parameter.getValue() / parameter.getIntValue() / parameter.getBoolValue(), or pass the parameter directly to any setter that accepts a Parameter overload (see Parameter Convenience).
// Fires whenever InstrumentVolume changes (built-in, MIDI CC 7 by default)
event InstrumentVolume(Parameter parameter)
{
// Push the new value into the current articulation's gain
Articulation art = getCurrentArticulation();
art.setGain(parameter); // Parameter overload — see Articulation reference
}
Articulation, Variation & Group
Every KODA instrument's samples are organised into a three-level container hierarchy:
- Articulation — the top-level grouping, typically a name for a playing style ("Sustain", "Staccato", "Pizzicato"). Switching articulations via the keyswitch UI changes which Articulation plays, as well as the elements contained within.
- Variation — a mid-level container inside an Articulation. Used for things like dynamic layers (pp / mf / ff), vibrato vs. non-vibrato, or alternate takes.
- Group — the bottom-level container inside a Variation. Where the actual sample mappings live. The level you'd reach to manipulate the insert FX chain attached to those samples.
All three containers share most of the same functions — identity / state, gain / pan, tuning / transposition, sample loading, note triggering, and per-container parameters — so they're documented together below. Three subsections at the end cover the per-type extras: navigating a parent's children (Articulation, Variation) and the insert FX chain (Group).
Get a handle via getCurrentArticulation(), getArticulation(), articulation.getVariation(), variation.getGroup(), or articulation.getGroup() (which jumps from Articulation directly to Group, using whichever Variation is currently active).
Example: Log which articulation handled each note
event initialize() { setLogEnabled(true); } // Without this, debug.log() output is silent
// Fires every time a note is played
event noteOn(NoteData note)
{
// Reach into the currently-active articulation and log its name
Articulation art = getCurrentArticulation();
debug.log("Note routed to articulation:", art.getName());
}
Example: Mute the current articulation when the host stops
// Fires whenever the host transport stops
event transportStopped()
{
// Reach into the currently-active Articulation
Articulation art = getCurrentArticulation();
// Silence it and stop any tails still playing
art.setGain(0.0f);
art.stopAllNotes();
}
Most setter functions on Articulation, Variation, and Group (gain, pan, tuning variants, transposition, active child, insert parameters) accept either a primitive value or a Parameter directly. See the Parameter convenience at the top of this reference.
Identity & state
| Function | Returns | Description |
|---|---|---|
| getName() | string | Returns the container's name as defined in the instrument (e.g. "Sustain"). |
| getIndex() | int | Returns the container's 0-based index within its parent. (Articulation's parent is the instrument itself.) |
| isIdValid() | bool | Returns true if this handle refers to a real container in the instrument. Use as a null check before calling functions on the handle. |
| validate() | void | Logs whether this handle is valid (and its name if so) to the console. Useful while debugging name lookups. |
| getTypeName() | string | Returns the type name as a string ("Articulation", "Variation", or "Group"). Useful for generic logging. |
Gain & pan
| Function | Returns | Description |
|---|---|---|
| setGain(float gain) | void | Sets gain (linear). Range: 0.0 (silence) to 4.0 (~+12 dB); 1.0 is unity. |
| setVolume(float decibels) | void | Sets volume in decibels. Effective range: -inf to ~+12 dB (clamped to 0.0–4.0 linear); 0 dB is unity. |
| setPan(float pan) | void | Sets pan position. Range: -1.0 (full left) to 1.0 (full right); 0.0 is center. Clamped. |
| getGain() | float | Returns the current gain. Range: 0.0–4.0. |
| getPan() | float | Returns the current pan position. Range: -1.0–1.0. |
Tuning & transposition
Tuning shifts the pitch of a sample. Transposition offsets the position of the note to trigger a different sample.
| Function | Returns | Description |
|---|---|---|
| setTuningSemitones(float semitones) | void | Sets tuning in semitones. 0.0 = no change. No hard clamp; typical range -24 to +24. |
| setTuningCents(float cents) | void | Sets tuning in cents. 100 cents = 1 semitone; 0.0 = no change. |
| setTuningFactor(float speedRatio) | void | Sets tuning as a speed/frequency ratio. 1.0 = no change; 2.0 = one octave up; 0.5 = one octave down. |
| setTransposition(int transposition) | void | Sets transposition in semitones by changing the note number played (not by pitch-shifting). 0 = no change. |
| getTuningSemitones() | float | Returns the current tuning in semitones. 0.0 = no change. |
| getTransposition() | float | Returns the current transposition in semitones. 0.0 = no change. |
Sample loading/purging
Load or purge this container's samples from memory. Useful for templating instruments that hold many articulations but only need a few in memory at once.
| Function | Returns | Description |
|---|---|---|
| loadSamples() | void | Loads this container's samples into memory if they aren't already. |
| purgeSamples() | void | Unloads this container's samples from memory (frees RAM). Re-trigger loadSamples() before playing again. |
Keyboard highlight
Overlay this container's playable range — the exact set of MIDI notes covered by every zone beneath it (across all child Variations and Groups) — on the GUI piano, tinted with a chosen Colour. Handy for showing the user which keys an Articulation, Variation, or Group responds to, e.g. when a menu switches the active Variation.
| Function | Returns | Description |
|---|---|---|
| showPlayableRange(Colour colour) | void | Highlights every note this container covers on the GUI piano in colour. Several containers can be highlighted at once — each keeps its own colour, and when two overlap on a note the most recently shown one wins, so call order acts as draw order (show a small, informative range last to keep it on top of full-keyboard layers). Remove with hidePlayableRange() or clearPlayableRanges(). |
| hidePlayableRange() | void | Removes this container's playable-range highlight from the GUI piano. No-op if it isn't currently shown. |
Playing notes
Trigger and stop notes scoped to this container. Notes started on an Articulation flow through its active Variation (and that Variation's active Group); notes started on a Variation flow through its active Group; notes started on a Group play that Group's samples directly.
| Function | Returns | Description |
|---|---|---|
| createNoteData() | NoteData | Returns a fresh NoteData struct with this container's indices pre-filled — ready to be customized (velocity, note number, envelope, etc.) and passed to startNote(). |
|
startNote(NoteData note) startNote(int note, float velocity) |
Note | Starts a note on this container and returns a Note handle you can use to modify or stop the note while it plays. The simple form takes a MIDI note number (0–127) and velocity (0.0–1.0); the NoteData form lets you pre-configure envelope, gain, pan, and routing first. |
| stopNote(NoteData note) | Note | Stops the matching note (sends it into its release envelope). |
| stopAllNotes() | void | Stops every active note belonging to this container. |
Parameters & properties
Each container can carry its own set of named parameters (numeric values) and properties (typed values) defined alongside it in the instrument. Use these to attach per-container settings without polluting the global parameter list.
| Function | Returns | Description |
|---|---|---|
| getParameterValue(string name) | float | Returns the container's named parameter value as a float. Returns 0.0 if not found. |
| getParameterIntValue(string name) | int | Returns the container's named parameter as an integer (cast from float). |
| getParameterBoolValue(string name) | bool | Returns the container's named parameter as a bool (true if value > 0.5). |
| getProperty(string name) | string | Returns the container's named property as a string. Returns "" if not found. |
| getIntProperty(string name) | int | Returns the container's named property as an integer. |
| getFloatProperty(string name) | float | Returns the container's named property as a float. |
| getBoolProperty(string name) | bool | Returns the container's named property as a bool. |
Variations & active variation Articulation only
Access an Articulation's child Variations, and control which Variation new notes route to.
| Function | Returns | Description |
|---|---|---|
|
getVariation(int index) getVariation(string name) |
Variation | Gets a child Variation by 0-based index or by name. |
| getNumVariations() | int | Returns the number of Variations under this Articulation. Useful for for-loops that iterate over every Variation. |
| getGroup(int index) | Group | Gets a Group by index from the Articulation's currently-active Variation (skips one level of the hierarchy). |
|
setActiveVariation(int index) setActiveVariation(string name) |
void | Sets the active Variation by 0-based index or by name. Subsequent notes routed through this Articulation will play from the chosen Variation. |
| setAllActive() | void | Makes all Variations active simultaneously (notes will layer through every Variation). |
| getActiveVariation() | int | Returns the index of the currently-active Variation. Returns -1 if all are active. |
Example: Crossfade between vibrato and non-vibrato
// Fires whenever Vibrato changes (built-in, MIDI CC 2 by default)
// (assumes the current articulation has Variations named "NoVibrato" and "Vibrato")
event Vibrato(Parameter parameter)
{
Articulation art = getCurrentArticulation();
Variation noVib = art.getVariation("NoVibrato");
Variation vib = art.getVariation("Vibrato");
float blend = parameter.getValue();
// 0.0 = full NoVibrato, 1.0 = full Vibrato, in-between = blended
noVib.setGain(1.0f - blend);
vib.setGain(blend);
}
Example: Trigger a release-tail Variation when a note is released
// Fires when the user lifts a key
// (assumes the current articulation has a Variation named "ReleaseTail")
event noteOff(NoteData note)
{
Articulation art = getCurrentArticulation();
Variation tail = art.getVariation("ReleaseTail");
// Trigger the tail sample through the ReleaseTail Variation directly,
// bypassing the Articulation's active-Variation routing
tail.startNote(note);
}
Groups & active group Variation only
Access a Variation's child Groups, and control which Group new notes route to.
| Function | Returns | Description |
|---|---|---|
|
getGroup(int index) getGroup(string name) |
Group | Gets a child Group by 0-based index or by name. |
| getNumGroups() | int | Returns the number of Groups under this Variation. Useful for for-loops that iterate over every Group. |
|
setActiveGroup(int index) setActiveGroup(string name) |
void | Sets the active Group by 0-based index or by name. Subsequent notes routed through this Variation will play from the chosen Group. |
| setAllActive() | void | Makes all Groups active simultaneously (notes will layer through every Group). |
| getActiveGroup() | int | Returns the index of the currently-active Group. Returns -1 if all are active. |
Insert FX Group only
Each Group can host a chain of insert FX (compressor, EQ, reverb, etc.) defined in the instrument. These functions let you modulate individual insert parameters or bypass entire inserts at runtime.
| Function | Returns | Description |
|---|---|---|
| setInsertParameter(int insertIndex, int paramIndex, value) | void | Sets a parameter on an insert FX. insertIndex: 0-based insert slot. paramIndex: 0-based parameter on that insert. value: float, int, or bool depending on the parameter type. |
| setInsertBypassed(int insertIndex, bool bypassed) | void | Bypasses (or un-bypasses) an entire insert FX slot. true = bypassed, false = active.TipIf you're wiring this to an "Enabled" button (where true means the user wants the effect on), invert the value: setInsertBypassed(0, !param.getBoolValue()). |
Example: Darken soft notes with a velocity-driven filter cutoff
// Fires every time a note is played
event noteOn(NoteData note)
{
Articulation art = getCurrentArticulation();
Group group = art.getGroup(0);
// Lower-velocity notes get a lower cutoff (darker tone, mimicking a softer note)
// (assumes insert slot 0 is a filter with cutoff frequency as parameter 1)
float cutoffHz = 200.0f + note.getVelocity() * 19800.0f;
group.setInsertParameter(0, 1, cutoffHz);
}
Example: Bypass a reverb with a UI button
// Fires when SustainPedal changes (built-in, MIDI CC 64 by default).
// Repurposed here as a reverb-bypass toggle for portable testing.
// (assumes a reverb at insert slot 1 of the current articulation's group)
event SustainPedal(Parameter parameter)
{
Articulation art = getCurrentArticulation();
Group group = art.getGroup(0);
// Pedal pressed = bypass on, pedal released = bypass off
group.setInsertBypassed(1, parameter.getBoolValue());
}
Articulation, Variation, and Group all share the same underlying state representation internally — which is why their gain / pan / tuning functions look identical.
Group is the leaf of the AVG hierarchy as exposed via these functions. At the engine level, Groups contain Zones (one per note/velocity region), and Zones contain Voices — alternate samples used for round-robin playback. Zone selection and round-robin cycling are normally handled by the engine automatically. Round robins can be manually overridden per-note via NoteData.forceRoundRobinIndex(); Zones themselves aren't reachable from scripts.
NoteData
A NoteData describes a note before it plays — its pitch, velocity, envelope, timing, and which articulation / variation / group / mic / bus it routes through. KODA hands one to your noteOn and noteOff handlers describing each incoming MIDI note, and you can also build your own by calling createNoteData() on an Articulation, Variation, or Group.
Once a NoteData is configured, trigger it with noteData.start() to play it through its current routing (see Triggering below). A NoteData is just a value — changing it after the note has started has no effect on the playing note (use the returned Note handle for that).
NoteData has setters for most of its fields, but several don't yet have matching getters. If you can't find a getter for a value you've set, the function is on the to-do list. For values you set yourself, the workaround is to keep a copy in module-level state.
Example: Add an octave-up echo to every note
event noteOn(NoteData incoming)
{
Articulation art = getCurrentArticulation();
// Build a copy of the incoming note, an octave higher, half as loud,
// delayed by 200 ms — KODA still triggers the original note normally
NoteData echo = art.createNoteData();
echo.setNoteNumber(incoming.getNoteNumber() + 12);
echo.setVelocity(incoming.getVelocity() * 0.5f);
art.startNote(echo);
}
Pitch & velocity
Set or read the note's pitch (note number, tuning offsets, transposition) and velocity. Velocity has two unit conventions exposed as separate function pairs — float (0.0–1.0) and MIDI integer (0–127) — that read and write the same underlying value.
| Function | Returns | Description |
|---|---|---|
| setNoteNumber(int note) | void | Sets the MIDI note number. Range: 0–127 (clamped). |
| getNoteNumber() | int | Returns the MIDI note number (before transposition). |
| getNoteNumberWithTransposition() | int | Returns the effective MIDI note that will be played (note number + transposition). |
| getNoteName() | string | Returns the note name as a string (e.g. "C4", "F#3"). |
| transpose(int interval) | void | Sets transposition to interval semitones (replaces, doesn't accumulate). |
| transposeToNote(int note) | void | Sets transposition so the note plays as note (e.g. transpose every incoming note to C4). |
| getTransposition() | int | Returns the current transposition in semitones. |
| setVelocity(float velocity) | void | Sets velocity as a float. Range: 0.0–1.0 (clamped). |
| setMidiVelocity(int velocity) | void | Sets velocity from a MIDI value. Range: 0–127. |
| getVelocity() | float | Returns velocity as a float (0.0–1.0). |
| getMidiVelocity() | int | Returns velocity as a MIDI integer (0–127). |
| setTuningSemitones(float semitones) | void | Sets fine-tuning in semitones (continuous, not quantised to MIDI notes). |
| setTuningCents(float cents) | void | Sets fine-tuning in cents. 100 cents = 1 semitone. |
| setTuningRatio(float ratio) | void | Sets tuning as a frequency ratio (2.0 = one octave up, 0.5 = one octave down). |
| setPitchShift(float semitones) | void | Shifts the note's pitch without changing its playback speed or sample timbre, using the time-stretch engine. 0.0 = no shift. Distinct from setTuning*, which retunes by changing the sample's playback rate (faster/slower, with a shifted timbre). |
Gain & pan
| Function | Returns | Description |
|---|---|---|
| setGain(float gain) | void | Sets gain (linear). Range: 0.0 (silence) to 4.0 (~+12 dB); 1.0 is unity. Clamped. |
| setVolume(float decibels) | void | Sets volume in decibels. Effective range: -inf to ~+12 dB (clamped to linear 0.0–4.0); 0 dB is unity. |
| getGainDecibels() | float | Returns the current gain in decibels. |
| setPan(float pan) | void | Sets pan position. Range: -1.0 (full left) to 1.0 (full right); 0.0 is center. Clamped. |
Delay, length & stretch
Control where in the sample to start playback (sample start), how long to delay before triggering, and how time-stretched it is.
| Function | Returns | Description |
|---|---|---|
| setDelaySamples(int delaySamples) | void | Sets the delay before the note starts, in audio samples. |
| setSampleStart(float sampleStartMS) | void | Sets the starting offset within the source sample, in milliseconds. 0 = play from the beginning. Milliseconds are used rather than samples because the triggered voice may run at a different sample rate than the engine. |
| setSampleStartMS(float sampleStartMS) | void | Alias of setSampleStart — sets the starting offset within the source sample, in milliseconds. |
| setStretchRatioByLength(float stretchRatio) | void | Sets time-stretch as a length ratio. 1.0 = no change; 2.0 = twice as long. |
| setStretchRatioBySpeed(float speedRatio) | void | Sets time-stretch as a speed ratio. 1.0 = no change; 2.0 = twice as fast (half as long). |
| getTimeElapsedSeconds() | float | For notes already playing, returns the time elapsed since the note started, in seconds. |
Envelope
Override the note's envelope stages individually, or apply a whole envelope at once via setEnvelope(). Curve exponents take values in the range 0.5–2.0: 0.5 = convex (fast start, slow finish), 1.0 = linear, 2.0 = concave (slow start, fast finish).
| Function | Returns | Description |
|---|---|---|
| setPreDelay(float preDelay) | void | Sets pre-delay before the envelope begins, in seconds. |
| setAttack(float attack) | void | Sets attack time in seconds. 0.0 = instant. |
| setAttack(float attack, float curve) | void | Sets attack time and curve exponent together. |
| setHold(float hold) | void | Sets hold time in seconds (after attack, before decay begins). |
| setDecay(float decay) | void | Sets decay time in seconds. |
| setDecay(float decay, float curve) | void | Sets decay time and curve exponent together. |
| setSustain(float sustain) | void | Sets sustain level. Range: 0.0 (silence) to 1.0 (full level). Clamped. |
| setRelease(float release) | void | Sets release time in seconds. |
| setRelease(float release, float curve) | void | Sets release time and curve exponent together. |
| setEnvelope(Envelope envelope) | void | Applies an entire envelope (all stages and curves) at once. |
| scheduleRelease(float timeToRelease, float releaseTime, float releaseCurve) | void | Schedules an automatic release a fixed time after the note starts. Useful for one-shot triggers that should release themselves without an explicit noteOff. |
Behavior flags
| Function | Returns | Description |
|---|---|---|
| setOneShot(bool oneShot) | void | Toggles one-shot mode. true = note plays through to completion regardless of MIDI note-off. false = note respects note-off. |
| forceRoundRobinIndex(int rr) | void | Overrides automatic round-robin cycling and plays the specified round-robin index for this note. rr: 0-based index into the available round robins. |
Routing
Direct the note through a specific articulation, variation, group, mic, or bus. The setTarget(...) overloads are the most ergonomic — they set the relevant indices in one call. For low-level control, use the individual index setters; indices of -1 mean "no override — use the engine's default routing".
| Function | Returns | Description |
|---|---|---|
|
setTarget(Articulation articulation) setTarget(Variation variation) setTarget(Group group) |
void | Routes the NoteData through the given Articulation, Variation, or Group. Indices below the target's hierarchy level are cleared (e.g. passing a Variation sets the articulation and variation indices but clears the group index). |
| setArticulationIndex(int index) | void | Low-level: routes through the Articulation at this 0-based index. |
| setVariationIndex(int index) | void | Low-level: routes through the Variation at this 0-based index. |
| setGroupIndex(int index) | void | Low-level: routes through the Group at this 0-based index. |
| setMicIndex(int index) | void | Forces the note through a specific Mic (by 0-based index). |
| setBusIndex(int index) | void | Routes the note's output to a specific Bus (by 0-based index). |
| setLayerIndex(int index) | void | Sets the velocity layer index used when looking up the sample. |
| getGroupType() | GroupType | Returns the most specific routing level set on this NoteData: GroupType::Group, ::Variation, ::Articulation, or ::Invalid if no routing is set. |
| getArticulation() | Articulation | Returns the Articulation this NoteData routes through. |
| getVariation() | Variation | Returns the Variation this NoteData routes through. |
| getGroup() | Group | Returns the Group this NoteData routes through. |
Triggering
Convert a configured NoteData into a playing note. The note plays through the routing set on the NoteData (via setTarget(...) or inherited from the incoming noteOn event).
| Function | Returns | Description |
|---|---|---|
|
startNote() start() |
Note | Triggers this NoteData and returns a Note handle you can use to modify or stop the note while it plays. |
Example: Make soft notes longer and breathier
event initialize()
{
// Disable default triggering so we can customise each note ourselves
setIgnoreNoteOns(true);
}
event noteOn(NoteData note)
{
if(note.getVelocity() < 0.4f)
{
// Slow swell, gentle release for soft notes (curve 2.0 = slow start / fast end)
note.setAttack(0.5f, 2.0f);
note.setRelease(2.0f, 2.0f);
}
// Use the routing already on the incoming note — no need to fetch the articulation
note.start();
}
Note
A Note is a handle to a currently-playing note. You get one back from startNote() on an Articulation, Variation, or Group — and you use it to modify or stop that specific note while it's still sounding.
Every Note function returns a bool: true if the note was still active and the change was applied, false if the note had already finished. That return value lets you safely call functions on a Note without first checking isActive().
Example: Apply a longer release to every incoming note
event initialize()
{
// Suppress KODA's default note triggering so we can start notes ourselves
setIgnoreNoteOns(true);
}
event noteOn(NoteData note)
{
Articulation art = getCurrentArticulation();
Note playing = art.startNote(note);
// Stretch the release out to 2 seconds with a slightly concave curve (slow start)
playing.setRelease(2.0f, 1.5f);
}
Lifecycle
| Function | Returns | Description |
|---|---|---|
| isActive() | bool | Returns true if the note is still playing (not yet released to silence). |
| stop() | bool | Triggers the note's release stage using its currently-configured envelope. Returns true if the note was active when stopped. |
| getTypeName() | string | Returns the type name as a string ("Note"). Useful for generic logging. |
To fade an active note out with a custom timing — for legato slurs, voice stealing, or any "active fade-out from script" — pair setRelease(lengthSeconds, curveExponent) with stop(). The first call overrides the envelope; the second triggers the fade using those new settings.
Calling stop() alone uses whatever release was configured when the note started. Calling setRelease() alone changes what will happen when the note is later released (e.g. when the user lifts the key), but doesn't itself produce sound.
Per-note properties
Adjust gain, tuning, length, time-stretch, pan, and release on a single note while it's playing. Each function returns true if the change landed (the note was still active), false if the note had already finished.
These setters overwrite the property's current value rather than adding to it. Each call replaces what the previous call set, so if two modulators want to influence the same property in the same audio block — e.g. a legato pitch glide running alongside a vibrato LFO — sum their contributions in script and apply the total with a single call.
| Function | Returns | Description |
|---|---|---|
| setGain(float gain) | bool | Sets the note's gain (linear). Range: 0.0 (silence) to 4.0 (~+12 dB); 1.0 is unity. |
| setVolume(float decibels) | bool | Sets the note's volume in decibels. Effective range: -inf to ~+12 dB. |
| setTuningSemitones(float semitones) | bool | Sets the note's tuning offset in semitones. 0.0 = no change. |
| setTuningCents(float cents) | bool | Sets the note's tuning offset in cents. 100 cents = 1 semitone. |
| setTuningRatio(float ratio) | bool | Sets tuning as a frequency ratio. 1.0 = no change; 2.0 = one octave up. |
| setPitchShift(float semitones) | bool | Shifts the playing note's pitch without changing its playback speed or sample timbre, using the time-stretch engine. 0.0 = no shift. Distinct from setTuning*, which retunes by changing the sample's playback rate. |
| setLength(float lengthSeconds) | bool | Sets the total length of the note in seconds. No hard clamp. |
| setStretch(float stretch) | bool | Sets the time-stretch factor. 1.0 = no change; > 1.0 = slower playback; < 1.0 = faster playback. |
| setStretchRatioByLength(float stretchRatio) | bool | Sets time-stretch as a length ratio (2.0 = twice as long). |
| setStretchRatioBySpeed(float speedRatio) | bool | Sets time-stretch as a speed ratio (2.0 = twice as fast). |
| setPan(float pan) | bool | Sets the note's pan position. Range: -1.0 (full left) to 1.0 (full right); 0.0 is center. |
| setRelease(float lengthSeconds, float curveExponent) | bool | Configures the note's release envelope. lengthSeconds: release time (0.0 = instant). curveExponent: range 0.5–2.0; 0.5 = convex, 1.0 = linear, 2.0 = concave.NoteThis only updates the envelope settings — it does not trigger the release. The new settings take effect when the note is released, either by the user lifting the key or by an explicit stop() call. |
Insert FX
| Function | Returns | Description |
|---|---|---|
| setParameter(int insertIndex, int paramIndex, float value) | bool | Sets a parameter on the note's insert FX chain. insertIndex: 0-based insert slot. paramIndex: 0-based parameter on that insert. value: float value (range depends on the insert parameter). |
Example: Vary release length by velocity
event initialize()
{
setIgnoreNoteOns(true);
}
event noteOn(NoteData note)
{
Articulation art = getCurrentArticulation();
Note playing = art.startNote(note);
// Soft notes ring out longer; loud notes cut off quickly
if(note.getVelocity() < 0.4f)
playing.setRelease(3.0f, 2.0f); // slow, lingering fade
else
playing.setRelease(0.5f, 1.0f); // short, linear fade
}
HeldNotes
A HeldNotes is a snapshot of every MIDI note the user is currently holding down — useful for chord recognition, arpeggiator-style logic, or any script behaviour that needs to know what's pressed at a given moment. Get one via the free function getHeldNotes(); for a quick single-note query, use isNoteHeld(int) instead.
HeldNotes stores up to 128 note numbers. The size member variable tells you how many are currently held.
HeldNotes tracks notes the user is physically holding on a keyboard or sending from the host DAW. Notes you trigger from a script via startNote() do not register as held — only incoming MIDI does.
Example: Detect chords by counting held notes
event initialize() { setLogEnabled(true); } // Without this, debug.log() output is silent
event noteOn(NoteData note)
{
HeldNotes held = getHeldNotes();
if(held.size >= 3)
debug.log("Chord detected, notes:", held.size);
}
Functions
| Function | Returns | Description |
|---|---|---|
| isEmpty() | bool | Returns true if no notes are currently held. |
| getHeldNote(int index) | int | Returns the MIDI note number at the given 0-based index, or -1 if the index is out of range. |
| visit<Type>(Type& visitor) | void | Generic-visitor iteration. Calls visitor.processHeldNote(int) for every held note. Pass any struct that defines a processHeldNote(int) function. |
Member Variables
| Member Variable | Type | Description |
|---|---|---|
| size | int | Number of notes currently held. |
Time-varying modulation
The script-side API has no built-in glide, fade, or ramp helpers for active notes — there's no glideTo(target, durationSec) or fadeOutOver(durationSec). Instead, you interpolate values yourself in event process() and call Note's per-note setters (setTuningSemitones, setGain, etc.) each audio block. This pattern is the canonical answer to "how do I make X change smoothly over time".
The process() event
event process() fires once per audio block (typical block size 64 samples ≈ 1.3 ms at 48 kHz). Use it to advance any time-varying state — pitch glides, fade-outs, scripted LFOs, parameter sweeps — and apply the result to active notes.
event process()
{
// runs every block — keep work small and predictable
}
Block-delta seconds
To advance a real-time animation, you need to know how much wall-clock time has passed between block calls. Divide the block size (samples) by the sample rate:
event process()
{
float blockSec = float(getBlockSize()) / float(getSampleRate());
// blockSec ≈ 0.00133 at 48 kHz / 64-sample blocks
// ... advance an elapsed-time counter, look up a position, etc.
}
Example: 30 ms pitch glide
Animate a Note's tuning offset linearly from a starting value down to zero over a fixed duration. The state lives at module level so it persists across block calls.
Note currentNote;
bool gliding;
float glideStartSemitones;
float glideElapsedSec;
const float kGlideDurationSec = 0.030f;
event noteOn(NoteData note)
{
Articulation art = getCurrentArticulation();
currentNote = art.startNote(note);
// Start the note one semitone below pitch and glide up to natural
currentNote.setTuningSemitones(-1.0f);
glideStartSemitones = -1.0f;
glideElapsedSec = 0.0f;
gliding = true;
}
event process()
{
if(!gliding)
return;
float blockSec = float(getBlockSize()) / float(getSampleRate());
glideElapsedSec += blockSec;
float ratio = min(glideElapsedSec / kGlideDurationSec, 1.0f);
currentNote.setTuningSemitones(glideStartSemitones * (1.0f - ratio));
if(ratio >= 1.0f)
gliding = false;
}
Combining modulators on the same note
Note setters are absolute — each call overwrites the property's current value. If two modulators want to influence the same property in the same block (e.g. a legato glide running alongside a vibrato LFO), accumulate their contributions and apply the total in a single call.
event process()
{
float blockSec = float(getBlockSize()) / float(getSampleRate());
float totalSemitones = 0.0f;
if(gliding)
{
glideElapsedSec += blockSec;
float ratio = min(glideElapsedSec / kGlideDurationSec, 1.0f);
totalSemitones += glideStartSemitones * (1.0f - ratio);
if(ratio >= 1.0f) gliding = false;
}
if(vibratoActive)
{
vibratoPhase += vibratoHz * blockSec;
float v = sin(vibratoPhase * 2.0f * pi);
totalSemitones += vibratoDepthSemitones * v;
}
currentNote.setTuningSemitones(totalSemitones);
}
If you want to remember something extra about a note — its vibrato phase, a glide timer, a custom envelope counter — there's nowhere to attach that data to the Note itself. Instead, declare your variables at the top of the script (outside any event handler) so they persist between calls.
For a single-voice instrument, plain variables are enough — see the glide example above. For polyphonic instruments, use an array with one slot per MIDI note (size 128) or one slot per voice the script keeps track of.
Bus
A Bus is one of the audio routing destinations in your instrument — the master bus, a mic bus, or an FX bus. It's the level you'd reach to adjust output gain, route signal between buses, or toggle an insert FX on a shared FX chain rather than per-Group.
Get a Bus via the free functions getMasterBus() or getBus() (by name or 0-based index).
A new Send type has been added; getSend(int) now returns a Send instead of a Bus, and Send currently has no functions — it's a placeholder while the dedicated send-bus API is fleshed out. getSend(string name) also exists but is a stub.
For now, set send levels via bus.setSendLevel(int sendIndex, float level) on whatever Bus is sending (still works as before — up to 8 send slots).
Example: Drive a reverb send from a UI knob
// Fires when Expression changes (built-in, MIDI CC 11 by default).
// Repurposed here as a reverb-send amount for portable testing.
// (assumes a reverb sitting on the first send bus)
event Expression(Parameter parameter)
{
Bus master = getMasterBus();
// Send a portion of the master bus's output to the first send bus (send index 0)
master.setSendLevel(0, parameter);
}
Most setter functions on Bus (gain, pan, bypass, solo, mute, send level, insert parameters, bypassInsert) accept either a primitive value or a Parameter directly. See the Parameter convenience at the top of this reference.
Identity & state
| Function | Returns | Description |
|---|---|---|
| isIdValid() | bool | Returns true if this Bus refers to a real bus in the instrument. Use as a null check before calling functions on the Bus. |
| validate() | void | Logs whether this Bus is valid (and its index if so) to the console. |
| getTypeName() | string | Returns the type name as a string ("Bus"). Useful for generic logging. |
Gain & pan
| Function | Returns | Description |
|---|---|---|
| setGain(float gain) | void | Sets the Bus's gain (linear). Range: 0.0 (silence) to 4.0 (~+12 dB); 1.0 is unity. |
| setVolume(float decibels) | void | Sets the Bus's volume in decibels. Effective range: -inf to ~+12 dB (clamped to 0.0–4.0 linear); 0 dB is unity. |
| setPan(float pan) | void | Sets pan position. Range: -1.0 (full left) to 1.0 (full right); 0.0 is center. |
Bypass, solo & mute
Toggle the Bus's playback state. These mirror the bypass, solo, and mute buttons users see on each bus in the plugin's mixer.
| Function | Returns | Description |
|---|---|---|
| setBypass(bool bypass) | void | Bypasses (or un-bypasses) the entire Bus. true = bypassed (Bus disabled), false = active. |
| setSolo(bool solo) | void | Solos the Bus. While any bus is soloed, only soloed buses are audible. |
| setMute(bool mute) | void | Mutes the Bus. true = silenced, false = audible. |
Send levels
Each Bus can send a portion of its signal to the instrument's send buses (up to 8 of them). Use this to drive parallel effects like reverbs and delays.
| Function | Returns | Description |
|---|---|---|
| setSendLevel(int sendIndex, float level) | void | Sets the send level on this Bus. sendIndex: 0-based index into the instrument's send buses (up to 8). level: typically 0.0 (no send) to 1.0 (unity send); no hard clamp. |
Insert FX
Like Groups, each Bus can host a chain of insert FX defined in the instrument. These functions let you modulate individual insert parameters or bypass entire inserts at runtime.
| Function | Returns | Description |
|---|---|---|
| setInsertParameter(int insertIndex, int paramIndex, value) | void | Sets a parameter on an insert FX. insertIndex: 0-based insert slot. paramIndex: 0-based parameter on that insert. value: float, int, or bool depending on the parameter type. |
| bypassInsert(int insertIndex, bool bypassed) | void | Bypasses (or un-bypasses) an entire insert FX slot on this Bus. true = bypassed, false = active.TipIf you're wiring this to an "Enabled" button (where true means the user wants the effect on), invert the value: bypassInsert(0, !param.getBoolValue()). |
Bus uses bypassInsert(index, bypassed) where Group uses setInsertBypassed(index, bypassed). The two do the same conceptual thing — toggle an insert slot — but the function names differ. setInsertParameter is spelled the same on both.
Example: Bypass a reverb on the master bus
// Fires when SustainPedal changes (built-in, MIDI CC 64 by default).
// Repurposed here as a reverb-bypass toggle for portable testing.
// (assumes a reverb at insert slot 0 of the master bus)
event SustainPedal(Parameter parameter)
{
Bus master = getMasterBus();
// Pass the button state straight to the insert's bypass flag
master.bypassInsert(0, parameter);
}
Output levels
Read a bus's current output meter. getLevels() returns a Levels snapshot, refreshed once per audio block — read it in event viewRefresh() or event process() to drive meters.
| Function | Returns | Description |
|---|---|---|
| getLevels() | Levels | Current output levels of this bus (also callable as the free function getLevels(myBus)). |
Levels
A read-only snapshot of a bus's output meter, returned by bus.getLevels(). Linear values are 0.0–1.0; decibel values are floored at −60 dB.
| Function | Returns | Description |
|---|---|---|
| getLeft() | float | Left channel, linear 0.0–1.0. |
| getRight() | float | Right channel, linear 0.0–1.0. |
| getLeftDecibels() | float | Left channel in dB (floored at −60). |
| getRightDecibels() | float | Right channel in dB (floored at −60). |
| getPeak() | float | Max of left/right, linear 0.0–1.0. |
| getPeakDecibels() | float | Peak in dB (floored at −60). |
| getAverage() | float | Mean of left/right, linear 0.0–1.0. |
| getAverageDecibels() | float | Average in dB (floored at −60). |
Mic
A Mic represents one of the microphone positions in your instrument — the same mics users see in the plugin's mic mixer. Mics are defined at the clip level: when you import samples, the Tokenizer identifies a "mic position" token in each filename (e.g. Close, Room, Overheads), and the unique set across all clips becomes the instrument's mic list. Each mic is surfaced as a routing object you can address by name or index.
Get a Mic via the free functions getMic(string name) or getMic(int index).
Example: Tie a mic's gain to a UI knob
// Fires when InstrumentVolume changes (built-in, MIDI CC 7 by default).
// Repurposed here as close-mic gain for portable testing.
// (assumes a mic named "Close" in the instrument)
event InstrumentVolume(Parameter parameter)
{
Mic close = getMic("Close");
// Pass the knob value straight to the mic's gain
close.setGain(parameter);
}
Every setter on Mic (gain, gain in dB, pan, solo, mute, enabled) accepts either a primitive value or a Parameter directly. See the Parameter convenience at the top of this reference.
Identity & state
| Function | Returns | Description |
|---|---|---|
| isIdValid() | bool | Returns true if this Mic refers to a real mic in the instrument. Use as a null check before calling functions on the Mic. |
| validate() | void | Logs whether this Mic is valid (and its index if so) to the console. |
| getTypeName() | string | Returns the type name as a string ("Mic"). Useful for generic logging. |
Gain & pan
| Function | Returns | Description |
|---|---|---|
| setGain(float gain) | void | Sets the Mic's gain (linear). Range: 0.0 (silence) to 4.0 (~+12 dB); 1.0 is unity. |
| setVolume(float decibels) | void | Sets the Mic's volume in decibels. Effective range: -inf to ~+12 dB (clamped to 0.0–4.0 linear); 0 dB is unity. |
| setPan(float pan) | void | Sets pan position. Range: -1.0 (full left) to 1.0 (full right); 0.0 is center. |
Solo, mute & enabled
These mirror the solo, mute, and enable buttons users see for each mic in the plugin's mic mixer.
| Function | Returns | Description |
|---|---|---|
| setSolo(bool solo) | void | Solos the Mic. While any mic is soloed, only soloed mics are audible. |
| setMute(bool mute) | void | Mutes the Mic. true = silenced, false = audible. |
| setMicEnabled(bool enabled) | void | Enables or disables the Mic entirely. true = enabled (in the mix), false = disabled. |
Example: Toggle a mic on/off from a UI button
// Fires when SostenutoPedal changes (built-in, MIDI CC 66 by default).
// Repurposed here as a Room-mic on/off toggle for portable testing.
// (assumes a mic named "Room" in the instrument)
event SostenutoPedal(Parameter parameter)
{
Mic room = getMic("Room");
// Pass the button state straight to the mic's enabled flag
room.setMicEnabled(parameter);
}
Modulator
A Modulator represents an LFO or envelope defined in your instrument — the same modulators users see attached to parameters in the plugin's modulation panel. From a script, you can adjust the modulator's shape (LFO frequency, waveform, depth) or envelope (attack, decay, sustain, release) at runtime, and enable or bypass it.
Get a Modulator via the free functions getModulator(string name) or getModulator(int index).
A modulator's effective output is the product of three things: its raw signal (LFO waveform output or envelope shape), the script-side depth scalar (setLFODepth for LFOs, setEnvelopeSustain for envelopes), and the routing amount set in the modulation panel in the KODA app when the modulator was attached to its destination.
The script's depth is a normalized 0..1 multiplier — the absolute cents, semitones, or dB applied to the target come from the routing amount. If you've called setLFODepth(1.0) and still hear no modulation, open the modulation panel, find the modulator's attachment to its destination parameter, and check that the routing amount isn't zero.
Example: Tie vibrato depth to the Mod Wheel
// Fires when Vibrato changes (built-in, MIDI CC 2 by default —
// the standard mod-wheel destination)
// (assumes a Modulator named "Vibrato" in the instrument)
event Vibrato(Parameter parameter)
{
Modulator vibrato = getModulator("Vibrato");
// 0.0 = no vibrato, 1.0 = full vibrato
vibrato.setLFODepth(parameter);
}
Every setter on Modulator (enable, all LFO controls, all envelope controls) accepts either a primitive value or a Parameter directly. See the Parameter convenience at the top of this reference.
Enable & bypass
| Function | Returns | Description |
|---|---|---|
| setEnabled(bool enabled) | void | Enables or bypasses the Modulator. true = enabled (active), false = bypassed (modulator has no effect). |
LFO controls
These shape the LFO's behavior — rate, waveform, phase, depth, and whether the LFO runs globally (shared across all notes) or per-note (each note starts a fresh phase).
| Function | Returns | Description |
|---|---|---|
| setLFOFrequency(float frequency) | void | Sets the LFO frequency in Hz. 0.0 = stopped. Typical range 0.01 to 100 Hz; no hard clamp. |
| setLFOWaveform(int waveform) | void | Sets the LFO waveform by index: Sine = 0, Triangle = 1, Saw = 2, Square = 3. |
| setLFOPhaseOffset(float phaseOffset) | void | Sets the LFO phase offset (normalized). Range: 0.0 to 1.0 (wraps); 0.5 = half cycle. |
| setLFOBipolar(bool bipolar) | void | Sets whether the LFO output is bipolar. true = -1 to 1 (centered on 0), false = 0 to 1 (centered on 0.5). |
| setLFOGlobal(bool global) | void | Sets whether the LFO is global or per-note. true = global (all notes share phase), false = per-note (each note has independent phase). |
| setLFODepth(float depth) | void | Sets the LFO depth scalar. Range: 0.0 (no modulation) to 1.0 (full routing-defined range). The absolute cents/semitones/dB are set by the routing in the editor — this just scales them. |
Envelope controls
These shape the envelope's pre-delay, attack-hold-decay-sustain-release stages, and their curve exponents.
| Function | Returns | Description |
|---|---|---|
| setEnvelopePreDelay(float preDelay) | void | Sets pre-delay in seconds. 0.0 = no delay. Typical range 0.0–10.0 seconds; no hard clamp. |
| setEnvelopeAttack(float attack) | void | Sets attack time in seconds. 0.0 = instant. Typical range 0.01–10.0 seconds. |
| setEnvelopeAttackCurve(float attackCurve) | void | Sets the attack curve exponent. Range: 0.5–2.0. 0.5 = convex (fast start, slow finish), 1.0 = linear, 2.0 = concave (slow start, fast finish). |
| setEnvelopeHold(float hold) | void | Sets hold time in seconds. 0.0 = no hold. Typical range 0.0–10.0 seconds. |
| setEnvelopeDecay(float decay) | void | Sets decay time in seconds. 0.0 = instant. Typical range 0.0–10.0 seconds. |
| setEnvelopeDecayCurve(float decayCurve) | void | Sets the decay curve exponent. Same conventions as attack curve. |
| setEnvelopeSustain(float sustain) | void | Sets the sustain level. Range: 0.0 (silence) to 1.0 (full level). |
| setEnvelopeRelease(float release) | void | Sets release time in seconds. 0.0 = instant. Typical range 0.0–10.0 seconds. |
| setEnvelopeReleaseCurve(float releaseCurve) | void | Sets the release curve exponent. Same conventions as attack curve. |
Example: Switch LFO waveform from a UI dropdown
// Fires whenever CurrentArticulation changes (built-in, integer-valued).
// Each articulation index picks a different LFO shape — fine for testing,
// since articulation buttons naturally produce 0, 1, 2, 3 ...
// 0 = Sine, 1 = Triangle, 2 = Saw, 3 = Square
// (assumes a Modulator named "Tremolo" in the instrument)
event CurrentArticulation(Parameter parameter)
{
Modulator tremolo = getModulator("Tremolo");
tremolo.setLFOWaveform(parameter);
}
Crossfader
A Crossfader blends smoothly between two or more Articulations, Variations, or Groups based on a single 0.0–1.0 value. As you move that value, the Crossfader interpolates gain across its targets — useful for dynamics layers, blendable mic positions, vibrato amount, or anywhere you want a UI knob to feel like a continuous fader across discrete sample sets.
Each Crossfader is locked to a single target type. The available types are ArticulationCrossfader, VariationCrossfader, and GroupCrossfader. Construct one with the templated factory createCrossfader<Articulation>(), createCrossfader<Variation>(), or createCrossfader<Group>(). Declare the Crossfader at script scope (outside any event handler) so it persists across events.
Example: Crossfade three dynamic-layer Articulations from a UI knob
// Persistent Crossfader, set up once and updated on each knob change
ArticulationCrossfader dynamics;
// (assumes Articulations named "Soft", "Medium", "Hard" in the instrument)
event initialize()
{
dynamics = createCrossfader<Articulation>();
dynamics.addTarget(getArticulation("Soft"));
dynamics.addTarget(getArticulation("Medium"));
dynamics.addTarget(getArticulation("Hard"));
dynamics.setEqualPowerCrossfade(); // constant perceived volume
}
event Dynamics(Parameter parameter)
{
// 0.0 = full Soft, 0.5 = blend Medium/Hard, 1.0 = full Hard
dynamics.set(parameter.getValue());
}
The three Crossfader types — ArticulationCrossfader, VariationCrossfader, GroupCrossfader — are aliases for a single templated Crossfader struct in source, specialised on Articulation, Variation, or Group respectively. They share the same member variables and behaviour; only the target type differs.
Up to 12 targets are supported per Crossfader. Internally, set(value) identifies the two adjacent targets surrounding value and applies setGain to each based on the curve shape; non-active targets are set to gain 0.
Construction & targets
| Function | Returns | Description |
|---|---|---|
| createCrossfader<Articulation>() | ArticulationCrossfader | Free function. Returns a new Crossfader specialised for Articulations, with no targets and an equal-power default curve. |
| createCrossfader<Variation>() | VariationCrossfader | Returns a new Crossfader specialised for Variations. |
| createCrossfader<Group>() | GroupCrossfader | Returns a new Crossfader specialised for Groups. |
| addTarget(Articulation articulation) | void | Adds an Articulation as a crossfade target. Only valid on an ArticulationCrossfader. |
| addTarget(Variation variation) | void | Adds a Variation as a crossfade target. Only valid on a VariationCrossfader. |
| addTarget(Group group) | void | Adds a Group as a crossfade target. Only valid on a GroupCrossfader. |
Blending
| Function | Returns | Description |
|---|---|---|
| set(float value) | void | Sets the crossfade position. Range: 0.0 (full first target) to 1.0 (full last target). Values in between blend across adjacent targets. Clamped to 0.0–1.0. |
Curve shape
Controls how gain transitions between adjacent targets. The exponent applies to both fading-out and fading-in halves: lowerGain = (1 - blend) ^ exponent, upperGain = blend ^ exponent.
| Function | Returns | Description |
|---|---|---|
| setLinearCrossfade() | void | Sets exponent to 1.0. Gains sum to 1.0 at the midpoint — natural for non-correlated material, but perceived volume dips slightly. |
| setEqualPowerCrossfade() | void | Sets exponent to 0.5. Gains follow a square-root curve — perceived loudness stays roughly constant across the blend. The default after createCrossfader<…>(). |
| setExponentialCrossfade(float exp) | void | Sets a custom curve exponent. Clamped to 0.25–2.0. Equivalent to setExponent(). |
| setExponent(float exponent) | void | Sets a custom curve exponent directly. Clamped to 0.25–2.0. |
Inspection
| Function | Returns | Description |
|---|---|---|
| getValue() | float | Returns the current crossfade position (last value passed to set()). |
| getNumTargets() | int | Returns the number of targets currently registered. |
Example: Switch between linear and equal-power curves from a button
// (assumes the top-example setup is in place: the "dynamics" ArticulationCrossfader
// declared at script scope and configured in initialize)
// Fires when SustainPedal changes (built-in, MIDI CC 64 by default).
// Repurposed here as an equal-power / linear curve toggle for portable testing.
event SustainPedal(Parameter parameter)
{
if(parameter.getBoolValue())
dynamics.setEqualPowerCrossfade(); // constant perceived volume
else
dynamics.setLinearCrossfade(); // constant gain sum
}
Component
A Component is a handle to a single GUI element from your instrument's UI — a knob, button, menu, label, panel, animation, etc. In Figma, components appear as layers marked in purple. From a script you can toggle its visibility, move it, set its value, bind it to a parameter, paint shapes on it, or drive its animation.
Get a Component via the free function getComponent(string controlID). The controlID is the name set on the element in Figma.
Example: Show or hide a panel from a UI button
// Fires when SustainPedal changes (built-in, MIDI CC 64 by default).
// Repurposed here as a panel visibility toggle for portable testing.
// (assumes a Component named "MixerPanel" in the instrument UI)
event SustainPedal(Parameter parameter)
{
Component mixerPanel = getComponent("MixerPanel");
mixerPanel.setVisible(parameter.getBoolValue());
}
Visibility & position
| Function | Returns | Description |
|---|---|---|
| setVisible(bool visible) | void | Shows or hides the Component. true = visible, false = hidden. |
| setX(int x) | void | Sets the Component's x position in pixels (relative to its parent). |
| setY(int y) | void | Sets the Component's y position in pixels (relative to its parent). |
| setAlpha(float alpha) | void | Sets the Component's opacity. Range: 0.0 (fully transparent) to 1.0 (fully opaque). |
| setEnabled(bool enabled) | void | Enables or disables interaction. true = interactive, false = greyed-out and unresponsive. |
Value, text & state
| Function | Returns | Description |
|---|---|---|
| setValue(float value) | void | Sets a numeric value on the Component. Valid range depends on the bound parameter or element type. |
| setText(string text) | void | Sets the text content (for labels, text fields, etc.). |
| setToggleState(bool state) | void | Sets the toggle state for buttons or switches. true = on/down/active, false = off/up/inactive. |
| setMenuSelection(int selection) | void | Sets the selected item on a menu by 0-based index. |
| setNumColumns(int columns) | void | Sets the number of columns for grid-style Components. Must be a positive integer. |
Binding
Connect a Component to data sources at runtime: a UI parameter (so the component reflects and controls that parameter) or a panel variant (for Figma multi-variant panels).
| Function | Returns | Description |
|---|---|---|
| setParameter(string parameterName) | void | Binds the Component to a UI Parameter by name. The Component will now reflect and control that parameter's value. |
| setPanelVariant(string variantName) | void | Switches a panel Component to a named Figma variant. |
Painting
Draw shapes, text, or images onto a Component at runtime. The vector shapes (Ellipse, Rectangle, Arc, Path) are drawn with fill(...) and stroke(...): each takes the shape plus a Colour or a Gradient. fill fills the shape; stroke outlines it (using the shape's strokeWidth / strokeThickness). They are separate operations — to both fill and outline a shape, call fill and then stroke. Image and Text are drawn with paint(...). Every function draws behind the Component's children; the matching ...Over(...) form draws in front of them.
Build and draw at most one Path per paint function. Two reachable fillPath/strokePath sites in the same function can cause the Component to render blank — split them across separate functions if you need more than one.
| Function | Returns | Description |
|---|---|---|
| fill(Rectangle r, Colour|Gradient) | void | Fills a rectangle with a solid colour or a gradient (behind its children). Same overloads exist for Ellipse and Arc. |
| stroke(Rectangle r, Colour|Gradient) | void | Outlines a rectangle (line width = the shape's strokeWidth) with a colour or gradient. Same overloads exist for Ellipse and Arc. |
| fillPath(Path p, Colour|Gradient) | void | Fills a Path with a colour or gradient (behind its children). |
| strokePath(Path p, Colour|Gradient) | void | Strokes (outlines) a Path with a colour or gradient, using its strokeThickness / jointStyle / endCapStyle. |
| paint(Image image) | void | Paints an image onto the Component (behind its children). |
| paint(Text text) | void | Paints text onto the Component (behind its children). |
| paint(Canvas canvas) | void | Paints a pre-recorded Canvas of paint actions onto the Component. Build a Canvas once, then reuse it across components. |
| fillOver / strokeOver(…) | void | Same as fill / stroke but draws in front of the Component's children (Rectangle, Ellipse, Arc). Paths have fillPathOver / strokePathOver; Image/Text/Canvas have paintOver(...). |
Animation
For Components that hold animated content (e.g. Lottie or sprite-sheet animations from Figma), these functions control playback.
| Function | Returns | Description |
|---|---|---|
| start() | void | Starts the animation playing. |
| pause() | void | Pauses the animation at its current frame. |
| stop() | void | Stops the animation. |
| reset() | void | Resets the animation to its first frame. |
| reverse(bool reversed) | void | Sets the playback direction. true = play backwards, false = play forwards. |
| setFrameRate(int fps) | void | Sets the animation frame rate in frames per second. Typical: 24–60 fps. |
| setFrame(int frame) | void | Jumps to a specific frame (0-based). |
| setLooping(bool isLooping) | void | Sets whether the animation loops. true = loop continuously, false = play once and stop. |
Example: Drive an animation with a toggle button
// Fires when SustainPedal changes (built-in, MIDI CC 64 by default).
// Repurposed here as an animation play/pause toggle for portable testing.
// (assumes an animated Component named "Spinner" in the instrument UI)
event SustainPedal(Parameter parameter)
{
Component spinner = getComponent("Spinner");
if(parameter.getBoolValue())
spinner.start();
else
spinner.pause();
}
Bounds, rotation & scale
Position, orient and scale a Component after creation.
| Function | Returns | Description |
|---|---|---|
| setBounds(Bounds bounds) | void | Sets position and size from a Bounds. |
| setRotation(float radians) | void | Rotates about the centre; angle in radians (2π = full turn), 0.0 = none. |
| setScale(float scale) | void | Uniform scale about the centre; 1.0 = original. |
| setScale(float scaleX, float scaleY) | void | Non-uniform scale per axis; 1.0 = original on that axis. |
| bringToFront() | void | Raises to the front of its sibling z-order. |
| getName() | string | Returns the Component's control ID. |
Text formatting
For Label, value-edit and text-edit Components.
| Function | Returns | Description |
|---|---|---|
| setText(float value) | void | Sets text to a float (2 decimal places). |
| setText(float value, int numDecimals) | void | Sets text to a float with N decimals. |
| setText(int value) | void | Sets text to an integer. |
| clearText() | void | Clears the text. |
| setSuffix(string suffix) | void | Appends a unit suffix to displayed text (e.g. " Hz"). |
| setUnitType(int unitType) | void | Formats the value with a unit; UnitType:: constants: None=0, FrequencyHz=1 (auto Hz/kHz above 1000), Decibels=2 (shows "-inf" at/below −60 dB), Percent=3, Milliseconds=4, Pan=5 (100L..C..100R), NoteName=6. |
| setAllowedCharacters(string characters) | void | Restricts typeable characters on a text-edit (empty string restores default). |
| setFont(Font font) | void | Sets the font from a Font. |
Effects & sprites
Attach visual effects and sprite sheets to a Component.
| Function | Returns | Description |
|---|---|---|
| addEffect(Effect effect) | void | Adds a visual effect (see Effect). |
| removeEffects() | void | Removes all effects. |
| setSprite(string path, int spriteWidth, int spriteHeight, int numFrames) | void | Attaches a sprite sheet (per-frame size + frame count). |
| setSprite(SpriteSheet sprite) | void | Attaches a sprite sheet from a SpriteSheet. |
Containers & event routing
Nest Components and route their input to script event handlers.
| Function | Returns | Description |
|---|---|---|
| addComponent(Component child) | void | Re-parents a child into this (panel) Component so it moves and scales with it. Call on the parent: panel.addComponent(child). |
| sendMouseEvents(int identifier) | void | Routes this Component's mouse events to a script event handleMouse(MouseEvent mouseEvent) handler; read mouseEvent.getIdentifier() to tell components apart. (Attach to Panel/Label/Default widgets — sliders and buttons consume their own mouse events.) |
| sendGuiEvents(int identifier) | void | Routes value/selection changes to a script event handleGuiEvent(GuiEvent guiEvent) handler; read guiEvent.getIdentifier() and guiEvent.getValue(). |
Menu & list items
For Menu and ListItemBox Components.
| Function | Returns | Description |
|---|---|---|
| addItem(string name, int value) | void | Adds an item (display label + user int reported on selection). |
| clearItems() | void | Removes all items. |
| setListSelection(int index) | void | Selects a ListItemBox item by index (−1 clears). |
| clearMenuSelection() | void | Clears a Menu's selection (same as setMenuSelection(−1)). |
| setItemSize(int width, int height) | void | Per-item pixel size. |
| setItemTextFont(Font font) | void | Item text font. |
| setItemTextBounds(Bounds bounds) | void | Item text layout bounds within each item. |
| setItemTextColour(Colour colour, int state) | void | Item text colour per state; MenuItemState:: Normal=0, Hover=1, Selected=2. |
| setMenuBackgroundColour(Colour colour) | void | Menu drop-down background. |
| setMenuOutlineColour(Colour colour) | void | Menu drop-down border colour. |
| setMenuOutlineThickness(float thickness) | void | Menu drop-down border thickness in pixels. |
The viewRefresh() event
event viewRefresh() fires once per UI "frame" at a configurable rate (default 24 Hz). Use it to update painted UI and animated readouts.
It is not synced to the actual GUI framerate, so don't rely on it for precise timing. (It was previously named refresh.)
| Function | Returns | Description |
|---|---|---|
| setViewRefreshRate(int hz) | void | Sets the viewRefresh rate; clamped 12–60 Hz (default 24). Only the instrument-level (root) script's setting takes effect. |
| getViewRefreshRate() | int | Returns the current viewRefresh rate in Hz. |
event initialize()
{
setViewRefreshRate(30);
}
// Update a numeric meter label from the master bus level, 30×/sec
event viewRefresh()
{
Bus master = getMasterBus();
Levels lv = master.getLevels();
Component meter = getComponent("Meter");
meter.setText(lv.getPeakDecibels(), 1);
}
Creating Widgets at Runtime
Beyond binding to widgets you laid out in Figma, a script can create widgets at runtime (e.g. in event initialize()). Each create function returns a Component handle you keep and drive like any other. Positions and sizes use a Bounds (x, y, width, height in pixels).
| Function | Returns | Description |
|---|---|---|
| createButton(string name, Bounds bounds) | Component | Creates a button. |
| createPanel(string name, Bounds bounds) | Component | Creates a panel; use as a container (addComponent) or a paint surface. |
| createRotarySlider(string name, Bounds bounds) | Component | Creates a rotary slider (knob). |
| createHorizontalSlider(string name, Bounds bounds) | Component | Creates a horizontal slider. |
| createVerticalSlider(string name, Bounds bounds) | Component | Creates a vertical slider. |
| createLabel(string name, Bounds bounds, string text) | Component | Creates a text label with initial text. |
| createXYPad(string name, Bounds bounds, string parameterX, string parameterY) | Component | Creates an XY pad bound to two parameters by name (one per axis). |
| createMenu(string name, Bounds bounds) | Component | Creates a drop-down menu; populate with addItem(). |
| createListItemBox(string name, Bounds bounds) | Component | Creates a scrollable list; populate with addItem(), track with setListSelection(). |
| createSearchField(string name, Bounds bounds) | Component | Creates a text/search entry field. |
| createStateDisplay(string name, Bounds bounds) | Component | Creates a state-display widget. |
| createAnimation(string name, Bounds bounds) | Component | Creates an animation widget; drive with the Animation functions (start/pause/setFrame/…). |
The name argument is the new Component's control ID (used later with getComponent).
event initialize()
{
Component play = createButton("Play", Bounds(10.0f, 10.0f, 80.0f, 30.0f));
}
Colour
Colour is the strong ARGB colour type used across the paint API, replacing the raw int32 values used in earlier builds. It has a single member variable value (int32, packed 0xAARRGGBB), but you normally build colours with the colours:: constructors and read or derive them with the methods below rather than touching value directly.
Construction
The colours:: constructors are free functions that each return a Colour.
| Function | Returns | Description |
|---|---|---|
| colours::fromRGB(int r, int g, int b) | Colour | Opaque colour from 8-bit channels (each 0–255). |
| colours::fromRGBA(int r, int g, int b, int a) | Colour | From 8-bit channels plus alpha (each 0–255). An overload takes a float alpha 0.0–1.0. |
| colours::fromRGB(int hex) | Colour | From a 24-bit hex RGB literal (e.g. 0xFF7F00), opaque. |
| colours::fromHex(int64 hex) | Colour | From a 32-bit ARGB literal. Use the _L suffix so an alpha ≥ 0x80 doesn't overflow int32 (e.g. 0xFFFFA500_L). |
| colours::fromFloat(float r, float g, float b, float a) | Colour | From float channels (each 0.0–1.0). A 3-arg overload omits alpha and returns an opaque colour. |
| colours::fromHSB(float h, float s, float b) | Colour | Opaque colour from HSB components (each 0.0–1.0). fromHSBA adds an alpha argument. |
| colours::fromHSL(float h, float s, float l) | Colour | Opaque colour from HSL components (each 0.0–1.0). fromHSLA adds an alpha argument. |
There are also named constants such as colours::white, colours::black, colours::red, colours::orange, colours::dodgerblue, and colours::transparent, along with the full CSS/JUCE colour-name set, each a const Colour.
Channels & HSB
Read individual channels as 8-bit integers or normalised floats, or pull out HSB components.
| Function | Returns | Description |
|---|---|---|
| getAlpha() / getRed() / getGreen() / getBlue() | int | Channel value 0–255. |
| getAlphaFloat() / getRedFloat() / getGreenFloat() / getBlueFloat() | float | Channel value 0.0–1.0. |
| getHue() / getSaturation() / getBrightness() | float | HSB component 0.0–1.0. |
Transforms
Each transform returns an immutable copy; the original colour is unchanged.
| Function | Returns | Description |
|---|---|---|
| withAlpha(float a) | Colour | Copy with alpha set (0.0–1.0). An int overload withAlpha(int a) takes 0–255. |
| withMultipliedAlpha(float m) | Colour | Copy with the current alpha multiplied by m. |
| brighter(float amt) | Colour | Copy blended toward white (0.0 = unchanged, 1.0 = white). |
| darker(float amt) | Colour | Copy blended toward black (0.0 = unchanged, 1.0 = black). |
| withHue(float h) / withSaturation(float s) / withBrightness(float b) | Colour | Copy with one HSB component replaced (each 0.0–1.0). |
| interpolatedWith(Colour other, float t) | Colour | Linear blend; t 0.0 = this colour, 1.0 = other. |
| overlaidOn(Colour background) | Colour | Alpha-composite this colour over an opaque background. |
// Build a base colour, then derive a translucent, brighter variant
Colour base = colours::fromRGB(96, 192, 128);
Colour lighter = base.brighter(0.3);
Colour overlay = lighter.withAlpha(0.5);
Paint Primitives
Paint primitives are plain data structs that describe shapes, text, or images to draw onto a Component. The vector shapes (Ellipse, Rectangle, Arc, Path) carry only geometry — colour is supplied at draw time: construct the shape, set its member variables, then pass it to fill(shape, colour) / stroke(shape, colour) (or fillPath / strokePath for a Path), passing a Colour or a Gradient. Image and Text are drawn with paint(image) / paint(text).
Unlike most KODA types, paint primitives don't expose getters or setters — you read and write their member variables directly. All coordinates are in pixels relative to the Component's origin. All sizes are in pixels.
Colours are passed to fill / stroke (and used inside a Gradient's stops) as the strong Colour type. Build one with a colours:: constructor (e.g. colours::fromRGB(96, 192, 128) or colours::fromHex(0xFF60C080_L)) or use a named constant (e.g. colours::aliceblue, colours::black). Use colours::transparent (or any colour with zero alpha) for an invisible draw. See the Colour section for channel getters and transforms.
Painting onto a Component requires specific widget setup in your instrument's UI — not every Component type will render paint actions, and Panel-type Components belong to visibility groups managed by the panel-group system. Examples and recipes for setting up a paintable Component will appear here once that workflow is documented. For now, refer to the Component section for known-good binding patterns.
Point & Bounds
Lightweight position and size structs used as building blocks (notably inside Path).
| Point — a 2D coordinate | ||
|---|---|---|
| Member Variable | Type | Description |
| x | float | X coordinate in pixels. |
| y | float | Y coordinate in pixels. |
| Bounds — a position + size rectangle | ||
|---|---|---|
| Member Variable | Type | Description |
| x | float | X position of the top-left corner. |
| y | float | Y position of the top-left corner. |
| width | float | Width in pixels. |
| height | float | Height in pixels. |
Ellipse
An ellipse shape. Draw it with fill(ellipse, …) and/or stroke(ellipse, …), passing a Colour or a Gradient.
| Member Variable | Type | Description |
|---|---|---|
| x | float | X position of the ellipse's bounding box. |
| y | float | Y position of the ellipse's bounding box. |
| width | float | Bounding-box width (full diameter on the x-axis). |
| height | float | Bounding-box height (full diameter on the y-axis). |
| strokeWidth | float | Outline thickness in pixels, used by stroke(). |
Rectangle
Same as Ellipse, plus a corner-radius member variable for rounded rectangles. Draw with fill(rectangle, …) / stroke(rectangle, …).
| Member Variable | Type | Description |
|---|---|---|
| x | float | X position of the top-left corner. |
| y | float | Y position of the top-left corner. |
| width | float | Width in pixels. |
| height | float | Height in pixels. |
| cornerRadius | float | Radius for rounded corners in pixels. 0.0 = square corners. |
| strokeWidth | float | Outline thickness in pixels, used by stroke(). |
Arc
A circular arc swept between two angles. Useful for knob indicators, ring meters, and pie-style readouts. Draw with fill(arc, …) / stroke(arc, …).
| Member Variable | Type | Description |
|---|---|---|
| x | float | X position of the arc's bounding box. |
| y | float | Y position of the arc's bounding box. |
| width | float | Bounding-box width. |
| height | float | Bounding-box height. |
| startAngle | float | Start angle in radians. 0 = 3 o'clock; positive = clockwise. |
| endAngle | float | End angle in radians. |
| strokeWidth | float | Outline thickness in pixels, used by stroke(). |
Path
A multi-segment vector outline built from straight lines and Bézier curves. Unlike the other primitives you don't set point member variables directly — you build the path by calling its segment functions, then draw it with the Component's fillPath(path, colour|gradient) or strokePath(path, colour|gradient).
| Member Variables | ||
|---|---|---|
| Member Variable | Type | Description |
| strokeThickness | float | Stroke thickness in pixels (for strokePath()). |
| jointStyle | int | How stroked corners join: JointStyle::mitered = 0, curved = 1, beveled = 2. |
| endCapStyle | int | How stroked ends are capped: EndCapStyle::butt = 0, square = 1, rounded = 2. |
| Build functions | ||
|---|---|---|
| Function | Returns | Description |
| startNewSubPath(float x, float y) | void | Begins a new sub-path at (x, y) without drawing a line to it. Call before the first segment of a disconnected shape. |
| lineTo(float x, float y) | void | Adds a straight line from the current point to (x, y). |
| quadraticTo(float controlX, float controlY, float x, float y) | void | Adds a quadratic Bézier curve to (x, y) using one control point. |
| cubicTo(float control1X, float control1Y, float control2X, float control2Y, float x, float y) | void | Adds a cubic Bézier curve to (x, y) using two control points. |
| close() | void | Closes the current sub-path by drawing a line back to its start point. |
| clear() | void | Removes all segments so the Path can be rebuilt. |
// Build a triangle and stroke it onto a Component named "Canvas"
Path tri;
tri.strokeThickness = 2.0f;
tri.startNewSubPath(10.0f, 10.0f);
tri.lineTo(90.0f, 10.0f);
tri.lineTo(50.0f, 80.0f);
tri.close();
Component c = getComponent("Canvas");
c.strokePath(tri, colours::fromHex(0xFFFFFFFF_L));
Gradient
A multi-stop colour gradient. Pass a Gradient in place of a Colour to any fill / stroke / fillPath / strokePath to fill or outline a shape with a gradient. Build it by setting the type and endpoints, then adding up to 16 colour stops with addStop(position, colour).
Set type to one of GradientType::Linear (0), GradientType::Radial (1), GradientType::Angular (2), or GradientType::Diamond (3). Only Linear and Radial render today — Angular and Diamond are reserved for a future JUCE version and currently draw nothing (a one-time warning is logged to the script console). For Linear the gradient runs along the axis from (x1,y1) to (x2,y2); for Radial it runs from the centre (x1,y1) out to (x2,y2). Coordinates are in pixels relative to the Component's origin.
| Member Variables | ||
|---|---|---|
| Member Variable | Type | Description |
| type | int | GradientType::Linear / Radial / Angular / Diamond. Default 0 = Linear. |
| x1 | float | X of the gradient start point (centre for Radial). |
| y1 | float | Y of the gradient start point (centre for Radial). |
| x2 | float | X of the gradient end point (edge for Radial). |
| y2 | float | Y of the gradient end point (edge for Radial). |
| Functions | ||
|---|---|---|
| Function | Returns | Description |
| addStop(float position, Colour colour) | void | Adds a colour stop at position (0.0 = start, 1.0 = end). Up to 16 stops; extra calls are ignored. |
| clear() | void | Removes all stops so the Gradient can be rebuilt. |
// Fill a rectangle with a vertical 3-stop linear gradient
Rectangle r;
r.x = 0.0f; r.y = 0.0f; r.width = 120.0f; r.height = 80.0f;
Gradient g;
g.type = GradientType::Linear;
g.x1 = 0.0f; g.y1 = 0.0f; // top
g.x2 = 0.0f; g.y2 = 80.0f; // bottom
g.addStop(0.0f, colours::dodgerblue);
g.addStop(0.5f, colours::white);
g.addStop(1.0f, colours::black);
Component c = getComponent("Canvas");
c.fill(r, g);
Image
Paints an image (looked up by name) into a rectangular region. The image must already be available to the instrument (typically exported alongside the Figma UI).
| Member Variable | Type | Description |
|---|---|---|
| image | string | Image identifier — the name of the asset to draw. |
| x | float | X position of the top-left corner. |
| y | float | Y position of the top-left corner. |
| width | float | Display width in pixels. |
| height | float | Display height in pixels. |
Text
Paints a string of text. Use for runtime-generated labels, value readouts, or status indicators where the text changes based on parameter values.
| Member Variable | Type | Description |
|---|---|---|
| text | string | The string to render. |
| font | string | Font family name (e.g. "Inter"). Must be available to the instrument. |
| fontSize | float | Font size in points. |
| fontColour | Colour | Text colour (ARGB). |
| x | float | X position of the text's bounding box. |
| y | float | Y position of the text's bounding box. |
| width | float | Bounding-box width (text wraps if it overflows). |
| height | float | Bounding-box height. |
Font
A font spec for label-style widgets, passed to setFont() / setItemTextFont().
| Font — a label/value font spec | ||
|---|---|---|
| Member Variable | Type | Description |
| name | string | Font family name (e.g. "Inter"). |
| style | string | Style name: "Regular", "Bold", "Italic", "Bold Italic", etc. |
| height | float | Point height. A value ≤ 0 keeps the widget's current height. |
| justification | int | Text alignment; Justification:: constants: Keep=0 (no change), Left=1, Center=2, Right=3, plus combined TopLeft=4 … BottomRight=12 (CenterLeft=7, Centered=8, CenterRight=9). |
| kerning | float | Extra inter-character spacing factor (0.0 = default). |
| colour | Colour | Text colour. An alpha of 0 keeps the current colour. |
Effect
A visual effect added to a Component via addEffect().
| Effect — a component visual effect | ||
|---|---|---|
| Member Variable | Type | Description |
| type | int | Effect kind; EffectType:: constants: Blur=0 (blurs the component's own content), BackgroundBlur=1 (frosted-glass blur of what's behind it), DropShadow=2 (offset shadow), Glow=3 (centred halo). |
| radius | float | Blur/spread radius in pixels. |
| tint | Colour | Shadow/glow colour (DropShadow/Glow) or tint. |
| offsetX | float | Horizontal shadow offset in pixels (DropShadow). |
| offsetY | float | Vertical shadow offset in pixels (DropShadow). |
Canvas
A Canvas records a list of paint actions you build once and then draw onto one or more Components with paint(Canvas) / paintOver(Canvas). Use it to avoid re-issuing the same paint calls per component. Declare one with Canvas myCanvas;.
| Function | Returns | Description |
|---|---|---|
| fill(Rectangle r, Colour|Gradient) | bool | Records a filled rectangle. Same overloads for Ellipse and Arc. Returns false if the canvas is full. |
| stroke(Rectangle r, Colour|Gradient) | bool | Records a stroked rectangle. Same overloads for Ellipse and Arc. Returns false if the canvas is full. |
| fillPath(Path p, Colour|Gradient) | bool | Records a filled path. Returns false if the canvas is full. |
| strokePath(Path p, Colour|Gradient) | bool | Records a stroked path. Returns false if the canvas is full. |
| paint(Text text) | bool | Records text. Returns false if the canvas is full. |
| paint(Image image) | bool | Records an image. Returns false if the canvas is full. |
| clear() | void | Clears all recorded actions. |
// Build a canvas once, then draw it onto a component
Canvas myCanvas;
Rectangle r;
r.x = 0.0f; r.y = 0.0f; r.width = 64.0f; r.height = 32.0f;
myCanvas.fill(r, colours::dodgerblue);
Component panel = getComponent("Panel");
panel.paint(myCanvas);
SpriteSheet
Describes a sprite sheet passed to setSprite() for animated Components.
| SpriteSheet — a sprite-sheet definition | ||
|---|---|---|
| Member Variable | Type | Description |
| path | string | Image asset name. |
| width | int | Per-frame width in pixels. |
| height | int | Per-frame height in pixels. |
| frames | int | Total number of frames in the sheet. |
MouseEvent
A MouseEvent is passed to your script when the user interacts with a Component that has a mouse event declared. You handle one by writing event <Name>(MouseEvent mouseEvent) { ... }, where <Name> matches the event name set on the Component in Figma. The event payload tells you where the mouse was (x, y), which modifier keys were held, and what kind of mouse action occurred.
In the current build, only Panel widgets forward mouse events to script handlers. Other widget types (Slider/Knob, XYPad, Table, Button) consume their clicks for their own behavior without forwarding, and Label / Animation / Default-type widgets don't intercept clicks at all by default. To make event <Name>(MouseEvent ...) fire reliably, attach the "MouseEvent" property to a Panel with "Floating Window": "True" in your Figma export. A worked example will appear here once the source-side fix lands that lets other widget types forward MouseEvents alongside their own handling.
Member Variables
| Member Variable | Type | Description |
|---|---|---|
| x | int | X position of the mouse relative to the Component's origin. |
| y | int | Y position of the mouse relative to the Component's origin. |
| mods | int | Bitfield of held modifier keys. Compare with MouseModifiers::Command, ::Shift, ::Alt — or use the isCommandDown() / isShiftDown() / isAltDown() helpers below. |
| rightClick | int | Non-zero when the press is a right-click. Prefer isRightClick() / isLeftClick(). |
| type | MouseEventType | What kind of mouse action this is. One of Down, Up, Enter, Exit, DoubleClick, StartDrag, Drag, DragEnter, DragExit, Drop. |
| identifier | int | Identifier used internally to associate the event with its source. Most scripts can ignore this. |
Modifier-key helpers
| Function | Returns | Description |
|---|---|---|
| isCommandDown() | bool | true if Command (⌘ on macOS, Ctrl on Windows) was held during the event. |
| isShiftDown() | bool | true if Shift was held. |
| isAltDown() | bool | true if Alt / Option was held. |
Event-type helpers
| Function | Returns | Description |
|---|---|---|
| isLeftClick() | bool | true on a left-button press (Down event, non-right-click). |
| isRightClick() | bool | true on a right-button press (Down event, right-click). |
| isDown() | bool | true if type is Down. |
| isUp() | bool | true if type is Up. |
| isEnter() | bool | true if the mouse entered the Component's bounds. |
| isExit() | bool | true if the mouse left the Component's bounds. |
| isDoubleClick() | bool | true on a double-click. |
| isStartDrag() | bool | true when a drag operation starts. |
| isDrag() | bool | true if this is a drag event (mouse moved while a button is held). |
| isDragEnter() | bool | true when a drag enters the Component's bounds. |
| isDragExit() | bool | true when a drag leaves the Component's bounds. |
| isDrop() | bool | true when a dragged item is dropped on the Component. |
KeyEvent
A KeyEvent is passed to your script when a keyboard key is pressed while a Component is focused. Handle one by writing event <Name>(KeyEvent keyEvent) { ... }, where <Name> matches the event name set in Figma.
In the current build, KeyEvent handlers don't have a verified delivery path. Every widget type we tested either doesn't intercept keyboard input at the script-reachable level (Label, Animation, Default), or it intercepts at an inner JUCE child (TextEdit's inner editor, Slider's inner slider) which prevents the script's keyPressed hook from firing.
The script-side declaration syntax (event <Name>(KeyEvent keyEvent)) is documented above, and the kdui side ("KeyEvent": "<Name>" property on a Component) compiles cleanly. A worked example will appear here once the source-side delivery path is verified for a specific widget type.
Member Variables
| Member Variable | Type | Description |
|---|---|---|
| keyCode | int | Raw key code. Compare with constants in the Qwerty namespace (e.g. Qwerty::SpaceBar, Qwerty::Enter, Qwerty::UpArrow) or use the helpers below. |
| mods | int | Bitfield of held modifier keys. Use isCommandDown() / isShiftDown() / isAltDown(). |
| identifier | int | Identifier used internally to associate the event with its source. |
Key helpers
| Function | Returns | Description |
|---|---|---|
| getKeyCode() | int | Returns the raw key code. |
| isSpaceBar() | bool | true if the event fired from the space bar. |
| isEnter() | bool | true if the event fired from Enter / Return. |
| isTab() | bool | true if the event fired from Tab. |
| isDelete() | bool | true if the event fired from the Delete key. |
| isBackspace() | bool | true if the event fired from Backspace. |
| isUpArrow() | bool | true if the event fired from the up-arrow key. |
| isDownArrow() | bool | true if the event fired from the down-arrow key. |
| isLeftArrow() | bool | true if the event fired from the left-arrow key. |
| isRightArrow() | bool | true if the event fired from the right-arrow key. |
| isCharacterKey(string character) | bool | true if the event matches the printable character passed in (e.g. "a", "$", " "). |
Modifier-key helpers
| Function | Returns | Description |
|---|---|---|
| isCommandDown() | bool | true if Command / Ctrl was held during the keypress. |
| isShiftDown() | bool | true if Shift was held. |
| isAltDown() | bool | true if Alt / Option was held. |
GuiEvent
A GuiEvent is passed to your script when a GUI control with a declared GuiEvent fires — typically used by widgets to send arbitrary value-plus-identifier signals. Handle one by writing event <Name>(GuiEvent guiEvent) { ... }, where <Name> matches the event name declared in Figma.
Before event <Name>(GuiEvent ...) will compile, the same name must be declared in your Figma export. Add a "GuiEvent": "<Name>" property to a Component in your kdui. Without the declaration, the script compiler reports Event handler '<Name>' does not match an event input.
Example: React to a menu selection
event initialize() { setLogEnabled(true); } // Without this, debug.log() output is silent
event MenuChoice(GuiEvent guiEvent)
{
debug.log("Menu item index:", guiEvent.getValue());
}
Member Variables
| Member Variable | Type | Description |
|---|---|---|
| value | float | The payload value associated with the event. |
| identifier | int | Identifier used internally to associate the event with its source. |
Functions
| Function | Returns | Description |
|---|---|---|
| getValue() | float | Returns the event's value member variable. |
| getIdentifier() | int | Returns the event's identifier member variable. |
KeyRange
A KeyRange describes a span of MIDI notes with a name, colour, and tooltip — used to highlight regions of the GUI piano. Construct one directly by setting its member variables, then pass it to showKeyRange() to display it (or hideKeyRange() to remove it).
Example: Highlight the C4 octave on startup
event initialize()
{
KeyRange octave4;
octave4.startNote = 60; // C4
octave4.endNote = 71; // B4
octave4.name = "Octave 4";
octave4.colour = colours::cornflowerblue;
octave4.tooltip = "Middle C through B above";
showKeyRange(octave4);
}
Member Variables
| Member Variable | Type | Description |
|---|---|---|
| startNote | int | First MIDI note in the range (inclusive). Range: 0–127. |
| endNote | int | Last MIDI note in the range (inclusive). Range: 0–127. |
| name | string | Display name shown on or near the highlighted region. |
| colour | Colour | Highlight colour shown on the GUI piano. Build with a colours:: constructor or use a named constant (e.g. colours::orange). |
| tooltip | string | Tooltip text shown when the user hovers over the highlighted region. |
Functions
Highlight regions of the GUI piano and define the playable range.
| Function | Returns | Description |
|---|---|---|
| showKeyRange(KeyRange range) | void | Displays a key range on the GUI piano. Also available as show(KeyRange) as a shorter alias. |
| hideKeyRange(KeyRange range) | void | Hides a previously-shown key range. Also available as hide(KeyRange). |
| clearAllKeyRanges() | void | Removes every visible key range from the GUI piano. |
| clearPlayableRanges() | void | Removes every playable-range highlight shown via a container's showPlayableRange() (all Articulations, Variations, and Groups at once). Distinct from clearAllKeyRanges(), which clears KeyRange overlays. |
| setPlayableRange(int startNote, int endNote) | void | Sets the keyboard's playable MIDI-note range. Notes outside this range won't trigger. |
| setPlayableRange(int startNote, int endNote, Colour colour) | void | Same as above, plus a highlight Colour shown on the GUI piano for the playable region. |
Pattern
A Pattern is a queue of NoteData events with timing, transposition, and optional tempo-sync — use it to sequence playback. Build it once, then advance it from event process(), pulling out notes as they come due. Declare with Pattern mySeq;.
Building the queue
| Function | Returns | Description |
|---|---|---|
| addNote(NoteData note) | void | Queues a note, kept sorted by its delaySamples. Set the note's delay (setDelaySamples/setDelayMS/setDelaySeconds) to place it in time. |
| clear() | void | Empties the queue and resets playback position. |
| setTarget(Group group) setTarget(Variation variation) setTarget(Articulation articulation) |
void | Routes every queued note through a specific Group, Variation, or Articulation. |
Playback
| Function | Returns | Description |
|---|---|---|
| start() | void | Starts playback from the beginning. |
| start(int initialOffset) | void | Starts playback, offset by N samples. |
| pop(NoteData& note, float64 step) | bool | If the next queued note is due within step samples, writes it into note, advances the queue, and returns true; returns false when nothing is due this step. Call in a loop in process(). |
| setLooping(bool looping) | void | Whether playback repeats from the start. |
| setTempoSyncEnabled(bool tempoSyncEnabled) | void | Whether timing follows the host tempo (pair with setOriginalTempoBPM). |
| setOriginalTempoBPM(float64 tempoBPM) | void | The BPM the pattern was authored at (defaults to 120 if ≤ 0); used to scale timing when tempo-sync is on. |
| setInitialOffset(int offset) | void | Sample offset applied when playback starts. |
| setTransposition(int transposition) | void | Transposes every played note by N semitones; 0 = none. |
Inspection
| Function | Returns | Description |
|---|---|---|
| isActive() | bool | true while the pattern is playing. |
| isLooping() | bool | true if looping is enabled. |
Pattern mySeq;
event initialize()
{
NoteData first;
first.setNoteNumber(60);
first.setDelayMS(0.0f);
mySeq.addNote(first);
NoteData second;
second.setNoteNumber(64);
second.setDelayMS(250.0f);
mySeq.addNote(second);
mySeq.setLooping(true);
mySeq.start();
}
event process()
{
NoteData n;
while(mySeq.pop(n, getBlockSize()))
n.start();
}
Saving & Restoring State
KODA can persist script state across sessions (and with the host project). Declare a struct State holding the values you want saved; KODA serializes it automatically when the host saves, and calls event loadState(State state) when restoring so you can read the values back. Keep State fields to plain values (float, int, bool).
The save side is automatic — you only declare struct State and (optionally) implement event loadState.
State should hold plain value-typed fields and stay small; it's for lightweight session settings, not large buffers.
struct State
{
float wetMix;
int mode;
}
float currentWetMix = 0.5;
int currentMode = 0;
// Called when KODA restores saved state
event loadState(State state)
{
currentWetMix = state.wetMix;
currentMode = state.mode;
}