@chessle/chess.js-extended
v0.1.0
Published
A powerful browser-only chess engine package that bundles Stockfish for position evaluation and analysis on top of chess.js
Downloads
53
Maintainers
Readme
chess.js-extended
A powerful browser-only chess engine package that extends chess.js with built-in Stockfish evaluation capabilities. This package bundles Stockfish for position evaluation and analysis directly in the browser using Web Workers and WebAssembly.
Features
- 🚀 Built on chess.js: All standard chess.js functionality included
- 🧠 Stockfish Integration: Bundled Stockfish engine for position evaluation
- 🌐 Browser-Only: Designed specifically for browser environments with Web Worker support
- ⚡ WebAssembly Support: Automatically uses WASM when available for better performance
- 🎯 Position Evaluation: Get numerical evaluations and mate scores
- 📈 Multi-PV Analysis: Get multiple principal variations for deeper insight
- 🔍 Suggested Lines: Get full suggested lines of play in SAN format
- ⚙️ Flexible Options: Configure search depth, time limits, and more
- 📦 Multiple Formats: CommonJS and ES Module builds included
Installation
pnpm install @chessle/chess.js-extendedQuick Start
import { ChessEngine } from "@chessle/chess.js-extended";
const engine = new ChessEngine();
// Make some moves
engine.move("e4");
engine.move("e5");
engine.move("Nf3");
// Evaluate the position
const lines = await engine.evaluatePosition();
if (lines.length > 0) {
const bestLine = lines[0];
console.log(`Evaluation: ${bestLine.evaluation}`);
console.log(`Best move: ${bestLine.line[0]}`);
console.log(`Suggested line: ${bestLine.line.join(" ")}`);
}Dual-Mode API: Promise and Streaming
ChessEngine offers two distinct modes for analysis to suit different use cases: a simple Promise-based mode for one-shot evaluations and a powerful Streaming API for continuous analysis.
Important: A single ChessEngine instance can only perform one analysis at a time. Attempting to start a new evaluation or analysis while one is already in progress will result in an error.
1. Promise-Based Analysis (One-Shot)
The evaluatePosition() method is perfect for when you need a single, final evaluation of the current board state. It returns a promise that resolves once Stockfish has completed its analysis.
import { ChessEngine } from "@chessle/chess.js-extended";
const engine = new ChessEngine();
engine.load("rnbqkbnr/pppp1ppp/8/4p3/4P3/8/PPPP1PPP/RNBQKBNR w KQkq - 0 2");
async function getEvaluation() {
console.log("Evaluating position...");
const lines = await engine.evaluatePosition({ depth: 18 });
console.log("Evaluation complete.");
if (lines.length > 0) {
const bestLine = lines[0];
console.log(`Evaluation: ${bestLine.evaluation}`);
console.log(`Best move: ${bestLine.line[0]}`);
}
}
getEvaluation();2. Streaming Analysis (Continuous)
The streaming API is ideal for applications that need to display real-time engine analysis, such as a live chessboard UI.
startAnalysis(options): Begins an infinite analysis of the current position.on('analysis', callback): Listens for analysis updates. The callback receives an array of the latestAnalysisLineobjects as the engine deepens its search.stopAnalysis(): Sends thestopcommand to Stockfish, which will then emit a finalbestmoveand terminate the analysis.
import { ChessEngine } from "@chessle/chess.js-extended";
const engine = new ChessEngine();
engine.load("r1bqkbnr/pp1ppppp/2n5/2p5/4P3/5N2/PPPP1PPP/RNBQKB1R w KQkq - 2 3");
// 1. Listen for analysis updates
engine.on("analysis", (lines) => {
console.clear();
console.log("Current Analysis:");
lines.forEach((line) => {
console.log(`- ${line.line.join(" ")} (Eval: ${line.evaluation})`);
});
});
// 2. Start the analysis
console.log("Starting continuous analysis...");
engine.startAnalysis({ multiPV: 3 });
// 3. Stop the analysis after a few seconds
setTimeout(() => {
console.log("Stopping analysis...");
engine.stopAnalysis();
console.log("Analysis stopped.");
}, 5000);API Reference
ChessEngine Class
The ChessEngine class extends the standard chess.js Chess class with evaluation capabilities.
Constructor
const engine = new ChessEngine();Properties
lines: AnalysisLine[]- An array of the most recent final analysis lines from the last completedevaluatePositioncall.
Methods
evaluatePosition(options?)
Evaluates the current position using Stockfish and returns a single result.
await engine.evaluatePosition(options?: StockfishOptions): Promise<AnalysisLine[]>;startAnalysis(options?)
Starts a continuous, infinite analysis of the current position. Use the on('analysis', ...) method to receive updates.
engine.startAnalysis(options?: StockfishOptions): void;stopAnalysis()
Stops a running analysis that was started with startAnalysis().
engine.stopAnalysis(): void;on(event, listener)
Subscribes to engine events. Currently, only the analysis event is supported.
engine.on('analysis', (lines: AnalysisLine[]) => void): void;off(event, listener)
Unsubscribes from engine events.
engine.off('analysis', (lines: AnalysisLine[]) => void): void;StockfishOptions Interface
interface StockfishOptions {
/** Search depth (default: 15) */
depth?: number;
/** Thinking time in milliseconds */
time?: number;
/** Exact time to think in milliseconds */
movetime?: number;
/** Number of nodes to search */
nodes?: number;
/** Number of principal variations to search for (default: 1) */
multiPV?: number;
/** Skill level of the engine (0-20) */
skillLevel?: number;
/** Contempt value for the engine (-100 to 100) */
contempt?: number;
/** Number of threads to use (1-512) */
threads?: number;
/** Hash table size in MB (1-32768) */
hash?: number;
/** Custom Stockfish worker URL (optional) */
stockfishUrl?: string;
}AnalysisLine Interface
interface AnalysisLine {
/** The evaluation of the position (e.g., 0.5, -1.2, "M5", "M-3") */
evaluation: number | string;
/** The suggested sequence of moves in SAN format */
line: string[];
}Usage Examples
Basic Position Evaluation
import { ChessEngine } from "chess.js-extended";
const engine = new ChessEngine();
engine.load("rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1");
const lines = await engine.evaluatePosition();
if (lines.length > 0) {
console.log(`Position evaluation: ${lines[0].evaluation}`);
// Output: Position evaluation: 0.15
}Multi-PV Analysis
Get the top 3 best moves and their evaluations.
const lines = await engine.evaluatePosition({ multiPV: 3 });
lines.forEach((line, index) => {
console.log(
`Rank ${index + 1}: ${line.line.join(" ")} (Eval: ${line.evaluation})`,
);
});
// Output:
// Rank 1: Nc6 ... (Eval: 0.15)
// Rank 2: c5 ... (Eval: 0.20)
// Rank 3: e6 ... (Eval: 0.22)Time-Limited Analysis
// Analyze for exactly 5 seconds
const lines = await engine.evaluatePosition({ movetime: 5000 });
if (lines.length > 0) {
console.log(`Timed evaluation: ${lines[0].evaluation}`);
}Configuring Engine Parameters
You can fine-tune the engine's performance and playing style by providing additional options.
const lines = await engine.evaluatePosition({
skillLevel: 10, // Set skill level to 10 (0-20)
contempt: 20, // Set contempt to 20 (-100-100)
threads: 4, // Use 4 threads
hash: 128, // Use 128MB of hash memory
});
if (lines.length > 0) {
console.log(`Evaluation with custom parameters: ${lines[0].evaluation}`);
}Getting the Best Move and Line
const lines = await engine.evaluatePosition();
if (lines.length > 0) {
const bestLine = lines[0];
console.log(`Best move: ${bestLine.line[0]}`);
engine.move(bestLine.line[0]);
console.log(`Principal variation: ${bestLine.line.join(" ")}`);
// Output: Principal variation: Nf3 Nc6 Bc4 Bc5
}Handling Mate Scores
// Load a position with checkmate
engine.load("rnb1kbnr/pppp1ppp/4p3/8/6Pq/5P2/PPPPP2P/RNBQKBNR w KQkq - 1 3");
const lines = await engine.evaluatePosition();
if (lines.length > 0) {
console.log(`Evaluation: ${lines[0].evaluation}`);
// Output: Evaluation: M-1 (mate in 1 for black)
}Using All chess.js Methods
Since ChessEngine extends Chess, you have access to all standard chess.js functionality:
const engine = new ChessEngine();
// Standard chess.js methods work as expected
console.log(engine.ascii());
console.log(engine.moves());
console.log(engine.inCheck());
console.log(engine.isGameOver());
// Make moves
engine.move("e4");
engine.move({ from: "e7", to: "e5" });
// Undo moves
engine.undo();
// Get FEN
console.log(engine.fen());
// And then evaluate
const lines = await engine.evaluatePosition();Browser Compatibility
This package requires:
- Web Worker support
- WebAssembly support (recommended, falls back to JavaScript if unavailable)
- Modern browser with ES6+ support
Performance Notes
- WebAssembly: Automatically detected and used when available
- Web Workers: Stockfish runs in a separate thread to avoid blocking the main thread
- Memory Management: Worker instances are properly cleaned up after analysis
- Timeout Protection: Analysis automatically times out after 30 seconds to prevent hanging
Error Handling
try {
const lines = await engine.evaluatePosition();
if (lines.length > 0) {
console.log(lines[0].evaluation);
}
} catch (error) {
if (error.message.includes("browser environment")) {
console.log("This package only works in browsers");
} else if (error.message.includes("worker failed")) {
console.log("Stockfish failed to load");
}
}Common Evaluation Values
- Positive numbers: Advantage for White (e.g.,
+1.5= White is ahead by 1.5 pawns) - Negative numbers: Advantage for Black (e.g.,
-0.8= Black is ahead by 0.8 pawns) - Mate scores:
M3= mate in 3,M-2= mate in 2 for opponent - Zero: Equal position
License
GPL-3.0 - See LICENSE file for details.
Contributing
This package is part of the Chessle project. Contributions welcome!
