Reverse-Engineering flipbook.page Into a Spec
April 26, 2026
A while ago I ran into flipbook.page. You type a topic, and it hands you back a single illustrated page — a watercolor explainer with a serif title printed inside the image. Then you click anywhere on that picture, and it draws you the next page: the same hand, the same paper, the same palette, but zoomed into whatever you just pointed at. Click again, and you go deeper. There is no end to it.
I loved the interaction, so I did what I usually do with something I love: I tried to rebuild it. But instead of shipping a clone, I ended up writing something else — a spec. The result is illustrated-explainer-spec, a stack-agnostic document that describes the whole experience precisely enough that you can hand it to any capable coding model and get a working app back.
This post is about why the spec turned out to be the more interesting artifact, and about the three or four decisions that actually make the thing work.

The thing worth copying is the loop, not the look
The temptation with a product like this is to reproduce its surface: the typography, the paper texture, the layout. That is the least valuable part. What makes flipbook.page feel magical is a loop you can describe in six lines:
Once I wrote that down, the rest of the design fell out of it. The document is an ordered array of pages plus a current-index pointer. Each page carries its id, its image URL, the id of the page it came from, and the click coordinates on that parent. Clicking from the middle of the array truncates everything after the current page and appends the new one — so branching exists conceptually, but the UI only ever sees a straight line. That single simplification removes an entire tree-rendering problem from the client.
The hard part: telling an image model "here"
The interesting engineering question in this product is narrow and specific: how do you tell an image model which part of a picture the user pointed at?
The obvious approach is to send coordinates. "Drill into the region at x=0.62, y=0.31." This does not work well. Image models are not built to reason about a numeric coordinate system laid over an image they are also being asked to redraw; you get a picture that drills into something plausible but usually not the thing under the cursor.
The approach that does work is embarrassingly literal. Before calling the model, the server reads the parent PNG, opens a canvas, and composites a big red ring with a filled center dot right at (x * width, y * height) — half-transparent ring, high-contrast outline, solid inner dot, radius about 4% of the image width. Then it sends that image as the reference, with a prompt that says:
The red circle marks where the reader pointed. Generate the next page by drilling into whatever the red circle is on (zoom in, internal structure, mechanism). Do not include the red circle in the output.
This translates an abstract action — pointing — into the one thing image models are natively excellent at: looking at pictures. The model does not need to understand a coordinate space. It just needs to notice a red circle, which it is very good at.
I think of this as the soul of the project. Everything else is plumbing.
One style string, two prompts
The second problem is coherence. Five pages deep, the illustration still has to look like it came from the same book — same line weight, same paper tone, same pastel palette, same title typography. If the first page is watercolor and the fourth is a 3D render, the whole illusion collapses.
The failure mode here is subtle and it is a writing failure, not a code failure: you write a beautiful style description in the first-page prompt, then paraphrase it in the child-page prompt, and the two drift. So the spec makes it a rule — there is exactly one style description string, it is the single source of truth, and both prompts include it by reference. Neither prompt is allowed to restate it.
The string itself is worth quoting, because most of its value is in what it forbids:
That last exclusion took several rounds to find. Ask for a "warm paper illustrated explainer" and image models love to give you a tourist map — roads, little landmarks, a compass rose. It is a strong attractor in the training data and you have to name it explicitly to escape it.
The child-page prompt, notably, has no slots at all. The only user-controlled string in the entire system is the topic on page one. "Where the user pointed" arrives visually, through the red marker, never as text. That is a nice property to have fall out of a design decision made for quality reasons: it also means there is essentially no prompt-injection surface past the first page.
Deterministic ids turn history into a cache
Image generation is slow and expensive, which makes Back and jump-to-page feel terrible if they regenerate. The spec's answer is to make page ids content-addressed rather than random:
normalize trims, collapses whitespace, and lowercases. Coordinates round to two decimals so that pixel-level jitter — clicking one pixel to the left — does not fragment the cache.
Images land at <static>/generated/<id>.png, and every request first checks whether that file exists and is non-empty. If it does, return the URL and never call the model. The consequences are all good ones:
- The same query always produces the same first page.
- Clicking the same spot on the same page always produces the same child.
- Back and thumbnail jumps are free.
- Restart the server, type the same query, get an instant answer off disk.
- Bumping the
versionstring invalidates everything at once.
There is something quietly satisfying about a design where the cache key is the identity. There is no cache invalidation logic because there is no cache — there is just a function from content to a filename.
A deliberately thin client
The client does three things: POST a topic or a click to /api/page, render the current image, and offer Back / jump / Reset. That is it.
The one detail I would call out is that clicks are converted to normalized 0–1 coordinates via getBoundingClientRect() before they leave the browser. The client therefore never needs to know the real resolution of the image it is displaying — the server can change image size, and every previously stored click still means the same thing.
Everything else is on the server: the prompts, the API key, the file paths, the serialization of generation requests behind a single in-process promise tail. The browser can only ever send a string of 1–300 characters and two floats in [0, 1]. File paths derive from hashes, so a client cannot name a file. Validation is three checks, and they cover the entire attack surface.
The acceptance checklist is the actual deliverable
Here is the part I did not expect to matter most.
A spec written as prose is easy to satisfy dishonestly. An implementation can technically follow every paragraph and still feel nothing like the original. So the spec ends with a behavioral checklist, and that list — not the prose above it — is what defines "done":
- Type "how volcanoes work" → a watercolor explainer page with the title printed inside, no map elements.
- Type "how a smartphone is built" → a same-style cross-section or exploded view, not a tourist map.
- Click a visible object → the next page clearly drills into that object, and the painting style is nearly indistinguishable from the previous page.
- Drill five pages deep → the style holds.
- Back returns to the previous page; thumbnails jump without triggering a generation (verify in the network panel).
- Restart the server, retype the same query → instant, from disk.
- Two rapid consecutive clicks → the second request is processed only after the first completes.
Every item is stack-agnostic and every item is falsifiable by a human in under a minute. None of them mention a framework, a language, or a file layout. If all of them pass, you have rebuilt the experience, and I do not care at all how you did it.
Why a spec and not a repo
The obvious way to share a rebuild is to publish the code. I think the spec is more useful, for one reason: code is a single frozen answer to a question, and the question is more portable than the answer.
A coding model handed this document will make different choices than I did — different runtime, different image model, different file structure — and as long as §12 passes, all of those are correct. The document survives the model it was written against. The repo would not.
There is a version of "AI-native engineering" that means typing faster prompts. I think the more durable version looks like this: figure out what the thing actually is, write it down precisely enough that the writing is executable by anyone or anything competent, and let the implementation be cheap and replaceable. The red-circle trick and the tourist-map exclusion are the two hard-won pieces of knowledge in that repo. Neither of them is code.
The spec is on GitHub, and the license is "do whatever you want with this." Full credit to flipbook.page for the idea — go play with the original first.