Skip to content

About

AI storytelling app for children that turns drawings into narrated stories — Flutter client, event-driven Firebase backend, multimodal generation and TTS. Published on the App Store.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 

Repository files navigation

Pictale app icon

Pictale

AI-powered storytelling app for children that turns a drawing into a narrated story.

A child uploads a drawing. Pictale reads the picture, writes an age-appropriate story around it, narrates it in a chosen voice, and plays it back with each word highlighted in time with the audio.

Published and live on the Apple App Store.

Download on the App Store

Flutter · Dart · Firebase · Cloud Functions · Gemini · Neural TTS · iOS 18+

Upload a drawing A drawing becomes a narrated story Choose a theme and a narrator voice Story library and favourites

Overview

Most children's story apps hand a child someone else's story. Pictale builds one from something the child made.

A parent photographs the child's drawing and picks the story's shape — a theme, a hero name, a length, and one of six narrator voices. The drawing itself 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 and to a warm, age-appropriate tone. The finished story is then narrated by a neural text-to-speech voice and returned to the app as audio with word-level timing data.

In playback, that timing data drives per-word highlighting synchronised to the audio, with the view scrolling to keep the current word on screen — so a child still learning to read can follow along rather than just listen.

Stories are saved to a personal library, can be favourited, and can optionally be published to a shared explore feed.

Product features

  • Drawing-led generation — photograph or pick a drawing, crop it, and generate a story from the image itself
  • Prompt-led generation — describe an idea instead of uploading a drawing
  • Story configuration — six themes, a custom hero name, three story lengths, six narrator voices
  • Narrated playback with word-level read-along highlighting, auto-scroll and ±10s seek
  • Personal library with favourites and play counts
  • Explore feed of shared stories, with per-story opt-in publishing
  • Achievements — badges and progressive titles earned across total and per-theme story counts
  • Sign in with Apple and Google Sign-In
  • Subscriptions and credit packs, with the first story free during onboarding
  • Phone and tablet layouts
  • Connectivity-aware UI with real reachability checks and recovery

Product flow

   Drawing or prompt
           │
           ▼
   Story setup                theme · hero · length · narrator · shared or private
           │
           ▼
   AI generation              the drawing and the constraints are read together
           │
           ▼
   Safety processing          unsuitable input is rejected before a story exists
           │
           ▼
   Text-to-speech             narration audio plus word-level timing data
           │
           ▼
   Story + narration          read-along playback, library, explore feed

Each stage is a state transition on a single story record. The app subscribes to that record while generation runs, so the loading screen reflects real backend progress rather than a timer — and a multi-minute generation survives the app being backgrounded.

System architecture

Pictale system architecture: the Flutter app writes a story record to Firestore, which triggers a chain of Cloud Functions for generation, narration and completion, calling Gemini and a neural TTS provider and persisting media to Cloud Storage, with Adapty entitlements and App Check attestation on separate paths.

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.

What I built

Sole developer. I designed, built, shipped and maintain the whole product — mobile client, backend, AI pipeline, monetisation and App Store release.

  • Mobile application — the Flutter client end to end: feature-first module structure, a Provider-based state layer, custom glass-morphism UI components, responsive phone and tablet layouts, and the read-along audio player.
  • Backend and cloud infrastructure — the Cloud Functions codebase, the data and storage layout, the trigger topology chaining the generation stages, and the query design behind the explore feed.
  • AI workflow — constraint construction, structured-output handling, defensive response parsing, and the safety gate that rejects unsuitable input before any story is produced.
  • Audio pipeline — asynchronous narration orchestration, persistence of audio and word-timing data, and the client-side synchronisation that turns those timings into read-along highlighting.
  • Monetisation — subscription and credit-pack integration, paywall routing, a server-authoritative credit ledger, and idempotent entitlement processing.
  • Release — App Store submission, privacy and data-use declarations, child-safety and advertising compliance configuration, and ongoing versioned releases.

Engineering highlights

Event-driven serverless generation pipeline. Story creation is modelled as a state machine over a single record. 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. There is no long-lived request to time out and no polling loop in the app.

Asynchronous multi-stage generation. Generation and narration together can take minutes. Rather than holding a request open, each stage hands off to the next through a durable state change, so the work is resilient to app backgrounding, network loss and provider latency.

Multimodal generation from a child's own artwork. The drawing is the primary input, not a text description of it. The image and the generation constraints — the child's age, theme, hero, language and target length — are sent together, so the story is written about what the child actually drew.

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. Token usage and per-story cost are recorded on every generation.

Asynchronous text-to-speech orchestration. Narration runs against a task-based synthesis API: submit, poll with bounded retries and explicit failure handling, then download and persist both the audio and its word-level timing data. Playback duration is derived from the timing data rather than probing the audio file.

