Minimap
inspired by Microsoft, Visual Studio Code minimap1
A code editor’s minimap shrinks a whole file into a narrow strip, so its shape can guide navigation.
Made with Claude Opus 5.5
- Technique
- pixel art
- Shape
- Any screen
- Added
- 27 September 2026
Export
Shapecropped from 16:9
Crop
SizeThis browser can’t draw a file that large.
FormatThis browser can’t make WebP files.
Colors
- #1C1B1Abackground
- #DAD8CEforeground
- #CF6A4Caccent
Notes
The code is made up: Python-like imports, classes and nested blocks, each bar as long as the name or string it stands for.
Sources
- Microsoft, Visual Studio Code minimap, 2017. ↑
Source code
wallpapers/
"""An editor minimap at wallpaper scale: made-up code as rounded bars."""
from typing import NamedTuple
from walldye import (
ACCENT,
BG,
BG_ALT,
UI,
UI_ALT,
UI_HI,
Canvas,
P,
Ref,
Rng,
Stop,
design,
mix,
smoothstep,
)
CW, PITCH, BAR, R = 6, 14, 7, 1.5 # char width, line pitch, bar height, bar corner radius
COLS = 100 # minimap width in characters; longer lines are cut at its right edge
RIGHT = 760 # the column's left edge stays at least this far from the canvas's right edge
HALF = 9 # the viewport shows 2 * HALF lines, centered on the caret line
COMMENT, IDENT, KEYWORD, STRING, CALL = range(5) # token kinds, indices into the tone tables
KINDS = (COMMENT, IDENT, KEYWORD, STRING, CALL)
TONES = (mix(BG_ALT, UI, 0.45), UI, UI_ALT, mix(UI, UI_ALT, 0.5), UI_ALT)
# inside the viewport each kind steps up one tone, so the slab reads as lit rather than fogged
LIT = (UI, UI_ALT, mix(UI_ALT, UI_HI, 0.55), mix(UI_ALT, UI_HI, 0.3), mix(UI_ALT, UI_HI, 0.55))
type Spec = list[tuple[int, int, int]] # (kind, length, gap after) per token
class Tok(NamedTuple):
col: int
n: int # length in characters
kind: int
class Code:
"""Pseudo-Python as lines of tokens, drawn from `r` in a fixed order."""
def __init__(self, r: Rng) -> None:
self.r = r
self.lines: list[list[Tok]] = []
def line(self, indent: int, spec: Spec) -> None:
"""Append one line, indented by four columns a level."""
col = indent * 4
toks: list[Tok] = []
for kind, n, gap in spec:
toks.append(Tok(col, n, kind))
col += n + gap
self.lines.append(toks)
def ident(self, lo: int = 3, hi: int = 12) -> int:
return self.r.randint(lo, hi)
def expr(self, depth: int = 0) -> Spec:
"""Tokens of an expression: names, calls with arguments, string literals."""
r = self.r
out: Spec = []
for _ in range(r.choice([1, 1, 2, 3])):
roll = r.random()
if roll < 0.3 and depth < 1:
out.append((CALL, self.ident(4, 14), 1))
out += self.expr(depth + 1)
elif roll < 0.5:
out.append((STRING, r.randint(4, 22), 2))
else:
out.append((IDENT, self.ident(), 2))
return out
def statement(self, ind: int) -> None:
r = self.r
roll = r.random()
if roll < 0.08:
self.line(ind, [(COMMENT, r.randint(12, 60), 0)])
elif roll < 0.5:
self.line(ind, [(IDENT, self.ident(), 3), *self.expr()])
elif roll < 0.7:
self.line(ind, [(CALL, self.ident(6, 18), 1), *self.expr(1)])
else:
self.line(ind, [(KEYWORD, 6, 1), *self.expr()])
def block(self, ind: int, n: int, depth: int = 0) -> None:
r = self.r
for _ in range(n):
if depth < 3 and r.random() < 0.22:
self.line(ind, [(KEYWORD, r.choice([2, 3, 5]), 1), *self.expr(), (IDENT, 1, 0)])
self.block(ind + 1, r.randint(1, 4), depth + 1)
if r.random() < 0.3:
self.line(ind, [(KEYWORD, 4, 1)])
self.block(ind + 1, r.randint(1, 3), depth + 1)
else:
self.statement(ind)
def function(self, ind: int) -> None:
r = self.r
head = [(KEYWORD, 3, 1), (CALL, self.ident(5, 16), 1)]
self.line(ind, head + [(IDENT, self.ident(3, 8), 2) for _ in range(r.randint(1, 4))])
if r.random() < 0.6:
self.line(ind + 1, [(STRING, r.randint(20, 64), 0)])
self.block(ind + 1, r.randint(3, 10))
self.line(ind + 1, [(KEYWORD, 6, 1), *self.expr()])
self.lines.append([])
def module(self, n: int) -> list[list[Tok]]:
"""The first `n` lines of a module: imports, then functions and classes of methods."""
r = self.r
for _ in range(r.randint(3, 6)):
name, alias = self.ident(), self.ident()
self.line(0, [(KEYWORD, 4, 1), (IDENT, name, 1), (KEYWORD, 6, 1), (IDENT, alias, 0)])
self.lines += [[], []]
while len(self.lines) < n:
if r.random() < 0.3:
self.line(0, [(KEYWORD, 5, 1), (CALL, self.ident(6, 14), 0)])
for _ in range(r.randint(2, 4)):
self.function(1)
else:
self.function(0)
self.lines.append([])
return self.lines[:n]
def extent(toks: list[Tok]) -> int:
"""The column just past the line's last token; 0 for an empty line."""
return max((t.col + t.n for t in toks), default=0)
def caret_token(toks: list[Tok]) -> int | None:
"""Index of the first mid-length identifier after the line's first token, if any."""
return next((j for j, t in enumerate(toks) if j and t.kind == IDENT and 5 <= t.n <= 12), None)
def fits(toks: list[Tok]) -> bool:
"""Whether the line can carry the caret: indented, three to five visible tokens, medium
width, with an identifier to pick out."""
shown = [t for t in toks if t.n * CW > BAR]
return (
bool(toks)
and toks[0].col >= 8
and 3 <= len(shown) <= 5
and 24 <= extent(toks) <= 44
and caret_token(toks) is not None
)
@design(aspects="any")
def draw(s: Canvas) -> None:
# right of center, but never nearer than RIGHT to the right edge (1160 at 16:9); low on portrait
at = s.pick(landscape=(0.605, 7 / 15), portrait=(0.3, 0.58))
x0, cursor = min(round(at.x), s.w - RIGHT), round(at.y / PITCH)
view0, view1 = (cursor - HALF) * PITCH, (cursor + HALF) * PITCH
n = s.h // PITCH + 1
code = Code(s.rng(11)).module(n + 60)
# scroll so a medium line of a few tokens sits mid-viewport; its identifier is under the caret
top = next(k - cursor for k in range(cursor + 10, len(code) - n + cursor) if fits(code[k]))
lines = code[top : top + n]
def inside(i: int) -> bool:
return view0 <= i * PITCH < view1
right = max(extent(t) for i, t in enumerate(lines) if inside(i))
slab = P().rect(x0 - 18, view0, min(right, COLS) * CW + 36, view1 - view0)
s.fill(slab, mix(BG, BG_ALT, 0.8))
m = round(12 * s.h / 1080) # gradient stops, 90 units apart
def fade(k: int) -> Ref:
"""TONES[k] at full strength around the viewport, sinking toward the ground at the top and
bottom of the sheet."""
mid = (view0 + view1) / 2
stops: list[Stop] = []
for j in range(m + 1):
beyond = abs(s.h * j / m - mid) - HALF * PITCH # distance past the viewport's edge
stops.append((j / m, mix(TONES[k], BG, 0.85 * smoothstep(0, 440, beyond))))
return s.linear_gradient(stops, (0, 0), (0, s.h))
# bucket 2k holds kind k outside the viewport (faded), 2k + 1 inside it (lit)
paints = [p for k in KINDS for p in (fade(k), LIT[k])]
caret = P()
sel = caret_token(lines[cursor])
with s.buckets(paints, "fill") as bars:
for i, toks in enumerate(lines):
y = i * PITCH + (PITCH - BAR) / 2
for j, t in enumerate(toks):
x, w = x0 + t.col * CW, min(t.n, COLS - t.col) * CW
if w <= BAR:
continue
if i == cursor and j == sel:
caret.rrect(x, y, w, BAR, R).rect(x + w + 3, i * PITCH, 2, PITCH)
else:
bars[2 * t.kind + int(inside(i))].rrect(x, y, w, BAR, R)
s.fill(caret, ACCENT)"""An editor minimap at wallpaper scale: made-up code as rounded bars."""
from typing import NamedTuple
from walldye import (
ACCENT,
BG,
BG_ALT,
UI,
UI_ALT,
UI_HI,
Canvas,
P,
Ref,
Rng,
Stop,
design,
mix,
smoothstep,
)
CW, PITCH, BAR, R = 6, 14, 7, 1.5 # char width, line pitch, bar height, bar corner radius
COLS = 100 # minimap width in characters; longer lines are cut at its right edge
RIGHT = 760 # the column's left edge stays at least this far from the canvas's right edge
HALF = 9 # the viewport shows 2 * HALF lines, centered on the caret line
COMMENT, IDENT, KEYWORD, STRING, CALL = range(5) # token kinds, indices into the tone tables
KINDS = (COMMENT, IDENT, KEYWORD, STRING, CALL)
TONES = (mix(BG_ALT, UI, 0.45), UI, UI_ALT, mix(UI, UI_ALT, 0.5), UI_ALT)
# inside the viewport each kind steps up one tone, so the slab reads as lit rather than fogged
LIT = (UI, UI_ALT, mix(UI_ALT, UI_HI, 0.55), mix(UI_ALT, UI_HI, 0.3), mix(UI_ALT, UI_HI, 0.55))
type Spec = list[tuple[int, int, int]] # (kind, length, gap after) per token
class Tok(NamedTuple):
col: int
n: int # length in characters
kind: int
class Code:
"""Pseudo-Python as lines of tokens, drawn from `r` in a fixed order."""
def __init__(self, r: Rng) -> None:
self.r = r
self.lines: list[list[Tok]] = []
def line(self, indent: int, spec: Spec) -> None:
"""Append one line, indented by four columns a level."""
col = indent * 4
toks: list[Tok] = []
for kind, n, gap in spec:
toks.append(Tok(col, n, kind))
col += n + gap
self.lines.append(toks)
def ident(self, lo: int = 3, hi: int = 12) -> int:
return self.r.randint(lo, hi)
def expr(self, depth: int = 0) -> Spec:
"""Tokens of an expression: names, calls with arguments, string literals."""
r = self.r
out: Spec = []
for _ in range(r.choice([1, 1, 2, 3])):
roll = r.random()
if roll < 0.3 and depth < 1:
out.append((CALL, self.ident(4, 14), 1))
out += self.expr(depth + 1)
elif roll < 0.5:
out.append((STRING, r.randint(4, 22), 2))
else:
out.append((IDENT, self.ident(), 2))
return out
def statement(self, ind: int) -> None:
r = self.r
roll = r.random()
if roll < 0.08:
self.line(ind, [(COMMENT, r.randint(12, 60), 0)])
elif roll < 0.5:
self.line(ind, [(IDENT, self.ident(), 3), *self.expr()])
elif roll < 0.7:
self.line(ind, [(CALL, self.ident(6, 18), 1), *self.expr(1)])
else:
self.line(ind, [(KEYWORD, 6, 1), *self.expr()])
def block(self, ind: int, n: int, depth: int = 0) -> None:
r = self.r
for _ in range(n):
if depth < 3 and r.random() < 0.22:
self.line(ind, [(KEYWORD, r.choice([2, 3, 5]), 1), *self.expr(), (IDENT, 1, 0)])
self.block(ind + 1, r.randint(1, 4), depth + 1)
if r.random() < 0.3:
self.line(ind, [(KEYWORD, 4, 1)])
self.block(ind + 1, r.randint(1, 3), depth + 1)
else:
self.statement(ind)
def function(self, ind: int) -> None:
r = self.r
head = [(KEYWORD, 3, 1), (CALL, self.ident(5, 16), 1)]
self.line(ind, head + [(IDENT, self.ident(3, 8), 2) for _ in range(r.randint(1, 4))])
if r.random() < 0.6:
self.line(ind + 1, [(STRING, r.randint(20, 64), 0)])
self.block(ind + 1, r.randint(3, 10))
self.line(ind + 1, [(KEYWORD, 6, 1), *self.expr()])
self.lines.append([])
def module(self, n: int) -> list[list[Tok]]:
"""The first `n` lines of a module: imports, then functions and classes of methods."""
r = self.r
for _ in range(r.randint(3, 6)):
name, alias = self.ident(), self.ident()
self.line(0, [(KEYWORD, 4, 1), (IDENT, name, 1), (KEYWORD, 6, 1), (IDENT, alias, 0)])
self.lines += [[], []]
while len(self.lines) < n:
if r.random() < 0.3:
self.line(0, [(KEYWORD, 5, 1), (CALL, self.ident(6, 14), 0)])
for _ in range(r.randint(2, 4)):
self.function(1)
else:
self.function(0)
self.lines.append([])
return self.lines[:n]
def extent(toks: list[Tok]) -> int:
"""The column just past the line's last token; 0 for an empty line."""
return max((t.col + t.n for t in toks), default=0)
def caret_token(toks: list[Tok]) -> int | None:
"""Index of the first mid-length identifier after the line's first token, if any."""
return next((j for j, t in enumerate(toks) if j and t.kind == IDENT and 5 <= t.n <= 12), None)
def fits(toks: list[Tok]) -> bool:
"""Whether the line can carry the caret: indented, three to five visible tokens, medium
width, with an identifier to pick out."""
shown = [t for t in toks if t.n * CW > BAR]
return (
bool(toks)
and toks[0].col >= 8
and 3 <= len(shown) <= 5
and 24 <= extent(toks) <= 44
and caret_token(toks) is not None
)
@design(aspects="any")
def draw(s: Canvas) -> None:
# right of center, but never nearer than RIGHT to the right edge (1160 at 16:9); low on portrait
at = s.pick(landscape=(0.605, 7 / 15), portrait=(0.3, 0.58))
x0, cursor = min(round(at.x), s.w - RIGHT), round(at.y / PITCH)
view0, view1 = (cursor - HALF) * PITCH, (cursor + HALF) * PITCH
n = s.h // PITCH + 1
code = Code(s.rng(11)).module(n + 60)
# scroll so a medium line of a few tokens sits mid-viewport; its identifier is under the caret
top = next(k - cursor for k in range(cursor + 10, len(code) - n + cursor) if fits(code[k]))
lines = code[top : top + n]
def inside(i: int) -> bool:
return view0 <= i * PITCH < view1
right = max(extent(t) for i, t in enumerate(lines) if inside(i))
slab = P().rect(x0 - 18, view0, min(right, COLS) * CW + 36, view1 - view0)
s.fill(slab, mix(BG, BG_ALT, 0.8))
m = round(12 * s.h / 1080) # gradient stops, 90 units apart
def fade(k: int) -> Ref:
"""TONES[k] at full strength around the viewport, sinking toward the ground at the top and
bottom of the sheet."""
mid = (view0 + view1) / 2
stops: list[Stop] = []
for j in range(m + 1):
beyond = abs(s.h * j / m - mid) - HALF * PITCH # distance past the viewport's edge
stops.append((j / m, mix(TONES[k], BG, 0.85 * smoothstep(0, 440, beyond))))
return s.linear_gradient(stops, (0, 0), (0, s.h))
# bucket 2k holds kind k outside the viewport (faded), 2k + 1 inside it (lit)
paints = [p for k in KINDS for p in (fade(k), LIT[k])]
caret = P()
sel = caret_token(lines[cursor])
with s.buckets(paints, "fill") as bars:
for i, toks in enumerate(lines):
y = i * PITCH + (PITCH - BAR) / 2
for j, t in enumerate(toks):
x, w = x0 + t.col * CW, min(t.n, COLS - t.col) * CW
if w <= BAR:
continue
if i == cursor and j == sel:
caret.rrect(x, y, w, BAR, R).rect(x + w + 3, i * PITCH, 2, PITCH)
else:
bars[2 * t.kind + int(inside(i))].rrect(x, y, w, BAR, R)
s.fill(caret, ACCENT)
Run it yourself
$ git clone https://github.com/nickolaj-jepsen/walldye && cd walldye$ uv run walldye render minimap --theme fireproof -o minimap-fireproof-16x9.svg