Project Story

Inspiration

We didn't start with a noble idea about education. We started with a confession.

One of us had a shelf. Real books, bought with real intent, all of them stopped somewhere around page 40. The other had a screen-time report that said four hours a day on reels. Same person, roughly. Same evening, usually. The book lost every single night — not because it was worse, but because it asked for something the phone never asks for: a running start.

Reading is a cold engine. You have to rebuild the world in your head every time you open the cover — who these people are, where we left off, why any of it matters. Reels ask for nothing. You arrive mid-thought, you're already inside, and the next one is already loading. It isn't a discipline problem. It's an interface problem. One medium front-loads all the cost and back-loads all the payoff, and the other does the exact opposite.

Then we noticed the thing we both actually did finish: manga. Hundreds of pages, in one sitting, no willpower involved. Manga is not a lighter version of a book. It's a book that solved the cold-start problem — panels give you the world instantly, faces carry the emotion, and your eye pulls you down the page faster than you decided to go. We had never once "tried" to read manga. We just read it.

So the question stopped being how do we make people read more and became something much more fun:

What if the book you never finished came to you as the thing you always finish?

That's ScrollStack. You hand it a PDF — a novel, a biography, a dense thing your friend keeps recommending — and it comes back as a manga edition you can actually sit and read. Chapters, recurring characters that look like themselves on page 3 and page 30, panels that hold a beat, pages that turn. And the parts that hit hardest get cut into short vertical reels, so the same scroll reflex that used to cost you an evening now spends it on something that stays with you.

We were deliberate about one thing: this is not a study tool. No quizzes, no streaks, no progress bar guilting you. It's entertainment that happens to be made of good books. If it feels like homework, we built the wrong thing.

What it does

You give ScrollStack a book and choose how much of it you want. It produces a manga edition — ordered, composed, lettered pages you read like any manga — and stores it in your library so you can reopen it, keep going, or ask for more.

Under the surface the product runs one honest pipeline:

PDF + the part of it you selected
  → versioned context pack   (what is true in this book)
  → manga plan + page scripts (what happens on each page)
  → thumbnails and composition (where it happens on the page)
  → RenderedPage + MangaManifest  (the immutable edition you read)
  → ReelSpec  (the vertical cuts derived from what you accepted)

The surfaces a reader actually touches:

  • /books/new — drop in a PDF, watch the stages move.
  • /library — every manga edition you've made, reopenable.
  • /manga/{edition} — the reader itself.
  • Reels — vertical cuts derived from the manga you already accepted, played through a real Remotion player and exportable as deterministic H.264/AAC MP4.

Two product rules we refused to bend:

  1. Source-grounded, not vibes-grounded. Every panel traces back to specific source units in the actual PDF. The system holds a versioned context pack so a character can't quietly become someone else forty pages later.
  2. Editions are immutable. When you accept an edition, it's frozen — pages, panel images, hashes, lineage, accepted and rejected attempts, and the exact cost. What you read today is byte-identical to what you read next month.

How we built it

Shape of the system. A FastAPI + Pydantic control plane owns truth and contracts. MongoDB (via Beanie) owns durable state — projects, runs, artifacts, memory. Celery + Redis own the workflow and retry lifecycle, because a manga run is a long chain of steps where any one of them can fail and none of them should take the whole run down. The frontend is Next.js 15 / React 19 with Tailwind and shared design tokens. Deterministic video is Remotion, run headless in a dedicated renderer package.

One seam, generated in both directions. The single cross-language boundary is packages/contracts/. Pydantic models are canonical; JSON Schema and TypeScript types are generated from them and committed with the source change. Generated files are never hand-edited. That one rule is why two people could build the manga lane and the reel lane in parallel worktrees without a week of integration pain — the reel player consumed manga-manifest.v1 and reel-spec.v1 off fixtures long before the backend could emit real ones.

Three boundaries carry the whole product:

Contract Meaning
RenderedPage the manga reader boundary — everything a page needs to display
MangaManifest the manga → reel handoff
ReelSpec drives live playback and deterministic export from one source

Determinism as a feature, not a nicety. Generation is expensive and non-deterministic; reading must not be. So the model touches the run only inside bounded creative sessions, and everything downstream — composition, lettering, page assembly, reel rendering — is deterministic given accepted artifacts. Re-run a run ID and you get the same pages back, no new spend.

Cost as a first-class model. Image generation dominates. If a page holds $n_p$ panels and $r$ is the fraction of attempts we reject on quality, the expected image spend for a $P$-page edition is

$$ C \;=\; c_{\text{img}} \sum_{p=1}^{P} \frac{n_p}{1 - r} $$

which makes the two levers obvious: cut panels per page, or cut the rejection rate. Rejections are the silent budget killer — in our demo edition, accepted panels cost \( \$0.4319757 \) while total image spend including three rejected attempts was \( \$0.549321 \). That's \( \approx 21\% \) burned on output nobody ever sees. Every rejected attempt is stored with its receipt precisely so that number stays visible instead of hiding inside a total.

