claude-brainrot
Subway Surfers while CC is thinking.
A narrow strip of generated gameplay that appears beside your Claude Code window while it is thinking, and gets out of the way when it stops.
No footage, no downloads. Every world is generated at runtime from a seed that
has never been used before and never will be again — rendered in real 3D with
models built by the Blender scripts in this repository. Those three clips are
brainrot shoot output at 15 fps, not a screen recording of something else.
you hit enter
│
▼
UserPromptSubmit hook ──UDP──► daemon ──► new seed ──► new world ──► fade in
...
Stop hook ────────────────────► daemon ──────────────────────────► fade out
The tower
The default scene, and where most of the work has gone. It is the Parkour Spiral format — a course winding up the outside of a tower that keeps going out of the top of the frame — against a course of thirty-three hand-designed levels, 385 authored landings, rather than a generated one.
Eight of the thirty-three. A level is a themed terrace with a landmark on it — a windmill, a watchtower, a bell frame, a crane, a great mushroom cap — and three things about it are load-bearing:
- The course goes through the landmark, not past it. Placing a structure and measuring how often it was placed says nothing: the first version stood one up on 97% of levels and a viewer watching five minutes of live strip remembered exactly one. Framing is the number that matters — how long a structure holds 25° of your view, unoccluded, and twelve of the fourteen blueprints now carry a passage the course is steered through.
- The course flies. After the first step up off the terrace, every landing stands at least two blocks over the ground. Miss a jump and you land on the terrace, which is a dead end — the course left it at landing one — so you walk back to the start of the level rather than rejoining halfway. It used to be that 88% of missed jumps walked straight back into the middle of the course; it is now 1 in 8,484.
- Every jump is checked against the physics before it is placed. Gravity, jump impulse and sprint speed are Minecraft's own numbers converted from ticks, so the furthest a hop can reach is computed rather than chosen. A designed landing that no jump can reach is refused at build time, not discovered mid-run.
Each level is also its own place, and the lever for that turned out to be the floor rather than the skyline: a median of six materials underfoot where a generated level uses two or three. (The real Parkour Spiral map measures fourteen, so there is room left.) Ten to thirteen seconds of strip each, its own signature move, its own way out.
What else plays
Four scenes, chosen per run from the run's seed — runner, parkour and
tower by default, with spiral one line of config away.
| Scene | What it is |
|---|---|
runner |
The classic: three locked lanes, a chase camera, subway cars with per-run liveries, hazard barriers, gantries, coin arcs, and a city canyon whose windows light up at night. The runner rides train roofs — the ramp, the train, the corridor onto it and the clear lane to bail into are laid down as one decision, and the leap is solved at generation time at both ends of the speed range, so a ramp whose train is out of reach is never built. An autopilot follows a corridor guaranteed reachable by construction and live-dodges oncoming trains. |
parkour |
First-person infinite parkour, the way the actual background reels do it: one flawless sprint-jump per beat, high over an ocean of wooded islands, reef shallows and drifting cloud shelves. The course arrives in set-pieces — a staircase, a plank causeway, a gate you run through, a long fall onto a lantern-lit platform — each built from one family of materials. Orbs hang on the exact arc of the jump that reaches them, so every one laid down is collected. Something is always in your hand: a block, a sword, a pickaxe, a torch. |
tower |
The thirty-three designed levels above. |
spiral |
The same tower format with the course generated instead of authored: one solid inverted cone, fluted with vertical ribs, fifteen themes out of a shuffled bag several to a revolution with hard seams between them, and the parkour built into each theme's own ground — lily pads across the desert's pond, a mine's ladders, an ice slide, a nether lava channel, a soul-sand bubble column. Still registered and still tested; tower simply reads better. |
Every run also generates its own palette, time of day, weather and sky — a sun or crescent moon with a real glow, parallax clouds, a starfield that thins toward the horizon, rain or snow. The same scene twice in a row does not look the same.
Sixteen consecutive runs, one frame each, nothing hand-picked — whatever the seed chose.
Install
Python 3.11 or newer, and git. There is nothing to compile and no GPU
requirement — pip pulls two wheels (raylib ~2 MB, Pillow) and that is the
whole dependency list.
Linux (GNOME/Wayland — Ubuntu, Fedora, …)
sudo apt install -y git python3-venv # Debian/Ubuntu; skip if you have them
git clone https://github.com/tristanmuzzu/claude-brainrot
cd claude-brainrot
python3 -m venv .venv
source .venv/bin/activate
pip install .
brainrot install # hooks, plus the gnome-shell extension
brainrot extension load # load it into the running shell (or just log out and in)
brainrot run # long-lived; leave it running
Verified on Ubuntu 26.04 / GNOME Shell 50.1 (Wayland, 200% scaling) — brainrot doctor reports 14 checks passing from a clean clone.
Windows 10/11
git clone https://github.com/tristanmuzzu/claude-brainrot
cd claude-brainrot
py -m venv .venv
.venv\Scripts\Activate.ps1
pip install .
brainrot install # writes hooks into ~/.claude/settings.json
brainrot run # long-lived; leave it running
That is the whole setup. No pywin32 — the window work is ctypes on user32.
Do not use a Microsoft Store Python. The Store redirects %APPDATA% into
the package's own LocalCache for reads as well as writes, so the config file
you can see in Explorer is one the daemon cannot open at all, and every setting
silently reverts to its default. Install from
python.org instead; brainrot doctor
prints the config path it is genuinely reading.
The hooks are written with the absolute path of the interpreter you
installed into, so the daemon and the hooks work whether or not the venv is
activated. Move or delete the venv and you must re-run brainrot install.
Why Linux needs that extra extension step
brainrot install copies a small gnome-shell extension into
~/.local/share/gnome-shell/extensions and switches it on. gnome-shell only
looks for new extensions when it starts, though, and on Wayland that means
the session — so it needs one of:
brainrot extension load # loads it into the running shell, no logout
which walks you through a one-time fifteen-second toggle in Looking Glass
(Alt+F2 → lg) and puts the setting back afterwards; or simply log out
and back in, which does the same job with no steps to follow.
The extension exists because a Wayland client is not allowed to know which window is in front, where any window is, or where the pointer is — and the strip needs all three, to appear only while you are looking at Claude Code, to stand beside that window, and to be draggable. It sends window geometry, window classes, process ids and the pointer to the daemon on loopback. No titles, no contents, no keystrokes.
It works without the extension too (brainrot install --no-extension, or
before you have loaded it): the strip docks to the work area, stays up for the
whole turn, and cannot be dragged. It also deliberately stays out of the
always-above band in that state — a window that floats over everything and
cannot hide itself is a window that sits on top of your browser, so instead it
comes up over the Claude Code window you were just typing into and gets buried
the moment you click anything else.
brainrot run says which of the two modes it is in, and says so again if that
changes underneath it. brainrot doctor and brainrot extension status both
report on it too.
Wayland-only distributions are the reason this is not simpler: GNOME 49 dropped the X11 session, so Ubuntu 25.10 and later have no X11 session to fall back to. The strip is therefore an XWayland window — X11 is still a first-class protocol on that desktop, and it is the one that lets a window place itself, stay above, shape its input region and be shown without taking focus.
Open Claude Code and give it something slow to do. Check any machine with
brainrot doctor. Remove with brainrot uninstall.
It belongs to Claude Code, not to your screen
The overlay is not on screen just because Claude is busy. The hook tells the daemon which window your prompt came from — that window is your Claude Code — and from then on:
- It only appears while you are looking at that window. Switch to your
browser and it fades out; switch back and it returns. Nothing known about
where Claude Code is means it stays hidden, rather than floating over
everything. (
follow_focus = falsefor the always-on behaviour, which is what demos and screen recordings want.) - It stands beside that window, not on your screen edge. A screen edge is
wherever the app keeps its sidebar; the strip goes into the empty gutter on
the
dockside of the Claude Code window instead, and outside that window entirely when the desktop has room. It follows the window if you move it. - It rides one z-level above it. On Windows the strip is an owned window
of the Claude Code window, so raising your terminal raises the strip with it
and anything else you focus covers both. mutter has no notion of an X11
window owned by a Wayland one, so on Linux the strip is in the always-above
band and the focus rule above — not the z-order — is what keeps it off your
other applications.
attach = "topmost"restores float-over-all on Windows. - You can move it. Hold
ctrl+altand drag it wherever you like; where you drop it is remembered and still follows the window. Double-click while holding the chord to go back to automatic placement. Clicks are caught only while that chord is held over the strip — otherwise the mouse falls straight through, and focus is never taken either way. (Linux: needs the extension, which is where the pointer position comes from.) - It is the size you asked for. On a HiDPI display the strip is drawn at
the configured size and scaled to the display, so
width = 360means 360 points rather than 360 device pixels — and the scenes keep their own pixel geometry rather than rendering a HUD at half size.
Why it does not annoy you
The gap between "Claude is busy" and "put something on screen" is where all the work is:
- Short turns never show anything. Nothing appears until Claude has been busy for 1.5s, so quick answers do not make the strip strobe.
- Nothing flashes. Once it has appeared it stays for at least 3s, even if Claude finishes immediately afterwards.
- It never takes focus or eats clicks. The window is click-through and cannot be activated, so it does not interrupt typing and you can click straight through it.
- It hides when Claude needs you. A permission prompt means your attention belongs on the terminal.
- It costs nothing while hidden. No scene is held, nothing renders, and the loop parks on a long sleep.
Try it without Claude Code
brainrot demo --scene tower # a normal window, always on
brainrot demo --scene runner --seed 4712 # replay one specific run
brainrot shoot --scene tower --frames 300 --out shots/ # frames to PNG
brainrot scenes # what is registered, and your run count
runner and parkour are also playable — arrows or WASD, up or space to
jump, down to duck, eight idle seconds to hand control back. brainrot demo
needs no chord for it. The overlay never reads a key until you deliberately
hand it focus; there is no global key hook, on purpose.
brainrot ping UserPromptSubmit and brainrot ping Stop drive the real daemon
by hand, which is the fastest way to check show/hide behaviour without waiting
on a real turn.
Configuration
Optional config.toml, next to the run-counter state:
- Linux/macOS:
~/.local/state/claude-brainrot/config.toml - Windows:
%APPDATA%\claude-brainrot\config.toml
Run brainrot doctor for the path it is actually reading. On a Microsoft
Store Python that is not the path above: the Store redirects %APPDATA%
into the package's LocalCache, and the copy you can see in Explorer is one
the daemon cannot open at all. The config check prints the real one.
[window]
width = 360
height = 640
dock = "right" # which side of the Claude Code window to stand on
margin_x = 12 # gap from that side
margin_y = 20 # gap from the top and bottom of that window
anchor_y = 0.0 # 0 = top of it, 1 = bottom of it
monitor = 0 # only used when no host window is known
opacity = 0.88
fps = 60
attach = "host" # "host" = one level above Claude Code, "topmost" = above all
[behaviour]
grace_seconds = 1.5
min_visible_seconds = 3.0
hide_on_notification = true
max_thinking_seconds = 900 # give up on a session that stops saying anything
follow_focus = true # on screen only while you are looking at Claude Code
drag_chord = "ctrl+alt" # hold to drag the strip; "" to disable
[content]
scenes = ["runner", "parkour", "tower"] # "spiral" is the fourth
quality = "high"
Any field can also be set with an environment variable:
BRAINROT_WIDTH, BRAINROT_GRACE_SECONDS, BRAINROT_SCENES, and so on.
Platform support
| Overlay | Click-through | Never focused | Follows Claude Code | Notes | |
|---|---|---|---|---|---|
| Windows 10/11 | yes | yes | yes | yes, as an owned window | ctypes on user32, no extra deps |
| Linux, GNOME/Wayland | yes | yes | yes | yes, via the shell extension | XWayland window; extension needs one log-out to load |
| Linux, GNOME/Wayland, no extension | yes | yes | yes | docks to the work area, stays up for the turn | still useful; brainrot doctor says so |
| Linux, other desktops | yes | yes | yes | work-area docking only | the X11 half is generic EWMH; only the compositor half is GNOME-specific |
| macOS | plain window | no | no | no | scenes, hooks and shoot all work; no overlay backend |
| Headless / CI | offscreen | n/a | n/a | n/a | software rasteriser, no display needed |
Verified on Windows 11 and on Ubuntu 26.04 / GNOME Shell 50.1 (Wayland, 200% scaling) — both by measurement on the machine rather than by inspection.
How it looks like that
The renderer is raylib (a ~2 MB wheel); the models
are glTF files built by scripts committed to this repo (assets/src/*.py),
executed under headless Blender. Each asset bakes its lighting — sun, sky
bounce, ambient occlusion — into a grayscale texture, and keeps its colours in
flat named material zones. At runtime every pixel is just
baked light map × zone colour × distance fog
which is why the daemon can recolour a hoodie or a train livery per seed without any lighting maths, and why CI screenshots (rendered on a software rasteriser) match what your GPU shows.
The character is skin-rigged with run, jump and roll clips authored in the same scripts. The parkour blocks use vanilla Minecraft's actual face-shading constants (top 1.0, sides 0.8/0.6, bottom 0.5) baked into 16×16 pixel-art atlases — over a light level, not on their own, which is a distinction that cost this project a whole set of frames half again too dark.
Rebuilding the kit after editing a script (needs pip install bpy, dev-only):
python assets/build.py # everything
python assets/build.py character # one asset
python assets/preview.py character # render it for your eyeballs
python assets/measure.py # always, after any rebuild
Development
pip install -e ".[dev]"
pip install raylib-software --force-reinstall --no-deps # headless machines/CI
pytest
641 tests covering the show/hide state machine, the seeding guarantees, hook install/uninstall, the real shim end to end, a full daemon driven over UDP, pixel-identical determinism per seed, the real overlay window driven against a real X server, the compositor bridge's parsing and host resolution, and the generation invariants — the corridor that can never strand the runner, the single-oncoming-train rule and the live dodge, zero interpenetration over hours of simulated running, no parkour hop the flight solver cannot fly, self-overlap refusal, the altitude band, and every orb the parkour generator hangs being collected.
The 34 skips are platform suites: tests/test_overlay_win32.py only runs where
os.name is nt, tests/test_overlay_x11.py only where there is a display. A
green run on one platform therefore means less than the count suggests, which
is why CI runs the whole suite on both — 632 passing on each — and then
renders a frame of every scene on each and keeps them as artifacts: same seed,
same software rasteriser, and the frames come back pixel-identical between
the two platforms.
What CI does not cover is the overlay window surgery itself. Both platform suites need a real desktop — the Win32 one skips on a runner as soon as raylib reports no native window handle, and the X11 one needs a display. Those are verified by hand on real machines.
Beyond the suite, each scene has a probe that turns "it feels wrong" into numbers, and those numbers are the acceptance criteria:
python tools/tower_probe.py --runs 16 --blocks 340 # the design, and what survived placement
python tools/reentry_probe.py --runs 6 --blocks 260 # can a missed jump rejoin the course
python tools/landmark_probe.py --runs 6 --seconds 40 # is the landmark ever actually seen
python tools/bypass_probe.py --runs 6 --blocks 240 # can a level be walked without jumping
python tools/runner_probe.py --runs 12 --seconds 120 # safety, pacing, riding
python tools/parkour_probe.py --runs 24 # interpenetration, hops, dead air
python tools/frame_cost.py # every scene, one process, vsync off
Docs: docs/ARCHITECTURE.md for how the pieces fit and
why, docs/TOWER.md for the tower's design and acceptance
criteria, docs/RULES.md for what a level must satisfy,
docs/HOOKS.md for the hook layer.
License
MIT. Every model ships as output of a script in assets/src/ — no third-party
assets are included or fetched.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file claude_brainrot-0.3.0.tar.gz.
File metadata
- Download URL: claude_brainrot-0.3.0.tar.gz
- Upload date:
- Size: 3.0 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8f1c1ac42fb4b90e2ecc5db0f36c47ff787d5327a8843d24879258f6073fc8ed
|
|
| MD5 |
8030548b3761a1a456d107fb4685d0a3
|
|
| BLAKE2b-256 |
f9795aca5da48a9b046cc9d94baf9fe7c0a281246508c3e9852a1f56e7a4f883
|
Provenance
The following attestation bundles were made for claude_brainrot-0.3.0.tar.gz:
Publisher:
release.yml on tristanmuzzu/claude-brainrot
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
claude_brainrot-0.3.0.tar.gz -
Subject digest:
8f1c1ac42fb4b90e2ecc5db0f36c47ff787d5327a8843d24879258f6073fc8ed - Sigstore transparency entry: 2485918610
- Sigstore integration time:
-
Permalink:
tristanmuzzu/claude-brainrot@66b5fdba0dfb4f6fa1e23fbf6e7c379515bafc40 -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/tristanmuzzu
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@66b5fdba0dfb4f6fa1e23fbf6e7c379515bafc40 -
Trigger Event:
push
-
Statement type:
File details
Details for the file claude_brainrot-0.3.0-py3-none-any.whl.
File metadata
- Download URL: claude_brainrot-0.3.0-py3-none-any.whl
- Upload date:
- Size: 3.0 MB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f0e69b9ff0c9cdfab1ecbfa789a46f5d5e99e74b1060e0f60b5c35c61abb0904
|
|
| MD5 |
04b5230485e275bac05d7244b86ff44e
|
|
| BLAKE2b-256 |
e54a0f341ada48472812afc891ec4ef4d7f400fe7bd7ec239d6b5e9cda22cdc0
|
Provenance
The following attestation bundles were made for claude_brainrot-0.3.0-py3-none-any.whl:
Publisher:
release.yml on tristanmuzzu/claude-brainrot
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
claude_brainrot-0.3.0-py3-none-any.whl -
Subject digest:
f0e69b9ff0c9cdfab1ecbfa789a46f5d5e99e74b1060e0f60b5c35c61abb0904 - Sigstore transparency entry: 2485918632
- Sigstore integration time:
-
Permalink:
tristanmuzzu/claude-brainrot@66b5fdba0dfb4f6fa1e23fbf6e7c379515bafc40 -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/tristanmuzzu
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@66b5fdba0dfb4f6fa1e23fbf6e7c379515bafc40 -
Trigger Event:
push
-
Statement type: