Skip to content
KODA
Reference

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.

Pre-release

KODA is in pre-release and the scripting language might change between versions.

Conventions used in this reference

FunctionReturnsDescription
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

Two ways to fetch

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 for loops 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 your noteOn handler describing each incoming MIDI note. You can modify it freely, build new ones via createNoteData(), and trigger them with start() or startNote().
  • Note — a handle to a note that's currently playing. You get one back from startNote(). 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

Same call, two ways

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.01.0; default 0.0, except where noted.

ParameterDefault CCDescription
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.

FunctionReturnsDescription
limit<Type>(Type value, Type min, Type max)TypeReturns 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.

Logging

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

FunctionReturnsDescription
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.

FunctionReturnsDescription
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.

FunctionReturnsDescription
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.

FunctionReturnsDescription
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).

FunctionReturnsDescription
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 (0getNumConvolutionFiles() - 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.

FunctionReturnsDescription
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.04.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.

FunctionReturnsDescription
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).
Taking manual control of routing

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.

FunctionReturnsDescription
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.

FunctionReturnsDescription
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.

FunctionReturnsDescription
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.

FunctionReturnsDescription
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.

FunctionReturnsDescription
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.

FunctionReturnsDescription
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.01.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

FunctionReturnsDescription
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

FunctionReturnsDescription
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();
}
Shared setter pattern

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

FunctionReturnsDescription
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

FunctionReturnsDescription
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.04.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.04.0.
getPan() float Returns the current pan position. Range: -1.01.0.

Tuning & transposition

Tuning shifts the pitch of a sample. Transposition offsets the position of the note to trigger a different sample.

FunctionReturnsDescription
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.

FunctionReturnsDescription
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.

FunctionReturnsDescription
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.

FunctionReturnsDescription
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.

FunctionReturnsDescription
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.

FunctionReturnsDescription
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.

FunctionReturnsDescription
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.

FunctionReturnsDescription
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());
}
Technical sidenote

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).

Pre-release: missing getters

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.01.0) and MIDI integer (0127) — that read and write the same underlying value.

FunctionReturnsDescription
setNoteNumber(int note) void Sets the MIDI note number. Range: 0127 (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.01.0 (clamped).
setMidiVelocity(int velocity) void Sets velocity from a MIDI value. Range: 0127.
getVelocity() float Returns velocity as a float (0.01.0).
getMidiVelocity() int Returns velocity as a MIDI integer (0127).
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

FunctionReturnsDescription
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.04.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.

FunctionReturnsDescription
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.52.0: 0.5 = convex (fast start, slow finish), 1.0 = linear, 2.0 = concave (slow start, fast finish).

FunctionReturnsDescription
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

FunctionReturnsDescription
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".

FunctionReturnsDescription
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).

FunctionReturnsDescription
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

FunctionReturnsDescription
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.
Pattern

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.

Absolute setters

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.

FunctionReturnsDescription
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.52.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

FunctionReturnsDescription
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.

Host MIDI only

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

FunctionReturnsDescription
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 VariableTypeDescription
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);
}
Per-note state lives in the script

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).

Send buses are mid-refactor

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);
}
Shared setter pattern

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

FunctionReturnsDescription
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

FunctionReturnsDescription
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.04.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.

FunctionReturnsDescription
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.

FunctionReturnsDescription
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.

FunctionReturnsDescription
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()).
Technical sidenote

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.

FunctionReturnsDescription
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.01.0; decibel values are floored at −60 dB.

FunctionReturnsDescription
getLeft()floatLeft channel, linear 0.01.0.
getRight()floatRight channel, linear 0.01.0.
getLeftDecibels()floatLeft channel in dB (floored at −60).
getRightDecibels()floatRight channel in dB (floored at −60).
getPeak()floatMax of left/right, linear 0.01.0.
getPeakDecibels()floatPeak in dB (floored at −60).
getAverage()floatMean of left/right, linear 0.01.0.
getAverageDecibels()floatAverage 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);
}
Shared setter pattern

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

FunctionReturnsDescription
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

FunctionReturnsDescription
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.04.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.

FunctionReturnsDescription
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).

Depth chain

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);
}
Shared setter pattern

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

FunctionReturnsDescription
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).

FunctionReturnsDescription
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.

