Skip to content

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 a World, a Renderer, and a Runtime, 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/core sits at the bottom with zero dependencies - pure ECS, math, and serialization.
  • @awaken/render depends only on core; @awaken/assets depends on core and render; @awaken/runtime depends on core plus 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:

  1. The runtime is portable. @awaken/player - a shipped game - pulls in @awaken/runtime and 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.
  2. Play mode is honest. The editor's Play button and the shipped player boot the same Runtime class. If Play depended on editor-only React state, "it worked in the editor" would stop meaning "it works when shipped."
  3. 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 PlayController and ScriptHost) must be rebuilt from fresh engine code rather than hot-patched - see the awaken-reload-on-engine-change plugin 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/player skips 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.3 pin), and the editor's custom Vite plugins.
  • Build & Player Template - the build pipeline, how build:player-template produces 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."

See also

Awaken — browser-native WebGPU game engine.