Skip to content

Shaped marks — the particle silhouette

swarm and emitter both carry shape and points. They are the only two scenes that do: the attractor’s marks are a chaos-game accumulation, and at the densities that make a figure one mark is a point, so a silhouette there would be invisible on principle rather than by tuning (ADR-0084).

shape is a numeric selector, like kaleido_edge — the expression grammar has no strings, so a star is shape = "3".

shapemarkwhat it draws
0discthe default, and exactly what these scenes drew before the roster existed. On the swarm a round blob; on the emitter its elongated glint (see spin above)
1ringan annulus the same size as the disc: brightest at 0.7 of the mark’s radius, dark at the centre, with a hole reaching 0.4 of it
2polygona regular points-sided polygon, one vertex pointing along +x
3stara points-pointed star, valleys at 0.45 of the tip radius
4hearta heart, point down

points is the count for polygon and star, 3 to 12, default 5. It does nothing on disc, ring or heart. Past a dozen the marks these are for — a few pixels across — are a disc with a rough edge.

The star arm’s three shape params

The star was one welded silhouette until a batch of six reference images turned out to be five requests for parameters this arm did not have. It has them now, and they reach swarm, emitter and shape_field alike — one chunk, one roster, so a shape a particle wears and a figure a field draws cannot drift.

paramrangedefaultdoes
star_valley0.05..0.950.45the valley radius as a fraction of the tip’s. Low is a sharp, thin-spiked star; high is a bumpy polygon
star_curve-0.9..0.90bows the edge between tip and valley. Positive bows it inward — the concave sparkle, which a straight-edged star cannot make at any valley radius. Negative bulges it out
star_jitter0..10per-spike variation in tip length, for the hand-drawn / irregular “bang” look

Every default is an exact identity, and that is an obligation rather than taste: the shared chunk means these knobs reach the particle path, so anything but the old welded constants would move every shipped shape = "3" preset. All 30 golden baselines re-bless byte-identical.

[params]
shape = "3"
points = "4"
star_valley = "0.18" # a long, sharp four-point sparkle
star_curve = "0.55" # ...with concave edges

Three things worth knowing before you tune them:

  • star_jitter is seeded, not random. The per-spike lengths come from an integer hash of the spike index, so the same preset draws the same figure in every run and on every machine. There is no separate lever to re-scatter the pattern while keeping the amount — if a look needs one, that is engine feedback.
  • star_curve is where the field’s precision goes. A bowed edge has no closed-form distance, so it is sampled; the contours shape_field draws are 0.0032 off true, which is invisible. star_jitter costs much more — up to 0.54 at seven points, because the angular fold measures against a point’s own spike and a longer neighbour can be nearer. On a particle mark none of this is visible (the sprite reads only the interior); on a big banded field, a heavily jittered star’s outer rings are approximate.
  • They are inert on the other four shapes, and nothing warns — the name is known, so no unknown-parameter warning fires. This paragraph is the warning.

These three MORPH the figure with the music, and that needs no engine work

All three are clamp-only — no rounding — so a binding drives them continuously and the silhouette genuinely deforms. That is worth stating because the two params beside them, shape and points, are rounded and therefore step. Verified by rendering: star_valley on bass and star_curve on treble over a 120 BPM click visibly thickens and thins the arms and changes the spike proportions.

[params]
star_valley = "0.20 + clamp(bass * 0.2941, 0, 0.25)" # spikes lengthen on the low end
star_curve = "0.55 - clamp(treb * 0.5000, 0, 0.30)" # edges straighten on the top end
[smoothing]
star_valley = 0.25
star_curve = 0.30

Three things to know before binding them:

  • On shape_field, an eased star_valley silently changes the RING COUNT as well as the shape. The scene normalizes by the arm’s inradius, and the inradius is a function of the valley radius — so morphing the valley changes what color_span means, and the contour structure breathes along with the figure. On a particle mark this does not arise; on a banded field it is a continuous wobble in the background structure that you did not ask for.
  • A binding that sweeps star_curve through 0 crosses a small discontinuity. At exactly zero the arm takes a closed-form straight-edge branch; either side of it takes a sampled one, and the two disagree by about 0.0032 (the polyline’s sagitta). Small, but it sits in the middle of the range you are most likely to animate through — bias the range to one side if it shows.
  • A curved or jittered star’s interior normalizes differently from a straight-edged one, and so does its contour spacing. That branch divides by the figure’s own deepest-point distance rather than by the straight edge plane’s perpendicular, so a color_span carried over from a straight-edged star is out by the ratio of the two references — roughly 1.1x at a shallow curve and 1.9x at star_valley = 0.18.

Not in the roster and not a parameter: a star with eyes. Composing two discs onto a silhouette is multi-shape composition, which is neither a knob nor a name in the closed roster, and it is the one thing in that reference batch this engine still cannot reach.

polygon has far less range than star, and the reason is geometry rather than tuning. A polygon’s corners sit at radius 1 and its edges at cos(pi / points) of that, so by seven sides the figure is within 10 % of a circle everywhere and reads as one: rendered side by side, a 7-gon field and a disc field are the same picture. Reach for points = 3 or 4 — a field of triangles is unmistakable — or reach for star, whose valleys sit at 0.45 of its tips at every count.

points is a stepped parameter: it is rounded to a whole number, so an eased points snaps at each half-integer rather than morphing. Both silhouettes fold the angle by the count, and that fold only absorbs atan2’s branch cut when the count divides the circle evenly — at a fractional count the mark tears along one ray. This is kaleido_order’s rule for kaleido_order’s reason, and it is worth stating because the surrounding vocabulary teaches the opposite: variant interpolates (ADR-0060) and the attractor’s IFS morphs (ADR-0075). A star’s angle fold is periodic in the count, so a fractional count is a discontinuity and not an intermediate figure. shape is stepped for the stricter version of the same reason — its values are names, and there is nothing halfway between a ring and a polygon.

Two consequences worth planning around:

  • hash(beat_index) on points is the trick. The count flipping on each onset detection is what already worked on parametric_curve’s radial_offset lobes, and it carries over: points = "7 + floor(hash(beat_index) * 2.999)" gives a field that re-cuts itself every beat, and the stepping is a feature there rather than a cost.
  • Small marks are where a silhouette earns its keep and also where it disappears. A seven-pointed star three pixels across is mostly its own anti-aliasing. If the shape is not reading, raise size before anything else — and note that on the swarm the population is fixed, so a mark large enough to have a legible figure fills the frame. The engine’s own shaped fixture reaches for zoom to thin the field rather than for a smaller mark.

A shaped mark drawn straight into the chain is a silhouette in additive light — there is no fill and no outline. The compositor adds, so black adds nothing and a dark edge cannot exist there: a heart drawn this way is a heart-shaped glow, brightest in its middle and fading to nothing at its outline, not a red body with a black rim. That is the scene’s deliberate scope.

It is not the whole story, though, and the route out is not a workaround. Put the same scene in a [layer] with blend = "multiply" and its marks subtract light instead — dark figures on a light ground, measured to display luma 0.9. See Dark on light. What stays true is the sentence’s other half: nothing in this engine decides what is in front of what, so a mark that occludes another figure is still unbuilt, and that is engine feedback.

The roster is closed: a shape that is not in the table is architect + dev work, not a preset. That is the same trade ADR-0061 made for fold edges.

Built from a8ce055 at version 0.115.0. This site tracks main and is not versioned per release.