# @f0rbit/forge TypeScript game engine on PIXI v8. Single-package, ESM-only, deterministic kernel + quarantined renderer. - Repo: https://github.com/f0rbit/forge - Docs: https://f0rbit.github.io/forge - License: MIT Five subpath exports: - `@f0rbit/forge` — engine kernel (ECS, schedule, time, rng, resources, input, replay, anim, snapshot, palette, debug) - `@f0rbit/forge/pixi` — PIXI v8 integration (boot, camera, sprite/anim sync, palette UI, debug overlay) - `@f0rbit/forge/debug` — standalone debug subsystem - `@f0rbit/forge/storage` — persistence (snapshotter, Store, save/load, engine_store) - `@f0rbit/forge/presets` — pre-built input bindings (movement_2d, movement_4way, movement_8way, platformer, twinstick, menu, ...) Hard rules: - ESM-only. Node 20+ / modern browsers. - Determinism contract: no Date.now / Math.random / setTimeout outside `src/pixi`. Use `time` and `rng` resources. - Errors as values via `@f0rbit/corpus` Result. Never throws. - snake_case data fields, camelCase functions, PascalCase types, kebab-case files. ## Installation ```sh bun add @f0rbit/forge bun add pixi.js # only needed for @f0rbit/forge/pixi ``` ## Subpath imports ```ts import { /* engine core */ } from "@f0rbit/forge"; import { presets } from "@f0rbit/forge/presets"; import { /* debug types */ } from "@f0rbit/forge/debug"; import { engine_store } from "@f0rbit/forge/storage"; import { boot, sprite_c } from "@f0rbit/forge/pixi"; ``` ## Quick start — minimal hello-sprite ```ts import { component, pos_c } from "@f0rbit/forge"; import { boot, sprite_c } from "@f0rbit/forge/pixi"; import { presets } from "@f0rbit/forge/presets"; const player_c = component("player"); const r = await boot({ mount: "#root", window: { width: globalThis.innerWidth, height: globalThis.innerHeight }, camera: { design: { width: 320, height: 180 }, mode: "letterbox" }, bindings: presets.movement_2d, }); if (!r.ok) throw new Error(`boot failed: ${r.error.kind}`); const app = r.value; app.world.spawn( [pos_c, { x: 160, y: 90 }], [player_c, true], [sprite_c, { texture: "__default__", frame: "__default_0__", anchor: { x: 0.5, y: 0.5 } }], ); app.schedule.add("update", (w, ctx) => { const [dx, dy] = ctx.input.vector("move.x", "move.y"); for (const [, p] of w.query([pos_c, player_c] as const)) { p.x += dx * 60 * ctx.time.fixed_dt; p.y += dy * 60 * ctx.time.fixed_dt; } }, "player.move"); app.start(); ``` ## Headless test harness ```ts import { harness, pos_c, component } from "@f0rbit/forge"; const player_c = component("player"); const h = harness({ seed: 1, fixed_dt: 1 / 60 }); h.world.spawn([pos_c, { x: 0, y: 0 }], [player_c, true]); h.schedule.add("update", (w, ctx) => { for (const [, p] of w.query([pos_c, player_c] as const)) { p.x += ctx.time.fixed_dt; } }, "tick"); h.run(60); // world is now 60 ticks ahead, deterministic ``` ## Determinism contract - Browser non-determinism (Date.now, Math.random, setTimeout, requestAnimationFrame) is allowed only inside `src/pixi/`. - The kernel uses `time` (fixed-step accumulator) and `rng` (seeded splittable PRNG) resources for all timing and randomness. - Schedule order is insertion-stable. Queries iterate the smallest store first, sorted by entity id. - Replays serialise the action event stream + the seed; replaying a recorded session is byte-identical to the original. - The `tools/no-throws.ts` lint script enforces no-throws, no-banned-globals outside `src/pixi/`. ## Public surface ### @f0rbit/forge Engine kernel — ECS, schedule, time, rng, resources, input, replay, anim, snapshot, palette, debug. #### World Sparse-set ECS with typed components and queries. - **world** (factory) — `() => World` — Create a fresh world. Returns { spawn, despawn, has, get, set, remove, query, count, ... }. - **component** (factory) — `(name: string) => Component` — Create a typed component descriptor backed by a global symbol (Symbol.for). - **internal** (constant) — `unique symbol` — Escape-hatch key for World[internal]. Used by snapshot/restore and tests. - **pos_c** (constant) — `Component<{ x: number; y: number }>` — Canonical position component used by PIXI sprite + anim sync systems. - **world.spawn_many** (function) — `(count, factory) | (specs) => readonly Id[]` — Bulk-spawn entities. Two forms: `(count, factory)` calls `factory(i)` for `i` in `[0, count)`; `(specs[])` spawns one entity per element of the array. Returns ids in spawn order. - **world.despawn_marked** (function) — `(...markers: readonly Component[]) => number` — Bulk-despawn every entity that has ALL of the given marker components. Snapshots before mutating; safe to call mid-iteration of unrelated queries. Returns count despawned. - **world.query** (function) — `(cs: C, opts?: QueryOpts) => Query` — Query the world. `Component` markers are auto-elided from the yielded tuple, so the destructure shape is only the data components plus `Id`. - **World** (type) — Core ECS interface returned by world(). - **Component** (type) — `Component` — Branded descriptor pairing a name, key, and data type. - **Id** (type) — Branded number identifying an entity. - **Query** (type) — `Query` — Iterable query result with .each and .collect helpers. Marker components are elided from the yielded tuple. - **QueryOpts** (type) — `{ without?: Component[] }` — Optional excludes for query(). - **ComponentTuple** (type) — Helper that maps a tuple of Component to a tuple of T (markers elided). - **SpawnFactory** (type) — `(i: number) => readonly [Component, any][]` — Factory passed to `spawn_many(count, factory)`. - **WorldInternal** (type) — Escape-hatch interface exposing internal stores + `spawn_at` for snapshot/restore. #### Schedule Insertion-ordered system runner with named stages. - **schedule** (factory) — `() => Schedule` — Create a schedule. Default stages: startup, pre, update, post, render. - **schedule.add** (function) — `(stage: Stage, sys: System, opts?: AddOpts | string) => Schedule` — Register a system. Pass a string for a name, or `{ every?, phase?, name? }` to gate by tick modulus. `every: 1` runs every tick; `every: N` runs every Nth tick offset by `phase`. - **Schedule** (type) — { add, remove, tick, run, stages }. - **Stage** (type) — `"startup" | "pre" | "update" | "post" | "render" | string` — Built-in or custom stage name. - **System** (type) — `(w: World, ctx: Ctx) => void` — A system function — receives the world and the per-tick context. - **AddOpts** (type) — `{ every?: number; phase?: number; name?: string }` — Options for schedule.add. `every` gates the system to every Nth tick; `phase` offsets the gate so two periodic systems can interleave. - **Ctx** (type) — `{ time, rng, res, input, debug, palette, store? }` — Per-tick context handed to every system. #### Time Deterministic fixed-step time resource. - **time** (factory) — `(opts?: { fixed_dt?: number }) => Time` — Create a time resource. Default fixed_dt = 1/60. - **Time** (type) — `{ tick, fixed_dt, elapsed, alpha, scale, advance, restore }` — Fixed-step accumulator + scale + alpha. #### RNG Seeded splittable random. - **rng** (factory) — `(seed: number) => Rng` — Create a seeded RNG. Use .fork() for per-subsystem isolation. - **Rng** (type) — `{ seed, next, int, pick, fork, state, restore }` — Splittable PRNG. Same seed + same call sequence = same output. #### Resources Symbol-keyed registry for shared engine state. - **resources** (factory) — `() => Resources` — Create a resources registry. - **resource** (factory) — `(name: string) => ResKey` — Create a typed resource key (uses Symbol.for for cross-bundle identity). - **Resources** (type) — { has, get, set, delete }. - **ResKey** (type) — `ResKey` — Typed key for resources.get/set. #### Animation Per-entity sprite animation, atlas registry, and event buffer. - **anim** (factory) — `() => Anim` — Animation system controller — play, stop, advance, snapshot. - **anim_c** (constant) — `Component` — Per-entity animation state component. The `t` field is an internal accumulator (tracked for snapshot determinism); never read or write it from game code. - **atlas_registry_r** (constant) — `ResKey` — Resource key for the registered atlas sequences. Renamed from `atlas_registry` in v0.3.0 for `_r` suffix consistency. - **anim_events_r** (constant) — `ResKey` — Resource key for the per-tick anim event buffer. Renamed from `anim_events` in v0.3.0 for `_r` suffix consistency. - **Anim** (type) — Animation controller interface. - **AnimData** (type) — Per-entity animation state (sequence, frame, time, speed). The `t` field is internal — used for snapshot/restore determinism only. - **AnimEvent** (type) — Anim event union — frame_changed, finished, looped. - **AnimEventBuffer** (type) — Per-tick buffer of animation events; cleared each schedule.tick. - **AtlasFrame** (type) — Single frame in an atlas — texture alias + duration. - **AtlasRegistry** (type) — Map of alias → AtlasSequences. - **AtlasSequences** (type) — Map of sequence name → AtlasFrame[]. #### Follow Parent-child primitive — follower's pos = target.pos + offset every tick. - **follow_c** (constant) — `Component` — Per-entity follow component — `{ target, offset }`. Spawn alongside `pos_c` to attach an entity's position to another entity's position with an x/y offset. - **follow_system** (factory) — `(pos_component: Component<{ x, y }>) => System` — System that writes `target.pos + offset` back into each follower's pos every tick. Wired into `boot()` post-stage automatically; pass `opts.pos` to override the position component (defaults to `pos_c`). Silently skips followers whose target was despawned. - **Follow** (type) — `{ target: Id; offset: { x: number; y: number } }` — Follow component data — id of the entity to track and a fixed x/y world-space offset. #### Input Action-based input layer with bindings, sources, and rebinding. - **input** (factory) — `(bindings?: Bindings) => Input` — Create an input system. Pair with a source via input.source(...). - **noop_source** (factory) — `() => InputSource` — Source that emits nothing — useful for tests. - **scripted** (factory) — `(events) => InputSource` — Source that replays scripted RawInput events at given ticks. - **ticked** (factory) — `(fn) => InputSource` — Wrap a per-tick function as an input source. - **empty_bindings** (factory) — `() => Bindings` — Empty bindings object — { digital: {}, axes: {}, deadzone: 0.15 }. - **merge_bindings** (factory) — `(...bs: Bindings[]) => Bindings` — Merge bindings layers; later layers override earlier. - **Input** (type) — { bind, source, advance, pressed, just_pressed, just_released, axis, vector, ... }. - **ActionState** (type) — Per-action edge + value state. - **RawInput** (type) — Raw event union — key, pad button, pad axis. - **InputSource** (type) — { poll, dispose? } interface for input producers. - **PadIndex** (type) — Branded gamepad index (0–3). - **Bindings** (type) — { digital, axes, deadzone } — declarative action map. - **Trigger** (type) — Single digital trigger — key | pad.button | pad.axis. - **AxisBinding** (type) — Single axis binding — key.pair | pad.axis | pad.button.pair. - **Action** (type) — Action name string. #### Replay Deterministic record/playback of action events. - **replay** (namespace) — `{ record, play, save, load }` — Namespace bundling recorder, player, and save/load helpers. The schema lives at the top-level `replay_schema` export. - **replay_schema** (schema) — `ZodType` — Top-level Zod schema for a replay document. Use directly for validation or to derive types via `z.infer`. - **replay.record** (function) — `(input: Input, ctx: Ctx, opts?: { seed?: number }) => Recorder` — Record action transitions into a `ReplayDoc`. Reads `seed`, `fixed_dt`, `get_tick` from `ctx`. A low-level overload `(input, { seed, fixed_dt, get_tick })` is available for tests without a `Ctx`. - **ReplayDoc** (type) — Serialised replay document — meta + events. - **ActionEvent** (type) — { tick, action, kind, value? } event row. - **ReplayError** (type) — Replay error union — parse, mismatch, etc. - **Recorder** (type) — { record, snapshot, dispose } recorder handle. - **Player** (type) — { apply, exhausted } player handle. #### Snapshot Persistence kernel — serialise and restore world + resources. - **snapshotter** (factory) — `(opts?) => Snapshotter` — Create a snapshotter. Provides take() and restore(). - **snapshot_schema** (schema) — `ZodType` — Zod schema for a snapshot document. - **Snapshot** (type) — Serialised world + resources blob. - **SnapshotMeta** (type) — { version, created_at, ... } header. - **EntitySnap** (type) — Per-entity row in a Snapshot. - **Snapshotter** (type) — { take, restore }. - **TakeOpts** (type) — Options for snapshotter.take(). - **RestoreOpts** (type) — Options for snapshotter.restore(). #### Storage (re-exports) Convenience re-exports from @f0rbit/forge/storage. - **mem** (factory) — `(opts?: MemOpts) => Store` — In-memory Store backend. - **file** (factory) — `(opts: FileOpts) => Store` — File-system Store backend (Node). - **store** (factory) — `(opts: StoreOpts) => Store` — Generic Store with custom IO. - **save** (function) — `(snapshotter, store, slot) => Promise>` — Compose snapshotter + store: take a snapshot and persist it to a slot. - **load** (function) — `(snapshotter, store, slot) => Promise>` — Compose snapshotter + store: load a slot and restore the world. - **engine_store** (factory) — `(opts?: EngineStoreOpts) => EngineStore` — Default engine store wrapping bindings + prefs persistence. - **bindings_schema** (schema) — Zod schema for persisted Bindings. - **prefs_schema** (schema) — Zod schema for persisted Prefs. - **default_prefs** (constant) — `Prefs` — Sensible default preferences object. - **Store** (type) — `Store` — Generic persisted-store interface. - **Slot** (type) — Branded slot identifier (string). - **SaveHandle** (type) — Handle returned by store.open(slot). - **SaveSlot** (type) — Slot + value pair. - **StoreError** (type) — Store error union. - **MemOpts** (type) — Options for mem(). - **FileOpts** (type) — Options for file(). - **StoreOpts** (type) — Options for store(). - **SaveError** (type) — Error union for save/load. - **EngineStore** (type) — Default-engine save layer. - **EngineStoreOpts** (type) — Options for engine_store(). - **Prefs** (type) — User-facing engine preferences. #### Debug Frame-buffered draw commands, per-entity pins, and __DEV__ gating. - **debug** (factory) — `(opts?: DebugOpts) => Debug` — Create a debug subsystem. Use __dev__ to toggle production no-op. - **debug_noop** (factory) — `() => Debug` — No-op debug — drops every command. Used in production builds. - **is_dev** (function) — `() => boolean` — Read the __DEV__ gate from the global. - **Debug** (type) — { line, rect, text, pin, unpin, frame, stats, ... }. - **DebugOpts** (type) — Options for debug(). - **DebugCmd** (type) — Frame-buffered draw command union. - **DebugStats** (type) — { entities, frame_ms, draw_calls, ... }. - **Pin** (type) — Per-entity inspection pin. - **PinKind** (type) — Pin classification — info, warn, error. - **Color** (type) — Numeric RGB triple or named colour. - **Inspection** (type) — Inspector snapshot for a single entity. - **ComponentInspection** (type) — Per-component inspection row. #### Palette Command palette — register, search, run. - **palette** (factory) — `(opts?: PaletteOpts) => Palette` — Create a command palette controller. - **palette_noop** (factory) — `() => Palette` — No-op palette for production. - **builtins** (function) — `(deps: BuiltinDeps) => Command[]` — Built-in commands (toggle pause, set time scale, snapshot, ...). - **tokenise** (function) — `(line: string) => string[]` — Tokenise a palette command line. - **parse_line** (function) — `(line: string) => ParsedLine` — Parse a tokenised line into command + args. - **fuzzy_score** (function) — `(query: string, target: string) => number` — Fuzzy match score (0..1). - **fuzzy_rank** (function) — `(query, items) => SearchHit[]` — Rank items by fuzzy score against a query. - **Palette** (type) — { register, run, search, history, ... }. - **PaletteOpts** (type) — Options for palette(). - **Command** (type) — `Command` — Registered command — id, label, run. - **CommandError** (type) — Command run error. - **CommandRunner** (type) — `CommandRunner` — (args: A, ctx: Ctx) => Result. - **SearchHit** (type) — Search result row — { id, score }. - **BuiltinDeps** (type) — Dependencies needed by builtins(). - **ParsedLine** (type) — { command, args } parsed from a palette line. #### Math Tiny vector helpers. - **vec2** (factory) — `(x: number, y: number) => Vec2` — Create a Vec2 record. - **Vec2** (type) — `{ x: number; y: number }` — Plain 2D vector record. #### Errors Engine-wide error union. - **EngineError** (type) — Discriminated union of every kernel error kind. Used in Result. #### Test harness Headless test driver — run schedules, assert, snapshot. - **harness** (factory) — `(opts?: HarnessOpts) => Harness` — Create a headless harness for integration tests. Wires world, schedule, time, rng, resources, input. - **Harness** (type) — { world, schedule, time, rng, res, input, tick, run, dispose }. - **HarnessOpts** (type) — Options for harness() — seed, fixed_dt, bindings, resources. #### Version - **VERSION** (constant) — `"0.0.1"` — Compile-time package version constant. ### @f0rbit/forge/pixi PIXI v8 integration — boot(), camera, sprite + anim sync, palette UI, debug overlay. The only subpath allowed to import pixi.js. #### Boot Wire-up factory that returns a fully-composed App. - **boot** (factory) — `(opts: BootOpts) => Promise>` — Mount, wire renderer + camera + assets + input + palette + debug, return a started App. - **App** (type) — Started application handle — { world, schedule, ..., tick, start, stop, dispose }. - **BootOpts** (type) — Boot options — mount, camera, bindings, assets, dev gate, optional overrides. - **BootError** (type) — Boot error union — mount_not_found, render_failed, asset_failed. - **AssetSpec** (type) — { kind: 'image' | 'atlas', alias, url } asset descriptor. #### Assets PIXI asset loader with atlas registry integration. - **assets** (factory) — `(opts?: AssetsOpts) => Assets` — Create the asset loader. Provides load(kind, alias, url), texture(alias), atlas(alias), registry(). - **assets.load** (function) — `(kind: K, alias: string, url: string) => Promise, AssetError>>` — Async loader unified by `kind` discriminator. `kind: 'image'` returns `Texture`; `kind: 'atlas'` returns `Spritesheet`. Replaces the old separate `image()`/`atlas()` async loaders. - **assets.texture** (function) — `(alias: string) => Result` — Synchronous getter for a previously-loaded image. `kind: 'not_loaded'` if the alias is unknown. - **assets.atlas** (function) — `(alias: string) => Result` — Synchronous getter for a previously-loaded atlas. `kind: 'not_loaded'` if the alias is unknown. - **Assets** (type) — { load, texture, atlas, get, has, register_atlas, registry, dispose }. - **AssetsOpts** (type) — { fixed_dt, register_default? } options. - **AssetKind** (type) — `"image" | "atlas"` — Discriminator for `assets.load`. - **LoadValue** (type) — `LoadValue = K extends 'image' ? Texture : Spritesheet` — Conditional return type for `assets.load`. - **AssetError** (type) — Asset error union — load_failed, not_loaded, invalid_atlas, wrong_kind. #### Browser source Keyboard + Gamepad + (optional) pointer InputSource. - **browser_source** (factory) — `(opts?: BrowserSourceOpts) => BrowserSource` — InputSource that polls keyboard + Gamepad API. - **BrowserSource** (type) — { poll, dispose }. - **BrowserSourceOpts** (type) — { deadzone?, get_time? } options. #### Camera Design-resolution camera with letterbox/stretch/integer modes. - **camera** (factory) — `(opts: CameraOpts) => Camera` — Create a camera. Modes: letterbox, stretch, integer, fit. - **Camera** (type) — { resize, viewport, set_mode, design, ... }. - **CameraOpts** (type) — { design: { width, height }, mode } options. - **CameraMode** (type) — `"letterbox" | "stretch" | "integer" | "fit"` — Camera scaling mode. - **Viewport** (type) — { x, y, w, h, scale } resolved viewport rect. #### Render Application + stage + overlay layers. - **make_render** (factory) — `(opts: RenderOpts) => Promise>` — Initialise the PIXI Application and overlay containers. - **RenderState** (type) — { app, world, debug_overlay, palette_overlay, render_system, canvas, dispose }. - **RenderError** (type) — Render init error — webgl_unavailable, ... - **RenderOpts** (type) — { mount, camera } options. #### Sprite ECS↔PIXI sprite bridge. - **sprite_c** (constant) — `Component` — Per-entity sprite component (texture, frame, anchor, tint, scale, visibility). - **sprite_sync_system** (factory) — `(opts: SpriteSystemOpts) => System` — System that syncs sprite_c to PIXI display objects each post-tick. Applies texture/frame, anchor, tint, scale, position, zIndex. Owns the `WeakMap>` keeping live PIXI nodes. - **sprite** (namespace) — `{ set, show, hide }` — Patch helpers for `sprite_c` — `sprite.set(w, id, patch)` merges a `Partial` into the existing component (no spread ceremony). `sprite.show`/`hide` toggle `visible`. - **sprite.set** (function) — `(w: World, id: Id, patch: Partial) => Result` — Read the entity's `sprite_c`, merge `patch`, write back. Errors `component_missing` if the entity has no sprite. - **sprite.show** (function) — `(w: World, id: Id) => Result` — Shortcut for `sprite.set(w, id, { visible: true })`. - **sprite.hide** (function) — `(w: World, id: Id) => Result` — Shortcut for `sprite.set(w, id, { visible: false })`. - **SpriteData** (type) — Config-only: `{ texture, frame?, anchor?, tint?, visible?, z?, scale?, alpha? }`. The PIXI runtime node is owned by `sprite_sync_system` (private `WeakMap`); no `node` field on the public type. - **SpriteData.scale** (type) — `{ x: number; y: number }` — Optional non-uniform scale applied to the underlying PIXI Sprite each frame. Defaults to (1,1). Use for HUD vs world sprite ratios, pixel-perfect upscale, mirroring (`{ x: -1, y: 1 }`). - **SpriteData.alpha** (type) — `number` — Optional 0..1 alpha applied to the underlying PIXI Sprite each frame. Default 1.0 (fully opaque). Pair with `Grid.lit_area` to drive gradient FOV — set per-tile sprite alpha from the lit-area intensity. - **SpriteSystemOpts** (type) — { assets, world_container, pos_component } options. #### Anim Animation sync — drives sprite frames from anim_c. - **anim_sync_system** (factory) — `(opts: AnimPixiOpts) => System` — System that reads anim_c, advances frames, writes back to sprite_c. - **AnimPixiOpts** (type) — { assets } options. #### Debug overlay Renders frame-buffered debug commands into a PIXI overlay. - **debug_pixi** (factory) — `(opts: DebugPixiOpts) => System` — System that renders Debug.frame() commands into the debug overlay each render-tick. - **DebugPixiOpts** (type) — { overlay, dev? } options. #### Palette UI PIXI overlay UI for the command palette. - **palette_pixi** (factory) — `(opts: PalettePixiOpts) => { system: System, dispose: () => void }` — Mount a PIXI palette overlay; returns the per-frame system + dispose. - **PalettePixiOpts** (type) — { overlay, palette, get_ctx } options. ### @f0rbit/forge/debug Standalone debug subsystem (also re-exported from the main entry). #### Factories - **debug** (factory) — `(opts?: DebugOpts) => Debug` — Create a debug subsystem. - **debug_noop** (factory) — `() => Debug` — No-op debug for production. - **is_dev** (function) — `() => boolean` — Read the __DEV__ global. #### Types - **Debug** (type) — { line, rect, text, pin, unpin, frame, stats, ... }. - **DebugOpts** (type) — Options for debug(). - **Color** (type) — Numeric RGB triple or named colour. - **DebugCmd** (type) — Frame-buffered draw command union. - **DebugStats** (type) — { entities, frame_ms, draw_calls, ... }. - **Pin** (type) — Per-entity inspection pin. - **PinKind** (type) — Pin classification. - **ComponentInspection** (type) — Per-component inspection row. - **Inspection** (type) — Inspector snapshot. ### @f0rbit/forge/storage Persistence subsystem — snapshotter, generic Store, save/load helpers, default engine store. #### Snapshot - **snapshotter** (factory) — `(opts?) => Snapshotter` — Create a snapshotter. - **snapshot_schema** (schema) — Zod schema for snapshots. - **Snapshot** (type) — Serialised world + resources. - **SnapshotMeta** (type) — Snapshot header. - **EntitySnap** (type) — Per-entity row. - **Snapshotter** (type) — { take, restore }. - **TakeOpts** (type) — Options for take(). - **RestoreOpts** (type) — Options for restore(). #### Backends Store implementations. - **mem** (factory) — `(opts?: MemOpts) => Store` — In-memory backend — useful for tests. - **file** (factory) — `(opts: FileOpts) => Store` — File-system backend (Node). - **store** (factory) — `(opts: StoreOpts) => Store` — Generic backend with custom read/write. - **Store** (type) — `Store` — { open, save, load, list, delete }. - **Slot** (type) — Branded slot identifier. - **SaveHandle** (type) — Slot handle. - **SaveSlot** (type) — Slot + value pair. - **StoreError** (type) — Store error union. - **MemOpts** (type) — Options for mem(). - **FileOpts** (type) — Options for file(). - **StoreOpts** (type) — Options for store(). #### Save / load - **save** (function) — `(snapshotter, store, slot) => Promise>` — Compose snapshotter + store: persist current world to a slot. - **load** (function) — `(snapshotter, store, slot) => Promise>` — Compose snapshotter + store: load a slot back into the world. - **SaveError** (type) — Error union for save/load. #### Engine store Bindings + prefs persistence layer used by boot(). - **engine_store** (factory) — `(opts?: EngineStoreOpts) => EngineStore` — Default engine save layer wrapping bindings + prefs. - **bindings_schema** (schema) — Zod schema for persisted Bindings. - **prefs_schema** (schema) — Zod schema for persisted Prefs. - **default_prefs** (constant) — `Prefs` — Sensible default Prefs object. - **EngineStore** (type) — { bindings, prefs } persistence handle. - **EngineStoreOpts** (type) — Options for engine_store(). - **Prefs** (type) — User-facing engine preferences. ### @f0rbit/forge/grid Grid-game primitives — pure cell math, Bresenham line, line-of-sight FOV, cell-keyed spatial index, axis-sliding tile movement, and tick/cell-rate calibration. Tree-shakeable; non-grid consumers pay nothing. #### Grid Cell math factory + spatial methods on the returned `Grid` record. - **grid** (factory) — `(opts: GridOpts) => Grid` — Build a Grid record bundling cell↔world conversion, key/unkey, neighbours, distances, and the spatial methods (line, line_of_sight, move_tile). - **grid.line** (function) — `(a: Cell, b: Cell) => Generator` — Bresenham line generator yielding every cell from `a` to `b` inclusive. Method on `Grid` — call as `g.line(a, b)`. - **grid.line_of_sight** (function) — `(opts: FovOpts) => ReadonlySet` — Symmetric Bresenham FOV — returns visible cell-keys from `opts.from` within Chebyshev radius. Method on `Grid` — call as `g.line_of_sight({...})`. - **grid.lit_area** (function) — `(opts: LightOpts) => Map` — Gradient FOV — same Bresenham LOS test as `line_of_sight`, but returns a `Map` where intensity is a float in [0, 1]. Default falloff is linear `1 - distance/radius`. Pass a custom `falloff` for quadratic / smoothstep / torch flicker. Origin is always 1.0. - **grid.move_tile** (function) — `

