This project is an experiment in building a lightweight open-world game engine in modern vanilla JavaScript. It started with early top-down open-world games (GTA 1/2) and PS2-era design principles as a spark — and grew into a noir, 1930s–40s city sandbox focused on architecture, procedural content, and the illusion of a living world rather than photorealism or recreating any commercial title.
Built around an ECS-lite (Entity-Component-System) core, it shares one world state across classic 2D Canvas and Three.js 3D, with a live, tweakable film / kinescope post-process — grain, flicker, scratches, and all.
In ~15 seconds, this README should answer:
- What is it? — A modular open-world sandbox engine (2D Canvas + 3D Three.js) on one shared world state.
- Why does it exist? — To explore and showcase lightweight game-engine architecture, procedural content generation and performance-oriented design in modern JavaScript.
- What does it demonstrate? — See the section below.
- ECS-lite architecture design
- Game loop design
- Event-driven systems
- 2D and 3D rendering pipelines sharing one world
- Three.js post-processing
- Procedural content generation
- Automated testing (including UI security regression)
- Internationalization (custom catalog, 5 locales)
- CI/CD deployment (GitHub Pages)
- Performance-oriented, separation-of-concerns design
- Procedural 1930s–40s vehicles —
VehicleModelFactorybuilds sedans, coupes, and panel vans from data-driven archetypes (rounded fenders, running boards, exposed round headlamps, whitewall tires) instead of licensed or overly detailed 3D assets. - Retro film / kinescope post-processing — a custom
ShaderPasswith film grain, gate jitter, flicker, animated scratches, dust, and vignette, plus four live-tweakable presets (off,subtle,classic,ruined). - Full internationalization — UI strings across 5 locales (
pl,en,de,es,fr) via a small customI18ncatalog system. - Adaptive input UI — on-screen touch controls auto-detected via pointer/hover media queries, with manual override in Settings.
- Dual rendering modes — 2D top-down Canvas and full 3D (Three.js) sharing the same ECS world/state.
- Security-conscious UI layer — HUD rendering has dedicated regression tests guarding against XSS through entity/display data.
/game
├── src/ # Source code of the engine & demo sandbox
│ ├── core/ # Engine core (Game Loop, Time, EventBus, GameState)
│ ├── entities/ # Data-only entities (Entity, Player, NPC, Car)
│ ├── systems/ # Logic systems (Movement, AI, Render, Mission, Audio, Vehicle Models, Retro Film, etc.)
│ ├── world/ # Environment (World, Camera, Tilemap, Waypoints, Grid)
│ ├── input/ # Input management (InputManager)
│ ├── i18n/ # Translation catalogs & I18n helper (pl/en/de/es/fr)
│ ├── ui/ # UI layers (HUD, MenuScreen, Options, UISettings)
│ └── main.js # Application entry point / bootstrap
└── .github/
└── workflows/ # CI/CD workflows (GitHub Pages deployment)
The core design strictly separates data from logic, allowing flexibility and targeted optimization.
- Entities are purely containers of data components (such as
transform,physics,visual,ai). They do not contain gameplay logic. - Systems are stateless, single-purpose logic processors. They query and manipulate components from entities stored in the
Worldbut do not store state themselves. - EventBus acts as the central decoupled communication layer. Systems communicate exclusively using events (
EventBus.publish/subscribe), preventing hard coupling.
- Entities contain ZERO gameplay logic. No update loops inside entity classes.
- Systems are stateless. They do not store entity states; they perform transformations on the data they receive from the current
Worldtick. - Extensibility through composition. New mechanics or features must always be introduced as new Systems, rather than modifying existing ones.
- No direct coupling. System-to-system communications are managed only via the
EventBus. - RenderSystem separation. The renderer (both 2D and 3D) is purely a visualizer—it reads transform/visual data and renders it, remaining completely oblivious to gameplay logic.
The engine intentionally favors inexpensive visual and architectural techniques over heavy simulation.
Examples include:
- Procedural vehicle generation instead of detailed meshes.
- Shared world state across 2D and 3D renderers.
- Stateless systems for predictable updates.
- Event-driven communication to reduce coupling.
- Lightweight post-processing effects designed for rapid iteration.
The goal is not photorealism, but maximizing perceived world complexity per unit of computational cost.
- Logic & Runtime: Vanilla ES Modules JavaScript
- Graphics (2D): HTML5 Canvas API
- Graphics (3D): Three.js (custom
EffectComposerpost-processing pipeline) - Internationalization: Custom lightweight
I18ncatalog system (5 locales) - Build Tool & Dev Server: Vite
- Testing Suite: Vitest + JSDOM
- Node.js (v20 or higher recommended)
- npm (comes bundled with Node)
Clone the repository and install dependencies:
npm installStart the Vite development server:
npm run devThe application will be running locally at http://localhost:5173/ (or the port specified in terminal output).
Compile the production bundle (emitted into src/dist):
npm run buildI employ a robust testing suite using Vitest and JSDOM to ensure logic correctness across all layers of the ECS engine — including dedicated security regression tests (e.g. XSS-injection guards on HUD text rendering).
Run all unit and integration tests:
npm testWatch for changes (during development):
npm run test:watch| Layer | What is Tested | Method |
|---|---|---|
Core (EventBus, Time, GameState) |
100% public API functionality | Unit testing, argument validation |
| Entities | Default components upon instantiation | Constructor verification (e.g., does Car have physics?) |
| Systems | Logic behavior inside update(dt) |
Mock entities with components, assert state changes after update |
| Renderers | Sorting logic, layers, and viewport culling | Logical checks via unit tests; visual correctness verified manually |
| UI Security | Injection safety in dynamically rendered text (HUD) | Regression tests with malicious payloads (<img onerror>, <svg onload>, etc.) |
"Low effort / High impact" — maximize the illusion of a living, breathing 1930s–40s city with minimal computational complexity.
- Retro inspiration (PS2 era + golden-age noir): procedural geometry, canvas-based facades, and shader post-processing instead of heavy simulation or licensed art.
- Arcade feel: vehicle and character controls prioritize player expectations over strict real-world physics.
- Rapid iteration: get a feature working first, polish visually, optimize only when bottlenecks appear.
- Engine over clone: the sandbox demos architecture — modular systems, shared world state, extensible pipelines. Early open-world games were only a starting inspiration; the result is its own noir city experiment.

