Wiki · Devlog

Developer Log

2026 update: CONTRABAND now ships as the official Steam PC release. The playable browser build has been retired from this site. Several posts below were written during the web prototype era and are kept as design history — check each article’s note for what still applies.

This section is for people curious about how CONTRABAND was made. It’s a solo project built in modular JavaScript, CSS, and HTML — no Unity or Godot — with Three.js handling real-time 3D combat and galaxy views.

Why no traditional engine?

The obvious choice would be Unity, Godot, or a full browser-game framework. I chose a custom JS stack because:

  1. Lean runtime. Core game logic stayed modular ES modules instead of a monolithic engine loop.
  2. Fast iteration. Hot-reload-friendly files during the long web prototype phase.
  3. Modular architecture. Each system is its own file with a clean boundary. Combat doesn’t know about stories. Stories don’t know about combat. They communicate via an EventBus.
  4. Portable stack. The same systems later wrapped into the Steam PC build without rewriting narrative or economy code.

Architecture

The game has five conceptual layers:

Key design decisions

EventBus over direct coupling

Every system emits events (combat:victory, story:decision, galaxy:jump) instead of directly calling other systems. Adding a feature like Analytics is just “listen to existing events.”

State as single source of truth

There’s one GameState instance. Saving is JSON.stringify(state). Loading is JSON.parse + merge. No complex reducers.

Audio evolution

Early browser builds used procedural ambient layers via Web Audio API. The Steam release adds a 21-track authored soundtrack (Across the Silent Stars) while keeping lightweight synthesized SFX for combat and UI. See the Procedural Audio post for the original approach.

ASCII portraits

Character portraits are ASCII art, not images. Each NPC has a small pattern rendered in the gold accent color — zero portrait assets, instant load.

Development stats (current)

Individual devlog posts