Walldye

Nix snowflake

after Simon Frankau and Tim Cuthbertson, NixOS logo1

The NixOS snowflake as a construction drawing, its lambdas’ edges run on as guide lines and one lambda filled in.

Made with Claude Opus 5.5

Free to use, with credit as given above.

Technique
technical drawing
Shape
Any screen
Added
27 September 2026

Colours

  • #1C1B1Abackground
  • #DAD8CEforeground
  • #CF6A4Caccent

Export

Format
Shape
Size

Notes

Each lambda is snapped to a triangular lattice at one sixth of the bar width, and the six are turned 60 degrees apart around a hexagonal opening, keeping the narrow seams of the official artwork between them. Inside the dash-dot circle, a guide line is drawn only where it leaves the end of a bar or frames the opening, so the pockets between the bars stay clear.

Sources

  1. Simon Frankau and Tim Cuthbertson, NixOS logo. ↑

Source code

wallpapers/nix-snowflake/design.py, 129 lines

"""The NixOS snowflake as a construction drawing: six lattice-snapped lambdas whose edges run on as fading guide lines, one picked out."""

import math
from dataclasses import dataclass

from shapely import LineString, Point, Polygon
from shapely.geometry.base import BaseGeometry
from shapely.ops import unary_union

from walldye import ACCENT, BG, BG_ALT, UI, UI_ALT, Canvas, P, Vec, design, polar
from walldye.geom import Affine, ngon, parts

U = 52  # bar width, the lattice unit
FADE = 620  # construction lines dissolve into the background by this radius
DASHDOT = (24, 6, 3, 6)
ARROW, ARROW_W = 13, 4.2
# One lambda on a 1/6 triangular lattice (a, b): a along +x, b along 60° down-right; centre at
# the origin. Snapped from the official nix-snowflake.svg, which leaves a ~0.14 U gap between
# neighbours.
LATTICE = (
    (-11, 1),
    (-11, 27),
    (-17, 27),
    (-17, 20),
    (-24, 27),
    (-27, 27),
    (-27, 24),
    (-17, 14),
    (-17, 7),
)
LAMBDA = [Vec(a / 6 + b / 12, b / 6 * math.sqrt(3) / 2) for a, b in LATTICE]  # in U
HEX = 11 / 6 * math.sqrt(3) / 2  # apothem of the central void, in U
R = U * max(abs(v) for v in LAMBDA)  # the circumscribed circle


@dataclass
class Guide:
    """A line some emblem edges lie on: direction `ang` (radians, mod pi), signed normal
    offset `off` from the centre, and the edges on it."""

    ang: float
    off: float
    edges: list[tuple[Vec, Vec]]


def guides(polys: list[list[Vec]], c: Vec) -> list[Guide]:
    """Every emblem edge grouped by the line it lies on; facing edges one gap apart share the
    line through the middle of the gap."""
    found: list[Guide] = []
    for p in polys:
        for a, b in zip(p, p[1:] + p[:1], strict=True):
            ang = math.atan2(b.y - a.y, b.x - a.x) % math.pi
            off = Vec(math.cos(ang), math.sin(ang)).perp().dot(a - c)
            for g in found:
                if abs((g.ang - ang + 0.1) % math.pi - 0.1) < 1e-3 and abs(g.off - off) < 10:
                    g.off = (g.off + off) / 2
                    g.edges.append((a, b))
                    break
            else:
                found.append(Guide(ang, off, [(a, b)]))
    return found


def strands(g: BaseGeometry) -> list[LineString]:
    """The line parts of `g` longer than 2 units; shorter ones are boolean slivers."""
    return [q for q in parts(g) if isinstance(q, LineString) and q.length > 2]


@design(aspects="any")
def draw(s: Canvas) -> None:
    c = s.pick(landscape=(31 / 48, 0.5), portrait=(0.5, 0.42))
    polys = [[Affine.frame(c, deg=60 * k, scale=U)(v) for v in LAMBDA] for k in range(6)]
    bars = unary_union([Polygon(p) for p in polys]).buffer(2.5, join_style="mitre")
    void = Polygon(ngon(c, HEX * U / math.cos(math.pi / 6) - 1, 6, deg=30))
    field = Point(c.x, c.y).buffer(FADE, quad_segs=128)
    inner = Point(c.x, c.y).buffer(R - 1, quad_segs=128)

    # construction: every edge extended to the fading field, less where it crosses a bar
    lines = P()
    for g in guides(polys, c):
        d = Vec(math.cos(g.ang), math.sin(g.ang))
        o = c + d.perp() * g.off
        runs = LineString([o - d * 2 * FADE, o + d * 2 * FADE]).intersection(field)
        runs = runs.difference(bars)
        if abs(abs(g.off) - HEX * U) < 10:  # the six lines framing the void are kept whole
            keep = strands(runs)
        else:
            # inside the circle, keep only runs that leave a bar's end, not lattice fill
            ends = [Point(v.x, v.y).buffer(4) for e in g.edges for v in e]
            keep = [
                q
                for q in strands(runs.difference(void))
                if not q.within(inner) or any(q.intersects(e) for e in ends)
            ]
        for q in keep:
            lines.poly(q.coords)
    fade = s.radial_gradient([(0, BG_ALT), (R * 1.2 / FADE, BG_ALT), (1, BG)], c, FADE)
    s.stroke(lines, fade, 1.2)

    # centre lines and the circumscribed circle
    cl = P()
    for k in range(3):
        cl.M(polar(c, R + 60, deg=60 * k)).L(polar(c, R + 60, deg=60 * k + 180))
    s.stroke(cl.circle(c, R), UI, 1.2, dash=DASHDOT)

    # dimensions: the overall width below, and the picked-out lambda's foot (one bar wide)
    # above it, with the arrows outside
    ext, dl, heads = P(), P(), P()
    yd = c.y + R + 80
    for x in (c.x - R, c.x + R):
        ext.M(x, c.y + 14).V(yd + 12)
    dl.M(c.x - R, yd).H(c.x + R)
    heads.arrowhead((c.x + R, yd), ARROW, deg=0, width=ARROW_W)
    heads.arrowhead((c.x - R, yd), ARROW, deg=180, width=ARROW_W)
    (x1, yf), x0 = polys[0][1], polys[0][2].x
    yc = yd - 36
    for x in (x0, x1):
        ext.M(x, yf + 12).V(yc + 12)
    dl.M(x0 - 34, yc).H(x0).M(x1, yc).H(x1 + 34)
    heads.arrowhead((x0, yc), ARROW, deg=0, width=ARROW_W)
    heads.arrowhead((x1, yc), ARROW, deg=180, width=ARROW_W)
    s.stroke(ext, UI, 1.2)
    s.stroke(dl, UI_ALT, 1.2)
    s.fill(heads, UI_ALT)

    # the first lambda carries the accent; the rest alternate UI and UI_ALT
    with s.buckets((ACCENT, UI, UI_ALT), "fill") as b:
        for k, p in enumerate(polys):
            b[0 if k == 0 else 2 - k % 2].poly(p, closed=True)

Run it yourself

$ git clone https://github.com/nickolaj-jepsen/walldye && cd walldye$ uv run walldye render nix-snowflake --theme fireproof -o nix-snowflake-fireproof-16x9.svg