The layer beneath the game.
AI-first, pure-data ECS 2D engine — headless core with an optional PixiJS bridge.
Quick start · Why · Reference games · Identity
npx create-sub-engine my-game
cd my-game && npm install && npm run devMost 2D engines make you choose between a simulation you can test and a game you can look at. Sub-Engine doesn't: the simulation is pure data and runs in Node, and PixiJS is a bridge you attach when you want pixels.
- Entities are integers. There is no object graph to serialise, diff or inspect. An agent can read the entire world state as JSON and write it back.
- The core has zero dependencies.
@sub-engine/corehas nodependenciesfield at all and will not touchwindow. Simulation logic runs in a test, a CI job or a game server. - Systems are pure functions.
(registry, dt) => registry. No hidden state, no lifecycle, nothing to mock. - Every write is validated. Schemas are registered once; a typo in a component name throws at the call site instead of at 3am.
- Typing is structural.
Registry<M>is parameterised by yourComponentMap, sogetEntitiesWithdestructures to exactly the components the query asked for.
npm install @sub-engine/pixi pixi.jsimport { createRegistry, registerSchema } from '@sub-engine/core'
import { createResponsiveContainer, DebugOverlay } from '@sub-engine/pixi'
import { Application } from 'pixi.js'
registerSchema('Position', { x: { type: 'number', required: true }, y: { type: 'number', required: true } })
const app = new Application()
await app.init({ resizeTo: window })
const registry = createRegistry()
const player = registry.createEntity()
registry.addComponent(player, 'Position', { x: 0, y: 0 })- Pure-data ECS — Entities are integers. Components are flat JSON. Systems are pure functions
(registry, dt) => registry. - Headless-first — Run simulation in Node.js. No browser needed for game logic.
- Fully typed — Generic
Registry<M>parameterized by yourComponentMapinterface. - Schema validation — Every write validates against registered schemas. Catches typos instantly.
- Built-in systems — Movement, combat, camera (follow + clamp + zoom), AABB collision (groups/masks), pathfinding (A*), particles, audio, z-ordering, tile maps.
- Utilities — Spatial hash grid, fixed-timestep game loop (
GameLoop), entity factory, undo/redo, serialization, debug overlay (F12). - Tiled support — Load
.tmj/.tsjmaps. Spritesheet + collection-of-images tilesets. Procedural map generation. - Columnar storage — Optional
createColumnarRegistry()for 11x faster bulk numeric operations. - AI-friendly — Clean JSON data, no classes, no hidden state.
┌─────────────────────┐ ┌──────────────────────────┐
│ @sub-engine/core │ │ @sub-engine/pixi │
│ │ │ │
│ Registry │ │ createResponsiveContainer│
│ Entity CRUD │ │ createAnimManager │
│ Schema validation │ │ createTiledMapRenderer │
│ Built-in schemas │ │ DebugOverlay │
│ Common systems │ │ (depends on core) │
│ Utilities │ │ │
└─────────────────────┘ └──────────────────────────┘
▲ ▲
│ │
└──────── game code ─────────┘
(imports both)
The games/ directory contains 10 complete reference implementations. Run any with:
npm run dev:td npm run dev:castle npm run dev:movement
npm run dev:combat npm run dev:map npm run dev:input
npm run dev:serial npm run dev:character npm run dev:camera
npm run dev:tilemap
Read games/tower-defense/ first — it's the most complete reference.
npm install # Install workspace
npm run dev # Root playground (arrow key movement demo)
npm run typecheck # Full type check (0 errors)
npm test # Run 201 engine tests
npm run build:pkgs # Build both packages to dist/├── packages/
│ ├── core/ # @sub-engine/core (headless ECS)
│ ├── pixi/ # @sub-engine/pixi (rendering bridge)
│ └── create-app/ # create-sub-engine CLI + template
├── games/ # 10 reference implementations
├── tests/ # 201 engine tests (vitest)
├── src/ # Root dev playground
├── package.json # Workspace root
└── tsconfig.json
The mark is a block in section — two seams the data rests on, one gate every system crosses, one entity in flight.
Assets live in assets/ and the full system — exploration,
construction, specification and the rules it ships under — is in
brand/.
assets/logo-lockup.svg |
|
assets/logo-mark.svg — 32px and up |
|
assets/logo-mark-inverse.svg — dark surfaces |
|
assets/favicon.svg — the designed 16px cut |
Solar orange #FF5A1F is inherited from Type-3 Studio;
everything else is Sub-Engine's own.
MIT
