A pointy-top hexagon with three printed paths

Each tile is a regular hexagon with its six sides numbered 0–5 clockwise, starting at the upper-right. Three paths are printed inside, each connecting a pair of sides:

Together the three paths form a perfect matching of the six sides: every side belongs to exactly one path. Rotation by one step (60° clockwise) relabels every physical side i → (i+1) mod 6, shifting which sides each path connects — but the arc geometry stays fixed inside the tile frame. There are therefore 6 distinct rotations (0–5), each producing a different matching of the physical sides.

The over/under rule at the crossing is fixed by the printing: the green 2↔4 arc is always drawn over the blue 3↔5 arc. This becomes important for topology (knots) later; for connectivity it makes no difference, because the crossing does not merge the two strands.

Close-up of two crossing arc ribbons, green 2-4 over blue 3-5
Close-up of the crossing anatomy at a single tile. The green 2↔4 ribbon passes over the blue 3↔5 ribbon; the black edge-lines mark where each arc crosses its side perpendicularly.

Arc ribbons crossing edges perpendicularly

The rendered ribbon for each path is a circular arc strip with a fixed width (radius ± k/6 around the arc centreline, where k is the circumradius). The arc centre is chosen so that the ribbon meets each hex side at a right angle. This is the key geometric property that makes tiles visually seamless across shared edges.

The construction is the same for both arc types:

Because O lies on the extension of each side-line, the vector from O to any side midpoint is perpendicular to that side — so the arc naturally arrives perpendicular to the edge. For the adjacent pair 0↔1, the lines intersect at a shared vertex (giving a tight arc, r ≈ k/3); for skip-one pairs the intersection is far outside the tile, giving the wide sweeping arcs (r ≈ 3k/2).

Why perpendicularity matters: When two tiles share an edge, both arcs arrive at the same edge midpoint from the same perpendicular direction. The ribbon ends meet exactly, and the combined path is smooth across tile boundaries — no gaps, no kinks.

Placing tiles on a lattice

Tiles sit on the axial hex lattice. Each cell is identified by an integer pair (col, row), and its pixel centre is

(x, y) = (√3·k·(col + row/2),  1.5·k·row)

Side e (0–5) of cell (q, r) faces the neighbour at (qq, rr):

Side e Screen direction col, Δrow)
030° upper-right(+1, −1)
190° right(+1,  0)
2150° lower-right(0, +1)
3210° lower-left(−1, +1)
4270° left(−1,  0)
5330° upper-left(0, −1)

When two tiles share an edge, the arc-ends from both sides meet at the same edge midpoint, and their perpendicular arrival guarantees a smooth joint. Gluing is purely by matching: tile A's side e connects to tile B's side (e+3) mod 6 (the opposite side).

Reduction to a union-find over lattice edges

Because crossings are over/under only — they never merge strands — the entire connectivity of any tiling reduces to a graph on the edges of the hex lattice itself. Each lattice edge is a potential arc endpoint; tiles connect them.

The lattice-edge graph. Nodes = lattice edges (one node per side of every placed tile; boundary sides have one adjacent cell, interior sides have two). Each placed tile adds 3 connections — its three matching pairs — merging node-pairs in a union-find. A loop closes exactly when a connection joins two nodes already in the same component.

This is both the computational model and the conceptual one. Two quantities are tracked as tiles are placed:

They satisfy a simple accounting rule. Each new tile registers up to 6 new lattice-edge nodes and merges up to 3 pairs. If a merge joins two nodes in the same component, loops increments; otherwise the two components merge and comps decrements. The invariant comps = paths + loops is maintained throughout.

Analytic checks

paths = B/2

There is a clean topological identity that holds for any filled convex patch, regardless of the individual rotations chosen:

paths = B / 2

where B is the number of boundary lattice edges (sides that face an unoccupied cell). The proof is a two-line counting argument: every open path component has exactly two ends, and ends can only sit on boundary edges; conversely every boundary edge belongs to exactly one open path end. So paths × 2 = B.

For an N-tile patch laid out as a filled hex spiral, the boundary length is B ≈ 2√3·√N (the perimeter of a hex disc). Therefore:

The identity paths = B/2 is verified immediately in the 3-tile triangle: B = 12 boundary sides (each tile has 6 sides, 3 pairs are shared, so 18−6=12), giving paths = 6. The widget below confirms this.
Three tiles arranged in a triangle forming one closed loop
The unique rotation triple (1, 3, 5) that closes a loop in the 3-tile triangle. All three red 0↔1 arcs converge at the shared central vertex, forming a single closed triangle loop. paths = B/2 = 12/2 = 6; loops = 1.
Filled hexagonal patch of 91 tiles with paths and loops highlighted in distinct colours
A fully-random 91-tile hexagonal patch (5 tiles per side). Each connected component — open paths and closed loops — is rendered in its own colour. Boundary path-ends are visible at the perimeter; interior loops appear as enclosed blobs. Count: paths ≈ B/2.

The first possible loop: 1 in 216 triples

The earliest a loop can form is when the third tile is placed, completing the smallest possible enclosed region — a triangle of three tiles sharing one central vertex. There are 6³ = 216 equally likely rotation triples for three randomly-placed tiles. Exactly 1 closes a loop.

The argument: for the loop to close at tile 3, each of the three tiles must route the red 0↔1 adjacent arc through the shared vertex. The probability of a single tile doing this is 1/6 (only one of the 6 rotations places the corner-cut arc at that vertex). Independent tiles give (1/6)³ = 1/216.

The unique looping triple for the canonical triangle (0,0)-(1,0)-(0,1) is rotations (1, 3, 5) — each tile's adjacent arc meets at the shared internal vertex.

The widget above runs an exhaustive search over all 216 triples on page load and reports the count in the footer. Try rotating the tiles by hand to find the loop, or press "Find the loop" to jump directly to (1, 3, 5).

Density extrapolation: at large N, the loop density converges to c ≈ 0.03568 loops per tile — roughly one loop per 28 tiles. Size-3 triangles contribute ρ(3) = 2/216 = 1/108 exactly (two lattice vertices per tile, each a triangle opportunity).

The shared simulation engine

All interactive widgets on this site use the same two JavaScript modules, ported directly from the Python ground-truth:

A node self-test (u_engine_selftest.mjs) verifies the JS port reproduces the Python results exactly: tile 1/2/3 path counts, the 1/216 triangle, and loop counts matching on a seeded 2000-tile spiral.