Fifty vertical strips per band, each broken into segments whose edges wander but always return.
A grid that is only half a grid
The composition is Rows bands stacked vertically, each divided into Cols vertical
strips. That much is a plain grid. What happens inside a strip is not:
int numSegments = rand.Next(MinSegments, MaxSegments + 1);
Each strip is cut into three, four or five segments, chosen independently. Each
segment is a quadrilateral, and its four corners are the strip’s nominal edges pushed
sideways by a random offset.
One band. Fifty strips wide, and already the vertical grid lines have stopped being
straight.
Wander, but return
The offsets are the piece:
leftOffset[j] = (float)(rand.NextDouble() - 0.5) * 2f * maxOffset;
rightOffset[j] = (float)(rand.NextDouble() - 0.5) * 2f * maxOffset;
leftOffset[0] = 0; rightOffset[0] = 0;
leftOffset[numSegments] = 0; rightOffset[numSegments] = 0;
Every joint between segments gets a random horizontal displacement of up to 35% of a
strip width — except the first and last, which are forced back to zero.
So a strip leaves its column, wanders for two to four joints, and arrives back
exactly where it started. It is a closed excursion, and it is why the composition
reads as a grid that has been disturbed rather than as a field with no grid in it.
Remove the two clamps and the columns would drift apart over the height of a band and
the whole structure would dissolve.
The grid itself is inset from the canvas sides by exactly that excursion, so the
outermost strips’ wander stays inside the frame: nothing is ever clipped at the
canvas edge.
That is the default draw, with the two edges of a strip sampled independently so a
segment tapers and overlaps its neighbours. CorrelatedEdges flips it: both edges
take the same offset at every joint, so a strip keeps its width and wanders as one
smooth, kinked ribbon — the difference between a torn-paper collage and a rope of
silk.
Two bands. Nothing carries between them: each is drawn from scratch with its own
segment counts and its own offsets.
Shared edges
A segment’s right edge is computed as rightX + rightOffset[j], and its neighbour’s
left edge as (rightX + stripGap) + leftOffset[j] — using that neighbour’s own
offset array, not this strip’s.
The two are drawn from different random values, so adjacent strips do not share an
edge. They overlap or leave a gap, by up to 70% of a strip width in the worst case.
That is where the piece gets its layered, torn-paper quality: the strips are opaque
fills, drawn left to right, so a strip that reaches too far right is simply painted
over by the one after it, and one that falls short leaves the background showing.
The apparent depth is a drawing-order artefact, and there is no z-value anywhere.
Three bands.
Four. The joint heights are jittered too — by up to 15% of a segment’s height — so
the horizontal seams within a band do not line up across strips either.
The full stack of six.
Colour, per segment or per strip
A colour is one palette draw. By default the draw happens per segment, so a single
column can change colour two or three times down its length — which is what stops
the bands reading as a bar chart.
ColorPerStrip moves the draw up one level: a strip takes one colour and keeps it
for every segment. The strips become single objects of solid colour, and the
composition reads as slats or as data rather than as a weave.
The renderings
| Rendering | Cols | Rows | Segments | Structure | Palette |
|---|
| Neon Spectrum | 50 | 6 | 3–5 | independent edges, colour per segment | eight neon hues on black |
| Cold Fusion | 40 | 8 | 4–7 | correlated edges — ribbons of constant width | cyan-to-magenta ramp on blue-black |
| Solar Flare | 60 | 5 | 2–4 | bands floating on wide gaps, colour per strip | sunset ramp on warm black |
| India Ink | 72 | 9 | 1–3 | strips inset from their columns, paper channels between | one ink on warm paper |
| Embers | 84 | 7 | 7–11 | the wild end: thin, choppy, leaning far out of column | ember ramp on warm black |
| Deep Field | 48 | 6 | 3–5 | a dim fine under-layer drawn beneath the main field | silver and slate over indigo on midnight |
Six renderings, one algorithm. The knobs are Cols/Rows (density), MinSegments/
MaxSegments (kinks per strip), MaxOffsetFraction (how far edges may lean),
GapFraction (empty space between bands — 0 makes bands butt, 0.35 floats them
apart), StripGapFraction (empty channels down every column), CorrelatedEdges,
ColorPerStrip, Underlay (a second strip field drawn first), plus Background,
Palette and Seed.
Narrowing the palette changes the reading completely: with eight unrelated hues the
strips read as separate objects, and with a restricted ramp they read as one surface
with variation in it. India Ink takes that to its limit — a single ink colour, so
everything the piece says is said by geometry.
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, with a label plate beneath
whose glyph is three wobbly strips — the piece’s signature geometry in
miniature. The lead preset, Neon Spectrum, 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 variants gets a framed
copy.
The proportions are fixed: the opening is 68% of the canvas height at the
artwork’s aspect, the mat 32 pixels and the frame 16 pixels around it, the
plate 30 pixels below, 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 margins stay
inside the opening instead of bleeding onto the wall. Everything in the frame
is drawn from fixed numbers — the glyph’s leans are literal constants, not
draws from the generator’s RNG.
Determinism
One Random is created per draw, seeded from the preset, and consumed in a strict
order — segment count, joint jitters, then one or two offsets per joint (one when
edges are correlated), then a colour per strip or per segment — strip by strip, row
by row. An under-layer draws its own field first with its own seed. The generator is
System.Random, which the project’s determinism policy flags as not guaranteed
stable across .NET versions.