Project Game design 2026

What does a 1996 arcade shooter teach you about product?

An on-rails light gun game, rebuilt from scratch in one HTML file

Virtua Justice title screen: rail shooter controls, a difficulty choice between Rookie and Veteran, and a Start Game button over a night-time street scene.
Launch video, 22 seconds, with sound

Virtua Justice is an on-rails light gun shooter, written as an original tribute to the arcade grammar of Virtua Cop 2. Nothing from the original is in it. No models, no textures, no sounds, no levels. What’s in it is the design language, rebuilt from scratch to see which parts of it still hold up and which parts I’d change.

Why this, of all things

Arcade games are the most ruthless onboarding ever designed. You’ve paid one coin, you’ve got about three minutes, and the game has to teach you the rules, make you care, and make you want to pay again, with no tutorial and no second chance. That’s a product problem. I wanted to work through it hands on, and the only honest way to do that was to build one.

The constraint

One HTML file. No asset pipeline, no build step, nothing downloaded at runtime. Every enemy, prop and piece of scenery is assembled from Three.js primitives when the level loads. Every gunshot, hit and alarm is generated by a small Web Audio synthesiser. The whole game is 2,325 lines. The constraint wasn’t an exercise in minimalism for its own sake. It meant I couldn’t hide a weak mechanic behind good art. If a moment didn’t work, it was the design.

What I kept

The parts of the arcade grammar that turned out to be genuinely good product design:

Threat reticles that shrink as an enemy’s timer runs down. It’s the whole game’s information architecture in one shape. You can read who will shoot first from across the screen, and the game never has to tell you.

Hit zones. Where you hit matters, and the feedback is immediate. The player learns by doing, not by reading.

A three-phase boss. The rules you’ve learned get stress-tested, then bent, then combined. It’s the same thing a good product does at the end of onboarding.

What I changed

The original’s incentive was simple: shoot fast, don’t get shot. I changed what the game rewards.

Disarming an enemy, by hitting the weapon rather than the person, pays a bonus. Killing pays the base score. So the highest-scoring run is the one with the fewest bodies, and the player who wants the top of the table has to play more carefully, not faster.

Civilians move through the same scenes as enemies. Shooting one costs you, and the reticle system doesn’t mark them, so the player has to look before firing. It turns a reflex game into a reading game for about half a second at a time, and that half second is where the tension lives.

Both changes were made for the same reason. The original mechanics reward speed. These reward judgement. I wanted to know whether the arcade format could carry a different value system without losing what made it fun, and mostly it can.

Where it’s rough

The difficulty curve was tuned by me, on my hardware, at my reaction speed. There’s no accessibility setting for reticle timing, which means someone slower than me isn’t playing the same game. That’s the first thing I’d add. The second is a proper level editor, because hand-placing every spawn in code is the reason there’s one level and not four.

All work