System Architecture
Web Engine Dev is a modular, composable web game engine ecosystem built with TypeScript. It follows a "Bring Your Own Engine" (BYOE) philosophy where every package can be used independently or combined with others. All packages are published under the @web-engine-dev scope.
Design Principles
The engine is built on five core architectural principles:
- Modular and Composable -- Every package works standalone or together. No monolithic engine lock-in.
- Data-Oriented Design -- All game state lives in the ECS World. Components are stored in contiguous typed arrays. Systems are pure functions that iterate data. See Data-Oriented Design for details.
- WebGPU-First -- The rendering pipeline standardizes on WebGPU runtime execution. See ADR-003.
- Type-Safe -- Full TypeScript strict mode with
noUncheckedIndexedAccess, branded types, and discriminated unions to catch errors at compile time. - Production-Ready -- 80%+ test coverage enforced per package, with performance-critical paths designed for zero-allocation steady state.
Monorepo Structure
The repository is organized into packages (libraries) and apps (runnable applications):
@web-engine-dev/monorepo
├── packages/
│ ├── core/ # Foundation (math, ecs, events, time, scheduler, hierarchy,
│ │ # resources, change-detection, serialization, reflection,
│ │ # scripting, splines)
│ ├── systems/ # Runtime systems (input, audio, physics2d, physics3d,
│ │ # physics2d-rapier, physics3d-rapier, animation, spatial,
│ │ # netcode, netcode-ecs, netcode-server, matchmaking,
│ │ # character, gesture, cloth, ragdoll, destruction)
│ ├── rendering/ # Graphics (render-graph, renderer, shader-compiler, gltf,
│ │ # particles, sprites, text, terrain, tilemap, ui, vfx, gizmos)
│ ├── gameplay/ # Game logic (ai, ai-ml, pathfinding, procgen, state)
│ ├── infrastructure/ # Support (assets, asset-pipeline, scene, prefab, save,
│ │ # streaming, pooling, debug, hot-reload, build, storage,
│ │ # level, binary-compression, texture-compression,
│ │ # geometry-compression)
│ ├── meta/ # Utilities (signals, tween, timer, camera, replay, devtools,
│ │ # engine, benchmark, screenshot, timeline, design-tokens,
│ │ # editor-core, editor-ui, editor-viewport)
│ ├── player/ # Player features (i18n, achievements, telemetry, accessibility)
│ ├── platform/ # Platform services (analytics, social, monetization, ugc,
│ │ # moderation, discovery, sandbox, embed, publishing)
│ ├── runtime/ # Runtime targets (mobile, pwa, xr)
│ └── games/ # Reference games (space-shooter)
├── apps/
│ ├── playground/ # Interactive demo environment (Vite-based)
│ ├── editor/ # Web-based visual editor (React + dockview)
│ ├── editor-server/ # Editor backend server
│ ├── docs/ # Documentation site (VitePress)
│ ├── examples/ # Example applications
│ ├── benchmarks/ # Performance benchmarks
│ └── server/ # Game server
├── shared/ # Shared configs (tsup, vitest, tsconfig)
├── tools/ # Developer tooling (MCP server, visual diff)
└── testing/ # Integration test infrastructurePackage Categories
| Category | Package Count | Purpose |
|---|---|---|
| Core | 12 | Foundation types, math, ECS, events, time, scheduling |
| Systems | 17 | Runtime systems for input, physics, audio, networking |
| Rendering | 12 | Graphics pipeline, shaders, particles, sprites, text, UI |
| Gameplay | 5 | AI, pathfinding, procedural generation, state machines |
| Infrastructure | 15 | Assets, scenes, prefabs, serialization, builds |
| Meta | 16 | Engine umbrella, editor, utilities, dev tools |
| Player | 4 | Accessibility, i18n, achievements, telemetry |
| Platform | 9 | Analytics, social, monetization, UGC, publishing |
| Runtime | 3 | Mobile, PWA, XR deployment targets |
Dependency Layer Model
Packages are organized into strict layers numbered 0 through 9. A package may only depend on packages in lower layers. No upward imports. No circular dependencies.
Layer Details
| Layer | Name | Packages | Description |
|---|---|---|---|
| 0 | Foundation | math | Zero dependencies. Pure computation. Vectors, matrices, quaternions, geometry primitives. |
| 1 | Primitives | ecs, events, time, scheduler, hierarchy, resources, change-detection | Standalone primitives usable without the ECS. Each provides a focused capability. |
| 2 | Integration | serialization, reflection, splines, scripting | Builds on Layer 1 for data processing, type introspection, and scripting lifecycle. |
| 3 | System Primitives | input, audio, spatial | Core runtime systems. May use ECS or run standalone. |
| 4 | Simulation | physics2d, physics3d, physics2d-rapier, physics3d-rapier, cloth, ragdoll, destruction | Physics and simulation. Collision detection via Rapier (ADR-005). |
| 5 | Animation | animation, character, gesture | Animation systems and character controllers. |
| 6 | Render Infrastructure | render-graph, shader-compiler | Render graph DAG scheduling and WGSL-to-GLSL transpilation. |
| 7 | Rendering Output | renderer, gltf, particles, sprites, text, terrain, tilemap, ui, vfx, gizmos | Visual output systems. The renderer is the core graphics package. |
| 8 | Content & Editor | scene, prefab, save, assets, netcode, netcode-ecs, netcode-server, ai, pathfinding, procgen, state, streaming, level, build, editor-core, editor-ui | Content management, gameplay, networking, and editor infrastructure. |
| 9 | Umbrella | engine | Re-exports all packages as namespaced modules. |
Layer Rules
- No upward imports -- A Layer N package may depend on Layer N-1 or below, never on its own layer or above.
- No circular dependencies -- Enforced by ESLint
import/no-cycleand the dependency layer model. - Interface segregation -- Systems with swappable backends (physics, audio) define interfaces at the abstract layer and implementations at the concrete layer (e.g.,
physics2ddefines the interface,physics2d-rapierprovides the Rapier implementation).
The Engine Umbrella Package
The @web-engine-dev/engine package at Layer 9 re-exports all modules as namespaces, providing a single import for the entire engine:
// Import namespaced modules
import { Math, ECS, Input, Audio, Renderer } from '@web-engine-dev/engine';
const vec = Math.vec3(1, 2, 3);
const world = new ECS.World();For tree-shaking and fine-grained control over bundle size, import individual packages directly:
import { Vec3, Mat4 } from '@web-engine-dev/math';
import { World, Entity } from '@web-engine-dev/ecs';The engine package also provides:
| Export | Description |
|---|---|
Engine, createEngine | Core engine class and factory |
Presets (Game2DPreset, Game3DPreset, etc.) | Pre-configured engine setups for common use cases |
definePlugin, PluginRegistry | Plugin lifecycle system |
Built-in plugins (RenderPlugin, InputPlugin, etc.) | Ready-to-use system plugins |
registerAllComponents | Scene component registration for serialization |
DiagnosticRegistry | Runtime diagnostic event system |
createDebugBridge | MCP/DevTools integration for AI-assisted debugging |
ConfigRegistry | CVar-like configuration system |
EngineSets, CoreSchedule | System scheduling and registration |
Engine Presets
Presets provide pre-configured engine setups for common game types:
import { createEngineFromPreset } from '@web-engine-dev/engine';
// Quick start with sensible defaults
const engine = await createEngineFromPreset('game3d', { canvas });Available presets: minimal, game2d, game3d, highPerformance, turnBased, simulation, mobile, headless.
Build System and Toolchain
Build Pipeline
Every package builds through the same pipeline:
All packages publish as dual ESM + CJS (ADR-002) with TypeScript declarations:
{
"type": "module",
"main": "dist/index.cjs",
"module": "dist/index.js",
"types": "dist/index.d.ts",
"exports": {
".": {
"import": { "types": "./dist/index.d.ts", "default": "./dist/index.js" },
"require": { "types": "./dist/index.d.cts", "default": "./dist/index.cjs" }
}
}
}TypeScript Configuration
| Setting | Value | Rationale |
|---|---|---|
target | ES2022 | Modern features (top-level await, private fields) |
module | ESNext | Tree-shakeable output |
moduleResolution | bundler | Modern bundler support |
strict | true | Full strict mode |
noUncheckedIndexedAccess | true | Array/object index safety |
verbatimModuleSyntax | true | Enforce import type for type-only imports |
isolatedModules | true | Required for tsup/esbuild |
Shared configuration is centralized in the shared/ directory:
shared/tsup.config.ts-- ESM + CJS, dts generation, ES2022 target, tree-shakingshared/vitest.config.ts-- Node environment, v8 coverage,globals: falseshared/tsconfig.package.json-- Extendstsconfig.base.json, strict modeshared/vitest-diagnostic-setup.ts-- Auto-catchesinvariant()violations in tests
Turborepo Orchestration
Key commands:
pnpm build # Build all packages (dependency-aware, cached)
pnpm build:affected # Build only packages changed since origin/main
pnpm test # Run all tests
pnpm test:affected # Test only changed packages
pnpm typecheck # Type-check all packages
pnpm lint # Lint all packagesCode Quality Tools
| Tool | Purpose |
|---|---|
| ESLint 9 (flat config) | Import ordering, consistent-type-imports, no-console restriction |
| Prettier | Consistent formatting (single quotes, 2-space indent) |
| Vitest | Unit tests with v8 coverage provider |
| Playwright | Browser integration tests and visual regression |
| Changesets | Semver versioning with automated changelog generation |
Versioning
Packages are published under the @web-engine-dev/* scope using Changesets for version management:
- Run
pnpm changesetto describe changes - Select affected packages and semver bump type
- Changesets are committed and processed on release
Linked packages version together (e.g., physics2d + physics3d).
How Packages Compose
The engine's composability manifests at three levels:
1. Package-Level Composition
Import only what you need. Dependencies are pulled in automatically:
// Using just math -- zero other dependencies
import { Vec3, Mat4 } from '@web-engine-dev/math';
// Using ECS -- pulls in events, time, scheduler, resources, change-detection
import { World, defineComponent, defineSystem } from '@web-engine-dev/ecs';
// Using renderer -- pulls in render-graph, shader-compiler, math, ecs
import { createDevice, ForwardRenderer } from '@web-engine-dev/renderer';2. Plugin-Level Composition
The engine plugin system lets you compose systems declaratively:
import { createEngine, RenderPlugin, InputPlugin, Physics3DPlugin } from '@web-engine-dev/engine';
const engine = await createEngine({
canvas,
plugins: [
RenderPlugin({ enableSystems: true }),
InputPlugin(),
Physics3DPlugin(),
],
});3. ECS-Level Composition
All systems coordinate through the ECS World. Components and systems from different packages interoperate seamlessly:
// Renderer components + Physics components + Custom components on the same entity
const entity = world.spawn(
Transform3D.create({ position: [0, 1, 0] }),
MeshComponent.create({ meshId }),
RigidBody3D.create({ type: 'dynamic' }),
Health.create({ value: 100 }),
);Cross-Cutting Patterns
Hexagonal Architecture (Ports and Adapters)
Systems with swappable backends use the ports-and-adapters pattern. The core defines interfaces (ports); concrete implementations connect as adapters:
physics2d (interface) ───> physics2d-rapier (Rapier WASM adapter)
physics3d (interface) ───> physics3d-rapier (Rapier WASM adapter)
render-graph (abstraction) ───> WebGPU backendThis enables backend substitution without changing consumer code. See ADR-004 for the third-party integration strategy.
ECS as Central Coordinator
All game state lives in the ECS World (ADR-007). Systems from different packages communicate through:
- Components -- Shared data attached to entities
- Resources -- Global singletons accessible by any system
- Events -- Double-buffered event queues for system-to-system communication
- Observers -- Lifecycle triggers (OnAdd, OnRemove, OnChange) for reactive updates
- Commands -- Deferred world mutations that prevent iterator invalidation
Plugin System
Plugins encapsulate system registration, resource initialization, and lifecycle management:
const MyPlugin = definePlugin({
name: 'my-plugin',
build(app) {
// Register components, systems, resources
app.addSystem(MySystem);
app.insertResource(MyResource, initialValue);
},
});Further Reading
- Data-Oriented Design -- Why DOD matters for a web game engine
- Rendering Pipeline -- Forward rendering, render graph, shader compilation
- Architecture Decisions -- All ADRs documenting key design choices
- ECS Concepts -- Entities, components, systems, queries
- Engine Package -- The umbrella package API reference