Word-level read-along playback. Timing data drives per-word highlighting synchronised to the audio playhead, with automatic scrolling that keeps the current word visible. This is the feature that makes the app usable by a child who is 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 — and the free onboarding story is enforced server-side so it cannot be replayed from the client.

Idempotent subscription webhook. Entitlement events are deduplicated against a persisted ledger before any balance changes, so provider retries and redeliveries cannot double-credit an account. Refunds reverse their grant, balances are clamped non-negative, and every processed event is written to an audit record.

Randomised explore-feed sampling. The explore feed draws a fresh random sample of shared stories per theme using stored randomisation keys and seeded range queries with wrap-around, then rotates the keys of the rows it returned. It samples without scanning the collection or maintaining a cursor, on indexes built for exactly those queries, and prefetches all themes in parallel at session start.

Device attestation. App Check attests requests using Apple's App Attest and Google's Play Integrity, so backend resources answer to genuine app installs rather than arbitrary clients.

Layered caching. Storage download URLs are cached in memory and on disk, remote images through a disk-backed image cache, and freshly generated audio and artwork are downloaded to local files — so the first playback of a new story starts immediately and survives a dropped connection.

Responsive Flutter UI. A device-class breakpoint threaded through the widget tree drives distinct phone and tablet scaling across every screen, under a portrait-locked dark theme built on custom glass-morphism components.

Safety-focused design

Pictale is rated 4+ and built for children, so safety constraints sit inside the generation pipeline rather than being applied after the fact.

  • Age-conditioned generation. The child's age is a generation constraint, not a display preference — it shapes vocabulary, length and content.
  • Tone constraints. Generation is explicitly constrained toward warm, positive stories, with violence, fear and unpleasant language excluded by construction.
  • Input suitability check. If an uploaded drawing or a typed idea is unsuitable for children, the pipeline halts before a story exists. The record is marked rejected, nothing is written to the library, and the app tells the user plainly that the content could not be used.
  • Defensive generation handling. Unexpected or malformed model output is contained and recovered from rather than surfaced to a child mid-story.
  • Opt-in sharing. Stories are private unless the user explicitly chooses to share them, and the onboarding story is never shared.
  • Advertising compliance. Advertising is configured as child-directed and under-age-of-consent, restricted to the most conservative content rating, shown only to non-subscribers, and never during onboarding.
  • No tracking. The app performs no cross-app tracking, requests no tracking permission, and disables advertiser-ID collection. A privacy manifest declares data use, and analytics data is unlinked.
  • Account deletion. Users can delete their account from within the app; a backend function removes both the stored profile and the authentication record.

Subscription & entitlements

  • Subscription-gated creation, presented through remotely configurable paywalls so pricing and presentation can change without an app release.
  • Credit-based consumption — each story costs credits scaled to its length, so cost tracks the generation and narration work actually performed.
  • Consumable credit packs for subscribers who create heavily.
  • Free first story during onboarding, enforced on the server.
  • Entitlement synchronisation — subscription state is reconciled from provider webhooks into the user record, with purchase and restore paths also handled on-device. The two paths are scoped so they cannot overwrite each other's results.
  • Webhook idempotency — every entitlement event is deduplicated against a persisted ledger before any balance is touched, with refund reversal and non-negative balance clamping.

Screenshots

Published App Store listing images, unmodified.

Upload a drawing A drawing becomes a narrated story
Choose a theme and a narrator voice Story library and favourites

Tech stack

Layer Technology
Client Flutter, Dart
State management Provider
Authentication Firebase Authentication — Sign in with Apple, Google Sign-In
Database Cloud Firestore
Object storage Firebase Cloud Storage
Backend Firebase Cloud Functions, Node.js
Story generation Google Gemini — multimodal vision and text
Narration Unreal Speech — neural TTS with word-level timings
Subscriptions Adapty — paywalls, entitlements, webhooks
Advertising Google Mobile Ads, child-directed configuration
Device attestation Firebase App Check — App Attest, Play Integrity
Analytics Firebase Analytics
Media image_picker, image_cropper, flutter_image_compress, audioplayers, cached_network_image
Platform iOS 18+

App Store

Pictale is publicly available on the Apple App Store.

Pictale: Draw Your Story

Education · Rated 4+ · Free with in-app purchases · Requires iOS 18.0 or later

Source code

Source code is private. Pictale is an actively developed production application. This repository contains a portfolio overview, high-level architecture, and publicly available product media only.


Mustafa Sepen · GitHub · App Store

About

AI storytelling app for children that turns drawings into narrated stories — Flutter client, event-driven Firebase backend, multimodal generation and TTS. Published on the App Store.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors