Skip to main content

Browser game in Rust

Neon Command

A tower defense game I wrote in Rust with the Bevy engine and compiled to WebAssembly. It runs in any browser on desktop or mobile, with nothing to install and no account.

Year
2026
Role
Solo
Status
Live
Neon Command tower defense game
Title screen, playable at td.wyatt-fleming.com

At a glance

  • ~14kLines of Rust
  • 8Tower Types
  • 8Enemy Types

Overview

I built Neon Command to learn Bevy and the Rust-to-WebAssembly toolchain end to end. The game runs entirely in the browser with no backend or account, and keeps high scores and settings in local storage. It has eight tower types that branch at level three, eight enemy types including bosses and splitters, four map presets, three player abilities on cooldown, and synergies that reward placing towers next to each other.

Why It Matters

  • Range beyond the web stack

    A systems-language project alongside my TypeScript work, covering Rust, ECS, and the WebAssembly toolchain.

  • Private by design

    No backend, accounts, or cookies. The game is a static bundle, and page views are counted with cookieless analytics.

  • Tracked performance work

    An audit records 11 known optimizations, including tower synergy and splash-damage loops.

  • Ongoing maintenance

    The game is deployed and maintained with the same release checks as my other production work.

Game Systems

Designed around a small set of mechanics that compose into deeper strategy:

  • 8 Tower TypesEach with a level-3 specialization branch. Pick a build, not just a tower.
  • 8 Enemy TypesBasic, Fast, Tank, Armored, Flying, Boss, Splitter, MiniSplitter. Counter-pick or get overrun.
  • 4 Map PresetsRandom procedural, Serpentine, Sprint, and Spiral, each playing differently
  • Tower SynergiesAdjacent towers activate bonuses, rewarding placement-as-strategy
  • Player AbilitiesFreeze, Gold Rush, and Artillery on independent cooldowns
  • Economy LoopInterest, kill streaks, combo bonuses, and early-send wave bonuses tighten the risk/reward dial

Rust + Bevy + WASM

Built on Bevy 0.14's ECS architecture and shipped as a WebAssembly bundle:

  • Pure ECS Architecture~14k lines of Rust organized as systems operating on components. No inheritance, no game-object tree.
  • WASM Build via TrunkCompiled for wasm32-unknown-unknown and served as a static bundle by Nginx
  • Aggressive Size OptimizationRelease profile uses opt-level=z, LTO, single codegen unit, panic=abort, and strip=true
  • WebGL2 RendererBevy's WebGL2 backend with custom particle, projectile, and HUD systems
  • Defensive Command BufferUses try_insert instead of insert to avoid panics when entities despawn before commands flush
  • Persistent StateHigh scores and settings live in LocalStorage via web_sys, no server required

Technical Architecture

Lean stack so the build stays fast and the binary stays small:

Language
Rust 2021 edition
Engine
Bevy 0.14 with the WebGL2 feature set
Toolchain
Trunk for WASM bundling, wasm-bindgen for JS interop
Grid
18x11 tiles, 50px each, 900x550 play area
Storage
LocalStorage via web_sys (no backend)
Hosting
Docker multi-stage build (Rust compile -> Nginx Alpine), auto-deploy via Coolify

Lessons

  • Bevy ECS PatternsInternalized the ECS mental model (components as plain data, systems as queries, no inheritance) and learned where Bevy specifically diverges via deferred commands, change detection, and SystemSet ordering.
  • Rust to WASM Build PipelineSet up the full wasm32-unknown-unknown pipeline including the getrandom_backend=wasm_js rustflag, js feature on transitive uuid deps, and Trunk build profiles. Every layer of the toolchain has its own gotcha.
  • Defensive Command PatternsBevy's deferred commands can panic if you reference an entity that gets despawned before the buffer flushes. Settled on try_insert as the default to make the system robust without sprinkling existence checks everywhere.
  • Performance as a RoadmapProfiled the hot loops (tower synergy is O(T^2), splash damage is O(E)), wrote an algorithm-audit doc with 11 prioritized optimizations, and treat it as a backlog rather than premature work.

Let's talk.

Planning an interactive or cross-platform product? I bring product planning and hands-on engineering together, from the core mechanic through release and ongoing maintenance.