FretHero · SoundCheck

From working loop
to full game.

June 7–8, 2026  ·  11 commits  ·  ~24 hours

One session took FretHero from a bare game loop to a complete guitar coaching experience: Milestone 1 scoring, Milestone 2 chromagram, a chromatic tuner, a feel Trainer, and a full SoundCheck rebrand.

Jun 7 · 11pm
Initial commit
Working game loop
Jun 7 · 11:20pm
audio.currentTime refactor
Single scoring clock
Jun 8 · 1:32pm
Milestone 1
Jukebox UI + reliable scoring
Jun 8 · 3:33pm
Milestone 2
Chromagram + vibe images
Jun 8 · 4:28pm
Tuner + Trainer
Two standalone tools
Jun 8 · 10:46pm
SoundCheck rebrand
Riff alignment engine

11 commits. A full guitar coaching app.

Working game loop

  • Initial commit: audio playback + tap scoring
  • audio.currentTime as the single scoring clock
  • Eliminated dual-timer drift that caused missed hits

Milestone 1 — Jukebox UI

  • Song selection screen with record crate UI
  • Shop image + back nav + calibration removed
  • First-run fix: game no longer starts mid-song
  • Reliable hit/miss scoring on every tap

Milestone 2 — Chromagram

  • Chromagram-based chord scoring (frequency analysis)
  • Vibe images: visual feedback per performance tier
  • Synced tab strip — lyrics/tab follows playhead
  • S-tier summary uses RockGodOutro background

Chromatic Tuner

  • Standalone tuner page
  • Real-time pitch detection via Web Audio API
  • Note name + cents deviation display
  • Works on desktop and mobile mic

Trainer — Feel Analyzer

  • Free-form feel analyzer with click metronome
  • Subdivision grid scoring (8th notes, 16ths)
  • Auto device detection for mic sensitivity
  • Overall grade headline on summary screen
  • No scrollbar on summary (layout fix)

SoundCheck Rebrand

  • FretHero → SoundCheck brand rename
  • Riff alignment engine: syncs scored notes to audio position
  • Scoring fixes: accuracy calculation overhauled
  • RockGod outro screen for S-tier finishes

The hard parts.

Dual-timer drift killing scoring accuracy

The game loop used a separate `setInterval` counter alongside `audio.currentTime`. Over a 3-minute song the two clocks drifted by hundreds of milliseconds, causing notes to register as misses even when tapped correctly.

Refactored to use `audio.currentTime` as the single source of truth for all scoring. The interval only triggers UI updates — it no longer owns the timestamp.

First-run bug: game started mid-song

On first load, audio autoplay was triggered before the user selected a song. The game would begin with the track already 2-3 seconds in, making the first notes unscorable.

Added a "not started" gate — audio does not begin until the user explicitly hits play from the song select screen.

Chromagram scoring: false positives on silence

The frequency analysis was scoring silence as "correct" when no string was played — the chromagram bins were reading ambient noise as a weak but non-zero chord match.

Added an amplitude threshold gate. Below a minimum RMS level the scorer returns no-match regardless of frequency content.

Trainer subdivision grid misaligned on mobile

The 16th-note subdivision grid rendered with correct spacing on desktop but collapsed unevenly on small screens. Tap zones shifted off the visual targets.

Switched grid from fixed pixel widths to percentage-based flex layout. Added auto device detection to adjust tap window tolerances for mobile vs desktop.

Summary screen scrollbar on short viewports

The Trainer summary added a grade headline that pushed the content just past the viewport height on phones, adding an unwanted scrollbar to what should be a single-screen result.

Wrapped summary in `overflow: hidden` and used `flex-shrink` on the details section so the grade headline takes fixed space and details compress.

🎸

audio.currentTime as the scoring clock

The refactor to a single clock source fixed every timing complaint in one commit. Two clocks that drift is a class of bug — using the media element's own clock eliminates it entirely.

🎨

Vibe images as performance feedback

Tiered background images (D-tier grimace face → S-tier RockGod outro) gave the scoring system emotional weight without any extra UI. The image IS the feedback — no score bar needed.

🎵

Chromagram for chord detection

Using frequency bin analysis (chromagram) instead of fundamental pitch detection made chord scoring far more robust. Players don't need perfect intonation — they just need to be in the right harmonic neighborhood.

🔁

Milestones as ship gates

Committing at Milestone 1 (jukebox + reliable scoring) before building Milestone 2 (chromagram) meant we always had a working app to fall back to. No mid-feature breakage reaching the user.

📱

Web Audio API on mobile

Both the tuner and the Trainer use the Web Audio API via `getUserMedia`. This worked out of the box on iOS Safari and Android Chrome without any native wrapper — no Capacitor, no Expo, no app store.

1

Use the media element's clock for scoring

Never maintain a separate counter alongside `audio.currentTime`. Dual clocks drift. `audio.currentTime` is the ground truth — use it directly in all scoring calculations.

2

Gate audio scoring behind an amplitude threshold

Frequency analysis on silence produces garbage. Always check RMS amplitude before running chromagram or pitch detection. Below threshold = no match, not a wrong match.

3

Milestone commits before adding complexity

Ship a working version before building the next layer. Milestone 1 → commit → Milestone 2. Never build two major features on an unstable base.

4

Flex layout for audio-synced grids on mobile

Fixed pixel widths break on small screens. Any grid that users interact with (tap zones, subdivision grids) must use percentage-based or flex layout to stay aligned with visual targets.

11
Commits
6
Features shipped
2
Milestones
4
New rules
~24h
Duration

"It went from a thing that plays audio and registers taps to an actual guitar coaching app. In one session."

— Zeb, June 8, 2026