Repository navigation
EN Create Playtests
Français · Type: how-to · Audience: author · Context: engine repository, created project · Status: ready · Applies from: 3.9.0
The solver tells you the game can be finished. It cannot tell you where a person gets stuck. A playtest can: players
share their session from the pause menu, you drop the files into the game, and playtests replays them on the current
content and sums them up.
A report of how real people played: play time per room, where they stalled, the hints they saw, the minigames they played, where each one stopped, the taps that just missed a target, and a heat map on the puzzle graph keyed like the solver's. The sessions stay in the game and are replayed by every later check, so you also learn when a content change makes an old session no longer replay.
- The engine repository (the files go in
games/<id>/playtests/) or a created project (game/playtests/). - From 3.9.0: playtests, field quotas (3.7.1) and near misses (3.8.0) are all there, and a created project has the
quest-n-click playtestscommand. The Studio's Check tab and theplaytestsMCP tool show the same report.
- A build of the game that testers can open on their phone: a deployed URL (Build and release a game) or your machine on the local network (Testing on a phone over the LAN).
- Testers who have never seen the game.
- On the tester's phone: the game's pause menu.
-
npm run playtests …: in the engine repository, on your machine.npx quest-n-click playtests …: in the created project, on your machine.
Each check catches something the others miss. Run them in this order:
| Step | Command (engine repository) | Catches |
|---|---|---|
| Validate | npm run validate |
broken ids, missing Look lines, long lines, flags never set |
| Solve | npm run solve |
dead ends, an unreachable ending, items never used |
| Walkthrough test |
npm test (tests/demo-walkthrough.test.ts for the demo) |
a scripted full playthrough on the real engine |
| Screenshots | npm run e2e |
what the solver cannot see: a character on a table, a line cut by the edge |
| A real phone |
npm run dev:lan, landscape |
thumb size, readability, sound, the rotate screen |
| Sealed ending |
npm run seal, npm run build, npm run check:spoilers
|
the ending text never in the clear |
| A person | the steps below | what nobody on the team can see any more |
In a created project, npm run verify runs validate, solve, the translation status, the lint and the playtests; the
other rows have no quest-n-click command, see Checking your game. The Studio's Check
tab runs validate and solve in one place.
Then hand the phone to someone who has never seen the game, and stay silent. Every time they hesitate more than a minute, write it down: it is a missing Look line, a vague hint, or an exit nobody saw.
- Send the game's URL. Say only: "play as you like; when you stop, finished or not, open the pause menu and tap Share session". Do not explain a puzzle, even when asked: write down the question.
-
The tester shares the session. Share session (the pause menu's row, named by
ui.shareSession) makes a file<game>-<date>-<time>.session.jsonand sends it through the phone's share sheet (Web Share), or downloads it where the browser cannot share. The file holds the inputs since the game started, as ids and indices only (no journal text, no name), the time of each input, the device family (ios,androidordesktop, nothing finer), and the near misses: a tap on nothing within 24 px of a target, by room and target. The tester sends it the way they like.
Guarantee ·
session-ids-only· referenced · scope self-checked by the author (AI), not reviewed A shared playtest session has a timestamp on every step and an empty journal, and a line the game said during the play is not in the file.
-
Drop the files into the game and commit them:
- engine repository:
games/<id>/playtests/(npm run auditscans that folder with the rest ofgames/); - created project:
game/playtests/.
Only files whose name ends in
.session.jsonare read. - engine repository:
-
Replay and read the report.
# in the engine repository npm run playtests -- --out=.cache/playtests # in a created project npx quest-n-click playtests --out=.cache/playtests
It replays every file on the current content and prints, as Markdown: play time per room (a gap over a minute between two inputs counts as a pause), where players stall (the same action three times in a row without effect), the hints shown, the minigames played, where each player stopped, the near misses, and the sessions the content has outgrown.
--outwritesreport.md,report.jsonandheat.svg(the puzzle graph, heated by the players);--jsonprints the report on stdout and nothing else;--dir=<folder>reads another folder.
- Turn the report into work. A stall is a puzzle to clue better, a near miss a hotspot to widen, an abandon a place to look at. In the Studio's Check tab, the puzzle graph's heat can come from the players instead of the solver.
-
Keep the sessions as a gate. The sessions stay in the game. Once the content changes they may no longer replay:
- without
--strict(whatverify:game,npm run buildand a created project'sverifyandbuildrun), a diverged session is reported, not an error, so a content change never blocks a push; - with
--strict(the release gate:verify:releaseandrelease-checkin the engine repository,quest-n-click releasein a created project, and the weeklyproveworkflow), a diverged session is an error, exit 1: re-record it or delete it.
--strictalone asks for no number of sessions: zero sessions pass it, and it says so. - without
-
For a release tested by players, ask for quotas.
# in the engine repository: verify:commercial, then 5 sessions that still replay, 3 played to the end, 2 device families npm run verify:field # in either context, your own numbers npm run playtests -- --strict --require=5 --require-completed=3 --require-devices=2 npx quest-n-click playtests --strict --require=5 --require-completed=3 --require-devices=2
A diverged session counts for nothing; device families are counted once each. One quota missed is an error, and so is no session at all.
verify:fieldis not a step ofrelease-check(decision D12): it is what a release tested by players needs. The maintainer's field pass is described in Field passes.
The engine records every input since the game started or a save was loaded (actions, map, character switches, script
steps) with the answers given on the way (choice answers, random draws). Besides Share session, the game's save
menu, the dev panel and the Studio's Play tab export it as <game>-session.json. A tester's bug report is that file
and a screenshot.
# in the engine repository
npm run replay -- session.json
npm run replay -- session.json --upTo=40 --jsonnpm run replay plays it back without a display and prints the journal, the semantic journal (4.1.11) and the final
state. It exits 1 at the first entry whose outcome differs from the recording, or when its semantic journal differs
from the file's. --upTo=N stops earlier; --json is for scripts. A created project has no replay command: play a
session in the Studio's Play tab (Play and replay a session). The solver's solution is a session
too (npm run solve -- --json gives steps), which is what npm run e2e taps through. The file's format is in
File formats.
The command ends with N session(s) replayed in … s, and nothing diverged. With --out, open report.md and
heat.svg. With --strict and quotas, the exit code is 0.
| Symptom | Cause | Fix |
|---|---|---|
no playtests in games/<id>/playtests |
The folder is empty, or the files do not end in .session.json (the save menu's export is <game>-session.json) |
Use Share session, or rename the file to end in .session.json. |
✖ <file>: recorded on game "x", not "y" (exit 1) |
A session of another game | Move it to that game's folder. |
⚠ <file>: recorded with save version N, the game is at M |
The save version changed since | A warning: the replay may diverge. |
… diverged (content changed since: re-record or delete) |
The content has outgrown the session | Re-record it, or delete it. With --strict this is exit 1. |
✖ [<id>] field quotas missed: … (exit 1) |
Fewer sessions, completed runs or device families than asked | Collect more sessions, from other kinds of device. |
The session files you add to playtests/. --out writes into the folder you give it (.cache/playtests above).
Nothing else is written.
A session is a plain file: delete it, or replace it with a fresh recording. Nothing else depends on it.
-
tools/playtests.ts,src/engine/tools/playtests.ts tests/playtests.test.ts- Checking your game, What the solver proves
- Field passes, Privacy
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