Case Study

TheCoverStory

A daily cognitive exercise routine disguised as a family English game. If he ever suspected it was treatment, he'd stop playing.

Role

Solo Designer & Developer

Platform

Mobile Web

Audience

Family (Dad, daily)

Type

Personal Project

Home dashboard with game tiles and daily points수학 대결, serial subtraction game activeFamily scoreboard with daily leaderboard

The Challenge

The apps that could help him are the ones he'd never open

My dad needed to exercise his mind. But the apps that exist for that in Korea are overwhelmingly clinical, research-branded, and aimed directly at older adults who already suspect something is wrong. They feel like a diagnosis before you've even started. The target audience is forced to face the possibility of dementia on the opening screen.

My dad runs away from that kind of thing. He won't engage with anything that implies he's struggling, not out of stubbornness, but because confronting it feels like admitting defeat. The apps designed to help people like him are precisely the apps he would never open.

So the design problem wasn't cognitive exercise. It was disguise. The app needed to train the right things, and screen for early decline in the background, without ever presenting itself as something medical. He had to want to open it every single day without knowing exactly why it was built.

The clinical stakes are real. The K-MMSE (Korean Mini-Mental State Examination) is the standard tool Korean physicians use to screen for early cognitive decline. The orientation domain (knowing today's year, month, date, day of week, and season) is the first thing to slip in pre-Alzheimer's. It deteriorates before memory. Before language. Before anything you'd notice in daily life. Every game in this app maps to a domain the K-MMSE measures. He just thinks he's playing.

Home dashboard with level badge, daily points in header, 7 activity tiles, bottom nav, and daily word recall banner

The cover story

Seven game tiles, each one feels like a learning activity. The clinical layer is invisible.

Leaderboard hook

Daily points in the header reset every day. His family's scores are always one tap away on the Scoreboard.

Bottom nav

Three destinations (Scores, Home, Progress), all reachable within thumb distance. Home sits center and raised.

My Role

End to end: concept, design, code, and clinical game mapping

I designed and built this end to end: concept, design, full-stack development, and clinical game mapping. This isn't a spec someone else shipped. Every interaction, every game mechanic, and every data point in the backend was a design decision I made and then built myself.

The constraint that shaped everything: it had to work for my parents without any onboarding, tutorial, or support. If they needed help, the design had failed.

The design constraint

If he ever suspects this is therapy, it stops working. So the cover story isn't a workaround. It's the whole product.

Approach

He thinks he's practicing English and competing against the family

That framing gives him a goal he genuinely cares about: staying sharp, keeping up with family, beating his wife on the leaderboard. The English learning surface (weekly vocabulary topics, translation quizzes, word lists) isn't decoration. It's the entire reason he opens the app.

Meanwhile, each session is quietly generating a behavioral profile: how quickly he orients to today's date, whether serial subtraction accuracy is holding, how his delayed recall compares week over week. The screening happens in the background. He never sees a score on that axis, only his leaderboard rank.

Designing the cover story meant asking: what would a real learning app show? What would make a proud, competitive person feel like they're making real progress? The design had to be convincing enough that the therapeutic layer stayed completely invisible.

Seven games, seven clinical targets

Each game maps directly to a domain in the K-MMSE

The 날짜 퀴즈 (Date Quiz) asks year, month, date, day of week, season, and time of day. These are six of the ten orientation items the K-MMSE tests, and they are the single most sensitive early Alzheimer's signal.

The 수학 대결 (Serial Subtraction) is the exact task from the K-MMSE: start from a number and subtract in fixed steps. It tests working memory and sustained attention, not arithmetic skill. 기억력 게임 (Memory Game) mirrors the registration and delayed recall tasks: five words presented, then tested after two other activities create interference. The rest cover semantic categorization, sentence sequencing, visuospatial tracing, and verbal recall.

Serial Subtraction active, showing 20 − 3 = ?, number pad, timer, score counter

K-MMSE calculation task

Serial 7s and 3s, the exact task from the exam. Each answer has to be held in mind while computing the next.

Cover story

Framed as a math duel against family. Competitive framing drives daily return, not clinical obligation.

Timer pressure

Tighter time limits at higher levels stress sustained attention, the same cognitive load the K-MMSE targets.

All seven games

A language toggle adds three more Korean-specific games, bringing the total to ten

None of them announce what they're measuring. In Korean mode the English vocabulary games swap for 단어 유창성 (word fluency by category), 속담 완성 (proverb completion), and 물건 이름 대기 (object naming from description). Each targets a different domain of the same clinical framework.

Date Quiz in-game screenshot

날짜 퀴즈

Date Quiz

K-MMSE orientation

Year · month · date · day of week · season · time of day. Six of the ten K-MMSE orientation items. The first domain to decline in early Alzheimer's.

Serial Subtraction in-game screenshot

수학 대결

Serial Subtraction

K-MMSE calculation

Serial 7s and 3s, the exact calculation task from the K-MMSE. Each answer has to be held in mind while computing the next, which stresses short-term working memory.

Memory Game in-game screenshot

기억력 게임

Memory Game

K-MMSE recall

Five words presented, then tested after two other activities have created semantic interference. Distractors come from the same vocabulary theme, which makes the recall harder and more clinically meaningful.

Vocab Quiz in-game screenshot

영어 공부

Vocab Quiz

Language domain

Word-object association across 14 weekly themes. The cover story that makes everything else believable. Difficulty scales with vocabulary tier as level increases.

Sentence Builder in-game screenshot

문장 맞추기

Sentence Builder

Executive function

Arrange English words into a grammatically correct sentence. It requires holding multiple chunks in working memory and applying language rules: executive function under mild cognitive load.

Letter Tracing in-game screenshot

글씨 연습

Letter Tracing

Visuospatial

Trace Korean and English characters on a pressure-sensitive canvas. It maps to the K-MMSE visuospatial task and exercises fine motor coordination and psychomotor speed.

Odd One Out in-game screenshot

다른 하나 찾기

Odd One Out

Abstract reasoning

Four words, one from a different category. It tests abstract reasoning and semantic memory: the ability to extract rules from examples, not just recall them.

The Duolingo problem

Serving two masters at once

The hardest tradeoff: to keep the cover convincing, I had to add enough English learning content to make it feel like a real tool. Some games lean more vocabulary drill than I wanted: content that served the story more than the cognitive goals.

Every decision lived in that tension: convincing enough to maintain the disguise, therapeutically dense enough to actually matter. Serving two masters at once: that constraint defined every design choice in the product.

A language toggle in Settings unlocked a second game set. The four language-neutral cognitive games always appear: 수학 대결, 날짜 퀴즈, 글씨 연습, 다른 하나 찾기. In English mode the remaining three are vocabulary games. In Korean mode they swap for 단어 유창성, 속담 완성, and 물건 이름 대기, each targeting a different domain of the same clinical framework. Ten games total, seven visible at once.

This directly resolved the Duolingo problem. The English learning surface became optional, not load-bearing. Dad can switch to the Korean set, which is frankly harder, and the app stays therapeutically complete. The cover story now adapts to the player, not the other way around.

Scoring & Leveling

Two parallel number systems serve completely different purposes

Daily points reset every day and drive the family leaderboard, the competitive surface. Dad loses his lead if he doesn't play. Completing three games earns that day's points. Three, not two, because two might be a warmup.

XP never resets. It's cumulative, and it determines level, which determines game difficulty. Each activity awards 14 to 71 XP on a quadratic curve. Scoring zero still earns 14, so he always makes progress.

Level governs difficulty invisibly: harder vocabulary tiers, bigger serial subtraction numbers, more memory words, tighter time limits. He doesn't see a difficulty setting. He just notices the games feel different as he levels up.

Scoreboard page with full family leaderboard, rank icons, and 0/3 game progress chips

Daily leaderboard

Points reset every day. He loses his lead if he skips, the mechanic that makes the habit self-reinforcing.

Progress chips

0 / 3 games completed today. Completing three unlocks the day's full points. Three, not two, because two might still be a warmup.

Why the streak had to be offline-safe

If the streak breaks because of a spotty connection, the habit breaks too

The streak is the highest-stakes number in the app. Miss a day and it resets. That's the mechanic. But if the streak breaks because Supabase was slow over home wifi, not because he actually missed a day, the trust is gone.

The backend writes to localStorage first and Supabase second for every streak update and session save. If the network fails, the local state is authoritative. The next time connectivity is restored, local data syncs back up automatically, using the higher streak value as the source of truth.

How the Backend Works

localStorage-first writes, optimistic locks on XP

Two Supabase tables: daily_sessions (one row per user per day: scores, completed activities, daily points) and user_progress (cumulative XP, level, streak). The leaderboard polls daily_sessions every 10 seconds, so when mom finishes a game, dad sees it within seconds.

Two design decisions made the backend feel trustworthy: writes go to localStorage first so the app works offline, and XP uses an optimistic lock so a network retry can never award the same points twice.

utils/supabase.js: localStorage-first write
export const updateStreak = async (userId) => {
  const today = getTodayDate();

  // Step 1: read best available prior state
  const local = readLocalStreak(userId);
  let curStreak  = local?.currentStreak    ?? 0;
  let longStreak = local?.longestStreak    ?? 0;

  // Step 2: compute new streak
  const { currentStreak, longestStreak } =
    computeStreak(local?.lastActivityDate,
                  curStreak, longStreak, today);

  // Step 3: always write locally, works offline
  writeLocalStreak(userId, {
    currentStreak, longestStreak,
    lastActivityDate: today,
  });

  // Step 4: best-effort Supabase sync
  if (!isSupabaseConfigured()) return;
  try {
    await updateUserProgress(userId, {
      current_streak: currentStreak,
      longest_streak: longestStreak,
      last_activity_date: today,
    });
  } catch (err) {
    // local state preserved, will sync next session
    console.warn('Supabase sync failed', err.message);
  }
};
utils/supabase.js: optimistic lock on XP
export const recordActivityXp = async (
  userId, activityPoints
) => {
  const gain    = computeXpGain(activityPoints);
  const prog    = await ensureUserProgressRow(userId);
  const curXp   = prog.xp ?? 0;
  const newXp   = curXp + gain;
  const newLevel = xpToLevel(newXp);

  // Optimistic lock: only succeeds if XP unchanged.
  // If a retry already ran, 0 rows match → no double-count.
  const { data } = await supabase
    .from('user_progress')
    .update({ xp: newXp, level: newLevel })
    .eq('user_id', userId)
    .eq('xp', curXp)        // ← the lock
    .select('xp, level')
    .maybeSingle();

  if (data) return { gain, xp: data.xp, level: data.level };

  // 0 rows matched: XP already updated. Re-read, award 0.
  const { data: latest } = await supabase
    .from('user_progress')
    .select('xp, level')
    .eq('user_id', userId)
    .single();

  return { gain: 0, xp: latest.xp, level: latest.level };
};

Design Iterations

The version that shipped wasn't the first idea

v1

Desktop first, camera-assisted

The first version was desktop and iPad-first. The thinking: a wider viewport gives the 글씨 연습 game more room, and MediaPipe hand tracking let you trace letters in mid-air with your finger.

It lasted 15 minutes. Hand tracking ran at ~12fps and was exhausting to use. But the real problem was the premise: my dad doesn't open his laptop or iPad every day.

V1 desktop camera feed
V1 dark canvas iteration
v2

Dark, professional, wrong signals

Even after moving to mobile, the design used dark navy with plain white tiles and emoji icons. It looked like a well-built productivity tool. That was the problem.

Dark themes signal "tool." Cream and pastels signal "game." We rebuilt the entire visual layer: warm off-white, seven pastel gradients, Lucide icons replacing every emoji.

Before
Old design in dark navy
After
New design in cream and pastels
v3

The grid game no one wanted to tap

규칙으로 집까지 was a 7×7 Korean syllable path game. The cognitive target was solid, but the interaction was not.

A 7×7 grid gives each cell ~40×40px, under the 44px touch minimum. My parents avoided it. I redesigned it, then replaced it with 날짜 퀴즈.

Grid game, 7×7 with undersized touch targets

Design Decisions

Seven choices that defined the product

From dark navy to cream

The first version used a dark navy palette: serious, app-like, clinical. The problem is that clinical is exactly what I was trying to avoid. Dark themes signal "tool." Cream and pastels signal "game." I rebuilt the entire visual layer from scratch with a custom design system: warm off-white background, seven distinct pastel gradients (one per game tile), soft shadows, and generous radii. The visual language has to read as a game before a single word is processed.

Mobile-first, not mobile-adapted

This app will be used exclusively on a smartphone, probably one-handed, probably at a kitchen table. Every interactive target is at minimum 44×44px. The bottom navigation pattern (Scores · Home · Progress) keeps primary actions within thumb reach without stretching. Safe-area insets handle notched phones correctly. The home button sits raised in the center of the nav bar, elevated and prominent, because it's the action he'll take most.

Icons, not emojis

The first version used emoji throughout: medals, icons, completion screens. Emojis render inconsistently across Android/iOS versions and OS themes, and they can't be styled. I replaced every emoji with Lucide SVG icons. They keep a consistent stroke weight, scale cleanly, take a theme, and look professional enough to not tip the hand that this is a "cute family app" rather than a real product.

No tutorial

If a 65-year-old needs a tutorial to figure out how to start a game, the design failed. Every game intro screen shows exactly one thing: what the game is, and a large start button. The rules are communicated through the first two questions of play. No modals. No walkthroughs. No help text that will be ignored.

Language mode: two sets, one toggle

Settings holds a single toggle: 영어 or 한국어. It swaps the three language-specific games without touching anything else. This made the English learning surface optional. Dad can run the Korean set entirely and the clinical layer stays complete. The cover story stopped being mandatory the moment it became a choice.

Accessibility as display preference

Settings offers four switches: bigger text, bolder text, high contrast, and reduced motion. None is labeled "for older users" or "accessibility." They're just display preferences, because that's how they should feel to someone who might actually use them. If the label implies a need, the need becomes a reason not to turn it on.

Level lock

XP accumulates and difficulty scales up invisibly, until the games feel too hard. A single toggle (난이도 고정) freezes difficulty at whatever level feels right. Dad doesn't have to explain why. He doesn't have to admit a game got too hard. He just turns it on, and the scaling stops. The clinical data keeps coming in at the same difficulty, and the habit stays intact.

Settings with language toggle, accessibility switches, level lock, invite codeDark mode, the same seven games with a different visual identity
Progress page with level XP bar, difficulty lock toggle, streak stats, words learned

XP never resets

Cumulative XP determines level and difficulty. Scoring zero still earns 14 XP, so he always makes progress.

Level lock (난이도 고정)

One toggle freezes difficulty without admitting it got hard. Clinical data keeps flowing at a consistent level.

Streak counter

Written to localStorage before Supabase, so it survives network failures and a spotty connection never breaks the habit.

Impact at a glance

Dad plays every day. Not because he has to, but because he doesn't want to fall behind.

Daily habit

Structured cognitive exercise every single day, on his own initiative, without it feeling like anything other than a game.

Zero onboarding

No tutorial, no walkthrough, no help text. My parents opened the app and started playing. If they'd needed help, the design had failed.

7 → 10 games

A language toggle added three Korean-specific cognitive games, making the therapeutic layer independent of the English learning cover story.

Outcome

The metric that matters

Mom and dad love it. Dad plays every day, not because anyone told him to, but because he doesn't want to lose his streak or fall behind on the leaderboard. He's doing structured cognitive exercises daily, on his own initiative, without it feeling like anything other than a game.

That's the metric that matters. Not activation rates or session length. Just: did he play today?

See the live appOpens in a new tab

Eun Ji and her dad in the mountains

with love, THE favorite daughter