Putt Panic · solo build log

Putt Panic:
a 2013 Flash idea that finally shipped.

I had the idea in 2013, in ActionScript for Flash: sliding tiles you push together to build the hole. I built it over Christmas 2025, scrapped that, started again in April, and shipped it — a mini-golf game with a level editor, a 108-hole campaign, multiplayer, and a 3D course I had to rebuild to fit on a phone.

2013first prototype
419commits
82days in this repo
1developer
Play the course
2013

The idea is from 2013.

ActionScript · Flash Player · 2013

Sliding tiles that build the hole.

I had this in ActionScript for Flash Player, in 2013. One mechanic: tiles you slide into place to build a course, and the course you build is the hole you play.

That's still the whole game. Every draggable tile and insert block in Putt Panic now comes from it.

Adobe killed Flash Player at the end of 2020, so I ended up building it on something else entirely — twice, as it turned out.

Slide a tile · finish the lane · play it

Before this repo

It took two runs at it.

The git history starts on 29 April 2026, but the work doesn't. I went at this properly over Christmas 2025, then scrapped it and started again in April.

A flat 2D top-down mini-golf board: green tiles, blue water, tan sand, arrow tiles marking which rows and columns slide.
December 2025 Flat 2D, and already called Putt Panic. Tiles slide, water and sand are in, there's a putter, a shot counter and a Build mode. The mechanic worked. It just looked like a puzzle game. Dec 30, 2025
A darker top-down prototype titled Row Slider Golf, with textured rows separated by wooden dividers, a tee, a cup and portal markers.
April 2026 Started over as "Row Slider Golf" — drag a whole row instead of nudging single tiles, and a Generate button for random courses. That generator is the one that ended up baking all 108 campaign holes. Apr 25, 2026
An L-shaped course on a blueprint grid, titled Putt Panic, in edit mode on hole 1 of 18, with draggable lane handles marked by double arrows.
Two days later Renamed, 18 holes, boards that aren't rectangles any more, and lanes you drag by a handle. Bumpers, sand and ice slide with the turf. Two days after this I made the first commit. Apr 27, 2026 · 2 days before the first commit
The look I was aiming forgenerated reference
A generated concept image of an isometric mini-golf course with cream rails, a dark wood skirt, grass tiles, ponds and flags.
What it actually shipped asJuly 2026 build
The shipped game rendered in the same isometric style: cream rails, wood skirt, textured turf, bunkers and a flag.
I made the picture before I could build it, then spent months closing the gap — the cream rails, the wood skirt under them, the tile seams, the pond shapes.
The game now

Same mechanic, running now.

Both clips are the real game, captured frame by frame off the build.

Build the hole Blocks of tiles swap along a shared axis, bunker and lane included. You spend swaps to open a route from the tee to the cup. Build phase · insert-block swap
Then play it One stroke, off the rail, down the lane you just opened. Same physics loop as the first commit. Play phase · one stroke
Commits

June was most of it.

June is when it stopped being a sandbox and turned into something I could ship.

Commits per month

Apr 29 – Jul 20, 2026
7APR
34MAY
283JUN
95JUL

Busiest day was June 30 — 50 commits, the day I tore the renderer out and rewrote it. That one's below.


The build

How it got built.

In the order it actually happened, with what went wrong at each step.

1
Apr 29 · initial commit

The first pond

First commit already had water, walls, sand, and tiles that slide — not a standing start, I'd built most of it twice by then. No menu, no title screen, no UI, just a playable board in a browser tab. Everything else got built around that physics loop.

phasercustom physicstile editor
2
Early May

Making it a phone game

May was the boring part: orientation lock, iOS scaling, a real HUD, icons, a splash screen, a first-run tutorial. The game is forced landscape, so every drag goes through a coordinate remap. Nobody notices that unless it's broken.

capacitorforce-landscapeonboarding
3
May

The editor, and multiplayer

The level editor and multiplayer landed close together. Free-play doubles as the obstacle showcase, so I only maintain one of them. Multiplayer runs on Firebase, and shaped courses have to ship their whole board mask or the other phone rebuilds the wrong hole.

firebase roomsdraggable tilesboard masks
4
June

108 generated holes

I didn't draw the campaign. It's 108 generated courses baked to JSON — families that cycle, signature holes at 9 and 18, par medals, and a bench of rival scores to chase. The generator is unit-tested, and I re-baked all 108 enough times that "re-bake campaign" is its own genre in my git log.

procedural generationvitestmedals + rivals
5
June

The renderer I threw away

The courses render in 3D. On desktop, running Three.js in a second WebGL context next to Phaser was fine. On a 3GB iPhone it killed the app — white screen mid-render. It wasn't device RAM. It was WebKit's per-process GPU ceiling, which you can't buy your way past.