(w: World, id: Id, dir: { dx, dy }, opts: TileMoveOpts

) => Result` — Step entity one cell with axis-sliding collision. Method on `Grid`. `opts.pos` defaults to forge's canonical `pos_c`; pass it for custom position components. - **Cell** (type) — `{ readonly x: number; readonly y: number }` — Integer-coordinate cell. Value object — pass by value. - **Grid** (type) — `{ cols, rows, tile, key, unkey, in_bounds, cell_to_world, world_to_cell, chebyshev, manhattan, neighbors4, neighbors8, line, line_of_sight, move_tile }` — Grid helper bundle returned by grid(). - **GridOpts** (type) — `{ cols: number; rows: number; tile: number }` — Options for grid() — `tile` is pixels per cell (square tiles). - **FovOpts** (type) — `{ from: Cell; radius: number; is_blocking: (cell: Cell) => boolean; include_origin?: boolean }` — Options for `grid.line_of_sight`. `include_origin` defaults to true. (No `grid` field — the method closes over the receiver.) - **LightOpts** (type) — `{ from: Cell; radius: number; is_blocking: (cell: Cell) => boolean; falloff?: (distance: number, max: number) => number }` — Options for `grid.lit_area`. `falloff` defaults to linear `(d, max) => 1 - d/max`. Custom falloffs are clamped to [0, 1]. - **TileMoveOpts** (type) — `{ blocked_by: (cell: Cell) => boolean; slide?: boolean; pos?: Component

}` — Options for `grid.move_tile`. `slide` defaults to true. `pos` defaults to forge's `pos_c`; override for custom position components. - **TileMoveResult** (type) — `{ from: Cell; to: Cell; moved: boolean }` — Result of `grid.move_tile` — gives consumers the resolved cell so they can react (e.g. did I just step onto the exit?). #### Spatial index Cell-keyed spatial lookup over an entity component. - **grid_index** (factory) — `

