Hey, it's me, josh - as a memoji

josh.miami

I built a Greek pottery roguelite with Claude Code in a weekend

Josh Echeverri
Josh Echeverri
5 min read

The premise was a trap I set for myself.

Sisyphus as a roguelite. The myth is already the genre: attempt, struggle, failure, reset, knowledge, attempt again. The boulder always comes back down. So the game can never be about beating the mountain. It has to be about what you carry between attempts.

I had a giant design document. Gods as strategic actors, adaptive difficulty, persistent boulder modification. Claude's first real contribution was refusing to build any of it. The doc cited Balatro but described Getting Over It, and those are opposite games. One is a deck you build. The other is a hill you white-knuckle. A prototype can only test one.

We built the white-knuckle version first. It had grip physics, stamina, a brace mechanic, a dive. I played it for about four minutes before saying what I actually wanted: hold a button, let go when the run is lost, and make everything interesting happen in the modifiers. A deck builder wearing a boulder.

The pivot deleted half the codebase in an afternoon. Grip height, stamina, dive, all of it. What replaced it is the whole game now: Sisyphus pushes on his own, you get two gestures. Tap to heave, a burst of strength on limited charges. Hold to let go. Every run ends with the gods offering a blessing, and blessings stack forever.

One number makes it a game. Baseline push force is 2250 newtons. Gravity along the final pitch is about 2650. The empty build stalls at 52% and slides back, every time. There is even a unit test asserting the game cannot beat itself. If tuning ever lets the boonless run out-push the mountain, the suite fails with a message that says so.

The mountain was invisible for three hours

The terrain generated, the collider worked, and the boulder rolled uphill on ground that did not render. No error, no warning, not even magenta.

Claude tried three fixes. Brightened the color. Reversed the triangle winding. Disabled backface culling. All three were reasonable and none of them worked.

The actual cause: Unity's URP 2D Renderer only draws shaders that declare a Universal2D pass. The standard Unlit shader does not have one, so the whole mesh was silently discarded before rasterization every frame. The fix was using the sprite shader for generated geometry and carrying color in vertex data. It is written into the repo's CLAUDE.md now, in a section literally called "Traps that have already cost time."

All three wrong fixes came from reasoning. The one that worked came from reading the shader's actual pass list, which took five seconds and would have saved three hours.

Programmer art was the art direction all along

The first visual pass looked like every physics prototype since the beginning of time. Gray slope, tan capsule. A friend tested it and said the figure looked like a potato.

Then I looked at an actual amphora. Greek black-figure pottery: flat dark silhouettes with incised line work on a clay ground. Which is exactly what a procedural sprite renderer produces when you have no artist.

The title screen

The whole reskin is palette, one OFL display font drawn from vase lettering, and two ornaments rasterized in code. A Greek key meander for structure. A laurel for honor, drawn only where the game acknowledges what you did. The boulder became the shield off an amphora at the Met, black disc, cream spokes, red rim. The mountain is the glaze band the figures stand on.

Mid-climb

Zero art assets. Two font files. Everything else is code.

The music never repeats because the task never ends

Balatro has one loop that survives two hundred hours. I looked into how it actually works: five arrangements of one composition in 7/4, played at 70% speed, crossfading invisibly.

I could not commission that on a Saturday. So the score is generative instead. Karplus-Strong synthesis: noise fed through a delay line until it rings like a plucked string. A plucked string is a lyre. The sequencer walks a lament bass, the descending line Baroque composers used for grief, under lyre notes harmonized to the current bar. Density follows altitude. When you summit, everything drops out except the bass, walking down with the stone.

The music never repeats exactly and never ends. For this game, that is the point.

The counter said 100% and then took it back

A tester crested the final pitch, watched the counter say 100%, and watched the boulder roll back down anyway. The altitude display rounded up half a percent before the summit trigger fired. The game told him he won and then robbed him.

The fix is one line: any altitude that rounds to 100 is the summit. The display's promise is binding.

Reaching 100%

That rule came from a design principle we kept returning to. The game is about an unwinnable task, so it must never be cruel about the parts you can win. Arrival is acknowledged instantly. Nothing can fail after 100%. You stand at the top while the stone rolls back down past you, and the counter falls with it.

What I actually shipped

A playable build on my iPhone, served over a Cloudflare tunnel from my laptop. A title screen with lifetime stats in localStorage. A tutorial written as a pottery scroll. A blessing gallery in the style of Balatro's joker collection. Sixteen unit tests, one of which encodes the game's central design constraint. And a pause menu, because a friend asked where the pause menu was, and he was right.

The boulder still comes back down.

Related Posts

I built a Pokemon game inside Claude Code's statusline

What started as 'can I put a Pokemon in the status bar' turned into a full companion system with ANSI sprites, evolution chains, and a Pokedex. All in one session.

TokenGolf hit v1.0 because the two-mode system was the wrong abstraction

The wizard asked you to commit a budget before you knew what the session needed. Par replaced that question with a better one: how efficient were you given what actually happened?

What I shipped today in TokenGolf v0.4.0

Expansion and hardening at the same time. More achievements, more hooks, more structure, and the release infrastructure to keep the rules from drifting.