You rarely find a designer who balances elite visual craft with the systematic thinking required for modern product development.

Sang H., VP of Design, Calm

How Growl was made, seat by seat

Four phases in order. Dashed tools are planned, not shipped.

01Thinkbefore the first screen · Make
02MakeMay to October · Ship
03Shipbefore launch · Run
04Runafter launch
in productionplanned before or after launch
The pod

Fourteen roles on one product. Hover a tool for what it does here; open a seat for what it produces and how it is directed.

Also

  • Growl for Android
    Bringing Growl to Android on the same design system and backend.
    In progress
  • Two new apps, with friends
    Two more products in the works, designed and built the same way.
    In progress
All work/Growl, Zovie Software

500 recipes, 4,794 narrated steps

Growl reads each step aloud, waits for you, and listens. Every image, clip and narration in the app is generated by a pipeline I designed. Built with Claude Code under direction.

The brief

The role
Founder, designer and builder, solo. Product, design system, SwiftUI app, backend, and the content pipeline that produced every asset in it.
The team
Me, Claude Code, and six parallel agents for bulk authoring. Review was never delegated.
The timeline
May 2026 to today. About 1,630 commits. The catalog reached 500 recipes on October 14, 2026. Launch targeted for end of October, pending Apple's organization conversion.
The problem
Recipe apps assume you are reading. Cooking assumes you are not: your hands are wet, floured, or holding a pan, and the app does not know you are behind.
The bet
Produce every recipe in-house as a narrated, step-by-step cook instead of licensing thousands, and make the whole cook work without touching the screen. The hard problem was never the app. It was producing 500 recipes of narration and imagery at a cost and quality one person could sustain.
What I did
Designed the app and its system in Figma. Set the architecture: SwiftUI, Supabase, Cloudflare R2, StoreKit 2. Wrote the rules the AI built from, approved every generation prompt, reviewed every contact sheet by eye, and tested every change on device.

Five months

Build timeline

Nine milestones from first commit to the launch gate, May 21 to October 14. Drag the handle or use the arrows.

The app, screen by screen

Growl, Home: Where you left off
01 · Home

Where you left off

Shortcuts to what you saved and what is cooking this week, then the recipes you liked but have not made yet. The green bar is the player: a cook is in progress, and it follows you through the app.

Growl, Search: Ask for a dish, not a keyword
02 · Search

Ask for a dish, not a keyword

One field, then rows chosen for you and the catalog by cuisine. Every card carries the total time up front, because that is the first question in a kitchen.

Growl, Recipe: What it costs you in time
03 · Recipe

What it costs you in time

Cooking time, overall time, and the clock time the dish will be ready if you start now. Level, step count, one play button. Nothing to read before deciding.

Growl, Directions: Every step has a length
04 · Directions

Every step has a length

The whole recipe as a list, each step with its own minutes, so a ninety-minute rise is visible before you commit, not discovered in the middle.

Growl, Ingredients: Scale it, then shop it
05 · Ingredients

Scale it, then shop it

Serving size and measurement system are controls, not footnotes. Change either and every quantity follows. Tap an ingredient to send it to groceries.

Growl, Groceries: One list across recipes
06 · Groceries

One list across recipes

Thirteen items from this recipe, counted on the tab. Add all, clear all, or pick. The list is the bridge between planning on the couch and cooking at the stove.

Growl, The player: Growl reads, you listen
07 · The player

Growl reads, you listen

The current step, read aloud, with the key ingredient underlined. The pill is red: the mic is closed while Growl speaks, so it never hears itself. Hands stay on the food.

Growl, Per-step video: Motion only where it teaches
08 · Per-step video

Motion only where it teaches

Some steps are better watched than heard. Tempering eggs is one. Video is a bonus on a step, not a format for the recipe, so the catalog is not limited to what was filmed.

Growl, Hands-free: Your turn to speak
09 · Hands-free

Your turn to speak

The mic turns green the moment narration stops. Say Next, Back, or Repeat, nothing else, and it happens. Dark on purpose: this screen runs on a counter, across the kitchen.

Growl, Timers: The step sets the timer
10 · Timers

The step sets the timer

Three minutes for the pastry cream, with the alarm time shown. Timers come from the step itself, and with Live Activities they keep running on the Lock Screen.

Growl, How to use: The rule, stated
11 · How to use

The rule, stated

When the mic is live and when it is not, in the app’s own words. An affordance nobody can see is not an affordance, so the rule is written down where it applies.

Growl, Library: Yours, in three shelves
12 · Library

Yours, in three shelves

Recipes, collections, and cookbooks you assemble yourself: a holiday menu, a city, a Tuesday. Liked Recipes is the first shelf because it fills itself.

Growl, Cookbook: a cookbook called Christmas Time with four recipes and recommendations from liked recipes
13 · Cookbook

A menu you assemble

Name it, add to it, give it a cover. Under the recipes you chose, Growl suggests more from what you have liked, so a holiday menu grows from the dishes you already wanted to make.

Thirteen screens

The shipped app on device, in the order a cook meets it: find a dish, see what it costs in time, shop it, then cook it with your hands on the food while Growl reads. It plays through on its own; tap a dot to stop on one, or the screen to see it large.

What happened

A 500-recipe catalog, every step narrated, every image generated, designed and built and produced by one person in five months, feature-complete for launch.

500
Recipes, 4,794 steps, every one narrated
Measured 2026-10-16 from the repo, pipeline logs and App Store Connect
4,717
Generated step images, plus 77 clips where watching teaches
Measured 2026-10-16 from the repo, pipeline logs and App Store Connect
12 of 12
Known defects fixed by the final image reference rule; simpler rules fixed about half
Measured 2026-10-16 from the repo, pipeline logs and App Store Connect

Measured October 16, 2026

Designed for hands you cannot use

Voice is the thesis, and the hard part was not recognition. It was deciding when the app is allowed to listen, and being honest about it. The microphone opens only in the gaps between narration, because while narration plays the app would hear itself. The pill reads red while Growl speaks and green when it is listening.

When is the app allowed to listen?
Tried on device
Apple Voice Control. One wake word per command. Fourteen "Hey Siri" for a fourteen-step recipe.
Chosen
Turn-taking. Read the step, then listen. Six everyday commands, none of which can destroy state.
Rejected
Always-on mic. The app answers to its own narration.
Cost. A cook waits for the step to finish before speaking. Accepted, because a misheard command must always be cheap.

The pushback. When per-step video shipped, should "play video" work during narration? Letting one command through the closed mic window was technically clean and failed on honesty: an affordance nobody can see is not an affordance, and a pill reading red while secretly accepting a command makes the pill a lie. The tap stayed; voice stayed for the hands-free moment it was built for.

“Next”you, from across the kitchen
0:00 / 0:17
One step, with sound

Growl reads it, the pill goes green, and the next step arrives. Seventeen seconds, unedited.

The listening pill in its two states: green microphone with the meter lit, and red microphone struck through
The state is always visible. Green with the meter lit when it is your turn. Red, struck through, while Growl speaks.
The Dynamic Island on a locked iPhone: the recipe thumbnail on the left, the 59:40 step timer on the right
The Live Activity in the Dynamic Island: the recipe on the left, the step timer on the right, with the phone locked. A 59-minute braise does not need the screen on. Ships before launch.

Made video a per-step bonus, not a recipe format

A recipe was either video, every step a clip, or audio, every step a still. That forced one answer for all eighteen steps of a dish where three need motion and fifteen do not. The test was always whether watching teaches something the narration cannot, and that is a question about a step. The catalog went from 617 clips to 77. The Watch & Cook browse surface was deleted in the same change.

1× / 6×
Cost of a still against a clip
Measured 2026-10-16 from the repo, pipeline logs and App Store Connect
4.6% / 10.7%
Defect rate, stills against clips
Measured 2026-10-16 from the repo, pipeline logs and App Store Connect
617 → 77
Clips, chosen by demand and by whether motion teaches
Measured 2026-10-16 from the repo, pipeline logs and App Store Connect

The cost score informs the format and never decides it. The hardest steps to generate, pleating, rolling, forming, are the ones a cook most needs to see.

Separated demand from friction

An early scoring script ranked recipes by how well they would render, and nothing weighed demand against it. Measured later: 50 soups and 2 salads out of 173 recipes. Every step of that reasoning was sound and the outcome was nobody's plan. Fifty more recipes went in and nothing moved; the redistribution was a deliberate act at a specific moment, not drift. The fix was structural: which dishes is answered by demand, what a US home cook makes on a Tuesday, and which format by whether motion teaches. Soup & Stew went from 29% to 14% of the catalog, not the 11% projected, because soups kept being added. Seafood went 3 to 27, Salad 2 to 15. Sushi went 2 to 3, because an empty category is not automatically worth filling.

Demand picks the dish

Three points from git. Bars are share of the whole catalog; the dish-type field is multi-value and nullable, so rows do not sum to it. Soup & Stew is the headline, 29% to 14%. Sushi, hatched, is the control: the one empty category the rule said not to fill.

Built the pipeline around how the model fails

Every image and clip in Growl is generated. The pipeline is 106 scripts across thirteen steps, from sourcing a recipe against three references to the final audit. Not a single defect reviewed contradicted its prompt. Every one filled a silence, so the test on a finished prompt is not "is this accurate?" but "what would a stranger have to guess at?"

The change
What came back
Tonkatsu, five generations

Four prompts produced four wrong images. Attempt four was better than three by every measure and failed anyway, because a chop lying in a crumb bed is coated underneath. The fix was not wording. It was the premise.

Known defects fixed, of 12
Chain to step 1
6 / 12
Chain to previous step
6 / 12
Chain to nearest earlier step showing this vessel
12 / 12
Rules one and two scored about half. Rule three is the one that ships.
Corrected by measurement

Three versions of the image reference rule, each measured against the same twelve known defects. Hover a bar for what the rule was and how it failed.

Which earlier image does each step reference, so one kitchen carries across a recipe?
Rule 1
Chain to step 1. Fixed about 6 of 12 known defects.
Rule 2
Chain to the previous step. Fixed about 6 of 12.
Rule 3
Chain to the nearest earlier step that already shows this step's vessel. 12 of 12.
Why it belongs here. A rule corrected by measurement rather than argument.

What I didn't build. Knives in motion. Three waves failed; cuts are two stills joined by a hard cut, at a third of the cost. Allergen filters. "Nut-free" is drawn in Figma and deliberately unimplemented, because a free-from claim being wrong can hospitalize someone, and the claim covers the whole dish. "Contains nuts" asserts nothing; "Nut-free" asserts everything.

