Skip to content

What a script can reach

Everything below is available inside the hooks, without importing anything. Nothing else is: see The fence.

log

The only way a script writes anything out. Lines land in the browser console, prefixed with the cue that produced them ([script] 12 Intro music).

function onStart() {
    log.info('starting');
    log.warning('the room is loud');
    log.error('that should not have happened');
}

Each takes any number of arguments, like console.log.

startTime and time

Two plain variables the engine refreshes before every hook and every timer callback:

VariableWhat it holds
startTimeEpoch milliseconds when the cue started playing — after its delay, the moment onStart ran
timeSeconds since then
function onTick() {
    if (time > 5) log.info('five seconds in');
}

Assigning to them does nothing useful: the next call overwrites what you wrote. A script that declares its own time or startTime fails to compile (“Identifier ‘time’ has already been declared”), which is the clearest way to find out the name is taken.

after(duration, callback)

Run something once, duration seconds from now.

function onStart() {
    after(2, function () {
        this.level.set(0.2);
    });
}

every(interval, callback)

Run something over and over. The first argument is either the interval in seconds, or an object: interval is the gap, delay is how long to wait before the first run and defaults to one whole interval. The callback is given its run index, counting from 0.

function onStart() {
    // The short form: every two seconds, first run two seconds in.
    every(2, function (index) {
        log.info('beat', index);
    });
}
function onStart() {
    // Right away, then every half second.
    every({ interval: 0.5, delay: 0 }, function (index) {
        this.textColor.set(index % 2 === 0 ? '#ff0000' : '#ffffff');
    });
}

Both timers run on the engine’s frames, not on setTimeout:

  • They belong to the cue. They cannot fire before it starts playing, and they are dropped the moment it ends — nothing keeps running after the cue is gone.
  • A frame that arrives late catches a repeat up rather than letting it drift, and the indexes stay consecutive.
  • A callback that throws is reported once and dropped; the hooks carry on.
  • A timer set inside a callback waits for the next frame rather than joining the one in flight.
  • An interval that is not a positive number, or a callback that is not a function, schedules nothing at all.

random

Numbers, points and colours picked at random. Bounds are coerced and put in order, so random.int(10, 1) is random.int(1, 10), and a bound that is not a number falls back to the default rather than putting NaN on stage.

CallGives
random.int(min, max)A whole number, both ends included. Defaults to 0–1.
random.float(min, max)A number, min included and max excluded. Defaults to 0–1.
random.vec2(min, max){ x, y }, each drawn separately
random.vec3(min, max){ x, y, z }, each drawn separately
random.color(options)A CSS colour, #rrggbb
random.element(array)One item out of the array; undefined when it is empty

random.color takes a hue, a saturation and a value. Each is either a fixed number or a [min, max] range to pick between. Hue is in degrees (0–360) and wraps, so [300, 420] sweeps through red; saturation and value are 0–1 and clamp. Left out, hue is anywhere on the wheel and the other two are 1.

random.color(); // any vivid colour
random.color({ saturation: [0.5, 1], value: 1 }); // bright, never washed out
random.color({ hue: [200, 260], value: [0.4, 1] }); // blues, any brightness
random.color({ saturation: 0, value: [0, 1] }); // a grey

random.element picks one item out of an array, and gives undefined for an empty one rather than throwing — a list that happens to be empty should not stop a show.

const GREETINGS = ['HELLO', 'BONJOUR', 'HOLA'];

function onStart() {
    every(0.5, function () {
        this.text.set(random.element(GREETINGS));
        this.textColor.set(random.color({ saturation: [0.6, 1] }));
        this.offsetX.set(random.float(-0.1, 0.1));
    });
}

this

The cue’s own properties, each an object with get() and — when the property can be changed — set():

function onStart() {
    log.info('level is', this.level.get());
    this.level.set(0.5);

    // A property with no setter is read-only; test for one before using it.
    if (this.brightness && this.brightness.set) {
        this.brightness.set(1.4);
    }
}

Which properties a cue has depends on its type — see properties.md. this works in onStart, onTick, onEnd and inside timer callbacks, as long as the callback is a function and not an arrow (() => {} has no this of its own).

What a script writes is live only: it changes what is playing, never the saved show. Editing the same field in the inspector while the cue plays overwrites what the script set.

The editor

  • Ctrl/Cmd+S compiles.
  • Ctrl+Space — or just typing — proposes the hooks, the API, this.<name> for the selected cue, and the variables the script itself declares. Enter or Tab accepts, Esc dismisses.
  • Tab indents when no proposal is open; Ctrl/Cmd+Z undoes.