Project 04
Pictale
Shipped iOS product — drawings into narrated stories
Why this is here
Context
This is not my professional direction. It is evidence that I can take a product from nothing to a public release on my own — client, backend, pipeline, billing, store submission — and keep maintaining it.
The robotics work on this site is where I want to spend my career. Pictale is the demonstration that I can also own the parts of engineering that have nothing to do with vehicles: asynchronous system design, idempotent transaction handling, third-party integration, and shipping something people actually use.
The system
Drawing → story → narration
A child photographs a drawing and picks the shape of the story — a theme, a hero name, a length, one of six narrator voices. The drawing is sent to a multimodal model, which reads the picture and writes a story about what is actually in it, constrained to the child's age. The story is narrated by a neural text-to-speech voice and returned as audio with word-level timing data, which drives per-word highlighting synchronised to the playhead.
The pipeline is deliberately asynchronous. The client never holds a long-running request open — it writes a record and listens for changes, and the backend advances that record through generation, narration and completion. A multi-minute generation survives the app being backgrounded.
Architecture
Event-driven, serverless
Engineering work
Sole developer
I designed, built, shipped and maintain the whole product. The parts that were actually difficult:
- Event-driven generation pipeline. Writing the record enqueues the work; backend triggers advance it through generation, narration and completion; the client subscribes to the same record for live progress. No long-lived request to time out and no polling loop in the app.
- Defensive structured-output parsing. Generation requests structured output but never assumes it. Parsing degrades through progressively looser strategies, so a malformed model response still yields a usable story instead of a failed job.
- Word-level read-along playback. Timing data from the synthesis API drives per-word highlighting against the audio playhead, with auto-scroll keeping the current word visible — the feature that makes the app usable by a child still learning to read.
- Transaction-based credit handling. Credits are reserved and debited inside a server-side transaction scaled to story length. The client checks affordability for user experience only; the server is the authority.
- Idempotent subscription webhook. Entitlement events are deduplicated against a persisted ledger before any balance changes, so provider retries cannot double-credit an account. Refunds reverse their grant and balances clamp non-negative.
- Randomised explore-feed sampling. A fresh random sample per theme using stored randomisation keys and seeded range queries with wrap-around, then rotating the keys of the rows returned — sampling without scanning the collection or maintaining a cursor.
- Safety inside the pipeline. Age is a generation constraint rather than a display preference, and unsuitable input halts the pipeline before a story exists — the record is marked rejected and nothing reaches the library.
Product
Published App Store listing images
Result
Live and maintained
Pictale is published and live on the Apple App Store — Education, rated 4+, free with in-app purchases, iOS 18 or later.
Pictale: Draw Your Story on the App Store (opens in a new tab)
Source code is private — Pictale is an actively developed production application. The linked repository is a portfolio overview with high-level architecture and publicly available product media only.
Tech stack
Client
- Flutter
- Dart
- Provider
- iOS 18+
Backend
- Firebase Cloud Functions
- Node.js
- Cloud Firestore
- Cloud Storage
AI pipeline
- Google Gemini (multimodal)
- Neural TTS with word timings
- Structured output parsing
Platform
- Firebase Auth
- App Check — App Attest
- Adapty subscriptions
- Firebase Analytics