paintbucket.dev

Selected work

I build and ship complete products — interface, systems, backend and release.

Software & systems engineer

Thirty-three projects built, fifteen of them shipped: applications on three storefronts, real-time multiplayer running on free infrastructure, and a dozen browser projects that cost nothing to operate. The work below is chosen for the engineering behind it rather than the screenshots — generated content that has to stay deterministic across machines, simulation that has to stay solvable, and multiplayer that had to fit inside a free tier.

0
projects built
0
shipped
0
storefronts
$0
$/mo to run the web projects
Scroll

Sizzle reels

Three titles, cut to the beat.

Captured from the shipping builds and cut against each track's own tempo map, so the on-screen events land on the beat. Drag the handsets; tap the front one for sound.

GeoPinQuest

Spin. Pin. Win.Reel 1 of 3 · 1:03

01 Shipped · Steam, App Store, Google Play

GeoPinQuest

Half a million places, inside a mobile download.

A 3D-globe geography game in Godot 4: spin the Earth and tap a country, pin a city under a bullseye, match a flag, or call higher-or-lower on a population. Five game types crossed with timed/untimed and fixed-round/endless gives twenty mode variants off one rule engine rather than twenty screens.

The constraint that shaped it was data. 251 countries come from a real GeoJSON boundary set, and roughly half a million localities from per-country NDJSON — far more than belongs in a phone's memory. The gazetteer ships gzip-compressed and is decompressed per country on demand, so the download stays reasonable and only the continent you are playing is ever resident.

251
countries
~500k
localities
20
mode variants
  • Godot 4
  • GDScript
  • GeoJSON
  • Steamworks
  • iOS
  • Android
Gameplay capture
Gameplay capture

02 Mobile · releasing on Google Play and the App Store

PopBlocks3D

A match puzzle with nothing holding the board up.

If the level starts as a cube, the cube exists only because several hundred coloured pieces are arranged into one. There is no frame behind them and no backing plate. Every piece cleared is gone from the solid for good, so the shape sags, opens into tunnels, loses corners, and when the last piece goes there is nothing left behind it.

That removes the assumption most match puzzles are built on: a fixed grid to clear against. Adjacency becomes a 3D neighbour query over an irregular solid that changes every move and that the player is also rotating — and the camera still has to keep a legible read on it.

120+
generated levels
12
regions
0
pieces left at the end
  • Godot 4
  • GDScript
  • Procedural generation
  • Headless test harness
  • Android
Releasing soon

03 Mobile · releasing on Google Play and the App Store

PickupStix3D

A physics pile where losing is emergent, not scripted.

A heap of coloured sticks dropped on a round table — an actual rigid-body pile, not a picture of one. Orbit it with a drag, work out which stick is genuinely free, and pull it into a matching bucket, or park it in a four-slot rack and gamble that the right bucket comes round. Pull the wrong one and the rest start rolling, sometimes off the table entirely.

Nothing about the failure state is authored. You lose when a stick has nowhere left to go, which is a consequence of how the pile happened to settle and what you have already taken out of it. That is the appeal, and it is also the problem: a level that physics settles differently every time cannot be hand-verified.

120
levels
4
rack slots
1 in 5
is a bonus round
  • Godot 4.6
  • GDScript
  • Rigid-body physics
  • Headless test harness
  • iOS
  • Android
Releasing soon
Gameplay capture
genoba.paintbucket.dev
Gameplay capture

04 Browser · generative multiplayer

GENOBA

The roster is the dictionary, and it costs nothing to run.

A three-lane battler where you summon by typing. Any word becomes a unit with its own stats and its own generated pixel art, and power runs from a worm at 1 through a dragon at 750 up to things that answer prayers. Around 47,000 WordNet-derived nouns are understood; anything unseen goes to an AI oracle that works out what it ought to be.

The hard requirement was zero running cost: no match server, no sign-in, AI only on a free tier with hard daily caps. That ruled out a shared server catalogue, so the catalogue is deterministic instead — text compiles into an immutable unit definition, and identical text produces an identical unit and identical art on every machine. Two players get the same quokka warlord without anything being synchronised.

~47k
nouns understood
21
duel maps
1–5
players
  • Godot 4.6
  • Cloudflare Workers
  • Durable Objects
  • Workers AI
  • WordNet
  • GitHub Pages
