All work

Project 09

Gloaming

The board that plays back.

Version one worked perfectly and frightened nobody. So I threw it away and rebuilt every rule as something you can watch move.

The Gloaming title card: the word GLOAMING in gold serif capitals, glowing faintly out of near-total darkness, with the words TAP TO SKIP in small type at the bottom.
The first thing you see, and it is deliberately almost nothing — a word barely emerging from the dark. The whole game is about light losing to darkness, so the title card had better lose first.

I wanted to make a co-op horror board game where the board itself is the enemy — no human running the monster, the game running it. I got it working. Every rule fired correctly, the numbers went up and down exactly as designed, and it was completely unfrightening. It played, as I wrote at the time, like a spreadsheet in a horror skin.

So I threw it out and rebuilt it on one rule: if a mechanic is a hidden number doing arithmetic, replace it with a thing that visibly moves, grows, shrinks or gets eaten. The night meter became tiles actually turning to void from the outside in. The currency became a torch that burns down, and at zero you become a drifting wisp. Progress became three lanterns you physically carry — and drop where you fall.

Then I had to prove the game was worth playing well, which is a harder question than whether it works. So I wrote a smart bot that reads the monster's telegraphed route and protects whoever is carrying a lantern, and a greedy bot that just grabs, and played them against each other on identical seeded games so the only variable was decisions. The first run came back flat — skill was not paying. That result changed the design: the torch went from eight to seven, and stepping on frayed ground went from costing one to costing two. Now a skilled bot scores sixteen points higher than a greedy one over the same seeded games, and I can show you the number.

Why it was hard

A co-op game where the board is the antagonist normally needs a human to run the antagonist’s bookkeeping, which caps how clever it can be. But the deeper problem arrived in the middle of the build: everything worked mechanically and it still played like a spreadsheet in a horror skin. Numbers went up and down correctly and none of it was frightening. So I tore it down and rebuilt it on a single rule — if a mechanic is a hidden number doing arithmetic, replace it with a thing that visibly moves, grows, shrinks, or gets eaten.

How it actually works

  • The board is rings inside rings, and the dark eats them from the outside in. Where you are standing IS the clock — there is no timer anywhere on screen.

    The board is a graph of concentric rings, and the dark consumes the outermost surviving ring inward. The geometry is the mechanic: which act you are in is read off how deep the dark has eaten, not from a counter.

    1 receipt
    • gloaming-gameRing-graph board with the dark advancing inward as the act structure.
  • Every hidden number became a thing you can watch move. The clock became tiles going dark. Money became a torch burning down. Hit zero and you are a drifting wisp.

    I replaced every abstraction with an object. The night meter became tiles turning to void; a currency became a torch that burns down, and at zero you become a drifting wisp; progress became three lanterns you physically carry, and drop where you fall.

    1 receipt
    • READMEThe session-five teardown and the rule it was rebuilt around.
  • The monster shows you exactly where it is going before it goes, and visibly changes its mind when it picks a new target. The dread is in watching it decide.

    The monster telegraphs its entire route before it moves, and visibly changes its mind when it re-targets. The fear comes from watching it decide, not from a dice roll you cannot see.

    1 receipt
    • READMETelegraphed movement, evolving by act; the Gate is sanctuary it cannot enter.
  • Two players or six, it squeezes just as hard. Otherwise a full table is an easy table.

    I normalise pace by player count, so a two-player game and a six-player game feel the same rather than one being trivially easy.

    1 receipt
    • PROGRESS.mdPer-round accumulators divided by party size.
  • You cannot cheat by clicking fast. The rules live inside the game itself rather than in whether a button happened to look clickable — checked across 1,272 turns of play.

    Out-of-order input is rejected by the engine, not hidden by the interface — asserted across 1,272 turns. A disabled button is a suggestion; a reducer that refuses is a guarantee.

    1 receipt
  • There is not one sound file in the whole project. Every noise you hear is built by the browser, live, out of nothing.

    Every sound in the game is synthesised at runtime. There is not a single audio file in the repository.

    1 receipt
    • package.jsonHowler driving procedurally generated tones — zero audio assets.
The Gloaming hero-selection screen, headed “Choose your heroes”, showing five characters — the Swift, the Lamplighter, the Ember-Hearted, the Unseen and the Stubborn Flame — each with one rule it is allowed to break.
Five heroes, and every one of them breaks exactly one rule of the game: the Swift always moves one extra square, the Lamplighter carries lanterns without slowing, the Stubborn Flame burns at half speed. “Every hero breaks one rule. No one gets out alone — the team does.” That sentence is the entire co-op design in twelve words.
A dark game board of concentric hexagonal nodes with a glowing amber gate at its centre, a vertical gauge on the left reading “THE DARK — DUSK”, a party panel, and a chronicle log.
The board mid-game. The gauge on the left is not a number going up — it is a record of how many rings the dark has already eaten.
The same board almost entirely consumed, roughly twenty black void tiles ringing a small island of light around the central gate.
Act two. Everything black was a tile you could stand on twenty minutes ago.
A banner reading “THE HOLLOW ONE catches Ash”, above an escape checklist and gold spokes radiating from the central gate.
Cause and effect stated plainly. You always know what just happened to you and why — which is what makes the dread land rather than confuse.

The numbers

+16 pts
how much better a skilled bot does than a greedy one, on identical seeded games
receipts
  • PROGRESS.md:17Measured at two players and positive at every player count.
53–55%
win rate across 2, 3 and 4 players — tuned, not guessed
receipts
2.4 pts
spread between the best and worst hero — the gate allowed 8
receipts
150
chaos games at 2–6 players, all of which terminate
receipts
0
softlocks, and zero console errors in production
receipts
0
API keys, network calls or runtime secrets — it is entirely self-contained
receipts
  • README:142"Runtime AI: None. Fully self-contained — no LLM, no network, no secrets at runtime."

The part most people would have skipped

My skill gap came out flat on the first measurement — a good player was winning no more often than a greedy one, which meant the decisions did not matter. Rather than shipping and hoping, I tightened two numbers until they did — one fewer turn of torch, one more point of cost for careless footing. Squeezing the margin is what turned reading the monster’s telegraph from a nicety into the difference between finishing and not. I tuned it until skill provably paid, and left the proof in the repository.

Built with
  • boardgame.io
  • React 19
  • Vite
  • TypeScript
  • Framer Motion
  • Howler (synthesised audio)
  • puppeteer-core
A luminous figure dissolving into a trail of stardust, with hand-lettered text reading: Fuck normal, I want magic.
I had a working game and I threw it away, because working is not the bar. This one is on the wall too.

NGC 6302 — the Butterfly Nebula

A star dying, and the dying is the most beautiful thing it ever did.

The gas in those wings is leaving at nearly a million kilometres an hour, lit from inside by the exposed core of the star that shed it — one of the hottest objects in the galaxy, and already on its way to going out. Nothing here is being destroyed by an enemy. It is just running out.

The Butterfly Nebula, NGC 6302 · NASA, ESA and the Hubble Heritage Team (STScI/AURA) · CC BY 4.0