← All work
3D · procedural generationLive

A 3D Game With No Art Files

Blaster Squad is a 3D first-person browser game I built for a seven-year-old in the family to play on an iPad. Ships with no models, no image files and no sound files. Every shape, every surface and every effect is generated by code the moment it loads.

The iPad is the target, not an afterthought. Thumbs on the glass, add it to the home screen, play it on the couch with no signal. The same build also takes a game controller or a laptop.

0
models, image files or audio files in the game
15
story levels across three chapters, each gated by a boss
3
ways to play: tablet, controller, or mouse and keyboard

Play it

It runs in a browser tab. On a laptop, click to lock the mouse and use WASD. On a tablet, the left half of the screen moves and the right half looks and fires.

On an iPad you can add it to the home screen. It gets its own icon, opens fullscreen, and works with no signal.

First person view of the Night City arena: a droid in the crosshair, the blaster in hand, pink neon signs across dark buildings, and the on-screen fire and reload buttons the tablet version is played with.
Every pixel here was generated at load. The droid, the neon, the ground, the blaster. No models, no textures, no image files. The fire and reload buttons on the right are the iPad controls.
Open the game → No login. Works offline once loaded.

Why generate everything

I cannot draw, model or record, and I was not going to buy an asset pack for a gift. So the constraint became the design: if it cannot be produced by code at load, it does not go in the game.

3 PNGs

are the only images in the whole repository, and all three are home-screen icons. Nothing the player actually looks at is a file.

Meshes are built out of primitives. Textures are painted into a canvas at startup and converted into normal maps from their own height data. Sound is synthesized with Web Audio oscillators and noise buffers at the moment it plays.

Three rendering problems this created, and the fixes
  • Metals rendered black. They need something to reflect. The fix is an environment map prefiltered at startup from a tiny sky and ground scene, so the whole thing is still generated.
  • The weapon clipped through walls. It now renders in its own scene and camera, composited on top of the world, so geometry can never poke through it.
  • Fill rate on a tablet. Pixel ratio is capped on touch devices. That is the budget that decides whether an iPad holds frame rate.

Built to be played by a kid, then by an adult

Three difficulty tiers scale enemy health, speed, count, damage and how badly they aim. CADET is tuned so a young player can win. LEGEND is tuned to be genuinely hard.

Story mode runs 15 levels across three chapters, an outpost, a frozen base and a night city. A boss closes each chapter. Replaying a level on a harder tier upgrades your medal and never downgrades it, so going back is always worth it.

The lives system, and why it exists

Three lives per run. Losing your shield costs one and respawns you at the deploy point with a couple of seconds of invulnerability.

Extra lives come from a rare drop and from clearing every third wave, up to five. A seven-year-old quitting after one unlucky hit is the actual failure mode, so the game gives ground back.

One rule about targets

Everything you shoot is a droid. There are no human targets anywhere in the game, and that is written into the repository as a content rule rather than left to whoever adds the next enemy.

The 2D game that came first has the same rule. It seemed worth deciding once, on purpose, instead of drifting.

Getting it onto the iPad

It is installable. Open the link in Safari, add it to the home screen, and it behaves like an app: its own icon, fullscreen, no browser chrome, and it keeps working offline.

Progress is stored per device, so his save is his.

How the code is laid out
  • textures, procedural canvas textures and height-to-normal-map conversion.
  • world, arena geometry, colliders, lighting, the environment map, ray casts.
  • droids, enemy models, walk cycle, AI and the debris system.
  • main, player controller, weapon, projectiles, waves, HUD and the render pipeline.
  • Plus audio, bosses, pickups, powers, story and progress, and a vendored copy of Three.js r169.

It is ES modules with an import map, so it needs to be served over http rather than opened as a file.

Why it is on this site

It is a kid's game and it is not business-relevant, which is exactly why it sits in its own section.

What it does show is a hard constraint held all the way through a build, on a stack I had not shipped 3D on before, for a user who gives blunt feedback and does not care about my reasons.