Inspiration

I wanted to make capture the flag. A flag in the middle, a handful of ships, everyone racing to haul it home the oldest multiplayer format there is.

Two things killed that version immediately, and both were entry requirements rather than opinions. Multiplayer is out of scope for this competition, including asynchronous multiplayer, so player-versus-player was gone. And a pure arena capture-the-flag game fits none of the three competition genres, which means it would have been rejected outright no matter how good it was.

So rather than abandon the idea, I looked for what was actually load-bearing in it. What makes capture the flag tense is not that there are other humans. It is that the moment you pick the flag up, you become the slowest and most visible thing on the field. You were fast and safe and worthless; now you are valuable and hunted. Everything good about the format comes from that inversion.

That inversion maps cleanly onto Survival & Resource Management: hauling the Core is the gathering, salvage into upgrades is the conversion, and waves of rival scavengers are the escalating threat. The flag-running survived intact. It just sits in a genre lane now, with AI rivals instead of people.

What it does

You fly a salvage ship into a wreck, haul a Core out of the middle, and carry it back to your dock while rival scavengers try to ram it loose and drag it to theirs.

One control: hold your finger anywhere and the ship flies toward it. Carrying the Core costs you nearly a third of your top speed, so the run home is the whole game. Ram a loaded rival and they drop it. Bank Cores for salvage and spend it on thrusters, plating or a dock turret. Bank five to win. Every Core a rival banks costs you hull, and losing all four ends the run.

How I built it

Entirely by prompting Claude, in a single day across four working sessions.

  • Three.js r149, UMD, unminified, vendored in vendor/. Chosen over the newer release specifically because the newer UMD build prints a deprecation warning and an ES-module build fails when the page is opened directly from disk rather than served.
  • A top-down orthographic camera with the frustum computed from a fixed arena size, which guarantees the whole field fits any phone aspect ratio instead of hoping it does.
  • Legibility rules written before any art. The player is the only cyan object on screen, rivals are the only red, the Core is the only white and the silhouettes differ too, so a sharp arrowhead, a blunt wedge and a spinning octahedron are never confusable even at a glance.
  • All game code in one readable index.html. No build step, no minification, no CDN, no web fonts. Sound is generated with oscillators in code, so there are no audio files to bundle and nothing external to load.

Then I had Claude build a headless test harness that runs the real game code with rendering stubbed out and plays it with simulated pilots at different reaction speeds and aim error. That harness is the only reason this game works at all.

Challenges I ran into

The first harness run returned identical results for every skill level. Every pilot, from precise to clumsy, banked exactly 4 and died on wave 2. Identical outcomes regardless of skill is not a balance problem. It is a bug signal. It turned out to be three bugs, and two of them were completely invisible from reading the code.

Drag was eating all the speed. I had set drag to 0.90 per frame 10% lost every single frame. Terminal velocity was about 2.25 no matter what the top-speed constants said, which meant PLAYER_TOP, every rival speed value, and the entire Thrusters upgrade were doing nothing at all. Players were buying an upgrade that could not possibly work. This is the kind of thing you would never find by playing; you would just conclude the ship felt sluggish and the upgrade felt weak.

The Core respawned on top of whoever was crossing the middle. After every bank it reappeared instantly at the centre of the arena. Tracing possession showed a rival mid-chase sitting 0.17 units away while the player was 5.3 away a free rival bank on almost every single cycle. That was the perfect alternation I kept seeing in the logs: player banks, rival banks, player banks, rival banks, run over. Fixed with a short cooldown during which the Core cannot be grabbed, plus a visible power-up so the delay reads as a rule rather than a glitch.

Ramming did nothing whatsoever. The Core was dropped at the carrier's exact position with no lock on it, so a rammed player simply re-grabbed it on the very next frame. The harness measured 558 frames of rival-player contact in a single run with zero changes of possession. The central verb of the game was purely decorative. Dropped Cores now scatter clear of the carrier and lock briefly.

And the game was mathematically unwinnable. Being rammed originally cost you both the Core and a hull point. With four hull and eight Cores to bank, no amount of skill could finish a run the arithmetic simply did not permit it. Ramming now costs cargo only, and hull drains solely when a rival banks a Core. Plating got repurposed at the same time, from soaking hull damage to giving you a chance to keep the Core when rammed, which is what a player in that moment actually wants.

Once ramming worked, difficulty swung violently the other way pilots were banking 0.5 out of 10. Several more passes on carry speed, rival speed and target count to bring it back.

What I learned

Check the entry requirements against your idea before you build it, not after. The multiplayer and genre problems would have cost me the entire submission if I had discovered them at upload. Finding them at the design stage cost minutes.

A build that runs is not a build that works. This game ran, looked complete, and was broken in three independent ways simultaneously.

Some bugs are only visible in aggregate. A drag constant quietly overriding every speed value in the game does not announce itself. Neither does a respawn that favours one side. You find those by measuring many runs and noticing that the results are suspiciously identical not by playing.

Final measured outcome, fifteen runs per profile: a skilled pilot with upgrades wins 67%, skilled without upgrades 40%, average 40%, clumsy 7%. Runs last 60 to 90 seconds and reach around wave 5. Upgrades roughly double the win rate, which is what makes spending salvage feel like a decision instead of a formality.

Honestly: this has not been playtested by a human. Everything above came from simulated pilots, so it is balance testing rather than feel testing. The feel of the one-finger flight is the part I would most want to validate first, because a control scheme like that either lands immediately or needs rebuilding, and I do not yet know which.

What's next for Deadlight Run

Keep the one-finger control and the carrying penalty and build outward from them: wrecks with different geometry and hazards, rival crews with genuinely distinct behaviours instead of one shared chase, and salvage that carries forward into how you outfit the next dive.

Built With

Share this project:

Updates

Submission history