Gerko Schrieken

Engineering case study · AI & TypeScript · 2026

A book becomes an adventure.
The player gets a say.

Book RPG started after I jailbroke my e-reader and wanted to make interactive stories work with as little logic on the device as possible. The result is a client/server story engine: choose a character, act and talk from their perspective, and follow the original story or take a different route while the server keeps the world coherent.

This demo, including its frontend, runs on an Azure server that scales to zero when idle. The first visit after inactivity may take longer while the server starts up.

EPUB → playable storySource-backed book analysis
Thin client by designDisplay and input stay separate from the story engine
1,120 tests passedValidated standalone public snapshot
01 · Origin

Keep the e-reader simple.
Move the hard work to the server.

The project began with a practical constraint: I had jailbroken my e-reader and wanted to experiment with an interactive book without turning the device itself into the story engine.

That pushed the architecture toward a thin client. A device client should only need to display the current scene and choices, collect input and send HTTP/JSON requests. EPUB import and indexing, AI calls, story progression, validation and saved games belong on the server.

The browser and Node.js terminal clients already use that separation. A KOReader/Lua client is still a future device target, so this is an architectural direction rather than a claim that the current application is already an e-reader product.

02 · Problem

Generating prose is only one part of a story engine.

A scene can read beautifully and still be wrong. A character might know something too early, act from the wrong perspective, or complete a decision before the player makes it.

The core problem is coordinating source material, player agency and persistent state. The engine needs to distinguish what has happened, what can happen now, and what still belongs to the future.

The original book provides a canonical route. It is a reference for progression, while the interactive timeline can diverge when the player makes a different choice.

03 · Design

Analyze the book once.
Reason about each turn in context.

  1. Build a source index

    Extract chapters from an EPUB, resolve character identities and organize source events into ordered story beats.

  2. Generate the next moment

    Use the selected character, current story progress and game history to create a scene with meaningful actions.

  3. Review and save

    Validate generated output and review continuity, presence and choices before accepting the next game state.

A story beat records an actor, action and agency. That distinction helps the engine separate automatic events from intentional actions that should wait for the player.
04 · Engineering choices

Put structure around the creative part.

Explicit player agency

The scene should set up a meaningful decision. Perspective and agency checks guard against narrating an unchosen player action or treating the player as a separate character.

Source progression as state

Completed beats are tracked for the active event. The engine tracks which source actions are still in the future and checks whether they remain compatible with the interactive timeline.

Dialogue has its own flow

Conversation choices open a separate dialogue state. The browser also supports free-text actions, turn history, undo and persistent world rules.

Provider-independent engine

OpenAI and xAI adapters sit behind one engine layer. Structured responses are validated before acceptance, with bounded generation attempts and retries.

05 · Runtime

One server-side engine.
Different clients can sit in front of it.

The current implementation uses Node.js and TypeScript. A responsive browser client and a terminal client both talk to the same HTTP API. The clients present state and collect input; the server owns book analysis, AI orchestration, progression rules and persistence.

Local development uses file-backed repositories. The Azure configuration uses Blob Storage for analyzed books and Cosmos DB for user-scoped game saves. A Docker image and Bicep infrastructure support deployment to Azure Container Apps.

That boundary is what makes a small KOReader/Lua client feasible later: it can reuse the same server rather than duplicate the story engine on the e-reader. The repository already includes a Kindle-oriented data package, but the browser and Node clients are the current implemented interfaces.

06 · Validation & limits

Test the deterministic parts.
Keep model behaviour separate.

The public snapshot was exported from the development code on 21 September 2026 and validated as a standalone repository. With Node.js 24.20.0 on Ubuntu, the TypeScript build passed and all 1,120 regular tests passed with 0 failures and 0 skipped tests. The same exported test suite also passed locally on Windows.

The tests cover source progression, player agency, validation, persistence, authentication, world rules, bridge logic and other deterministic behaviour. Separate Playwright tooling exercises the browser interface and repeated multi-turn flows.

Per-game logs record source progression, AI inputs and outputs, rejected generations and saved turns. These tools help reproduce failures such as premature dialogue suggestions, perspective drift and scenes that do not make the next action possible.

This remains an experimental AI application. Passing deterministic tests does not prove that every live model response, EPUB or sequence of player choices will behave correctly. Live-model evaluation and real e-reader testing remain separate concerns, and no aggregate reliability or latency benchmark is claimed here.

The sanitized public source snapshot contains the code and regular regression tests behind these results. Private book data, logs, repair directories and development Git history are excluded from that snapshot.
07 · Explore the work

Explore another engineering challenge.

Amsterdam 750 explores a different set of constraints: high-demand registration and personal data protection. Its case study includes a public implementation and published load-test evidence.