Saga: Baby Names

Swipe baby names. An on-device model learns your taste.

Status
In testingWeb app live at saga-baby-names.web.app; Android 1.0.0 in Google Play internal testing; iOS 1.0.0 ready in App Store Connect, not yet submitted
Platforms
iOS, Android, Web
Role
Solo: product, design, data pipeline, recommender, web, iOS, Android, backend and store prep
Timeline
Sep 2026 – Nov 2026142 commits
Version
iOS 1.0.0 (build 1) · Android 1.0.0 (version code 1)

01 The story

A swipe-first baby-name app for iOS, Android and the web. Every swipe updates a Bayesian taste model on the device, and each of 14,598 names carries its origin, meaning, pronunciation and 145 years of US birth data. Partners swipe separately, and a Final Round ends with Saga calling the one.

I built Saga because picking a baby name usually means scrolling an endless alphabetical list. Saga makes it a deck of cards. On the phones you say who you're naming and swipe eight names picked to cover as much of the style space as possible; on the web you tap favorites on three style boards. Either way the deck is shaped around you before the real swiping starts. Every card flips to show the name's story: the word it started as (Θεόδωρος, 雪, Méabh), what it meant, how to say it, and how its popularity has moved since 1880.

The names come from a Python pipeline that merges US Social Security records from 1880 to 2025 (105,966 names) with Wiktionary in about 50 language editions, the CMU Pronouncing Dictionary, a letter-to-sound model and Wikidata. It resolves etymologies through a chain of nine fallbacks, folds 669 nicknames into their full names, groups spellings into families, and labels a name a modern coinage rather than guess. Each of the 14,598 cards gets a 123-number feature vector covering origin, meaning, sound, era, popularity and trend, quantized to int8 so the whole matrix is about 1.8 MB.

The taste model is a Bayesian logistic regression with a diagonal Laplace posterior, updated in microseconds per swipe and sampled with Thompson sampling. It runs in a web worker on the web and is ported line for line to Swift and Kotlin, so nothing round-trips to a server. Every default was chosen in a simulation lab: 11 personas, paired runs on fixed seeds, and a change ships only if it beats two standard errors and holds on holdout seeds. That lifted the simulated like rate in the first 20 swipes from 36% to 78%.

Couples each keep their own model. Their loves sync through Firestore into a shared nest, a match drops the name's letters in with a celebration, and 20% of each deck comes blind from the partner's loves, which in simulation cut the wait for a first match from 61 to 89 swipes down to 18 to 36. The Final Round ranks finalists with a Bradley-Terry model; on the phones a Laplace-approximated posterior decides when Saga is 80% sure and calls the one. There's also a pet mode, a name Almanac, a Name Galaxy built with UMAP and story reels.

I built it solo between September 30 and November 4, 2026. The web app is live on Firebase Hosting. Android 1.0.0 is on Google Play's internal testing track, and the iOS 1.0.0 build and listing are ready in App Store Connect but not yet submitted for review.

▶ Film

A 31-second promo, made in code with Remotion.

02 What it does

  1. 01

    Swipe deck with love, pass, maybe and super-love; it reshapes after every swipe and says why a name is there ('Because you loved Maeve')

  2. 02

    Every card flips to its story: original word and script, literal meaning, pronunciation with audio, popularity since 1880, famous namesakes

  3. 03

    Partner and family nests: separate tastes, live matches with a celebration, Taste Match %, blended decks and a head-to-head Final Round

  4. 04

    Saga's call: favorites duel two at a time until the model is 80% sure, then a full-screen reveal names the one

  5. 05

    Name Galaxy: every name is a star placed by style similarity, with Saga's guess lighting up where your taste lives

  6. 06

    Story reels that tell one name's history in seconds, plus a Name of the day widget on iOS and Android

  7. 07

    Middle-name matcher and Initial Watch, ranked on real middle-name use from Wikidata, sound flow and initials that spell words

  8. 08

    Modes for naming a dog or cat (2,597 real pet names) and for exploring your own name, plus a US Almanac of top names since 1880

03 How it's built

Outside serviceSSA, Wiktionary, CMUdict, Wikidata, pets
ServicePython data pipeline
ServiceFirebase Hosting (app + data shards)
AppSwiftUI app + widget
AppCompose app + widget
ModelSimulation lab (personas, sweeps)
AppReact PWA
ServiceFirebase Auth
ModelOn-device taste engine
DataFirestore nests
DataBigQuery analytics

