Build an Operator Graph
std/visuals has ready-made operators — gfx.Noise, gfx.Blur, gfx.Displace, gfx.Composite
and more — that you wire into a graph instead of writing GLSL. Every operator, with its uniforms
and inputs, is in the std/visuals reference.
Chain operators
Section titled “Chain operators”Each constructor takes a name. Set uniforms with <<, and chain with >>, which feeds the left
node into the right one’s main input:
use "std/visuals" as gfx;
let field = gfx.Noise("field");field.parameter("period") << 2.0;
let soft = gfx.Blur("soft");soft.parameter("size") << 16.0;
field >> soft >> screen;PLAY;A uniform takes a number, a signal, a meter feed, an array of 2–4 numbers for a vector, or a table of rows, such as the keys of a ramp:
edge.parameter("strength") << Sine(8).range(0.5, 2.0);blur.parameter("size") << drums.rms.smooth(80).range(1, 40);move.parameter("scale") << #[1.5, 1.5];ramp.parameter("keys") << #[#[0, 0.1, 0.0, 0.2, 1], #[1, 1, 1, 1, 1]];Feed a second input
Section titled “Feed a second input”An operator with two inputs names its second one. Bind a node to it with <<:
comp.parameter("b") << other; // Composite's input Bdisp.parameter("by") << field; // what Displace displaces bylook.parameter("table") << ramp; // Lookup's lookup tableBoth inputs must be wired. An operator with an empty input is not drawn: putting its graph on screen is an error that names the operator and the input, and the picture on screen stays.
Use one node in several places
Section titled “Use one node in several places”Keep the node in a variable and wire it wherever it is needed. It is drawn once per frame, and every consumer reads that result:
use "std/visuals" as gfx;
let field = gfx.Noise("field");field.parameter("period") << 2.0;
let soft = gfx.Blur("soft");soft.parameter("size") << 12.0;let softer = gfx.Blur("softer");softer.parameter("size") << 48.0;
let comp = gfx.Composite("comp");comp.parameter("operand") << gfx.Operand.subtract; // A - Bfield >> soft >> comp; // Acomp.parameter("b") << (field >> softer); // B
comp >> screen;PLAY;Write a helper that builds nodes
Section titled “Write a helper that builds nodes”The name is the operator: the same name gives back the same node. A helper that builds its nodes with fixed names hands every caller the same ones, so take the name as an argument:
use "std/visuals" as gfx;
fn glow(name, src) { let b = gfx.Blur(name); b.parameter("size") << 24.0; return src >> b;}
glow("halo", gfx.Noise("field")) >> screen;PLAY;Loop last frame back in
Section titled “Loop last frame back in”To read what the graph drew in the previous frame, build it inside gfx.feedback. The function
gets prev, last frame’s output; wire it into an input, and return the node that is the output.
The first argument is the seed: prev is the seed’s frame until the output has drawn one of its
own:
use "std/visuals" as gfx;use "std/signals" { Time };
let field = gfx.Noise("field");field.parameter("period") << 3.0;field.parameter("tz") << Time(0.2).set_timebase(seconds(1));
let look = gfx.feedback(field, fn(prev) { let disp = gfx.Displace("trail"); disp.parameter("displace_weight_x") << 0.1; disp.parameter("displace_weight_y") << 0.1; disp.parameter("by") << prev; return field >> disp;});
look >> screen;PLAY;The noise moves through its third axis, tz, by 0.2 each second. Each frame displaces it by the
frame before, which leaves a flowing trail. The noise is also the seed, so on the first frame the
noise displaces itself.
The trail survives your edits. To start it again from the seed, reset the node the function returned:
look.reset();examples/05_visuals/04_voronoi_feedback.non is a whole patch built this way: a TouchDesigner
feedback patch whose cells flow and warp, using fan-out and feedback together.
Related
Section titled “Related”- The Visual Graph — names, wiring, fan-out and feedback, and why they work this way
- Bind Audio to a Shader — the audio feeds you bind to uniforms