TDN / TOUCHDESIGNER NETWORK FORMAT

# your network,
as text.

TDN exports any TouchDesigner network as clean, human-readable JSON — only the parameters you actually changed, structured so any tool can understand it. Your AI agent reads it in one call, at 20–90× fewer tokens than walking operators one by one. Your team reviews it. Git diffs it line by line.

Read the specification → See examples

THE CONTRAST

## binary vs. text.

TDN gives your TouchDesigner networks a second representation — one designed for diffing, reviewing, and reading. A format where every change is visible, every version is comparable, and every operator is text your tools can work with.

### binary `.toe` / `.tox`

- Opaque — requires TouchDesigner to inspect
- Binary diff — "file changed"
- Invisible to AI agents
- Revert = restore entire binary file
- Merge conflicts are unresolvable
- Fixed size regardless of complexity

### text `.tdn`

- Readable in any text editor
- Line-by-line diff — "noise frequency changed from 1 to 2.5"
- Full network context in a single JSON document
- Revert = `git checkout` a specific change
- Merge conflicts are readable and fixable
- Non-default only — a simple network is a small file

COMPLETE REPRESENTATION

## everything that matters.

TDN captures the complete state of a network. Everything that defines the network is in the file; everything that doesn't is left out.

■

### operators

Name, type, position, size, color, comment, tags — the full identity of every node.

■

### parameters

Only non-default values. Constants, expressions (`=`), and binds (`~`) — nothing else.

■

### connections

Input wiring as arrays. Position equals input index. No wrappers, no ceremony.

■

### custom parameters

Complete definitions — pages, styles, ranges, menus, defaults, help text. Ready to rebuild.

■

### flags

Viewer, display, render, bypass, lock, expose — compact arrays with `-` prefix for negation.

■

### DAT content

Text and table data embedded directly in the JSON. Scripts, lookup tables, configuration — all in one file.

■

### operator storage

Dicts, lists, tuples, sets, and bytes. Python types round-tripped with `$type` wrappers.

■

### annotations

Annotate, comment, and network box modes — position, size, color, opacity. Your documentation, preserved.

■

### hierarchy & nesting

Recursive children, COMP inputs, docking relationships, and `tdn_ref` pointers for independently externalized COMPs.

■

### palette clones

Clone relationships preserved. Children excluded — TD recreates them from the clone source automatically.

■

### parameter sequences

Resizable parameter blocks — GLSL uniforms, Combine blocks, channel constants — round-tripped with portable base names.

FORMAT DESIGN

## designed to be small.

Every design decision serves a single goal: the smallest possible output that remains fully readable. A network with 50 operators where you only changed a few parameters produces a file measured in kilobytes, not megabytes.

    // expression — just prefix with =
    "period": "=absTime.frame * 0.1"

    // bind — just prefix with ~
    "tx": "~op('ctrl').par.x"

    // negated flag — just prefix with -
    "flags": ["viewer", "-expose"]

    // connections — position = input index
    "inputs": ["noise1", null, "level1"]

    // constant — no wrapper needed
    "amp": 0.8

- Non-default only — parameters at their default values are omitted entirely, often eliminating 90%+ of the parameter space.
- Type defaults — properties shared across all operators of a given type are hoisted into a top-level section and removed from each operator.
- Parameter templates — identical custom parameter pages appearing on 2+ operators are extracted into `par_templates` and referenced via `$t`. Definitions stored once, values per-operator.
- Compact shorthands — prefix characters instead of verbose wrapper objects. `=` for expressions, `~` for binds, `-` for negated flags.
- Simplified connections — `["noise1"]` instead of `[{"index": 0, "source": "noise1"}]`. Array position equals input index.

Round-trip safe. Export a network, edit the JSON, import it back. Same network. Every time.

And — this is where it earns its keep with AI. The same compaction that keeps `.tdn` files small on disk keeps them cheap in a context window. Envoy's `read_tdn` MCP tool returns an entire subtree — operators, parameters, connections, DAT content — in one compacted call. 20–90× fewer tokens than walking the same network with `get_op` + `query_network`, with a conservative 5× floor verified in CI. Your agent reads the structure once and starts working.

RELIABILITY

## built for safety.

TDN is the source of truth for externalized networks. Every write must be bulletproof.

