B-tree descent
Looking up key 417 in a three-level B+-tree lights one pointer per level, then the key in its leaf.
Made with Claude Opus 5.5
- Technique
- technical drawing
- Shape
- Any screen
- 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
In a B+-tree every key sits in a leaf, and the leaves are linked in order so a range scan can walk along them. The nodes above hold only separating keys, which tell a search which pointer to follow at each level. The two-character codes under the leaves stand for the records the keys point to.
Sources
- Rudolf Bayer and Edward McCreight, Organization and Maintenance of Large Ordered Indexes, 1972.
- B+ tree.
Source code
wallpapers/btree-descent/design.py, 128 lines
"""A B+-tree index as a textbook diagram of pointer and key cells; the pointers a lookup of key 417 follows are filled in from root to leaf."""
from walldye import ACCENT, BG, UI, UI_ALT, UI_HI, Canvas, P, Paint, Vec, design
from walldye.pixel import glyphs
KW, PW, CH = 36, 10, 28 # key cell width, pointer cell width, node height
LEAF_SIZES = (2, 3, 2, 2, 3, 2, 3, 2, 2) # keys per leaf, three leaves under each inner node
LEAF_GAP, DOT = 22, 2.2
TARGET = 417
def node_w(n: int) -> float:
"""The width of a node holding `n` keys between n + 1 pointer cells."""
return n * (KW + PW) + PW
@design(aspects="any")
def draw(s: Canvas) -> None:
rng = s.rng(11)
pool = [k for k in range(104, 990) if k != TARGET]
rest = sorted([*rng.sample(pool, sum(LEAF_SIZES) - 1), TARGET])
leaves: list[list[int]] = []
for n in LEAF_SIZES:
leaves.append(rest[:n])
rest = rest[n:]
# separators: the first key of every child after the first
inner = [[leaf[0] for leaf in leaves[3 * g + 1 : 3 * g + 3]] for g in range(3)]
root = [leaves[3][0], leaves[6][0]]
# the B+-tree search: at each level, one child to the right per separator not above TARGET
ri = sum(TARGET >= k for k in root)
ii = sum(TARGET >= k for k in inner[ri])
li = 3 * ri + ii
lit, edges, boxes, hot, links = P(), P(), P(), P(), P()
dots, lit_dots = P(), P()
labels: list[tuple[Vec, str, Paint]] = [] # key cell centre, text, paint
def node(
x: float, y: float, keys: list[int], ptr: int | None = None, key: int | None = None
) -> list[Vec]:
"""Lay out the cells p k p k ... p of a node with its top-left corner at (x, y), filling
in pointer number `ptr` or the cell of key `key`; returns the pointer cell centres."""
ptrs: list[Vec] = []
cx = x
for i in range(2 * len(keys) + 1):
w = KW if i % 2 else PW
if i:
boxes.M(cx, y).V(y + CH)
c = Vec(cx + w / 2, y + CH / 2)
if i % 2:
k = keys[i // 2]
if k == key:
lit.rect(cx, y, w, CH)
labels.append((c, f"{k:03d}", BG if k == key else UI_HI))
else:
on = len(ptrs) == ptr
if on:
lit.rect(cx, y, w, CH)
(lit_dots if on else dots).circle(c, DOT)
ptrs.append(c)
cx += w
boxes.rect(x, y, cx - x, CH)
return ptrs
# The leaf row is wider than a portrait screen: there the root moves right so the whole
# lookup path fits, and the tree runs off the edge mid-way through the seventh leaf.
root_top = s.pick(landscape=(0.5, 0.25), portrait=(0.75, 0.32), snap=1)
step = 250 if s.landscape else 300 # between the tops of two rows
rows = [root_top.y + k * step for k in range(3)]
total = sum(node_w(n) for n in LEAF_SIZES) + LEAF_GAP * (len(LEAF_SIZES) - 1)
x = root_top.x - total / 2
leaf_x: list[float] = []
for n in LEAF_SIZES:
leaf_x.append(x)
x += node_w(n) + LEAF_GAP
pairs = list(zip(leaf_x, leaves, strict=True))
leaf_tops = [Vec(lx + node_w(len(keys)) / 2, rows[2]) for lx, keys in pairs]
leaf_ptrs = [
node(lx, rows[2], keys, key=TARGET if i == li else None)
for i, (lx, keys) in enumerate(pairs)
]
# inner nodes centred over their three leaves; each edge leaves from a pointer cell's foot
inner_tops: list[Vec] = []
for g, seps in enumerate(inner):
kids = leaf_tops[3 * g : 3 * g + 3]
top = Vec(sum(k.x for k in kids) / 3, rows[1])
inner_tops.append(top)
ptrs = node(top.x - node_w(len(seps)) / 2, top.y, seps, ptr=ii if g == ri else None)
for c, (p, kid) in enumerate(zip(ptrs, kids, strict=True)):
(hot if g == ri and c == ii else edges).M(p + (0, CH / 2)).L(kid)
ptrs = node(root_top.x - node_w(len(root)) / 2, rows[0], root, ptr=ri)
for c, (p, kid) in enumerate(zip(ptrs, inner_tops, strict=True)):
(hot if c == ri else edges).M(p + (0, CH / 2)).L(kid)
# each leaf's last pointer links to the next leaf, ending in an open arrowhead
for lp, nx in zip(leaf_ptrs[:-1], leaf_x[1:], strict=True):
p, tip = lp[-1], nx - 3
links.M(p.x + 5, p.y).H(tip).M(tip - 5, p.y - 4).L(tip, p.y).L(tip - 5, p.y + 4)
s.fill(lit, ACCENT)
s.stroke(edges, UI, 1.2)
s.stroke(boxes, UI_ALT, 1)
s.stroke(hot, ACCENT, 2)
s.stroke(links, UI_ALT, 1.2)
s.fill(dots, UI_ALT)
s.fill(lit_dots, BG)
for c, text, paint in labels:
glyphs(s, text, paint, at=(round(c.x), c.y - 8), font="8x16", px=1, anchor="middle")
# the record id each leaf key points at, under its cell
rids = s.rng(5)
for i, (lx, keys) in enumerate(pairs):
for k, key in enumerate(keys):
cx = round(lx + PW + k * (KW + PW) + KW / 2)
paint = UI_HI if i == li and key == TARGET else UI_ALT
rid = f"{rids.randrange(16, 255):02x}"
glyphs(s, rid, paint, at=(cx, rows[2] + CH + 14), font="5x8", px=1, anchor="middle")
glyphs(
s,
f"find({TARGET})",
UI_HI,
at=(root_top.x, rows[0] - 48),
font="5x8",
px=2,
anchor="middle",
)"""A B+-tree index as a textbook diagram of pointer and key cells; the pointers a lookup of key 417 follows are filled in from root to leaf."""
from walldye import ACCENT, BG, UI, UI_ALT, UI_HI, Canvas, P, Paint, Vec, design
from walldye.pixel import glyphs
KW, PW, CH = 36, 10, 28 # key cell width, pointer cell width, node height
LEAF_SIZES = (2, 3, 2, 2, 3, 2, 3, 2, 2) # keys per leaf, three leaves under each inner node
LEAF_GAP, DOT = 22, 2.2
TARGET = 417
def node_w(n: int) -> float:
"""The width of a node holding `n` keys between n + 1 pointer cells."""
return n * (KW + PW) + PW
@design(aspects="any")
def draw(s: Canvas) -> None:
rng = s.rng(11)
pool = [k for k in range(104, 990) if k != TARGET]
rest = sorted([*rng.sample(pool, sum(LEAF_SIZES) - 1), TARGET])
leaves: list[list[int]] = []
for n in LEAF_SIZES:
leaves.append(rest[:n])
rest = rest[n:]
# separators: the first key of every child after the first
inner = [[leaf[0] for leaf in leaves[3 * g + 1 : 3 * g + 3]] for g in range(3)]
root = [leaves[3][0], leaves[6][0]]
# the B+-tree search: at each level, one child to the right per separator not above TARGET
ri = sum(TARGET >= k for k in root)
ii = sum(TARGET >= k for k in inner[ri])
li = 3 * ri + ii
lit, edges, boxes, hot, links = P(), P(), P(), P(), P()
dots, lit_dots = P(), P()
labels: list[tuple[Vec, str, Paint]] = [] # key cell centre, text, paint
def node(
x: float, y: float, keys: list[int], ptr: int | None = None, key: int | None = None
) -> list[Vec]:
"""Lay out the cells p k p k ... p of a node with its top-left corner at (x, y), filling
in pointer number `ptr` or the cell of key `key`; returns the pointer cell centres."""
ptrs: list[Vec] = []
cx = x
for i in range(2 * len(keys) + 1):
w = KW if i % 2 else PW
if i:
boxes.M(cx, y).V(y + CH)
c = Vec(cx + w / 2, y + CH / 2)
if i % 2:
k = keys[i // 2]
if k == key:
lit.rect(cx, y, w, CH)
labels.append((c, f"{k:03d}", BG if k == key else UI_HI))
else:
on = len(ptrs) == ptr
if on:
lit.rect(cx, y, w, CH)
(lit_dots if on else dots).circle(c, DOT)
ptrs.append(c)
cx += w
boxes.rect(x, y, cx - x, CH)
return ptrs
# The leaf row is wider than a portrait screen: there the root moves right so the whole
# lookup path fits, and the tree runs off the edge mid-way through the seventh leaf.
root_top = s.pick(landscape=(0.5, 0.25), portrait=(0.75, 0.32), snap=1)
step = 250 if s.landscape else 300 # between the tops of two rows
rows = [root_top.y + k * step for k in range(3)]
total = sum(node_w(n) for n in LEAF_SIZES) + LEAF_GAP * (len(LEAF_SIZES) - 1)
x = root_top.x - total / 2
leaf_x: list[float] = []
for n in LEAF_SIZES:
leaf_x.append(x)
x += node_w(n) + LEAF_GAP
pairs = list(zip(leaf_x, leaves, strict=True))
leaf_tops = [Vec(lx + node_w(len(keys)) / 2, rows[2]) for lx, keys in pairs]
leaf_ptrs = [
node(lx, rows[2], keys, key=TARGET if i == li else None)
for i, (lx, keys) in enumerate(pairs)
]
# inner nodes centred over their three leaves; each edge leaves from a pointer cell's foot
inner_tops: list[Vec] = []
for g, seps in enumerate(inner):
kids = leaf_tops[3 * g : 3 * g + 3]
top = Vec(sum(k.x for k in kids) / 3, rows[1])
inner_tops.append(top)
ptrs = node(top.x - node_w(len(seps)) / 2, top.y, seps, ptr=ii if g == ri else None)
for c, (p, kid) in enumerate(zip(ptrs, kids, strict=True)):
(hot if g == ri and c == ii else edges).M(p + (0, CH / 2)).L(kid)
ptrs = node(root_top.x - node_w(len(root)) / 2, rows[0], root, ptr=ri)
for c, (p, kid) in enumerate(zip(ptrs, inner_tops, strict=True)):
(hot if c == ri else edges).M(p + (0, CH / 2)).L(kid)
# each leaf's last pointer links to the next leaf, ending in an open arrowhead
for lp, nx in zip(leaf_ptrs[:-1], leaf_x[1:], strict=True):
p, tip = lp[-1], nx - 3
links.M(p.x + 5, p.y).H(tip).M(tip - 5, p.y - 4).L(tip, p.y).L(tip - 5, p.y + 4)
s.fill(lit, ACCENT)
s.stroke(edges, UI, 1.2)
s.stroke(boxes, UI_ALT, 1)
s.stroke(hot, ACCENT, 2)
s.stroke(links, UI_ALT, 1.2)
s.fill(dots, UI_ALT)
s.fill(lit_dots, BG)
for c, text, paint in labels:
glyphs(s, text, paint, at=(round(c.x), c.y - 8), font="8x16", px=1, anchor="middle")
# the record id each leaf key points at, under its cell
rids = s.rng(5)
for i, (lx, keys) in enumerate(pairs):
for k, key in enumerate(keys):
cx = round(lx + PW + k * (KW + PW) + KW / 2)
paint = UI_HI if i == li and key == TARGET else UI_ALT
rid = f"{rids.randrange(16, 255):02x}"
glyphs(s, rid, paint, at=(cx, rows[2] + CH + 14), font="5x8", px=1, anchor="middle")
glyphs(
s,
f"find({TARGET})",
UI_HI,
at=(root_top.x, rows[0] - 48),
font="5x8",
px=2,
anchor="middle",
)
Run it yourself
$ git clone https://github.com/nickolaj-jepsen/walldye && cd walldye$ uv run walldye render btree-descent --theme fireproof -o btree-descent-fireproof-16x9.svg