Architecture Overview
Awaken is an engine-first, editor-as-a-client system: a set of framework-free engine packages with a clean public API, driven by a React editor that only ever talks to that API - and by the shipped player through the same API. This section maps how the code is laid out, how it builds, and how it is tested.
Engine-first, editor-as-a-client
The core idea is a strict separation between the engine (what draws, simulates, and runs your game) and the editor (the tool you use to author it).
- The engine is four TypeScript packages under
packages/*-@awaken/core,@awaken/render,@awaken/assets, and@awaken/runtime. They have no UI framework in them at all. They expose a public API (ECS, renderer, importers, play loop) and nothing else. - The editor is a React app under
apps/editor. It is a client of the engine: it constructs aWorld, aRenderer, and aRuntime, and drives them through their public methods. It never reaches inside the engine's internals. - The player is a second, much thinner client under
apps/player. It boots a saved scene and runs it, with no editor UI.
Because the editor authors through the same API the engine exposes to a shipped game, edit == play == ship: the object you drag in the Viewport is the object the runtime ticks in Play, which is the object the player runs after export.
The one-way dependency direction
Dependencies flow one way, from the base outward. Nothing in the engine ever depends on anything above it, and nothing anywhere depends on the editor.
@awaken/coresits at the bottom with zero dependencies - pure ECS, math, and serialization.@awaken/renderdepends only oncore;@awaken/assetsdepends oncoreandrender;@awaken/runtimedepends oncoreplus the Rapier physics WASM.- Both apps depend on all four packages, and nothing depends on an app.
This is not just tidiness. The one-way graph is what lets Play mode run the actual shipping runtime rather than an editor-flavoured imitation, and it is what keeps the engine reusable outside the editor.
See Monorepo & Packages for the exact package boundaries and dependency rules.
Why the engine never imports React
The engine packages never import React (or any UI framework). This is a hard rule, and it buys three concrete things:
- The runtime is portable.
@awaken/player- a shipped game - pulls in@awaken/runtimeand gets the play loop, scripts, and physics with zero React in the bundle. If the engine imported React, every exported game would ship a UI framework it never uses. - Play mode is honest. The editor's Play button and the shipped player boot the same
Runtimeclass. If Play depended on editor-only React state, "it worked in the editor" would stop meaning "it works when shipped." - The seam is enforceable. The editor's dev server even reloads the whole page when engine code changes, because the editor store (which owns the
PlayControllerandScriptHost) must be rebuilt from fresh engine code rather than hot-patched - see theawaken-reload-on-engine-changeplugin in Monorepo & Packages.
If you find yourself wanting to import React inside packages/*, that is the signal that a responsibility is on the wrong side of the seam.
One runtime, two front-ends
The same @awaken/runtime powers editor Play and the exported player. The difference is only what wraps it.
- In the editor, Play mode snapshots the world on Play, ticks the runtime, and restores the snapshot exactly on Stop, so play-time changes never persist.
- In a shipped game,
@awaken/playerskips the snapshot and just runs - the scene is the game.
Both call into the same code path, which is why a script or physics behaviour that runs in Play runs identically in the exported game.
What this section covers
- Monorepo & Packages - the pnpm workspace layout, the four
@awaken/*packages and the two apps, dependency rules, the shared TypeScript config (raw-source consumption, the~5.6.3pin), and the editor's custom Vite plugins. - Build & Player Template - the build pipeline, how
build:player-templateproduces the single-file player that exports embed, and the critical rule that a stale template ships stale game code. - Testing - Vitest unit tests, the headless Deno WebGPU shader/pipeline check, the Puppeteer smoke tests, and the lint / Husky gates, with the exact command for each.
📸 Screenshot - save as img/architecture-overview.png
The editor open with a scene, and a second browser tab showing the same scene running as an exported game.html - side by side to illustrate "edit == play == ship."