Aakib Ansari.
Back to articles
Hands-on TestTested live

I Prompted Ox Alpha to Build a Top-Down Zombie Wave Survival Game — Spatial-Grid Pathfinding for 40+ Enemies, One Shot

Md Aakib Ansari
Md Aakib AnsariWeb Developer & AI Tools Reviewer
•6 min read•Model: Ox Alpha•Company: Z.ai
I Prompted Ox Alpha to Build a Top-Down Zombie Wave Survival Game — Spatial-Grid Pathfinding for 40+ Enemies, One Shot

The final genre in our Ox Alpha stress-test round is a top-down horde shooter — a format that lives or dies on how well a large number of concurrent AI-controlled enemies can move without clumping into a single unreadable mass or tunneling through each other.

What I asked for

Raw Prompt (Unedited)
Skip to clean version ↓

You are an expert Three.js and Web Audio game developer.

Build a complete, fully playable, single-file HTML/JS Top-Down 3D Zombie Wave Defense game.

Requirements:

  1. Single File: Self-contained index.html with inline styles and scripts.
  2. CDN: Load Three.js (r128). No external asset dependencies.
  3. Player & Controls:
    • Top-down isometric camera following the player.
    • WASD movement + smooth mouse aim rotation.
    • Left-click shooting with raycast bullet trails, muzzle flash, and reload timers (R).
  4. Enemy AI & Waves:
    • Dynamic zombie horde pathfinding toward player position (20–50 concurrent entities).
    • Scaling wave manager (Wave 1 to endless) with increasing enemy health and runner variants.
    • Spatial partitioning or optimized distance loops to sustain 60 FPS.
  5. Procedural Audio (Web Audio API):
    • Synthesized gunshots (noise burst + filter decay), dry click on empty mag, zombie groan frequency sweeps, hit markers, and ambient drone.
  6. HUD & Upgrades:
    • Health bar, Ammo counter, Wave counter, and Kill Score.
    • Between-wave upgrade cards (Fire Rate, Damage, Max Health, Speed).
    • Game Over screen with restart trigger.

Output only complete, production-ready code.

Generation
One-shot, no fixes
Effort setting
High
Concurrent enemy cap
Up to 46, scaling by wave
Enemy AI
Spatial-grid separation + obstacle avoidance
Enemy types
Walker, Runner (wave 3+), Brute (wave 5+)
Upgrade options
6 cards, 3 offered per wave
Object pooling
Zombies, blood particles, bullet tracers
Access
OpenRouter, stealth/ox-alpha
Live Demo

Interactive Demo

W A S D  /  Arrow Keys to drive

This is Ox Alpha's unedited one-shot output. WASD to move, aim with the mouse, left-click to shoot, R to reload.

Camera
Fixed isometric offset that smoothly follows the player
Enemy pathfinding
Direct-to-player steering with spatial-grid separation and obstacle avoidance
Performance strategy
Uniform spatial hash grid rebuilt every frame for O(1) neighbor lookups
Wave system
Escalating spawn count, health scaling, and concurrent cap per wave
Upgrade system
3-of-6 random card choice between waves, stacking modifiers
Audio
Distance-attenuated zombie groans, filtered noise-burst gunfire, ambient sub-bass drone

Spatial-grid enemy separation

Rather than an O(n²) loop checking every zombie against every other zombie for separation, Ox Alpha built a uniform spatial hash grid (gridMap, keyed by cell coordinates) that's rebuilt once per frame and queried per-zombie for only nearby neighbors within a small radius. Each zombie's movement vector combines a direct vector toward the player with a weighted separation force from nearby zombies and a push-away force from obstacle colliders — the same steering-behavior pattern used in real crowd-simulation systems, not a naive "walk straight at the player" approach that would let zombies stack on top of each other.

Object pooling throughout

Zombies, blood particles, and bullet tracers are all pooled rather than created and destroyed per-spawn. The zombie pool specifically reuses inactive mesh groups by type — when a walker dies, its mesh group becomes available for the next walker spawn rather than being disposed — which avoids the geometry/material churn that would otherwise show up as frame hitches during a large wave spawn.

Wave director and enemy variety