balloon-pop.paintbucket.dev
Balloon Pop screenshot
Screenshot

05 Browser · real-time multiplayer

Balloon Pop

Real-time multiplayer with no server bill.

One balloon, a capacity nobody is told, and the growing suspicion that you should have stopped a second ago. Players hold to inflate, rates sum while several hold at once, and whoever is still holding when it bursts loses.

It existed to answer a specific question: how little infrastructure does real-time multiplayer actually need? The answer was a Durable Object per room holding the WebSockets and the authoritative clock, plus a static SPA on GitHub Pages — two independent deploys whose only coupling is one build-time URL, both inside free tiers.

2–10
players per room
~0.3s
to test the rules
$0
to operate
  • TypeScript
  • Cloudflare Workers
  • Durable Objects
  • WebSockets
  • React
  • Vitest
paper-plane.paintbucket.dev
Gameplay capture

06 Browser · simulation

Paper Plane

Fold it yourself, and the fold is what flies.

You get a flat sheet, three tools, and a tower in an empty hangar. Draw creases freehand, pick a face up by an edge and drag it over, then throw what you built and watch it go.

The paper is a tree of flat faces joined by hinges: creasing partitions a face, folding rotates one face about one hinge, and nothing is ever mirrored, so a face is exactly where it looks like it is. Either side of a crease can be the side that moves, because the tree is re-rooted into the half that should stay still — which is what makes drawing on a flap standing up at 70° work at all.

3
folding tools
0
scripted flight paths
∞
foldable designs
  • TypeScript
  • Three.js
  • Computational geometry
  • Rigid-body aerodynamics
  • Vite
boardgame-roguelite.paintbucket.dev
Gameplay capture

07 Browser · action roguelite

Boardgame Roguelite

Five heroes that play like five different games.

Tabletop archetypes rebuilt as action heroes climbing a tower: three waves and a boss per floor, a reward after each, and a run that lasts the better part of an hour. The chess champion fires down ranks and files and cares where everything stands; the tycoon weaponises his bank balance; the general arrives alone and leaves with an army; the tinkerer builds machines that set off other machines.

The design rule was that a hero had to change how the game is played, not which numbers are larger. Every boss chest rewrites an ability rather than scaling it, so by the top floor the rules have stopped applying to you in a way specific to the character you picked.

5
heroes
35–60
minutes per run
3+1
waves and a boss per floor
  • TypeScript
  • Three.js
  • Headless simulation
  • Automated balance probe
  • Playwright
bug-bucket.paintbucket.dev
Gameplay capture

08 Browser · open world

Bugs to Bucket

Catching them is the easy part.

A fenced 240-metre plot on a sunny afternoon: a pond you can swim, a cliff you can stand on, a cave at its foot, a corn field and a forest. Twenty-eight species live where they ought to — skaters on the water, cicadas up the trunks, something glowing in the dark.

The inversion is the design. Swinging the net is trivial, so the net is not the game: it leaks fast, the bucket leaks slowly, and the more a bug is worth the harder it fights to get out. Value is tied directly to escape pressure, which turns the walk back to the shed into the actual risk and makes a full bucket a liability rather than a score.

28
species
240m
square of world
10×
for a gold find
  • TypeScript
  • Three.js
  • Behaviour-driven spawning
  • Vite
hop-to-top.paintbucket.dev
Gameplay capture

09 Browser · precision platformer

Hop to the Top

1,577 metres, and you can see every one of them.

Jump King as a frog. Hold to gather a leap, release to commit to it, and accept that the frog is a physics object from the moment it leaves — as is the ledge you misjudged. Nine zones stack from pond water and oversized lily pads through an ancient tree, a tower nobody maintained, machinery, ice, and cloud that is only ever a strongly worded suggestion.

The whole climb is one connected world rather than a stack of separate levels, and that is a rendering decision made for a design reason: from the clouds you can still see the pond. Falling is only frightening if you can see exactly how much you are about to lose, so the game spends its frame budget keeping the drop visible.

1,577m
pond to moon
9
zones
1
mistake needed
  • JavaScript
  • Three.js
  • Physics-driven movement
  • Playwright
  • Vite
frog-platformer.paintbucket.dev
Gameplay capture

10 Browser · 3D platformer

