Are You Certifiable?

Building Bitcoin Runner

Bitcoin Runner is a 2D auto-runner with a fixed-length, score-attack structure — not an endless runner, since there's no fail state to run out the clock against and every level is a finite, named dataset with a real finish line. The terrain isn't procedurally generated — it's real Bitcoin price history. You auto-run across a pixel-art Mountain Range shaped by actual daily closing prices, and the only input you have is a bet: jump Up ("Buy") or Down ("Sell") to predict whether the market is about to rise or fall before you land. Guess right and you land safely with a leveraged gain; guess wrong and you land safely too, just poorer — there's no fail state, no death. The game never punishes you for playing, only for guessing wrong.

This is a design post-mortem, written mid-development rather than after the fact. The game is live at bitcoin.certifiable.media and still very much in progress — twelve levels exist, each mapped to a real, named era of Bitcoin's price history (Bull Run 2017, the Covid Crash, crypto winter, and so on), but several are still running placeholder terrain art. Consider this an invitation to poke at the rough edges as much as a description of what's finished.

By the numbers so far: 114 tracked issues (89 closed, 23 open), 138 commits, built across roughly ten days (2026-08-12 to 2026-08-21).

The core idea

The mechanic is simple on purpose: press Up or Down, land, find out if you were right. What makes it more than a coin flip is that every jump is a real leveraged trade against real price data. The position itself is denominated in Bitcoin, not dollars: jumping converts a flat $10,000 into however much BTC that actually buys at the exact historical price the instant you take off — a lot of BTC during a crash-era level where price is low, a sliver of one during a bull-run level where price is high — then holds that BTC quantity fixed until you land. The dollar figure you see the whole time is a live conversion of that real BTC holding back to USD at whatever the in-game historical price is at that moment, the same math a real position's mark-to-market value would use. Landing correctly closes it at a profit (amplified by your current Leverage), landing wrong closes it at a loss. Leverage steps up by 1 on every correct guess with no ceiling — a hot streak can compound into an enormous position — and steps down on every miss, floored at 1x so a bad run can't zero out your exposure. Dice pickups scattered across the terrain multiply Leverage outright on a 1-6 roll, a much bigger and riskier swing than the steady per-landing step.

The Wallet is a genuine dollar profit-and-loss balance, not an abstract score. It can go, and is meant to go, deeply negative on a bad run — High Scores tracks separate "High" and "Low" leaderboards per level for exactly that reason, because a spectacularly bad run is still a story worth keeping. Losses are deliberately unbounded, the same as gains: there's no floor on how negative the Wallet can go, and no auto-liquidation that closes out a losing position early. Real leveraged trading doesn't work that way — margin calls and liquidations exist precisely to stop a loss before it goes unbounded — but this isn't real trading, and letting a bad streak crater all the way down is a lot more fun to watch (and recover from) than getting stopped out quietly.

Every run is a fixed 90 seconds regardless of level, but each level has its own real difficulty curve: scroll speed and jump duration both scale up to roughly 2.7x from level 1 to level 12. The trickiest piece of the whole design was making a run's pacing exactly match what the level-select screen promises — every level shows its entire real date range on the price chart before you play it, and every run needs to actually traverse that whole range in exactly 90 seconds, at a speed that's also a genuine difficulty knob. Those two constraints fight each other, and getting them to agree took two prior designs that each solved half the problem before landing on the current one (see Lessons, below).

Why real price data

The terrain isn't just "wavy hills that look nice" — it's literally Bitcoin's own price chart, redrawn as a mountain range you run across. Bull Run 2017's climb to ~$19.8k and subsequent crash is a genuinely thrilling shape to traverse; the Covid Crash's near-vertical drop is a genuinely stomach-dropping one. That authenticity was the whole point from the first brainstorm — a generic procedural terrain generator would have been easier to build and much less interesting to play.

Twelve non-overlapping, real, named eras were selected from the full historical series (2011-08-22 through present), each with its own pixel-art color theme and mood (molten lava for a crash, blizzard snow for crypto winter, neon for a speculative bull run). That data-to-visual mapping — deciding where an era starts and ends, how price normalizes to screen height, and how much of each range fits into a 90-second run — went through more revisions than any other single system in the game.

Biggest lessons learned

A dataset can be technically correct and still feel wrong. The first scoring model paid out raw price-delta times multiplier. It felt fine on paper and unfair in practice: a level where Bitcoin traded at $5 could never produce swings as dramatic, in absolute dollar terms, as a level trading at $60,000, even for an identical percentage move. The fix was reframing every trade as a fixed $10,000 notional position — same dollar risk on every jump, on every level, regardless of the era's price magnitude — so the game's difficulty curve is about timing and leverage, not accidents of Bitcoin's own price history.