### atomic writes

Every save writes to a temp file first, calls `fsync`, then uses `os.replace` to swap it in. No partial writes. Ever.

### backup rotation

A `.tdn_backup/` directory keeps two generations of previous versions. Recover from any bad export instantly.

### post-write validation

After every write, the file is read back and JSON-parsed to confirm integrity. If validation fails, the backup is restored automatically.

### content comparison

If the new export matches the existing file, the write is skipped entirely — preserving git timestamps and avoiding noise commits.

### best-effort import

The importer runs 8 ordered phases. A single failed operator never aborts the entire import — partial results are more useful than total failure.

VERSION DURABILITY

## survives TD upgrades.

Some TouchDesigner operators reset certain parameters during initialization — a camera's `tz` is reported as `0` by default, but TD actually creates it at `5`. A non-default comparison that trusts the reported value will silently omit your intended setting. TDN catches this.

The first time you open a project on a new TD build, Embody scans every creatable operator type in the background, samples the actual creation values, and writes a per-build catalog. TDN exports compare against those real values instead of the reported defaults. When you upgrade TD, the catalog detects which creation values shifted between builds and automatically patches your operators to preserve your intent. An elegant solution to a very obscure problem — but the kind that would otherwise cost you hours of debugging a parameter that "just changed" after an update.

Upgrade-proof. Export on one build, open on another. Every parameter value survives.

THE WORKFLOW

## part of the loop.

TDN is not a standalone format. It's the substrate that connects the other two layers of Embody — the format that makes both forward and lateral velocity possible.

### Envoy

forward velocity.

Your agent calls `read_tdn` to see the live network in one compacted payload — 20–90× cheaper than walking operators one at a time. It generates or modifies the JSON, then `import_network` applies it back. Changes appear instantly in your live session.

### Embody

lateral velocity.

Tag a COMP as TDN and every save writes a `.tdn` next to the `.toe` — diffable, reviewable, readable by your agent. Opt into Roundtrip mode and the `.tdn` becomes the source of truth, rebuilding children on open.

### Git

the record.

Commit the `.tdn` file. Every change is a clean diff. Review network modifications in pull requests. Branch off working versions. Revert broken experiments.

The substrate. Tag a COMP. Save it as TDN. Commit the file. Let your agent read it, modify it, import the result. All without touching a binary.

SPECIFICATION

## format details.

extension `.tdn`
encoding UTF-8
MIME type `application/json`
current version 1.3
backward compatible v1.0 through v1.3
JSON Schema tdn.schema.json

A REAL FILE

## this is what it looks like.

    {
      "format": "tdn",
      "version": "1.3",
      "generator": "Embody/5.0.372",
      "td_build": "2025.33020",
      "exported_at": "2026-04-10T08:15:00Z",
      "network_path": "/project1",
      "type": "containerCOMP",
      "options": { "include_dat_content": true },

      "operators": [
        {
          "name": "noise1",
          "type": "noiseTOP",
          "parameters": {
            "amp": 0.8,
            "period": "=absTime.frame * 0.1",
            "harmons": 4
          },
          "flags": ["viewer"]
        },
        {
          "name": "blur1",
          "type": "blurTOP",
          "parameters": { "size": 8 },
          "inputs": ["noise1"],
          "position": [400, 0]
        },
        {
          "name": "level1",
          "type": "levelTOP",
          "parameters": {
            "opacity": "~op('ctrl').par.Fade",
            "brightness1": 1.2
          },
          "inputs": ["blur1"],
          "flags": ["display", "render"],
          "position": [800, 0]
        }
      ],

      "annotations": [
        {
          "name": "annot1",
          "mode": "annotate",
          "title": "noise pipeline",
          "position": [-100, -200],
          "size": [1100, 400]
        }
      ]
    }

Three operators, one annotation, one expression, one bind. Every required field present, every value spec-accurate — readable, diffable, importable.

DOCUMENTATION

## go deeper.

### specification

The complete field reference — every property, every inclusion condition, every default value.

→

### JSON schema

Machine-readable schema for validation and IDE auto-completion.

→

### examples

Complete, minimal, and annotated `.tdn` files you can study and use as templates.

→

### import & export

Keyboard shortcuts, MCP tools, the 8-phase import process, and error handling.

→