Walldye

Two folds

inspired by Bridget Riley, Movement in Squares1

A four-row checkerboard narrows into slivers at two folds, one shallow and one deep. The thinnest columns, at the deeper fold, are lit.

Made with Claude Opus 5.5

Technique
flat shapes
Inspired by
op art
Shape
16:9 (cropped for other screens)
Added
27 September 2026

Colours

  • #1C1B1Abackground
  • #DAD8CEforeground
  • #CF6A4Caccent

Export

Format
Shape
Size

Notes

Borrows the folding checkerboard of Bridget Riley’s Movement in Squares (1961), where columns narrow towards one line so the flat grid seems to bend.

This frieze has four rows and two folds, a shallow one on the left and a deep one on the right, with full squares between them.

Sources

  1. Bridget Riley, Movement in Squares, 1961. ↑

Source code

wallpapers/movement-squares/design.py, 57 lines

"""A four-row checker frieze whose columns narrow towards two folds, a shallow one and a deep one where the thinnest are lit."""

from itertools import pairwise

from walldye import ACCENT, ACCENT_1, ACCENT_3, UI, Canvas, design

X0, X1 = 120, 1800  # band ends
SHALLOW, DEEP = (640, 12.0), (1330, 3.0)  # each fold's x and the column width at it
Y0, ROWS, CELL = 540, 4, 90
POW = 1.35  # how steeply widths fall towards a fold
# Plain squares, slivers, the columns nearest the deep fold, and the core: indices 0 to 3.
TONES = (UI, ACCENT_3, ACCENT_1, ACCENT)
HEAT = 6.5  # columns narrower than this are slivers
CORE, HOT = 4, 12  # columns nearest the deep fold, by rank, that take the core, then the next tone


def edges(start: float, end: float, wmin: float) -> list[float]:
    """Column edges from `start` to `end`: the first column is CELL wide, and widths shrink
    with distance to `end` down to `wmin`, the last one clipped so the list ends at `end`."""
    d, out, x = abs(end - start), [start], start
    step = 1 if end > start else -1
    while abs(end - x) > wmin:
        w = (CELL - wmin) * (abs(end - x) / d) ** POW + wmin
        x += step * min(w, abs(end - x))
        out.append(x)
    out[-1] = end
    return out


@design()
def draw(s: Canvas) -> None:
    (f1, w1), (f2, w2) = SHALLOW, DEEP
    mid = (f1 + f2) / 2  # between the folds the columns widen again, to full squares at mid
    xs = (
        edges(X0, f1, w1)
        + edges(mid, f1, w1)[::-1][1:]
        + edges(mid, f2, w2)[1:]
        + edges(X1, f2, w2)[::-1][1:]
    )
    # half-pixel edges land on whole device pixels at 4K, so slivers don't shimmer
    cols = list(pairwise(round(x * 2) / 2 for x in xs))
    near = sorted(range(len(cols)), key=lambda c: abs(sum(cols[c]) / 2 - f2))
    rank = {c: k for k, c in enumerate(near)}

    def tone(c: int) -> int:
        a, b = cols[c]
        if rank[c] < CORE:
            return 3
        if rank[c] < HOT:
            return 2
        return 1 if b - a < HEAT else 0

    with s.buckets(TONES, "fill") as squares:
        for c, (a, b) in enumerate(cols):
            for r in range(ROWS):
                if (c + r) % 2 == 0:
                    squares[tone(c)].rect(a, Y0 + r * CELL, b - a, CELL)

Run it yourself

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