FunctionReturnsDescription
setEnvelopePreDelay(float preDelay) void Sets pre-delay in seconds. 0.0 = no delay. Typical range 0.010.0 seconds; no hard clamp.
setEnvelopeAttack(float attack) void Sets attack time in seconds. 0.0 = instant. Typical range 0.0110.0 seconds.
setEnvelopeAttackCurve(float attackCurve) void Sets the attack curve exponent. Range: 0.52.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.010.0 seconds.
setEnvelopeDecay(float decay) void Sets decay time in seconds. 0.0 = instant. Typical range 0.010.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.010.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());
}
Technical sidenote

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

FunctionReturnsDescription
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

FunctionReturnsDescription
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.01.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.

FunctionReturnsDescription
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.252.0. Equivalent to setExponent().
setExponent(float exponent) void Sets a custom curve exponent directly. Clamped to 0.252.0.

Inspection

FunctionReturnsDescription
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

FunctionReturnsDescription
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

FunctionReturnsDescription
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).

FunctionReturnsDescription
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.

One Path per function

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.

FunctionReturnsDescription
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.

FunctionReturnsDescription
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.

FunctionReturnsDescription
setBounds(Bounds bounds)voidSets position and size from a Bounds.
setRotation(float radians)voidRotates about the centre; angle in radians ( = full turn), 0.0 = none.
setScale(float scale)voidUniform scale about the centre; 1.0 = original.
setScale(float scaleX, float scaleY)voidNon-uniform scale per axis; 1.0 = original on that axis.
bringToFront()voidRaises to the front of its sibling z-order.
getName()stringReturns the Component's control ID.

Text formatting

For Label, value-edit and text-edit Components.

FunctionReturnsDescription
setText(float value)voidSets text to a float (2 decimal places).
setText(float value, int numDecimals)voidSets text to a float with N decimals.
setText(int value)voidSets text to an integer.
clearText()voidClears the text.
setSuffix(string suffix)voidAppends a unit suffix to displayed text (e.g. " Hz").
setUnitType(int unitType)voidFormats 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)voidRestricts typeable characters on a text-edit (empty string restores default).
setFont(Font font)voidSets the font from a Font.

Effects & sprites

Attach visual effects and sprite sheets to a Component.

FunctionReturnsDescription
addEffect(Effect effect)voidAdds a visual effect (see Effect).
removeEffects()voidRemoves all effects.
setSprite(string path, int spriteWidth, int spriteHeight, int numFrames)voidAttaches a sprite sheet (per-frame size + frame count).
setSprite(SpriteSheet sprite)voidAttaches a sprite sheet from a SpriteSheet.

Containers & event routing

Nest Components and route their input to script event handlers.

FunctionReturnsDescription
addComponent(Component child)voidRe-parents a child into this (panel) Component so it moves and scales with it. Call on the parent: panel.addComponent(child).
sendMouseEvents(int identifier)voidRoutes 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)voidRoutes 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.

FunctionReturnsDescription
addItem(string name, int value)voidAdds an item (display label + user int reported on selection).
clearItems()voidRemoves all items.
setListSelection(int index)voidSelects a ListItemBox item by index (−1 clears).
clearMenuSelection()voidClears a Menu's selection (same as setMenuSelection(−1)).
setItemSize(int width, int height)voidPer-item pixel size.
setItemTextFont(Font font)voidItem text font.
setItemTextBounds(Bounds bounds)voidItem text layout bounds within each item.
setItemTextColour(Colour colour, int state)voidItem text colour per state; MenuItemState:: Normal=0, Hover=1, Selected=2.
setMenuBackgroundColour(Colour colour)voidMenu drop-down background.
setMenuOutlineColour(Colour colour)voidMenu drop-down border colour.
setMenuOutlineThickness(float thickness)voidMenu 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.

Note

It is not synced to the actual GUI framerate, so don't rely on it for precise timing. (It was previously named refresh.)

FunctionReturnsDescription
setViewRefreshRate(int hz)voidSets the viewRefresh rate; clamped 12–60 Hz (default 24). Only the instrument-level (root) script's setting takes effect.
getViewRefreshRate()intReturns 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).

