Describe the Color property node in the shader graph (#298)

- Shaders: a row for Color property in the node table, the colour
  field in the preview, the vec3 uniform, and that it cannot feed
  the Vertex output.
- Development: e2e total 1704.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
adminandClaude Opus 5.5 committed 2026-10-03 11:44:11 +02:00
1 parent 55cffd1ac2
commit 7bddb77b5f
2 files changed
+11 -8

No files matched your search

+1 -1
@@ -18,7 +18,7 @@ php tests/php/accounts.test.php # the API's accoun
# has and the web server's container has not (SKIP there) # has and the web server's container has not (SKIP there)
php tests/php/snapshots.test.php # the snapshot store (#231), the same way php tests/php/snapshots.test.php # the snapshot store (#231), the same way
php tests/php/releases.test.php # the release store (#266), the same way php tests/php/releases.test.php # the release store (#266), the same way
node tools/seed-e2e.mjs && node tools/e2e.mjs # 1703 editor checks, screenshots in /tmp/gm-e2e-*.png node tools/seed-e2e.mjs && node tools/e2e.mjs # 1704 editor checks, screenshots in /tmp/gm-e2e-*.png
GM_STEPS=20 node tools/e2e.mjs # or 1-12, or 6,20 — a subset while working on one step GM_STEPS=20 node tools/e2e.mjs # or 1-12, or 6,20 — a subset while working on one step
./e2e.sh start [GM_STEPS] # the same run detached, see below ./e2e.sh start [GM_STEPS] # the same run detached, see below
./e2e.sh status # live progress, or the last run's result ./e2e.sh status # live progress, or the last run's result
+10 -7
@@ -37,11 +37,12 @@ back. The test image is 64 pixels square: colour runs from left to right and fro
checker of 8-pixel cells shows where a UV lands, and the outer 8 pixels fade out so an alpha effect checker of 8-pixel cells shows where a UV lands, and the outer 8 pixels fade out so an alpha effect
has something to work on. has something to work on.
Below the picture there is **a slider for every property** the shader declares. The sliders belong Below the picture there is **a slider for every property** the shader declares, and **a colour
to the preview: moving one never writes into the project, not into the sprite and not into the field for every colour property** (a click on the swatch opens the colour picker). They belong to
the preview: changing one never writes into the project, not into the sprite and not into the
Property node. They start at the sprite's own values when the picture is a sprite that already wears Property node. They start at the sprite's own values when the picture is a sprite that already wears
this shader, otherwise at the property's default. They reach from the property's `min` to its `max`. this shader, otherwise at the property's default. A slider reaches from the property's `min` to its
A property without a `max` gets twice its default's distance from the `min`. `max`. A property without a `max` gets twice its default's distance from the `min`.
A shader with a **Time** node runs on the preview's own clock. `Hold` stops it at the second it shows A shader with a **Time** node runs on the preview's own clock. `Hold` stops it at the second it shows
(the button then reads `Run`), `Restart` goes back to 0. Without a Time node both buttons are greyed (the button then reads `Run`), `Restart` goes back to 0. Without a Time node both buttons are greyed
@@ -82,6 +83,7 @@ plugged into a colour port, straight from a `Number` or a `Luminance` as well.
| **Output** | in `color`, `alpha` (number) | What the sprite is drawn with: the colour, clamped to 0-1, and the opacity, clamped too. Each input left unconnected stays the texture's. One per graph. Its *Edge* setting says how the picture continues past its border when sampled outside it: *Clamp* (the default) holds the border pixel, *Mirror* turns the picture back on itself, *Repeat* starts it over for tiles, *Transparent* leaves everything outside empty. Its *Padding* is room around the picture to draw into, in pixels of the picture per side (see *Room around the picture*). | | **Output** | in `color`, `alpha` (number) | What the sprite is drawn with: the colour, clamped to 0-1, and the opacity, clamped too. Each input left unconnected stays the texture's. One per graph. Its *Edge* setting says how the picture continues past its border when sampled outside it: *Clamp* (the default) holds the border pixel, *Mirror* turns the picture back on itself, *Repeat* starts it over for tiles, *Transparent* leaves everything outside empty. Its *Padding* is room around the picture to draw into, in pixels of the picture per side (see *Room around the picture*). |
| **Vertex output** | in `x`, `y` (number) | Moves the corners of the grid the picture is drawn on: `x` to the right and `y` down, in pixels of the picture; the picture bends with them. One per graph, and optional: without it the wearer draws on a plain rectangle. Its *Subdivisions* setting is how many cells the grid has each way, 1 to 64, 8 by default; 1 is the plain rectangle (see *Moving the corners*). | | **Vertex output** | in `x`, `y` (number) | Moves the corners of the grid the picture is drawn on: `x` to the right and `y` down, in pixels of the picture; the picture bends with them. One per graph, and optional: without it the wearer draws on a plain rectangle. Its *Subdivisions* setting is how many cells the grid has each way, 1 to 64, 8 by default; 1 is the plain rectangle (see *Moving the corners*). |
| **Property** | out `value` | Declares a number the sprite sets: `Name` is the row below the sprite's Shader dropdown and the uniform in the fragment; `Default`, `Min`, `Max` are the row's range. A name is one word (letters, digits, `_`), unique in the graph, and not one of the rows a sprite has anyway — `enabled`, `scale`, `rotation`, `mirrorX`, `mirrorY`, `width`, `height`, `color`, `texture`, `shader`. Both rows would sit on the same key and the uniform would read the sprite's width instead of the declared default, so the node says *"width" is a sprite property* and nothing compiles. | | **Property** | out `value` | Declares a number the sprite sets: `Name` is the row below the sprite's Shader dropdown and the uniform in the fragment; `Default`, `Min`, `Max` are the row's range. A name is one word (letters, digits, `_`), unique in the graph, and not one of the rows a sprite has anyway — `enabled`, `scale`, `rotation`, `mirrorX`, `mirrorY`, `width`, `height`, `color`, `texture`, `shader`. Both rows would sit on the same key and the uniform would read the sprite's width instead of the declared default, so the node says *"width" is a sprite property* and nothing compiles. |
| **Color property** | out `color` | Declares a colour the sprite sets: the same as **Property**, with a colour row (swatch and hex) instead of a number row and a `vec3` uniform. `Default` is a colour, white unless picked; there is no `Min` and `Max`. Placed from the palette it is called `tint`, then `tint2`. Number and colour properties share one set of names: a second node of the same name says *Another property is named "tint"*. Like `Color` it cannot feed the Vertex output. |
| **Color** | out `color` | A constant colour, picked in the inspector. | | **Color** | out `color` | A constant colour, picked in the inspector. |
| **Number** | out `value` | A constant number, typed on the node. Handy where the same value feeds more than one input, or where an input has nothing to type on (Luminance, the Output's alpha). | | **Number** | out `value` | A constant number, typed on the node. Handy where the same value feeds more than one input, or where an input has nothing to type on (Luminance, the Output's alpha). |
| **Add**, **Subtract**, **Multiply**, **Divide** | in `a`, `b` (any) · out `result` | Arithmetic, per channel for colours. An unconnected input is the number typed on the node (0, or 1 for the second input of Multiply and Divide). **Dividing by 0 gives 0**, the same answer the `Divide` of a behavior and of a particle graph give: `a / 0.0` is NaN in GLSL, and the Output's `clamp(NaN, 0, 1)` is undefined — the pixel would be whatever the driver does, different on Intel, on Nvidia and on a phone. | | **Add**, **Subtract**, **Multiply**, **Divide** | in `a`, `b` (any) · out `result` | Arithmetic, per channel for colours. An unconnected input is the number typed on the node (0, or 1 for the second input of Multiply and Divide). **Dividing by 0 gives 0**, the same answer the `Divide` of a behavior and of a particle graph give: `a / 0.0` is NaN in GLSL, and the Output's `clamp(NaN, 0, 1)` is undefined — the pixel would be whatever the driver does, different on Intel, on Nvidia and on a phone. |
@@ -215,7 +217,7 @@ there are no corners in between, and the whole picture leans instead. For a wave
What may feed it is what a corner can know: `UV` (the corner's own place on the picture), `Time`, What may feed it is what a corner can know: `UV` (the corner's own place on the picture), `Time`,
`Property`, `Number` and the calculating nodes. A corner has no pixel, so `Texture color`, `Property`, `Number` and the calculating nodes. A corner has no pixel, so `Texture color`,
`Texture alpha`, `Sample texture`, `Texture size` and `Color` cannot feed it; the Vertex output `Texture alpha`, `Sample texture`, `Texture size`, `Color` and `Color property` cannot feed it; the Vertex output
then says "Not available per corner: …" and the shader does not compile. The same node may feed then says "Not available per corner: …" and the shader does not compile. The same node may feed
both outputs: one `Property` can drive the bend and the colour. both outputs: one `Property` can drive the bend and the colour.
@@ -238,7 +240,8 @@ stands 6 units to the right.
## Wearing a shader ## Wearing a shader
The sprite's `Shader` row is a picker over the project's shader assets (`None` is no shader: the The sprite's `Shader` row is a picker over the project's shader assets (`None` is no shader: the
sprite draws in Pixi's batch like any other). The rows below it are the shader's `Property` nodes, in node order; they are sprite draws in Pixi's batch like any other). The rows below it are the shader's `Property` and
`Color property` nodes, in node order; they are
ordinary sprite properties: overridable per placed object, reset (↺) and apply (↑) like any other, ordinary sprite properties: overridable per placed object, reset (↺) and apply (↑) like any other,
readable and writable by a `Properties` node at runtime — a behavior can fade a sprite by writing readable and writable by a `Properties` node at runtime — a behavior can fade a sprite by writing
its `brightness` every frame. That node's `+ Property` list (under *Type: Sprite*) offers the its `brightness` every frame. That node's `+ Property` list (under *Type: Sprite*) offers the
@@ -315,7 +318,7 @@ to one.
`src/core/shader/nodes.ts` is the catalogue, `src/core/shader/compile.ts` turns a graph into the `src/core/shader/nodes.ts` is the catalogue, `src/core/shader/compile.ts` turns a graph into the
fragment (every node reachable from the Output becomes one local per output, in dependency order; fragment (every node reachable from the Output becomes one local per output, in dependency order;
every Property a `uniform float u_<name>`; the walk itself is `src/core/graph/emitGraph.ts`, shared with the every Property a `uniform float u_<name>`, every Color property a `uniform vec3 u_<name>`; the walk itself is `src/core/graph/emitGraph.ts`, shared with the
particle compiler). The type of a `result` has one source: `liftedType` in the catalogue is what particle compiler). The type of a `result` has one source: `liftedType` in the catalogue is what
`emit` declares the local with, and `liftedPortType` (the group's `agree`, see *Ports that carry `emit` declares the local with, and `liftedPortType` (the group's `agree`, see *Ports that carry
one and the same value* in [Behaviors](Behaviors)) is the same rule for the port the editor draws. one and the same value* in [Behaviors](Behaviors)) is the same rule for the port the editor draws.