-
Notifications
You must be signed in to change notification settings - Fork 0
Attribute Wrangle
← Home · Solarxy Web · Expressions · Node Reference
attribute_wrangle runs a small program once per point (or once per
primitive) over the geometry flowing through it. Where
expressions let one parameter compute from another, a
wrangle lets one point compute from another.
It is the general-purpose attribute tool. attribute_create writes a
constant and attribute_randomize writes noise; neither can compute a lane
from another lane, or from a point's own position. A wrangle can, which is
what turns attributes from storage into something you shape with.
Drop an attribute_wrangle after any primitive. It arrives holding:
@Cd = set(@P.x + 0.5, @P.y + 0.5, @P.z + 0.5);
The geometry turns into a colour gradient immediately, because @Cd is the
colour attribute the viewport already displays. Nothing else to wire up.
Now replace it with this and press play on the playbar:
@P = set(@P.x, @P.y + sin(@P.x * 4 + $T) * 0.2, @P.z);
The surface ripples. The wrangle is being re-run every frame against a clock, not playing back a recording.
Statements are separated by ;. Each one assigns to an attribute or to a
local variable. The trailing ; on the last statement is optional.
float d = length(@P);
@P = @P * (1 + 0.1 * sin(d * 8 + $T));
@Cd = set(d, 1 - d, 0.5);
Everything the expression language offers is available
inside a wrangle: the same operators and precedence, the same thirty-odd
functions, ch("box1/width") to read another node's parameter, and
npoints() or bbox("size") to measure the incoming geometry. It is
literally the same parser, so anything you learn in one place works in the
other.
The field is a real code editor, not a text box.
-
Completion on
@,$and bare names, each with a one-line description. The@list is not a fixed catalogue: it includes the attribute lanes actually present on the geometry arriving at this node, so it is also how you find out what is there. -
Brackets match and auto-close, and
Ctrl/Cmd + /toggles a comment on the selection. -
Find with
Ctrl/Cmd + F, and selecting a word highlights its other occurrences. - Errors underline the exact token, not the whole line.
-
Ctrl/Cmd + Entercommits without leaving the field;Escreverts to the last committed program.
Word wrap, line numbers and font size live in Preferences > Display > Code editor and apply to every code field in the app.
@name reads or writes an attribute on the element currently being
processed.
@P |
position (vector). Assigning it moves the geometry |
@N |
normal (vector) |
@Cd |
colour. Three components are fine; alpha fills in as opaque |
@uv |
texture coordinate (vector2) |
@ptnum / @numpt
|
this point's index, and how many there are |
@primnum / @numprim
|
the same for the primitive domain |
@anything |
any other attribute on the input |
@ptnum and the other three counters are read-only; they describe where
you are, not something you set.
An attribute the input does not carry is created at the width of its
first assignment: @heat = 1; makes a float lane, @dir = set(0, 1, 0);
makes a vector one. Reading a lane that is not there is an error naming
the lane, rather than a silent zero.
Declare with a type, assign in the same statement:
float t = @P.y;
vector2 flat = set(@P.x, @P.z);
vector rgb = set(t, 1 - t, 0);
vector4 rgba = set(t, 1 - t, 0, 1);
A scalar widens into a wider local, so vector v = 0; is three zeroes. A
wider value into a narrower local is an error rather than a silent
truncation, because quietly dropping z is a bug nobody finds.
Deliberately. With no loops, a program's cost is exactly (statements × elements) and cannot run away on a cook that shares a thread with the viewport. For a branching value, use the conditional the expression language already has:
@Cd = @P.y > 0 ? set(1, 0, 0) : set(0, 0, 1);
Run Over picks the domain. Points is the usual choice and the only one
that can move geometry, because @P is a point attribute. Primitives runs
once per triangle, segment or point primitive, and reaches the
primitive-domain lanes attribute_promote writes.
Two nodes read attributes a wrangle is the first thing able to author:
-
copy_to_pointsreads apscalefloat and multiplies its own Scale by it, so@pscale = fit(rand(@ptnum), 0, 1, 0.4, 1.6);on a scatter gives you copies of mixed sizes. -
scattertakes a Density Attribute, weighting where its points land.@density = fit(@P.y, 0, 1, 0, 1);gathers them toward the top of a surface.
A parse error names the line and column, underlines the token it choked on, tints that line, and badges the node. The geometry keeps its last good state rather than vanishing.
An arithmetic edge is not an error. Dividing by zero gives you an infinity, the way it does anywhere else, because one bad element must not blank an entire scene.
A program that assigns nothing at all cooks and warns, since it is legal and almost certainly not what you meant.
The program is parsed once per cook, never per element, and runs over a fixed set of registers with no allocation in the inner loop.
Past about 50,000 elements the node warns, because that is roughly where a re-cook stops feeling instant: the cook is single-threaded, and dragging a parameter anywhere upstream re-runs the whole program every frame. The warning is advisory. If you are happy to wait, nothing stops you.
- Expressions for the shared language
-
Runtime and playback for
$T - Node Reference for every parameter
Solarxy - A cross-platform 3D model viewer, validator and browser-based node modeler built with Rust and wgpu.
GitHub Repository · Releases & Downloads
© 2026 Marko Koljancic · MIT License
Getting Started
Solarxy Web
- Solarxy Web
- Your first scene
- Expressions
- Attribute Wrangle
- Runtime and playback
- Publishing a scene
- Node Reference
Tutorials
Using Solarxy
Reference
Help
Project