FunctionReturnsDescription
createButton(string name, Bounds bounds)ComponentCreates a button.
createPanel(string name, Bounds bounds)ComponentCreates a panel; use as a container (addComponent) or a paint surface.
createRotarySlider(string name, Bounds bounds)ComponentCreates a rotary slider (knob).
createHorizontalSlider(string name, Bounds bounds)ComponentCreates a horizontal slider.
createVerticalSlider(string name, Bounds bounds)ComponentCreates a vertical slider.
createLabel(string name, Bounds bounds, string text)ComponentCreates a text label with initial text.
createXYPad(string name, Bounds bounds, string parameterX, string parameterY)ComponentCreates an XY pad bound to two parameters by name (one per axis).
createMenu(string name, Bounds bounds)ComponentCreates a drop-down menu; populate with addItem().
createListItemBox(string name, Bounds bounds)ComponentCreates a scrollable list; populate with addItem(), track with setListSelection().
createSearchField(string name, Bounds bounds)ComponentCreates a text/search entry field.
createStateDisplay(string name, Bounds bounds)ComponentCreates a state-display widget.
createAnimation(string name, Bounds bounds)ComponentCreates 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.

FunctionReturnsDescription
colours::fromRGB(int r, int g, int b)ColourOpaque colour from 8-bit channels (each 0255).
colours::fromRGBA(int r, int g, int b, int a)ColourFrom 8-bit channels plus alpha (each 0255). An overload takes a float alpha 0.01.0.
colours::fromRGB(int hex)ColourFrom a 24-bit hex RGB literal (e.g. 0xFF7F00), opaque.
colours::fromHex(int64 hex)ColourFrom 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)ColourFrom float channels (each 0.01.0). A 3-arg overload omits alpha and returns an opaque colour.
colours::fromHSB(float h, float s, float b)ColourOpaque colour from HSB components (each 0.01.0). fromHSBA adds an alpha argument.
colours::fromHSL(float h, float s, float l)ColourOpaque colour from HSL components (each 0.01.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.

FunctionReturnsDescription
getAlpha() / getRed() / getGreen() / getBlue()intChannel value 0255.
getAlphaFloat() / getRedFloat() / getGreenFloat() / getBlueFloat()floatChannel value 0.01.0.
getHue() / getSaturation() / getBrightness()floatHSB component 0.01.0.

Transforms

Each transform returns an immutable copy; the original colour is unchanged.

FunctionReturnsDescription
withAlpha(float a)ColourCopy with alpha set (0.01.0). An int overload withAlpha(int a) takes 0255.
withMultipliedAlpha(float m)ColourCopy with the current alpha multiplied by m.
brighter(float amt)ColourCopy blended toward white (0.0 = unchanged, 1.0 = white).
darker(float amt)ColourCopy blended toward black (0.0 = unchanged, 1.0 = black).
withHue(float h) / withSaturation(float s) / withBrightness(float b)ColourCopy with one HSB component replaced (each 0.01.0).
interpolatedWith(Colour other, float t)ColourLinear blend; t 0.0 = this colour, 1.0 = other.
overlaidOn(Colour background)ColourAlpha-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.

Colour format

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.

Target Component setup

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 VariableTypeDescription
xfloatX coordinate in pixels.
yfloatY coordinate in pixels.
Bounds — a position + size rectangle
Member VariableTypeDescription
xfloatX position of the top-left corner.
yfloatY position of the top-left corner.
widthfloatWidth in pixels.
heightfloatHeight in pixels.

Ellipse

An ellipse shape. Draw it with fill(ellipse, …) and/or stroke(ellipse, …), passing a Colour or a Gradient.

Member VariableTypeDescription
xfloatX position of the ellipse's bounding box.
yfloatY position of the ellipse's bounding box.
widthfloatBounding-box width (full diameter on the x-axis).
heightfloatBounding-box height (full diameter on the y-axis).
strokeWidthfloatOutline 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 VariableTypeDescription
xfloatX position of the top-left corner.
yfloatY position of the top-left corner.
widthfloatWidth in pixels.
heightfloatHeight in pixels.
cornerRadiusfloatRadius for rounded corners in pixels. 0.0 = square corners.
strokeWidthfloatOutline 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 VariableTypeDescription
xfloatX position of the arc's bounding box.
yfloatY position of the arc's bounding box.
widthfloatBounding-box width.
heightfloatBounding-box height.
startAnglefloatStart angle in radians. 0 = 3 o'clock; positive = clockwise.
endAnglefloatEnd angle in radians.
strokeWidthfloatOutline 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 VariableTypeDescription
strokeThicknessfloatStroke thickness in pixels (for strokePath()).
jointStyleintHow stroked corners join: JointStyle::mitered = 0, curved = 1, beveled = 2.
endCapStyleintHow stroked ends are capped: EndCapStyle::butt = 0, square = 1, rounded = 2.
Build functions
FunctionReturnsDescription
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).