(w: World, pos_c: Component

, grid: Grid, filter?: Component) => GridIndex` — Build a cell-keyed spatial index over entities with `pos_c`. Optional marker `filter` restricts the indexed set. Eagerly refreshes on construction. - **grid_index_sync_system** (factory) — `(idx: GridIndex) => System` — System that calls `idx.refresh()` once per tick. Add to `pre` so subsequent systems see fresh lookups. - **GridIndex** (type) — `{ at, all_at, around, refresh }` — Spatial index interface — `at(cell)` first match, `all_at(cell)` all matches, `around(cell, r)` everything in a Chebyshev-r square, `refresh()` rebuild. #### Timing Calibrate movement speed in cells/sec instead of ticks-per-step. - **ticks_per_step** (function) — `(cells_per_second: number, fixed_dt: number) => number` — Convert a desired cells-per-second to the integer tick gate. Decouples movement speed from grid resolution. ### @f0rbit/forge/presets Pre-built Bindings for common control schemes — hand to boot() or merge with custom bindings. #### Built-in presets Each preset is a Bindings object covering keyboard + gamepad. - **presets.movement_2d** (constant) — `Bindings` — 2D analogue movement: move.x, move.y axes (WASD / arrows / left stick / d-pad). Renamed from `movement2d` in v0.3.0. - **presets.movement_4way** (constant) — `Bindings` — 4-way digital-only movement: move.{left,right,up,down}. No axes — ideal for tile-step games. Renamed from `movement_4way_digital` in v0.3.0. - **presets.movement_8way** (constant) — `Bindings` — 8-way digital movement: move.{left,right,up,down} digital + move.x, move.y axes. Renamed from `movement8way` in v0.3.0. - **presets.platformer** (constant) — `Bindings` — Side-scroller: jump (digital, Space / pad south) + move.x axis. - **presets.twinstick** (constant) — `Bindings` — Twin-stick shooter: move.x, move.y, aim.x, aim.y axes. - **presets.menu** (constant) — `Bindings` — Menu navigation: up, down, left, right, confirm, cancel digital actions. ## See also - Full reference table: https://f0rbit.github.io/forge/reference - Per-page guides under each section in the sidebar at https://f0rbit.github.io/forge - Example consumer: https://github.com/f0rbit/coin-collector