Frog Platformer

Every jump in every level, re-flown on every build.

Five levels of low-poly climbing with a portal that stays shut until every bug on the level is inside you. Hold to charge a leap; hold right-click to aim the tongue and release to fire it. The tongue eats bugs and sticks to scenery, so it is a weapon and a grapple depending on what you point it at.

Meadow Steps teaches the charge on honest footing, the green gives way to snow, Whiteout turns the ground to ice, and the last level removes the ground entirely.

5
levels
100%
of jumps re-flown in CI
2
projects' assets reused
  • TypeScript
  • Three.js
  • Automated traversal tests
  • Shared rigs
  • Vite
sewer-run.paintbucket.dev
Gameplay capture

11 Browser · endless runner

Sewer Run

A study of predatory design, built honestly.

You are a small log of poo with big dreams, riding the current toward the water treatment plant. Three gates arrive at a time and exactly one is a good idea: multiply your size, bolt on lumps, or take the one that halves you. Jump the weirs, duck the drums, and do not let the heat dry you out, because a poo that stops moving has lost.

The brief was explicitly to take the visual grammar of those unskippable mobile ad games — the three-lane choice gates, the multiplier maths, the escalating crowd — and build the version that is actually a game, responsive on desktop rather than a funnel toward a store page.

3
lanes of choice
2
ways to grow, two to shrink
1
very determined log
  • TypeScript
  • Three.js
  • Vite
animal-stacker.paintbucket.dev
Gameplay capture

12 Godot · physics stacking

Circus Drop

A walrus does not sit on a swan the way you hoped.

Forty-eight low-poly animal figurines across six sets, balanced on a raised carnival deck with a U-shaped moat bin running around it. Set them down carefully, merge matching pairs, and try not to put the whole tower on the floor.

You work it from a console of levers and buttons, with a camera you can walk the entire way around the stage — which matters, because whether a piece is actually seated is a question you can only answer by looking at it from somewhere else. Eleven skins sit on top of that, from the classic to the one that is on fire.

48
figurines
11
stage skins
6
collections
  • Godot 4.6
  • GDScript
  • Rigid-body physics
  • Mobile + desktop
sandman-server.paintbucket.dev
Gameplay capture

13 Browser · order management

The Sandman

Overcooked, except the orders are people.

A town falls asleep beneath you and every sleeper needs a dream mixed to fit the day they have had and the one they are dreading. Pour coloured sand to exactly the bowl's capacity, in the proportions that sleeper's colour scores call for, and send it before they wake up on their own and count as zero.

Matching is scored out of 100 against a colour model rather than a recipe lookup, so dreams are near-misses rather than right or wrong, and three deliveries above 85 in a row start a multiplier. Nights ramp by adding mechanics — plot lines on night two, antagonists on night three, and the free aura readings taken away.

7
sands
100
point match score
0
dependencies
  • JavaScript
  • ES modules
  • Colour-distance scoring
  • Zero dependencies
british-chef.paintbucket.dev
Gameplay capture

14 Browser · a joke, executed properly

The British Chef

Three menu options. One outcome. That is the joke.

You are sat at a kitchen table and offered breakfast, lunch or dinner. Whichever you pick, the camera pushes in on a slice of toast, a tin of beans arrives from above, tips over, and empties itself across it. Order again and the kitchen does exactly the same thing, with a receipt that gets less apologetic each time.

One shared timeline drives all three options — same kitchen, same light, same tin. The repetition is implemented deliberately rather than achieved by copy-paste; the only things that vary are the name on the receipt and the flavour text.

3
menu options
1
possible meal
CC0
asset provenance, credited
  • JavaScript
  • Three.js
  • Instanced particles
  • Height-field simulation
  • Vite

15 Browser · instrument

1-Minute

Find out whether you know how long a second is.

Click or hit space once a second, sixty times. That is the entire game, and it is over in a minute.

The product is the report at the end: how far off a minute you landed, your average interval, your steadiness, your fastest and slowest gap, and the drift that crept in while you were quite certain you had it. A per-tap chart shows where you lost it, which is almost always somewhere around tap forty.

60
taps
6
statistics reported
0
dependencies
  • JavaScript
  • No build step
  • No dependencies
1min.paintbucket.dev
1-Minute screenshot
Screenshot