Open source from Noise Factor · more projects
Measured support is recorded on the compatibility report;
open gaps are the issues labelled gap.
Run Noisemaker's procedural visuals inside Derivative TouchDesigner.
This package supports the "Export Shader Pipeline" feature in Noisedeck.app. The feature runs shader compositions on other platforms. Noise Factor derives this package from the upstream Noisemaker Engine project and tests it for pixel-level parity.
Noisemaker is a procedural visual engine. You write tiny text programs — chains of effects — and it renders live, animated GPU textures:
search synth, filter
noise(scaleX: 60).bloom().write(o0)
render(o0)
That little language is Noisemaker's DSL (a domain-specific language for visuals). The original engine runs in the browser at noisedeck.app.
Noisemaker for TouchDesigner runs that same engine inside TouchDesigner: the same programs and
the same 210 effect definitions, built as a live network of TouchDesigner's own GLSL operators. The
export kit lists every effect except media, scope and spectrum, which read external inputs. Use it to generate textures,
backgrounds, and animated source material from code, with no image files.
It is self-contained: the runtime compiles the DSL and renders it entirely inside TouchDesigner — no internet, no Node.js, no separate engine to install.
- Generate animated textures from a short program — noise, gradients, patterns, color grades, blurs, warps.
- Run simulations on the GPU — particle/agent systems (flocking, slime/physarum, reaction-diffusion) and fluid (navier–stokes).
- Render 3D, too — volume raymarching, lit volumes, and six-face cubemaps, not just flat images.
- Wire the result into any network — the output is an ordinary TOP, so it feeds anything a TOP feeds: composites, materials, projection, output.
- TouchDesigner 2025.32820 or later (arm64-native, macOS). The free Non-Commercial tier is enough.
- A logged-in, GPU-capable desktop session. TouchDesigner is not headless — there is no dedicated-server / CI rendering.
- Verified on Apple Silicon / Metal.
brew install --cask touchdesigner- One time only: create a Derivative account. Activate the license through the GUI. A fresh install stops at the activation modal until activation is complete.
That is everything needed to render. The effect data (210 JSON definitions) and shaders (301
translated .frag files) are committed, and the runtime imports only Python's standard library
and TouchDesigner's built-in td module. Node and a reference-engine checkout are needed only to
regenerate assets or run the parity tests (see Contributing).
The quickest way to see it work — build the flagship demo (present_hero.dsl: particles →
navier–stokes → palette → lighting → lens) as a live TouchDesigner network, evolve it 30 s, and write
a PNG to parity/out/:
parity/present_screenshot.sh # a few minutes at 1024²; set NM_FRAMES=300 NM_SAMPLES=300 for a faster lookIt compiles the DSL with the in-engine compiler (no reference engine required), writes the rendered
frame to parity/out/<prog>.f1800.candidate.png, and saves a screenshot to
parity/out/<prog>.tdshot.png. On a successful capture it closes TouchDesigner.
If the screenshot is blank (see the Screen-Recording note below), it leaves TouchDesigner open so you can view the result.
Note: The on-screen screenshot needs macOS Screen Recording permission for your terminal (System Settings → Privacy & Security → Screen Recording) and numpy + Pillow (
python3 -m venv parity/.venv && parity/.venv/bin/pip install -r requirements.txt). Without them, the render still saves toparity/out/<prog>.f1800.candidate.png, but the screenshot step reports BLACK.
Every DSL program has the same shape:
- Name the namespaces it uses (
search synth, filter). - Chain the effects.
- Write the result to an output surface (
.write(o0)). - Select a surface to show (
render(o0)).
The runtime is one Python class, NMRenderer, that builds the network under a Base COMP from a
DSL string. Drop this in an Execute DAT (onStart) inside your COMP:
import sys
sys.path.insert(0, '/path/to/noisemaker-for-touchdesigner/td') # the package dir (package name: noisemaker)
from noisemaker.runtime.nm_renderer import NMRenderer
def onStart():
comp = parent() # a Base COMP to build the network under
nm = NMRenderer(comp, width=1280, height=1280)
comp.store('nm', nm) # keep a reference alive
nm.set_dsl('search synth\nsolid(color: [0.9, 0.3, 0.5]).write(o0)\nrender(o0)')
# The display TOP must live in the same network as the built nodes: TD wires connect
# siblings only, so a cross-network connect (COMP-internal nm.Output -> an external
# 'out') is silently refused. Create 'out' inside your COMP, next to the nm network:
out = comp.create(nullTOP, 'out') # your display/export TOP
if nm.Output is None:
print('[noisemaker] build failed — nm.Output is None; check the Textport '
'for the compile error before wiring a display TOP.')
else:
out.inputConnectors[0].connect(nm.Output) # nm.Output is an ordinary TOP
out.cook() # out now shows the rendered frame| Member | Purpose |
|---|---|
NMRenderer(owner_comp, *, shaders_root=None, width=256, height=256, time=0.25) |
Build under owner_comp (a Base COMP). shaders_root relocates the committed effect assets. |
set_dsl(src) |
Compile a DSL string live in-engine and build the network. |
set_graph(path) / set_graph_dict(d) / set_graph_str(text) |
Build from a pre-compiled render graph (file / dict / JSON string). |
Output |
The terminal TOP of the built network — wire it into your own network. |
resize(width, height) |
Re-cook at a new resolution. |
render_to(path, time=0.25) |
Cook one frame and save a PNG. |
Integration constraints:
- 3D-volume effects are clamped by
NM_MAX_VOLUME_SIZE(default 32) so the volume stays under the free tier's 1280×1280 cook limit. It is applied when the network is built. Raise it on a Commercial/Educational license. .toe/.toxfiles are binary and can't be authored offline. Runtd/make_bootstrap.pyonce inside an activated TouchDesigner to materialize and save a reusable hosttd/noisemaker.toe.
- Compiler. The in-TouchDesigner Python compiler produces the same render graph as the reference
compiler for the programs in
parity/corpus/andparity/programs/(parity/compiler/check_*.pyat the revision pinned inscripts/test). The one program it skips,parity/corpus/B5oBsA.dsl, is invalid and both compilers reject it. - Rendering.
parity/sweep.shrenders every program inparity/programs/in TouchDesigner and grades it against the reference engine's own render at the pinned revision (same GPU class, Metal on macOS) with the per-effect tolerance listed in the script. Feedback effects are graded over 8 frames byparity/accumulate.sh. The latest results are on the compatibility report; this README carries no copy of them. - 3D. Volume raymarching, lit volumes and six-face cubemaps render, with volumes clamped to
NM_MAX_VOLUME_SIZE(see Integration constraints). - Chaotic programs. Chaotic particle-and-fluid programs and continuous solvers render deterministically and stay bounded, but they are a different instance of the same chaos, not a pixel match (docs/CHAOS-GATE.md).
Platform notes: docs/TD-PLATFORM-NOTES.md.
Noisemaker turns a DSL program into a render graph — a normalized list of GPU passes. That graph is the shared seam every Noisemaker port targets. Noisemaker for TouchDesigner ports the whole compiler to Python (so it runs inside TouchDesigner) and builds the graph as a live network of GLSL TOP operators that TD cooks.
Because TouchDesigner's GLSL TOP is OpenGL GLSL with the same raster origin as the reference's WebGL2 backend, the per-effect shaders are translated directly from the reference GLSL — no Y-flip, no math edits.
→ ARCHITECTURE.md (how it maps onto TouchDesigner) · PORTING-GUIDE.md (translating a shader) · docs/GRAPH-JSON-SCHEMA.md (the render-graph format).
Contributions follow the Noise Factor contributing policy and Code of Conduct. The notes below cover this repository's own tooling.
Rendering needs nothing external. The parity tooling, however, compares TouchDesigner's output
against the reference engine, so it needs NM_REFERENCE_ROOT set to a noisemaker checkout at the
revision pinned in scripts/test (NM_REFERENCE_SHA_PINNED):
scripts/test # unit tests + compiler gates (no TD, no GPU)
NM_REFERENCE_ROOT=/path/to/noisemaker parity/sweep.sh # per-effect catalog in TD
NM_REFERENCE_ROOT=/path/to/noisemaker scripts/parity-summary # the sweep, summarized as PARITY-SUMMARY
NM_REFERENCE_ROOT=/path/to/noisemaker parity/corpus_sweep.sh # live DSL → graph → render the corpus in TD→ ARCHITECTURE.md (how parity runs without a headless TD) ·
PORTING-GUIDE.md (translating a shader) · reference/01–10 (engine specs
shared across all Noisemaker ports).
td/noisemaker/ the package — runtime (network builder) + live compiler + committed effects & shaders
parity/ golden-image test harness + DSL programs (per-effect catalog + blaster corpus)
tools/ Node dev tooling (reference graph export, definition + shader codegen)
reference/ engine specs shared across all Noisemaker ports
scripts/test the engine-free test entry point
docs/ ARCHITECTURE.md PORTING-GUIDE.md design, porting rules, platform notes, chaos gate
MIT (see LICENSE). Use of the Noisemaker and Noise Factor names in derivative products is subject to the Trademark Policy.
Copyright © 2026 Noise Factor LLC
