Kickturn is a 3D skateboarding game for the Playdate: a 168 MHz handheld with no GPU and a 400 by 240 one-bit screen. You push, steer and grind with the buttons, and every trick lives on the crank. It runs at 30 fps on the device, and it's my entry for 1-BIT JAM 15.
On Friday it was an idea. On Monday it was a jam game. This post is how it got there: what the agents built, how every round was checked, what the hardware taught me, and a few general things I'd tell anyone starting a Playdate game. The rule is the same as in my last post: I decide, agents build and prove it.
Play it: Kickturn on itch.io, a download for Playdate. Voting for 1-BIT JAM 15 runs until 6 November 2026.
A trick lives on the crank
A pushes, B brakes or powerslides, the D-pad steers. To pop, you wind the crank back and snap it forward. The D-pad direction you hold during the wind picks the flip: left is a kickflip, up-left a varial kickflip. Keep snapping past 220 degrees and you get the big variant, which for up-left is a 360 flip. In the air, turning the crank spins the body, and a D-pad press after take-off is a grab. Land on a rail with a direction held and it picks the grind.
For now there's one park, 29 m across: a kicker, a ledge, rails, six stairs with a handrail and a hubba, a gap, a manual pad, a bank and three quarter pipes. The skater has 8 flips plus 6 big variants, 4 grabs, 9 grinds and slides, manuals and quarter pipe airs.
Everything has a price

The jam theme was Everything Has a Price, so in Kickturn the price is time. Score Attack gives you 90 seconds. Every trick you add to a combo costs a second, a bail costs five, and landing a combo of three or more tricks pays three back. Chain big lines to stay alive, or play it safe and run out of time.
The whole game was made inside the jam window, from the first commit on Friday night to the submission on Monday. The timed mode was the last round, on Monday, reviewed by a separate model like every round before it.
Three days, 289 commits

On Friday evening I wrote down the concept and the rules: what the game is, the controls, the hard device limits, and which calls stay mine. The first commit landed at 23:41.
On Saturday a Grok Bot on its own Linux build box built rounds 1 to 9, and separate agents judged each one. By the evening I'd raised the pass bar to 9 out of 10 on every judge track.
On Sunday, Claude Code took over on my Mac for round 10. That's when the renderer and the skeleton rig moved to C, and it's where the device numbers below come from: builds 9 to 14, measured on the Playdate itself.
What stayed mine: tricks, the park layout, the skater's look, the controls, trick timing and names. Anything an agent proposes in those areas stays a labelled proposal until I've played it.
Every round: build, prove, judge, play

Each round starts from the judges' numbered list of open items. One item becomes one small commit, with the before and after numbers in its message. Before a fix, the builder writes a test that fails on the old commit. Tasks stay around 40 minutes: two long sessions were cut off mid-change early on, and small pushed steps lose nothing.
The gate is one script. It replays 57 scripted scenarios headless in the Simulator, diffs 251 key frames against their baselines, and checks budgets and even the docs: a number in the status file that drifts from the code fails the gate.
The judges are separate agents that didn't write the code. They work read-only on the commit, run the gate themselves and make their own captures; the builder's screenshots aren't evidence. They score six tracks: visuals, feel and controls, trick depth, UX and HUD, polish, and tech on the device. Every number in a report carries its commit, tool and file. Without a run on real hardware, the tech score is capped at 7.
Measure on the device

The Simulator draws a frame in under a millisecond on average. It can't tell you which of two changes is cheaper. Only the device can.
When round 10 started, full quality ran at 17 fps on the Playdate: 59.1 ms a frame against a 33.3 ms budget. Fast quality took 37.6 ms. At build 14, full quality does 27.1 ms of work and fast 23.2 ms, both measured over the same 70 second scripted ride. Then the frame waits for the 30 fps tick. 2 to 3% of frames still land a little late. That's frame pacing, not the CPU, and it's next on the list.
No depth buffer for the park

Rounds 1 to 9 used Mini3D+, a good open renderer. On this game its biggest cost was a full-screen depth buffer: clearing it took 5.4 ms every frame, before anything was drawn.
The renderer we wrote in C for round 10 skips that. The park never moves, so a BSP tree built at load draws it back to front, and it needs no depth buffer at all. Only the skater, the board and the shadow get depth, in a small box around them. That box is filled in 7 KB bands on the stack, which lives in fast on-chip RAM. The whole box, 36 to 56 KB, never fit the 16 KB cache.
Count cache lines, not multiplies

