Game ·

Deep Dive

A free browser submarine game built in a day, and the third test of whether a sprite tool I wrote can carry all of a game's art on its own.

StackTypeScript · Web Audio · Cloudflare Workers · D1
StatusLive
LinksVisit

Deep Dive is a small real time game that runs in a browser tab. You take a research sub down a 1000 metre trench, collect the specimens that glow, and get back up to the boat to bank them. Banked points buy a bigger battery, a tougher hull and a longer searchlight, so each dive pays for the next one. There is a blue crystal on the trench floor. Bring that home and you have won the run.

Getting in the way: jellyfish, an anglerfish that lunges at anything drifting near its lure, eels in the walls, old mines, a squid that follows your searchlight, and something enormous on the floor. It is free, with nothing to install and no sign up, on itch.io and on PixelDeck.

Two frames of the game side by side. On the left the sub near the surface in bright blue water, the research boat above it, kelp and coral on the trench wall, two green specimens and a jellyfish. On the right the sub at 975 metres in near darkness, its searchlight cutting a wedge, a cluster of blue crystals glowing on the floor and the flank of an enormous creature filling the top of the frame.

Near the surface you can see the whole trench and the boat is a short climb away. On the floor the searchlight is most of what you can see, it drains the battery while it is on, and the shape across the top is the leviathan.

It is an art test with a game attached

Deep Dive is the third of three throwaway games I built in a day each, to find out how much of a game's art Bitwright could draw on its own. Bitwright is my own tool: you describe a sprite in one sentence, a model writes a small data file, and a renderer I wrote draws it pixel by pixel. The first two were Lights Out, a top down horror game, and Last Train, a side scrolling one.

The cost of the art in all three came out in the same band. Lights Out took 17 sprites for 151p. Last Train took 18 for 210p. Deep Dive took 19 for about 208p, plus 26.3p for three of them that needed drawing again.

Nineteen pixel art sprites laid out in three rows at twice size: the sub and its dented twin, rock, kelp, coral, a vent, four glowing specimens, a jellyfish, an anglerfish, an eel, a squid, a mine, a battery, a leviathan, a wreck and the research boat. Under each is its name and what it cost to draw, from 4.5p to 29.5p.

The whole cast, at the size the game draws it. The three in amber were drawn twice and carry the price of both draws.

What three games taught me about generated sprites

Side on and front on hold up. Top down is ignored. Ask for a sprite seen from above and it comes back side on, every time, however the sentence is phrased. Lights Out is a top down game and that is the single thing that made it hard. Deep Dive is side on and never fought the tool once.

Under about 16 pixels a subject loses its shape. A 12 pixel oil can in Lights Out read as an apple. The ceiling lamps read as vases. 14 of Deep Dive's 19 sprites are 32 pixels wide or less, and the ones that work at that size are the ones with a distinctive outline: a whip thin eel, a squid that is all arms, a sub that is a fat yellow capsule.

Walk cycle frames barely differ from each other, so motion is better written in code than drawn. Every bit of movement in Deep Dive is code: the sub tilts as it turns, the jellyfish pulse, the kelp sways, the leviathan slides along the floor.

A tile that ends on a dark row grids when it repeats. The trench wall is one 32 pixel sprite tiled down a thousand metres, and a dark bottom row turns into a hard horizontal line every 32 pixels that reads as a mistake in the level rather than in the art.

A draw that fails validation is still billed. That is the right behaviour, since the model has already been called, and it makes a sloppy sentence cost real money rather than just time.

Building a real game also found a bug in the tool. The damaged sub failed before any model call, because the sprite set named its anchor as an object and the "match this to the sub" lookup expected a string. It was skipped, reported as a failure, and drawn afterwards with an append run for 8.5p. Nothing but a real set with an anchor in it would have gone near that path.

One day, and what is under it

The game is TypeScript on a plain 320 by 180 canvas, scaled up by whole device pixels so nothing blurs. Every sound is synthesised in code while you play, so there are no audio files in the build at all.

There are 185 unit tests in the game and 44 more in the leaderboard. That is a lot for a day's work and it is why the day was enough. Movement, collisions, battery and hull, specimen values, upgrades, creature behaviour and both ways a run can end are all covered, so retuning the difficulty in the last hour did not mean replaying the whole trench to find out what I had broken.

The leaderboard is a Cloudflare Worker with a D1 database behind it, holding two boards: fastest run and deepest dive. Names are three letters. Submissions are capped at ten per IP per hour, and the IP is only ever stored as a SHA-256 hash, so the rate limiter works without the database ever holding an address. If the Worker is down the game carries on and just does not offer a score.

Four people have played it and nobody has finished

Within hours of publishing there were four names on the deepest dive board. The best of them got to 720 metres of the 1000. The fastest run board is still empty, so the crystal is on the floor and nobody has brought it up yet.

What is mine and what is not

The art is generated and the game's page says so plainly. The code was written with Claude. What is mine is the design and the direction: what the trench does to you on the way down, what the squid is for, what a specimen at 900 metres is worth against one at 100, and how much battery a searchlight should cost before turning it off becomes a real decision.

Reading