Button scripts
This topic is covered in the video tutorial at:
Run Python directly from a mapped button, knob, fader, or push-knob, with no external DAT and no wiring a CHOP Execute. Flip the slot to Script in the inspector (alongside Assignment, and on buttons also Function, Slot, and Modify). Scripts are stored per slot in your TDMap config, compiled in memory, and fire on MIDI like built-in function buttons.
Setup#
- Select a button, knob, or fader slot (or a push-knob's push row).
- In the inspector, choose Script from the mode row.
- Optionally set ownerComp, the COMP that
me,ownerComp, and relativeop('child')resolve from. Empty inherits bank → device → project root (/). Bank defaults live in Bank settings… → Advanced; device defaults in ⚙ Device settings → Advanced. - Edit the script inline, or open Full editor… for a larger pane (Ctrl+S / ⌘+S to save). Changes auto-save after a short debounce.
Callbacks#
These use CHOP Execute-style names. Define only the ones you need:
| Callback | When it fires | Arguments |
|---|---|---|
onOffToOn |
MIDI value crosses from 0 to > 0 | val, prev, ownerComp, source |
onOnToOff |
MIDI value crosses from > 0 to 0 | val, prev, ownerComp, source |
onValueChange |
Any MIDI value change | val, prev, ownerComp, source |
whileOn |
Every frame while held (> 0) | val, ownerComp, source |
whileOff |
Every frame while released (0) | val, ownerComp, source |
val / prev are normalized 0–1 (using from_min / from_max when set). source is the same identity dict as lifecycle callbacks and function-mode Callback: device, slot, channel, index, message type, is_push, and the callback name. Knobs and faders fire onValueChange only. Compile and runtime errors log to the Textport with the slot number.
What your script can touch#
Scripts run in an ownerComp-scoped namespace, plus the TDMap public API:
me/ownerComp: the resolved COMP (see inheritance above).op('child'): relative toownerComp; paths starting with/are absolute.op.Shortcut: global OP shortcuts (for exampleop.A) still work.ext,parent: onownerComp(not TDMap internals).TDMap:Learn,SelectBank,CreateBank,RemoveBank,RetargetBanks,SetOwnerComp,AssignSlot, config load/save, and so on. These are the same methods as the public interface. Omitdevicefor every device where applicable;AssignSlotalways needs one.absTime,app,ui,project: TouchDesigner globals.
Script vs Callback vs public interface#
- Script mode: Python lives in the slot, edited in the web UI, ideal for one-off hardware behavior tied to a specific button. Call
TDMap.*directly from callbacks. - Function → Callback: one named function in
TDMap_callbacks(Integration → Create Callbacks), reused across many slots with string args. - Public interface: call the same
TDMapmethods from outside TDMap viaop.TDMap.Method(...). Active-agnostic: omitdevicefor every device where applicable; omitbankonSendAllFeedback/AssignSlotfor each device's active bank.
Presets: factory device presets are layout templates, so script-mode slots revert to assignment when saved as factory. Your own saved layouts keep script text. Push-knob scripts set source['is_push'] to true.