Repository navigation
EN Studio API
Français · Type: reference · Audience: author, contributor · Context: local Studio · Status: ready · Applies from: 4.4.0
The local Studio · contract: stable · verified by CI (support matrix of 4.4.0, 2026-10-23): Chromium end to end (the Studio's checks); Windows: the unit suite only · automated checks: jobs
e2e,windows; CI plane2e,windows; 2 test files · people's passes:studio-hero-matrixnot run (#238) · known limits:hosted-only-routesThe Studio Server (hosted team Studio) · contract: experimental · verified by CI (support matrix of 4.4.0, 2026-10-23): Postgres, S3 and Keycloak in CI; Chromium and Firefox (job studio-hosted); WebKit on macOS, by hand only · automated checks: jobs
studio-hosted,studio-install-smoke; CI planstudioHosted; 3 test files · people's passes:studio-hostednot run (#149),studio-collabnot run (#150),studio-previewnot run (#166),studio-assets-hostednot run (#167),studio-mcp-remotenot run (#196),studio-oidc-genericnot run (#197) · known limits:hosted-only-routes
The JSON API the Studio's page talks to: read the game, edit a room, run the checks. Each route below has its own
section; link to it as studio.api.md#put-room-id-layout (the method and the path in lowercase, - between words).
-
Where it lives. The dev server (
npm run devornpm run studio, in the engine repository or in a created project) serves it under/__studio/api/. The Studio Server (4.2.1) serves the same table of routes under/api/, and names that base in a meta tag of the page it serves (src/studio/api-base.ts,studio-server/src/server/http.ts). One table,studio-server/src/ops/routes.ts, so both answer alike; the routes marked "dev server only" below exist on the dev server alone. -
What a path is relative to. Every path is relative to the current game (
GAME, see environment variables). -
Bodies. A
PUT,POSTorDELETEbody is a JSON object (the body must be a JSON objectotherwise, 400); aGETtakes none. The answer is JSON (cache-control: no-store), except the change feed, the screenshots, the Assets files and the Assistant's stream. -
On the Studio Server, a write must carry the header
x-studio-request: 1(the Studio's page sends it on every request): withOriginand a JSON body, it is what no other site's page can send without a preflight. The session, the roles and the spans held by others apply there too (403, 423). -
Errors. A refusal answers
{ "error": "<message>" }, sometimes with more fields, and a 4xx or 5xx status: 400 bad input, 404 unknown room, path or endpoint (no such endpoint: <path>), 405 wrong method (method <M> not allowed on <path>), 409 already exists or changed since, 422 the write would add validation errors (or nodefineRoom({...})in the file), 423 a span another member holds (shared Studio), 500 the game does not load or an edit would break the file, 501 unavailable here (a screenshot without Playwright, a capture on a hosted Studio). Every status and message is listed in the error register.
The routes call the plain functions of
studio-server/src/ops/core.ts, which the Vite plugin, the tests and the MCP
server use too: gameInfo(), getRoom(id), setLayout(id, layout), setText(id, path, value), addEntity(id, e),
getStoryboard(), setStoryboard(sb), exportStoryboardMarkdown(), getNotes(), addNote(n),
editNote(id, { text, about? }), deleteNote(id), validate(), solve(from?),
screenshot(room, checkpoint, devServerUrl) for the current game, and createStudio({ gameDir, root, importFresh })
for any other game folder.
- They never exit the process: an error is thrown as a
StudioError, with itsstatus. - The game is imported again on every call, so an edit on disk is seen: by default with tsx's scoped loader (fast, in
the same process);
importInChild(a childnode+ tsx process) is the fallback where in-process imports are cached, inside Vitest for instance. - Writes are serialized. Since 4.2.1 each write is one operation of the game's journal
(
studio-server/src/ops/journal.ts, kept in.cache/studio-journal/): every file it touches is written at once or not at all, durably (fsync), and an operation a crash interrupted is finished when the Studio opens again. - Every write whose files the validator reads is guarded on the game as it would be after it: 422 when it adds validation errors (text edits included since 4.2.1).
- Adding a prop, a hotspot or an actor writes the room and its layout as one operation, undone in one step.
- A change made outside the Studio (an editor, an agent, git) is recorded before the next write touches that file; a file another process rewrote while an edit was being prepared is a 409.
- The shared types are in
tools/studio/types.ts; the storyboard's schema, its normalisation and the Markdown export are intools/pages/storyboard-data.ts(pure, shared by the page generator, the core and the page).
Limit ·
hosted-only-routesThis page covers the table both servers share and the dev server's own routes. The Studio Server's own routes (sessions and login, members and invitations, comments, builds and previews, its Assets routes, the journal's cursor/api/ops, the engine upgrade, the probes,/mcp) are not described here: see the hosted Studio's pages.
A string literal under defineRoom({...}) is a text, addressed by its JSON path, when the path ends in name,
topic, toast, lines[n], say[1], shout[1], guide.say, choice[n].text, look.<id> or look.<id>[n], or
when it is a bare string in a command list (do, once, then, else, cutscene, after, onEnter, or a branch
of nth, cycle, random, parallel): those are hero lines. kind says which (name, look, say, hero,
topic, hint, toast, choice, guide); who is the speaker of a say.
- Ids, image refs, flags and conditions are never texts, and the API refuses to edit them.
- A replacement touches only the literal (the same quote style, escaped as needed).
- An append follows the list's layout (inline, or one item per line with the same indentation).
- A deletion removes the item and its separator.
- The paths of the following items shift after an append or a deletion: read the room again.
The Studio page is studio.html at the root of the engine (entry src/studio/main.ts). Any dev server serves it at
/__studio/ (npm run dev too); npm run studio also opens it. A production build leaves it out unless STUDIO=1
(the demo mode). The Rooms tab shows /?edit=<room>&at=<checkpoint> in an iframe and talks to the editor through
postMessage (placement editor). The Studio page ignores Vite's full reloads
caused by the game's files: the engine view reloads, the Studio keeps what you are typing and follows the change
through GET /events.
Read the game, read a room, and write a room's layout, texts, entities and structured values. Every write is one operation of the game's journal, guarded on the game as it would be after it.
- Answer:
{ id, title, hero, rooms: [{ id, name, decor }], characters: { id: { name, color, portrait } }, items: { id: { name, icon } }, verbs, checkpoints, images, sfx }, read from the game module (with itsobjectivessince 4.1.12).imagesis the asset manifest, id →[w, h], thumbnails at/assets/img/<id>.webp;sfxthe ids ofaudio.sfx. - Served by: both the dev server and the Studio Server (the shared table,
studio-server/src/ops/routes.ts).
- Answer:
{ def: RoomDef, layout: Layout, texts: TextRef[], file }, withTextRef = { path, value, file, line, kind, who? }for every text literal of the room file (see Texts). 404 for an unknown room. - Served by: both the dev server and the Studio Server (the shared table,
studio-server/src/ops/routes.ts).
- Body: the whole
Layoutobject. - Answer:
{ ok }:layout/<id>.jsonwritten. - Served by: both the dev server and the Studio Server (the shared table,
studio-server/src/ops/routes.ts).
- Body:
{ path, value },valuea string ornull. - Answer:
{ ok, line, changed }: the string literal atpathreplaced inrooms/<id>.ts;changed: falsewhen the value was already there (nothing is written).value: nulldeletes a list line (or a wholelook.<id>); a path ending in[+]appends a line (look.piano[+],hints[2].lines[+],on[3].do[+]). A path that is not a text (an id, an image, a flag, a condition) is refused;valuethat is neither a string nor null is a 400. Guarded since 4.2.1: 422 when the edit would add validation errors. - Served by: both the dev server and the Studio Server (the shared table,
studio-server/src/ops/routes.ts).
- Body:
{ kind: 'prop' | 'hotspot' | 'actor', id, name?, img?, char?, at: [x, y], look? }. - Answer:
{ ok, line, changed }: the entry inserted in the room file (its section created if absent), the optional first look line, and a place in the layout (prop: foot atat, height 60; hotspot: a 60 × 60 box centred onat; actor: feet atat), as one operation undone in one step.nameis required for props and hotspots; 409 when the id exists;kind,idandatare checked first (`kind` must be prop, hotspot or actor,`at` must be [x, y]). - Served by: both the dev server and the Studio Server (the shared table,
studio-server/src/ops/routes.ts).
- Body:
{ path, value?, dry? }. - Answer:
{ ok, line, changed, diff, dry? }: a structured value (a reactionon[3]oron[<length>]to add one, a conditionon[3].if, a command liston[3].dooronEnter, the room'sstageorrenderer) written as code atpathin the file's style;valueabsent removes the path;dry: truereturns the diff only. 422{ error, errors, diff }when the write would add validation errors: since 4.2.1 nothing is written, the guard judges the game as it would be. - Served by: both the dev server and the Studio Server (the shared table,
studio-server/src/ops/routes.ts). - Since: 3.4.
- Body:
{ path, value?, dry? }. - Answer: The same for the game file's
defineGame({...}), objectives only:objectives,objectives.<id>({ title, done, optional?, parent? }) or one of its fields; any other path is a 400. Validated the same way, so an objective whosedonecan never hold is refused. - Served by: both the dev server and the Studio Server (the shared table,
studio-server/src/ops/routes.ts). - Since: 4.1.12.
What changed between two revisions of the journal, the rooms it names drawn at both, and the undo and redo of the Studio's own edits.
- Answer: What changed between two revisions of the journal, by what the game names:
{ files: [{ path, change: added|removed|changed, ids?: { added, removed, changed: [{ id, fields }] }, values?: { added, removed, changed }, opaque?, cut? }] }; 2,000 revisions at most. The Check tab's "Changes between revisions". Each revision is 1 to 12 digits. - Served by: both the dev server and the Studio Server (the shared table,
studio-server/src/ops/routes.ts). - Since: 4.2.3.
- Answer: Local Studio only: each room the diff names, drawn at both revisions:
{ from, to, size: [960, 600], rooms: [{ room, why, before, after, box?, same? }], cut?, ms }. A side is{ file, url }(a PNG data URL and its file under.cache/studio/) or{ missing }(why it is not drawn: the revision does not build, the room is not there);box[x, y, w, h]where they differ,samewhen nowhere. 501{ unavailable, reason }on a hosted Studio or without Playwright. - Served by: both the dev server and the Studio Server (the shared table,
studio-server/src/ops/routes.ts). - Since: 4.4.0.
- Answer:
{ ok, file, what }, or{ ok: false, reason }when there is nothing to undo; 409 when one of the operation's files changed since (an AI, an editor, git, another author). Itself one operation of the journal. On a shared Studio (4.2.2) each member undoes their own latest operation, and a span another member holds is a 423. - Served by: both the dev server and the Studio Server (the shared table,
studio-server/src/ops/routes.ts). - Since: 3.4.
- Answer:
{ ok, file, what }, or{ ok: false, reason }when there is nothing to redo; 409 when one of the operation's files changed since (an AI, an editor, git, another author). Itself one operation of the journal. On a shared Studio (4.2.2) each member redoes their own latest operation, and a span another member holds is a 423. - Served by: both the dev server and the Studio Server (the shared table,
studio-server/src/ops/routes.ts). - Since: 3.4.
- Answer:
{ undo: [what], redo: [what] }: what an undo and a redo would take back, latest last. - Served by: both the dev server and the Studio Server (the shared table,
studio-server/src/ops/routes.ts). - Since: 3.4.
The storyboard (storyboard.json), the groupings of the scenario map (groupings.json) and the shared notes (notes.json) of the game folder.
- Answer:
storyboard.json,{ boards: [] }when it is absent. - Served by: both the dev server and the Studio Server (the shared table,
studio-server/src/ops/routes.ts).
- Body:
{ boards: [...] }. - Answer:
{ ok, changed }: an unchanged storyboard is not rewritten, a changed one is written compactly (what fits on 120 columns stays on one line). - Served by: both the dev server and the Studio Server (the shared table,
studio-server/src/ops/routes.ts).
- Answer:
{ ok, file, bytes, boards, panels }:games/<id>/storyboard.mdwritten from the savedstoryboard.json(the same text asnpm run page:storyboard -- --md); 404 without astoryboard.json. - Served by: both the dev server and the Studio Server (the shared table,
studio-server/src/ops/routes.ts).
- Answer: The groupings file,
{ groupings: [{ id, kind: chapter|puzzle|character, title, refs }] }. - Served by: both the dev server and the Studio Server (the shared table,
studio-server/src/ops/routes.ts). - Since: 4.2.3.
- Body:
{ groupings: [...] }. - Answer: Writes the groupings file, checked: ids unique and lowercase, kinds known, references without spaces, 500 at most of each.
- Served by: both the dev server and the Studio Server (the shared table,
studio-server/src/ops/routes.ts). - Since: 4.2.3.
- Answer: The scenario map derived from the groupings and the IR:
{ groupings: [{ …, refs: [{ ref, kind, at? }] }], ungrouped: { <kind>: [refs] } }. - Served by: both the dev server and the Studio Server (the shared table,
studio-server/src/ops/routes.ts). - Since: 4.2.3.
- Answer:
{ entries: Note[] }, withNote = { id, about, author, text, at, edited? }. - Served by: both the dev server and the Studio Server (the shared table,
studio-server/src/ops/routes.ts).
- Body:
{ about?, author?, text, task? }. - Answer: The new
Note, appended tonotes.json(created on first write);authordefaults toyou,atis an ISO date.task: truemarks a request for an MCP-connected agent. - Served by: both the dev server and the Studio Server (the shared table,
studio-server/src/ops/routes.ts).
- Body:
{ text, about? }. - Answer: The updated
Note: text trimmed,editedset to now;at,authorand the order unchanged. 400 for an empty text (or anaboutthat is not a string), 404 for an unknown id. - Served by: both the dev server and the Studio Server (the shared table,
studio-server/src/ops/routes.ts).
- Answer:
{ ok }: the note removed fromnotes.json; 404 for an unknown id. - Served by: both the dev server and the Studio Server (the shared table,
studio-server/src/ops/routes.ts).
The validator, the solver, the lint, the playtests, the coverage of the storyboard, the profiler, the world map, the puzzle graph and the game's IR: read-only, the game reloaded on each call. On the Studio Server, validate, solve, lint and playtests run on the processing worker.
- Answer:
{ ok, errors: string[], warnings: string[], ms }. - Served by: both the dev server and the Studio Server (the shared table,
studio-server/src/ops/routes.ts).
- Answer: The content profiler:
{ report, markdown, ms }, what each room, item and character amounts to and what is thin (src/engine/tools/report.ts, the MCP toolcontent_report). - Served by: both the dev server and the Studio Server (the shared table,
studio-server/src/ops/routes.ts).
- Answer: The world map:
{ graph, svg, dot }, the rooms and the ways between them (src/engine/tools/graph.ts, the MCP toolworld_graph). - Served by: both the dev server and the Studio Server (the shared table,
studio-server/src/ops/routes.ts).
- Body:
{ id? }. - Answer: The puzzle graph:
{ graph, svg, dot, markdown, classes, id? }, what every rule needs and changes;markdownis the overview, or withidthe card of that item, flag or prop;classessays why the solver keeps each node (critical, world, visible or dead). The MCP toolpuzzle_graph. - Served by: both the dev server and the Studio Server (the shared table,
studio-server/src/ops/routes.ts).
- Answer:
{ coverage, markdown, ms }: the saved storyboard checked against the content (src/engine/tools/coverage.ts: per board and panel, each room, speaker, line, topic, sound and action ok / partial / missing / unknown). - Served by: both the dev server and the Studio Server (the shared table,
studio-server/src/ops/routes.ts).
- Answer:
{ report, markdown, files, ms }: the playtests ofgames/<id>/playtests/replayed and summed up (src/engine/tools/playtests.ts). - Served by: both the dev server and the Studio Server (the shared table,
studio-server/src/ops/routes.ts).
- Body:
{ prove? }. - Answer:
{ lint, markdown, mode, ms }: the content lint after a solver run (src/engine/tools/lint.ts;prove: the exhaustive search first). - Served by: both the dev server and the Studio Server (the shared table,
studio-server/src/ops/routes.ts).
- Body:
{ from?: checkpoint, prove? }. - Answer:
{ finished, states, truncated, path, roomsReached, unlockedReached, flagsReached, itemsNeverUsed, unusedItems, deadEnds: [{ room, inventory, path }], errors, from, ms, profile };profileis theSolveProfileofsrc/engine/tools/solve.ts. 400 for an unknown checkpoint. 20,000 states at most;proveruns the exhaustive search instead of the witness. On the Studio Server it runs on the worker, under a time limit. - Served by: both the dev server and the Studio Server (the shared table,
studio-server/src/ops/routes.ts).
- Answer: The game's IR (DSL reference): its logic as data with the
file:lineof each id (provenance) and the trusted extensions' hash; the same JSON asnpm run ir -- --json. - Served by: both the dev server and the Studio Server (the shared table,
studio-server/src/ops/routes.ts). - Since: 4.1.12.
The voice table of each language (voices.json) and the hero × language matrix.
- Answer: The voice table of a language (the game's own by default; an unknown language falls back to it):
{ lang, langs, statuses, rows }. - Served by: both the dev server and the Studio Server (the shared table,
studio-server/src/ops/routes.ts). - Since: 3.4.
- Body:
{ status?, note?, actor? }(nullcounts as absent). - Answer:
{ ok }, the row written intovoices.json(statusdraft, empty note and actor by default). The id is the line's, URL-encoded by the client: a clip of a line with switches is keyed<id>~<flag>=<value>(4.3.0,=sent as%3D); 404 when the game has no such line; a field that is not a string is a 400. - Served by: both the dev server and the Studio Server (the shared table,
studio-server/src/ops/routes.ts). - Since: 3.4.
- Answer:
{ lang, csv }: the voice table as CSV. - Served by: both the dev server and the Studio Server (the shared table,
studio-server/src/ops/routes.ts). - Since: 3.4.
- Answer: The hero × language matrix (
src/engine/tools/hero-matrix.ts), read-only:{ langs, flags, columns, rows, truncated? }, each row'scells[column][language]{ text, want, clip?, file?, fallback?, missing?, borrowed?, unlisted? }and itsuntranslated,agreement,problem. - Served by: both the dev server and the Studio Server (the shared table,
studio-server/src/ops/routes.ts). - Since: 4.4.0.
The dev server's server-sent events and the room screenshots it renders.
- Body:
{ room, checkpoint? }. - Answer:
{ file, url }: a PNG of the room (the editor's overlays hidden) under.cache/studio/<game>-<room>[-<checkpoint>].png, served aturl(GET screenshots/<name>.png). 400 withoutroom; 501{ unavailable: true, reason, error }when Playwright or its Chromium is missing. - Served by: both the dev server and the Studio Server (the shared table,
studio-server/src/ops/routes.ts).
- Answer: The PNG a screenshot wrote (
image/png, not cached); 404no such screenshot.nameis letters, digits,.,_and-, ending in.png. - Served by: the dev server only (
tools/studio/plugin.ts).
- Answer: Server-sent events:
{ type: 'hello', game }on connection, then{ type: 'changed', file }when a file of the game folder changes on disk (filerelative to it, e.g.rooms/house.ts; dotfiles, editor backups andprivate/are ignored; 150 ms debounce per file); a: pingcomment every 25 s. - Served by: the dev server only (
tools/studio/plugin.ts).
Every image and sound of the game with where it is used, the art prompts per sheet, and the uploads. Nothing is ever deleted: a replaced file is first kept as <name>_v<N>.<ext> beside it. Since 4.2.1 every upload is one operation of the journal: the backups, the new files and the cells a tool cut are written together or not at all. A JSON body is 96 MiB at most (413 above). The Studio Server serves its own Assets routes (studio-server/src/server/hosted-assets.ts), not described here.
- Answer:
{ sheets: [{ id, kind: 'sprites' | 'furniture' | 'talk', character?, grid, promptKind?, cells: [{ id, file, w, h, used, ids, prepared, asset?, backups, mtime, missing? }] }], decors: [{ name, rooms, …cell }], sounds: { music, sfx: [{ id, kind, file, used, prepared, asset?, backups }] }, missing, unprepared, prompts: { sheets: [{ id, kind, markdown, missingMarkdown? }], style } }. - Served by: the dev server only (
tools/studio/assets.ts).
- Answer: A source file of
art/oraudio/, its path relative to the game folder; anything else is a 404no such file. - Served by: the dev server only (
tools/studio/assets.ts).
- Answer:
{ markdown }: the wholenpm run promptsdocument;?missing=1givesnpm run prompts -- --missing's. - Served by: the dev server only (
tools/studio/assets.ts).
- Body:
{ sheetId, grid?: '6x4', cells?: 'r1c1,r2c3', data },datain base64 or a data URL. - Answer:
{ ok, file, output, written, backups, cells }: the sheet saved asprivate/sheets/<sheetId>-<time>.<ext>and cut withtools/cut-sheet.pyintoart/<sheetId>/. 409{ conflicts }when cells of that sheet exist andcellsdoes not name them; a named cell that exists is first kept as<cell>_v<N>.png. - Served by: the dev server only (
tools/studio/assets.ts).
- Body:
{ sheetId, cell, data, key?: 'auto' | 'always' | 'never' }. - Answer:
{ ok, file, backup?, keyed: 'keyed' | 'kept' | 'opaque', cell }:art/<sheetId>/<cell>.pngreplaced (or added), the old file kept as<cell>_v<N>.png; a flat background is keyed with the cutter's rule. - Served by: the dev server only (
tools/studio/assets.ts).
- Body:
{ kind: 'music' | 'sfx', file, data }. - Answer:
{ ok, file, backup? }: written toaudio/<kind>/<file>(any format ffmpeg reads), the old file kept as a backup. - Served by: the dev server only (
tools/studio/assets.ts).
- Body:
{ name, data }, a PNG or a JPEG. - Answer:
{ ok, file, backup? }: written toart/decor/<name>.<ext>, the old picture kept as<name>_v<N>.<ext>. - Served by: the dev server only (
tools/studio/assets.ts).
- Answer: Runs
npm run assets:{ ok, code, output }. With?stream=1, the output as plain text while it runs, ending with[exit <code>]. - Served by: the dev server only (
tools/studio/assets.ts).
The Studio's Assistant on the dev server: a conversation relayed to a chat model, and a task for an MCP-connected agent.
- Body:
{ provider: { kind, baseUrl, model, apiKey? }, messages: [{ role: 'user' | 'assistant', content }], context? }. - Answer: Server-sent events of the conversation:
{ type: 'text', delta },{ type: 'tool_call', id, name, args },{ type: 'tool_result', id, name, result, isError? },{ type: 'done', usage?, wrote?, stopped? },{ type: 'error', message }. The model's tool calls run on the game through the MCP tools' registry (MCP tools). The API key comes with each request and lives in memory for that request only, never on disk nor in a log. 400 for a bad provider or conversation:kindmust beopenai,anthropicorollama;baseUrlan http(s) URL without credentials, one of the known providers' origins or an Ollama on the loopback, otherwise refused unlessWEB_SCUMM_ALLOW_CUSTOM_PROVIDER=1, and then HTTPS to a public host only;modelrequired; the last message the user's. - Served by: the dev server only (
tools/studio/assistant.ts).
- Body:
{ text, about? }. - Answer: The new note, tagged
task: true, authoryou: a request for an MCP-connected agent, no key needed. 400 withouttext. - Served by: the dev server only (
tools/studio/assistant.ts).
Generated publication · publication générée — current documentation · documentation courante: quest'n'click 4.4.0 r1, documentation revision · révision de la documentation 8dc717c69acb62c4742b69c54a7e7dfb620f75d8.
Earlier versions and permalinks · versions précédentes et liens permanents: https://wanoo.github.io/quest-n-click/docs/
An edit made in this wiki is overwritten at the next publication · une modification faite dans ce wiki est écrasée à la publication suivante.
Edit the source · Modifier la source · Report an error · Signaler une erreur
Index (6)
Start (6)
Play (9)
Install (17)
- Requirements
- Install from the release archive
- Install from npm, once published
- Install from the repository to work on the engine
- doctor: checking your machine
- Image toolchain
- Audio toolchain: ffmpeg, fluidsynth, Furnace, uv
- Upgrade
- Upgrade notes: 4.4
- Upgrade notes: 4.3
- Upgrade notes: 4.2
- Upgrade notes: 4.1
- Upgrade notes: v3
- Upgrade notes: v2 to v3
- Coming from web-scumm
- Package reference
- Install troubleshooting
Create a game (55)
- Your first game
- Layout of a created project and of the engine repository
- The working loop
- Content principles and the identity contract
- Rooms
- Geometry: placement, walk zones, masks, layers, camera
- Something is visible but unreachable
- Reactions
- Game commands
- Conditions
- Flags, variables and events: which to use
- Custom code
- Characters
- Items and inventory
- World map
- Dialogues
- Scripts and events
- Moving characters: patrols and walks
- Exits
- Chapters and objectives
- State and saves: persistent or temporary, renaming an id, migrations
- Staging: wide rooms and animations
- The stage: lights, particles, floors
- Skin and fonts
- Title and credits
- Heroes: several characters and hero choice
- Built-in minigames
- A custom minigame
- Translations
- Music in stems
- Sound effects
- Voices: per language and per hero
- Images: prompts, cutting sheets, transparency, scale
- Asset provenance
- The storyboard, source of truth
- Puzzle design
- Classic mechanics
- Review pages
- DSL reference
- Checking your game
- What the solver proves
- Solver options
- Playtests
- Remix
- Speedrun for authors
- The sealed ending
- Production template
- Example games
- Working with an AI
- Build and release a game
- Publish on GitHub Pages
- The static game on Clever Cloud
- Other static hosts
- Sub-path, cache and service worker, incomplete deploys, going back
- Testing on a phone over the LAN
Local Studio (24)
- The local Studio: overview and launch
- Edit a room
- Placement and stage
- Preview, apply, undo
- Storyboard
- Assets and cutting
- Check: validator, solver, critical path, heat, health, coverage
- Before and after captures
- Semantic diff
- Play and replay a session
- Rule explainer
- Language and objectives
- Voices and the heroes × languages matrix
- Map
- Notes
- Remix in the Studio
- Music
- The Assistant: bring your own key
- Demo mode and studio-apply
- Local and LAN security
- Studio API
- Local MCP: setting up each client
- MCP tools
- Studio troubleshooting
Team Studio (21)
- What runs where
- Concepts: revision, transaction, span, lease, preview, build
- Starting as an invited dev
- Working together day to day
- Roles and actions: the full matrix
- Invitations
- Managing members
- Write refused on the hosted Studio: stale base revision (409)
- Write refused on the hosted Studio: span locked (423)
- Recovering after a lost lease, a disconnection or a stale preview
- Comments
- Map and semantic diff
- Publishing builds
- Remote MCP
- The game in Git
- Upgrading the engine of a hosted game
- Hosted assets
- The server's command line
- Health and metrics routes
- Trying it locally with compose.yml
- Known limits
Clever Cloud (23)
- Clever Cloud: overview, cost and prerequisites
- Apps and add-ons
- Domains and TLS
- Keycloak
- Generic OIDC and GitLab
- Configuration
- First deployment
- Backups
- RPO and RTO
- Restore the Studio on a fresh installation
- Redeploys
- Upgrading the server and checking state after a migration
- Limits of going back
- Monitoring
- Security
- Rotating keys
- Recovering admin access
- Changing domain or OIDC provider
- Worker, PostgreSQL or Cellar down
- Exporting the game and leaving the installation
- Retention
- Clever Cloud troubleshooting
- Costs and teardown
Reality Bridge and connectors (16)
- The Reality Bridge: what it is
- Quick start
- Guarantees
- Install and run the Bridge
- Backup and restore
- Several tenants
- Signals and the manifest
- The Bridge's command line
- Connectors overview
- Email connector
- Telnet connector
- SSH connector
- Open Badges connector
- The connector SDK
- Security: Biscuit and signatures
- Bridge and connector troubleshooting
Contribute (25)
- Welcome
- Developer setup
- The dev loop
- Before a pull request
- Conventions
- Writing documentation
- Architecture
- Code tour
- Import boundaries
- Which tests run when
- Mutation testing
- The coverage ratchet
- The quality baseline
- CI explained
- Field passes
- Benchmarks
- Releasing
- npm packages
- Decisions and ADRs
- Governance
- Bot pull requests
- Contributing with AI agents
- Security policy
- Code of conduct
- Licences and credits