Walldye

Zigzag runner

inspired by Anni Albers, Study for Camino Real1

A road of lit triangles zigzags twice across a woven runner of half-square chevrons, fringed at both ends, lighting up more strongly as it goes.

Made with Claude Opus 5.5

Technique
flat shapes
Inspired by
modernism
Shape
Any screen
Added
27 September 2026

Colours

  • #1C1B1Abackground
  • #DAD8CEforeground
  • #CF6A4Caccent

Export

Format
Shape
Size

Notes

The weave of half-square triangles and the stepped road come from mid-century weaving. Here the cloth lies across as a runner, the road makes a W from one corner, and about one cell in ten turns its diagonal the other way, picked out one step stronger.

Sources

  1. Anni Albers, Study for Camino Real, 1967. ↑

Source code

wallpapers/camino/design.py, 108 lines

"""A woven runner of half-square triangles in chevron bands, fringed at both ends, crossed by a zigzag road of triangles lit more strongly as it goes."""

from shapely.geometry import Polygon
from shapely.ops import unary_union

from walldye import (
    ACCENT,
    ACCENT_1,
    ACCENT_3,
    ACCENT_6,
    BG_ALT,
    UI,
    UI_ALT,
    Canvas,
    Colour,
    P,
    Vec,
    design,
    ladder,
    ramp,
)

COLS, ROWS, CELL = 24, 10, 40  # along and across the runner
# (row step per column, columns): a W from the lower left corner, rising first
RUNS = ((-1, 7), (1, 6), (-1, 6), (1, 4))
THREAD = 20  # fringe pitch; threads hang 44 to 56 beyond each end

type Tri = list[Vec]


def tris(i: int, j: int, flip: bool) -> tuple[Tri, Tri]:
    """Cell (i, j)'s two triangles (upper, lower) in runner space, top-left corner at the origin;
    `flip` cuts the cell along / instead of \\."""
    tl = Vec(i * CELL, j * CELL)
    tr, br, bl = tl + (CELL, 0), tl + (CELL, CELL), tl + (0, CELL)
    return ([tl, tr, bl], [tr, br, bl]) if flip else ([tl, tr, br], [tl, br, bl])


def road() -> dict[tuple[int, int], tuple[int, bool]]:
    """{cell: (step index, flip)} along the zigzag, left to right."""
    i, j, out = 0, ROWS - 1, {}
    for dj, n in RUNS:
        for _ in range(n):
            out[(i, j)] = (len(out), dj < 0)
            i, j = i + 1, j + dj
    out[(i, j)] = (len(out), RUNS[-1][0] < 0)
    return out


@design(aspects="any")
def draw(s: Canvas) -> None:
    # landscape: the runner lies across, a little below centre; portrait: it hangs, mirrored
    # across its diagonal so the road zigzags down the screen
    size = Vec(COLS * CELL, ROWS * CELL)
    wide = s.landscape
    zoom = 1.0 if wide else 1.25  # a hanging runner is drawn larger to fill the tall screen
    shown = (size if wide else Vec(size.y, size.x)) * zoom
    o = s.pick(landscape=(0.5, 0.54), portrait=(0.5, 0.5), snap=1) - shown / 2

    def at(p: Vec) -> Vec:
        return o + (p if wide else Vec(p.y, p.x)) * zoom

    def tri(t: Tri) -> Tri:
        return [at(p) for p in t]

    def area(pieces: list[Tri], paint: Colour) -> None:
        """Fill the union of `pieces` as one shape, so no internal edge can seam."""
        s.fill(P().shape(unary_union([Polygon(tri(t)) for t in pieces])), paint)

    r = s.rng(24)
    path = road()
    n = len(path)
    light = ladder((ACCENT_3, ACCENT_1, ACCENT), n)
    dark = ramp(ACCENT_6, ACCENT_3, n)
    ui: list[Tri] = []
    hi: list[Tri] = []
    for j in range(ROWS):
        for i in range(COLS):
            if (i, j) in path:
                continue
            odd = r.random() < 0.1
            upper, lower = tris(i, j, (i % 2 == 1) ^ odd)
            # the plain weave: lit upper halves in bands of two rows
            if odd:
                hi.append(upper)
            elif (j // 2 + i) % 2 == 0:
                ui.append(upper)
            else:
                ui.append(lower)
    # painted in layers so every edge blends into the colour beneath, not the wall
    s.fill(P().poly(tri([Vec(0, 0), Vec(size.x, 0), size, Vec(0, size.y)]), closed=True), BG_ALT)
    area(ui, UI)
    area(hi, UI_ALT)
    for (i, j), (k, flip) in path.items():
        cell = [
            Vec(i, j) * CELL,
            Vec(i + 1, j) * CELL,
            Vec(i + 1, j + 1) * CELL,
            Vec(i, j + 1) * CELL,
        ]
        s.fill(P().poly(tri(cell), closed=True), dark[k])
        s.fill(P().poly(tri(tris(i, j, flip)[0]), closed=True), light[k])
    fringe = P()
    for k in range(int(size.y) // THREAD):
        y = THREAD / 2 + k * THREAD
        for x0, sign in ((0.0, -1), (size.x, 1)):
            fringe.M(at(Vec(x0 - sign, y))).L(at(Vec(x0 + sign * r.uniform(44, 56), y)))
    s.stroke(fringe, UI, 1.5 * zoom)

Run it yourself

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