Context: per-element values
One node can feed thousands of copies — and each copy can quietly carry its own number.
This is the idea that makes node graphs click. A single node doesn't just hold one value. When it feeds many elements — many points, many duplicates, many loop passes — a value can resolve differently for each one. Once you see it, a huge amount of the app stops feeling like magic.
The simplest version: per-row attributes
Most things you make are collections — a bag of elements (points, shapes, copies) that all travel together. Every element in that bag can carry its own labelled values, called attributes. You write an attribute with an @ in front of its name.
A few you'll meet constantly:
@id— a unique number for each element (0, 1, 2, …).@P— each element's position.@total— how many elements there are in the bag.
The trick is that @id isn't one number. It's a different number on every element. So when you reference @id somewhere downstream, you're not asking "what is the id?" — you're asking "what is this element's id?", and you get a different answer for each.
Think of an attribute like a column in a spreadsheet. The node is the whole sheet; @id is the column. Each row reads its own cell.
Make it click: colour 200 points by their @id
Here's the canonical example. Scatter some points, then tint each one differently — without touching them one by one.
Add a scatter source so you have, say, 200 points. Each one automatically gets its own
@id(0 through 199).Add an The Expression language node and write a per-element rule. For example:
@Cd = @id / @totalEvery point now reads its own
@id, divides by the total count, and gets a brightness from 0 (the first point) to 1 (the last). The result is a smooth ramp across all 200 points — from one tiny rule.
The Expression node ran once per point, and each run saw that point's own @id. You never wrote a loop. You wrote the rule for one element, and the app applied it across the whole bag.
Want a colour sweep instead of grey? Feed @id / @total into a Scene values and read the colour out. Same per-element idea, prettier result.
Copies: each duplicate gets its own context
The utility.iterator node stamps copies of whatever you feed it — in a line, a grid, a circle, along a path, or onto existing points. Crucially, each copy knows which copy it is.
While it builds the copies, the Iterator hands each one a set of per-copy values you can read:
@index— which copy this is (0, 1, 2, …).@id— a stable unique number for the copy.@total— how many copies in all.@index_x/@index_y/@index_z— the row/column/layer in a grid.@path_t— how far along a path this copy sits (0 at the start, 1 at the end).
So "scale each copy a bit more than the last" is just reading @index and turning it into a scale. "Fade copies along a path" is just reading @path_t. You author the relationship once; every copy fills in its own number.
The per-copy inputs on the Iterator (rotation, offset, scale, colour, and so on) accept either a single value or a list. Give it one value and every copy shares it; give it a list and each copy reads its own entry.
Loops: each pass is its own context
Loops are the time-based cousin of copies. Instead of many elements side by side, you get many passes, one after another — and each pass has its own context too.
A For loop (loop.for_start) exposes
@index— the current pass number — so the body can do something a little different each time round.A Feedback loop (loop.feedback_start) goes further: it hands the result of one pass into the next, so each pass builds on the last. It exposes the current iteration and the time elapsed, and a While mode keeps going until a condition you wire in stops being true.
Same mental model as copies: you describe what one pass does, and the loop supplies the changing context (@index, the running state) for each one.
Why this matters
Per-element context is the thing that lets one small graph describe enormous variety. You don't build 200 points, 50 copies, or 30 loop passes by hand — you build the rule, and the app resolves it freshly for every element, copy, or pass.
When something downstream "knows" which point it is, which copy it is, or which pass it is, this is why: it's reading a per-element value like @id, @index, or @path_t.
A value only varies per element if it actually is a per-element attribute. A plain number typed into a parameter is one value shared by everything. To get variety, read an attribute (@id, @index, @path_t) or feed the Iterator a list instead of a single value.
See also
Copying & instancing — making the copies this context rides on
Loops & feedback — the time-based cousin (passes)
utility.iterator