How AI was directed

CLAUDE.md is the project's memory: 2,018 lines and 36,512 words of rules, each tied to the failure that produced it. Figma is the single source of truth with three rules: never invent a value, 1:1 parity, build only what is marked Ready for Dev. Six parallel agents authored the final 542 image descriptions from one brief; all 41 contact sheets were then reviewed by eye. That found six defects the automated check passed. The numbers said 583 of 583 succeeded. A clean automated check is not a pass.

CLAUDE.md, excerpt. Verbatim, 2026-10-16. Operational detail removed and marked.
lines 1031 to 1040
## 1. Step 4 — Write and approve Higgsfield prompts

*CLAUDE.md lines 1031–1040, under the heading at line 883.*

> [Editor's note, not part of the source: the "third attempt" below refers to a step in
> a Vietnamese summer-roll recipe that called for peeled, halved shrimp and kept
> rendering whole shell-on shrimp with heads, eyes and antennae.]

**Negative exclusion does not work on anatomy, and the evidence says it backfires.** The obvious fix is the one that fixed the sriracha bottle and the fried-egg lace — name the wrong thing and forbid it — so a third attempt spelled it out exhaustively: `every shrimp completely shelled with the head, tail, legs and antennae all removed, smooth naked coral-pink flesh only, no shells, no heads, no black eyes, no legs, no antennae and no tails anywhere in the frame`. It came back with the heads, eyes and antennae rendered *more* prominently than the attempt that never mentioned them. Three generations, three failures, the most explicit one worst.

**Treat this as a real boundary on the exclusion pattern rather than a one-off.** Exclusion works where the forbidden thing is a *separable object or artifact* that can simply be absent from the frame — a squeeze bottle, a lace pattern, a round dome. It fails where the forbidden thing is *inherent to the scene the model has been asked for*, because the words summon it and there is nothing left to render without it. Two shapes of inherent, both confirmed:

- **Part of the subject's own body.** The model cannot render "shrimp" while suppressing what it believes a shrimp is. Fixed by describing the object positively and never naming the missing parts: `eight split shrimp halves laid cut face up in two neat rows, each half a smooth curled crescent of firm white flesh with a bright coral outer edge` — first attempt, no heads, no shells, no antennae.
- **A state the scene canonically has, most often heat.** A saucepan belongs on a lit burner, so asking for one that is *off* the heat asks the model to withhold the defining feature of the setting. Moussaka's `enrich-the-bechamel` said "lifted clear of the burner with the flame plainly out" and rendered the pan **on a live flame** with a single yolk sitting whole and unbroken like a fried egg for six of eight seconds — a technique error, since yolks over direct heat scramble. Tom Yum's `finish-off-the-heat` — the step *named* for it — said "lifted clear of the burner and set down on dark matte stone with the flame plainly out" and rendered a lit blue flame for the entire clip, opening on an empty burner and animating the pot **onto** it.

**The fix in both shapes is the same: replace the negation with a positive relocation or a positive description.** Do not say where the vessel is *not*; give it somewhere else to be, and put nothing in the frame it could be misread onto — `standing on a thick wooden board on a dark stone counter, the whole frame filled by the board and the counter with no stove, no cooktop and no burner anywhere in view`. Name the destination, fill the frame with it, and let the absence follow from the framing rather than from an instruction to withhold. Where a step's whole point is that the heat is off, that is a claim about *location*, so shoot it somewhere a burner could not be.

[removed]

> [The removed line, 1040, counted how many prompts across the catalog still use the
> off-the-heat construction and pointed at the queue tracking the unreviewed ones.]
lines 1420 to 1450
## 2. Audio recipes at batch scale — what 51 recipes in one wave taught

*CLAUDE.md lines 1420–1450, under the heading at line 1413.*

**The reference chain is the architecture, and its one failure mode is knowable in
advance.** Step 1 is a full establishing shot written out in words. Every later step
carries **step 1's job id** as its `medias` reference, so the whole recipe inherits one
surface, one light direction, one set of cookware from a single generation. That is what
makes cross-step consistency a property of the batch rather than something re-argued in
every prompt. **327 stills came back with 6 defects — 2.1%** — and five of the six were
the same thing: *the anchor imposed its vessel or its heat on a step that had legitimately
left both.* A step that moves the food into a baking dish, onto a wire rack, or off the
burner entirely is being asked to match a reference that contradicts it, and the
reference wins.

That first rule was *chain to step 1 by default; chain to the immediate predecessor
whenever the step changes vessel or leaves the heat*, and it fixed all five
first-attempt. **Wave 3 then showed it is a special case of the real rule, and it
misses the commoner half.** 323 stills, **15 defects — 4.6%**, of which **11 were
vessel drift**, and every one of the 11 is the same shape: *the step's reference did
not contain the vessel the step needed.* Two ways that happens, and only the first is
what the old rule describes:

