Read, Import & Export¶
Reading a Network (no disk I/O)¶
MCP Tool¶
Use the read_tdxn tool to return the live network as a TDXN dict without writing anything to disk. This is the preferred read path for LLM workflows exploring networks of more than ~3 operators — typically 20-90× fewer tokens than walking the same subtree with get_op + query_network because of default-omission, type_defaults, and par_templates compaction.
| Parameter | Default | Description |
|---|---|---|
comp_path |
"/" |
Starting COMP path |
include_dat_content |
Toggle setting | Also include file-backed DAT text/table content (content saved nowhere else is always included) |
max_depth |
null (unlimited) |
Cap recursion on large roots |
embed_all |
false |
Recurse into TDXN-tagged COMPs instead of skipping their children |
Works in all three Tdxnmode values (Off / Export-on-Save / Roundtrip) — read_tdxn reads live state, not .tdxn files on disk.
When NOT to use read_tdxn¶
For these, reach for the runtime-state MCP tools instead:
| Need | Use |
|---|---|
| Evaluated-expression runtime values | get_parameter |
| Cook errors / warnings | get_op_errors |
| DAT / CHOP / TOP output data | get_dat_content, capture_top |
| Cook timing | get_op_performance |
| Flag state after runtime mutation | get_op_flags |
Exporting a Network¶
Keyboard Shortcuts¶
- Ctrl+Shift+E — Export the entire project to a single
.tdxnfile - Ctrl+Alt+E — Export just the current COMP to a
.tdxnfile
MCP Tool¶
Use the export_network tool with these options:
| Parameter | Default | Description |
|---|---|---|
root_path |
"/" |
Starting COMP path |
include_dat_content |
Toggle setting | Also include file-backed DAT text/table content (content saved nowhere else is always included) |
output_file |
null |
File path (use "auto" for automatic naming, null for dict-only). A path other than the COMP's tracked file writes a snapshot: the tracked file and its table row are left untouched. A relative path is anchored at the project folder. |
max_depth |
null (unlimited) |
Maximum recursion depth |
embed_all |
false |
Recurse into TDXN-tagged COMPs instead of writing tdn_ref pointers, producing a self-contained export |
What Gets Exported¶
- All operators under the root path (recursively)
- Only non-default parameter values
- Connections between operators
- Custom parameter definitions
- Built-in parameter sequences with non-default block counts or values (
sequences) - Flags, positions, sizes, colors, comments, tags
- Serializable operator storage entries (unless disabled per-COMP or globally)
- Annotations
- Optionally: DAT text/table content
What Gets Excluded¶
- System paths (
/local,/sys,/perform,/ui) - Pulse, Momentary, and Header parameters (no persistent state)
- Read-only parameters
- COMP externalization parameters (
externaltox,enableexternaltox, etc.) - Children of palette clones (TD recreates them from the clone source)
Importing a Network¶
Use the import_network MCP tool:
| Parameter | Description |
|---|---|
target_path |
Destination COMP path |
tdn |
The TDXN document, parsed object (full document or operators array) |
clear_first |
Delete existing children before importing |
Import Phases¶
The import process runs in a pre-phase plus the ordered phases below. This ordering ensures dependencies are satisfied:
| Phase | Action | Details |
|---|---|---|
| Pre | Resolve templates and defaults | Expand $t references and merge type_defaults into operators. With clear_first, excluded COMPs (the tdxn_exclude tag) are preserved, not destroyed. |
| 0 | Create target custom parameters | The target COMP's own custom parameters it does not have yet (all of them on a bare tdn_ref shell, new ones on a reload), created before any child so an extension or expression inside finds them. |
| 1 | Create operators | Depth-first creation. COMPs first so children can be placed inside. |
| 2 | Create custom parameters | Pages, types, ranges, menu entries, defaults. |
| 2.5 | Expand sequences | Resizable parameter blocks (sequences on ops like mathmixPOP, glslPOP, constantCHOP) have their sequence parameters created before any values are set. |
| 3 | Set parameter values | Both built-in and custom. = prefix → expression, ~ prefix → bind. Absolute OP references inside the document's network_path (sequence blocks and custom parameters written by Embody 6.2.56 or earlier) are remapped onto the destination -- see the specification's OP-Reference Values. |
| 4 | Set flags | Array entries without - → true; with - → false. |
| 5 | Wire connections | Resolve sources (sibling name first, then full path). |
| 6 | Set DAT content | Text or table data loaded into DATs. |
| 6a | Restore storage | Storage key-value pairs restored via op.store(); $type wrappers deserialized. |
| 7 | Set positions | Positions, sizes, colors, comments applied (later phases still follow). |
| 7b | Set docking | Docking relationships restored between operators. |
| 7a | Create annotations | Annotations created with utility=True. |
| 8 | Restore file links | File/syncfile parameters restored on externalized DATs. |
| 8.5 | Restore TOX content | .tox content loaded into tox_ref shells. |
| 8.6 | Restore nested TDXN content | tdn_ref shells filled from their own .tdxn files in the same import (recursive, with an ancestor-chain cycle guard). Skipped by startup reconstruction and the post-save restore, whose own depth-sorted loops import every tracked TDXN COMP exactly once. |
| 9 | Apply target COMP properties | The target COMP's own type, custom and built-in parameters, flags, color, tags applied last. |
| 10 | Warn about locked non-DATs | Locked TOP/CHOP/SOP/POP operators this import created are logged with their source (lock preserved, frozen data is not), after every wire is restored. See Lock Flag Limitation. |
Version Compatibility¶
The importer checks metadata for compatibility:
version: Warning only when the file's version is newer than the running build (the risky direction — the file may use schema this build does not understand). Equal or older versions import silently.td_build: Info message if TD version differs (parameter defaults may vary)build: Logged for informational purposes
These checks are non-blocking — import always proceeds.
Diffing a Network¶
diff_tdxn answers the question git cannot: what have I changed but not saved? It compares the live in-memory network against its on-disk .tdxn — the unsaved window, which git never sees (git only reads files on disk, not TouchDesigner's live state).
| Parameter | Default | Description |
|---|---|---|
target |
(optional) | A COMP path, or a .tdxn file path / bare filename (e.g. "mixer.tdxn"). Omit it for a whole-project summary across every live TDXN COMP. |
max_changed_ops |
200 |
Cap on the total changed entries returned (added + removed + modified combined, modified filled first). The reported counts always reflect the true totals; entries beyond the cap are dropped from the payload and counted in dropped. |
max_bytes |
60000 |
Cap on the output size. |
The comparison is semantic, not byte-level: both sides normalize through the same type_defaults / par_templates expansion, and the volatile export header (build, generator, td_build, exported_at, source_file) is ignored — so a no-op re-export shows nothing. Each change is {old, new} (old = disk, new = live), tagged root, op, or annotation.
Git diffs of .tdxn files¶
diff_tdxn covers the unsaved window; git diff covers what you have committed. Embody keeps those diffs quiet at the source instead of filtering them: the header of any export of a tracked COMP (one with a build number) carries only fields that change for a reason -- format and version (the file format), generator (the Embody version), td_build (the TouchDesigner build) and build (the COMP's save count). The export time lives in the externalizations.tsv timestamp column and git history; the source .toe name is no longer recorded. Exports of COMPs without a build number (untracked or portable networks) still write exported_at and source_file. Files written before this change keep both fields until their next real change.
Earlier versions of Embody installed a git textconv driver (.embody/tdxn_textconv.py, git config diff.tdxn.textconv) that hid the header from diffs. VS Code reads old versions through that driver, so it hid the header there too and could stage files without one. Embody retires it the next time the project opens, Envoy on or off: it unsets the key only when it points at Embody's own script, then deletes the script; a driver you pointed at your own tool is left alone. The diff=tdxn lines in .gitattributes stay -- with no driver configured, git shows a plain diff. For a header-free view on the command line, use git diff -I'^(build|generator|td_build):'.
Crash safety and .embody_backup¶
Every .tdxn write is atomic — the content goes to a temp file in the same directory, is flushed and fsynced, then os.replace()s the target. A crash or power loss mid-write leaves either the complete old file or the complete new one, never a half-written one.
Before each write, Embody also rotates two generations of the previous content into a .embody_backup/ folder beside your .toe, mirroring the network file's relative path:
my-project/
├── embody/
│ └── Foo/
│ └── bar.tdxn ← current
└── .embody_backup/
└── embody/
└── Foo/
├── bar.tdxn.bak ← previous write
└── bar.tdxn.bak2 ← the one before that
A rewrite that changes only the header's format or version (a format bump re-saving an unchanged network) skips the rotation, so it never pushes an older network out of .bak2.
After each write the file is read back and re-parsed; if that validation fails, the newest surviving backup is restored automatically and the log names the exact file it came from (a header-only rewrite puts the previous file back instead). ext.Embody.reconstructTDXNComps and the post-save export roll back the same way if reconstruction fails. Recovery tries .bak first, then .bak2.
The folder holds backups of both .tdxn and .tdn files — a COMP externalized before v6.1.0 keeps writing .tdn forever — which is why the name carries no format token.
Renamed in v6.1.6
This folder was called .tdn_backup/ before v6.1.6. Nothing moves or deletes an existing one: new backups are written to .embody_backup/, and recovery still reads .tdn_backup/ when the newer folder has nothing for that file. Both stay git-ignored. Each COMP's legacy copies are retired automatically the first time Embody rotates a new backup for that COMP — which happens on the first save where its network actually changed (an unchanged COMP is skipped entirely and keeps its old copies, deliberately). Leave the old folder alone and it drains itself; there is no need to delete it by hand.
Backups are machine-local scratch and should not be committed. They are superseded on every write and carry no history git does not already have — the .tdxn files themselves are the versioned record. Envoy adds both folder names to your project's auto-managed .gitignore on startup. If your repo predates those entries and already tracks a backup folder, untrack it once:
# run for whichever folder your repo actually tracks -- git errors on a path it does not
git rm -r --cached .embody_backup
git rm -r --cached .tdn_backup
Deleting either folder at any time is safe — the current one is recreated on the next export.
Error Handling¶
TDXN import is best-effort — individual failures don't abort the entire operation.
| Situation | Behavior |
|---|---|
| Unknown field | Ignored (forward compatibility) |
Missing name or type |
Skip operator silently (no log) |
| Missing connection source | Skip connection, log warning |
| Unrecognized parameter style | Skip parameter, log warning |
| Unrecognized flag | Ignored |
| Invalid parameter value | Attempt type coercion; skip with warning if impossible |
| Version newer than build | Log warning, proceed (older/equal versions import silently) |
Unknown $t template reference |
Log warning, skip page |
Design principle
Log warnings for anything skipped so the developer can inspect the result. Never abort an entire import because a single operator, parameter, or connection failed — the partial result is more useful than no result.