Circles of fixed size on a grid of shrinking pitch, until the overlaps become the subject.
Two constants and one variable
Everything in this piece is fixed except the count:
var extent = Spacing * Scale; // the fixed span of the grid, in pixels
var pitch = extent / (float)count; // the cell step, which closes as count grows
var radius = Radius * Scale; // the ring size, which never knows the count
for (var x = 0; x < count; x++)
for (var y = 0; y < count; y++)
canvas.DrawCircle(firstX + x * pitch, firstY + y * pitch, radius, paint);
Each circle sits at an explicit grid position — the first cell’s centre plus a whole
number of pitches — rather than being nudged into place by a chain of offsets.
The extent is constant: the grid always spans Spacing × Scale regardless of how
many circles are in it, because the pitch is divided by the count. The radius is
constant too, at Radius × Scale, and does not know about the count at all.
So raising the count does not add more of the same picture. It closes the spacing
underneath circles that stay exactly the same size, and the piece turns into a study
of overlap.
Three per side. The rings are far apart — nothing meets, and each colour reads as
its own sparse grid.
Where the pattern comes from
At the preset’s own count of twenty, the pitch is
Spacing / count × Scale = 167 / 20 × 50 ≈ 418
against an amber diameter of 2 × Radius × Scale = 500 and a violet one of 300.
The lead thins the lattice to a checkerboard and threads the violet rings through
the empty cells, offset half a pitch in both axes: every amber ring then sits
√(0.5² + 0.5²) × 418 ≈ 295 from the four violet rings around it, against a
combined reach of 400, so each ring crosses exactly those four — always of the
other colour — and nothing else. No ring crosses its own colour, whose nearest
neighbours stand √2 × 418 ≈ 590 away.
That is all the mechanism there is. The lattice of crossing points is regular
because both lattices and the grid are, and the eye assembles them into curves
that no stroke follows — the same effect as two sheets of dotted acetate laid at
an angle, except that the angle here is a half-pitch offset between two lattices
rather than a rotation of one. The interference comes purely from the ratio of
the two diameters to that offset.
Six per side. The pitch has closed, and the two colours have drawn near each other
— but no ring touches another.
Ten. The amber and violet rings have closed to about half a reach of each other,
but the two lattices still do not meet.
Fifteen. Contact. Each ring now just crosses the four of the other colour around
it, and the first crossings appear.
Twenty. The rings are still the same two sizes, still on the same two grids, and
the crossing is now deep enough that the piece reads as one woven field rather
than two lattices.
The canvas is a window, not a frame
There is a nudge in the vertical placement worth pointing out, because it is the
only asymmetry in the lead’s placement:
var firstY = Height / 2f - extent / 2f + YShift * pitch; // YShift defaults to −0.9
The −0.9 (what the historical 0.9 + yOffset formula with yOffset = −1.8
amounted to) shifts the whole grid up by nearly one cell. The lattice is larger
than the canvas in every direction — an 8350-pixel square against 8000 × 6000 —
so the field bleeds past all four edges and the picture is a window onto a larger
grid, never a bordered one. In the lead rendering the amber ink runs from
y = −1808 to y = +6639 and from x = −224 to x = +8224, and crosses all
four edges: each side crops about 224 px off the outermost amber column. Four
amber rows and three violet rows fall entirely above the canvas, and one amber
row and the two last violet rows fall entirely below it; the violet lattice never
meets the left or right edge — its rings run from x = +88 to x = +8330, the
last column entirely off the canvas — and only its top row crosses the top edge.
The crop is deliberate, but it is not even. Horizontally it is symmetric; the
vertical nudge makes it deeper at the top than at the bottom, which is what gives
the piece its poster-like hanging. XShift and YShift are per-preset knobs now,
so a variant that wants the window centred can have it — Cream Lanterns does.
Six renderings
| Rendering | Count | Radius | Pitch | Stroke | Ink |
|---|
| Ember Weave | 20 | 250 | 418 | 15/9px | #FFB85C and #9D7BFF on #0E0A18 |
| Ice Blue | 20 | 250 | 418 | 20px | #7DF9FF on black |
| Gold Grid | 15 | 300 | 557 | 25px | #FFD700 on #111111 |
| Rose Drift | 26 | 200 | 321 | 13px | #FF1493 on #0a0a0a, seeded jitter |
| Silver Diagonal | 20 | 250 | 418 | 18px | #DDE4EC on #0B0D10, rotated 45° |
| Cream Lanterns | 22 | 225 | 380 | 25% fill | #F4E3BC on #081014 |
The number that decides the character is still the ratio of diameter to pitch: 1.20
for the amber rings of Ember Weave, 1.20 for Ice Blue, 1.08 for Gold Grid, 1.25 for
Rose Drift, 1.20 for Silver Diagonal, 1.19 for Cream Lanterns. Gold Grid is only just
overlapping, which is why it reads as circles; Rose Drift is the finest interference
of the six. Stroke width tracks circle size, keeping the line weight proportionate.
The variants are not recolors
Five knobs stand behind the renderings, and each changes the structure rather than
the colouring:
- Rotation turns the whole lattice around the canvas centre. The pitch is kept
as if unrotated — the density does not change — and the span widens so the turned
square still covers the canvas. Silver Diagonal is the 45° case, and the weave
runs corner to corner instead of edge to edge.
- Parity thins the lattice to a checkerboard, leaving half the cells empty.
- Accent draws a second lattice in its own ink and size, offset half a pitch
and sitting on the complementary parity, so the two checkerboards weave through
each other. The lead pairs amber rings on the even cells with smaller violet
rings on the odd: every ring crosses only the other colour, and the two-colour
chainmail is the subject.
- Jitter + Seed perturbs each ring’s radius by up to ±Jitter of its own size,
from a hash of its cell and the seed. Rose Drift is the pink lattice at 25%
jitter: the regular weave drifts into an organic one, and the same seed always
drifts the same way.
- FillAlpha fills the rings as translucent discs instead of stroking them.
Overlaps then read as accumulated light rather than as crossings. Cream Lanterns
also sets
YShift = 0, so its window sits centred instead of nudged toward the
top.
The museum plate
The gallery ends with the piece the way it would hang on a wall. MuseumFrame
is a presentation layer, not a generative one: it takes any ISkiaArt piece,
clears to a gallery wall, and draws a frame, a mat with a bevel rule, and the
artwork contain-fitted into the opening behind them. The lead preset, Ember Weave,
appears in the gallery twice — once as the raw plate, once framed — because the
frame is a way of showing the work rather than a rendering of it, and none of
the other renderings gets a framed copy.
The proportions are fixed: the opening is 65% of the canvas in each axis, the mat
4% and the frame 2% of the shorter side around it, and the rest is wall with a
soft drop shadow under the frame. The artwork is clipped at the mat edge, so the
piece’s own edge crop stays inside the opening instead of reaching the wall.
Determinism
Nothing is random, and the things that look random are not. Every centre is an
arithmetic function of its two integer indices, drawn in a fixed nested-loop order.
The jittered variants add one more arithmetic function: each ring’s size factor is
a splitmix32-style hash of its cell and the preset’s seed, so equal seeds and
parameters draw byte-identical lattices. No state is carried between circles, and
no clock, Random, or parallelism is involved anywhere.