Skip to content
All posts

6 min readCase study, Game design, Vanilla JS

Under the Sycamore: a playable fable, one gesture at a time

Ten phone-first chapters inspired by The Alchemist, built in vanilla JavaScript with procedural Canvas art, synthesized sound, seven endings plus a bonus, and original narration throughout.


Under the Sycamore is a short browser game inspired by Paulo Coelho's The Alchemist. It takes about half an hour, runs best on a phone, and asks for one gesture at a time: press and hold to listen, drag something into place, keep two drops of oil balanced, draw a stone only when you can't read the signs, stand still under a blade, let go. The names, places and events come from the book. Every line of narration is original writing.

That last rule shaped the project as much as any technical decision. The engineering conventions state it as rule two: never reproduce sentences from the novel, and rewrite any line that sounds like the book. A dedicated writing pass, documented in the repo, went chapter by chapter rewriting lines that tracked the source too closely. We're following the same rule here: this post is about how the game is built, not a retelling of the story.

Chapters
10
Endings + bonus
7 + 1
Image files
0
Dependencies
0

One gesture, many verbs

The design document describes the core input in one line: press and hold on a hotspot or on the scene, and a ring fills at your fingertip. Every chapter builds on that. Listening is a hold. Balancing is moving left and right. Reading an omen is tapping whatever shimmers. One test is a stillnessprimitive that only completes if you don't move. Another is a hold that the game always interrupts on purpose. The engine exposes these as a small UI kit (hold, stillness, hotspots, shimmer, grid, readOmen, choose), and each chapter file puts them together rather than inventing its own input handling.

Phone-first was a rule, not a goal. Every chapter has to work at 400×800 with touch and still look right at 1280×800 with a mouse. All custom interaction uses pointer events, drag surfaces set touch-action: none, and hit targets are at least 44px. The game is also fully playable from the keyboard: Tab to a target, Space or Enter to hold, arrow keys to steer, 1–3 to pick a choice, Esc for the menu.

Choices that come back

The mechanic we like best spans the whole game. Early in chapter one you drag a marker to choose where the boy sleeps. In the last chapter you dig, and the treasure is only where you chose to sleep, within a set tolerance. Dig anywhere else and you hit roots or stone. The church in the final chapter is painted from the same place seed as the first, so it really is the same room, nine chapters later.

Seven endings are reachable. Six are temptations to stop along the road, and each is a real ending with its own painted scene, marked “a page turned early.” From that screen you can return to the exact decision and choose again. The seventh is the true ending. A bonus eighth unlocks if you reach the true ending after learning enough of the alchemist's lore along the way. The engine keeps endings outside the journey's save data: starting a new journey resets everything except your endings and your accessibility settings. Revisiting a single chapter replays it from a snapshot of the state you carried in, without touching your current journey.

No framework, no build, no images

The whole thing is plain HTML, CSS and ES2020 JavaScript in classic script tags. There's no bundler and no npm package. One engine file of about 2,250 lines handles state, save and load, the UI kit, the chapter runner, the HUD and the endings. Each chapter is its own file that registers itself with the engine. Classic scripts rather than ES modules was a deliberate choice: the game also ships as a Claude Artifact, whose host wraps the page, and the simplest possible load order survives that wrapping. A 17-line build script writes that variant.

There isn't a single image file in the repo. Every scene is painted procedurally on a Canvas: skies that shift with the hour, layered ridges, silhouette figures. Randomness comes from seeded mulberry32 generators: each place has a fixed seed, so its scenery repaints identically every time, and in-game events draw from the journey seed, the chapter and the turn. Sound is synthesized with Web Audio, created on the first gesture, and stays off until you turn it on. Nothing in the game depends on it. Saves go through a wrapper that catches storage errors, so the game still runs, in memory, where localStorage is blocked.

Tested by a harness with no dependencies

Testing uses a homemade headless-Chrome harness that talks to the DevTools protocol directly. It needs only Node and Chrome. It serves the game, loads any chapter through a #dev-hash with fixture state, runs a scripted scenario, takes screenshots, and exits non-zero on any uncaught page error. The repo holds 166 scenario scripts: one per chapter, a full playthrough, a run through every ending, an engine smoke test, an audio bench that checks every sound stays under 0 dBFS, and the probes and repro scripts left over from three QA rounds.

The process is written down as well. The game was designed and built with Claude Code through multi-agent workflows, with the author directing. A design panel produced three proposals and three judge reviews, which were merged into the design document every chapter is built against. Then came a build phase and three rounds of QA personas, triage and fixes, covering keyboard-only play, hostile input on landscape phones, and reading fidelity.

Honest edges

  • Phone testing so far has run in headless Chrome's mobile emulation, not on a physical phone.
  • The audio has been checked by measured output levels, not by ear.
  • On landscape phones the body text drops to 17px, below the design's 18px minimum. It's a documented exception.
  • No license has been chosen yet.

What we'd tell a client

  • If an adaptation must avoid the source's wording, make that a written rule and check it in a dedicated editing pass, not as an afterthought.
  • A small set of input primitives, reused everywhere, teaches players faster than a new control scheme every chapter.
  • Choices that pay off chapters later are cheap to build when the earlier choice is saved state, not a cutscene flag.
  • A dependency-free test harness can be a couple of hundred lines, and with no dependencies there is nothing in it to go stale.
No framework, no images, no borrowed sentences. The craft is in the constraints, and in a treasure that's buried where you chose to sleep.