One line that reaches every cell of a square, and keeps neighbours in the plane neighbours on the line.
Index to position
The curve is not built by recursion here. It is built by a bit trick that turns a
position along the curve directly into a position in the square:
for (var s = 1; s < order; s *= 2)
{
var rx = 1 & (d / 2);
var ry = 1 & (d ^ rx);
if (ry == 0)
{
if (rx == 1) { x = s - 1 - x; y = s - 1 - y; }
(x, y) = (y, x);
}
x += s * rx;
y += s * ry;
d /= 4;
}
Two bits of d at a time choose a quadrant; the reflections and the swap orient the
sub-curve so its ends line up with its neighbours’. The whole conversion is O(order)
per point with no recursion, no stack and no stored curve — which is why an order-eight
curve with 65,536 points costs nothing to draw.
Order three: an 8 × 8 grid, every cell entered once, the path ending adjacent to where
it started.
Self-similar by construction
Order four. Each cell of the previous order has become four cells, and the curve
inside each of them is a copy of the whole previous curve — rotated and reflected so
the four copies join end to end.
That is the definition, and it is why the picture at any order contains the picture at
every lower order. The stages above are not different curves; they are the same
construction read at different depths.
Order five.
The property that makes it useful
Hilbert’s curve is not the only space-filling curve, and it is the one people
actually use, because of locality: two points close together on the line are close
together in the square, and — unlike the simpler Z-order curve — the converse holds
almost as well.
That makes it the standard way to linearise two-dimensional data. Database indexes on
latitude and longitude, texture memory layouts, and IPv4 address-space maps all use
it, because a range along the curve corresponds to a compact region of the plane
rather than to a scatter of stripes.
The Z-order curve, by contrast, has jumps: when its index rolls over a power of two the
position leaps clear across the square. The Hilbert curve never moves more than one
cell per step, at any order.
Order six: 4,096 cells.
Order seven: 16,384 cells, in one unbroken path of 16,383 unit steps.
Refit at every order
Every stage is scaled to fill the same frame:
var cellW = (Width - 2 * pad) / gridSize;
The curve’s extent is always the unit square, so the fit does not change shape between
orders, only the cell size. Unlike the dragon curve, whose bounding box changes shape
with every fold, the Hilbert curve fills exactly the same square at every order — so
the progression is a genuine zoom into detail rather than a refit.
The stroke width does not scale, which is what makes the higher orders read as a
solid textured field rather than as a finer line. At order seven the cells are about
six pixels across against a two-pixel stroke, and the curve is close to filling its
square in the literal sense as well as the mathematical one.
Eight renderings
| Rendering | Order | Cells | Treatment |
|---|
| Aurora | 7 | 16,384 | teal-to-violet ramp along the path |
| Ember | 6 | 4,096 | indigo-to-amber ramp on a ribbon of a stroke |
| Quarters | 6 | 4,096 | the 4 × 4 sub-grid traced in four inks |
| Mosaic | 6 | 4,096 | every cell a tile, every quadrant a weave |
| Pollen | 7 | 16,384 | dots instead of strokes, gold-to-rose wash |
| Scribe | 7 | 6,882 | the first 42% in ink, the rest an echo |
| Sigil | 5 | 1,024 | one bold glyph on paper |
| Twilight | 7 | 16,384 | a hairline over a dusk gradient |
Aurora and Ember colour the stroke by position along the curve, which turns an
abstract property into something visible: the colour moves smoothly across the square
without ever jumping, and that smoothness is the locality property. Aurora sweeps a
cool arc — teal through azure to violet — while Ember runs the same idea hot, from
indigo through magenta to amber.
Quarters and Mosaic colour by block instead of by position. The top bits of the
Hilbert index name the cell of the sub-grid a point lies in, so colouring by block
makes the recursive construction itself visible: Quarters traces the sixteen cells of
the 4 × 4 sub-grid in four inks, and Mosaic tiles every cell, weaving the four
quadrants in four shades.
Pollen and Mosaic show that the geometry and the drawing are separate decisions: the
same vertices can be rendered as strokes, discs, or tiles without changing anything
else. Pollen is one disc per cell, washed gold to rose across the diagonal — at order
seven the discs are five pixels across and the square reads as a point field.
Scribe stops the path after 42% of its segments — the curve, interrupted mid-sentence.
The rest of the sentence is still drawn, at a tenth of the strength: the echo keeps the
canvas in play, so the interruption reads as composition rather than a render that
stopped. Sigil is the same construction at order five, with a stroke thick enough to be
a shape rather than a line — one glyph on paper. Twilight puts the gradient in the
background instead, a hairline curve over a wash that runs from deep navy to plum.
Framed
Alongside the eight renderings, the gallery closes with one finished presentation: Aurora
rendered at print scale and hung on a gallery wall — a lit wall, a cast shadow, a frame,
and a bevelled mat. MuseumFrame, the gallery framing in SkiaSharp.Extensions, is a
pure presentation layer: the artwork renders itself offscreen at its own size and is
composited into the mat opening, so the framing never touches the generative design and
the artwork never knows it is framed. There is deliberately one framed presentation, not
a framed copy of every variant.
Determinism
Nothing is seeded and nothing is random. Every point is a pure bitwise function of its
index along the curve.