Walldye

Vinyl record

inspired by ECM Records3

A long-playing record runs off the screen, five tracks of groove spiralling in to a filled label.

Made with Claude Opus 5.5

Technique
line art
Shape
Any screen
Added
27 September 2026

Colours

  • #1C1B1Abackground
  • #DAD8CEforeground
  • #CF6A4Caccent

Export

Format
Shape
Size

Notes

The grooves are drawn as separate rings rather than one spiral, each nudged off its spacing by a hair, with wider bands of bare vinyl between the tracks. Past the last track the lead-out groove circles the label three times and ends in the locked groove, the silent loop that keeps the needle off the label.

Sources

  1. LP record.
  2. Unusual types of gramophone records.
  3. ECM Records. ↑

Source code

wallpapers/vinyl/design.py, 80 lines

"""An LP cut off by the screen edge: jittered concentric grooves in five tracks, two stepped sheen wedges and a lead-out spiral into the label."""

import math
from itertools import pairwise

import numpy as np

from walldye import (
    ACCENT,
    ACCENT_1,
    ACCENT_5,
    BG,
    BG_ALT,
    BG_DEEP,
    UI,
    UI_ALT,
    Canvas,
    P,
    Vec,
    design,
    lerp,
    mix,
    ramp,
    smoothstep,
)

R, LABEL, INNER = 700, 168, 230  # disc, label and innermost groove radii
LOCK = LABEL + 6  # the locked groove the lead-out settles into
PITCH = 3.5
GAPS = (325, 415, 500, 595)  # track gaps, from the inside out
# Per-track groove tone, BG_DEEP to BG_ALT; loud cuts read lighter.
TRACKS = tuple(mix(BG_DEEP, BG_ALT, t) for t in (0.8, 1.0, 0.7, 0.95, 0.85))
SHEEN = -135  # sheen axis in degrees; its twin points the opposite way
# Sheen steps as half-angles from the axis in degrees, brightest along it.
SHEEN_STEPS = (0, 2.5, 5, 8, 11, 14)
SHEEN_TONES = (UI_ALT, mix(UI, UI_ALT, 0.4), UI, mix(BG_ALT, UI, 0.5), mix(BG_ALT, UI, 0.2))
TONES = (*SHEEN_TONES, *TRACKS)
RUNOUT = ACCENT_5
# The lead-out warms from the inner track's tone to RUNOUT over its first 30 of 900 samples.
LEAD_CUTS = (0, 8, 16, 24, 30, 900)
LEAD_TONES = ramp(TRACKS[0], RUNOUT, len(LEAD_CUTS))[1:]


@design(aspects="any")
def draw(s: Canvas) -> None:
    # landscape: bleeding off the right edge and both long edges; portrait: off the bottom-right
    c = Vec(s.w - 360, 630) if s.landscape else Vec(s.w - 280, s.h - 400)
    rng = s.np_rng(3)
    radii = [
        r + rng.normal(0, 0.25)
        for r in np.arange(INNER, R - 20, PITCH)
        if min(abs(r - g) for g in GAPS) > 6
    ]

    s.fill(P().circle(c, R), BG_DEEP)
    # Every groove is split by angle: stepped sheen tones inside both wedges, its track tone between.
    half = SHEEN_STEPS[-1]
    with s.buckets(TONES, "stroke", stroke_width=1) as grooves:
        for r in radii:
            track = len(SHEEN_TONES) + sum(r > g for g in GAPS)
            for axis in (SHEEN, SHEEN + 180):
                for k, (h0, h1) in enumerate(pairwise(SHEEN_STEPS)):
                    grooves[k].arc(c, r, deg=(axis + h0, axis + h1))
                    grooves[k].arc(c, r, deg=(axis - h1, axis - h0))
                grooves[track].arc(c, r, deg=(axis + half, axis + 180 - half))
    s.stroke(P().circle(c, R - 1), mix(BG_ALT, UI, 0.4), 2)

    # Lead-out: leaves the innermost groove tangentially and eases into the locked groove over
    # three turns.
    t = np.linspace(0, 1, LEAD_CUTS[-1])
    rr = lerp(radii[0], LOCK, smoothstep(0, 1, t))
    a = math.radians(SHEEN + 90) + t * 6 * math.pi
    xy = c + np.column_stack([rr * np.cos(a), rr * np.sin(a)])
    for tone, (i0, i1) in zip(LEAD_TONES, pairwise(LEAD_CUTS), strict=True):
        s.stroke(P().poly(xy[i0 : i1 + 1]), tone, 1.1, cap="round")
    s.stroke(P().circle(c, LOCK), RUNOUT, 1.1)

    s.fill(P().circle(c, LABEL), ACCENT)
    s.stroke(P().circle(c, 132), ACCENT_1, 2)
    s.fill(P().circle(c, 9), BG)

Run it yourself

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