Building Reusable Components in TouchDesigner
Design self-contained, shareable TouchDesigner components with custom parameters, internal scripting patterns, and .tox file packaging for cross-project reuse.
As your TouchDesigner projects grow, you will build the same patterns repeatedly — an audio visualiser here, a colour mapper there. Reusable components solve this by encapsulating a network inside a Container COMP (or Base COMP) with a clean external interface of custom parameters and standardised inputs and outputs. Once built, a component drops into any project as a black box, with no knowledge of its internals required.
Why Components
A well-designed component:
- Hides implementation complexity behind a simple parameter surface
- Can be updated in one place and redistributed as a new
.tox - Enables non-technical collaborators to use it without opening the network
- Makes projects easier to reason about because each box has a defined contract
The analogues in other software are VST plugins, Unreal Blueprints, or React components: encapsulated, parameterised, composable.
Creating Custom Parameters
Select a Base COMP or Container COMP. Go to Edit > Custom Parameters (or press Ctrl+P with the component selected in the network). Click the + button to add a parameter.
Parameter types and their uses:
| Type | TD Type String | Use |
|---|---|---|
| Float | Float |
Continuous values (frequency, amplitude, size) |
| Integer | Int |
Discrete counts, mode selectors |
| Toggle | Toggle |
On/Off switches |
| Menu | Menu |
Dropdown with named options |
| String | Str |
File paths, label text |
| Operator Reference | OP |
A reference to another operator |
| XY, XYZ, XYZW | XY etc. |
Grouped multi-component values |
| RGB, RGBA | RGB etc. |
Colour pickers |
After adding a Float parameter named Speed, it appears on the component’s parameter panel as a standard slider. Users do not need to enter the component to change it.
Accessing Custom Parameters Inside the Component
Inside the component (press i to enter), me refers to the component itself. Its custom parameters are on me.par:
# Inside a script inside the component
speed = me.par.Speed.val
amplitude = me.par.Amplitude.val
colour_r = me.par.Colour.r # for an RGB parameter
colour_g = me.par.Colour.g
Use these values to drive internal operator parameters through expressions or Python scripts:
# In a Parameter expression on an internal Noise TOP's Amplitude
me.parent().par.Amplitude
me.parent() from inside a sub-operator accesses the component, so me.parent().par.Amplitude reads the component’s custom Amplitude parameter from anywhere inside it.
Promoting Internal Parameters
Sometimes you want to expose a deeply nested parameter without writing Python. Use a Parameter CHOP inside the component to read an internal parameter value, then export that CHOP’s channel to the custom parameter — but the cleaner direction is the reverse: drive the internal parameter from the custom parameter using an expression.
For complex mapping, a Parameter Execute DAT inside the component listens for changes to the component’s own custom parameters:
# Parameter Execute DAT — fires when any custom parameter changes
def onValueChange(par, val, prev):
if par.name == 'Sensitivity':
# Remap to internal gain
internal_gain = val ** 2.0 # quadratic response
op('audio_gain').par.gain = internal_gain
elif par.name == 'Mode':
# Menu parameter returns string
if val == 'Organic':
op('noise_type').par.type = 'Simplex'
elif val == 'Sharp':
op('noise_type').par.type = 'Sparse'
return
The __init__.py Pattern
A Base COMP can contain a DAT named __init__ (a Text DAT). TD automatically runs this DAT’s contents when the component is first loaded into a project. Use it to set defaults, validate the environment, or register callbacks:
# __init__ Text DAT — runs once on component load
me.par.Version = '1.3.0'
# Check dependencies
if not op('kinectazure1'):
debug(f'{me.name}: Warning — no Kinect device found, body tracking disabled')
# Register a global handler
op('oscin1').par.callbacks = me.op('osc_callbacks')
Designing the Interface
A good component interface follows these conventions:
- Inputs: follow TouchDesigner convention — TOP inputs on left, CHOP inputs via explicit paths or OP reference parameters
- Outputs: one or more clearly named output TOPs/CHOPs on the right side
- Custom parameters: group them with separator parameters (type: Header) for organisation
- A
Helpparameter (type: String, read-only) containing one-sentence documentation - Version parameter: a read-only String showing the current
.toxversion
Group related parameters on named pages (add pages in the Custom Parameters editor). For example, an Audio Visualiser component might have pages: “Appearance”, “Audio”, and “About”.
Saving as .tox
When the component is complete, right-click it in the network and choose Save Component .tox. This saves the component’s entire contents — all internal operators, scripts, and parameter definitions — into a single file.
Convention: name the file after the component with the version, e.g. AudioVisualiser_v1_3.tox.
Save external: For components you actively develop across projects, use Save External instead. This keeps the .tox file hot-linked to the project: when you modify the .tox, you can reload it into any project.
Loading a .tox
Drag the .tox file from the file browser into any TouchDesigner network. It appears as a component with its parameter interface intact. No installation step — the entire component is self-contained.
To update a .tox across projects, right-click the component → Re-initialize Component (if using external), or replace the .tox file and re-drag.
Full Example: Self-Contained Audio Visualiser Component
External interface:
- Custom parameters:
Colour(RGB),Sensitivity(Float 0–2),Shape(Menu: Bar/Circle/Wave) - TOP Output:
out1— the rendered visualisation at 1920×1080
Internal network (abbreviated):
audio_in (Audio Device In CHOP)
└─→ spectrum (Audio Spectrum CHOP, 64 bands)
└─→ lag (Lag CHOP, 0.05s)
└─→ math_gain (Math CHOP, multiply by me.parent().par.Sensitivity)
└─→ chop_to_top (CHOP to TOP, 64×1)
└─→ lookup (Lookup TOP, maps level to colour from gradient_top)
└─→ comp (Composite TOP, composites bars)
└─→ out1 (Out TOP)
The gradient_top (Ramp TOP) is driven by the Colour custom parameter — a Python expression on its first colour stop sets:
# Ramp TOP colour[0] Red parameter expression
op('/').op(me.parent().name).par.Colour.r
Or more cleanly from inside the component:
# In a Parameter Execute DAT
def onValueChange(par, val, prev):
if par.name in ('Colourr', 'Colourg', 'Colourb'):
r = me.par.Colour.r
g = me.par.Colour.g
b = me.par.Colour.b
op('gradient_ramp').par.color0r = r
op('gradient_ramp').par.color0g = g
op('gradient_ramp').par.color0b = b
return
The Shape menu parameter switches which branch of the internal network flows to out1:
def onValueChange(par, val, prev):
if par.name == 'Shape':
op('out1').par.top = {
'Bar': 'comp_bars',
'Circle': 'comp_circle',
'Wave': 'comp_wave',
}.get(val, 'comp_bars')
return
The entire component is saved as AudioVisualiser_v1_0.tox and dropped into any project. Users interact only with three parameters — the internals are invisible.