chessPGN is a comprehensive TypeScript chess library for chess move generation, validation, and PGN parsing. Based on a fork of chess.js, it extends the original with powerful multi-game PGN file parsing capabilities while maintaining full backward compatibility.
- βοΈ Complete Chess Rules - Move generation, validation, check/checkmate/stalemate detection
- π PGN Support - Parse and generate Portable Game Notation
- π― Multi-Game Parsing - Efficiently handle large PGN files with cursor-based iteration
- β‘ Worker Thread Support - Parallel parsing for 3-5x performance boost on large files
- π Position Analysis - Attack detection, piece finding, board queries
- π Rich API - Both legacy (
ChessPGN) and modern (Game) interfaces - π¨ TypeScript First - Full type safety with comprehensive type definitions
- β Well Tested - 500+ tests with high code coverage
- π Extensive Documentation - Detailed guides and API reference
npm install @chess-pgn/chess-pgnimport { ChessPGN } from '@chess-pgn/chess-pgn'
const chess = new ChessPGN()
// Make some moves
chess.move('e4')
chess.move('e5')
chess.move('Nf3')
chess.move('Nc6')
// Get current position
console.log(chess.fen())
// rnbqkbnr/pppp1ppp/2n5/4p3/4P3/5N2/PPPP1PPP/RNBQKB1R w KQkq - 2 3
// Check game state
console.log(chess.isCheck()) // false
console.log(chess.isCheckmate()) // false
// Get legal moves
console.log(chess.moves())
// ['a3', 'a4', 'b3', 'b4', 'c3', 'c4', 'd3', 'd4', ...]
// Export as PGN
console.log(chess.pgn())
// 1. e4 e5 2. Nf3 Nc6import { ChessPGN } from '@chess-pgn/chess-pgn'
const chess = new ChessPGN()
while (!chess.isGameOver()) {
const moves = chess.moves()
const move = moves[Math.floor(Math.random() * moves.length)]
chess.move(move)
}
console.log(chess.pgn())import { ChessPGN } from '@chess-pgn/chess-pgn'
const pgn = `[Event "Casual Game"]
[Site "New York"]
[Date "2025.01.15"]
[White "Alice"]
[Black "Bob"]
[Result "1-0"]
1. e4 e5 2. Nf3 Nc6 3. Bb5 1-0`
const chess = new ChessPGN()
chess.loadPgn(pgn)
console.log(chess.getHeaders())
// { Event: 'Casual Game', Site: 'New York', ... }
console.log(chess.history())
// ['e4', 'e5', 'Nf3', 'Nc6', 'Bb5']Efficiently parse large PGN files with multiple games:
import { indexPgnGames } from '@chess-pgn/chess-pgn'
import * as fs from 'fs'
const pgnContent = fs.readFileSync('games.pgn', 'utf8')
// Create cursor with worker threads for parallel parsing
const cursor = indexPgnGames(pgnContent, {
workers: 4,
workerBatchSize: 10,
onError: (err, idx) => console.error(`Game ${idx}: ${err.message}`),
})
// Iterate through games
for await (const game of cursor) {
const headers = game.getHeaders()
console.log(`${headers.White} vs ${headers.Black}: ${headers.Result}`)
// Analyze final position
if (game.isCheckmate()) {
console.log('Checkmate!')
}
}
// Clean up worker threads
await cursor.terminate()import { ChessPGN } from '@chess-pgn/chess-pgn'
const chess = new ChessPGN()
chess.move('e4')
chess.move('e5')
chess.move('Nf3')
// Check if square is attacked
console.log(chess.isAttacked('e5', 'w')) // true (knight attacks e5)
// Get all attackers of a square
console.log(chess.attackers('e5'))
// ['f3']
// Find pieces
console.log(chess.findPiece({ type: 'n', color: 'w' }))
// ['b1', 'f3']
// Get piece at square
console.log(chess.get('f3'))
// { type: 'n', color: 'w' }import { ChessPGN } from '@chess-pgn/chess-pgn'
const chess = new ChessPGN()
chess.move('e4')
chess.move('e5')
chess.move('Nf3')
// Get detailed move history
const history = chess.history({ verbose: true })
console.log(history)
/*
[
{
color: 'w',
from: 'e2',
to: 'e4',
piece: 'p',
san: 'e4',
before: 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1',
after: 'rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1'
},
...
]
*/import { ChessPGN } from '@chess-pgn/chess-pgn'
const chess = new ChessPGN()
chess.move('e4')
chess.setComment('The most popular opening move')
chess.setSuffixAnnotation('!!') // Brilliant move
chess.move('e5')
chess.setComment('Symmetric response')
chess.setSuffixAnnotation('!') // Good move
// Get all comments
const comments = chess.getComments()
console.log(comments)
/*
[
{
fen: 'rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1',
comment: 'The most popular opening move',
suffixAnnotation: '!!'
},
...
]
*/
// Export with comments
console.log(chess.pgn())
// 1. e4!! {The most popular opening move} e5! {Symmetric response}ChessPGN- Legacy wrapper class with full chess functionalityGame- Modern core implementation (can be used directly)Move- Rich move object with detailed information
load(fen)- Load position from FEN notationreset()- Reset to starting positionclear()- Clear the boardfen()- Get FEN string of current position
move(move)- Make a move (SAN string or object)moves()- Get all legal movesundo()- Undo last movehistory()- Get move history
isCheck()- Check if in checkisCheckmate()- Check if checkmateisStalemate()- Check if stalemateisDraw()- Check if drawn positionisGameOver()- Check if game is overturn()- Get side to move
get(square)- Get piece at squareboard()- Get 2D board arrayascii()- Get ASCII board representationattackers(square)- Get pieces attacking a squareisAttacked(square, color)- Check if square is attacked
pgn()- Export game as PGN stringloadPgn(pgn)- Load game from PGNsetHeader(key, value)- Set PGN headergetHeaders()- Get all headers
setComment(comment)- Add comment to positiongetComment()- Get comment for positiongetComments()- Get all commentssetSuffixAnnotation(suffix)- Add move annotation (!!, !, ?, etc.)
indexPgnGames(pgn, options)- Create cursor for multi-game PGN files
Options:
workers- Enable parallel parsing with worker threadsworkerBatchSize- Games per batch (default: 10)strict- Strict PGN parsing modeonError- Error callback for malformed games
- Full API Documentation - Complete API reference
- Advanced Features - Multi-game parsing, worker threads, cursor API
- Contributing Guide - How to contribute to the project
chessPGN uses a delegation pattern where ChessPGN wraps the core Game
class:
Game- Core chess logic, single source of truthChessPGN- Legacy wrapper, delegates toGameIChessGame- Common interface for both classes
Both classes implement the same interface and produce identical results, verified through extensive parity testing across 469 real games.
# Install dependencies
npm install
# Run tests
npm test
# Run all checks (format, lint, test, build)
npm run check
# Build the project
npm run build
# Format code
npm run formatchessPGN has comprehensive test coverage:
- β 527+ tests covering all functionality
- β Unit tests for individual features
- β Integration tests for real-world scenarios
- β
Parity tests ensuring
ChessPGNβ‘Game - β Perft tests for move generation correctness
- Node.js: 20.x, 22.x, 24.x
- Browsers: All modern browsers (Chrome, Firefox, Safari, Edge)
- TypeScript: Full type definitions included
Contributions are welcome! Please read our Contributing Guide to get started.
Ways to contribute:
- π Report bugs
- π‘ Suggest features
- π Improve documentation
- β¨ Submit pull requests
- β Star the repository
This project is licensed under the BSD 2-Clause License - see the LICENSE file for details.
- Based on chess.js v1.4.0 (October 2025) by Jeff Hlywa
- Extended and maintained by Jeff Lowery
- Thanks to all contributors
- π Documentation
- π Issue Tracker
- π¬ Discussions
