Runtime Bindings
Runtime Bindings
Some audio attributes accept a binding rather than a plain number: the value you configure can be a literal, a direct lookup of a property on the triggering event, or a full expression evaluated against live game state. The form is chosen automatically from what you write, so the simple cases stay simple and the dynamic cases are available when you need them.
Where Bindings Apply
| Consumer | Binding attributes |
|---|---|
source "synth" inside a route block | pitch, velocity |
effect blocks inside synth_effects | feedback, wet_level (reverb); low_gain, mid_gain, high_gain, low_freq, high_freq (equalizer); threshold, ratio, makeup_gain (compressor) |
Only these attributes are bindings. On a synth source, the sibling attributes weight, duration, sustain, and the position block take literal values only. And the same effect block written inside a mixer channel or bus takes literal numbers — expression parameters are accepted only in a top-level synth_effects block.
synth_triggeris retired. Bindings used to be written on asynth_triggerblock. That block is no longer authorable — a.cadefile containing one fails to load with an error pointing at the replacement. Author the voice as a top-levelroutewith asource "synth"instead;pitchandvelocityexpressions carry over verbatim. See Synthesis.
Binding Forms
A binding attribute accepts three forms. You do not declare which one you are using — Cade picks the form from the value.
Literal — a fixed number. No work happens at trigger time beyond returning the value.
route {
owners = [event_handler.bumper_hit_sound]
source "synth" "bumper_hit" {
weight = 1
pitch = 880
velocity = 1.0
}
}Event-property lookup — from:event.<field> reads a numeric property off the triggering event. This is the one binding form that must be quoted, since from:event.velocity is not valid bare syntax.
route {
owners = [event_handler.bumper_hit_sound]
source "synth" "bumper_hit" {
weight = 1
pitch = "from:event.pitch"
velocity = "from:event.velocity"
}
}from:event. is the only lookup prefix — there is no from:var. or from:signal.. To read a variable, write it directly as an expression.
Expression — anything that is not a literal or a from: lookup is evaluated as an expression against live game state. Write the expression bare, as everywhere else in the config; a fully quoted expression parses to the same thing and is accepted for back-compat.
route {
owners = [score.jackpot]
source "synth" "chime" {
weight = 1
pitch = 440 + (score / 1000000) * 220
velocity = min(var.bumper_count / 5, 1.0)
}
}Effect parameters take the same forms:
synth_effects "ball_fx" {
effect "reverb" {
feedback = var.tension * 0.5 # expression
wet_level = 0.3 # literal
}
}Expression Namespace
Inside an expression-form binding, the following identifiers are available.
| Identifier | Value |
|---|---|
score | The active player’s current score. A user variable named score cannot shadow this built-in — score is a reserved name. |
var.<name> | The named game variable’s current value. |
event.<field> | A numeric property on the triggering event, for example event.velocity. |
signal.<name> is recognized by the binding syntax but is not yet available in a running table — no shipped host supplies signal state to the audio binding context, so a binding that references signal.<name> fails at trigger time and the sound is skipped. Gate on signals with a condition on the owning handler instead.
For the operators, comparisons, ternary, and math functions usable inside these expressions, see Console Expressions. Note that binding arithmetic is floating-point — unlike the integer-first arithmetic of scoring expressions — so score / 1000000 keeps its fractional part here.
Evaluation Model
A binding is compiled once and then resolved on every trigger. At resolution, every identifier reflects state at that exact instant:
scoreis the active player’s score right now.var.<name>is the variable’s current value.event.<field>reads from the event that caused this trigger.
Literal and event-property bindings do effectively no work at trigger time. Expression bindings cost a full evaluation, so for parameters on a hot path prefer a literal or an event-property lookup unless the parameter genuinely needs cross-cutting context — for example scaling velocity by a current multiplier.
Error Handling
Bindings fail closed. When a binding cannot resolve, the value is not substituted with zero — the trigger is skipped and a warning is logged, so the sound simply does not play.
| Condition | When surfaced | Result |
|---|---|---|
Malformed expression syntax in a route’s pitch / velocity | cade validate and config load | Validation error naming the synth and the field |
Malformed expression syntax in a synth_effects parameter | Config apply | The effects block is skipped with a logged warning |
Reference to an undefined variable, or to signal.<name> | Trigger time | Trigger skipped, warning logged |
from:event.<field> naming a field the event does not carry | Trigger time | Trigger skipped, warning logged |
| Expression producing a non-numeric result | Trigger time | Trigger skipped, warning logged |
Only syntax is checked ahead of time. A binding that references a variable you later renamed still validates cleanly and only goes quiet at runtime — so if a sound stops playing after a rename, check the binding’s identifiers first.