Expression cookbook
Copy-paste recipes for the Expression node, short enough to drop in and tweak.
Each recipe goes in an Expression node sitting downstream of whatever it affects. @name reads a per-element attribute, one value per point; $NAME reads a global such as time.
Every recipe assumes the incoming data already carries the attribute being read. Points carry @P and @id automatically, but @Cd (colour) needs a node upstream to create it first. Reading one that is not there is a named error at cook time, listing the columns the input does have.
Jitter points with noise
Nudge every point by a small smooth random amount, turning a regular grid organic.
@P = @P + noise(@P * 0.5) * 10.0;
noise(@P * 0.5) samples a smooth 3D noise at each point's position. The * 0.5 zooms the noise pattern, and a smaller number gives bigger, smoother lumps. The * 10.0 is how far points move. Multiplying by $T inside the noise makes the jitter drift:
@P = @P + noise(@P * 0.5 + $T) * 10.0;
Colour by height
Tint points by how high they sit, which reads instantly on terrain, scatters and simulations.
@Cd = @P.y > 0 ? {1, 0.6, 0.1, 1} : {0.1, 0.4, 1, 1};
@P.y is the up axis, so this picks warm above zero and cool below. For a gradient instead of a hard cutoff, blend with mix and a normalised height:
float h = clamp(@P.y / 100.0, 0.0, 1.0);
@Cd = mix({0, 0, 0, 1}, {1, 1, 1, 1}, h);
For richer ramps, write h to an attribute and feed that into a Gradient node's value input rather than hand-blending two colours.
A wave over time
A sine wave driven by the playhead. This one bobs points up and down.
@P.y = @P.y + sin($T * 2.0 + @P.x * 0.1) * 20.0;
$T is animation time in seconds. The * 2.0 sets the speed and the * 20.0 the height. Adding @P.x * 0.1 offsets each point by its X position, so a row ripples like a flag instead of moving as one block.
The same idea works in any parameter field: type sin($T * 2.0) * 10.0 + 72.0 straight into a number box to make it oscillate.
Point at the mouse
The pointer is not a global. Expose two sliders with param(), then wire the position output of a Pointer node into them.
float d = length(@P.xy - {param("pointer_x", 0.0), param("pointer_y", 0.0)});
@Cd = mix({1, 1, 1, 1}, {1, 0, 0, 1}, clamp(d / 200.0, 0.0, 1.0));
Points near the cursor stay white, far ones go red. Swap the colour blend for a position nudge to make points flee or chase:
@P.xy = @P.xy + normalize(@P.xy - {param("pointer_x", 0.0), param("pointer_y", 0.0)}) * 5.0;
Every param() name gets its own pin, so anything that outputs a number can drive it. See Recording live input.
Colour by position in the loop
@ptnum is the element's index and @numpt the element count, both available on any collection. Together they give a 0 to 1 ramp with no upstream attribute needed.
float t = float(@ptnum) / float(@numpt - 1);
@Cd = {t, t, t, 1.0};
Both are integers, so the float() casts matter: an integer divide would truncate to 0 for every element but the last.
Combine a few at once
Several lines in one Expression node run top to bottom.
// drift, then colour by the new height
@P = @P + noise(@P * 0.5 + $T) * 8.0;
@Cd = mix({0, 0.2, 0.6, 1}, {1, 0.9, 0.4, 1}, clamp(@P.y / 80.0, 0.0, 1.0));
Keep one effect per line while dialling it in. A single line is easy to mute by commenting it out with //.