The most useful number of the round came from a tiny memory bench. On this chip, a read that misses the cache costs about 1.3 µs. A read in order costs about 0.07 µs per 32-byte line. So a loop costs the cache lines it touches, not its arithmetic. Projecting 1,532 vertices into six separate arrays took 6.3 ms. The rule since: one array of structs, laid out in the order it's read.
Lua pays the same way, because every table access is a scattered read. That's why the skeleton rig moved to C as well. Its Lua side went from 8k to 14k VM instructions a frame to about 70: one call that hands over the skater's state. On the host, milliseconds were too noisy to rank changes, so the tests count Lua instructions instead, with a bound for every state.
A frame at full quality now splits into 18.5 ms of rendering in C, 7.1 ms of Lua for the game, camera and audio, 1.5 ms of HUD, and 6.2 ms free.
Two quality modes, both 30 fps

Full quality draws the ground shading, the skyline and the graffiti, and sees 42 m. Fast drops those and sees 26 m. Both run at 30 fps on the device, with 27.1 and 23.2 ms of work.
What Playdate taught me

General things I'd tell anyone starting a Playdate game, 3D or not.
- Measure on hardware. A compiled device build isn't a device test, and Simulator speed isn't device speed. Frame pacing and crank feel need a real Playdate.
- Budget 3 to 4 ms for the display. It comes on top of your work, so 30 fps means keeping the work under about 30 ms.
- Start in Lua, move measured hotspots to C. Lua is great for game rules. In one test, an optimized Lua mesh renderer averaged 13.7 fps on the device. Moving only the drawing to C, with the gameplay left in Lua, is the pattern that works.
- Read the crank once a frame.
getCrankChange()consumes the change, so two systems reading it fight. Keep one owner for input, and use relative rotation so nothing snaps when a scene starts. - Mind the stack. The SDK warns past 8 KB per function. A 32 KB array in a Lua C callback hung my Playdate until a manual reboot.
- Floats are 32-bit. Lua on the device uses 32-bit floats, desktop Lua uses doubles. When you port Lua to C, compare float with float: doubles can put a point on the other side of an edge.
- Declare your table fields. A Lua table that gets new fields mid-run rehashes, and that's garbage. Declare every field up front, keep hot code flat, and gate the garbage per frame. Kickturn's average is 0.014 KB a frame.
- Make it replayable. Scripted input gives you identical runs, and identical runs give you screenshot baselines an agent can diff.
- On a Mac, check your tools. The SDK's bundled ARM compiler is an Intel binary on Apple silicon, so install the arm64 toolchain. Homebrew's
luais 5.5, while the Playdate runs 5.4. App Nap throttles the Simulator in the background (one replay took 545 s instead of 149 s), and the Simulator grabs a connected Playdate's serial port.pdutil3.1.2 couldn't install builds for me, so installs go through data disk mode. - 3D is open territory. Open renderers exist, like Mini3D+ and playdators, but a research pass found no 3D level editor, no rigged animation workflow and no 3D physics for Playdate. Panic's 3D collection lists 12 games.
- Write down where everything comes from. A credits file lists every asset, what made it and its license, plus an AI disclosure that says what the agents did.
What's next
Jam voting runs until 6 November. After that it's back to the feel and the late frames. It stays one park until the feel is right; more parks come after that.
Three things to take home
Measure on the device from day one. The Simulator tells you whether it works, not how fast.
Judges who didn't write the code. Their own evidence, the same rubric every round, and no score without a device run.
Count memory before math. On this chip, one cache miss costs about as much as reading 18 lines in order.
Have you shipped 3D on a Playdate? I'd like to compare notes.
Tools and thanks
- Playdate SDK by Panic, and the 3D collection for proof it can be done.
- Mini3D+, which ran rounds 1 to 9. Our renderer ports its raster rules, dither table and lighting formula.
- T3 Code, my orchestrator. Thanks to Theo and Julius.
- 1-BIT JAM 15, hosted by MYline media, for the theme and the deadline.
- Claude Code and Grok, the builders and judges.
- Theo's video on running Claude for weeks, which got me to try this.
Aleksandr Nariadchikov · @sashanaria

