Skip to content

Heatmap overlay for next-move frequencies#18

Open
jeffpalm wants to merge 2 commits into
atamano:masterfrom
jeffpalm:feature/heatmap
Open

Heatmap overlay for next-move frequencies#18
jeffpalm wants to merge 2 commits into
atamano:masterfrom
jeffpalm:feature/heatmap

Conversation

@jeffpalm
Copy link
Copy Markdown

@jeffpalm jeffpalm commented May 7, 2026

Hey! 👋 Just discovered Opening Scanner — really impressed with it. The no-server-state, all-client-side scan model is a delightful piece of engineering and the warm wood/paper aesthetic is gorgeous. Whipped this up in an evening and figured I'd toss it over the fence.

What this adds

A "Heatmap" toggle next to the Export button on the board panel. Flip it on and every candidate next move at the current position gets a per-square overlay:

  • Cold-to-hot color tint on the destination square — blue for the least-played continuation, red for the most-played, the short way through purple/magenta around the hue wheel. Skips green/yellow on purpose so the gradient doesn't fight the warm wood board.
  • Differentiating opening name — when every candidate falls under the same family (e.g. you're sitting in a Sicilian and looking at Najdorf / Closed / Alapin), the shared family prefix is stripped so each square shows just the variation. Otherwise the full ECO name shows.
  • Share % and game count — significant cells (≥ 3% of games) get whole-percent precision; insignificant cells drop the tint and use board-aware text colors with hundredth-of-a-percent precision (so a 1-game outlier reads as 0.02% instead of collapsing to 0%).

Everything fades in/out smoothly when you drill, and the typography is cqi-driven so it scales with the rendered square width — not the viewport.

Screenshots

Hikaru on chess.com, last year, blitz + rapid, 500 games:

Default — heatmap off
Dashboard with heatmap off

Heatmap on, initial position
Heatmap at the initial position

Board close-up — gradient + adaptive labels
Board close-up with heatmap

You can see the cold-to-hot scale at work: Nimzo-Larsen (b3) at 36% reads as deep red, Queen's Pawn / King's Pawn step down through magenta, English (c4) at 5% reads as cool blue. Insignificant cells (Mieses, Van't Kruijs, Hungarian, Zukertort) skip the tint entirely and switch to board-aware text colors so the label still reads cleanly on the bare squares.

Heatmap on, mid-opening (after 1.Nf3 d5 2.c4)
Heatmap at depth 3

Implementation notes

A few things you might want to know before reviewing:

  • No new server state, no new dependencies. The heatmap is a pure transform over the existing MoveNode continuations tree. Lives in lib/heatmap/heatmap.ts.
  • ECO catalog is dynamic-imported. eco-data.ts is ~800 kB. Users who never flip the toggle don't pay for it — hooks/use-eco-lookup.ts lazy-imports the module on first enable, then caches the lookup.
  • No worker boundary changes. Worker still streams + classifies + emits aggregated stats; the heatmap reads what's already there.
  • Stays inside the existing react-chessboard API. Uses the squareRenderer option (v5+) to paint per-square overlays. No fork, no monkey-patch.
  • i18n complete. All 13 locales got heatmap.label / heatmap.tooltip translations in this PR.

Files

  • lib/heatmap/heatmap.tsbuildHeatmap(), gradient color, significance threshold (3% share), variation-name picker
  • hooks/use-eco-lookup.ts — lazy ECO catalog loader
  • components/chess/heatmap-toggle.tsx — labelled <Switch> with a flame icon (lit when on)
  • components/chess/chess-board.tsx — accepts a heatmap prop, renders the overlay via squareRenderer with a fade-in/out state machine
  • components/scanner/dashboard.tsx — owns the toggle state, plumbs cells to the board

Test plan

  • pnpm typecheck clean
  • pnpm lint clean (0 new warnings)
  • Manual smoke: scan Hikaru on chess.com, toggle heatmap on, drill in/out, toggle off → on again (the toggle-back-on path was a bug at one point — fixed in this PR)
  • Light + dark squares both legible at every gradient stop
  • Long opening names truncate to 3 lines + ellipsis instead of spilling
  • Long counts (49% · 1,071) shrink the stat font instead of overflowing

Happy to iterate on color, threshold, label placement, or anything else — and equally happy if this just turns out not to be the direction you want for the project. Either way, thanks for building Opening Scanner.

🤖 Generated with Claude Code

Adds a "Heatmap" toggle next to the Export button. When on, every candidate
next move at the current position renders an overlay on its destination
square: a cold-to-hot color tint (saturated blue → red, skipping green/yellow
so it doesn't clash with the wood board), the differentiating opening name,
the share %, and the game count. Insignificant cells (< 3% share) drop the
tint and use board-aware text colors so the label still reads cleanly.

The ECO catalog (~800 kB) is dynamic-imported the first time the toggle is
enabled, so users who never open the heatmap don't pay for it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@vercel
Copy link
Copy Markdown

vercel Bot commented May 7, 2026

Someone is attempting to deploy a commit to the atamano's projects Team on Vercel.

A member of the Team first needs to authorize it.

@atamano
Copy link
Copy Markdown
Owner

atamano commented May 8, 2026

Hey, thanks for the contribution! The idea looks interesting, I'll take a closer look and get back to you ASAP.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants