TouchDesigner via td-atlas
Перевод английской документации от 15.09·English
TouchDesigner is a node-based visual programming environment. td-atlas gives you an offline index of the installed build (operators, parameters, documentation, palette) plus a live bridge into a running instance.
#The one thing that will waste your time
TouchDesigner reports almost nothing when work silently does nothing. A network that never cooks raises no error. Nor does a flag left off, an output device switched off, a CPU-bound operator holding the frame rate down, or a resolution silently halved by the licence. Three separate hours have been lost to exactly this.
Run td_health after building anything, and whenever something "looks fine but does nothing". It is the tool that catches these. When it says nothing is cooking, td_flags says which flag is the reason. The keys are the ones TouchDesigner uses, so cooking is allowCooking, not cooking; the full set is display, render, bypass, lock, expose, viewer, activeViewer, cloneImmune, allowCooking, selected, pickable. td_set_flags reads every write back, so a flag a family will not take comes back as a refusal, and so does a name that is not one of these.
#Order of work
- Search the index first. It is offline and free, with no round trip to TouchDesigner. Never guess a parameter name. The index is not there until
td-atlas buildhas run once;references/tools.mdhas that command and the rest of the setup. - Read the schema of any operator before creating it.
- Build with
td_build, not a series of single calls. - Look at the result with
td_render, and at motion with a contact sheet. - Run
td_health.
td_log(failures=True) is your own trail, every bridge call this host made, with the text each refusal came back with. Reach for it when a call refuses and you do not know why, or when the artist says something broke while you were working. td_log(summary=True) says which method has been failing repeatedly, which is the signal that the approach is wrong. It outlives the TouchDesigner session, so it also answers "what happened yesterday".
#Finding the right operator
Ask for what the operator does, in plain language, with td_search_operators("displace an image with noise").
Two things it cannot do, and the way round each:
- Artist slang is not TouchDesigner vocabulary. There is no "strobe" or "melt" operator; those are techniques built from several nodes. Search for the mechanism ("brightness over time", "displace by a texture"), or use
td_docs, which reaches 2,060 wiki pages of concepts and techniques, not just operator help. - Check the palette before building.
td_palette("projection mapping")searches 277 finished components that ship with TouchDesigner, among them mappers, corner-pinners, colour pickers and audio analysers. Install one withtd_palette_load(name, parent), which checks the .tox is on disk first and reports the name TouchDesigner actually gave the node.
td_glossary("cook") defines the vocabulary the documentation assumes.
#Parameter names
td_operator_schema("noiseTOP") always gives exact names. Defaults, menu options and ranges appear once td-atlas probe has run against a live TouchDesigner; without that run the schema says so itself, in place of the values. Two traps:
- *The documentation describes parameter groups; you must set the members.* The docs say
t(Translate); the settable parameters aretx,ty,tz. The schema returns the members, and settingtfails. - Menu labels carry information the value does not. The schema prints them as
value — Label: the Noise TOP'stypemenu readssimplex3d — Simplex 3D (GPU)against a plainsparse — Sparse, and the second one runs on the CPU at ~96 ms per cook at 1280×720. Read the labels, not just the values.
td_build validates parameter names against the index before sending anything, resolving each target's type itself, so a mistake comes back as t: is a parameter group, not a settable parameter (try: tx, ty, tz). The check is a convenience, not a guarantee: with no index built, and for an existing node whose type the bridge cannot report, the batch goes out unchecked. td_set_params does the same only when you pass op_type; without it the name goes straight to TouchDesigner.
#Building
Use td_build for anything multi-step:
[
{"method": "op_create", "params": {
"parent": "/project1", "type": "noiseTOP", "name": "n1",
"position": [-600, 0],
"pars": {"type": "simplex3d", "period": 3.5,
"tx": {"expr": "absTime.seconds * 0.1"}}}},
{"method": "op_create", "params": {
"parent": "/project1", "type": "blurTOP", "name": "b1",
"pars": {"size": 6},
"connect": [{"from": "/project1/n1", "index": 0}]}}
]
The whole batch lands in one ui.undo block. If any step fails everything rolls back, and a successful batch is a single Ctrl+Z for the person using TouchDesigner. Parameter values may be a constant, {"expr": "..."} for an expression, {"bind": "..."}, or {"pulse": true}.
A DAT's contents are not a parameter, so they go in their own text key on op_create. That covers shader source, script bodies and callbacks. Doing it there puts the text in the same rollback and the same Ctrl+Z, and saves a follow-up td_exec:
{"method": "op_create", "params": {
"parent": "/project1", "type": "textDAT", "name": "frag",
"text": "out vec4 fragColor;\nvoid main() { fragColor = vec4(1.0); }"}}
A DAT whose text is an output (Select, Null, Info, the script generators) is refused, since it would overwrite the assignment on its next cook.
This was measured live on 2026-08-30, against build 2025.32460. A fragment shader delivered this way to a textDAT, with a glslTOP created in the next step pointing its pixeldat at it, compiled. compileResult read Compiled Successfully, and the TOP's first pixel came back as the colour the shader writes. A shader that does not compile is a warning, not an error, and compileResult is where the line number lives, which td_health reads for you.
The methods are op_create, op_delete, op_connect, op_disconnect and par_set. undo is not one of them, since it walks the history the batch is being recorded into. Call td_undo on its own, after.
If another agent or session may be in the same project, claim your subtree with td_claim_scope(path, owner) first. Then pass that same owner into every tool that writes: td_build, td_set_params, td_set_flags, td_annotate, td_palette_load and td_extension_add, or your own claim refuses your own writes. td_release_scope hands it back, and td_scopes says what is already held.
td_annotate leaves the reason for what you built beside the nodes, as a box the artist can read. td_annotations reads notes back, including a brief a person left for you, which no other tool here shows.
#Seeing what you made
td_render("/project1/b1") returns the image. Use it, since inferring appearance from parameter values does not work.
For a strobe, a feedback trail or any other animation, a single frame is a coin toss. Use a contact sheet:
from td_atlas.bridge.client import BridgeClient
from td_atlas.bridge.filmstrip import contact_sheet
contact_sheet(BridgeClient.discover(), "/project1/b1", "sheet.png",
frames=9, interval=0.13, columns=3)
#Reading projects from disk
None of this needs TouchDesigner running, and td_project_read gives the tree. td_project_text gives the whole network as JSON, every parameter, wire, flag and DAT line. td_project_grep reaches the Python and GLSL inside DATs, which no file search can, since that code lives inside the .toe container. A network too big for one result is refused, and never truncated. Narrow it with path, or write it out with td-atlas project text FILE -o network.json.
td_project_write is the return leg, an edited dump into a new file. It is a patcher, so it needs the original file too, and it lists what it could not write, so read those gaps before assuming.
Before trying a direction, td_variant_save(file, label) keeps the current state, and td_variant_list/_restore/_diff branch, restore and compare. On a live instance the same shape is td_snapshot("before") → work → td_snapshot("after") → td_project_diff.
Never save the artist's project. project.save() is Save As in disguise and moves the file they have open. Nothing here needs it; TouchDesigner writes the network text beside the .toe when they save.
#Things that will bite you
Detail in references/gotchas.md, and the short list follows:
- A branch nothing displays or records never cooks. Terminal nodes (Movie File Out, and any chain not feeding a viewer) are not pulled. Add a
cacheTOPwithalwayscookon to keep a chain live. - A Python class attached the way the wiki shows it fails in total silence. Use
td_extension_add, which uses the form that works and reads it back. - TouchDesigner throttles rendering when its window is in the background.
- Colour operations inside a feedback loop accumulate. A hue rotation in the loop drives everything to grey; an additive composite drives it to white. Feed back from before the grade, and prefer
maximumoveradd. - Non-Commercial caps resolution at 1280×1280, silently, with only a warning, so asking for 1920×1080 gives 1280×720.
execblocks TouchDesigner's main thread, so do not poll during a recording.
#Command line
td-atlas status # install, index and bridge state
td-atlas doctor # every link, with the command that fixes it
td-atlas search "blur an image"
td-atlas op noiseTOP --page Noise
td-atlas render /project1/n1 -o out.png
td-atlas project diff a.toe b.toe
td-atlas project variant save a.toe --label try1 # branch, restore, compare
td-atlas log --failures # the same trail as td_log
td-atlas reload # upgrade the bridge in place
#If the bridge is unreachable
td-atlas install prints a one-line bootstrap to paste into TouchDesigner's textport. The textport opens from the menu, Dialogs → Textport and DATs. Do not reach for the keyboard shortcut. A blind key combination lands in the network editor when the window is not focused, and creates operators in the artist's project. Re-running the bootstrap upgrades in place. td_instances says which running TouchDesigner these tools actually reach. Check it before believing an edit landed in the project you meant.