Project

Stone to Stars

A browser empire-survivor roguelite, built solo. Lead a people from the Stone Age to the stars one survivor run at a time. The reason it is on this site is the engineering: a framework-free game-logic core under 400+ unit tests, procedural audio and art, and a static build with no backend.

Personal project · TypeScript + Phaser · 400+ tests · Ships to static files

What it is

Two genres wired together. The action layer is a survivor-style auto-battler: you move, the weapons fire themselves, the screen fills, and you bank what you kill. Between runs you spend that progress on a tech tree and a base camp that carry forward, advancing your people through nine ages from the Stone Age to a space-age final stand. Death is not the end of a run in the usual sense. Whatever a run banks, the empire keeps, and the next run starts stronger.

It is single-player, free, and has no monetization and no backend. It runs entirely in the browser and saves to local storage. The point of building it was not the design; it was finishing and shipping a complete piece of software, solo, with a real test suite and a real build.

Why I built it

I spend my days on security architecture and policy. This was a deliberate swing in the other direction: take a project from an empty repo to a shipped, playable product, owning every layer myself. Logic, rendering, audio, art, build, and tests. No team, no framework doing the hard parts for me.

It is also a working demonstration of AI-augmented execution. I built it the way I build everything now, pairing with AI tooling to move at a pace that used to take a team, while keeping the architecture decisions and the quality bar my own. The interesting question for a portfolio is not "can you make a game." It is "can you hold an entire system in your head, keep it tested, and actually ship it." This is the evidence that I can.

Architecture

The design decision that made the rest possible: the game rules do not know how the game looks. The economy, tech tree, camp, and run state are framework-free TypeScript with zero Phaser or DOM dependency, so they run and test in a plain Node environment. Rendering is a separate concern layered on top.

Logic core (framework-free)

Economy, tech tree, base camp, run state, and progression as pure TypeScript with no engine or DOM imports. This is where the 400+ unit tests live. Because the rules are decoupled from rendering, the hard-to-get-right parts are testable in isolation and the engine is swappable in principle.

Run scene (Phaser)

The survivor auto-battler runs on a Phaser canvas: spawning, movement, projectiles, collisions, the gem economy, and the in-run upgrade draft. This is the only part that needs a game engine, and it is kept to exactly that.

Empire screen (DOM)

The between-runs layer (tech tree, base camp, ages, persistence) is plain HTML and CSS, not canvas. Using the right tool per surface: a document-shaped UI for a menu-driven 4X layer, a canvas for the action.

Procedural audio (Web Audio)

Every sound effect is synthesized at runtime from oscillators, filtered noise, and ADSR envelopes. No SFX files ship. Music is a set of context-selected CC0 tracks routed through one master gain, with a procedural fallback bed if a track fails to load.

Procedural sprite pipeline

All sprites are generated from a shape-data plus render-pass system rather than authored image files, with a one-line toggle between a flat and a shaded style. The art is data, which keeps the repo light and the look consistent.

Static build

Vite builds the whole thing to static files that deploy to any static host. No server, no database, no telemetry. State persists in local storage. A security habit that turned out to be a clean architecture too.

What shipped

Built in tracked feature batches, each gated on its own tests and a play session before it counted as done. 40 of 42 planned work items delivered, 400+ unit tests green.

The full loop

Run, bank resources, research, build, advance an age, run again. Verified end to end: a complete survivor run feeds the empire, which unlocks the next run's options.

Nine ages to a finale

Stone, Bronze, Iron, Classical, Medieval, Renaissance, Industrial, Modern, and Space, ending in a scripted last-stand boss run against an invasion.

Build depth

A weapon draft and a Forge-and-Fuse fusion system, a multi-tier gem economy, perks and relics, dungeon expeditions, points of interest, and mini-boss events. Enough interacting systems to make two runs play differently.

Polish layer

Difficulty pass, onboarding help, pause menu, a persisted volume and mute panel, and an art pass that replaced every placeholder with the procedural pipeline output.

What this demonstrates

Solo full-stack delivery

One person across logic, rendering, audio, generated art, build tooling, and tests. Not a fork, not a tutorial, not a framework demo. An empty repo to a shipped product.

Test discipline where it counts

400+ unit tests, concentrated on the game logic that is easy to break and hard to debug by playing. Tests written alongside the code, not bolted on after.

Architecture that earns its keep

Separating rules from rendering is the decision the whole project rests on. It is why the logic is testable, why the code stayed maintainable across 40 feature batches, and why the action and menu layers could use different technologies cleanly.

AI-augmented pace, owned decisions

Shipped a feature batch most days while keeping the design and quality bar my own. The throughput is the point; the judgment stayed human.

Stack & scale

Stack

TypeScript Phaser Vite Web Audio API Vitest HTML / CSS DOM

Scale

400+ tests 9 ages 40 / 42 items shipped No backend Procedural art & audio Static deploy

Credits and licensing

Code and original art and sound are mine, released under the MIT license. Sound effects are synthesized at runtime, so no audio files ship for them. The background music is a set of Creative Commons CC0 (public domain) tracks from OpenGameArt.org, selected by age and biome. CC0 needs no attribution; the authors are credited in the project's CREDITS file out of respect for their work.