Chessalyzer
Heatmaps

Custom heatmap functions

Write your own per-square calculation for generateHeatmap.

A heatmap function answers one small question — "what value should this square have?" — and generateHeatmap asks it 64 times, once per square. If you can write that one function, you can make any heatmap.

Here's one from scratch: "how much of the game did white pieces occupy each square?" (as a percentage of all half-moves):

import { generateHeatmap } from 'chessalyzer/trackers';

const heatmapData = generateHeatmap(tiles.state, ({ data, square }) => {
    const cell = data.squares[square];
    if (!cell) return 0;
    return (cell.w.total.occupiedFor * 100) / data.movesTotal;
});

Need an extra value in your function — a piece you care about, a precomputed average? Just close over it:

const queen = { color: 'w', name: 'Qd' };

const queenSquares = generateHeatmap(tiles.state, ({ data, square }) => {
    const cell = data.squares[square];
    if (!cell) return 0;
    return cell[queen.color].byPiece[queen.name].movedTo;
});

The arguments

Your function gets a single object with three properties:

PropertyTypeWhat it is
datayour tracker stateWhatever you passed to generateHeatmap (e.g. TileTrackerState)
squareSquareThe square being calculated, e.g. 'a2'
startingPieceHeatmapPieceRef | nullThe piece that starts the game on this square, or null if it's empty

Most functions only need data and square — each cell is a board location. Reach for startingPiece when each cell should represent a piece identity instead (that's how PIECE_CAPTURED and TILE_OCC_BY_PIECE work):

import type { HeatmapFn } from 'chessalyzer/trackers';

// HeatmapPieceRef: { color: 'b' | 'w'; name: StartingPieceName }

const byPieceIdentity: HeatmapFn = ({ data, startingPiece }) => {
    if (!startingPiece) return 0;
    return lookUp(data, startingPiece.color, startingPiece.name);
};

Type the function as HeatmapFn<YourState> and data is fully inferred.

Learn from the presets

The built-in presets are ordinary heatmap functions written with this exact API — if one is close to what you want, its behavior is a great starting point to copy and adjust.

On this page