Step sequencer
inspired by Teenage Engineering, OP-Z1 and Elektron, Digitakt2
A step sequencer splits a measure into sixteen steps and plays the pads switched on as the playhead passes.
Made with Claude Opus 5.5
- Technique
- instrument displays
- Shape
- Any screen
- Added
- 27 September 2026
Export
Colors
- #1C1B1Abackground
- #DAD8CEforeground
- #CF6A4Caccent
Notes
The kick lands on every beat and the snare on two and four, with one extra hit on the last step. The chord track is muted, so its steps are hollow. On a tall screen the grid takes a tracker’s layout, with time running down the screen and one column per track.
Sources
- Teenage Engineering, OP-Z, 2018. ↑
- Elektron, Digitakt, 2017. ↑
- Music sequencer.
- Tracker (music software).
Source code
wallpapers/
"""A 16-step drum sequencer grid of rounded pads; tall screens get a tracker's layout."""
from walldye import ACCENT, ACCENT_5, UI, UI_ALT, UI_HI, Canvas, P, Vec, design
PAD, GAP, BAR = 40, 12, 12 # step pad, gap between pads, extra gap between beats
PITCH = PAD + GAP
RULER, LED = 20, 39 # offsets from the grid of the step ruler and the track LEDs
HEAD = 10 # playhead step
MUTED = {7}
PATTERN = (
"x...x...x...x...", # kick
"....x.......x..x", # snare
"..x...x...x...xx", # closed hat
"......x.......x.", # open hat
"...x......x.....", # clap
"x.......x.x.....", # bass
"..........x...x.", # perc
"x.......x.......", # chord (muted)
)
# The grid's center along the steps, and the center of ruler plus grid across the tracks.
MID = Vec((16 * PITCH - GAP + 3 * BAR) / 2, (len(PATTERN) * PITCH - GAP - RULER) / 2)
def step_at(i: int) -> float:
"""Offset of step `i`'s pad along the steps, counting the extra gap after each beat."""
return i * PITCH + (i // 4) * BAR
@design(aspects="any")
def draw(s: Canvas) -> None:
# landscape: steps run left to right, right of center; portrait: they run down, as in a tracker
c = s.pick(landscape=(0.6708, 0.5), portrait=(0.5, 0.52), snap=1)
def at(u: float, v: float) -> Vec:
"""The canvas point `u` along the steps and `v` across the tracks from the grid's corner."""
if s.landscape:
return c + (u - MID.x, v - MID.y)
return c + (v - MID.y, u - MID.x)
off, on, muted, head_off, head_on = P(), P(), P(), P(), P()
for j, row in enumerate(PATTERN):
for i, ch in enumerate(row):
if ch == "x" and j in MUTED:
d = muted
elif i == HEAD:
d = head_on if ch == "x" else head_off
else:
d = on if ch == "x" else off
x, y = at(step_at(i), j * PITCH)
d.rrect(x, y, PAD, PAD, 6)
s.stroke(off, UI, 1.5)
s.path(on, fill=UI, stroke=UI, stroke_width=1.5)
s.stroke(muted, UI_ALT, 2.5)
s.stroke(head_off, ACCENT_5, 1.5)
s.path(head_on, fill=ACCENT, stroke=ACCENT, stroke_width=1.5)
# Per-track mute LEDs: the muted track's LED is lit and its steps are hollow.
leds, lit = P(), P()
for j in range(len(PATTERN)):
center = at(-LED, j * PITCH + PAD / 2)
if j in MUTED:
lit.circle(center, 6)
else:
leds.circle(center, 5)
s.stroke(leds, UI_ALT, 1.5)
s.fill(lit, UI)
# The step ruler: steps already played, still to come, and the one under the playhead.
done, todo, now = P(), P(), P()
for i in range(16):
d = now if i == HEAD else done if i < HEAD else todo
d.M(at(step_at(i), -RULER)).L(at(step_at(i) + PAD, -RULER))
s.stroke(done, UI_HI, 3)
s.stroke(todo, UI, 3)
s.stroke(now, ACCENT, 3)"""A 16-step drum sequencer grid of rounded pads; tall screens get a tracker's layout."""
from walldye import ACCENT, ACCENT_5, UI, UI_ALT, UI_HI, Canvas, P, Vec, design
PAD, GAP, BAR = 40, 12, 12 # step pad, gap between pads, extra gap between beats
PITCH = PAD + GAP
RULER, LED = 20, 39 # offsets from the grid of the step ruler and the track LEDs
HEAD = 10 # playhead step
MUTED = {7}
PATTERN = (
"x...x...x...x...", # kick
"....x.......x..x", # snare
"..x...x...x...xx", # closed hat
"......x.......x.", # open hat
"...x......x.....", # clap
"x.......x.x.....", # bass
"..........x...x.", # perc
"x.......x.......", # chord (muted)
)
# The grid's center along the steps, and the center of ruler plus grid across the tracks.
MID = Vec((16 * PITCH - GAP + 3 * BAR) / 2, (len(PATTERN) * PITCH - GAP - RULER) / 2)
def step_at(i: int) -> float:
"""Offset of step `i`'s pad along the steps, counting the extra gap after each beat."""
return i * PITCH + (i // 4) * BAR
@design(aspects="any")
def draw(s: Canvas) -> None:
# landscape: steps run left to right, right of center; portrait: they run down, as in a tracker
c = s.pick(landscape=(0.6708, 0.5), portrait=(0.5, 0.52), snap=1)
def at(u: float, v: float) -> Vec:
"""The canvas point `u` along the steps and `v` across the tracks from the grid's corner."""
if s.landscape:
return c + (u - MID.x, v - MID.y)
return c + (v - MID.y, u - MID.x)
off, on, muted, head_off, head_on = P(), P(), P(), P(), P()
for j, row in enumerate(PATTERN):
for i, ch in enumerate(row):
if ch == "x" and j in MUTED:
d = muted
elif i == HEAD:
d = head_on if ch == "x" else head_off
else:
d = on if ch == "x" else off
x, y = at(step_at(i), j * PITCH)
d.rrect(x, y, PAD, PAD, 6)
s.stroke(off, UI, 1.5)
s.path(on, fill=UI, stroke=UI, stroke_width=1.5)
s.stroke(muted, UI_ALT, 2.5)
s.stroke(head_off, ACCENT_5, 1.5)
s.path(head_on, fill=ACCENT, stroke=ACCENT, stroke_width=1.5)
# Per-track mute LEDs: the muted track's LED is lit and its steps are hollow.
leds, lit = P(), P()
for j in range(len(PATTERN)):
center = at(-LED, j * PITCH + PAD / 2)
if j in MUTED:
lit.circle(center, 6)
else:
leds.circle(center, 5)
s.stroke(leds, UI_ALT, 1.5)
s.fill(lit, UI)
# The step ruler: steps already played, still to come, and the one under the playhead.
done, todo, now = P(), P(), P()
for i in range(16):
d = now if i == HEAD else done if i < HEAD else todo
d.M(at(step_at(i), -RULER)).L(at(step_at(i) + PAD, -RULER))
s.stroke(done, UI_HI, 3)
s.stroke(todo, UI, 3)
s.stroke(now, ACCENT, 3)
Run it yourself
$ git clone https://github.com/nickolaj-jepsen/walldye && cd walldye$ uv run walldye render sequencer --theme fireproof -o sequencer-fireproof-16x9.svg