TouchDesigner

Making Plugins

Author your own sources, effects, and system plugins for Lightpath as TouchDesigner .tox files.

Plugins are how you extend Lightpath with content and behavior that isn't in the box. They're regular TouchDesigner .tox files that follow a small contract: expose the right parameters, plug into the right slot in your project, and Lightpath will load them and surface them in the UI.

The three types

TypeWhat it doesLook paramsConfig paramsStatus params
SourceProduces a texture (the visual signal for a look)
EffectTransforms an incoming texture
SystemA singleton that reads, computes, or holds data other plugins can use

Sources and effects are the visual building blocks of every look. System plugins are the supporting cast — they don't render anything themselves, but they make data available (sensor reads, audio analysis, MIDI input, computed state) for sources and effects to react to.

See Making an Output for the per-output equivalent. Outputs follow the same parameter contract; they just sit at the end of the chain rather than inside it.


Sources

A source is a .tox whose output is a texture — anything TouchDesigner can render. Generative patterns (noise, gradients, particle systems, etc.), audio-reactive visuals, media playback, camera feeds, shaders — if it produces pixels, it's a source.

The contract

A source takes in a palette (as a texture input) and outputs a texture. That's the shape Lightpath expects:

  • Input 1: a palette texture. Lightpath wires the active look's palette TOP into this input automatically. When the operator hasn't picked a palette, Lightpath leaves the input disconnected.
  • Output 1: the source's rendered texture — whatever your component produces, colorized however it likes with the palette.

Don't depend on anything outside the project folder — see the accessing project files note in the outputs guide; it applies here too.

Working with the palette

The palette is a horizontal gradient TOP: pixel x-coordinate maps to the position along the palette, RGB at that x is the color at that stop. Most sources sample it via a Lookup TOP, using the luminance of some internally-generated pattern as the lookup index — white in your pattern picks the rightmost palette color, black picks the leftmost, mids pick the in-between gradient.

You don't have to use a Lookup TOP, though. The palette is just a texture — multiply with it, sample at specific x positions, drive a particle recolor, feed it to a shader. What you do with it inside the source is up to you.

Set a default palette as the second fallback input to your In TOP, pointing it at a built-in fallback (a ramp TOP, a hand-tuned gradient, whatever you like). When Lightpath disconnects the external input — because the operator hasn't picked a palette in the look — TouchDesigner uses this default automatically.

Custom parameters

Sources support Look and Config parameter pages. Use Look for things operators dial in per-look (pattern speed, scale, density, audio reactivity threshold, style toggles); use Config for install-level settings (resolution, an expensive post-process toggle, a path to an external LUT). See TouchDesigner Overview → Custom parameters for the full rules.


Effects

An effect is a .tox that takes one texture in and emits a (usually different) texture out. Color shifts, blurs, mirrors, masks, geometric distortions, time-warps — anything that transforms an incoming signal.

The contract

  • Accept the incoming texture on the component's first input.
  • Output the transformed texture on the first output.
  • Effects should be self-contained — Lightpath stacks multiple effects on one source, so each effect must work standalone.

Custom parameters

Effects support Look and Config parameter pages. Look is the bread and butter — strength, blend amount, hue shift, blur radius, mask threshold — every per-look knob an operator expects. Config is rarer on effects (quality preset, mask image path, expensive-feature toggle) but available when you need it. See TouchDesigner Overview → Custom parameters for the full rules.

Category tags

Effects can declare one or more category tags that surface as filter chips in the effect picker. The built-in categories are color, distort, mask, and transform; you can use these or declare your own. Group several effects under the same tag and an operator can find them all in one click in a long list.

Tags are optional — an untagged effect still works, it just lands in the "all" bucket in the picker.


System plugins

System plugins are singletons — one instance per type, mounted at a known op shortcut so other plugins can find them. They don't participate in the render chain. Instead, they hold and expose data that sources, effects, outputs, and actions can read.

