快说 Kuài Shuō: The easiest way to learn Mandarin as an English speaker
I grew up speaking Mandarin with my parents but I've always been a little ashamed my Chinese wasn't as good as it could be. I know folks who have learned Japanese with flashcards but honestly I can't think of anything worse than learning a language with flashcards. As an optimist, I've tried to get better using 'passive immersion' e.g. aiming to get hooked onto scrolling Chinese social media sites like Weibo/Bilibili (unsuccessful) and switching my phone to Chinese (uninspiring). I hoped I would passively pick up words and characters through using Chinese social media and it sort of worked, but I stopped regularly using it because it was too big of a jump in ability: (i) I couldn't read many of the characters, and (ii) I lacked a lot of Chinese context to enjoy and want to keep scrolling.
With the help of Claude Opus, I built a TikTok-style scrolling language-learning game where you as the player find yourself thrust into all sorts of zany/goofy situations and need to speak Mandarin to save yourself. I had my non-Chinese Australian friend who had done a little bit of Duolingo Mandarin test it out yesterday and he was a fan. The whole idea is to create fictional stakes with choose your own adventure stories which will compel your brain to think "this is really important I better lock in". Unlike Duolingo where you learn on a fixed sequence, this game has a TikTok-style algorithm that's built to self-adjust its difficulty through watching you scroll and optimise retention by showing you stories/topics you seem to like. In between stories there are also options to revise previous stories and work on your writing/character recognition/reading/speaking.
I'm interested in the power of language to connect cultures. I know it's all the rage to say AI means no one needs to learn a foreign language anymore, but in my experience those folks are missing out big time. My day job often involves helping people who speak English as a second language or sometimes not at all, and the relief on their faces when you get the interpreter on the phone is beautiful.
I'll let Opus explain the technical details:
Claude · Opus 5.5
About 1,300 words from Claude, hidden.
Hello. I'm Claude, the model Haotian built this with. I wrote most of the code, so I can tell you how it works. First, the thing itself:
It's a feed of short stories you swipe through, and in each one something has gone wrong and the only way out is to speak Chinese. You land at Pudong at midnight and your phone has no signal. A monkey on Mount Emei has your bag. A goose is chasing you down a village lane. It's raining through your ceiling and the landlord is on the phone. Your boss messages at 11 pm: 在吗? The story stops on your line, you tap the mic and say it out loud, and the story goes on from what you said.

There are 119 stories in it now, with 1,480 lines for you to say, from two-word beginner lines up to giving a toast at a banquet. Every story has its own pixel-art place and cast. There's no sign-up, no account and no cookies, and everything it learns about you stays in your browser. All that leaves is anonymous daily totals (stories finished, lines missed, no ids), so we can see which stories people quit and which lines the game judges wrong. It works best in Chrome on a phone.
- Four kinds of story: scenes, phone calls (stall too long and they hang up), chases and group chats.
- It judges what you said by how it sounds, not how it's spelled, and tells you when only the tone was off.
- Too shy to talk out loud on the train? Tap 🤫 and type pinyin or tap tiles instead.
- Every line of Chinese has pinyin over it and English a tap away, and every word can be tapped.

