Append-only build log. Protocol: every work chunk gets an entry — timestamp, what was done, verification evidence, and an updated ## NEXT. A resuming assistant reads PLAN.md fully, then continues from ## NEXT.
- Deep research + hands-on verification of the
.toepipeline completed (see docs/RESEARCH.md): binary container is proprietary; officialtoeexpandround-trips to text; full graph (nodes, wires, params, expressions, code) recoverable; PoC parser + browser viewer proved end-to-end on a 213-node production file. - Verdict adopted for WebToe: original engine +
toeexpand-based importer; no Derivative assets ever enter this repo.
- PLAN.md authored, then revised per user direction before any code: WebGPU as first-class second backend (backend-agnostic pass contract in
core/passes.ts, WGSL pilot ops in v1, parity = M7, compute path reserved for v2 particles), WASM seams (Kernelsinterface, typed-array layouts, benchmark-gated adoption), and long-term modular monorepo (npm workspaces: core/ops/gpu/io/editor/cli + apps/web; downward-only dependency rule; plugin op API; versioned format with migrations; embeddable editor). - Workspace scaffold + CI (typecheck + tests + build + Pages deploy), zero runtime deps.
- Repo created at github.com/frank890417/WebToe and pushed.
packages/core: types, NodeInst, Graph (TD-style unique namingnoise1/noise2, hierarchy, path resolution incl.../and absolute), Engine (pull cook, per-frame memo, cycle guard, bypass), expression system (new Functionagainst fixed scope: time/me/op()/math whitelist + TD-Python translation with dry-run validation), backend-agnostic pass contract (passes.ts, GpuFacade), versioned serialize with migrations + family-aware stub fallback for unknown types.packages/ops: CHOP family (constant, lfo w/ integrated phase, noise fBm, math, lag, merge, select w/ wildcards, mousein) on the wasm-readyKernelsseam; comp:container, dat:text, per-family stubs. Registry keys namespacedfamily:name.- Evidence:
npm run checkgreen — 23/23 tests (graph structure/wiring/paths, expr eval + TD translation accept/refuse cases, engine cooking incl. expr-driven params, cycle guard, lag convergence, serialize round-trip + stub fallback + version guard). - Fix found by tests: cycle-guard error was wiped by post-input
error=nullreset — moved reset to cook start.
packages/gpu: WebGL2 backend complete (program/uniform caches, per-(node,slot) ping-pong texture pools, previousFrame for feedback, media upload, letterboxed blit, readPixels thumbnails, release/dispose). WebGPU backend working skeleton: explicit shared bind-group layout (uniform UBO at 0 globals/1 ops with 256-aligned offsets, sampler+4 textures), pilot pipelines, media upload, blit; readPixels deferred to M7 (async readback), blit aspect-fit deferred to M7.packages/opsTOP family (17 ops, original GLSL): constant, noise (3D value-noise fBm), ramp, rectangle (sdf), transform (inverse-map + extend modes), level, monochrome, hsvadjust, blur (separable multi-pass via slots), composite (4-input, 6 blend ops), displace, edge (sobel), feedback (lazyInputs + previousFrame), null/out, imagein/videoin/camerain (media upload, placeholder + error states). WGSL pilots: constant, ramp, level.- Core contract additions:
lazyInputs(feedback cycle-break), runPassslotparam,setTimeon facade. - Evidence: 26/26 tests incl. backend-contract checks (shader sources per declared backend, WGSL packing-rule lint, full WebGL2 coverage). Visual: hello-noise renders animated at 60 fps on webgl2 with lfo→level.brightness expression visibly modulating;
?backend=webgpurenders animated circular ramp via pilot WGSL at 60 fps. Zero console errors on both.
- M3
packages/editor(embeddable, framework-free): NetworkView (DOM nodes over SVG wires; pan/zoom; node drag; wire drag incl. input-disconnect-redrag; palette via Tab/double-click with search + keyboard nav; COMP enter/exit + breadcrumb; display flag; delete; error badges), ParamPanel (float/int slider+number, toggle, menu, string, color+swatch, xy; per-param ƒ expression toggle, disabled-expr display for imports), Viewer (TOP blit follows selection, CHOP scope with per-channel sparklines + values, DAT text, feeds engine.io.mouse), thumbnails via readPixels at ~6 Hz with row-flip.packages/iosave/load (.webtoe.json download/picker + URL fetch for examples). apps/web boots the editor with a starter patch;window.__webtoedebug handle. - M4: five examples authored and browser-verified: 01 hello-noise (load pipeline + expr binding), 02 feedback-trails (full feedback loop verified — mouse orbit leaves fading trails; required fix: feedback now seeds transparent black on first frame instead of null), 03 lfo-garden (3 chains, add composite, hue drift — renders rich), 04 webcam-displace (graceful placeholder path verified headless; live with camera), 05 chop-playground (scope shows raw vs lagged channels; lag drives ramp rotation).
- Interaction smoke (real pointer events in browser): select ✓ params ✓ palette-create ✓ wire-drag rewire ✓ (thumbnails visibly switch source) COMP enter/exit + breadcrumb +
u✓ CHOP scope ✓. Zero console errors. 26/26 tests green.
packages/io/toedir.ts: ProjectLoader adapter parsing toeexpand output (.n node grammar incl. inputs blocks, .parm modes 0/16/17 with BOM tolerance, .text DAT payloads, nested networks). Conservative TYPE_MAP (~30 TD types) + per-op PARAM_MAP (incl. menu token translation, color component gathering, level/transform/lfo/math tables), TD-Python expressions translated or kept inert as tdExpr, per-family stubs for everything else, honest ImportReport (mapped/stubbed counts, stub-type histogram, skipped cross-network wires, unmapped parm count).- Editor: "import .toe.dir" toolbar button (webkitdirectory) + import report dialog.
packages/cli/toe-convert.mjs: locates the USER'S OWN toeexpand (mac bundle / win bin globs or --toeexpand), runs in tmpdir (tolerating toeexpand's nonzero-on-success exit), emits .webtoe.json structure. Never bundles Derivative binaries.- Evidence: 30/30 tests (synthetic-fixture importer suite: mapping, stubs+foreignType, wires, hierarchy, color gather, expression translate/inert, menus, report). REAL-WORLD test (local only, not committed): a 213-node production .toe imported 213/213 nodes (56 runnable after adding TD's
TOP:comptoken discovered in the report histogram; 157 stubs — POP/SOP-heavy project), then loaded in the browser: noise TOPs live-animating, ramp running, switch stub passing video through, layout/wires preserved, viewer rendering the imported network. CLI verified end-to-end on the same .toe.
- README (features, quickstart, op table, architecture summary, roadmap, trademark disclaimer), docs/ARCHITECTURE.md (layers, load-bearing contracts, backend matrix, decisions log incl. lessons: toeexpand exits nonzero on success; TD type tokens ≠ UI names e.g. Composite TOP =
comp). - Production verified at https://frank890417.github.io/WebToe/ — app serves (200), examples fetch, lfo-garden loads and renders end-to-end, all five thumbnails paint, viewer blits final output. Note for future debugging: headless/hidden tabs pause RAF — drive
__webtoe.loop()manually when verifying via automation. - Bundle: 90 KB JS / 27.6 KB gzip, zero runtime dependencies. 30/30 tests, CI green per milestone.
v1 (M0–M6) COMPLETE.
- Automated ".toe reading works" suite (the goal's core ask): original fixture network authored as expanded text, round-tripped through the real
toecollapse/toeexpand(2025.32460) and committed as both binary.toeand canonical expansion (provenance in tests/fixtures/README.md). Layer 1 (CI, always-on): importer → graph assertions covering types, COMP-boundary + tunnel wires, parm modes 0/16/17, expression translation (math.sin(absTime.seconds)*0.5+1→ engine-evaluated), stub honesty (TOP:mirror→top:stub), report numbers. Layer 2 (auto-skips without TD): realtoeexpandon the committed binary → identical import to the committed expansion;toe-convertCLI end-to-end. Both layers ran locally (binary layer ~300 ms/test). - COMP in/out tunneling: top:in/chop:in (index param, TD name-digit import), chop:out, container-output-via-out-child, COMP-boundary wiring + capacity from in-children, editor stubs. Real-project effect: CLI import improved 56 → 71 runnable nodes (TOP:in×7, TOP:comp×6, CHOP in/out now live).
- M7 WebGPU parity: WGSL for all shader-driven TOPs (noise/rectangle/transform/monochrome/hsv/blur/composite/displace/edge + placeholder/feedback-seed), letterboxed blit via setViewport, async copyTextureToBuffer thumbnail cache. Browser-verified on
?backend=webgpu: lfo garden renders, thumbnails paint, feedback trails accumulate (lit-pixel check 69.6% vs ~1% single-rect), zero node errors; WebGL2 regression-checked clean. - 42 tests green; @types/node added for the node-API test layers.
- Corpus analysis (private tooling in the lab repo): 199 unique production .toe projects found (2022–2026 daily practice), 60 expanded and aggregated — 28,698 nodes. Findings: 32.3% node coverage today; 3D pipeline (geo 48/60, render 46/60, SOP/MAT) is the dominant blocker; then routing TOPs (switch/math/reorder/select),
parent().parexpression family, TOP:glsl (32/60), POPs (2025–26 era), replicator+table (60/60). Published as docs/ROADMAP.md (aggregate numbers only, nothing project-identifying) with phases R1–R7 and measured targets. - Importer upgrade from the data: parameter mode is a bitfield — bit 0 = expression (decoded from samples: 17, 49, 273…), bit 4 = string-with-default-expr. Importer now catches flagged expression modes (~7k more live expressions in the corpus); regression test added (mode 49).
- Engine: fps delta floor 4 ms (250 fps ceiling) so abnormal frame drivers can't skew the estimate.
- README completed: real screenshots (docs/media/, 6 shots incl. webgpu + import report with real measured numbers) captured via committed tools/capture-screens.mjs (playwright-core + system Chrome, drives
__webtoe.loop()because headless tabs pause RAF); badges, gallery, import guide, architecture/roadmap links. Repo metadata set: description, homepage, 9 topics. Pages already on workflow builds.
- Expression engine v2: live
.paraccess on node proxies (parent(n),op('x').par.y,me.par.x) with a parameter-evaluation cycle guard; Python ternary → conditional,and/or/not,int()→trunc(),mod.math.*,None; f-strings/.menuIndex/panel.*stay inert. Corpus says.parpatterns are 38% of all 38.5k live expressions — this was the multiplier. - R1 ops: top:switch/select/math/reorder/flip (GLSL+WGSL, parity tests enforce), chop:switch/speed/par, DAT-lite (table w/ .table sidecar, select/null/in/out), COMP:null→container. Container out-children now include dat:out.
- Importer: ~20 new TYPE_MAP entries + param maps; .table sidecar text attach (NUL-guarded).
- Measured (the self-evolution metric): corpus 32.3% → 47.1% of 28,698 nodes runnable; reference production project 56 → 71 → 88 runnable. Live browser verification: switch/flip/math chain renders;
op('lfo1').par.frequency*10evaluates 4.0; zero errors. 48/48 tests. - Deep research landed (104 agents, 23 claims verified 3-0): TD GLSL TOP injected contract fully documented → ROADMAP R4 shim table (headerless GLSL 4.60, sTD2DInputs arrays, vUV, uTD*Infos res packing, TDOutputSwizzle, pixel/compute modes). WebGPU-particle + minimal-renderer findings inform R3/R5 specs. JSON-format status: no newer-than-2025-10 statements found.
- Engine: fps delta floor refined earlier today holds; param cycle guard added (
.parchains can recurse).
- Real-time previews: one transparent GPU compositor canvas overlays the editor; each frame it scissor-blits the viewer rect plus every visible TOP node's thumb rect on both backends (contain-fit, clipped to the network panel). The 6 Hz readPixels thumbnail path is gone — previews run at full frame rate with zero CPU readbacks.
blitToCanvas(tex, rect?)+clearCanvas()joined the pass contract. - OP Create dialog: palette redesigned (family tabs TOP/CHOP/COMP/DAT, searchable 3-column grid, arrow/Tab/Enter keyboard flow, search spans families). Original styling.
- Three real 2022 sketches imported as bundled examples (06 pseudo-voronoi, 07 fractals, 08 chop study): converted via the full importer from the original .toe files, TD scaffolding (local/perform) stripped, lightly adapted (dead movie source → noise; circle TOP stub → soft rectangle;
clickchannel idiom →lmbwith idle offsets). All three verified rendering + interacting, zero node errors. - Two engine fixes found by the real sketches (the point of dogfooding): (1) composite/over layer order now TD-compatible — input 0 is the TOP layer (the imported patch's opaque background had covered everything; bundled trails example rewired accordingly); (2) importer unquotes quoted string constants (
channames 0 "tx ty"had kept literal quotes, breaking select patterns) — regression tests added for both. 49 tests.
- Drop-anywhere .toe intake: drag a
.webtoe.json(loads), a.toe.dirfolder (recursive FileSystemEntry walk → importer), or a raw.toe/.tox(guide modal: proprietary-binary explanation, copyable per-OS toeexpand commands pre-filled with the filename, CLI alternative, "pick the expanded folder…" button). The load button also accepts.toeand routes to the guide. Re-verified: no zlib streams anywhere in .toe binaries (full-file scan) — in-browser binary parsing remains impossible, the guide is the honest path. - Inlet/outlet tooltips: hover shows
input N · FAMILY — description (required)/output 0 · FAMILY (texture/channels/text); per-input descriptions via newOpSpec.inputLabels(displace, composite, math ops); containers explain tunnel routing. - Math CHOP completed to the TD pipeline: channel pre op → combine channels → combine CHOPs → channel post op → mult-add → range remap, organized in parameter pages (op / mult-add / range) via new
ParamSpec.page; the panel renders page section headers; TOP common page groups resolution params. Importer maps preop/chanop/postop/fromrange/torange tokens. Pipeline unit test (49 total). - GitHub link (octocat icon) in the toolbar → repo (configurable via
EditorOptions.repoUrl). - Browser-verified: modal renders + copies, drop-load of a synthetic project works, param pages render, tooltips correct, zero errors.
- docs/TD-PARITY.md: crawled the official docs.derivative.ca operator categories (TOP ~147, CHOP 172, SOP 115, DAT 77, MAT 13, COMP 45, POP 106 ≈ 675 ops) and wrote the full "evolve toward TD" charter — portable/web-equivalent/native-only classification, per-family tier targets cross-referenced with corpus usage, engine-concept gap table (multi-sample+time-sliced CHOPs as the audio prerequisite, WebAudio bundle, 3D R3, GLSL R4, POP compute R5, parameter-system depth incl. pulse/bind/custom COMP pars, panels R7, perform-mode-as-route, timeline/keyframes, JS-callback-DAT as the Python-boundary answer), and the standing measure→pick→implement→verify→record loop.
- 09 showcase example: 27 nodes / 25 distinct op types across all four families — camera→flip→edge (graceful placeholder headless), circular-ramp kaleidoscope COMP (in/out tunnels, ± speed-integrator rotation, difference mix), color-noise + hue drift, mouse-thirds source switch, reorder-remixed noise displacement, hue-drifting feedback trails, math-TOP pulse, CHOP rig (mousein→select→lag, lfo×2, speed, par reader, full-pipeline math, merge scope), DAT notes. Verified: switch follows mouse thirds (index 0/1/2), speed integrates, output non-black, zero node errors.
- Plan first (user directive): docs/R3-3D-PLAN.md — data model, 18-SOP/5-MAT/4-COMP op tables with crawled TD tokens (Render/Geometry/Camera COMP + Line MAT pages fetched from docs.derivative.ca), scene-pass contract, renderer design, 5 declared v1 limitations, R3a–R3e commit gates.
- Core: Family +SOP/MAT; GeometryData (typed-array, versioned for VAO caching); MaterialSpec/SceneObject/SceneLight; zero-dep mat4 (compose w/ pivot + TD rotate order, perspective/ortho, lookAt, invertRigid — unit-tested); ScenePassSpec + renderScene on the pass contract.
- Ops: 19 SOPs incl. skin lofting w/ computed normals, copy-to-points w/ template Cd inherit, spatial noise displacement (deterministic), tunnels; MATs constant/lit(phong+pbr→one honest shader)/line/pointsprite/wireframe/switch/null; comp:geo (display-SOP pick, material+instanceop with TD
./path semantics — resolveParamPath fix found by tests), comp:cam (look-at, vertfov), lights; top:render (TDgeo* ^geo7patterns, headlight fallback). - WebGL2 scene renderer: depth renderbuffers in the target pool, VAO/program caches, instanced draws (interleaved offset+color), translucent-last sort, 4 original GLSL shaders. WebGPU declares the limitation cleanly.
- Editor: SOP/MAT palette tabs + colors; every SOP/geo node gets a live auto-orbit 3D preview through the compositor.
- Importer/fixture: ~40 new TYPE_MAP entries + token-accurate PARAM_MAPs; fixture v3 regenerated through real toecollapse/toeexpand with a 3D branch (geo/SOP-chain/MAT/cam/light/render, canonical parm modes) — 23 nodes, CI-covered.
- Measured: corpus 47.1% → 62.3%; DNA sketch 55→71%, Fire Dance 65→74%. Example 10 (ribbons + noise-scattered instanced spheres + orbit cam + glow post) renders at full rate, zero errors. 60/60 tests.
Backlog (v2 — full detail and measured targets in docs/ROADMAP.md, full parity map in docs/TD-PARITY.md):
- M8 compute particle family (POPs spirit) + audio-rate CHOPs (wasm decision per PLAN §5 benchmark gate).
- Importer round 3: cross-network wire resolution (258 wires skipped on the 14.7k-node show file), more TYPE_MAP/PARAM_MAP entries from the real histograms below, media relink-by-drop flow.
- Editor round 2: marquee select, undo/redo, node rename UI, COMP display-child preview thumbs, mobile/touch pan-zoom.
- Watch derivative.ca for the official JSON text format → add the second ProjectLoader (PLAN §3 adapter).
- Bridge round 2: progress events for multi-second expansions (a 20 MB project spends ~4.6 s in
toeexpandbehind a single toast), and--watchso re-saving in TD re-imports live.
- docs/HANDOFF.md: the full continuity document for the next agent — resume protocol, measured curve (32.3→47.1→62.3), experiment ledger with verdicts (incl. the twice-confirmed .toe-binary dead end), architecture invariants, footgun catalog (TD semantics + platform + editor), strategy principles, standing order with design head-starts (multi-sample CHOP sketch, R4/R5), 12-month structural notes. Private machine/corpus companion lives outside the repo.
- NDI In/Out: honest architecture — local bridge (
packages/ndi-bridge: ws server,--mockanimated UYVY test source + frame sink with zero NDI deps; real mode via user-installed NDI runtime + optionalgrandiose) ⇄ versioned binary frame protocol ⇄top:ndiin/top:ndioutops ⇄ WASM video kernels (AssemblyScript inpackages/wasm-kernels, 1 KB artifact committed + served; BT.601 UYVY⇄RGBA + BGRA swizzle; JS reference is unit-tested fallback; loader swaps provider at boot — console logsvideo kernels: wasm). - Verified end-to-end in browser with the mock bridge: ndiin renders the animated rainbow pattern through the WASM path (zero errors, frames animate), ndiout streams noise back (bridge log:
← browser frame #1 320x180 RGBA). 66/66 tests incl. kernel math + protocol round-trip. - TD-PARITY: NDI reclassified native-only → web-equivalent via local bridge; the pattern generalizes (Art-Net, Syphon capture). README documents usage + NDI trademark notice.
The brief was "actually read/parse .toe with no extra operation". Two halves: retire the manual step, and fix what only shows up on real files.
- Re-attacked the binary container first (HANDOFF §2 says do not retry without new information — the new information was a 16,562-file local corpus). Per-byte-position entropy over 590 files: the magic is 3 bytes
"10\0", and the previously documented "length field at offset 4" is falsified (ratio to filesize 0.00–14,053; unrelated large files share near-identical values there). Raw deflate at every offset plus all 7 bit shifts, and lzma/bz2 sweeps: zero hits.toeexpandlinks zlib vialibUTbut plainly does not use it for this. Third confirmation of the dead end — recorded in RESEARCH §1 with the old model kept, marked superseded. packages/bridge(npx webtoe) — zero-dependency loopback service: finds the user's owntoeexpand, expands an uploaded container in a temp dir, returns the expansion as importer-ready files, and serves the built app so there is no mixed-content or CORS problem at all. Ships nothing of Derivative's; binds 127.0.0.1; CORS +Access-Control-Allow-Private-Networkso the hosted GitHub Pages app can use it too.- Editor: dropping (or picking) a
.toe/.toxnow probes the bridge and imports straight through. No bridge → the guide modal leads withnpx webtoeand keeps polling, so starting it in a terminal makes the dialog continue on its own; the manualtoeexpandroute and folder drop stay as fallbacks. - Framed sidecar container decoded (RESEARCH §2.2,
packages/io/src/tdContainer.ts): real.text/.tablearetag + "\n*" + u32be×4 + (type,len,bytes)…, not plain text. Before this every DAT body in a real import was either 27 bytes of garbage or silently dropped — the committed fixture is hand-authored plain text, so no test could have caught it.ImportFilegrew an optionalbytes(); all four producers supply it. toeexpandcannot open non-ASCII filenames (reads paths as Latin-1). Every Chinese-named project failed. Bridge and CLI now stage under an ASCII name; regression-tested.- Network view frames its content on entry — imported networks keep TD tile coordinates thousands of pixels from the origin, so a correct import used to look like an empty canvas. Defers when the panel has no measurable size instead of computing a transform from zeros.
- Measured, on the user's real 20 MB show file (
.toe→ running graph, drag only): 8.0 s end to end (4.6 s of ittoeexpand), 14,710 nodes, 10,239 runnable (70%), 2,244 expressions translated, 2,251 DAT bodies recovered with zero framing leaks, no console errors. Small daily sketch: 79 nodes, 57%, 0.2 s. - 80/80 tests (was 66): new
td-containersuite (7) andbridgesuite (protocol everywhere, expansion + non-English filename where TD is installed).
- npm name
webtoeis free (registry 404, checked today). Package made publishable: binwebtoe/webtoe-bridge, tarball carries the built app inpublic/(findAppDistalready looked there by design),npm run release:prepstages it, own README with trademark notice. - Proven the way npx will run it: packed the tarball, installed into a clean prefix, ran
npx webtoe→ served the app (200), expanded a.toe(38 files). Found and fixed a real bug on the way: npm's bin is a symlink andpath.resolveis lexical, so the is-main check never matched and the CLI silently did nothing —realpathSyncon both sides fixes it. - Shared-bridge hardening (for the zero-install-for-visitors option):
--hostto bind beyond loopback,--tokenbearer gate on /expand (health advertisestokenRequiredwithout leaking it), 3-way concurrency cap → 429, loud warning when public without a token. Client side:?bridgeToken=saved once, sent as Authorization from then on. +2 tests (82 total). - docs/PUBLISH.md: the three switches (npm publish / git push / shared converter) with the sharp edges written down — toeexpand parses untrusted input so a public converter belongs on a disposable machine, EULA for server use is unverified per RESEARCH §6, privacy story changes, HTTPS-only for tunnels.
- String flip (README quickstart + guide modal →
npx webtoe) staged as its own commit, to be pushed afternpm publishso the site never instructs a 404 command.
webtoe@0.1.0is on npm (owner published). Verified the way a stranger hits it:npx -y webtoe@0.1.0from a clean prefix → serves the app (200) and expands a.toe(38 files). CI had gone red on the string-flip commit (npm cilockfile out of sync after the package rename) — fixed by committing the syncedpackage-lock.json; green again, Pages redeployed. Confirmed the live https://frank890417.github.io/WebToe/ now carries the bridge client (new bundle) and imports a real.toethrough the loopback bridge end to end.- bridge.py — the no-Node path. Every TouchDesigner install ships a Python interpreter, so a single stdlib-only file gives the exact same protocol as
npx webtoe(/health,/expand, same JSON, same CORS + Private-Network headers, same ASCII-staging + framed-sidecar + token/host/concurrency behavior). Served at/WebToe/bridge.py, linked from the guide modal. Verified with TouchDesigner's own bundled Python 3.11 against the live hosted page: dropped a.toe, imported 23 nodes 96% through the Python bridge, CJK filename handled, PNA preflight present. - Guide modal now offers three tiers in order —
npx webtoe, downloadbridge.py, manualtoeexpand— and keeps polling so any of them continues the dialog on its own. - +1 test suite (
bridge-py, protocol parity, auto-skips without python3): 86 tests. docs/PUBLISH.md gains the visitor-tier map.
Dogfooding two of the user's own sketches exposed what "imports fine but does nothing" actually meant.
- POP family (TD 2025 GPU point operators): a 2025 project was 38% POP
nodes, all stubs. POPs carry geometry like SOPs, so faithful counterparts now
run on the SOP implementations; attribute-only/analysis POPs pass through;
dynamics stay stubs; unmapped POPs degrade to
sop:stubso the chain holds. 43% → 73% runnable. - Network backdrop (TD-style output behind the nodes). First attempt painted
into the GL compositor — which sits above the node DOM (that is how thumbs
paint), so it covered every label. It now owns a 2D canvas under
.wt-world, fed by a throttled readback. Toggled;ienters a COMP,ugoes up. - Fixed a self-inflicted bug: adding
u_opacityto the shared__blitprogram brokereadPixels(an unset uniform reads 0 → every readback black). Lesson: adding a uniform to a shared program means auditing every call site. - CHOP-channel instancing on
comp:geo— TD's dominant particle technique (instanceop → CHOP, one channel per transform component). Was SOP-points only.
It imports at 74% with zero node errors and decodes in-browser in ~3 ms, but both Render TOPs are black. Remaining chain, in order:
chop:sopto(SOP → CHOP channels) is unimplemented, somerge2carries one 1-sample channel instead of the tx/ty/tz arrays the instancing reads. This is the blocking piece.geo1has no base geometry — it contains onlyin1/out1(sop:inwith no source). Determine what TD feeds it before inventing a fallback.SOP:twist(×2) andSOP:filein(×3) still stub;fileinneeds external model files, which.toedoes not embed.- Then verify
mat:pointspriteagainst the reference render.
Goal was "opens fully and looks close to TouchDesigner". It does now.
Traced the project's real chain instead of guessing, which turned "black render" into five specific gaps:
grid1 -> twist1 -> noise3 -\
merge1 -> sopto1 -> merge2 -> geo1 (4200 instances)
grid1 -> twist2 -> noise4 -/ |
cam1 -> render1 -> lookup1 <- ramp1(keys DAT)
- sop:twist — a twisted grid is the double helix; animating the pivot
sends the twist travelling. Entirely procedural: no external geometry, so
the
SOP:fileinstubs turned out to be irrelevant to the visual. - chop:sopto — one sample per point as tx/ty/tz (+rgba), the bridge that lets geometry drive instancing.
- comp:geo CHOP-channel instancing — TD's dominant particle technique; WebToe had SOP-points only.
- Node flags were never parsed. TD keeps render/display/bypass on the
.nflags =line, and the Render TOP only draws Geo COMPs whose render flag is on — so a byte-perfect import still drew nothing. This one would have silently broken every 3D project. - top:lookup + ramps that read their keys DAT — the palette lives in a
pos r g b atable, not in the colour parameters.
Measured: 108 nodes, 78% runnable, 4200 live instances, 234 files decoded in-browser in ~3 ms, and the output matches the reference render's palette (black → deep blue → teal → green → orange → pale blue).
Follow-ups from that session, now also done:
- Per-instance scale — the instance buffer packs translate+colour+scale (stride 40) behind a new vertex attribute; point sprites take their size from it, which is how CHOP-driven rigs vary dot size.
- Hold-last channel semantics — a channel shorter than the instance count
holds its last sample, as TD does. This is what lets a single-sample channel
drive every instance (the DNA sketch feeds
chan1to both scale and colour for all 4200); previously every instance after the first silently got 1. pointscale— the Point Sprite MAT's size token ispointscale, notpointsize, so real projects had been importing default-sized sprites.
- POP dynamics (particle/force solvers). POPs import and their geometry ops run; the simulation side is still stubbed. Deliberately deferred.
SOP:filein(external models) andCHOP:audiospectremain stubs — neither affects this sketch.- WebGPU has no scene pass yet, so instancing is WebGL2-only.
- The importer's
.toebinary path (private) still degrades on very large multi-segment projects; the bridge covers those.