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
FormatThis browser can’t make WebP files.
Shapecropped from 16:9
Crop
SizeThis browser can’t draw a file that large.
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
- 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)"""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