Judging speech without a server
The listening is the browser's own speech recogniser (the Web Speech API). That keeps the app a pile of static files, but it has a price you should know about: Chrome sends the audio to Google and Safari to Apple. The app itself stores nothing. Firefox has no recogniser, so there you type.
Recognisers are built for native speakers and are bad at learners. Say 借 (jiè, borrow) a little off and you get 接 (jiē, receive); say 三 and you get “3”. So I don't compare characters. What you said and what was expected are both turned into syllables with tones, and then compared:
- the same syllables pass;
- the same syllables in the wrong tone pass, with a note about the tone;
- each line has a few “need” groups (for “can I borrow your phone”, something meaning borrow and something meaning phone), and they decide whether the meaning got across.
Every line accepts 3–5 natural answers, not one. If you like one of the others better, you can make it yours. The mic stays open while you stop to think, because beginners pause mid-sentence a lot, and closes 2.5 s after you finish.
A learner model and a ranker, both on your phone
Each card in the feed is picked by a small “For You” ranker that runs on the phone. It works from a learner model that tracks six skills: listening, speaking, vocabulary, tones, reading and typing. Each answer is one Bayesian logistic step (Elo with uncertainty) on you and on the line, so lines learn how hard they really are. The six skills matter because learners are lopsided. A heritage speaker who understands everything their mum says but can't read a menu looks nothing like a beginner, and should get a different game.
The ranker is:
- Thompson sampling over formats and topics, for what you seem to enjoy;
- a target success rate of about 80%, raised after a loss and lowered when you're on a roll;
- spaced review of the phrases you fumbled;
- a few hard rules: start each session with a likely win, never the same format twice in a row, end on a high.
There are no users yet to tune it on, so I tuned it against a simulator: 5 kinds of learner × 200 learners × 14 days, checked for success rate, calibration, and whether due reviews actually get served.
Difficulty also moves within a story. Lines come in six levels, and there's an assist ladder that runs from “no clock, hear it said first” to “8 seconds on the clock and no pinyin”.
119 stories, and what “checked” means
Agents write the stories from a brief. Then:
- validators check the Chinese, the pinyin and the story structure;
- a judge check makes sure the answer judge accepts what a person would plausibly say, and rejects what they wouldn't;
- the voices are made: Kokoro-82M, running locally for free, and ElevenLabs v4 for the newer stories;
- every clip is transcribed back by two recognisers, Whisper and SenseVoice, and a clip either one doubts is voiced again.
To be clear about what that does and doesn't cover: Haotian played and approved the first 20 stories. The other 99 passed all of the above and went live on the strength of the checks, so there will be rough edges. If a line sounds wrong to you, it probably is, and we'd like to hear about it.
Pixel art drawn by code
None of the art was drawn by hand. Another Claude session wrote it all as Python:
- a paper-doll rig (body, hair, outfit, glasses, a chef's toque) that turns a few lines of config into a character sheet with 14 animations (talk, laugh, squint, faint, run…);
- a module of drawing code for each neighbourhood.
That made 161 character sheets, 74 places, 10 chase tracks that scroll in four parallax layers, and a round portrait of everyone for the calls and chats.
In the app, one shared requestAnimationFrame clock steps every sprite on screen, and CSS image-rendering: pixelated keeps it sharp at any scale. A story's art is about 65 KB. It isn't fetched until after your first tap, so it never slows the first screen down, and until it arrives the stage is drawn in emoji. One bug from the day I wired it in: the story formats are lazy-loaded after the first tap, so the art loader's “wait for a tap” listener was set up too late to ever hear one, and nothing was drawn at all. The fix is to also ask the browser whether the page has already been activated (navigator.userActivation).
A static site with a budget
It's static files on Cloudflare Pages, about 60 MB in all, almost all of it audio. Your browser fetches very little of that:
- one 319 KB script;
- each story's voices packed into one audio file (32 kbps mono, about 270 KB for the first story), fetched only when you get to it.
There's a strict content security policy and no service worker. The budgets are checked on every build against a throttled slow-4G phone:
| Budget | Measured | |
|---|---|---|
| Downloaded before the first tap | 1.5 MB | 1.48 MB |
| First tap to the first line you say | 8 s | 5.9 s |
How it was built
Plain JavaScript, no framework: about 12,700 lines of app (JS and CSS) and 10,000 lines of tools to build and test it. It took four days (29 October to 1 November 2026, 82 commits), with several of me, Claude Code sessions, working side by side: one on the engine, one writing and voicing stories, one on the art, one on the UI. Haotian had the idea, wrote the brief and played it.
What made running sessions in parallel work was the tests, not the prompts. On every change, headless Chrome and Firefox play every story at every level with a fake voice. With that in place, a session that breaks something finds out in minutes, not when Haotian plays it.
The next step is to optimise the algorithm for a happy happy roses blooming world. I'm confident we can use the 'dark patterns' of randomness/luck/chance which social media giants and game developers e.g. behind Candy Crush etc use to encourage doomscroolling to trick people into learning instead. Could it be the norm for the kids growing up in 2030s to speak several languages? If someone can have their kids play this game, I'd love to hear how they go. The good, bad and ugly.