All recommendation happens on the device. Names data is static, built offline by the pipeline and served as cache-forever shards. Firestore only carries shared nests (partner loves, matches, slips, Final Round picks) and, when switched on, usage events that export to BigQuery.

All 15 connections, as text
  • SSA, Wiktionary, CMUdict, Wikidata, pets → Python data pipeline: download
  • Python data pipeline → Firebase Hosting (app + data shards): cards, int8 features, shards
  • Python data pipeline → SwiftUI app + widget: bundled data
  • Python data pipeline → Compose app + widget: bundled data
  • Simulation lab (personas, sweeps) → On-device taste engine: locked defaults
  • Firebase Hosting (app + data shards) → React PWA: app + data
  • React PWA → On-device taste engine: worker
  • SwiftUI app + widget → On-device taste engine: Swift port
  • Compose app + widget → On-device taste engine: Kotlin port
  • React PWA → Firestore nests: nest sync
  • SwiftUI app + widget → Firestore nests: nest sync
  • Compose app + widget → Firestore nests: nest sync
  • Firebase Auth → Firestore nests: rules
  • Firestore nests → BigQuery analytics: event export
  • BigQuery analytics → Python data pipeline: learned prior

04 Engineering notes

  • On-device taste model: diagonal Laplace Bayesian logistic regression over 123 int8 features with Thompson sampling, in a web worker (comlink) and ported line for line to Swift and Kotlin with shared test vectors

  • Simulation lab: 11 personas on the real 14,598-card deck, paired comparisons with a 2-standard-error bar and holdout seeds. It lifted the first-20-swipe like rate from 0.36 to 0.78 and cut swipes to 10 loves from 29 to 13

  • Python pipeline: SSA + Wiktionary (via kaikki.org) + CMUdict + g2p_en + Wikidata, a nine-step etymology resolver, nickname folding, name families, TF-IDF and phoneme SVD embeddings, 36 KMeans style clusters and a UMAP layout

  • Data ships as content-hashed static shards on Firebase Hosting with immutable caching and an offline PWA; the same files are bundled into the iOS and Android apps

  • Couples math: three blend modes, blind partner injection tuned in simulation, and Bradley-Terry ranking fitted by minorization-maximization on the web or Newton + Laplace with 4,000 seeded posterior draws on the phones

  • Firestore rules validate writes on every nest path and partner codes are single-use, covered by 35 emulator attack-and-flow tests; usage events export to BigQuery, where a learn job can fit the model's prior from real swipes

05 By the numbers

14,598
Name cards
105,966
Names searchable
123
Features per name
295
Source languages
78%
Simulated like rate in the first 20 swipes (15% shuffled, 36% earlier engine)
13
Simulated swipes to 10 loves (best baseline 30)
10.08 million
Simulated swipes in the model benchmark
2,597
Pet names

06 Stack

App

  • React
  • TypeScript
  • Vite
  • Tailwind CSS
  • Motion
  • three.js / react-three-fiber
  • Web Worker (comlink)
  • PWA (Workbox)
  • SwiftUI
  • WidgetKit
  • Jetpack Compose
  • Glance widgets
  • Remotion

Backend

  • Firebase Auth (anonymous, email, Google)
  • Cloud Firestore (Enterprise edition)
  • Firebase Hosting
  • Firestore BigQuery export extension

Data

  • Python 3.13
  • pandas
  • NumPy
  • scikit-learn (TF-IDF, TruncatedSVD, KMeans)
  • UMAP
  • g2p_en
  • jellyfish
  • US SSA baby names 1880-2025
  • Wiktionary via kaikki.org
  • CMU Pronouncing Dictionary
  • Wikidata
  • City pet-license open data
  • BigQuery

Infrastructure

  • Firebase Hosting with immutable data shards
  • Google AdMob native ads (phones)
  • Google UMP consent
  • Sentry (wired, off)

Tooling

  • pnpm workspaces
  • Vitest
  • Playwright
  • Firebase emulators
  • pytest + uv
  • XCTest / Swift Testing
  • Android instrumented tests
  • TypeScript simulation lab
  • Pillow store-panel generator
  • fastlane metadata layout

Lines of code (approx.)

  • TypeScript42,804
  • Kotlin33,657
  • Swift30,281
  • Python11,684
  • CSS502
  • SQL306

≈ 119,234 lines

Repositories

  • saga-baby-namesprivate

Single private repo, 142 commits between 2026-09-30 and 2026-11-04. A usage-analytics branch holds one extra commit that switches analytics on.