The demo edition, honestly. For the hackathon slice we ran pages 1–15 of a real PDF through the pipeline with zero new text-model calls — reusing an accepted context pack — producing an immutable edition of five composed 1200×1800 pages from ten accepted panel images, one accepted character reference, and full lineage. One rejected panel was retried once; no accepted panel was ever regenerated.

How the two of us worked. Hard ownership lines, written down before any code: one lane owns the backend, contracts, manga surfaces, and the shared visual system; the other owns the Remotion renderer, reel components, and reel routes. Separate git worktrees driven by separate Codex sessions, PR-only into a shared dev, and a standing rule that no PR is complete without its contract fixture and visual evidence. Every handoff in NEXT_SESSION.md states what passed, what's still broken, and who moves next. The full agent workflow is documented in CODEX_USAGE.md.

Challenges we ran into

Character continuity is the whole ballgame. A model asked twice for "the same person" gives you two people. Manga dies instantly if the protagonist's face drifts between pages — the reader doesn't consciously notice, they just stop believing it. We solved it by treating a character as an accepted, hashed reference artifact that every later panel is anchored to, rather than as a sentence in a prompt.

Text inside generated images. We explicitly instructed no embedded text and got back a panel with an English heading burned into the artwork, plus pseudo-lettering scribbled around the speech balloons. That output was technically a beautiful monochrome manga render and we rejected it anyway, because lettering has to come from the deterministic renderer or it can never be edited, translated, or trusted. We still ship a known limitation here: OCR finds no letters in accepted image layers, but some accepted panels contain empty balloon shapes the model drew unprompted. Real words are renderer output; those hollow shapes are on the list.

Front matter is not a story. Our parser stores one source unit per PDF page, and the first fifteen pages of a real book are title pages, copyright, and a table of contents. Source-correct, dramatically worthless. It taught us that selection — which part of the book — is a product decision, not a preprocessing detail.

Latency vs. iteration speed. Mid-build, the text model was slow enough that a single full run ate the feedback loop. Rather than fake it, we cut scope on purpose: prioritize a small page range, reuse accepted upstream artifacts, spend zero new text tokens, and get something real on screen. Constrain the run, not the honesty.

Two people, one repo, no stomping. Avoided almost entirely by the generated contract seam and worktree isolation. The one place we did collide was NEXT_SESSION.md — and since each lane appended its own dated section, even that merged cleanly.

Infrastructure honesty. A Mongo 7 archive is currently being served by Mongo 8.2.3. It works. We wrote the cross-major restore warning into the handoff anyway, because a demo that succeeds doesn't retroactively make the setup correct.

Accomplishments that we're proud of

  • A real, immutable manga edition generated from a real PDF — five composed pages, ten accepted panels, complete hashes, lineage, receipts, and exact cost.
  • A single generated contract seam that let two people build in parallel from day one and merge additively, with no breaking contract change to date.
  • Determinism end to end — the same run ID reproduces the same pages, and ReelSpec drives both the live player and the exported MP4 from one source.
  • Rejections are visible. Failed attempts and their costs are stored, not swallowed. We can tell you exactly what the waste was.
  • We shipped what actually worked. Nothing is marked complete without its fixture and visual evidence — a rule we enforced against ourselves more than once.

What we learned

  • The bottleneck was never comprehension — it was cold start. Books ask you to rebuild a world before they give you anything. Panels hand you the world free. That one asymmetry explains the entire shelf of unfinished books.
  • Consistency beats beauty. A merely-good panel of a character who looks right lands harder than a gorgeous panel of a stranger. Readers forgive rough art; they do not forgive a face that changed.
  • Push non-determinism to the edges. Let the model be creative inside bounded sessions, then make every downstream step deterministic. It's the difference between a demo and a product.
  • Generated contracts are a collaboration technology. They weren't a typing convenience — they were the reason two lanes never blocked each other.
  • Cutting scope honestly beats faking scope. "Pages 1–15, zero new text calls, here's the receipt" got us further than a broad claim we couldn't stand behind.

What's next for ScrollStack

  • Kill the empty balloons and finish the lettering pass so every visible word is deterministic renderer output.
  • Smarter source selection — skip front matter, find the scenes worth drawing instead of walking the PDF linearly.
  • The full reel loop in production, with signed media delivery, thumbnails, and persisted render receipts.
  • Reader-side continuity — progress that survives a reload, a library that remembers where the good part was.
  • Director's cut. Let a reader reorder panels, retitle a chapter, add a line of commentary, and share their own edition of a book. The scroll becomes authorship.
  • Cost transparency for readers — show what an edition cost to make. Nobody else does this. We think people would rather know.

Built With

Share this project:

Updates