Walldye

NixOS logo

after Simon Frankau and Tim Cuthbertson, NixOS logo1

The NixOS logo is a snowflake of six lambdas, and Nix configures systems in its own functional language.

Made with Claude Opus 5.5

Free to use, with credit as given above.

Technique
technical drawing
Shape
Any screen
Added
27 September 2026

Versions

Export

Shape
cropped from 16:9
Across
Down
Scale
Crop
Size
Format

Colors

  • #1C1B1Abackground
  • #DAD8CEforeground
  • #CF6A4Caccent

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/nixos-logo/design.py, 137 lines

"""The NixOS snowflake as a construction drawing of six lattice-snapped lambdas with guide lines."""

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, Params, Vec, design, knob, 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; center at
# the origin. Snapped from the official nix-snowflake.svg, which leaves a ~0.14 U gap between
# neighbors.
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 center, 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):
            # the nudge keeps rounding from turning a level edge by pi
            ang = (math.atan2(b.y - a.y, b.x - a.x) + 1e-9) % 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]


class Drawing(Params):
    dimensions: bool = knob(default=True, doc="the overall width and one bar's width")


@design(aspects="any", variants={"undimensioned": Drawing(dimensions=False)})
def draw(s: Canvas[Drawing]) -> None:
    at = s.pick(landscape=(31 / 48, 0.5), portrait=(0.5, 0.42))
    with s.motif(at, FADE, handle=R + 60):  # grabbed by the snowflake and its center lines
        c = Vec(0, 0)
        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)

        # center 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)

        if s.params.dimensions:
            # 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 nixos-logo --theme fireproof -o nixos-logo-fireproof-16x9.svg