Loading...
Loading...
Control a running TouchDesigner instance via twozero MCP — create operators, set parameters, wire connections, execute Python, build real-time visuals. 36 native tools.
npx skill4agent add nousresearch/hermes-agent touchdesigner-mcptd_get_par_infotdAttributeErrortd_get_operator_infome.parent()scriptOp.parent()td_create_operatortd_set_operator_parstd_get_errorstd_execute_pythontd_get_hintsHermes Agent -> MCP (Streamable HTTP) -> twozero.tox (port 40404) -> TD PythonGET http://localhost:40404/mcpbash "${HERMES_HOME:-$HOME/.hermes}/skills/creative/touchdesigner-mcp/scripts/setup.sh"twozero_td~/Downloads/twozero.toxnc -z 127.0.0.1 40404 && echo "twozero MCP: READY"outputresolution = 'custom'proresmjpatd_get_par_infoCall td_get_par_info with op_type for each type you plan to use.
Call td_get_hints with the topic you're building (e.g. "glsl", "audio reactive", "feedback").
Call td_get_focus to see where the user is and what's selected.
Call td_get_network to see what already exists.td_execute_pythontd_create_operatortd_create_operator(type="noiseTOP", parent="/project1", name="bg", parameters={"resolutionw": 1280, "resolutionh": 720})
td_create_operator(type="levelTOP", parent="/project1", name="brightness")
td_create_operator(type="nullTOP", parent="/project1", name="out")td_execute_python# td_execute_python script:
root = op('/project1')
nodes = []
for name, optype in [('bg', noiseTOP), ('fx', levelTOP), ('out', nullTOP)]:
n = root.create(optype, name)
nodes.append(n.path)
# Wire chain
for i in range(len(nodes)-1):
op(nodes[i]).outputConnectors[0].connect(op(nodes[i+1]).inputConnectors[0])
result = {'created': nodes}td_set_operator_pars(path="/project1/bg", parameters={"roughness": 0.6, "monochrome": true})td_execute_pythonop('/project1/time_driver').par.colorr.expr = "absTime.seconds % 1000.0"td_execute_pythonop('/project1/bg').outputConnectors[0].connect(op('/project1/fx').inputConnectors[0])td_get_errors(path="/project1", recursive=true)
td_get_perf()
td_get_operator_info(path="/project1/out", detail="full")td_get_screenshot(path="/project1/out")win = op('/project1').create(windowCOMP, 'display')
win.par.winop = op('/project1/out').path
win.par.winw = 1280; win.par.winh = 720
win.par.winopen.pulse()| Tool | What |
|---|---|
| Run arbitrary Python in TD. Full API access. |
| Create node with params + auto-positioning |
| Set params safely (validates, won't crash) |
| Inspect one node: connections, params, errors |
| Inspect multiple nodes in one call |
| See network structure at a path |
| Find errors/warnings recursively |
| Get param names for an OP type (replaces discovery) |
| Get patterns/tips before building |
| What network is open, what's selected |
| Tool | What |
|---|---|
| Read DAT text content |
| Write/patch DAT content |
| Read CHOP channel values |
| Read TD console output |
| Tool | What |
|---|---|
| Capture one OP viewer to file |
| Capture multiple OPs at once |
| Capture actual screen via TD |
| Jump network editor to an OP |
| Tool | What |
|---|---|
| Find ops by name/type across project |
| Search code, expressions, string params |
| Tool | What |
|---|---|
| Performance profiling (FPS, slow ops) |
| List all running TD instances |
| In-depth docs on a TD topic |
| Read/write per-COMP markdown docs |
| Reload extension after code edit |
| Clear console before debug session |
| Tool | What |
|---|---|
| Send mouse/keyboard to TD |
| Poll input queue status |
| Stop input automation |
| Get screen coords of a node |
| Click a point in a screenshot |
| Convert screenshot pixel to absolute screen coords |
td_project_quittd_test_sessiontd_dev_logtd_clear_dev_logreferences/mcp-tools.mduTDCurrentTime# Call td_get_par_info(op_type="glslTOP") first to confirm param names
td_set_operator_pars(path="/project1/shader", parameters={"value0name": "uTime"})
# Then set expression via script:
# op('/project1/shader').par.value0.expr = "absTime.seconds"
# In GLSL: uniform float uTime;rgba32floattopoutputresolution = 'custom'/tmp/file.glsltd_write_dattd_execute_pythonpoint.P[0]point.P[1]point.P[2].x.y.zext0object"op('./datName').module.ClassName(me)"td_write_dattd_reinit_extensionme.parent()scriptOp.parent()list(root.children)child.valid# via td_execute_python:
root = op('/project1')
rec = root.create(moviefileoutTOP, 'recorder')
op('/project1/out').outputConnectors[0].connect(rec.inputConnectors[0])
rec.par.type = 'movie'
rec.par.file = '/tmp/output.mov'
rec.par.videocodec = 'prores' # Apple ProRes — NOT license-restricted on macOS
rec.par.record = True # start
# rec.par.record = False # stop (call separately later)proresmjpaffmpeg -i /tmp/output.mov -vframes 120 /tmp/frames/frame_%06d.pngtd_get_perftd_get_screenshotAudioFileIn CHOP (playmode=sequential)
→ AudioSpectrum CHOP (FFT=512, outputmenu=setmanually, outlength=256, timeslice=ON)
→ Math CHOP (gain=10)
→ CHOP to TOP (dataformat=r, layout=rowscropped)
→ GLSL TOP input 1 (spectrum texture, 256x2)
Constant TOP (rgba32float, time) → GLSL TOP input 0
GLSL TOP → Null TOP → MovieFileOutoutputmenu='setmanually'outlength=256mix(prevValue, newValue, 0.3)outlength// Input 0 = time (1x1 rgba32float), Input 1 = spectrum (256x2)
float iTime = texture(sTD2DInputs[0], vec2(0.5)).r;
// Sample multiple points per band and average for stability:
// NOTE: y=0.25 for first channel (stereo texture is 256x2, first row center is 0.25)
float bass = (texture(sTD2DInputs[1], vec2(0.02, 0.25)).r +
texture(sTD2DInputs[1], vec2(0.05, 0.25)).r) / 2.0;
float mid = (texture(sTD2DInputs[1], vec2(0.2, 0.25)).r +
texture(sTD2DInputs[1], vec2(0.35, 0.25)).r) / 2.0;
float hi = (texture(sTD2DInputs[1], vec2(0.6, 0.25)).r +
texture(sTD2DInputs[1], vec2(0.8, 0.25)).r) / 2.0;references/network-patterns.md| Family | Color | Python class / MCP type | Suffix |
|---|---|---|---|
| TOP | Purple | noiseTOP, glslTOP, compositeTOP, levelTop, blurTOP, textTOP, nullTOP | TOP |
| CHOP | Green | audiofileinCHOP, audiospectrumCHOP, mathCHOP, lfoCHOP, constantCHOP | CHOP |
| SOP | Blue | gridSOP, sphereSOP, transformSOP, noiseSOP | SOP |
| DAT | White | textDAT, tableDAT, scriptDAT, webserverDAT | DAT |
| MAT | Yellow | phongMAT, pbrMAT, glslMAT, constMAT | MAT |
| COMP | Gray | geometryCOMP, containerCOMP, cameraCOMP, lightCOMP, windowCOMP | COMP |
td_execute_pythonsetup.sh| File | What |
|---|---|
| Hard-won lessons from real sessions |
| All operator families with params and use cases |
| Recipes: audio-reactive, generative, GLSL, instancing |
| Full twozero MCP tool parameter schemas |
| TD Python: op(), scripting, extensions |
| Connection diagnostics, debugging |
| GLSL uniforms, built-in functions, shader templates |
| Post-FX: bloom, CRT, chromatic aberration, feedback glow |
| HUD layout patterns, panel grids, BSP-style layouts |
| Wireframe rendering, feedback TOP setup |
| Geometry COMP: instancing, POP vs SOP, morphing |
| Audio band extraction, beat detection, envelope following |
| LFOs, timers, keyframes, easing, expression-driven motion |
| MIDI/OSC controllers, TouchOSC, multi-machine sync |
| POPs and legacy particleSOP — emission, forces, collisions |
| Multi-window output, corner pin, mesh warp, edge blending |
| HTTP, WebSocket, MQTT, Serial, TCP, webserverDAT |
| Custom params, panel COMPs, button/slider/field, panelExecuteDAT |
| replicatorCOMP — data-driven cloning, layouts, callbacks |
| Execute DAT family — chop/dat/parameter/panel/op/executeDAT |
| Lighting rigs, shadows, IBL/cubemaps, multi-camera, PBR |
| Automated setup script |
You're not writing code. You're conducting light.