Gradient types

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 VariableTypeDescription
typeintGradientType::Linear / Radial / Angular / Diamond. Default 0 = Linear.
x1floatX of the gradient start point (centre for Radial).
y1floatY of the gradient start point (centre for Radial).
x2floatX of the gradient end point (edge for Radial).
y2floatY of the gradient end point (edge for Radial).
Functions
FunctionReturnsDescription
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 VariableTypeDescription
imagestringImage identifier — the name of the asset to draw.
xfloatX position of the top-left corner.
yfloatY position of the top-left corner.
widthfloatDisplay width in pixels.
heightfloatDisplay 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 VariableTypeDescription
textstringThe string to render.
fontstringFont family name (e.g. "Inter"). Must be available to the instrument.
fontSizefloatFont size in points.
fontColourColourText colour (ARGB).
xfloatX position of the text's bounding box.
yfloatY position of the text's bounding box.
widthfloatBounding-box width (text wraps if it overflows).
heightfloatBounding-box height.

Font

A font spec for label-style widgets, passed to setFont() / setItemTextFont().

Font — a label/value font spec
Member VariableTypeDescription
namestringFont family name (e.g. "Inter").
stylestringStyle name: "Regular", "Bold", "Italic", "Bold Italic", etc.
heightfloatPoint height. A value ≤ 0 keeps the widget's current height.
justificationintText alignment; Justification:: constants: Keep=0 (no change), Left=1, Center=2, Right=3, plus combined TopLeft=4BottomRight=12 (CenterLeft=7, Centered=8, CenterRight=9).
kerningfloatExtra inter-character spacing factor (0.0 = default).
colourColourText 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 VariableTypeDescription
typeintEffect 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).
radiusfloatBlur/spread radius in pixels.
tintColourShadow/glow colour (DropShadow/Glow) or tint.
offsetXfloatHorizontal shadow offset in pixels (DropShadow).
offsetYfloatVertical 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;.

FunctionReturnsDescription
fill(Rectangle r, Colour|Gradient)boolRecords a filled rectangle. Same overloads for Ellipse and Arc. Returns false if the canvas is full.
stroke(Rectangle r, Colour|Gradient)boolRecords a stroked rectangle. Same overloads for Ellipse and Arc. Returns false if the canvas is full.
fillPath(Path p, Colour|Gradient)boolRecords a filled path. Returns false if the canvas is full.
strokePath(Path p, Colour|Gradient)boolRecords a stroked path. Returns false if the canvas is full.
paint(Text text)boolRecords text. Returns false if the canvas is full.
paint(Image image)boolRecords an image. Returns false if the canvas is full.
clear()voidClears 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 VariableTypeDescription
pathstringImage asset name.
widthintPer-frame width in pixels.
heightintPer-frame height in pixels.
framesintTotal 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.

Target widget setup

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 VariableTypeDescription
xintX position of the mouse relative to the Component's origin.
yintY position of the mouse relative to the Component's origin.
modsintBitfield of held modifier keys. Compare with MouseModifiers::Command, ::Shift, ::Alt — or use the isCommandDown() / isShiftDown() / isAltDown() helpers below.
rightClickintNon-zero when the press is a right-click. Prefer isRightClick() / isLeftClick().
typeMouseEventTypeWhat kind of mouse action this is. One of Down, Up, Enter, Exit, DoubleClick, StartDrag, Drag, DragEnter, DragExit, Drop.
identifierintIdentifier used internally to associate the event with its source. Most scripts can ignore this.

Modifier-key helpers

FunctionReturnsDescription
isCommandDown()booltrue if Command (⌘ on macOS, Ctrl on Windows) was held during the event.
isShiftDown()booltrue if Shift was held.
isAltDown()booltrue if Alt / Option was held.

