Pick a hero and race the clock through math.
Addition, subtraction, multiplication, and division across 5 difficulty tiers, wrapped in a comic-book hero theme built for kids.
Play it live -> math-hero-bheng.vercel.app
- 12 illustrated heroes - each with its own color, element, and treasure icon; pick one to start.
- 4 operations x 5 difficulty tiers - addition, subtraction, multiplication, division, with the number range scaling per tier.
- Beat the clock - a per-question countdown; run out of time and the answer is revealed.
- Learn from every answer - a wrong pick or a timeout reveals the correct answer before moving on.
- Persistence - your operation, difficulty, and sound settings plus a best score per mode are saved between sessions.
| Area | Choice |
|---|---|
| Framework | Next.js 16 (App Router) |
| Language | TypeScript (strict) |
| Styling | CSS Modules + next/font (self-hosted Playpen Sans) |
| Images | next/image (self-hosted hero art) |
| Effects | canvas-confetti, Web Audio |
| Tests | node:test (pure logic) + Playwright (e2e) |
| Hosting | Vercel |
flowchart LR
A[Browser] -->|static, client-only| B["components/Game.tsx"]
B -->|"buildRun(hero, op, difficulty)"| C["lib/gameEngine.ts<br/>pure reducer"]
C -->|"generateRun (injectable rng)"| D["lib/math.ts<br/>question generator"]
B --> E["lib/settings.ts<br/>localStorage"]
The quiz is a pure state machine: all randomness lives in the buildRun / generateRun factories (an injectable rng), so gameReducer is a pure, fully unit-tested function of (state, action).
npm install
npm run devOpen http://localhost:3026.
| Script | Does |
|---|---|
npm run dev |
Dev server on port 3026 |
npm run build |
Production build |
npm run typecheck |
tsc --noEmit (app + tests) |
npm run lint |
ESLint |
npm test |
Unit tests (math + engine + settings) |
npm run e2e |
Playwright end-to-end tests (builds + serves on 3034) |
MIT (c) 2026 Bunlong Heng
Built by Bunlong Heng · See it in my portfolio →

