June 2 Update: Bug Fixes and an Essay
Three bugs got fixed today. The profile graph got a proper overhaul. And then I wrote something I've been putting off for a while. This is all of it in one post.
The WPM formula wasn't wrong. The inputs were.
Someone could theoretically finish a run with 743 WPM. That's not a flex — it was a bug.
The formula is (correctChars / 5) / (timeElapsed / 60). Standard. Correct. The problem was timeElapsed. If you lost all your lives within two seconds — say, by mashing random keys on hard mode — the timer would report 1 or 2 seconds, but correctChars could still be non-zero from whatever you typed before the lives ran out. Divide a few characters by 0.03 minutes and you get a WPM number that looks like a data entry error.
The fix is blunt: any run that ends in under 3 seconds now reports 0 WPM. No record saved. No graph point plotted. The formula didn't need to change — it was the input data that was nonsense.
Song HUD going off-screen on mobile
The song mode HUD has three segments: hits/misses on the left, accuracy in the middle, and the progress bar on the right. On narrow viewports — the kind you get on most Android phones — the stats segment was vanishing off the right edge of the screen.
The culprit was margin-left: auto on the stats container. That was pushing it as far right as it could go, which on a 360px wide screen meant partially or fully off-screen. Removed it. Also tightened the minimum width on the progress bar so all three HUD segments have room to coexist without forcing horizontal scroll. Small fix, actually usable now.
The profile graph was silently dropping all Song runs
The profile graph shows your last 30 games. There's a filter row at the top: All / Words / Sentences / Song. If you clicked "Song" it worked fine — showed only song games. If you clicked "All" it showed everything except song games.
One stray filter line in the rendering logic was stripping Song mode entries before they ever reached the canvas draw function. It had probably been there since Song mode was added to the history tracking. All 30 games now render correctly across all modes, all filters.
For context, here's what the graph actually shows: WPM (or normalized score for Song) as the main line, accuracy as a faint orange overlay, dots colored by difficulty (green for Baby, cyan for Standard, red for Hard), and dot shape by mode (circles for Words, squares for Sentences, diamonds for Song). The first run in your history was also being dropped due to an off-by-one in the data slice. That's fixed too. If you have exactly 1 game in history it now shows up.
You can see a live example of what a full profile looks like at /profile-demo — a demo profile with 47 games across all modes and difficulties, with real badge unlocks, so you don't have to grind to see what the graph looks like populated.
On AI tools and effort
AI tools were used to build this. The AI slop discourse is valid — the internet is full of zero-effort garbage, apps that are clearly a prompt and a deploy button. That's a real problem.
The distinction that matters: AI wrote the code. The decisions were mine. Song mode, lives instead of timers, the PRNG for Daily Challenge, ghost race, 37 badges — every feature was a deliberate call about what was worth building. The tool can't tell you what's interesting. That part is still on the person.
Not a 20-minute project. The Lighthouse score is 100/100/100/100 because someone spent real time understanding what was regressing it. The badge system has 37 entries because the scope kept growing. The code is more considered than a first glance suggests.
On ads: they're going in blogs only, not the game. The game stays clean. No P2W, no nag screens, no paywall.
Wait. I just realized I'm several paragraphs deep into this. Too lazy to quit now.