Button scripts

Video tutorial

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#

  1. Select a button, knob, or fader slot (or a push-knob's push row).
  2. In the inspector, choose Script from the mode row.
  3. Optionally set ownerComp, the COMP that me, ownerComp, and relative op('child') resolve from. Empty inherits bankdevice → project root (/). Bank defaults live in Bank settings… → Advanced; device defaults in ⚙ Device settings → Advanced.
  4. 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 to ownerComp; paths starting with / are absolute.
  • op.Shortcut: global OP shortcuts (for example op.A) still work.
  • ext, parent: on ownerComp (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. Omit device for every device where applicable; AssignSlot always 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 TDMap methods from outside TDMap via op.TDMap.Method(...). Active-agnostic: omit device for every device where applicable; omit bank on SendAllFeedback / AssignSlot for 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.