A music memory match game. Flip pairs of cards and match them by sound — across ten sampled instruments, four difficulties, and an optional ear-training mode where labels are hidden and you match by listening alone.
Originally a freshman-year vanilla-JS project. Modernized to Svelte + TypeScript + Vite + Tone.js with a concert-hall visual direction.
prefers-reduced-motionlocalStoragenpm install
npm run dev # vite dev server at http://localhost:5173
npm run test # vitest unit tests (56 tests across engine / shuffle / scoring / storage)
npm run check # svelte-check + typescript
npm run build # type-check + production build to dist/
npm run preview # preview the production build
src/
├── lib/
│ ├── game/ — pure FSM, scoring, seeded shuffle (unit-tested)
│ ├── audio/ — Tone.js engine singleton, instrument factory, note tables
│ ├── stores/ — Svelte stores for game / audio / settings / prefs
│ ├── storage.ts — localStorage facade with schema versioning
│ └── types.ts
├── screens/ — one Svelte component per top-level screen
├── components/ — Card, Hud, Button, WaveformViz, Confetti
└── App.svelte — store-driven screen router
The match logic lives in src/lib/game/engine.ts as a pure finite state machine (idle → firstFlipped → secondFlipped → resolving → won), which is what makes it unit-testable and what eliminates the race-condition bug in the v1 click handler.
Audio is a single Tone.js AudioContext unlocked on first interaction. Piano uses Tone.Sampler over the original mp3s (pitch-shifting to fill chromatic notes); the rest are synthesized so no new sample files are required.
MIT.