Inspiration
There is a gap between a photograph and the story underneath it. You stand at a ruined colonnade, take a photo, and the stone just stares back — nothing tells you that people argued philosophy there 2,400 years ago, or that the same corner will be buried under neon in 125 years. The place has a voice; it needs a translator.
A second spark came from a failure in an earlier project: a generated picture book whose six pages contained six unrelated strangers. The only fix that worked was repeating one fixed character description, word for word, in every prompt. That measured finding became this project's central pillar — the scene bible.
What it does
Photograph a place — a ruin, a street corner, a café, your own kitchen. The app reads the image, works out what and when it is, and hands you an editable sentence:
Tell me about Skógafoss waterfall, Iceland, set in the present day, in the voice of a gossiping friend, told as the place itself.
Every bold span is a button. Tap Transform and you get a 4–6 panel illustrated comic, narrated aloud, exportable as a vertical 1080×1920 video for Reels or TikTok.
Four lenses decide the world — Socratic Anime, Future-Sketch, Noir, and Pet's-Eye View, that last one narrated by whoever actually lives in the room. Four narrators decide the telling, each with its own writing register and its own voice. Three perspectives decide the angle, including the one people react to: the place itself.
The key word is your photograph. The geometry and the vantage point stay recognisable under the artwork — it is not a generic comic about somewhere vaguely ancient, it is about the colonnade you just walked past.
How we built it
A SwiftUI iOS app over a FastAPI backend, split across five endpoints so the user starts reading before generation finishes and one failed panel never costs the whole comic.
photo + intent
│
├─ /v1/recon Claude Haiku 4.5 → place, era, suggested lens/narrator (~3s, editable)
├─ /v1/cast Claude Sonnet 4.6 vision → scene bible + narrated scripts (validated)
├─ /v1/panel Gemini 2.5 Flash Image, image-to-image, once per panel
└─ /v1/narrate ElevenLabs, in the narrator's voice
video export AVFoundation, on the device — Ken Burns push + burned-in subtitles
Lenses and narrators are data, not code. A lens bundles its system prompt, image style, UI accent and loading copy into one JSON entry, and both platforms decode the same file. There is no if lensID == "socratic-anime" anywhere and there cannot be — so adding a lens is a data change, served over the air.
The scene bible keeps panels consistent. Derived once, in the same vision call that writes the panels, then injected verbatim into every panel prompt. The source photograph is also sent to the illustrator on every panel. Two anchors — the bible for faces, the photo for place. Losing either is instantly visible.
The machine's guesses are correctable. v1 inferred place and era silently while writing the panels, so when it was wrong there was no seam at which anyone could notice — a modern footbridge got narrated as a 19th-century argument. /v1/recon now pulls that inference into a cheap pre-flight call and hands the guesses back as editable blanks.
| Layer | Technology |
|---|---|
| iOS app | Swift / SwiftUI, iOS 17+ |
| Monetization | RevenueCat (subscriptions + consumable credits) |
| Shared domain | Swift Package (ChronoCastCore) — UI-free, testable without a simulator |
| Backend | FastAPI (Python 3.11), Docker on Render |
| Scripting / recon | Claude Sonnet 4.6 (vision + tool use), Claude Haiku 4.5 |
| Illustration | Gemini 2.5 Flash Image (image-to-image) |
| Narration | ElevenLabs (eleven_turbo_v2_5) |
| Video export | AVFoundation, on device |
Monetization with RevenueCat
The pricing model is the part I would defend hardest, because the obvious version does not survive arithmetic.
A cast costs ~$0.39 to produce. A $7.99 weekly subscription nets ~$6.79 after Apple's 15%, so unlimited casts break even at about 17 casts a week — a number an engaged user passes without trying. An annual plan would have been worse: break-even at ~5 casts a month, locked in for twelve months, priced before a single day of real usage data existed.
So Pro is capped, and the caps are backed by top-ups:
| Price | Included | |
|---|---|---|
| Free | — | 1 cast, 4 panels |
| Pro Weekly | $7.99 | 10 casts/week, 6 panels |
| Pro Monthly | $23.99 | 30 casts/month, 6 panels |
| Top-up | $9.99 | 10 casts, never expire |
The cap bounds the tail, not the median. At typical utilisation nobody reaches it; the people who do are exactly the people who should be buying credits. Worst case moved from unbounded loss to roughly break-even.
Two details worth naming. Panel count is a tier, enforced in the schema — free casts get 4 panels and Pro gets 6, a ~28% cost difference, and the tool schema pins minItems == maxItems because when offered "4 to 6" the model picked 6 every single time. And consumables grant no entitlement: credits never expire in RevenueCat, so attaching them to the ChronoCast Pro entitlement would have handed every Pro feature away permanently for one $9.99 payment.
The paywall leads with the four narrators rather than the plans. A quota is an abstraction; four voices with names are a thing you can want.
Challenges we ran into
Video export belonged on the phone, not the server. It began as an endpoint and failed instructively — a missing ffmpeg filter, then a six-panel render dying on memory rather than time. The honest conclusion was that the job never belonged on a server: the panels, the narration and the captions are already on the device, and the upload existed only to reach ffmpeg. Rebuilt on AVFoundation it is faster, works offline, costs nothing per export, and the failure mode is gone rather than tuned. It shipped a silent video twice on the way, which taught the more useful lesson — the first fix made a failed audio insert non-fatal, turning a loud error into a clean render of a muted film. Failures that look like success are the expensive kind.
The cat that was not the same cat. Pet's-Eye View came back as six framings of one animal in one room, and a user note read "the cat is not same". The scene bible — this project's founding idea — was supposed to make that impossible. It only protects entities the model registers as characters, and nothing ever told it the narrator was one; the shared prompt even said "keep the cast to two or three people at most", which quietly argued against putting a cat there. So the cat was scenery, no appearance was written, and it was redrawn from scratch six times. The fix for the project's oldest idea came from a lens that had never had a human in it.
Accomplishments that we're proud of
- A streaming comic reader. You read panel 1 while panels 4 and 5 are still generating, so a 90-second job feels fast because you are never watching a bar.
- An editable sentence instead of a form, which also gives the AI's guesses somewhere to be wrong in public — the only way a user can fix them.
- A backend that structurally cannot leak one user's cast to another. No database, no object store, no accounts — the privacy property is architectural, not a policy promise. The 1.1 rating is anonymous the same way: its request body has no field for any identifier, so there is nothing to link a rating to even in principle.
- 167 tests across two languages, including a contract test that fails if the shared JSON drifts from what Swift decodes.
What we learned
- Empirical prompt engineering beats theorising. The scene bible came from a measured failure in another project, not a hypothesis — and building the fix into the architecture was worth more than any reasoning about what the model "should" do.
- A constraint that content satisfies by accident is not a constraint. Nothing required panels to look different from each other, and three lenses never revealed it because their stories moved through centuries and the framing followed for free. It took a lens set in one room to show the rule had never been written down.
- Ask, then wait. The in-app rating shipped before any of the fixes it produced. Two notes — "the images were deteriorated" and "the cat is not same" — pointed at two entirely different defects that looked identical from outside.
What's next for ChronoCast AI
Two more lenses are half-shipped: Napkin Pitch, and Solarpunk — the optimistic twin to Future-Sketch, after a user pointed out that "cyberpunk" is a dystopian genre and the model was simply obeying the word. Both card designs are already in the binary, so both ship as catalog data with no App Store release. That note generalises into per-lens variants: every lens carries a bias its user cannot steer, and the answer is an optional list each lens declares for itself rather than one axis bolted onto all of them. Beyond that, counters that survive a reinstall, a render that survives leaving the app, and panel re-roll — all small changes over a backend that already supports them, and all deliberately held out of 1.1 rather than forgotten.
Log in or sign up for Devpost to join the conversation.