The story
A 2D action-platformer in the vein of Hollow Knight, which I built for a music group. The story, the characters and the soundtrack are theirs. The code is mine from end to end, from the world generator to the installers, and it is open source. I wanted to see how far one person could take a real game on a web stack: Phaser and TypeScript in the browser, Electron around it, and a harness that can replay the whole game frame by frame.
- rooms generated, each checked by a simulated gecko
- 1,000
- bosses, all drawn by code
- 30
- memories the engine shoots as films
- 13
- scenarios that replay the game frame by frame
- 200
A world that is generated, then played
I didn't want to place a thousand rooms by hand, and I didn't want a generator that only looks right. Each chapter is written as a sequence: where the story happens, where the bosses and the secrets go. An offline generator lays that sequence out as a region of rooms, using its own model of how far the gecko can jump.
A model can be wrong, so it doesn't get the last word. A simulated gecko does, with the same physics as the game, the same collisions with the tiles and the same controls as the player. From the spawn it tries every walk, drop and jump from every place it can stand. A region ships only if the exit and every part of the story are within reach and no pit traps you. Ability gates are checked twice: a reward you can see early must be out of reach with that chapter's abilities, and within reach with the ones that come later.
The same game on every screen
The logic runs in fixed steps of 1/60 of a second, whatever the monitor does. At 60, 120 or 144 Hz the jumps reach the same height and the fights keep the same timing, and a slow frame never turns into a faster gecko. On top of that sits the movement the genre expects: some grace after you run off a ledge, a jump pressed slightly early that still counts, a slide that nothing can hit, and a downward strike that bounces you off enemies.
- to still jump after leaving a ledge
- 110 ms
- early, and the jump still counts
- 130 ms
- of slide that nothing can hit
- 170 ms
A cast drawn by code
The backgrounds are painted. Everything that moves is drawn by code. Enemies, bosses, characters and passers-by come out of a small ink toolkit: shaky outlines, cross-hatching on the side away from the light, stippling. All of it is seeded, so a creature is drawn the same way every time. Each sheet gets a normal map derived from its silhouette, so the lights in the level really shape it, and an emissive layer that keeps the eyes glowing in the dark. Textures are drawn at twice their size and scaled down, so a boss stays sharp when it is scaled up.
Memories shot as films
The flashbacks are not videos recorded somewhere else. They are short films the engine shoots live from a script: a set, a time of day, a cast on their marks, props, and a list of shots. Every shot has a length, a camera with its framing and an optional move, a shallow focus for close-ups, and gestures timed to the millisecond: walking, turning, nodding, handing over an object, saying a line, switching on a screen, dimming a light. The film plays in its own scene, in sepia, letterboxed, with grain and the actors' shadows on the wall, while the game waits underneath.
Two players, one world
Co-op runs over the internet, peer to peer. I split authority by what each player feels most. You own your gecko, so your movement never waits for the network. The host owns the world: enemies, bosses, projectiles, drops, the story and the save. When the guest hits something, it sends a request and the host settles it.
- 1
Handshake
The two builds compare their protocol version first. Two different builds never talk to each other.
- 2
Two lanes
Story, choices and saves travel as reliable typed messages. Positions travel as small binary packets, 30 times a second for each gecko.
- 3
A little in the past
The other player is drawn slightly in the past, between two real states, so they move smoothly even when packets arrive in bursts or go missing.
- 4
Rules for two
Enemies get tougher for two. A fallen player watches until the other lights a checkpoint, and either player can rejoin a running game with the same code.
A game that replays itself
A game this size breaks where nobody is looking. So I built a harness that runs the real build in a headless browser with a fake clock and seeded randomness: the same input always produces the same game, down to the bit. On every frame a probe records a fingerprint of everything you could observe, from the gecko and the bosses to the lights, the sounds, the interface and the save.
On top of that sit 200 scenarios: every chapter from its entrance and from its save, every ending, the fights, the systems, recorded key tapes, and a bot that plays the whole campaign from start to finish. A change that shouldn't alter the game has to produce identical traces. When two traces differ, the harness names the first frame where they split and the source line that caused it.
- chapters31
- combat28
- systems25
- exploration22
- entrances21
- key tapes20
- more systems19
- characters16
- endings9
- 4 more kinds9
- scenarios
- 200
- of the source lines run by them
- 95.6%
- frames in the bot's run through the campaign
- 92,311
Out on three systems
The game is wrapped in Electron and built natively on each operating system. Pushing a version tag builds the Windows installer, the macOS disk image and the Linux AppImage in CI and publishes them as a release. That release is where the download link above points.
Gallery






Want something like this?
I can build the same thing for your product.
Tell me what you have in mind. You get an answer the same day, with a realistic scope and timeline, not a sales pitch.