Clockworks runs user-authored Lua 5.4 scripts to react to playback and send control data to other systems (OSC, Art-Net, MIDI). There are two kinds of scripts — cue scripts and the global script — plus a shared extensions file.
| Cue script | Global script | |
|---|---|---|
| Attached to | A single cue (per track) | The whole project (one, app-wide) |
| Runs when | That cue fires during playback | Continuously — you define event handlers it calls |
| Shape | A plain script body; the whole thing runs top-to-bottom on each fire | A set of named functions (onMeta, onBeat, …); Clockworks calls the ones you define |
| Edited via | Cue list → edit-script (pencil) on a cue | Navbar → Global Script |
| Stored in | The project's scripts sheet, keyed by cue id |
Same sheet, under a reserved id |
Both kinds share one Lua environment — the same global namespace, the same exposed functions, and the same user extensions. A value set as a global in one is visible to the other.
The entire body executes each time the cue fires. Example:
-- Fires every time this cue is triggered
sendOSCDest("10.0.0.5", 9000, "/scene/go", 3)
sendMidiNote(1, 60, 127)
You define handler functions; Clockworks invokes whichever ones exist and silently ignores the rest (an unimplemented handler is never an error). If you don't need a handler, don't define it.
function onMeta(deck, title, artist, album)
sendOSCDest("127.0.0.1", 9600, "/nowplaying", title, artist)
end
function onDeckSelected(deck)
-- deck == 0 means "no deck selected"
sendMidiCC(1, 20, deck)
end
| Handler | Signature | Called when |
|---|---|---|
onMeta |
onMeta(deck, title, artist, album) |
Track metadata changes on a deck |
onBeat |
onBeat(deck, beat, bpm) |
Beat/tempo update for a deck |
onLive |
onLive(live) |
Live state toggles on/off |
onDeckSelected |
onDeckSelected(deck) |
The active deck changes |
Argument types:
deck — integer, 1–4; onDeckSelected uses 0 to mean no deck selected.title, artist, album — strings.beat — integer.bpm — number (float).live — boolean.