The Visual Graph
A visual in resonon is a node in a graph, whether it is a shader you wrote or a ready-made operator
like gfx.Blur. Nodes are wired with the same two operators that route audio, and the graph is
turned into a sequence of GPU passes every time it changes. This page explains the rules of that
graph and why they are shaped the way they are.
A name is an operator
Section titled “A name is an operator”Every visual is made with a name: gfx.Blur("soft"),
gfx.load_shader("plasma", "shaders/plasma.glsl"). The name is its identity for the whole
session. The first time a name is used, the operator is built; after that, the same name hands
back the same operator.
This is what makes a visual editable while it plays. Load an edited plasma.glsl again and the
operator already on screen takes the new source — its wiring and its bound uniforms stay, because
nothing about which operator it is has changed. It is the same idea as a track: running
AudioTrack("drums") again gives you the drums, not a second drum track.
Two consequences follow:
- A name outlives the variable holding it. Rebind
softto something else, andgfx.Blur("soft")still has its radius when you come back to it. Only a Reset forgets names. - Using a name for a different kind of operator —
gfx.Edge("soft")aftergfx.Blur("soft")— replaces it. The new operator starts with no inputs and no bindings, and whatever read from the old one reads from the new one.
A helper that builds nodes with fixed names would hand every caller the same nodes, so a helper takes the names as arguments.
Two wiring rules
Section titled “Two wiring rules”>> fills the main input. a >> b feeds a into b’s primary input — the sampler
iChannel0 — and evaluates to b. So a >> b >> c reads left to right, a chain is itself a value,
and >> screen at the end puts the result on the display. >> never changes a: the upstream node
can be wired into any number of other places.
<< binds anything else, by name. What it binds depends on the value on its right:
- a node becomes a texture input — the second input of
Composite, the displacement field ofDisplace; - a number, a signal, a meter feed or a short array becomes a uniform.
These are the same two operators as in the audio graph — >> routes, << sets — and they mean the
same there: a track >> a bus, a parameter << a signal.
A shared node renders once
Section titled “A shared node renders once”A node used by several consumers is one node, not several copies: the graph is de-duplicated by
identity. When a field feeds two blurs, the noise is drawn once per frame and both blurs sample
that one result. Fan-out costs nothing beyond the consumers themselves.
Within a frame the graph is acyclic: every edge points from a node drawn earlier to one drawn later. That is what lets the whole graph be drawn in one ordered pass list.
Feedback reads the previous frame
Section titled “Feedback reads the previous frame”A graph that fed its own output back in would be a cycle, and a cycle has no drawing order. Feedback
breaks the cycle in time instead: gfx.feedback(fn(prev) { … }) gives the graph inside it prev,
the output of the previous frame.
The renderer keeps two buffers for the node the closure returns: it draws into one while prev
reads the other, then swaps them. That swap is the only thing feedback adds — everything inside the
closure is an ordinary acyclic graph. gfx.feedback has no name of its own, because prev is not
an operator: the buffer pair belongs to the named node the closure returns.
Because the buffers belong to a name, a trail survives your edits. Editing that operator’s shader,
rewiring it, or inserting an operator upstream keeps the trail flowing. It also carries across a
section cut when the next section draws the same name. When a section’s screen lane stops drawing
the operator — over a gap, or in a section without it — the trail holds still and goes on from
there when the operator is drawn again, as an uncooked TouchDesigner TOP keeps its image. It starts
over from black when the name becomes another kind of operator, when the operator is no longer
routed to screen at all, when the render size or screen.quality changes, when the visuals window
closes, and on a Reset.
To start a trail over on purpose, call reset() on that operator: look.reset() clears its buffers
on the next frame, like the Reset pulse on a TouchDesigner Feedback TOP. Nothing else in the graph
changes and nothing recompiles, and the trail builds again from black.
The graph changes as a whole
Section titled “The graph changes as a whole”Every edit to a visual that reaches screen — a bind, a new edge, a rewire — takes effect when the
block you ran finishes. Only the block’s end state is drawn or checked, so a block may pass through a
half-wired graph on its way to a complete one.
An edit that would break the graph — a cycle outside gfx.feedback, say — keeps the last good
picture on screen. The error is shown on the block that caused it and stays until a later block
fixes the graph.
Related
Section titled “Related”- Build an Operator Graph — the graph in practice
- Visuals & the Audio Engine — how the pictures keep time with the music
std/visuals— every operator