Skip to content
clariant @ arch : ~/clariant/projects/piano-memory-match
cd ~/projects

~/projects/piano-memory-match on ⎇ main []cat case-study.md

shipped

Piano Memory Match

A music memory game — match pairs of notes by sight or by ear, across ten instruments. Built in Svelte + Tone.js with a pure-FSM game engine and 56 unit tests.

Piano Memory Match main menu — title, tagline, and Play / Free Play / High Scores / Settings buttons on a curtain-dark background
role
Solo developer
period
May 2026
status
shipped
Svelte TypeScript Vite Tone.js Vitest

Context

A small but properly-built music game: flip pairs of cards and match them by sound, across ten instruments, four difficulties, and an optional ear-training mode where labels are hidden and you match by listening alone. The point wasn’t novelty — it was building something small to the bar of every interaction is tested and every edge case is handled.

What I built

  • Designed the game logic as a pure finite state machine (idle → firstFlipped → secondFlipped → resolving → won) — unit-testable, no race conditions, easy to reason about.
  • Built the audio layer on a single Tone.js AudioContext singleton — piano uses Tone.Sampler over real mp3 samples (pitch-shifted to fill chromatic notes); the rest are synthesized, so no extra sample files are required.
  • Added an “Match by Ear” mode — cards stay face-down even when flipped; pairs are identified by sound alone.
  • Free-Play virtual keyboard, four difficulty curves (4 / 8 / 12 / 16 pairs, Expert spans two chromatic octaves), star ratings with per-instrument personal bests, waveform viz synced to master output, confetti on win.
  • Keyboard nav (Tab/Space/R/Esc), prefers-reduced-motion-aware 3D flip, mobile-friendly with ≥44px touch targets.
  • 56 unit tests in Vitest covering the engine, seeded shuffle, scoring, and storage.

Stack

Svelte 4 · TypeScript · Vite 5 · Tone.js 15 · canvas-confetti · Vitest

Outcome

Live at piano.iant.my.id. Small surface area, but every screen, transition, and audio path has a deliberate decision behind it.

~/projects/piano-memory-match on ⎇ main []cat links.md