Chessalyzer
Trackers

Built-in trackers

gameTracker, pieceTracker, and tileTracker — what they count and what their state looks like.

Chessalyzer ships with three ready-made trackers. All examples below run on the three-game games.pgn from the quickstart, and every output block is the real thing — paste the code, and you'll see the same numbers.

gameTracker — results and openings

The simplest of the three: it reads each game's headers and result, and counts. It needs headers, which Chessalyzer parses automatically as soon as a game tracker is attached.

import { analyzePGN } from 'chessalyzer';
import { gameTracker } from 'chessalyzer/trackers';

const games = gameTracker();

await analyzePGN('games.pgn', { trackers: [games] });
console.log(games.state);
{
    results: { white: 1, black: 1, draw: 1 },
    gameCount: 3,
    eco: { A02: 1, B20: 1, C23: 1 },
}
FieldWhat it counts
resultsWins by white, wins by black, draws
gameCountGames processed
ecoGames per ECO opening code

pieceTracker — who captures whom

A capture matrix: for every starting piece, how often it captured each opposing piece. Pieces are named by their starting square — Qd is the queen, Pe the king's pawn, Nb the knight that began on the b-file — and each color gets its own table under w and b.

import { analyzePGN } from 'chessalyzer';
import { pieceTracker } from 'chessalyzer/trackers';

const pieces = pieceTracker();

await analyzePGN('games.pgn', { trackers: [pieces] });
console.log(pieces.state.w.Qd.Pf); // 1 — white's queen captured black's f-pawn

Our file only has three captures, so the (large, mostly zero) state boils down to:

{
    w: {
        Qd: { Pf: 1 }, // game 1: Qxf7 — the scholar's mate
        Bf: { Bc: 1 }, // game 3: Bxd7 — bishop takes bishop
    },
    b: {
        Qd: { Bf: 1 }, // game 3: Qxd7 — black's queen strikes back
    },
}

tileTracker — the life of every square

The workhorse. For each of the 64 squares it records, per color and per piece:

  • movedTo — how often a piece moved to this square
  • occupiedFor — how many half-moves a piece sat on this square
  • captures — captures that happened on this square
  • losses — pieces that were captured on this square
import { analyzePGN } from 'chessalyzer';
import { tileTracker } from 'chessalyzer/trackers';

const tiles = tileTracker();

await analyzePGN('games.pgn', { trackers: [tiles] });

console.log(tiles.state.movesTotal); // 19
console.log(tiles.state.squares['f7']);

State is keyed by algebraic square — tiles.state.squares['f7'] is the f7 report. And f7 turns out to be the most dramatic square in our file — the scholar's mate happened right there:

{
    w: {
        total: { movedTo: 1, occupiedFor: 0, captures: 1, losses: 0 },
        byPiece: {
            Qd: { movedTo: 1, occupiedFor: 0, captures: 1, losses: 0 },
            // …15 more white pieces, all zero
        },
    },
    b: {
        total: { movedTo: 0, occupiedFor: 18, captures: 0, losses: 1 },
        byPiece: {
            Pf: { movedTo: 0, occupiedFor: 18, captures: 0, losses: 1 },
            // …15 more black pieces, all zero
        },
    },
}

Reading it: black's f-pawn (Pf) never moved and defended f7 for 18 half-moves across the games, until white's queen (Qd) took it — once, and checkmate.

total combines all pieces of one color; byPiece breaks it down. You'll usually read state through heatmap presets rather than by hand — both tileTracker and pieceTracker states plug straight into generateHeatmap.

On this page