A smaller texture wasn't going to fix it, so I took Three.js out of the runtime. On June 30, in 50 commits, I replaced it with a Canvas2D shell that draws the board inside Phaser's one GL context off a baked projection. Three still bakes the look offline. The phone never pays for a second renderer.

three.js → canvas2dgpu budgetbaked projection
6
July 1

32 commits on a wooden rail

Getting the wooden edge rails right took 32 commits in one day: cream vs ivory, oak grain, square butt joints instead of a 45° mitre, flush corners, the skirt on diagonal edges, the shadow in the corner. It's one detail. It's also what stops the board looking like a prototype.

it's always the cornerswall lab
7
July

The menu reads the clock

The menu sky runs off the actual time of day — dawn through dusk on one deterministic clock, with its own light arc per campaign cup. The windmills run on that same clock, so they're timing gates now instead of decoration.

time-of-daydeterministic clockambient fx
8
July

Getting it out

The last stretch was batches: a rebuilt results card, per-hole leaderboards, a redesigned obstacle guide, Speed Golf, wind on free-play and campaign courses, and an IAP layer stubbed for RevenueCat. The Shop stays hidden until real purchases work.

testflightleaderboardsiap (staged)

Bugs

Five bugs, and the notes I kept.

The ones where the symptom and the cause were nowhere near each other.

white screen · iphone xr

The XR white screen

Courses white-screened on iPhone XR — no JS error, nothing in the console, because iOS was killing the whole WebContent process. idevicesyslog showed the OOM. The ceiling is WebKit's per-process GPU budget, and it varies by device — so I wrote a native DeviceMemory probe to read the actual RAM and tier the 3D budget per device, then cut peak allocation until the XR held.

phaser 4 regression

Masks that quietly no-op

Geometry masks silently stopped working on WebGL in Phaser 4. setMask just did nothing, so I rebuilt shaped ramps as a silhouette plus fill strips.

rendering · context loss

Two GL contexts, one phone

The course — rails included — rendered through Three.js in a second WebGL context beside Phaser's. 3GB iPhones couldn't hold both: the second context kept getting killed, taking the board with it. That's what forced the rewrite — draw everything in Phaser's one GL context and never pay for a second renderer.

stale payload

The menu kept reopening Speed Golf

Backing out of multiplayer would drop me straight into the Speed Golf hole browser. Returning to the menu passed Phaser undefined for the no-options case — and Phaser only replaces a scene's stored data when you hand it something truthy. So the previous launch's payload just stayed put: one trip back from a Speed Golf run stamped {openSpeedGolf} onto the menu, and every visit after that re-delivered it. Always pass a concrete object.

board input

Touch a tile, lose the board

Tapping a tile made every other movable block disappear. The guide layer hid all non-selected blocks on select or hover, so a single touch emptied the board of the pieces you were supposed to be sliding. They stay drawn in an idle style now.

process

115 notes

I kept 115 notes as I went, one fact each, so I didn't lose two days to the same wall twice. Most of this page came out of them.


2023 → 2026

Then, and now.

Long before either of the builds above, there was this. March 2023: a stock photo of a golf course, a clip-art driver, and a ball the size of a grapefruit — served from 127.0.0.1:5173. It still runs on that same port today.

March 2023the first swing
A photo, a ball, and a club No tiles, no board — just a club, a ball, someone else's photograph, and a losing fight with a physics engine. The swing worked; the hit wouldn't register. I was literally pulling the physics apart piece by piece trying to get the club to touch the ball, and it never quite did. (The shipped game runs physics I wrote myself — this clip is why.) The checkerboard is where the sky hadn't been drawn yet. Filmed off the laptop with a phone. Mar 6, 2023 · 127.0.0.1:5173
July 2026the finished round
The shipped game's campaign result card: a party-cat photo card reading BIRDIE, a three-star rating, strokes, par and to-par tiles, and a rival scoreboard through 9 of 18 holes with the player in fifth.
A birdie, and four rivals ahead of you Two strokes on a par 3, three stars, and a rival board that reshuffles every hole — each one playing to its own temperament. You're fifth through nine with some ground to make up. One screen out of dozens, and the sky is drawn now. Jul 2026 · shipped build · 127.0.0.1:5173
Where it landed

From a 2013 Flash idea to something on the App Store.

Putt Panic has a level editor, a 108-hole campaign, multiplayer, time-of-day skies, and a 3D course I rebuilt to fit inside a mobile GPU budget. I built all of it myself, start to TestFlight.

13years, idea to ship
419commits
50on the biggest day
108campaign holes
115engineering notes
1renderer retired