Walldye

Whirling squares

inspired by Le Corbusier, Le Modulor1

A 1 by 1.618 rectangle on a drafting sheet, cut into twelve shrinking squares with a spiral of quarter arcs drawn through.

Made with Claude Opus 5.5

Technique
technical drawing
Shape
Any screen
Added
27 September 2026

Colours

  • #1C1B1Abackground
  • #DAD8CEforeground
  • #CF6A4Caccent

Export

Format
Shape
Size

Notes

The long side comes from the compass construction left on the sheet as a dashed arc: a radius from the midpoint of the first square’s base to its far top corner swings down to the base line. Every square cut off leaves a smaller rectangle of the same shape, and the squares close in on the point where the dash-dot diagonals cross.

The spiral is one quarter circle per square, so it only approximates the logarithmic spiral through the same corners. The dimension lines split the long side into 1 and 0.618 and give the short side as 1.

Sources

  1. Le Corbusier, Le Modulor, 1948. ↑
  2. Golden rectangle.

Source code

wallpapers/fibonacci-squares/design.py, 126 lines

"""Whirling squares: a 1:phi rectangle on a drafting sheet, cut into twelve squares whose quarter arcs make one tapering spiral."""

from typing import NamedTuple

from walldye import ACCENT, BG, BG_ALT, UI, UI_ALT, Canvas, P, Vec, design, mix, ramp
from walldye.geom import Affine

PHI = (1 + 5**0.5) / 2
RW = 1080  # long side; the construction is drawn with the rectangle's top-left corner at (0, 0)
RH = RW / PHI
STEPS = 12
TINTS = ramp(BG, BG_ALT, STEPS + 1)[1:]  # squares tint up towards the eye, barely above the paper
FAINT = mix(BG_ALT, UI, 0.6)  # sheet furniture, a step quieter than the dimensions
DASHDOT = (18, 6, 3, 6)
DIM = 56  # offset of the dimension lines from the rectangle


class Cut(NamedTuple):
    """A square cut off what is left of the rectangle, and its quarter arc of the spiral."""

    corner: Vec  # top-left
    side: float
    start: Vec  # the arc runs clockwise from start to end around pivot
    end: Vec
    pivot: Vec


def cuts() -> tuple[Cut, ...]:
    """The STEPS squares cut off the rectangle in turn from its left, top, right and bottom."""
    x, y, w, h = 0.0, 0.0, float(RW), RH
    out: list[Cut] = []
    for k in range(STEPS):
        if k % 4 == 0:  # left
            a = h
            out.append(Cut(Vec(x, y), a, Vec(x, y + a), Vec(x + a, y), Vec(x + a, y + a)))
            x, w = x + a, w - a
        elif k % 4 == 1:  # top
            a = w
            out.append(Cut(Vec(x, y), a, Vec(x, y), Vec(x + a, y + a), Vec(x, y + a)))
            y, h = y + a, h - a
        elif k % 4 == 2:  # right
            a = h
            x0 = x + w - a
            out.append(Cut(Vec(x0, y), a, Vec(x + w, y), Vec(x0, y + a), Vec(x0, y)))
            w -= a
        else:  # bottom
            a = w
            y0 = y + h - a
            out.append(Cut(Vec(x, y0), a, Vec(x + w, y + h), Vec(x, y0), Vec(x + w, y0)))
            h -= a
    return tuple(out)


CUTS = cuts()


def sheet(s: Canvas) -> None:
    """Crop marks at the sheet corners and a blank title block in the bottom-right corner."""
    m, t = 60, 28
    marks = P()
    for cx, sx in ((m, 1), (s.w - m, -1)):
        for cy, sy in ((m, 1), (s.h - m, -1)):
            marks.M(cx + sx * t, cy).H(cx).V(cy + sy * t)
    bw, bh = 264, 74
    bx, by = s.w - m - bw, s.h - m - bh
    marks.rect(bx, by, bw, bh)
    marks.M(bx, by + bh * 0.45).H(bx + bw).M(bx + bw * 0.62, by).V(by + bh)
    marks.M(bx + bw * 0.62, by + bh * 0.72).H(bx + bw)
    s.stroke(marks, FAINT, 1.2)


def construction(s: Canvas) -> None:
    """The rectangle, its squares, construction lines, dimensions and spiral, in local units."""
    for tint, c in zip(TINTS, CUTS, strict=True):
        s.fill(P().rect(c.corner.x, c.corner.y, c.side, c.side), tint)
    grid = P()
    for c in CUTS[1:]:
        grid.rect(c.corner.x, c.corner.y, c.side, c.side)
    s.stroke(grid, UI, 1.5)
    s.stroke(P().rect(0, 0, RW, RH), UI_ALT, 1.8)

    # compass construction: swing the half-square diagonal down to find the long side
    foot, corner = Vec(RH / 2, RH), Vec(RH, 0)
    r = abs(corner - foot)
    s.stroke(
        P().M(corner).A(r, r, 0, 0, 1, RW, RH).M(foot).L(corner),
        mix(UI, UI_ALT, 0.5),
        1.3,
        dash=(6, 4),
    )

    # the eye sits where the diagonals of the rectangle and its first remainder cross
    s.stroke(P().M(0, 0).L(RW, RH).M(RH, RH).L(RW, 0), UI, 1.2, dash=DASHDOT)

    # compass pin holes: the construction's foot and the first six arc centres
    pins = P().dots([foot, *(c.pivot for c in CUTS[:6])], 4)
    s.path(pins, fill=BG, stroke=UI_ALT, stroke_width=1.4)

    # dimensions: 1 and 1/phi along the top, 1 down the left side
    dims, heads = P(), P()
    for x in (0, RH, RW):
        dims.M(x, -12).V(-DIM - 12)
    for xa, xb in ((0, RH), (RH, RW)):
        dims.M(xa, -DIM).H(xb)
        heads.arrowhead((xb, -DIM), 14, deg=0).arrowhead((xa, -DIM), 14, deg=180)
    for y in (0, RH):
        dims.M(-12, y).H(-DIM - 12)
    dims.M(-DIM, 0).V(RH)
    heads.arrowhead((-DIM, RH), 14, deg=90).arrowhead((-DIM, 0), 14, deg=-90)
    s.stroke(dims, UI, 1.3)
    s.fill(heads, UI_ALT)

    for i, c in enumerate(CUTS):
        w = round(4.2 - 2.4 * i / (STEPS - 1), 2)
        s.stroke(P().M(c.start).A(c.side, c.side, 0, 0, 1, c.end), ACCENT, w, cap="round")


@design(aspects="any")
def draw(s: Canvas) -> None:
    sheet(s)
    # Landscape keeps the original's spot right of centre. Portrait stands the rectangle upright
    # with a quarter turn anticlockwise: the first square at the bottom, dimensions left and below.
    c = s.pick(landscape=(37 / 64, 14 / 27), portrait=(0.53, 0.465))
    turn = 0 if s.landscape else -90
    with s.group(transform=Affine.frame(c, deg=turn) @ Affine.translate(-RW / 2, -RH / 2)):
        construction(s)

Run it yourself

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