Event-type helpers

FunctionReturnsDescription
isLeftClick()booltrue on a left-button press (Down event, non-right-click).
isRightClick()booltrue on a right-button press (Down event, right-click).
isDown()booltrue if type is Down.
isUp()booltrue if type is Up.
isEnter()booltrue if the mouse entered the Component's bounds.
isExit()booltrue if the mouse left the Component's bounds.
isDoubleClick()booltrue on a double-click.
isStartDrag()booltrue when a drag operation starts.
isDrag()booltrue if this is a drag event (mouse moved while a button is held).
isDragEnter()booltrue when a drag enters the Component's bounds.
isDragExit()booltrue when a drag leaves the Component's bounds.
isDrop()booltrue 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.

Feature not yet end-to-end verified

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 VariableTypeDescription
keyCodeintRaw key code. Compare with constants in the Qwerty namespace (e.g. Qwerty::SpaceBar, Qwerty::Enter, Qwerty::UpArrow) or use the helpers below.
modsintBitfield of held modifier keys. Use isCommandDown() / isShiftDown() / isAltDown().
identifierintIdentifier used internally to associate the event with its source.

Key helpers

FunctionReturnsDescription
getKeyCode()intReturns the raw key code.
isSpaceBar()booltrue if the event fired from the space bar.
isEnter()booltrue if the event fired from Enter / Return.
isTab()booltrue if the event fired from Tab.
isDelete()booltrue if the event fired from the Delete key.
isBackspace()booltrue if the event fired from Backspace.
isUpArrow()booltrue if the event fired from the up-arrow key.
isDownArrow()booltrue if the event fired from the down-arrow key.
isLeftArrow()booltrue if the event fired from the left-arrow key.
isRightArrow()booltrue if the event fired from the right-arrow key.
isCharacterKey(string character)booltrue if the event matches the printable character passed in (e.g. "a", "$", " ").

Modifier-key helpers

FunctionReturnsDescription
isCommandDown()booltrue if Command / Ctrl was held during the keypress.
isShiftDown()booltrue if Shift was held.
isAltDown()booltrue 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.

Required UI declaration

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 VariableTypeDescription
valuefloatThe payload value associated with the event.
identifierintIdentifier used internally to associate the event with its source.

Functions

FunctionReturnsDescription
getValue()floatReturns the event's value member variable.
getIdentifier()intReturns 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 VariableTypeDescription
startNoteintFirst MIDI note in the range (inclusive). Range: 0127.
endNoteintLast MIDI note in the range (inclusive). Range: 0127.
namestringDisplay name shown on or near the highlighted region.
colourColourHighlight colour shown on the GUI piano. Build with a colours:: constructor or use a named constant (e.g. colours::orange).
tooltipstringTooltip text shown when the user hovers over the highlighted region.

Functions

Highlight regions of the GUI piano and define the playable range.

FunctionReturnsDescription
showKeyRange(KeyRange range)voidDisplays a key range on the GUI piano. Also available as show(KeyRange) as a shorter alias.
hideKeyRange(KeyRange range)voidHides a previously-shown key range. Also available as hide(KeyRange).
clearAllKeyRanges()voidRemoves every visible key range from the GUI piano.
clearPlayableRanges()voidRemoves 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)voidSets the keyboard's playable MIDI-note range. Notes outside this range won't trigger.
setPlayableRange(int startNote, int endNote, Colour colour)voidSame 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

FunctionReturnsDescription
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

FunctionReturnsDescription
start()voidStarts playback from the beginning.
start(int initialOffset)voidStarts 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)voidWhether playback repeats from the start.
setTempoSyncEnabled(bool tempoSyncEnabled)voidWhether timing follows the host tempo (pair with setOriginalTempoBPM).
setOriginalTempoBPM(float64 tempoBPM)voidThe BPM the pattern was authored at (defaults to 120 if ≤ 0); used to scale timing when tempo-sync is on.
setInitialOffset(int offset)voidSample offset applied when playback starts.
setTransposition(int transposition)voidTransposes every played note by N semitones; 0 = none.

Inspection

FunctionReturnsDescription
isActive()booltrue while the pattern is playing.
isLooping()booltrue 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.

Keep it simple

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;
}