The split is deliberate. Ink drives authored outcomes. React owns dialogue, inputs, settings, transcript, and evidence. Phaser only renders what the controller already decided. No layer gets to smuggle in branch logic through animation timing.
// featured case study · games and interactive · 2026
Before the applause.
The waiting room says HERO.
We shipped an adult comedy visual novel, Your Happy Ending Has Been Revoked. Vivienne wants out. A Director wants the hero's ending. Your evidence decides whether she leaves freely or under his terms.
- Product problem
- Hero stories promise a clean ending. A vain Director tries to own yours.
- Shipped surface
- Chapter I live at endingrevoked.com (~5 Sep 2026). Playable SPA.
- Branch model
- Three investigation leads reconverge. Named outcomes, not a score screen.
- Save contract
- Schema-3 IndexedDB. Atomic writes. Revision guards. No guessed migrations.
// the product problem
Browser story games feel disposable, or like a chat window in a costume.
We wanted authored branches, exact-line voice, and consequences that survive when the narrator rewrites the plot. The comedy targets creative control and genre pastiche, not the player. Adult preferences are stated plainly: reduced motion, larger text, optional sound, a transcript of reached passages, skip intro, and Reduce explicit content (four authored softer lines, not child-safe).
// product decision
The controller owns truth. Presentation never chooses branches.
Ink authors branches. React validates semantic actions and owns durable state. Phaser presents the stage at 30 FPS on Canvas. Animation and audio never decide outcomes. Static voice at playtime (~144 clips, WebP on Bunny). Evidence survives the narrator.
Three investigation leads reconverge. Named outcomes include No One's Reward, Freedom Subject to Availability, and An Excellent Position. What you collect changes what the Director can claim, even when the prose tries to rewrite the plot.
Schema-3 IndexedDB with atomic writes, stale-tab guards, and revision checks. When the save format moves forward, the path is explicit: Start the updated preview. No silent migration that pretends old state still means what it meant.
// the system
Comic-collage UI on a static edge stack.
Cloudflare Workers Static Assets hosts the SPA. Bunny serves immutable media at assets.endingrevoked.com/preview/v2. Vite 8, Vitest, and Playwright guard the build. The root URL is playable. Adult comedy is labeled plainly on the catalogue and in the product copy.
- 01 Author
Ink branches with exact-line voice. React semantic actions for dialogue, evidence, settings, and transcript. Phaser stage for presentation only.
- 02 Persist
Schema-3 IndexedDB saves with revision guards and stale-tab detection. Explicit Start the updated preview instead of guessing migrations.
- 03 Present
Phaser 4.2 Canvas at 30 FPS. Static voice (~144 clips). WebP on Bunny. Comic-collage UI. Reduced motion and optional sound respected at playtime.
- 04 Ship
Chapter I live on endingrevoked.com. Cloudflare Workers Static Assets. Vitest plus Playwright on core flows. Only claim what is playable.
// the product, in frames
Screens from the shipped surface.
// what it holds
Proof, not adjectives.
The useful bits: what changed for the people using it, and what the stack actually is.
Playable at endingrevoked.com since ~5 Sep 2026. Three investigation leads reconverge. Named outcomes ship as authored branches, not a score screen. No invented player counts.
Catalogue and product copy state adult comedy plainly. Reduce explicit content swaps four lines for authored softer alternatives. That is not child-safe, and we do not claim it is.
Media release folders are versioned paths on Bunny. Changed files get a new release base, not an overwrite. The build pins to a verified media base at playtime.
- Ink
- React 19
- Phaser 4.2
- Cloudflare Workers
- IndexedDB
- Vite 8
- Vitest
- Playwright