updateWaveDirector() tracks a per-wave spawn count and a concurrent cap that both scale with wave number, spawning enemies from a ring around the player at increasing rate as waves progress. Three enemy types are properly differentiated in stats, not just color: Walkers are the baseline, Runners (unlocked wave 3+) trade health for roughly double speed, and Brutes (wave 5+) have over 4x a Walker's health and higher per-hit damage. Enemy health also scales per-wave (t.hp * (1 + (state.wave - 1) * 0.12)), so late-wave Walkers are meaningfully tougher than wave-1 Walkers even before any Runner/Brute variety kicks in.

Between-wave upgrade cards

After clearing a wave, the player is shown 3 of 6 possible upgrade cards (Fire Rate, Damage, Max Health, Speed, Fast Hands/reload speed, Deep Mags/magazine capacity), each applying a real stacking multiplier to player stats rather than a flat one-time bonus — player.fireInterval *= 0.8 compounds correctly across repeated picks of the same upgrade rather than resetting to a fixed value each time.

What impressed me

The spatial-grid neighbor query is the standout technical choice here — it's exactly the right data structure for "many entities that need to avoid clumping," and it's not something the prompt spelled out in implementation detail (it only asked for "spatial partitioning or optimized distance loops," leaving the actual approach open). Building a real uniform grid rather than falling back to a simpler but slower distance-loop shows the model reaching for the technique that actually matches the problem's performance profile at 40+ concurrent entities.

The stacking upgrade system is also a small but easy-to-get-wrong detail done correctly — multiplicative stat modifiers that compound across multiple picks of the same card, rather than a naive implementation that would overwrite the previous bonus.

What needs work

  • Screenshot testing showed a fairly dark, low-contrast scene — the fog and lighting combination reads moodier than the HUD elements, which could make distant zombies harder to spot at a glance; a brightness/contrast pass would likely help readability.
  • No zombie attack animation beyond a lunge stop — enemies stop at melee range and deal damage on a cooldown, but there's no distinct wind-up or telegraph before a hit lands.
  • Only one effort tier tested — consistent with the rest of this round, this run used High only.

Honest assessment

Try it yourself

Try It Yourself

You are an expert Three.js and Web Audio game developer.

Build a complete, fully playable, single-file HTML/JS Top-Down 3D Zombie Wave Defense game.

Requirements:

  1. Single File: Self-contained index.html. Load Three.js (r128) from CDN. No external asset dependencies.
  2. Player: Top-down isometric camera following the player. WASD movement, smooth mouse aim rotation, left-click shooting with raycast bullet trails and muzzle flash, reload timer (R).
  3. Enemy AI & Waves: 20-50 concurrent zombies pathfinding toward the player with proper separation (avoid clumping/overlapping). Scaling wave manager with increasing enemy health and a faster runner variant. Use spatial partitioning or an optimized neighbor-query approach to sustain 60 FPS with many enemies.
  4. Procedural Audio: Synthesized gunshots, empty-mag click, zombie groans, hit markers, ambient drone.
  5. HUD & Upgrades: Health bar, ammo counter, wave counter, kill score, between-wave upgrade cards (fire rate, damage, health, speed), Game Over screen with restart.

FAQ

Frequently Asked Questions

What model generated this?
Ox Alpha, an unannounced stealth model (provider alias 'Stealth') available via OpenRouter (stealth/ox-alpha) and OpenCode. No lab has officially claimed authorship.
How does it keep 40+ zombies performant?
A uniform spatial hash grid is rebuilt every frame and used to query only nearby zombies for separation calculations, avoiding an O(n²) check against every other zombie. Zombies, blood particles, and bullet tracers are all object-pooled rather than created and destroyed on demand.
Did it need any follow-up prompts or fixes?
No. This was a clean one-shot — the generated code ran correctly on the first attempt with no edits.
Can I run this locally?
Yes. Download the single HTML file and open it in any modern browser. The only external dependency is the Three.js CDN script.
How do the enemy types differ?
Walkers are the baseline enemy. Runners unlock at wave 3 and trade lower health for roughly double movement speed. Brutes unlock at wave 5 with over 4x a Walker's health and higher damage per hit. Enemy health also scales gradually with wave number independent of type.

Related Articles