Speedrun for players: LiveSplit and OBS
Français · Type: how-to · Audience: player · Context: player's browser · Status: ready · Applies from: 4.1.14
Speedrun and Remix · contract: stable · verified by CI (support matrix of 4.4.0, 2026-10-23): Replays in four runtimes (cross-runtime) · automated checks: jobs
cross-runtime,e2e; CI plane2e; 2 test files · people's passes:runs-real-playersnot run (#85),run-resume-power-cyclenot run (#84),mystery-deployednot run (#82),livesplit-obsnot run (#81),code-wheel-humannot run (#78) · known limits:integrity-not-authenticity,livesplit-obs-not-tried
How to run a quest'n'click game against the clock, export the proof of your run, and show your timer in OBS or drive LiveSplit. For authors who want to declare categories: Speedrun for authors.
What you get
A timed attempt in a category the game declares (Any%, for instance), split on what happens in the game, never on
pixels; at the finish, a sealed .wsrun file that anyone can replay and verify; your records on your device; and, if
you stream, a timer in OBS or LiveSplit driven by the game.
A replay proves that a run is possible and that its in-game time is exact. It does not promise to detect every cheat: only a server's or a person's observation raises the trust further (see the limits).
Context and versions
The player of a game that declares speedrun categories (speedrun in its content). The engine has had speedruns since
4.1.14 "Time Attack"; this page holds from that version. The sample game at
wanoo.github.io/quest-n-click declares no category, so its pause menu has no
Speedrun row; the reference game in the engine repository (games/reference) declares several.
Prerequisites
- A game whose pause menu shows Speedrun.
- For OBS or LiveSplit only: Node 22.12 or newer on the machine you stream from, and a copy of the engine (a created project, or the engine repository); OBS or LiveSplit.
Where to run the commands
The run itself needs no command: it happens in the game. The streaming tools are small Node processes on your own
machine, never on a server and never in the game: in the engine repository through npm run, or in a created
project from the engine package it installed (node_modules/quest-n-click/tools/speedrun/).
Steps
- Start: pause menu › Speedrun › a category. The game restarts with the category's seed; a small timer shows the category, the time it is ranked on (real time, in-game time, or in-game time without pauses) and the last split.
- During the run: the pause menu shows Abandon run. Opening the menu is recorded as a pause, and the tab going to the background is recorded too; neither stops the in-game time (the game does not run while it waits).
- At the finish: the run is sealed, and the pause menu shows Export run: a
.wsrunfile, the proof to send to a leaderboard or a friend. The dev panel (?dev) has the same export.
- Your records stay on the device, offline (IndexedDB
web-scumm-runs): the personal best and its splits, the best segment of every split, the sum of best, attempts, finishes, abandons, notes. A run resumes after a crash or a closed tab from its last stored chunk (every 500 inputs); what was played after it is played again.
The ghost is your personal best played beside you, on semantic targets: the room it is in, the action it does and what it holds, ahead or behind at each split, never a pixel trail. It is off the first time you play a category on this device, because it would show the answers of the puzzles.
Streaming: OBS and LiveSplit
The tools run on the player's machine (decision D23), never on the Bridge, never in the game. Open the game with
?speedrunTool=<port> in its address: the page then posts its run's events (the category, the split ids and names,
the times; nothing else: no save, no player id, no token) to 127.0.0.1:<port>. A page carries one port, so it feeds
one tool at a time. The tools accept events from the game's origin only: the dev server and the preview by default
(localhost and 127.0.0.1, ports 5173 and 4173), --origin=<url> for a deployed game. Another page open in the
browser cannot post fake splits.
OBS: start the overlay, then add an OBS Browser Source at
http://127.0.0.1:7777/?mode=full(orcompact: the timer and the last split;transparent: the same on no background). It updates by Server-Sent Events.# in the engine repository, on your machine npm run speedrun:overlay -- --port=7777 --origin=https://wanoo.github.io# in a created project, on your machine node node_modules/quest-n-click/tools/speedrun/overlay.mjs --port=7777 --origin=https://wanoo.github.ioThen open the game at
https://…/?speedrunTool=7777. Use your game's own address in--origin.
LiveSplit: in LiveSplit, Control › Start WebSocket Server (it listens on
ws://127.0.0.1:16834/livesplit), then start the bridge and open the game with?speedrunTool=7778. The game drives LiveSplit: start, in-game time, split, skip, pause, reset.# in the engine repository, on your machine npm run speedrun:livesplit -- serve --port=7778 --origin=https://wanoo.github.io--livesplit=ws://…points to another LiveSplit address.
LiveSplit splits from a run: write a LiveSplit
.lssfile with a run's splits as the personal best.# in the engine repository, on your machine npm run speedrun:livesplit -- export run.wsrun --names=Pantry,Garden,Market --out=run.lssWithout
--names, the segments are named by the split ids; without--out, the file is written beside the run.
Expected result, and how to check it
- In the game: the timer, then Export run at the finish.
- In OBS: the Browser Source shows the category's name instead of "waiting for a run…", and the splits as you reach them.
- In LiveSplit: the timer starts when the run starts and splits with the game.
Common errors
| Symptom | Cause | Fix |
|---|---|---|
| No Speedrun row in the pause menu | The game declares no category | Nothing to do: that game has no speedrun |
| The overlay stays on "waiting for a run…" | The page was opened without ?speedrunTool=<port>, or with another port | Reload the game with the overlay's port in the address |
| The overlay gets nothing from a deployed game | The tool accepts the dev server's origins only by default | Restart it with --origin=<the game's address> |
| LiveSplit does not move | Its WebSocket server is not started | LiveSplit › Control › Start WebSocket Server |
| The run does not start | The category could not start (the message says why) | Read the message; report it to the game's author |
Files or data changed
In the browser: the run's chunks and your records in IndexedDB (web-scumm-runs). On disk: the .wsrun you export,
the .lss that export writes. The tools keep nothing.
Recovery or going back
Abandon run ends an attempt (counted as an abandon). A run resumed after a crash continues from its last chunk; a run cannot be resumed in another world or with another proof. A run submitted to a leaderboard on a Bridge can be deleted with the token the Bridge gave at submission (Privacy).
Limits
Limit ·
integrity-not-authenticityThe chain proves a file was not altered after it was sealed, not that the player was honest: pauses, menus, saves, the real time and the inputs used are the player's word. A run whose inputs a script chose replays as valid; only a witness or a moderator rules it out.
Limit ·
livesplit-obs-not-triedOBS and LiveSplit are tested by machines only. A real session by a person is the passlivesplit-obs, open (#81) as of 4.4.0.
References
- Speedrun for authors: categories, timing, verification, leaderboards, trust levels.
- Accessibility: options that never count as cheats.
- Design: ADR 0016 (the clock, the seeded generator, the chained proof), ADR 0017 (verdicts and trust).
- The code:
tools/speedrun/overlay.mjs,tools/speedrun/livesplit.mjs,tools/speedrun/local.mjs.