Solving two constraints separately doesn't work when they're actually coupled. Getting a level's real difficulty-curve speed to coexist with "every run shows the whole dataset in exactly 90 seconds" took three attempts: first, ending a run whenever the data ran out (broke the fixed-duration promise); then, looping the terrain to fill fixed time (lost the sense that you were traversing a real, finite history); finally, solving for horizontal point-spacing per level so speed stays an independent difficulty knob and the terrain simply compresses or stretches to fit — both constraints satisfied at once, by construction rather than by fiat.

Desktop testing can hide GPU limits that mobile devices enforce. One character's spritesheet was built as a single flat strip 14,740 pixels wide. It rendered perfectly everywhere it was tested — because every desktop GPU used for testing supports textures far past that width. It rendered as a solid black box on the one real Android phone it was actually tried on, because mobile GPUs enforce a much lower maximum texture size. The fix was reflowing the sheet into a grid instead of a strip; the real lesson was that "it works on my machine" means almost nothing for GPU-level constraints, and the fix shipped only after being confirmed on the actual device that reported the bug.

An AI-generated asset doesn't automatically share scale, color, or fitness-for-purpose with another AI-generated asset, even from the same prompt family. Level 9's terrain needed a wooden bridge motif to sit convincingly over a snow-buried village. Two separate close-up generations of "a bridge" and a separate wide shot of "a village" turned out to have no common real-world scale between them — nothing tying their pixel-per-meter together — which only became obvious once a candidate was pasted in at literal 1:1 scale with zero resizing. The eventual fix was hand-drawing the bridge tile with code instead of generating it, anchored explicitly to the same price-line data the rest of the terrain already respects.

Licenses can quietly veto a design decision made in good faith. The original audio plan named Strudel.cc based on how it was described in the project's own tooling docs. Implementing it surfaced that Strudel is AGPL-3.0 — a real conflict for a private repository, not a style preference — caught only once actual implementation started. The pivot to hand-written Web Audio API oscillators cost an afternoon; not catching it before shipping would have cost considerably more.

Generated art that looks great at native resolution can fail completely at the size it's actually used. Replacing the game's stock favicon seemed like the smallest possible task — right up until a photorealistic 3D coin render, however nicely lit at full size, turned to mud the moment it was scaled down to a 16-pixel browser tab icon, no matter how it was resized, sharpened, or hand-pixeled from scratch afterward. The asset that finally worked wasn't a new generation at all — it was the game's own already-shipped, already-approved in-game Bitcoin pickup sprite, simply recolored to true Bitcoin orange and shipped at a size the browser could scale on its own. The working answer was already sitting in the project; the miss was reaching for something new before checking what already existed and was already good.

Claude can't reliably judge whether an image is actually good. Across dozens of generated sprites, terrain textures, and backgrounds, the recurring failure mode wasn't generating something broken — it was Claude looking at a technically-valid result (right dimensions, right palette, no obvious glitches) and judging it "done" when it wasn't actually fit for purpose: a pixel sprite that didn't read clearly at gameplay scale, a terrain texture that looked fine in isolation but wrong once tiled, art that was simply off in a way a human eye catches instantly and an AI's own self-assessment doesn't. The fix wasn't a better prompt or a smarter check — it was a workflow change: every image-generation step now requires an explicit human review before the result is used, rather than trusting the model's own judgment that an image looks right.

What's next

The single largest piece of unfinished work is real terrain art for six of the twelve levels (Frozen Peaks, Industrial Wasteland, Neon Metropolis, Solar Flare, To the Moon, and House of Cards/Lincoln Memorial) — they're playable today on placeholder art while the same real, price-data-fitted pipeline used for the finished levels gets rolled out to each.

Past that, the next real design frontier is a "Player-driven exploration" epic: reworking scoring around live, continuously-updating delta pricing rather than only discrete landings, building out a full achievements/stars/unlockable-character system (one achievement — 1000x Leverage — is live today; a "Pro Trader" achievement for a run of ten consecutive good trades is designed but not built), and deciding what triggers meaningful in-run feedback beyond the current landing-by-landing verdict.

A handful of known visual bugs are queued too — a couple of levels' character-select backgrounds and near-parallax layers don't quite fit their panels yet.

If you're playtesting: the run is 90 seconds, there's no way to lose, and the interesting failure mode is getting greedy with Leverage right before a level's biggest swing goes the wrong way. Try it, break it, and tell us what felt wrong.

The stack, for anyone curious

-- Claude