- The step moves somewhere new, so the anchor contradicts it. (The old rule's case.)
- **The step continues in a vessel the anchor never showed.** Buttermilk Waffles'
  anchor is a hot waffle iron, so its batter was beaten *inside the iron*.
  Spanakopita's anchor is a sink, so the model invented a glass dish for one step and
  a ceramic one for the next. Chicken Piccata's pan sauce moved to a ceramic pan
  because the step before it was a plate of flour.

> **The rule: chain to the NEAREST PRECEDING STEP THAT ALREADY SHOWS THIS STEP'S
> VESSEL, falling back to step 1 when nothing does.** That subsumes both branches —
> a step that changes vessel finds no earlier match and falls back to the anchor,
> a step that stays in the pot finds the pot — and it is the only version that scored
> 12 of 12 against the wave-3 defects. [removed]

> [The removed clause, at the end of line 1450, named the script implementing the rule.]
lines 182 to 184 and 715 to 734
## 3. The rule was wrong while the practice was right

Two instances of the same failure, in two different sections of the file. The second
explicitly cites the first.

### 3a. Cuisine (and fusion) tagging

*CLAUDE.md lines 182–184, under the heading at line 168.*

**Why it has to be this way: `cuisine` is the key to a browse surface, not a provenance record.** It drives Browse by Cuisine, Search's cuisine tiles and cuisine affinity. A US diner tapping *Chinese* means "the Chinese food I eat", so **Orange Chicken is Chinese** for the same reason Chicken Parmesan is Italian — even though Panda Express invented it in 1987. Tagging it American by birthplace would file it beside meatloaf and apple pie, and it would be missing from the one tile anyone would look in.

Settled 2026-09-29, after reasoning from the old wording twice and getting it wrong twice. The practice was already right; only the rule was incomplete.

### 3b. Step 1 — Create the recipes_master.json entry

*CLAUDE.md lines 715–734, under the heading at line 634.*

  **The `Salad` row said "primarily vegetables or grains" until 2026-09-30, and
  that clause excluded dishes the catalog already tags.** Shipped **Cobb Salad**
  carries `["Salad"]` and its own description is "chicken, bacon, egg, tomato,
  avocado and blue cheese" — protein-forward, and correctly tagged. A drafting
  agent reading the old wording concluded Chicken Salad should be `null`,
  flagged the contradiction rather than guessing, and was right to: **no shipped
  recipe has "Salad" in its name without the tag**, so the practice was never
  what the rule said.

  The clause is replaced by *recognised as a salad by the person eating it*,
  which is the same browse-surface test the cuisine section settled on — this
  field keys Library's filter and Search's tag matching, so a US cook tapping
  *Salad* and not finding Chicken Salad reads as a bug, not as taxonomy. The
  exclusions still do the real work and are unchanged: a **hot** protein, a warm
  roasted bowl, and a cold dressed side from another tradition are all still
  out, which is what keeps Sigeumchi Namul and Buddha Bowl from qualifying.

  Same shape of failure as the cuisine section, which was reasoned from twice
  and got wrong twice before its wording was completed. An incomplete rule is
  worse than a terse one, because someone careful follows it off a cliff.
01 · prompts.md: One prompt per step, in Cursor
01 · prompts.md

One prompt per step, in Cursor

Seafood Paella’s steps as Higgsfield prompts, written to the same visual rules as every recipe: dark walnut, linen, strong directional light, 9:16. The prompt is the design; the still is its output.

02 · Cloudflare R2: The rendered still, at the path the app reads
02 · Cloudflare R2

The rendered still, at the path the app reads

Step one, sear the seafood, as an object in R2 with its CDN URL. Every step image in the catalog lives here under the same naming, so the app never needs to know which recipe it is.

03 · On device: The same step, read aloud
03 · On device

The same step, read aloud

That still as the step card in the simulator, with the ingredients underlined and the mic closed while Growl speaks. Prompt to asset to screen, nothing in between by hand.

04 · GrowlTheme.swift: The system, in code
04 · GrowlTheme.swift

The system, in code

The tokens the screen is built from, as Swift: surface, text, border and brand, each resolving light and dark in one place, with a live palette preview on the device. The same tiered idea as Foundation, one person deep.

One recipe, end to end

Seafood Paella in four views: the prompts that describe each step, the still those prompts produced, the step on device reading it, and the token file the screen is built from. Switch between them; nothing in the chain was done by hand.

Drag to compare. Cast Iron Ribeye, as designed and as shipped. Drag the divider to compare, drag anywhere else to pan, zoom with the control or ⌘ scroll, double-click to reset. The node reference under the slider is the Figma ID the SwiftUI file cites.

Cast Iron Ribeye recipe screen, Figma frame
Cast Iron Ribeye recipe screen, shipped SwiftUI build on device
100%
FigmaSwiftUI, device node id → File.swift
Design to code, 1:1

Drag to compare the Figma frame with the shipped SwiftUI screen. The node ID under the slider is the one the Swift file cites.

Stripped Figma file: tokens, components, Ready for Dev markers, and one logged divergence that owes Figma a correction.

Nothing in this vocabulary should be able to destroy state, because a misheard command must always be cheap.

A rule from the hands-free spec

Results

500 recipes, one dot each

500 recipes, one person

4,794 narrated steps, 4,717 stills, 77 clips, 650 ingredient photographs, 7,029 ingredient links across 3,358 steps. 13 cuisines.

Measured 2026-10-16 from the repo, pipeline logs and App Store Connect
Chain to step 16 / 12Chain to previous6 / 12Nearest step with the vessel12 / 12

Defects traced to one cause

Every reviewed defect filled a silence in its prompt. The reference rule that came out of it fixed 12 of 12 known defects.

Measured 2026-10-16 from the repo, pipeline logs and App Store Connect
617 clips generated. 77 kept, where motion teaches.

Stills by default, video where it teaches

Clips cost about six times a still and fail 2.5 times as often. Chosen by demand and by whether motion teaches.

Measured 2026-10-16 from the repo, pipeline logs and App Store Connect
Cast Iron Ribeye screen in Figma beside the same screen shipped on deviceFigmaSwiftUI, device

Design and code, 1:1

162 Swift files, about 45,700 lines, built under three Figma rules. Logged divergences owe Figma a correction.

Measured 2026-10-16 from the repo, pipeline logs and App Store Connect
Launch surfaceLLC conversionRebrand across 500 data filesPrivacy manifest, rewritten policyIn-app account deletionA paywall that refuses to guess a price

The unglamorous half, shipped

LLC conversion, a rebrand across 500 data files, a privacy manifest, a rewritten policy, account deletion, a paywall that shows nothing rather than a price it cannot charge.

Measured 2026-10-16 from the repo, pipeline logs and App Store Connect
500catalog gate, met Oct 140external cooks

Not yet in cooks' hands

The 500-recipe gate was met October 14. Launch targeted for end of October, pending device QA, the Live Activity, and Apple. Zero users and zero retention data until then.

Measured 2026-10-16 from the repo, pipeline logs and App Store Connect

What I'd change

Strategically

The scoring script that produced 50 soups ran for weeks before anyone counted. I'd put a demand check in front of any ranking that spends credits. When a system optimizes well and the result looks wrong, the objective is wrong, and the fix is to split the question, not re-weight the answer.

Technically

The same failure has been hit eight times: a check that matches a word rather than the thing it names. Each cost a batch. I'd write the general rule the first time a check passes something a human would fail, not the eighth.

All work/Foundation, Calm design system

A design foundation leadership passed on, shipped inside one app and adopted by a second business line

Primitives, semantics and a brand tier, so a new vertical is a set of values rather than a new library. Calm Sleep launched on it in four months. Calm Health adopted it for a product still being built.

Foundation components in light mode: stats, cards, buttons, inputs, chips, tabs, a hero cardThe same components in dark mode

The brief

The role
Sole designer on the system, tokens through adoption. I worked in tandem with the product's lead designer: she owned the product's design direction and I owned the system under it, so every token and component was built in the context of what her screens actually needed, and the context for using it was there for her, not handed over after. We split the work the same way both directions, design-initiated explorations and UXR- and product-driven tasks alike. It was a good partnership in both directions.
The team
2 designers (the lead product designer and me, the only one on the system), 2 engineers, 1 motion designer.
The timeline
July 2025 to October 2025. Calm Health adoption sessions ran weekly for three to four months after.
The problem
Research made the financial case for a sleep-centric app, so Calm moved to a multi-app strategy. Building new apps on the twelve-year-old foundation would compound its debt into every new product.
The bet
Rebuild the foundation before building more apps on the old one. Leadership passed, and not on the merits: design had no sponsor at the top, the case for a system was never understood there, and our VP of Design left midway through, saying on his way out that the business did not understand design's value. DTC's priority stayed shipping speed. I was the only person arguing that a system would pay everyone back. A few agreed; Calm Health agreed strongly. So the bet became: build it inside the app I was already designing from zero, and let adoption make the case.
What I did
Two jobs. With engineering, I captured Calm Classic as a system: its primitives, semantics and brand encoded as the first brand of the foundation, so twelve years of shipped UI resolved from tokens for the first time. Then I built Calm Sleep from scratch on that foundation, screen one onward, and taught Calm Health the mechanics in weekly sessions while their product was still in build. What is shown on this page runs on the Foundation file's own token values, checked against the shipped Sleep screens.

One of the most thorough systems designers I've ever worked with. He refreshed our outdated design system, brought it to parity with industry standards, and built relationships with engineering so handoffs were smoother and production matched design.

Jeany N., Staff Product Designer, Netflix, formerly Calm

What happened

Calm's first new consumer app in twelve years shipped on the foundation in four months, Android followed about six weeks later from the same system, and Calm Health adopted it for a product that had not shipped yet, without a mandate.

4 months
Concept to global iOS launch, entirely on the foundation
Calm, July to October 2025. Source: ship dates; Fast Company and TechCrunch coverage
2 platforms
iOS and Android from one source; Sleep shipped on both, Android six weeks after iOS
Calm, September to November 2025. Source: ship dates
5 brand modes
Foundation, Sleep, Health, Parenting, Fitness, one semantic set, thirty component pages
The Foundation token export and the brand collection

From the export: 73 primitives, 26 semantics per mode, 5 brand files of 10 overrides each. Rebuilt for this page: 7 components, 22 variants, 24 text styles mapped to Dynamic Type and Compose, 4 elevation styles, a 14-token Motion collection, and every derived value marked as such.

Three tiers, not two

Two tiers is the common setup and faster to author. The extra indirection cost authoring time up front and bought the ability to re-theme a whole vertical, light and dark included, without touching a component. Interaction states work the same way: a component picks a role, and the system resolves default, hover, active and disabled.

How many tiers?
Common
Two tiers: primitives and semantics. Faster to author. A new brand means forking the semantic layer.
Chosen
Three tiers: primitives, semantics, brand. Only the brand tier knows it is Sleep or Health.
Rejected
Per-product libraries. Five brands, five libraries, five places for drift.
Cost. One more concept for every designer to learn. The authoring itself costs almost nothing once you know how; the payback is a better mental model for the designers using the components. They reach for a role instead of a value, break fewer components, drift less, and go to engineering less often.
Primitivesblue/600 = #2a6dfaBrandaccent/light → blue/600Semanticaccent/accent → accent/lightOne semantic, resolved under each brand modeaccent/accent→blackFoundationblue/600calm.sleeppurple/600calm.healthpeach/600calm.parentingamber/600calm.fitnessgray/0calm.classicOnly the Brand tier knows what vertical it is. Components read Semantic and nothing else.
One semantic token, one brand mode, one primitive. The component never changes.
Fifteen states on calm.sleep, dark. Hover and active come from accent-hover and accent-active; focus is a 2pt ring on border/focus; disabled is a real value, accent-disabled with text/disabled, not opacity.

The one I re-spent. At Calm, disabled states resolved as a primitive at 40% opacity instead of carrying their own value. It halved the accent group and read correctly on every flat surface shipped, but a disabled control inside a dimmed container landed at 16%, and nothing in the system prevented it. The reconstruction spends the tokens: accent/accent-disabled and text/disabled are values, and the states figure below uses them.

One component, four brands

Nothing inside the card changes between verticals. Pick a brand and a mode; every value resolves through a semantic token to a primitive, and the trace shows the path. Tokens are from my export of the system, resolved exactly as the file resolves them.

Subtitle

Title

This is a paragraph for the component. It will show how the type layout holds across brands and modes.

Button
Button
Brand resolution

Pick a brand and a mode. The card never changes; the brand tier supplies every value. Sleep's accent shipped; the others are proof-of-concept values that show the tier working, not brand palettes.

73 primitives, 26 semantics per mode, four brand files that override only the accent family. Disabled and elevation tokens were unset in this export and are omitted.

The system, inspectable

A reviewer should be able to open the system, not take my word for it. Everything below runs on the Foundation token set, three collections and five brand modes, checked against the shipped Sleep screens. Three values differ from the file on purpose and are logged in the decision table: dark labels on the Parenting and Fitness accents, a label token for secondary buttons, and a tonal secondary in dark mode. Filter the semantics, switch brands and modes, check the states and the contrast.

SemanticResolves toValueUsed by
Token explorer

Every semantic token, what it resolves to in the selected brand and mode, and which components read it. The brand tier only ever answers for the accent family; that is the whole trick. Type to filter.

3 roles × 5 states × 6 brands, one definition
States, authored once

A component picks a role. The system resolves default, hover, active, focus and disabled, per brand, per mode. Disabled is a value, not opacity; focus is a 2pt ring on the accent. Nothing is redrawn.

Figtree, as shipped in both apps. Sizes 100 to 900 with paired line heights, each shown in the weight its role ships at; four weight primitives; space 0 to 24 on a 4px base; radius measured. From the export.
Scales

Type and spacing from the export, drawn at size. Radius was not in the export; the four values were measured off the Sleep screens.

Text on surface, every brand, both modes. WCAG AA is 4.5:1 for body text.
ThemeModePairRatioAA
Contrast, audited

Four pairs for every brand and mode, computed from the tokens; translucent fills are composited over the surface before measuring. Three fixes came out of this audit: dark labels on the Parenting and Fitness accents, a label token for secondary buttons, and a tonal secondary in dark mode so the quiet action no longer outshouts the primary.

One token, three platforms. The variable carries all three names.
// Figma variable: Semantic / accent/accent
Light → {Brand.accent/light}     Dark → {Brand.accent/dark}
Brand mode Sleep → {Primitives.blue/600}   Parenting → {peach/600}   Classic → {gray/0}
// Code syntax on the variable
WEB      var(--accent-accent)
iOS      Semantic.accentAccent
ANDROID  Semantic.accentAccent
// Tokens.swift, generated from the export
enum Semantic {
    static var accentAccent: Color { Foundation.brand.accent }
    static var surfaceBg: Color { Foundation.semantic.surfaceBg }
}
// No literals in views. Button(role: .primary) resolves default, pressed, focus, disabled.
// Tokens.kt, generated from the same export
object Semantic {
    val accentAccent: Color @Composable get() = FoundationTheme.brand.accent
    val surfaceBg: Color @Composable get() = FoundationTheme.semantic.surfaceBg
}
// FoundationTheme wraps MaterialTheme: ColorScheme.primary = Semantic.accentAccent,
// onPrimary = accentAccentOn, surface = surfaceSurface, background = surfaceBg,
// so Material components inherit the brand without being rewritten.
FoundationTheme(brand = Brand.Sleep, dark = isSystemInDarkTheme()) { PrimaryButton("Continue") }

How it reaches code, on both platforms. The system shipped to iOS and Android from one source. Every variable carries three names, CSS, Swift and Kotlin, so the token a designer sees in Figma is the identifier an engineer types. On iOS the semantics resolve through a Foundation theme object; on Android a FoundationTheme wraps MaterialTheme and maps the color scheme onto the semantics, so stock Material components take the brand without being rewritten. Sizes and spacing export as points and dp from the same numbers. I designed directly in SwiftUI and Jetpack Compose with the iOS and Android engineers reviewing the builds; Android followed Sleep's iOS launch about six weeks later, from the same system with no separate library. For Calm Health, Spencer S., their lead product designer and the person I met with weekly, ran the token export to code and handed it to their engineers himself, on the process I had walked him through: the system was theirs to operate before their product shipped.

Accessible by default, on both platforms

A system that is not accessible at the token level cannot be made accessible per screen. Foundation treats Dynamic Type, touch targets, contrast, focus and reduced motion as properties of the system, so a screen built from it inherits them without a designer remembering.

iOS content size category. The card is the Plan item card; nothing is redrawn, the type styles scale and the layout follows its rule.
StyleiOS Dynamic TypeComposeUse
Type scales, layout followsEvery style maps to a Dynamic Type text style, so the system's sizes are the defaults at Large and scale with the user to AX5. Side-by-side layouts stack past the accessibility sizes instead of truncating. size/measure-max · 600
Targets never shrinkButtons, rows, tabs and icon buttons pad to the minimum target regardless of their visual size. size/tap-min · 44pt iOS, 48dp Android
Contrast is computed, not assumedFour text pairs per brand and mode in the audit, translucent fills composited before measuring; the audit changed three tokens. accent-accent-on, accent-accent-secondary-on
State for every senseFocus is a token and a variant; disabled is a value, not opacity; reduced motion collapses inside each choreography token. Component descriptions carry the VoiceOver and TalkBack label pattern. border/focus · text/disabled · motion.reducedMotion
Accessibility, built in

Dynamic Type was non-negotiable for a product people use in the dark, half asleep, often with the largest text setting. Sleep shipped supporting it through the accessibility sizes, to AX5. The type ramp is defined against Apple's text styles and Compose's typography so scaling comes from the platform, and the components carry rules for what happens when text gets big. Pick a size and watch the card reflow.

Motion is a tier too

Calm is a product you feel more than read. The signature animations shipped: Breathe Bubbles and the check-in, designed in Rive with the motion designer, then rebuilt in Swift when the Rive runtime did not hold up on device. What did not ship was the system underneath them: motion tokenized in the same three tiers as color, so a check-in, a sheet and a breath would move with one vocabulary across Sleep and Health. Those values were defined and available to engineering before I left; Calm treated applying them as a refinement stage after launch, and I left before that stage came for any app. What is below is the system as I built it, with the values reconstructed here rather than copied from a build, and the two shipped animations beside it.

Values and vocabulary. A duration ladder that speaks to pace, and curves that speak to intent. Press play on a rung to run it.
Recipes. Each pairs a duration with an easing under the name of the interaction that uses it, so a human or an agent reaches for intent, not numbers. Press play to run; Token shows the recipe.
Sequences that key/value tokens cannot say: several elements, several phases, one name. Each carries its own reduced-motion collapse. Play either version.
Motion, as a system

Three tiers, the same shape as color. Primitives set the ladder and the curves; semantic recipes name the use; choreography names the sequence and carries its reduced-motion collapse inside the token. The values here are reconstructed; the originals went to engineering and never reached a build.

Breathe Bubbles, shipped. The animation the breath recipe describes: in on a sine curve, hold, out. Designed in Rive with the motion designer, rebuilt in Swift to ship.
The check-in, shipped. The ring reacts to the rating as it moves; the choreography token above describes the completion that follows.

Governed, not just published

A system without governance is a library that drifts. Foundation shipped with a contract for builders, a lint that enforced it, and a log of decisions, including the ones made against design. Rules are easy to write. These three are the ones that operated.

1 · The contract is a gate
Button / Primary
Continue

A frame is built only when marked Ready for Dev. Before that, engineering sees a picture; after it, every value is a token with its three code names. The status is the handoff.

2 · Never invent a value
The same check the parity rule ran on every PR. Try a hex the product never shipped, or a size between two rungs.
3 · The decision log
4 · Contribution and versioningReconstructed from the zeroheight pages
Proposing
  1. Anything new starts as a proposal in the product file: a semantic name, the use it serves, two screens that need it. One use is a one-off, not a token.
  2. Propose the semantic, never the primitive. "We need surface/sunken" is a proposal; "we need #141414" is a lint failure.
  3. Brand tiers are closed. A new brand gets the ten accent overrides; anything beyond is a semantic proposal for everyone.
Reviewing
  1. One owner reviews for structure, one consuming engineer reviews for cost. Both approve, or it waits.
  2. Two working days to review. A proposal untouched for two weeks is closed, not left open.
Publishing
  1. Only the system owner publishes Foundation; product owners publish their own libraries. A token without code syntax on all three platforms is drafted, not published.
  2. Every publish ships release notes: added, changed, deprecated, and which products are affected, one line each with the log reference.
Versioning
  1. Semantic versioning. Patch: a value corrected. Minor: tokens or components added, nothing removed. Major: a name or tier boundary changes, or a token is removed. Products pin a major and take minors freely.
  2. Code and Figma carry the same version string. A mismatch is the bug, whatever the screen looks like.
Deprecating
  1. Nothing is deleted in the release it is deprecated. The old token aliases the new one, is marked with its replacement, and survives two minors. Removal is a major.
  2. Deprecations are announced where consumers look: the component description in Figma, a compiler warning in code.
The standing rule
  1. Parity. Divergence in code owes Figma a correction, logged. Divergence in Figma owes code a release.
Governance, operating

Flip the gate and watch what engineering sees change. Paste a value and get the lint's answer: the nearest token, or the rung either side. Filter the log, open a row, and follow it to the place on the page where the decision shows. The full documentation lived where the team worked, in the Figma file and in zeroheight: the builder contract, the library structure, contribution and versioning, and the decision log per product. It stayed with Calm when I left, so what is here is rebuilt from the system and from memory, and the three pieces above are the ones that operated.

The proof, composed

A system is proven by a screen, not a token list. These are the Discover screens I built in the Foundation file, one per brand, from the same components: the search field, the category tiles, the Content Hero Card with its Chip, the Button and the Tab bar. Switching the brand mode re-themed them; nothing inside a component was touched. The accents are the brand tokens: blue/600, peach/600, amber/600. Sleep's blue shipped; the Parenting, Fitness and Health accents were proof-of-concept values to exercise the brand tier, not palettes chosen for those apps. Fitness's white label on amber is the pair the audit flagged; the system now resolves it to black. Rebuilding the composition for this page surfaced one fix: the hero card's text was bound to accent-on, which turns navy on Classic, so text over artwork got its own semantic, text/on-media.

Pick three
One screen, any three brands

Discover on Foundation, built in the file for Classic, Sleep, Parenting and Fitness, in light and dark where the brand has both; four brands, pick any three. Health is not shown: its product was still in build when I left, so there is no shipped screen to put beside these. The brand tier answers only for the accent family, so a new vertical is ten overrides, not a new library. Inspect labels which semantic each composed component reads; on the real screens it marks the source. Built in the Foundation file from the same components and token values.

Built the foundation into the app I owned

Without a mandate, the system needed a product to ship inside. Classic had been captured, with engineering, as the foundation's first brand; Calm Sleep had no legacy to protect and I was designing it from zero, so it ran on the new foundation from the first screen as its second. It did not slow the launch. It sped me up: fewer decisions per screen and no rebuilding patterns that already existed.

The foundation also kept paying after launch. The MVP plan card was a surface with the artwork inset and the text below it, built fast to ship. Once Foundation's Content Hero Card existed, I replaced it: full-bleed artwork, the adaptive scrim, the category as a chip, the title over the image, a single play affordance. Same data, same slot in the plan, a component swap rather than a redesign. That is what a system is for after the launch, not just before it.

Calm Sleep plan at launch: surface card with inset artwork, title and meta belowThe same screen in dark mode
Calm Sleep plan after the swap to the Content Hero Card: full-bleed artwork, chip, title over the imageThe same screen in dark mode
100%
At launchPost-launch, Content Hero Card
Before and after the swap

Drag to compare the plan at launch with the plan after the Content Hero Card replaced it. Same data, same slot; a component swap, not a redesign. Light and dark, both from the system.

Where does a system with no mandate live?
Rejected
Build it in parallel as a library and pitch adoption. Nobody adopts a library with no shipped proof.
Chosen
Build it into Calm Sleep from screen one. The app becomes the proof.
Rejected
Retrofit Classic first. Capturing Classic as a brand was possible and happened; migrating its screens was never going to be prioritized by DTC, and a system with no new product to prove it would stall.
Cost. If Sleep had slipped, the foundation would have taken the blame.
Four months

From leadership passing to Calm Health adopting. Drag or use the arrows.

Pushback. Contrast could not be guaranteed over artwork that changes every session, so the MVP kept text off the image. I pushed for full-bleed cards and engineering built a scrim that adapts to the image at runtime. It went back into the foundation, so every product on the system inherited it.

Earned adoption instead of mandating it

Buy-in was thin where I needed it most and strong where I had not expected it. I took the system to other teams directly. Calm Health, on the Enterprise side, adopted it while their product was still being built and met with me weekly for three to four months to learn it and push it further. I built Health's brand tier with them, ten overrides on the accent family and nothing else, and taught the mechanics: aliasing, how modes resolve against a component, how to build component properties so collection theming holds. They left able to extend it without me. They never pushed back on the system itself; they embraced the working relationship. The one worry they voiced was about time, not structure: with AI moving as fast as it was, would what I was teaching still hold a year on, and would the way we were structuring things still be the right way. It is a fair question for any system built in 2025, and the answer I gave is the one this page makes: tiers, named intent and tokens that carry their own rules are what an agent can read, which makes them more durable as AI advances, not less. Their product had not shipped when I left, so this page shows the brand file rather than screens.

One foundation, five brand modes. Tap a brand to see the only ten values it owns.
// brand-calm-health.css
1--accent-light: var(--purple-600);
2--accent-dark: var(--purple-600);
3--accent-accent-on-light: var(--color-gray-0);
4--accent-accent-on-dark: var(--black-black);
5--accent-accent-hover-light: var(--purple-700);
6--accent-accent-hover-dark: var(--purple-700);
7--accent-accent-active-light: var(--purple-800);
8--accent-accent-active-dark: var(--purple-800);
9--accent-accent-secondary-light: var(--purple-100);
10--accent-accent-secondary-dark: var(--purple-100);
A new vertical is a set of values. Health's entire brand tier is ten lines.
3 to 4 months
Weekly sessions with the Health team
Calm Health, autumn 2025
10 lines
Health's whole brand tier: the accent family, nothing else
brand-calm-health.css, from the token export
Their own export
Health ran the token export to code and handed it to their engineers
Spencer S., Lead Product Designer, Calm Health

What Health pushed back on. Nothing in the system. The one worry they voiced was about time: whether what I was teaching would still be the right structure a year on, with AI moving as fast as it was. The answer is the one this page makes, that tiers and named intent are what an agent can read, so they age well.

What I didn't build

Classic was captured, not migrated. With engineering I encoded Calm Classic's primitives, semantics and brand into the foundation, so its twelve years of UI resolved from the same tokens, dark mode included. Moving Classic's screens onto the new components was never prioritized by DTC. The system holds Classic; the product still runs its old code.

Classic on Foundation tokens, the prototype. Its primitives, semantics and brand were captured with engineering; the product's screens still run the old code.

How AI was directed

Twice. At Calm, Claude drafted the token structure and naming conventions and I set the boundaries: what belongs in a primitive, what is a semantic, what only a brand may know. Then I showed Health's team the same loop, feeding the token structure and a component into Claude and getting screens back already speaking the system's language.

And again for this page, as a test. In October 2026 I had Claude, working through Figma's MCP, rebuild the system from my token export and 290 shipped screens, 103 from Sleep and 187 from Classic, without access to the Foundation file: three collections in my naming, every component fill and stroke bound to a semantic, a composed screen flipped across the brand modes. Then I compared it to the Foundation tokens. The shipped accent sampled at #2b6cfa against blue-600 #2a6dfa; the tier structure, the ten brand overrides and the semantic set matched; the reconstruction missed the Health purples and the elevation ladder, which only the file had. The method is the same one the system was built with: AI drafts, I decide, the product confirms.

Foundation, reconstructed · the brief Claude worked fromOctober 16, 2026
# Collections, in Eric's names
Primitives   one mode       color/gray/0 … color/gray/1000, blue/600, space/1 … space/24, size/100 … size/900
Brand        five modes     accent/light, accent/dark, accent/on-light … accent/secondary-dark  (ten variables, nothing else)
Semantic     Light, Dark    surface/bg, surface/surface, text/fg, text/muted, border/border, accent/accent → Brand

# Rules
- Every fill and stroke on a component binds to a Semantic variable. No hex in a component.
- The Brand tier answers only for the accent family. A new vertical is ten overrides.
- Verify against the shipped screens before publishing a value.

# Result
69 primitives · 10 brand · 21 semantic · 24 text styles · 6 components, 17 variants · 1 screen, 4 brands, 2 modes
Figma variables panel: the brand collection with Foundation, Calm Sleep, Calm Fitness, Calm Parenting and Calm Health modes, ten accent variables
The brand collection: five modes, ten variables, every cell an alias to a primitive. The whole brand tier.
The Button component under five brand modes: Foundation, Sleep, Fitness, Parenting and a fifth column, each showing primary, hover, active, disabled, secondary and tertiary states
One Button, five brand modes. Primary, hover, active, disabled, secondary, tertiary; the component is the same instance in every column.

Deep design systems craft paired with real fluency in AI-to-code workflows. It's not common to find in one person.

Spencer S., Lead Product Designer, Calm Health

Results

4 months to launch

Calm Sleep, from concept to global iOS, built entirely on the foundation; Android about six weeks later from the same system. Covered by Fast Company and TechCrunch.

Calm, July to October 2025; Android, November 2025. Source: ship dates, press coverage.

Adopted without a mandate

Calm Health chose the system for a product still in build and met weekly for three to four months to extend it, then ran the token export to code themselves.

Calm Health, autumn 2025. Source: the weekly sessions; Spencer S., Lead Product Designer.

Adaptive scrims, system-wide

Built for Sleep's full-bleed cards, returned to Foundation, inherited by every product on it. Toggle it and read the ratio.

Measured on the page: text luminance against the artwork under it, with and without the scrim.

Five brands, one token set

Sleep shipped. Health in build. Parenting and Fitness proven with placeholder accents. Each brand is ten overrides on the accent family; tap to cycle.

The brand collection: five modes, ten variables, every cell an alias.

States authored once

Fifteen interaction states resolved by the system instead of rebuilt per component. Tap a state to see the token that supplies it.

Three roles by five states; the matrix above has every cell.

Classic captured, never migrated

Its primitives, semantics and brand live in Foundation: a ten-line brand file. Moving its screens onto the system was never prioritized by DTC.

brand-calm-classic.css, from the token export. The miss on this page.

What I'd change

Strategically

DTC saw the system as a distraction from shipping speed. Calm Sleep proved the opposite. I should have made that case in DTC's terms, with the Sleep timeline as the evidence, before they passed rather than after.

Technically

Disabled states resolve at 40% opacity instead of carrying their own value. It halved the accent group and breaks inside a dimmed container. Next time I'd spend the tokens.

All work/Calm Sleep

Three quarters of users never came back after day one. A daily plan took 5-day engagement to 15%

Calm's first new product in twelve years, built in four months on a system built alongside it. It met its launch targets. It also proved that craft and speed are not the constraint: a modern, well-built product for an audience you know will come can still cannibalize the main app and lose users' trust if it asks them to pay again for content they already had. The product was right; the premise was set before design was in the room.

The brief

The role
Product designer, concept through launch. Built all of the UI: the plan surface, Sleep Readiness, onboarding-to-plan logic, check-in, Insights, the player and library. Gave art direction with Jeany Ngo, Staff Product Designer: we commissioned BUCK for the illustration work and reviewed with them weekly for about eight weeks to reach the final set. Joined the product June 12, 2025.
The team
3 designers, 1 PM, 4 engineers, a motion designer from Calm's studio team, and UXR.
The timeline
June 12 to October 16, 2025. Four months, concept to global iOS launch in September.
The business stake
A bet on verticals. Calm's sleep presence was diluted inside the main app, and the plan was to break the company into focused apps under one name, sleep first, then fitness and parenting. The October date was fixed by the need to show revenue outside Classic before the end of the year; product set the install-to-paid target. Four months was the constraint, not the plan.
The problem
73 to 74% of users who opened Calm Sleep never came back after day one. The content was there; the entry point was not. From analytics on the existing Calm sleep experience, before the rebuild; Home engagement was low and content was found through other surfaces.
The bet
Replace the content library with a daily plan built from each user's onboarding answers, and one score that makes progress visible.
What I did
Helped map the nine scientific foundations into every plan item, designed the plan and Sleep Readiness end to end alongside the staff designer, and reshaped priorities twice from testing before launch. It ran on the Foundation design system.

Eric became my design partner right away. Together we turned product requirements into fully fleshed-out concepts, shipping the product within months of his start. When I moved off the team, Eric took over Calm Sleep.

Jeany N., Staff Product Designer, Netflix, formerly Calm

How I ran it

Daily with
The VP of Design, the staff designer, and a lead product designer who received PRDs when I did; we split the work by strength. Calm's studio team gave us a motion designer for the Rive and Swift animations. UXR tested what I built and I sat in on the sessions, live and in person, with users of all backgrounds; I tested by default, and skipped it only when I was certain of a direction, which was rare.
I decided
The design system. Anything visual, pattern or UX. The VP shaped how things looked; how things worked was mine.
The loop
Explore, as a flow against the problemVP in from the first sketchRefine with the lead and productVP check at walls and landingsEngineering, documented to the pixel
Traded away for the dateHistory, the content model, and differentiation. Each raised, each lost.

A real plan with history, so people could see what they had done and where it was going. What shipped was tonight only; the progressive player on this page is that trade-off being paid back.

A content model that made sense. Calm's library mixed titles with subtitles and playlists with albums, and I wanted it fixed before this app inherited it. It inherited it.

Differentiation. Design pushed, repeatedly, for features Classic did not have rather than Classic's content in the shape of a plan. The plan shipped.

The argument I lostA longer onboarding that explained itself. Product cut it, then came back for it.

My case: people do not mind questions when the app is visibly learning from them, as long as the plan shows why each was asked and the answers can be changed in settings. Product disagreed and none of it shipped.

After launch, when the numbers turned, product came back and asked how we would change onboarding to give users control.

What I escalatedThe system, constantly. The VP backed it; nobody outside design did.

The team was asked to move faster each quarter on a component library with no documentation and no token architecture. I built Foundation while shipping this product.

When the VP left midway, with less support than before and the pace still rising, I kept building it, consulting the enterprise designers for feedback.

Who built on the workFour engineers, the PM, the lead researcher, two designers, and marketing.

Two iOS and two Android engineers had the system before the screens existed and built the app from the Figma file and the system file; they came to me for interaction questions and I ran QA on the builds.

The PM wrote copy straight into system components. The lead researcher assembled her own test screens and brought me in only for what exceeded her reach in Figma. The lead and the staff designer worked from it daily.

Marketing came to design for the outward-facing material: the App Store visuals for both apps, and the launch and release imagery on this page.

The studio team's motion designer built Breathe Bubbles and the check-in from the motion curves and the screens; UXR ran the two testing rounds on my prototypes and the smaller tests between them.

What happened

Calm's first new product in twelve years launched on iOS in four months, with a 4.7 rating and install-to-paid above target in both markets.

4.7
App Store rating at launch
Calm Sleep, first weeks after the September 2025 launch. Source: product analytics, launch readout
15.2%
5-day engagement, from under 3% remaining by day 3
Calm Sleep, first weeks after the September 2025 launch. Source: product analytics, launch readout
+34% / +14%
Install-to-paid against target, US and AU
Calm Sleep, first weeks after the September 2025 launch. Source: product analytics, launch readout

Engagement and conversion measured in the first weeks after the September 2025 launch, against the targets set before it.

The targets were met. Then the numbers turned, for the two reasons design had argued about before launch: users found they had no control over their plan, and recognized the content as Classic's. The app did not differentiate itself enough, and the main app lost users to it.

Replaced the library with a plan

Calm Sleep had good content and no way in. Browsing assumes you know what you need, which at 11pm nobody does. The plan is a curated set of tasks tied to each user's onboarding answers, with a Sleep Readiness score that makes progress visible. Every item maps to one of nine scientific foundations, so personalization is grounded in sleep science, not preference.

What replaces browsing?
Rejected
A tuned library: better recommendations on the same browse surface. The existing recommendations were static and not informed by behavior, and tuning them keeps the entry point that was failing.
Chosen
A daily plan built from lever-based onboarding answers, with one score.
Rejected
A coach: a conversational guide. It needs signals the product did not collect; there was no system to gather or interpret them yet. The plan builds that system first.
Cost. Content the library surfaced freely now had to earn a slot in a plan. Discover kept the full catalog, so nothing was removed, only moved off the front door.
Nine scientific foundations

Tap one: what it is, the onboarding answer that feeds it, and the kind of plan item it produces. The plan is a set of foundations with a schedule, not a library.

Twelve plan item illustrations in a grid: stretches, a phone, a sleeper, a glowing orb, a breath ring
Plan item artwork. Art direction by Jeany Ngo and me; illustration commissioned from BUCK and reviewed weekly for about eight weeks. One style for every foundation so the plan reads as one thing.
The Calm Sleep brand guide, sixteen pages: app icon, logo, color usage, Figtree, gradient and its variations, color overview, UI colors, hero illustration, product illustration, practical to poetic, color story, color balance, how to scale color, sense of light
The brand guide, sixteen pages, the outcome of the BUCK sessions. The brief was a deck of unfilled elements; the direction came from us. Jeany and I met before every BUCK call with a plan for the round, so the feedback arrived as one voice: where I was taking the UI, what she wanted from the illustration, and the words we kept returning to: whimsical, light, airy, dreamlike, something that would sit well beside what Apple was doing with glass. The guide is what came back. Logo, color and gradient, Figtree, and the illustration principles, practical to poetic with one sense of light.
Content strategy for plan items: six cards with foundation, title, duration and type
Content strategy per item: foundation, title, duration, type. The item has to answer what, how long and why before it earns the slot.
Onboarding, ten screens

From the splash to the plan. The order was the argument: promise, proof, six questions that each map to a lever, a visible wait, the schedule, then the account. Use the arrows or the keys.

SOS, on device

Awake at 3am

  1. The sheet opens on Ready to sleep? with one story in frame. No plan, no score, no list.
  2. Swipe sideways to change the story. Title and narrator only; the artwork does the persuading.
  3. One gesture to play. The slider is the commitment: drag, and the screen goes dark with the audio.
  4. Close, and the plan is where you left it. Ready to sleep? stays as a pill at the bottom of Home.
0:00
The 3am path

SOS skips the plan. A screen recording from the shipped app: the sheet, the carousel, the swipe to play, and the way back. The notes follow the recording.

I heavily rely on that SOS sheet. I have trouble selecting what's best for me at night. I wake up in the middle of the night and it helps so much.

Research participant, UXR usability test
The loops underneath the plan

Play a loop: the micro loop runs every night, the macro loop every week, and SOS is the branch for 3am. The check-in is where the two meet.

The morning check-in: how did you sleep, from terrible to excellent, then what helped you sleep
The check-in is the plan's input: how you slept, then what helped. Two screens, one minute, every morning.
A week of check-ins as rings: Sun through Sat, each ring a night
Seven nights as rings. What helped and what got in the way, counted, so the next plan can lean on it.

Made the mechanics legible before making them smarter

Early testing showed users understood what the plan was for but not how it worked. What counted toward Sleep Readiness was not clear. The tempting move was more personalization. I prioritized making the scoring immediately legible, because tuning a system users do not trust yet only compounds the problem. The first round, moderated sessions on the mid-fidelity prototype, returned three things to fix: make the plan mechanics immediately clear, reduce the cognitive load so the screen reads as a plan rather than a checklist, and personalize the activities to the sleep problems people named.

Users could not read the score. Tune it or explain it?
Tempting
More personalization. The plan gets smarter and the score stays opaque.
Chosen
Make what counts visible in place: item states, the bar filling as items complete.
Rejected
Remove the score. Considered, briefly. Without the score the plan is a list again.
Why. A score nobody can predict is a score nobody trusts. Legibility first, then trust, then personalization.
What counts toward your score, made obvious. The bar moves as each plan item is completed; three of three unlocks the night.
A plan item with its I did this action, the Sleep Readiness bar above it mid-fill
The score, explained in place.
Mid-fidelity explorations of the plan item card: five accent blues and two layouts side by side
Mid-fidelity: the plan item card across five blues and two layouts before the system's accent was fixed at blue/600.
Mid-fidelity explorations of the hero plan item with artwork, four variants
The hero item, four ways. The full-bleed version became the Content Hero Card after launch.

Cut the plan surface, not just the content

Users described the tasks as short and manageable, but the screen still read as a checklist. The fix was not fewer or shorter tasks. It was tightening the surface itself: the difference between "here is your plan" and "here is everything you could do." The second round confirmed the plan was understood and the tasks felt short and manageable, with consistency still hard on busy days. Two asks came out of it: clearer guideposts on what matters most, and the right balance of repetition and variety.

The earlier plan screen: a weekly focus row of four chips, a Daytime routine header, a large tip with a time, and a content card below
Manageable tasks, overwhelming screen. The content tested well; the surface did not.
The final Your Day screen: the Sleep Readiness bar at two of three, then two full-bleed plan items, Balancing Breath completed and a Digital Hygiene item, with the Ready for sleep sheet below
A plan you finish, not a list you face.

Pushback. Testing reshaped priorities twice before launch. The second instance was the ask for guideposts: it became the ordering of the plan, the item on the strongest foundation first, with the readiness milestones saying why. I argued against adding a difficulty label per item and won; the label would have reintroduced the checklist feel the round had just removed.

What I didn't build

The progressive player. A guided, sequential content experience aimed at the two drop-off points in the data: tonight's items queued in order, the week visible above. Explored and tested, never shipped. It is the version I would build next.

The progressive player exploration: five screens fanned, from tonight's plan as a playlist through Breath, Meditation and a Sleep Story with Hold to Complete, to the weekly rings
A plan you play rather than check off. Stayed exploration.
Six Calm Sleep screens: Check-in, Discover, the player, My Library, Insights
The rest of the app, all on Foundation: check-in, Discover, the player, library, Insights.
The Calm Sleep component library sheet in light mode: cards, tab bar, list items, chips, progress rings, slidersThe same library sheet in dark mode
The Sleep library, both modes: every component on Foundation tokens, the kit the rest of the app was built from.

How personalization was designed

This page has no generative AI in it, and says so. The personalization is designed logic: onboarding answers flag foundations; each foundation owns a pool of plan items; a day's plan is three items drawn across the flagged foundations, with the sleep-wake schedule as the anchor. Sleep Readiness is a count, three of three, resolved by rules I could explain on a whiteboard, not a model. I owned the mapping and the item taxonomy; engineering owned the engine. For a reader at an AI company the point is the sequence: legibility, then trust, then personalization. The same sequence applies to any system that adapts to a person.

Results

01Activation
After signing up, did users interact with recommendations?
  • Enrollment to first session started
  • Health data permissions and submissions
  • FTUE completion
  • First plan item or audio started
02Engagement
Did the app keep people engaged day to day?
  • Days engaged during trial
  • Plan item usage
  • Audio consumption: stories, music, soundscapes
  • Where sessions began: Home, Discover, Sleep tab
03Retention & conversion
Did people return and eventually subscribe?
  • Free user retention, D0 to D3
  • Trialist early churn
  • Trial to paid
  • Install to paid
04Feature adoption
Were Sleep-specific tools valuable?
  • Insights tab returns
  • Push opt-in
What was measured

The measurement plan, set before launch: four questions, each with the signals that would answer it. The results below are these signals read after the September launch.

4.7App Store rating at launch

4.7 stars at launch

Calm's first new product in over a decade, concept to global iOS in four months.

Calm Sleep, first weeks after the September 2025 launch. Source: product analytics, launch readout
+34% / +14%install-to-paid vs targetUS and AU

More users paid

Install-to-paid came in 34% above target in the US and 14% above in Australia.

Calm Sleep, first weeks after the September 2025 launch. Source: product analytics, launch readout
<3% → 15.2%5-day engagementfrom under 3% remaining by day 3

People came back

5-plus day engagement reached 15.2%, from a start where 73 to 74% never returned after day one.

Calm Sleep, first weeks after the September 2025 launch. Source: product analytics, launch readout
+31% / +8%plan engagement / completion

The plan got used

31% more users engaged with a plan item, with 8% higher completion.

Calm Sleep, first weeks after the September 2025 launch. Source: product analytics, launch readout
AaFigtreein place of Avenir
MoonlightSky HazeCalm BlueEvening Black

A brand built for sleep

Figtree in place of Avenir, a palette from Evening Black to Moonlight, Jasper Lake reimagined as the hero illustration, and the Breathe Bubbles and check-in animations, designed in Rive and rebuilt in Swift to ship.

Calm Sleep, first weeks after the September 2025 launch. Source: product analytics, launch readout
ShippedCalm Sleep, the appiOS September 2025, Android six weeks later
ShippedThe daily plan, Sleep Readiness, SOS, check-in, InsightsAll on Foundation
Didn't shipThe progressive playerPrototyped and tested; stayed exploration

Shipped, and what didn't

The app shipped on both platforms. The one piece that stayed in exploration was the progressive player, the plan as a playlist, prototyped against the two drop-offs in the data.

Calm Sleep, September to November 2025. The prototype is in the next-build block above.

What I'd change

Strategically

The premise. A second app for sleep cannibalized the main app and confused users about why they needed two, and that was said from day one. With what I know now I would not have built a second app. I would have built the sleep experience into Classic, fixed its content model so the library reads as a story rather than a catalog, and turned Classic from a library into a mission-driven, segmented program that changes with you, measuring whether the content is helping with the goals people set. The second lesson still holds: legibility first, then trust, then personalization.

Technically

The MVP shipped with Rive animations driving the home surface, and on device in SwiftUI they did not hold up. Post-MVP we pulled the Rive runtime and rebuilt the animations in Swift, with less motion on Home and clearer hierarchy. Motion has to be tested on a real device early, not at the end.

What I'd build next

The plan as a playlist

  1. Tonight's plan arrives ready to play: first up is Breath, then one more. One tap starts the night instead of three decisions.
  2. Each item hands off to the next: breath, meditation, story. Hold to complete in the player, so the plan progresses without returning to Home.
  3. The week as a sheet, Monday to Sunday, with the items already slotted. Repetition and variety, the two things round two asked for, become a visible schedule.
  4. The rings carry over. Seven nights, what helped, what got in the way, feeding the next week's playlist.
0:00
The next build

The progressive player, prototyped. It aims at the two drop-offs in the data: the decision at the top of the night, and the return to Home between items. Explored and tested before I left; not shipped.

All work/Experian, consumer credit

Finding what affects your credit score went from 9% to 82% of users. Paid churn fell from 7% to 5%

CreditHub: one Experian tab answering the three questions people actually came with, shipped in three tested phases across web, iOS and Android. It proved that pace is a design decision. Twenty-one months, each phase tested before the next was built, nobody burned out, and the tab still runs today as it did the day I left. Not flashy and not AI-driven; it answered exactly what people came to ask, and it held.

CreditHub components on a grid: additional FICO scores by industry, a score ingredient with its share, Credit Tools, Key Factors tagged Helping and Hurting, the 597 score with a color range, the debt summary, credit usage by account type, and the bureau switcher in its three states

The brief

The role
Designer, research through high fidelity and shipped. iOS and Android were mine alone; web was shared with a lead product designer.
The team
1 senior PM, 4 engineers, 2 UX writers, 1 researcher, 1 design systems partner.
The timeline
June 2021 to March 2023, across web, iOS and Android at once. Phase one took about a year; phases two and three about five months each.
The problem
Credit information was split across Reports and Scores. Users could not find their score or what affected it, and many gave up before getting an answer. The most reported issue to customer service was that people could not find why their score was what it was, and they were leaving over it. Churn and support cost, one root cause.
The business stake
The data had always existed, gathered by Experian or from FICO, and had never been shown to consumers in a form they could use. The product was making it digestible, at a price: the score with a color range so standing reads at a glance, a debt summary, Score Ingredients, Credit Tools gathering years of scattered tools, Additional Scores for auto, cards and mortgage, and the marketplace entry point.
The bet
Stop linking the two sections. Build one Credit tab organized around the three questions users asked, not around where the data lived.
What I did
Designed CreditHub end to end and shipped it in three phases, each tested with users before the next was designed. Credit data is regulated content. Under the Fair Credit Reporting Act a consumer is entitled to the full contents of their file, so nothing could be dropped or buried for simplicity; every element the old sections exposed stayed reachable. The reasons a score was what it was came from the scoring model's factor codes and were licensed language, so that copy could not be edited without sign-off from each of the three bureaus. And because implied outcomes are treated as deceptive, the product could not suggest that completing a task would raise a score, or hint that it might. The design had to make the information findable and understandable without ever telling anyone what to expect.

How I ran it

Paired with
A lead product manager. She conveyed the needs, I explored them; we prioritized together and put out fires together. Always aligned, and we had each other's backs.
I decided
The experience on iOS and Android, end to end. Web, shared with a lead product designer. When I was unsure how users would react, UXR tested it before it was committed.
The loop
PRD and the need, with the PMExplore, across all three platforms at onceTest with UXR, 25 to 30 people a round, after each phaseCompliance review, three bureausBuild with engineering, then the next phase
The constraintThe design system was being built underneath the product at the same time.

It was the one thing that slowed the work. Rather than wait on it, I built most of CreditHub's components myself, sat in the systems team's meetings, and argued for them to become slot components so other teams could use what we built.

This is where the conviction behind Foundation comes from: how fast a team can move is set by its system, and I watched it set ours.

The argument I lostThe score widget at the top of the tab. I still think I was right.

It is tappable with no sign that it is, and clunky in use. For a long stretch it was also a different widget from the one on the dashboard, because the dashboard could not be changed until it was rebuilt, so the two sat mismatched. We were aligned with engineering on the timing; I still did not like it, and I still do not.

What I advocated forSlot components. I built most of CreditHub's components myself, then got the systems team to make them slots other teams could fill.

The system under construction did not have what CreditHub needed, so I built most of the components myself: the score card and its states, the switcher, Key Factors, Score Ingredients, the debt summary, the quick actions, the marketplace card. Then I made the case to the systems team that these should not stay credit-specific, and got their buy-in to rework them as slot components, a fixed frame with regions other teams could fill with their own content. That is what let the marketplace run a dozen offers off one template, and it is where the conviction behind Foundation comes from: how fast a team can move is set by its system.

Who built on the workEngineers on three platforms, two UX writers, the lead researcher, and the systems team.

Four engineers built web, iOS and Android from the files. Two UX writers wrote into the component structure, inside the licensed-language constraints. The lead researcher ran the rounds on my prototypes. And the components designed for the credit tab went into the global system for other teams to use.

What happened

Task success rose on every tested task. Free-to-paid conversion rose 5.25% and paid churn fell from 7% to 5% in the first month after launch.

9% → 82%
Users who could find what affects their score
Experian, first month after launch, 2023. Source: UXR moderated task testing, at least 25 to 30 participants per round, and product analytics
6% → 92%
Users who could find their debt and credit usage
Experian, first month after launch, 2023. Source: UXR moderated task testing, at least 25 to 30 participants per round, and product analytics
7% → 5%
Paid churn, first month after launch
Experian, first month after launch, 2023. Source: UXR moderated task testing, at least 25 to 30 participants per round, and product analytics
Task success, before and after
BeforeAfter
Find score factors
9% → 82%
Find debt and usage
6% → 92%
Compare bureaus
19% → 89%
Industry-specific scores
17% → 55%
Every tested task improved. Industry scores stayed behind.
Findability, measured

Usability task success before and after CreditHub. Hover a task for the numbers and the test.

Moderated with UXR, at least 25 to 30 participants per round, one round after each phase.

Merged two sections into one tab instead of linking them

Users' three questions were answered in two places. Linking Reports and Scores would have been the easier fix. I consolidated the content into one Credit tab, organized around the questions users asked rather than how the data was stored, and kept the full report one tap away.

Two sections answer one question. Link them or merge them?
Easier
Cross-link Reports and Scores. Cheap, and the user still makes the trip.
Chosen
One Credit tab ordered by the three questions: what is my score, what affects it, what do I do next.
Consequence
The merge changed what the home surface had to do; the dashboard was redesigned afterward to point into the new tab.
Cost. Consolidating under compliance meant nothing could be hidden. Everything the report offered stayed reachable. Nothing could be removed, only reorganized, and the factor language stayed as licensed.
Organized around what users asked

The same ten pieces of content. On the left, where the data lived: two sections, split by source. Switch, and they re-sort into the three questions people came with: what is my score, what affects it, what do I do next. Nothing is removed; everything moves.

The trip, before and after

A paid user with one question: why is my score what it is. Before, the answer lived in two sections and the trip ran through the Help Center and, for one participant, his photo app. After, it is one tab. Tap a step for what they said; switch to see the same question answered after CreditHub.

A Key Factor detail: Serious delinquency, with the explanation in the scoring model's licensed language and a tag placeholder from the design file
A factor opened. The explanation is the model's own text, which could not be edited without each bureau's sign-off; the design's job was the hierarchy around it.
Score Ingredients: five detail screens fanned, payment history, amount of debt, credit history, new credit and credit mix, each with its percentage of the score, a rating and key data points
Score Ingredients. The five factors as a mental model: each with its share of the score, a plain rating, and the data points behind it, in the model's own licensed language. Consolidating did not mean hiding.
Component explorations: the score card across its states from no score to 803, quick actions with their status variants, credit usage, score history, and education cards
The score in every state it can be in, from no history to Excellent, and the quick actions in theirs. Every state designed before the first was built, so engineering had the whole component.
More components: the twelve-month payment history grid, credit usage at four levels, Additional Scores, the report header, and the premium upsell cards
Payment history as a grid, usage at four levels, Additional Scores, and the premium cards: the parts the tab is built from, designed for the global system.

Made the three bureaus comparable in place

Users needed to compare Experian, Equifax and TransUnion, and the old experience made each one a separate trip. A sticky bureau switcher lets them compare without leaving the view. Comparing went from 19% to 89% task success, and users named it one of the most valuable parts of the redesign.

How do three bureaus get compared?
Before
Each bureau a separate trip and a separate layout, so differences had to be reconstructed from memory.
Chosen
A pinned switcher. The data below changes; the layout does not.
Tested first
Depth over sprawl. Experian had always surfaced everything on one screen; I was not sure people would accept drilling down, so UXR tested it before it was committed.
Why. Seeing three bureaus in the same layout makes the differences obvious instead of something users reconstruct.
Experian FICO Key Factors: the bureau switcher pinned above a 597 score rated Fair, factors listed as Helping or Hurting, and an upgrade prompt for the other two bureaus
Key Factors under the pinned switcher. Each factor is tagged Helping or Hurting in plain words; switch bureaus and the layout holds while the factors change.
The switcher in use. Experian, Equifax, TransUnion: the score, the range and the ingredients change; the layout does not. The same component shipped on web, iOS and Android.
CreditHub on the web: the Credit tab with the bureau switcher, the 755 score on its range, quick actions, Score Ingredients, and the premium and education rails
The web. The same tab at desktop width: score, quick actions and Score Ingredients in the main column, premium and education in the rail. Shared with a lead product designer.
Mobile web: eight phone-width views of CreditHub, the score, the debt summary, additional scores, the premium rail, the menu and Score Ingredients
Mobile web, the third surface. Same components, one column, without a native shell.

Shipped in three phases instead of one launch

I broke the MVP into three phases with product and engineering, sequenced by how each section relates to the others and by effort. The score came first because everything else explains it or acts on it. Each release went through usability testing before the next, so feedback shaped the product while it was still being built.

One launch or three?
Rejected
One launch. Everything lands at once and testing lands after.
Chosen
Three phases: your score, what affects it, what to do next. Tested between each.
How the order was set
A joint audit by product, engineering and design: what had to exist first for the rest to connect. Phase one's decisions carried two and three.
Cost. Three release cycles, three rounds of compliance review. Each cycle also waited on the system being built underneath it at the same time.
Marketplace cards for CreditHub: insurance profile, membership benefits, weigh your options, credit lock, cancel subscriptions, dark web scan, identity protection, score prediction, small business, bill negotiation, financial future, auto insurance, build credit, and a partner offer
The business side of what do I do next. A card system for marketplace offers inside CreditHub, one template, a dozen offers, every one on the same components: title, illustration, two lines, one action, the disclosure. Built so marketing could add an offer without a designer.
Three releases

Each phase tested before the next was designed. Drag or use the arrows.

The phase-one UXR prototype in Figma: the Credit hub, dashboard, spinner, shimmer load and scroll states for iOS, with the test questions pinned beside the screens they test
The phase-one prototype as it went to research, with the questions pinned to the screens they tested: does the user know what a FICO score is before landing here, do they understand the Fair badge refers to the 597, do they expect more about the score here, do they know what Equifax and TransUnion are. Findings changed what came next instead of landing after launch.

Pushback. I pushed for contextual education in the MVP and lost to scope: product held the MVP to the three questions and the education came after. So I built the IA to accept it later. Industry-specific FICO scores stayed at 55% success when every other task cleared 70%.

What I didn't build

Contextual education, in the MVP. An education slot: a prompt, a two-line answer, one link deeper, placed inside the section that raised the question. Designed, argued for, cut from the MVP; the IA held the slot. After launch I built it: an educational carousel of the questions new members ask, and an education screen, Tips for a good score. Two more, discussed and never built: a settings experience that let users control what CreditHub showed them; and richer infographics for the content, which the rules on how score information may be represented did not allow.

The educational carousel: four cards, why don't I have a FICO Score, what is a FICO Score, why is a high score important, when will I get my FICO Score, each with Tips for a good score
The carousel, built after the MVP. Four questions a new member asks, answered in place.
Tips for a good score: set yourself up for success, with expandable tips on paying on time, paying in full and using your card
The education screen, Tips for a good score: plain basics, expandable, no promises about the score.

How AI was directed

None. This work predates the pipeline, and the page says so in one line rather than retrofitting a story. What carries forward is the method: test between every release, and sequence by what explains what.

I have to go to Reports to view my debt summary and then go back to Scores to access the categories of that debt. I screenshot them both on my phone and go to my photos to compare them.

Research participant, UXR usability test

Results

Findability, fixed

Score factors from 9% to 82%. Debt and usage from 6% to 92%. Every tested task improved.

Experian, first month after launch, 2023. Source: UXR moderated task testing, at least 25 to 30 participants per round, and product analytics

More free users upgraded

Free-to-paid conversion rose 5.25% in the first month after launch.

Experian, first month after launch, 2023. Source: UXR moderated task testing, at least 25 to 30 participants per round, and product analytics

Fewer paid users left

Paid churn fell from 7% to 5% in the first month after launch. The most reported support issue had been that people could not find why their score was what it was.

Experian, first month after launch, 2023. Source: UXR moderated task testing, at least 25 to 30 participants per round, and product analytics

Bureaus side by side

Comparing Experian, Equifax and TransUnion went from 19% to 89% task success. Users named the switcher one of the most valuable parts of the redesign.

Experian, first month after launch, 2023. Source: UXR moderated task testing, at least 25 to 30 participants per round, and product analytics
CreditHub on three phones: the native iOS app with the bureau switcher and debt summary, mobile web with the 755 score, and Android with the credit usage sheet

One experience, every platform

The same credit experience on web, mobile web, iOS and Android, from one set of components.

Experian, first month after launch, 2023. Source: UXR moderated task testing, at least 25 to 30 participants per round, and product analytics
17% → 55%industry scoresbehind every other task

Industry scores stayed difficult

Finding industry-specific FICO scores rose to 55%, well behind every other task.

Experian, first month after launch, 2023. Source: UXR moderated task testing, at least 25 to 30 participants per round, and product analytics

What I'd change

Strategically

The pace was right and I would keep it; three tested phases over twenty-one months is why the tab still stands. What I would change is the feel. Experian's product reads like paper, like documentation, and the red tape makes that hard to escape, but the system we built then was almost entirely about UX and components flexible enough for every team. The visual layer was never the focus. With that system in place, a fresh take on the surface, the kind the AI era is making normal everywhere else, would do more for the product now than any new feature.

Specifically

The score widget. A tappable element should look tappable, and the tab and the dashboard should never have shown two different versions of the same number. The settings experience that let people choose what the tab showed them should have been built; it was only discussed.

Eric Fillingame
Education
Master of Design, Interaction Design
ArtCenter College of Design
Bachelor of Arts, Graphic Design, Flagler College
Years active
2011 to 2026
Recognition
Work featured by the App Store, TechCrunch, Fast Company, GQ, Vogue, Esquire, ESPN, and Hypebeast.
Skills and specializations
Product design
Design systems
Systems thinking
Design to code
SwiftUI
AI orchestration
Generative media
Front end, backend
Personalization
AI product
Elsewhere
About

I am a product designer who also ships the code. Fifteen years in, most of it untangling complex, data-heavy workflows: five leading information architecture at Experian, then AI and Personalization at Calm, where I built the design foundation the company's newest apps run on.

I run Zovie Software, where I designed, built and produced Growl alone: the design system, the SwiftUI app, the backend, and a pipeline that generated every image and narrated every step of 500 recipes. The method is what I call precision orchestration. Agents are directed, reviewed and overridden; they are not handed the keys.

I am looking for a Staff role at a company building at the frontier of AI, where design, systems and code are one job. Based in Los Angeles. hello@ericfillingame.com

From people I’ve worked with

You rarely find a designer who balances elite visual craft with the systematic thinking required for modern product development.

Sang H., VP of Design, Calm

One of the most thorough systems designers I've ever worked with. He refreshed our outdated design system, brought it to parity with industry standards, and built relationships with engineering so handoffs were smoother and production matched design.

Jeany N., Staff Product Designer, Netflix, formerly Calm

Deep design systems craft paired with real fluency in AI-to-code workflows. It's not common to find in one person.

Spencer S., Lead Product Designer, Calm Health

When given an exploratory concept, he brought clarity and structure to ambiguity, end-to-end, with minimal direction.

Brett M., Designer Advocate, Figma

Colleagues complimented his approach: design thinking and a systems thinking mindset when crafting solutions.

Chad F., Sr. Product Designer, Spectrum

The products he designed often rose to the top of our sales reports.

Taylor S., Founder and CEO, FutureStitch
Current
2026 to present
Founder. Designer and builder of Growl
2025 to 2026
Senior Product Designer, AI and Personalization
2020 to 2025
Senior Product Designer, Foundations and Consumer Credit
2013 to 2019
Founding Designer, then Senior Innovation Designer of Product
2011 to 2013
Visual Designer, Art Department
All work/Archives

Archives

Earlier work: concepts, redesigns and products across mobile, web, watch and in-car. Open any project for its full set.

Gem Car's in-car screens in light: navigation, a software update, charging, climate, drivers and media
The same in-car screens in dark
The dashboard display in a light Gem Car interior, showing turn-by-turn navigation
The dashboard display in a dark Gem Car interior
Gem Car's companion app in light: launch, parked, driving and charging
The companion app's same four screens in dark
Automotive

Gem Car

An infotainment system and companion app for Gem Car.

Candor on a MacBook: a dark dashboard of Pangaia's site, music, an inbox and a Tree Fund story, with the assistant on sustainability
Candor open on Pangaia"s site, a photo of two people in Pangaia clothing, with other tabs fanned behind
Candor"s dashboard for Pangaia: music, products, a Tomorrow Tree Fund story and a savings chart
Candor"s dashboard of design sites: Stay Curious, Noomo, Behance and more
A recently played card, Cool by Dua Lipa, beside a reading list
The reading list card in front, between recently played and liked products
Liked products in front, with a Pangaia piece and the Tomorrow Tree Fund behind
The Tomorrow Tree Fund card from Delta Trips in front of liked products
A Pangaia sustainability story in front, between the Tree Fund and the Pangaia website
The Pangaia website card in front, with a savings chart beside it
A savings reminder chart: shop around for savings, you"re saving for Japan
Your Pangaia inbox: an invite to the LA launch, a content creator role and an Apple collab
A TED podcast episode card in front of the Pangaia inbox
Candor"s sidebar with recent tabs and a Getting Started tip, 1 of 3
Candor"s Generate Dashboards walkthrough over a dimmed browser
The address bar suggesting Pangaia"s dashboard and official site as you type
Asking Candor what some popular design sites are, from the address bar
Candor"s answer: a dashboard of design sites, Medium and Figma first
Following up with Candor over a dashboard of Webflow, UI8 and Adobe Stock
Candor"s assistant chat finding design sites and checking which offer templates
Candor"s assistant offering a news brief more focused on factual reporting
A Netflix-style series page for Our Universe, with Candor"s cursor collaborators over it
AI

Candor

An agentic web browser that conceptualizes AGI based user interactions.

Outings on four phones: a group's card, a map of San Francisco, places to go, and the invite to create, plan and organize bill pay with friends
Outings' Explore screen with a breakfast spot, between group-chat bubbles deciding where to eat, booking a table and splitting the bill
The booking flow, steps 1 to 5: from the Explore tab into Where to?, searching, and picking a place
Steps 6 to 10: choosing when, a specific date and reservation time, then who's coming
Steps 11 to 13: the map of places, a place's quick details, and the full outing with friends added
Social

Outings

A social bill pay app with a community.

Three Tesla app screens: a Supercharger's details, the Roadster interior shop, and your place in line with a 6-digit code to start charging
Automotive

Tesla

A redesign of the Tesla mobile app for reserved charging.

Dink's live pickleball scoreboard on a phone, between two Apple Watches showing a friends list and the score
Sports

Dink

A pickleball app that helps users keep track of their score in real time.

Brutal 1.0 on a phone, telling you a product appeared in your Instagram feed because an algorithm targeted you
Brutal across three phones: a nudge over Netflix, suggestions outside what the algorithm usually shows, and Boyz n the Hood picked from them
AI

Brutal AI

An LLM that is brutally honest and helps users navigate echo chambers, filter bubbles, and more.

Able.ai on three phones: who's free today in San Francisco, a new connection with Oscar Pentonod, and an outing's guest list
Social

Able.ai

Social planning and event booking with friends.

The CB2 app on a phone: search, browsing by room, and the Hershel couch marked most favorited
Retail

CB2

A redesign of CB2 mobile app experience.

Experian's web dashboard in a browser: a 780 credit score, an insurance offer, Smart Money and a partner card offer
The same dashboard on a phone, opening with today's snapshot and the 780 score
Design board of score gauges from 501 to 812, score-change modules, tags and card layouts
Board of dashboard modules: quick actions, the score card, offer cards and the mobile layout
Illustrated offer cards: insurance, identity protection, subscriptions, bill negotiation, building credit and more
Annotated anatomy of the dashboard on desktop and mobile, numbering each section and its display rules
Finance

Experian Dashboard

A redesign for Experian’s dashboard.

Two phones: the Jami Squad NFT drop with its countdown, and Top Artists above Exclusive Drops
Finance

Stealth Crypto Venture

A crypto experience built to gamify NFT marketplaces.