ExpressionControl
The script half of the controller pattern: reading a slider, checkbox, colour, point, dropdown, angle or layer control.
An expression control handle (the controller pattern's script half). value and a dropdown's text are LIVE getters (the track's sample at the fold frame, else the base) in WIRE units: slider/angle number, dropdown the 0-based index, point [x, y], colour [r, g, b, a] in 0..1, checkbox boolean, layer a Layer handle or null.
id
Type: string (field).
The control id ("xcontrol_").
Worked example: Stable ids under any name
The scene. A gold sun near the centre, a small blue planet above it, and a hidden Rig null carrying the expression controls that steer the scene.
- The comp Orbit: 640 by 360 at 25 fps, 200 frames (8 seconds).
- Sun: a solid layer, at [320, 180], 80 by 80, filled f2a833.
- Planet: a solid layer, at [320, 60], 30 by 30, filled 4d9de0.
- Rig: a null (an invisible controller host), at [320, 180].
- An expression control: Speed, a slider (0 to 100, step 1, unit %), set to 40.
- An expression control: Show planet, a checkbox, set to true.
- An expression control: Start angle, a angle, set to 90.
- An expression control: Direction, a dropdown (items Clockwise / Anticlockwise), set to 0.
- An expression control: Centre, a point, set to [320, 180].
- An expression control: Target, a layer, set to layer_2.
The script. Add a project script named Id proof and paste:
export const outputs = ["layer_1:transform.rotation"];
export function frame() {
const start = control("Start angle");
return start.id === "xcontrol_3" ? 30 : 0;
}
Line by line:
- Lines 4-5: Expression controls carry document-unique ids. The editor SHOWS the control's name in the control() call and heals a typed name into the id on Apply, but what is stored - and what id answers - is always the stable id, so renaming Start angle can never break this script. The check passes and the Sun turns 30 degrees.
What you see. The sun square rests turned 30 degrees - the receipt that the control's stored identity survives any renaming.
name
Type: string (field).
The display name (the kind's default label when unnamed).
Worked example: Read the control's label
The scene. A gold sun near the centre, a small blue planet above it, and a hidden Rig null carrying the expression controls that steer the scene.
- The comp Orbit: 640 by 360 at 25 fps, 200 frames (8 seconds).
- Sun: a solid layer, at [320, 180], 80 by 80, filled f2a833.
- Planet: a solid layer, at [320, 60], 30 by 30, filled 4d9de0.
- Rig: a null (an invisible controller host), at [320, 180].
- An expression control: Speed, a slider (0 to 100, step 1, unit %), set to 40.
- An expression control: Show planet, a checkbox, set to true.
- An expression control: Start angle, a angle, set to 90.
- An expression control: Direction, a dropdown (items Clockwise / Anticlockwise), set to 0.
- An expression control: Centre, a point, set to [320, 180].
- An expression control: Target, a layer, set to layer_2.
The script. Add a project script named Label glow and paste:
export const outputs = ["layer_2:transform.opacity"];
export function frame() {
const speed = control("Speed");
return 40 + speed.name.length * 10;
}
Line by line:
- Lines 4-5: name is the label the control tile shows - "Speed", five characters, resting the Planet at 90 percent. A control that was never named answers its kind's default label (a bare checkbox reads "checkbox").
What you see. The planet rests at 90 percent opacity - forty as the floor plus ten per letter of the Speed control's name.
kind
Type: 'slider' | 'checkbox' | 'colour' | 'point' | 'layer' | 'dropdown' | 'angle' (field).
The control kind.
Worked example: One script, many control kinds
The scene. A gold sun near the centre, a small blue planet above it, and a hidden Rig null carrying the expression controls that steer the scene.
- The comp Orbit: 640 by 360 at 25 fps, 200 frames (8 seconds).
- Sun: a solid layer, at [320, 180], 80 by 80, filled f2a833.
- Planet: a solid layer, at [320, 60], 30 by 30, filled 4d9de0.
- Rig: a null (an invisible controller host), at [320, 180].
- An expression control: Speed, a slider (0 to 100, step 1, unit %), set to 40.
- An expression control: Show planet, a checkbox, set to true.
- An expression control: Start angle, a angle, set to 90.
- An expression control: Direction, a dropdown (items Clockwise / Anticlockwise), set to 0.
- An expression control: Centre, a point, set to [320, 180].
- An expression control: Target, a layer, set to layer_2.
The script. Add a project script named Kind dispatch and paste:
export const outputs = ["layer_2:transform.scale"];
export function frame() {
const toggle = control("Show planet");
return toggle.kind === "checkbox" ? [120, 120] : [80, 80];
}
Line by line:
- Lines 4-5: kind names one of the seven control kinds - slider, checkbox, colour, point, layer, dropdown or angle - and with it the shape .value will take. Show planet is a checkbox, so the wide branch wins and the Planet renders at 120 percent. Dispatching on kind lets one helper script accept whatever control it is pointed at.
What you see. The planet renders at 120 percent scale - the branch for a checkbox control, picked by inspecting the control's kind.
layer
Type: string (field).
The owning layer's id.
Worked example: Find the control's home
The scene. A gold sun near the centre, a small blue planet above it, and a hidden Rig null carrying the expression controls that steer the scene.
- The comp Orbit: 640 by 360 at 25 fps, 200 frames (8 seconds).
- Sun: a solid layer, at [320, 180], 80 by 80, filled f2a833.
- Planet: a solid layer, at [320, 60], 30 by 30, filled 4d9de0.
- Rig: a null (an invisible controller host), at [320, 180].
- An expression control: Speed, a slider (0 to 100, step 1, unit %), set to 40.
- An expression control: Show planet, a checkbox, set to true.
- An expression control: Start angle, a angle, set to 90.
- An expression control: Direction, a dropdown (items Clockwise / Anticlockwise), set to 0.
- An expression control: Centre, a point, set to [320, 180].
- An expression control: Target, a layer, set to layer_2.
The script. Add a project script named Rig altitude and paste:
export const outputs = ["layer_2:transform.position"];
export function frame() {
const speed = control("Speed");
const host = layer(speed.layer);
return [value[0], host.position.value[1] - 100];
}
Line by line:
- Lines 4-5: layer is the id of the layer hosting the control - controls always live on a layer, conventionally a null acting as the rig. Handing that id to layer() climbs from the control to its home.
- Line 6: The Planet floats 100 pixels above wherever the Rig sits: move the rig null and the planet's perch moves with it.
What you see. The planet floats exactly 100 pixels above the Rig null - drag the rig around the stage and the planet keeps its perch.
enabled
Type: boolean (field).
The display flag (an unused control still evaluates).
Worked example: The eye greys, scripts still read
The scene. A gold sun near the centre, a small blue planet above it, and a hidden Rig null carrying the expression controls that steer the scene.
- The comp Orbit: 640 by 360 at 25 fps, 200 frames (8 seconds).
- Sun: a solid layer, at [320, 180], 80 by 80, filled f2a833.
- Planet: a solid layer, at [320, 60], 30 by 30, filled 4d9de0.
- Rig: a null (an invisible controller host), at [320, 180].
- An expression control: Speed, a slider (0 to 100, step 1, unit %), set to 40.
- An expression control: Show planet, a checkbox, set to true.
- An expression control: Start angle, a angle, set to 90.
- An expression control: Direction, a dropdown (items Clockwise / Anticlockwise), set to 0.
- An expression control: Centre, a point, set to [320, 180].
- An expression control: Target, a layer, set to layer_2.
The script. Add a project script named Eye check and paste:
export const outputs = ["layer_2:transform.opacity"];
export function frame() {
const speed = control("Speed");
return speed.enabled ? 90 : 20;
}
Line by line:
- Lines 4-5: enabled mirrors the control tile's eye toggle. IMPORTANT: the eye only greys the tile for humans - a disabled control still evaluates, still keyframes and still answers .value. Read enabled when you want your script to HONOUR the eye; ignore it and the control keeps working regardless.
What you see. The planet rests at 90 percent while the Speed control's eye is on - this script chooses to dim to 20 if someone greys the control, even though the value itself would keep flowing.
min
Type: number (field).
The slider/angle minimum (settings).
Worked example: The slider's floor as data
The scene. A gold sun near the centre, a small blue planet above it, and a hidden Rig null carrying the expression controls that steer the scene.
- The comp Orbit: 640 by 360 at 25 fps, 200 frames (8 seconds).
- Sun: a solid layer, at [320, 180], 80 by 80, filled f2a833.
- Planet: a solid layer, at [320, 60], 30 by 30, filled 4d9de0.
- Rig: a null (an invisible controller host), at [320, 180].
- An expression control: Speed, a slider (0 to 100, step 1, unit %), set to 40.
- An expression control: Show planet, a checkbox, set to true.
- An expression control: Start angle, a angle, set to 90.
- An expression control: Direction, a dropdown (items Clockwise / Anticlockwise), set to 0.
- An expression control: Centre, a point, set to [320, 180].
- An expression control: Target, a layer, set to layer_2.
The script. Add a project script named Floor size and paste:
export const outputs = ["layer_2:transform.scale"];
export function frame() {
const speed = control("Speed");
return [speed.min + 80, speed.min + 80];
}
Line by line:
- Lines 4-5: min is the slider's configured lower bound - 0 on Speed, so the Planet renders at 80 percent. min, max, step and unit are the SETTINGS a slider was given, readable as plain data; the editor clamps the slider's own value into [min, max] as you scrub it.
What you see. The planet renders at 80 percent scale - the Speed slider's floor of zero plus eighty.
max
Type: number (field).
The slider/angle maximum (settings).
Worked example: The slider's ceiling as data
The scene. A gold sun near the centre, a small blue planet above it, and a hidden Rig null carrying the expression controls that steer the scene.
- The comp Orbit: 640 by 360 at 25 fps, 200 frames (8 seconds).
- Sun: a solid layer, at [320, 180], 80 by 80, filled f2a833.
- Planet: a solid layer, at [320, 60], 30 by 30, filled 4d9de0.
- Rig: a null (an invisible controller host), at [320, 180].
- An expression control: Speed, a slider (0 to 100, step 1, unit %), set to 40.
- An expression control: Show planet, a checkbox, set to true.
- An expression control: Start angle, a angle, set to 90.
- An expression control: Direction, a dropdown (items Clockwise / Anticlockwise), set to 0.
- An expression control: Centre, a point, set to [320, 180].
- An expression control: Target, a layer, set to layer_2.
The script. Add a project script named Half ceiling and paste:
export const outputs = ["layer_1:transform.rotation"];
export function frame() {
return control("Speed").max / 2;
}
Line by line:
- Line 4: max is the slider's configured upper bound - 100 on Speed, so the Sun turns a steady 50 degrees. Reading the bounds instead of hard-coding them means retuning the slider's range in the panel retunes the script with it.
What you see. The sun stands turned 50 degrees - half the Speed slider's ceiling, and it re-tunes itself if the slider's range is edited.
step
Type: number (field).
The slider step (settings).
Worked example: Snap to the slider's grid
The scene. A gold sun near the centre, a small blue planet above it, and a hidden Rig null carrying the expression controls that steer the scene.
- The comp Orbit: 640 by 360 at 25 fps, 200 frames (8 seconds).
- Sun: a solid layer, at [320, 180], 80 by 80, filled f2a833.
- Planet: a solid layer, at [320, 60], 30 by 30, filled 4d9de0.
- Rig: a null (an invisible controller host), at [320, 180].
- An expression control: Speed, a slider (0 to 100, step 1, unit %), set to 40.
- An expression control: Show planet, a checkbox, set to true.
- An expression control: Start angle, a angle, set to 90.
- An expression control: Direction, a dropdown (items Clockwise / Anticlockwise), set to 0.
- An expression control: Centre, a point, set to [320, 180].
- An expression control: Target, a layer, set to layer_2.
The script. Add a project script named Step lift and paste:
export const outputs = ["layer_2:transform.position"];
export function frame() {
const speed = control("Speed");
const notches = 20 / speed.step;
return [value[0], value[1] - notches];
}
Line by line:
- Lines 4-5: step is the slider's scrub increment - 1 on Speed, so twenty units are twenty notches. A script can respect the same grid the slider snaps to, keeping script-made and hand-made values on one lattice.
- Line 6: The Planet lifts one pixel per notch, twenty in all.
What you see. The planet floats 20 pixels above its resting spot - one pixel for each notch the Speed slider would click through across twenty units.
unit
Type: string (field).
The unit label (settings; "" when none).
Worked example: Honour the declared unit
The scene. A gold sun near the centre, a small blue planet above it, and a hidden Rig null carrying the expression controls that steer the scene.
- The comp Orbit: 640 by 360 at 25 fps, 200 frames (8 seconds).
- Sun: a solid layer, at [320, 180], 80 by 80, filled f2a833.
- Planet: a solid layer, at [320, 60], 30 by 30, filled 4d9de0.
- Rig: a null (an invisible controller host), at [320, 180].
- An expression control: Speed, a slider (0 to 100, step 1, unit %), set to 40.
- An expression control: Show planet, a checkbox, set to true.
- An expression control: Start angle, a angle, set to 90.
- An expression control: Direction, a dropdown (items Clockwise / Anticlockwise), set to 0.
- An expression control: Centre, a point, set to [320, 180].
- An expression control: Target, a layer, set to layer_2.
The script. Add a project script named Unit gate and paste:
export const outputs = ["layer_2:transform.opacity"];
export function frame() {
const speed = control("Speed");
return speed.unit === "%" ? 65 : 100;
}
Line by line:
- Lines 4-5: unit is the display suffix the slider was configured with - "%" on Speed (an empty string when none was set). It is a hint for humans, but a careful script can branch on it to interpret the number the way the rig's author intended.
What you see. The planet rests at 65 percent - the branch taken because the Speed slider declares itself a percentage.
items
Type: string[] (field).
A dropdown's items ([] for other kinds).
Worked example: The dropdown's menu as data
The scene. A gold sun near the centre, a small blue planet above it, and a hidden Rig null carrying the expression controls that steer the scene.
- The comp Orbit: 640 by 360 at 25 fps, 200 frames (8 seconds).
- Sun: a solid layer, at [320, 180], 80 by 80, filled f2a833.
- Planet: a solid layer, at [320, 60], 30 by 30, filled 4d9de0.
- Rig: a null (an invisible controller host), at [320, 180].
- An expression control: Speed, a slider (0 to 100, step 1, unit %), set to 40.
- An expression control: Show planet, a checkbox, set to true.
- An expression control: Start angle, a angle, set to 90.
- An expression control: Direction, a dropdown (items Clockwise / Anticlockwise), set to 0.
- An expression control: Centre, a point, set to [320, 180].
- An expression control: Target, a layer, set to layer_2.
The script. Add a project script named Menu census and paste:
export const outputs = ["layer_1:transform.rotation"];
export function frame() {
const direction = control("Direction");
return direction.items.length * 20;
}
Line by line:
- Lines 4-5: items is a dropdown's menu as an array of strings (empty for every other kind). Direction offers Clockwise and Anticlockwise - two entries, so the Sun turns 40 degrees. Reading the menu lets a script stay correct when options are added later.
What you see. The sun stands turned 40 degrees - twenty for each entry in the Direction dropdown's menu.
value
Type: T (accessor).
The LIVE effective value at the fold frame in wire units (see the class doc).
Determinism: pure.
Worked example: The live dial
The scene. A gold sun near the centre, a small blue planet above it, and a hidden Rig null carrying the expression controls that steer the scene.
- The comp Orbit: 640 by 360 at 25 fps, 200 frames (8 seconds).
- Sun: a solid layer, at [320, 180], 80 by 80, filled f2a833.
- Planet: a solid layer, at [320, 60], 30 by 30, filled 4d9de0.
- Rig: a null (an invisible controller host), at [320, 180].
- An expression control: Speed, a slider (0 to 100, step 1, unit %), set to 40.
- An expression control: Show planet, a checkbox, set to true.
- An expression control: Start angle, a angle, set to 90.
- An expression control: Direction, a dropdown (items Clockwise / Anticlockwise), set to 0.
- An expression control: Centre, a point, set to [320, 180].
- An expression control: Target, a layer, set to layer_2.
The script. Add a project script named Speed dial and paste:
export const outputs = ["layer_1:transform.rotation"];
export function frame() {
return control("Speed").value;
}
Line by line:
- Line 4: value is the control's LIVE reading at the current frame, in the same units the panel shows: sliders and angles answer numbers, a checkbox true or false, a point [x, y], a colour [r, g, b, a] in 0..1, a dropdown its 0-based index, and a layer control a Layer handle or null. Keyframe the control and the read animates; scrub it and the Sun turns under your pointer. This one line is the whole controller pattern: the control is the surface, the script is the wiring.
What you see. The sun stands turned 40 degrees - the Speed slider's value read as an angle. Scrub the slider and the sun turns live under your hand.
text
Type: string | null (accessor).
A dropdown's item at the sampled index; null out of range or for other kinds.
Determinism: pure.
Worked example: The dropdown's words
The scene. A gold sun near the centre, a small blue planet above it, and a hidden Rig null carrying the expression controls that steer the scene.
- The comp Orbit: 640 by 360 at 25 fps, 200 frames (8 seconds).
- Sun: a solid layer, at [320, 180], 80 by 80, filled f2a833.
- Planet: a solid layer, at [320, 60], 30 by 30, filled 4d9de0.
- Rig: a null (an invisible controller host), at [320, 180].
- An expression control: Speed, a slider (0 to 100, step 1, unit %), set to 40.
- An expression control: Show planet, a checkbox, set to true.
- An expression control: Start angle, a angle, set to 90.
- An expression control: Direction, a dropdown (items Clockwise / Anticlockwise), set to 0.
- An expression control: Centre, a point, set to [320, 180].
- An expression control: Target, a layer, set to layer_2.
The script. Add a project script named Direction gate and paste:
export const outputs = ["layer_2:transform.opacity"];
export function frame() {
const direction = control("Direction");
return direction.text === "Clockwise" ? 80 : 30;
}
Line by line:
- Lines 4-5: text answers the dropdown's SELECTED item as its string - the menu entry at the current index - and null when the index is out of range or the control is not a dropdown. Branching on the words reads clearly; branching on .value (the index) survives the menu being reworded. Direction rests on Clockwise, so the bright branch wins.
What you see. The planet rests at 80 percent while the Direction dropdown says Clockwise - and dims to 30 the moment the menu is switched to Anticlockwise.