When to author one

  • Reading external data — lidar, depth cameras, audio analysis, MIDI controllers, OSC ingestion, MQTT subscriptions.
  • Computing shared state — beat tracking from audio, presence detection from a sensor stack, smoothing filters on noisy input.
  • Coordinating between plugins — a "show clock" that multiple visuals reference, a global tempo, an environmental mode flag.

If two sources want to react to the same audio analysis, that analysis belongs in a system plugin — not duplicated inside each source.

The contract

  • No render input or output. The plugin doesn't sit in the texture chain.
  • Expose data via custom parameters, CHOPs, DATs, or your preferred TouchDesigner mechanism.
  • Set up a global op shortcut on the COMP so other plugins can resolve it from anywhere in the project — see below.

Surfacing status to Lightpath

System plugins (alongside outputs) support a Status parameter page. Any parameter you put on a page named Status shows up live in the TouchDesigner device on the Devices page, and is available as a target in the Alerts rule editor. See TouchDesigner Overview → Status parameters for the full rules.

This is what makes system plugins observable from the rest of Lightpath — operators can see connection state, error counts, and last-message timestamps without ever opening TouchDesigner.

The op shortcut

The op shortcut is what makes a system plugin reachable from elsewhere. Set the COMP's Common → Global OP Shortcut parameter to a name, and op.NAME resolves to the COMP from anywhere in the project — no path traversal, no parent() walks, no fragile relative references.

Convention: ALL_CAPS_UNDERSCORE. Lightpath's system plugins use this form (AUDIO_ANALYSIS, PRESENCE_TRACKER, SHOW_CLOCK) so they read as constants at the call site and don't collide with component or extension names. Stick to it for any plugin you author.

From a source's, effect's, or output's Python:

# Read a custom parameter
bpm = op.AUDIO_ANALYSIS.par.Bpm.eval()
 
# Read a CHOP / DAT / TOP inside the plugin
beat = op.AUDIO_ANALYSIS.op('out1')['beat'].eval()

Or wire it into a parameter expression in the editor:

op.AUDIO_ANALYSIS.par.Bpm

System plugins are the only TD components in Lightpath that other plugins should reach into directly. Sources and effects must stay self-contained — referencing one source or effect from another makes the look editor unpredictable when the referenced plugin isn't loaded. If two visual plugins need shared data, put the data in a system plugin and have each reach into it from there.


Registering a plugin

Plugins aren't auto-discovered. To make a .tox available to Lightpath, register it via Settings → TouchDesigner → Plugins. Once registered, Lightpath loads the .tox from disk into the running TouchDesigner project on project load.

The registration record tracks the plugin's type (Source / Effect / System), its disk location, and any metadata Lightpath needs to surface it in the UI. Move or rename the .tox on disk? Update the registration to point at the new location.


Accessing project files

Same pattern as outputs — see TouchDesigner Overview → Accessing project files for the project/... shortcut.


Lifecycle callbacks (advanced)

Sources and effects can react to load and teardown events by adding an extension to the COMP with capitalized methods matching the callback names. Lightpath calls them automatically at the right moments — if you don't need them, just leave them out. Most sources and effects don't.

OnLoad()

Fires after the plugin has been placed into its slot and is fully ready — parameters applied, cooking enabled. Use it for setup that can't happen during cook: connecting to an external API, allocating resources, resetting internal state.

Available on sources and effects.

OnDestroy()

Fires before the plugin's COMP is torn down — a look change, slot swap, or output disable. Use it for cleanup: closing connections, stopping streams, releasing handles.

Available on sources and effects.

OnSlotChange(abValue)

Fires when the output's slot crossfade value changes (0 = slot A fully visible, 1 = slot B fully visible). Useful for effects that need to know whether they're currently on screen — for example, a heavy effect that should only run when visible.

Available on effects only.

Example

class MyEffectExt:
    def __init__(self, ownerComp):
        self.ownerComp = ownerComp
 
    def OnLoad(self):
        # Plugin just got placed into a slot — set things up
        ...
 
    def OnDestroy(self):
        # About to be torn down — clean up
        ...