g2w - Game To Website
g2w — Game to Website is a TypeScript-first, AI-agent-ready framework for building and shipping web games: HTML5 games, 2D first with 3D planned, for desktop and mobile web browsers.
Status: MVP R5 + E3 + E4 + R6-1 + R6-2 (Effect is the primary application runtime) — gamepad & touch input, renderer backend selection, shared renderer conformance suite, and browser-verified rendering backends. The repository provides a
Gameruntime (lifecycle + frame loop), the Effect-nativeG2WGameapplication runtime that the browser demo boots through, a renderer-agnosticSceneManager, a DOM-freeInputStatewith a browser adapter plus a DOM-freeGamepadStatewith a Gamepad API adapter (@g2w/input-gamepad), a minimal asset system (AssetManagerin core plus a PixiJS texture loader in the renderer adapter), a renderer-agnostic 2D sprite model (Transform2D/Sprite2Din core with normalized anchors and z-order, projected onto PixiJS by the adapter), a minimal game-object layer (GameObject/GameObjectCollectionin core), a small ownership-explicit audio system (AudioManagerin core plus a Web Audio backend in@g2w/audio-web), and renderer backend selection with actual-backend reporting (R6-1) on top of the MVP 0 foundation: monorepo, renderer contract, PixiJS adapter, a working browser demo, and the Effect-based@g2w/runtimefoundation (E1 bootstrap plus the E2 Effect-native game lifecycle in@g2w/runtime; coreGameis unchanged). See What is implemented below.
Architecture
g2w is a pnpm monorepo of small packages with one strict rule: @g2w/core never depends on a rendering technology. Renderers implement the core contract and depend on core, never the other way around.
packages/core/src/index.ts Renderer contract + RendererError + Game runtime + SceneManager + InputState + GamepadState + AssetManager
packages/input-dom/src/ DOM (browser) adapter feeding InputState from real events (keyboard, pointer, touch)
packages/input-gamepad/src/ Gamepad API (browser) adapter sampling controllers into GamepadState
packages/audio-web/src/ Web Audio (browser) backend: decodes sound files, owns the AudioContext
packages/renderer-pixi/src/ PixiJS implementation of the renderer contract + texture asset loader
packages/devtools/src/ Read-only dev overlay: scenes, objects, transforms, FPS, input, logs
packages/runtime/src/ Effect-based runtime/bootstrap foundation (managed runtime, explicit shutdown) + Effect-native game lifecycle (G2WGame)
packages/create-g2w/ create-g2w CLI: scaffolds a new TypeScript + Vite g2w game project
examples/hello-world/ Vite browser demo: G2WGame (Effect runtime) + SceneManager + input + PixiRenderer
docs/architecture.md Architecture decisions and direction
For the full picture, read docs/architecture.md.
Target Architecture
The diagram below shows the intended target architecture — where g2w is going, not a claim that every planned capability exists. Solid edges and current package names reflect what is implemented and tested today; dashed edges mark planned or unverified capabilities. See ROADMAP.md for statuses and docs/architecture.md for the implemented design.
flowchart TB
APP["Game Application<br/>Game Logic · Scenes · UI"]
subgraph g2w["g2w framework"]
RUNTIME["@g2w/runtime<br/>Effect-based Application Lifecycle (G2WGame)"]
CORE["@g2w/core<br/>Engine Contracts · Game Model · Scene APIs<br/>zero runtime dependencies"]
INPUT["Input Adapters<br/>@g2w/input-dom (DOM · touch) · @g2w/input-gamepad"]
AUDIO["@g2w/audio-web<br/>Web Audio Backend"]
DEVTOOLS["@g2w/devtools<br/>Read-only Dev Overlay (dev-only)"]
PIXI["@g2w/renderer-pixi<br/>2D Rendering"]
THREE["@g2w/renderer-three<br/>Planned 3D Rendering"]
end
WEBGL["WebGL<br/>(PixiJS 8 backend)"]
CANVAS["Canvas Backend<br/>Verified (R6-2)"]
DOMAPI["DOM · Pointer Events"]
PADAPI["Gamepad API"]
WEBAUDIO["Web Audio API"]
APP -->|"compose and run"| RUNTIME
APP --> CORE
APP --> INPUT
APP --> AUDIO
APP -.->|"development builds only"| DEVTOOLS
RUNTIME --> CORE
INPUT --> CORE
AUDIO --> CORE
DEVTOOLS --> CORE
PIXI --> CORE
THREE -.->|"future adapter (R8)"| CORE
PIXI --> WEBGL
PIXI -->|"verified backends (R6-2)"| CANVAS
INPUT --> DOMAPI
INPUT --> PADAPI
AUDIO --> WEBAUDIO
Layer responsibilities
| Layer / package | Responsibility | Current status |
|---|---|---|
@g2w/core |
Renderer-agnostic engine foundation: renderer contract, Game runtime, SceneManager, DOM-free InputState/GamepadState, AssetManager, Transform2D/Sprite2D, game objects, AudioManager |
Done — zero runtime dependencies, enforced by architecture tests |
@g2w/runtime |
Effect-based application orchestration: one managed Effect runtime, G2WGame lifecycle with LIFO finalizers, idempotent shutdown with observable cleanup failures |
Done (E1 + E2) — Effect only here, never in core or adapters |
@g2w/renderer-pixi |
Implements the renderer contract on PixiJS 8: async Application lifecycle, logical sizing, sprite views, texture loading, optional backend preference with actual-backend reporting (R6-1) |
Done — 2D rendering; the g2w feature set (sprites, textures, alpha, resize, cleanup) browser-verified per backend on WebGL/Canvas/WebGPU (R6-3 smoke — see docs/renderer-audit-r6.md) |
@g2w/input-dom |
Adapts DOM keyboard, pointer, and touch (Pointer Events) onto InputState |
Done |
@g2w/input-gamepad |
Samples the Gamepad API into GamepadState through caller-driven poll() |
Done |
@g2w/audio-web |
Web Audio backend for the core AudioManager: decode, playback, AudioContext ownership, autoplay resume() |
Done |
@g2w/devtools |
Read-only development overlay observing core state through immutable snapshots | Done — opt-in, dev-only, absent from production builds |
| Asset management | Generic AssetManager in core; non-destructive PixiJS texture loader in the renderer adapter |
Done |
@g2w/renderer-three |
3D rendering adapter proving the renderer contract generalizes | Exploratory (ROADMAP.md R8) — no package exists yet |
| Canvas rendering backend | 2D rendering without WebGL | Browser-verified (R6-2/R6-3 smoke): init, rendered frames, and the g2w feature set verified in a real browser via backend: "canvas"; PixiJS Graphics/masks/filters have no g2w public API (demo-boundary, out of contract). See docs/renderer-audit-r6.md. |
Architecture principles
- Dependency direction. Everything above the core depends on
@g2w/core; core depends on nothing at runtime. Applications compose the packages directly — the Effect runtime orchestrates the lifecycle, but adapters are plain imperative packages a host wires up and registers for cleanup. - Renderer independence. Game code targets the renderer contract in core, never PixiJS. A future Three.js adapter would provide 3D the same way, without Three.js reaching the core.
- Effect ownership. Effect is the application-runtime technology, confined to
@g2w/runtime: it owns lifecycle, resource acquisition/release, initialization failure, and shutdown. No Effect execution or fiber per frame — the frame loop stays synchronous on one injected scheduler, with no runtime, effect, or promise allocated per frame. - Adapter boundaries. Browser APIs (DOM events, Pointer Events, Gamepad API, Web Audio) live only in adapter packages; core stays DOM-free and renderer-free, keeping the engine portable and independently testable.
- Explicit ownership. Every resource (display objects, listeners, contexts, playbacks) has a documented owner and a safe disposal path; shared resources like textures are never destroyed by framework disposal.
Current vs. planned capabilities
| Capability | Status |
|---|---|
| Effect as the primary application runtime | Done — the demo boots through G2WGame; core Game still ships alongside it as the reference implementation |
| 2D rendering | Done — PixiJS 8 over WebGL (auto-detected) |
| Renderer backend selection & reporting | Done (R6-1) — optional backend preference on init; capabilities.backend reports the actually initialized backend |
| Canvas (non-WebGL) rendering | Browser-verified (R6-2/R6-3 smoke): init + feature-level rendering checks in a real browser |
| WebGPU rendering | Browser-verified (R6-2/R6-3 smoke): init + feature-level rendering checks in headless Chrome with --enable-unsafe-webgpu |
| 3D rendering | Exploratory — Three.js adapter (R8) |
| Touch input | Done — multi-touch through the Pointer Events stream |
| Desktop and mobile web browsers | Target platform; verified today on desktop browsers via the demo's smoke runs |
Prerequisites
- Node.js 22 or newer
- pnpm 12 (activated automatically via
packageManager, e.g. through Corepack)
Getting started
pnpm install
Create a new game (R7)
create-g2w scaffolds a working TypeScript + Vite 2D game from a template that boots through the public g2w API (G2WGame, SceneManager, InputState, PixiRenderer):
pnpm create g2w my-game
cd my-game
pnpm install
pnpm dev
The generator is a zero-dependency Node CLI (packages/create-g2w): it accepts a target directory, refuses to overwrite existing files without --force, and prints next steps. After generating, it initializes a local Git repository in the project (plain git init; no remote, no initial commit, no push). Initialization is skipped with a warning when Git is not installed or the destination already contains Git metadata, and it never fails the scaffold — the generated files always remain. Requires Node >= 22 and, for the Git step, a Git executable on PATH. Validated end to end (install, typecheck, unit tests, production build, browser smoke) against locally packed @g2w/* tarballs with node scripts/smoke-create-g2w.mjs — the @g2w/* packages are not published to npm yet (ROADMAP R10), so until then a generated project resolves them from packed tarballs, and pnpm create g2w works only after publishing. From a clone, the CLI can be run directly: node packages/create-g2w/dist/cli.js my-game (after pnpm build).
Development commands
| Command | What it does |
|---|---|
pnpm dev |
Run the hello-world browser demo with Vite |
pnpm build |
Type-check and build all packages to dist/ |
pnpm build:example |
Build the hello-world demo bundle (production build) |
pnpm typecheck |
Type-check all sources, tests, and examples (no output) |
pnpm check |
Format and lint check with Biome |
pnpm format |
Auto-format sources with Biome |
pnpm test |
Run all tests with Vitest |
pnpm verify:exports |
Verify built packages load through their entry points |
pnpm test:pack |
R10 packability validation: packs all eight intended publishable packages, audits each tarball (manifests, exports, archive contents, no workspace:* residue), and consumes them from a throwaway TypeScript + Vite project outside the monorepo (manual, not in CI — fresh installs are network-dependent) |
pnpm release:rehearsal |
R10 release rehearsal: version-consistency and metadata audit plus npm pack --dry-run for every package, then the planned release order — never publishes or touches the registry (see docs/release.md) |