Build log · Software & apps

Escape House — a Desmos escape room, rebuilt as one 85 KB offline file

Software & appsdone3 versionssingle fileDesmos reverse-engineered85,196 bytes3-tier hints21 rooms

reflection not written yet · every fact below is generated from this project’s own spec and files

What it is

A first-person escape game that began life inside a Desmos graphing calculator: rooms, codes, items and flags all sitting in the activity's expression list. Reverse-engineered from that state — 794 expressions and 55 clickable hotspots — and rebuilt as one 85,196-byte HTML file that opens with no network and no assets. 21 rooms, six lock mechanics (dials, a keypad, a six-wheel word lock, a clock you have to set, a slice wheel, a lights-out grid), the original's exact codes, and a three-tier hint system that reads your own save file to know which step you are stuck on.

Version history

Straight from the project’s own card data — what each version added.

V1

House rebuilt from the activity state: 9 rooms, 6 locks, the original's codes.

V2

Clock hands + bike chain, three-stage computer, screwdriver corrected, 6-wheel word lock.

V3

Three-tier hint ladder with context from the open panel, H key, in-lock button, idle pulse, 32 beats.

Files on the site

What broke, and what I’d change

Not written yet. This is the part only I can write — what actually went wrong, and what I’d do differently. It lives in builds/reflections/escape-house.md (see _TEMPLATE.md); as soon as that file has words in it, this section fills in on the next build.

Everything else on this page — the spec, the version history, the file counts — is generated from the project’s own build notes and files, so nothing here is guesswork.

Build spec

The project’s own build notes, rendered. Open the raw .md ↗

Show the spec

Escape House — build spec

Deliverable: EscapeHouse.html — one self-contained HTML file (no network, no assets, no install) that plays as a first-person point-and-click escape game. Source lives in ~/Desktop/escape-house/ and is concatenated by python3 build.py into the single file.

Origin: a Desmos activity whose entire game — rooms, codes, items, flags — lives in the expression list. Fidelity target is that graph, not the screenshots.


1 · Constraints

  1. Single file. No <script src>, no <img src>, no fonts, no fetch. grep -oiE 'src="[^"]"|href="[^"]"'
  2. must return only xmlns URLs.

  3. Fixed logical room space. Every hotspot and prop is positioned in percent of the room box, so one set of
  4. coordinates works at 390 px and at 2560 px. Props are SVGs (viewBox="0 0 100 100", preserveAspectRatio="none") stretched into their box — do not add meet, it re-composes every room.

  5. No text inputs. All codes are dials, keypads, wheels or clocks.
  6. State in one object. G = {level, room, inv[], sel, f{}, unlocked[], hard}; every puzzle gate is a flag in
  7. G.f, every save is JSON.stringify(G) into localStorage. Anything a hint needs must be derivable from G.

  8. Mobile is a first-class target (Isaac plays on a phone and on a Mac). Under 560 px the room becomes a
  9. fixed-aspect band; nothing may overflow horizontally.

2 · Reading the original

  • Export the activity state, walk expressions.list (794 entries: 576 expression, 175 image, 42 folder,
  • 1 text), and collect every entry carrying clickableInfo.

  • Flags are brace-named in LaTeX — u_{lightsOn}, u_{iceKey}, u_{sedDart} — so strip _{} before searching.
  • Codes appear as literal arrays in the graph (e.g. DrawerClicked = [1,3,1]); read them there, never guess.
  • Rooms are identified by P_{centerScene} clusters on a 3×3 grid; each room also names its own art folder.

3 · The chain (level 1, must be preserved exactly)

fridge → ice ─┐
              ├→ hot tap → ice key → trapdoor → basement landing
light switch → basements ─→ slice wheel → dart ─→ balloon → rope → stairs → bedroom + office
bedroom: gloves · nightstand → screwdriver · keypad (screwdriver) 962 → greenhouse
greenhouse: cactus (gloves) → gold key → glass case → magnifier
office: lamp + magnifier → fire → library exit opens
kitchen: drawer 131 → tape → bookcase panel 4·5·19·13·15·19 → UV light
clock: hands (library) + 7:30 (UV, basement) → opens → bike chain → bike
office computer: screwdriver → 4 nodes → power → lights-out → disk

Codes: drawer 131, keypad 962, bookcase 4 5 19 13 15 19 (A1Z26 "DESMOS"), clock 7:30.

4 · The hint system (the actual feature request)

“add a hit button on the top that helps poeple who are doing the thing and they dont know how to do”
HINTS[level] = [ { id, room, done(G), need(G), t:[rung1, rung2, rung3] } , … ]
  • Rung 1 direction only · rung 2 the move, in order · rung 3 the answer and the room.
  • Escalation: new beat → rung 1; same beat re-asked → rung 2; again → rung 3 (capped, never loops).
  • resolveStep(): open lock panel wins (a panel sets activeHint); otherwise the first beat with
  • !done(G) && need(G); otherwise "all done, head for the exit".

  • Surfaces: header 💡 Hint, the H key, and a 💡 Stuck? button inside every lock panel.
  • The hint card sits at z-index:25 — above .lock (20), below .menu (30).

  • Idle: 55 s without a click makes the button pulse; any click clears it.
  • Progress is shown as n / total plus one dot per beat. 18 beats in the house, 7 in the lab, 7 in the vault.

Pitfall: showHint() and paintHint() must share one resolver. When they resolved separately, the context set by an open panel was silently discarded and every hint fell back to the earliest unsolved beat.

5 · Verification (do this every build)

  1. node --check every source file, then python3 build.py.
  2. Headless Chrome over CDP, viewport 1440×900 and 390×844:
  • click through the whole level-1 chain with real MouseEvents (SVG elements have no .click());
  • assert each HINTS beat flips to done and the ladder ends on the exit beat;
  • screenshot: menu, a hint at each rung, a lock with the hint card open, the win card, plus 390 px.
  1. Assert no console errors, no horizontal overflow (document.documentElement.scrollWidth <= innerWidth).
  2. After publishing: shasum -a 256 the artifact against /demos/escape-house/index.html and
  3. /demos/escape-house-mirror/index.html — all three must be identical.

6 · Known deviations (state them, do not hide them)

  • Props are redrawn SVG; the original's 175 images are the activity author's artwork and are not shipped.
  • The four UV clue graphics are polygons in the graph, so which wall shows which code cannot be read from the
  • state dump — codes exact, room placement inferred so the chain stays solvable.

  • Lights-out is 5×5 for phone-sized modals (original: 8×8).
  • The original's hardMode switch is not wired up.