The problem
Bedtime stories run out. Parents read the same book for the fifth night running, or they trawl for something new at the exact moment they have least energy to do it.
The interesting version of this problem is not “generate a story” — anything can do that now. It is delivering a specific story, calibrated to one child’s age and the values a family actually wants reinforced, illustrated, narrated, and reliably present before bedtime every single night without anyone pressing a button.
That last clause is the whole engineering problem. A story that arrives late is worse than no story at all, because it breaks a routine that a family has built their evening around.
What we built
Dreamtime is a subscription product with a React Native app on iOS, Android and web, a Next.js marketing site, and a Supabase backend that does the real work.
Each afternoon a cron job enqueues one generation job per household. A queue consumer drafts the story against the household’s saved children, ages and chosen values, then requests watercolour illustrations, then writes the finished story into the library so it is waiting well before bedtime.
Generation runs ahead of when anyone needs it. Nothing is generated while a parent waits, because a loading spinner at 7pm with a tired child is a product failure.
Decisions
Queue first, not request-response
The obvious build generates on demand when a parent opens the app. We inverted it:
everything is generated hours early and queued through pgmq inside Postgres. It costs
more — some stories are never opened — but it converts a user-facing latency problem
into a background job that can retry quietly.
One monorepo, shared types
Mobile, web, docs and backend live in a Turborepo with a shared types package. The contract between the app and the edge functions is a TypeScript type both sides import, so a change to the story shape breaks the build rather than production.
Costs modelled before launch, not after
Per-subscriber cost is modelled per story: input and output tokens, illustration generation, storage and delivery, converted to sterling at a fixed rate. That model decided the price point, and it flagged a ~30% cost rise from a model tokeniser change before it showed up on a bill rather than after.
Where it is now
Live on the App Store and Google Play with a paid subscription, a free trial, and an SEO and content programme running against it.
- React Native
- Expo
- Next.js
- Supabase
- Deno edge functions
- PostgreSQL
- pgmq
- Stripe
- Cloudflare Workers
- Turborepo