rootandClaude Fable 5.1 5a46124454 Initial commit: Game Maker phase 1
Canvas-only 2D game editor/runtime built on PixiJS v8 + Vite + TypeScript.

- core: asset model (Scene/Camera/Sprite), scene objects with sparse
  overrides, property schema + resolve, command stack (undo/redo),
  serialization and project store interface (16 unit tests)
- ui: custom in-canvas widget toolkit (panels, fields, color picker,
  list/scroll, popup) with a hidden <input> keyboard bridge
- editor: asset/hierarchy panels, schema-driven inspector, toolbar
- viewport: box rendering, camera frame, selection gizmo, drag/zoom/pan
- runtime: world/entity instantiation for play mode, input, camera follow
- api: PHP JSON project store with revision counter (409 on conflict)
- tools: CDP-based e2e smoke test

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 10:07:06 +02:00
2026-09-07 10:07:06 +02:00
2026-09-07 10:07:06 +02:00
2026-09-07 10:07:06 +02:00
2026-09-07 10:07:06 +02:00
2026-09-07 10:07:06 +02:00

Game Maker

A browser-based tool "one step before a game engine": create assets (scenes, cameras, sprites), place them into scenes, tweak their properties in an inspector and press Play – all in one program, all drawn inside a single WebGL canvas.

Live: https://nockal.com/game-maker/ · Widget playground: https://nockal.com/game-maker/?playground

Concepts

Term Meaning
Asset Named thing with a type (scene, camera, sprite) and typed properties. Assets have no position.
Scene object An asset placed into a scene: a link to the asset + a position + sparse overrides of the asset's properties. Scenes cannot be placed.
Override A per-object value that replaces the asset's value for one property. Only changed keys are stored; the inspector marks them bold and offers a reset (↺).
Edit mode The viewport draws the project model; panels, grid, gizmos and drag-to-move are active.
Play mode A World is instantiated from the active scene (one entity per scene object, resolved properties as mutable state). Systems tick it; nothing writes back. Stop discards the world.

Phase-1 demo behaviour in Play: the arrow keys move the sprite that was selected when Play was pressed (or the first sprite); the first camera in the scene follows it.

Stack

  • PixiJS v8 – rendering and pointer events. No game engine; runtime systems live in src/runtime/.
  • Own canvas widget toolkit (src/ui/) – panels, buttons, lists, text/number/color fields, popups. The only DOM element is a hidden <input> used as a keyboard bridge (IME, mobile keyboard, clipboard) while a canvas text field is focused.
  • Vite + TypeScript, Vitest for the pure core.
  • PHP 8 JSON API (api/) storing one JSON file per project in data/projects/, with atomic writes and an integer revision that rejects stale saves (HTTP 409).

Layout

web/index.html      Vite source page (dev)            index.html   built page (generated, served)
src/core/           model, property schema, resolve, commands, store – no Pixi imports
src/runtime/        World, Entity, systems (Input, PlayerControl, CameraFollow)
src/ui/             widget toolkit (generic, knows nothing about assets)
src/viewport/       scene renderer: model in edit mode, world in play mode
src/editor/         App composition, state, panels, inspector field factory
api/                PHP backend            data/projects/   persisted projects
tests/              Vitest unit tests      tools/e2e.mjs    headless CDP smoke test

Rules that keep it organised: core/ imports nothing from other layers; ui/ never sees assets; position exists only on scene objects; overrides are merged in exactly one place (core/properties/resolve.ts); the inspector is generated from PropertySchema – a new property is one schema line.

Development

Node 22 is required (on this machine: PATH=/opt/node22/bin:$PATH).

npm install
npm run dev          # http://localhost:5175/game-maker/dist/ – API calls are proxied to production
npm test             # unit tests (core)
npm run typecheck
npm run build        # writes dist/ and regenerates index.html
node tools/e2e.mjs <projectId>   # headless end-to-end run with screenshots in /tmp/gm-e2e-*.png

The workspace folder itself is what nginx serves, so npm run build is the deployment.

API

All routes go through api/index.php?r=… (the site's nginx does not pass PATH_INFO).

Method Route Body → Result
GET projects [{id,name,revision,updatedAt}]
POST projects {name} → project document (201)
GET projects/{id} {id,name,revision,updatedAt,project}
PUT projects/{id} {revision, project} → {revision,updatedAt} or 409 {revision} when stale
DELETE projects/{id} 204

There is no authentication yet; data/ is world-readable. Both are phase-2 topics.

Keyboard

Ctrl+Z / Ctrl+Shift+Z / Ctrl+Y undo/redo · Delete remove selection · Ctrl+Enter play · Esc stop · Tab cycles fields · number fields: drag to scrub (Shift ×10, Alt ×0.1), ↑/↓ step · viewport: wheel zooms, middle-drag or Shift+drag pans.

Out of scope for phase 1

Textures on sprites, physics, scripting, multi-select, z-order UI, auth, editing while playing, touch-optimised layout.

S
Description
Browser game maker: Pixi v8 canvas editor with visual behavior graphs, PHP API
Readme
11 MiB
0 Stars 1 Watchers 0 Forks
Languages
TypeScript 80.5%
JavaScript 17.1%
PHP 2.3%