npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@jalpp/stockfishts

v1.0.1

Published

TypeScript library for running Stockfish engines in browser and Node.js environments with WebAssembly and worker support.

Readme

stockfishts

stockfishts is a TypeScript library for running Stockfish engines from both frontend and backend projects. It supports WebAssembly-based engines, worker-based execution, and a small, ergonomic API for evaluating chess positions and receiving incremental updates. Stockfishts also supports API wrapper for cloud based stockfish based chessdb.

Why use stockfishts?

  • Run Stockfish-style engines in the browser with Web Workers and WebAssembly
  • Use the same API in Node.js environments when a Worker-compatible runtime is available
  • Send UCI commands directly through a simple engine abstraction
  • Receive evaluation updates while the engine is thinking
  • Connect to chessdb via ts based APIs

Installation

npm install @jalpp/stockfishts

Quick start

Browser usage

import { StockfishWasmEngine, EngineName } from '@jalpp/stockfishts';

const engine = new StockfishWasmEngine('/stockfish.wasm.js', EngineName.Stockfish16);
await engine.init();

const result = await engine.evaluatePositionWithUpdate({
  fen: 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1',
  depth: 12,
  setPartialEval: (value) => console.log(value),
});

console.log(result);
engine.shutdown();

Node.js usage

import { StockfishWasmEngine, EngineName } from '@jalpp/stockfishts';

const engine = new StockfishWasmEngine('/path/to/stockfish.wasm.js', EngineName.Stockfish18);
await engine.init();

const evaluation = await engine.evaluatePositionWithUpdate({
  fen: 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1',
  depth: 10,
});

console.log(evaluation);
engine.shutdown();

ChessDB cloud database usage

import { ChessDbApi } from '@jalpp/stockfishts';

const cdb = new ChessDbApi();

const startpos = 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1';

// All known candidate moves, with eval, win rate, rank and a quality note
const all = await cdb.queryAll(startpos);
if (all.success) {
  console.log(all.data[0]); // { uci: 'e2e4', san: 'e4', score: '0.34', rawEval: 34, winrate: '55.32', rank: '1', note: 'Best' }
}

// ChessDB's single best known move
const best = await cdb.queryBest(startpos);
if (best.success) {
  console.log(best.data.move); // 'e2e4'
}

// Principal variation (best line) with score and depth
const pv = await cdb.queryPv(startpos);
if (pv.success) {
  console.log(pv.data.pvSAN.join(' '));
}

// Queue a position ChessDB doesn't know yet for background analysis
await cdb.queue(startpos);

Every ChessDbApi method returns a ChessDbResult<T> ({ success: true, data: T } or { success: false, error: string }) instead of throwing, so expected outcomes like an unknown position or an invalid FEN can be handled without try/catch.

ChessDB fetch implementation notes

ChessDbApi uses the runtime's globalThis.fetch by default, so in most setups you can create it without any extra configuration:

import { ChessDbApi } from '@jalpp/stockfishts';

const cdb = new ChessDbApi();

Browser

Modern browsers expose fetch globally, so no fetchImpl is required.

const cdb = new ChessDbApi();

Node.js

Node.js 18+ includes a global fetch. In older Node versions, or when you want to use a specific implementation, pass one explicitly:

import { ChessDbApi } from '@jalpp/stockfishts';
import { fetch as undiciFetch } from 'undici';

const cdb = new ChessDbApi({
  fetchImpl: undiciFetch as typeof fetch,
});

Tests and custom runtimes

If you are mocking network calls or running in a custom runtime, provide a fetchImpl that matches the fetch signature:

import { ChessDbApi } from '@jalpp/stockfishts';

const mockedFetch = async (input: RequestInfo | URL, init?: RequestInit) => {
  return new Response(JSON.stringify({ status: 'ok', moves: [] }), {
    status: 200,
    headers: { 'Content-Type': 'application/json' },
  });
};

const cdb = new ChessDbApi({ fetchImpl: mockedFetch as typeof fetch });

Runtime notes

  • In browser-based environments, the package expects a Worker-capable runtime and a reachable engine script URL.
  • In Node.js environments, a Worker-compatible runtime must be available. If your runtime does not expose a global Worker constructor, provide one before creating an engine instance.
  • WebAssembly support is checked at runtime before instantiating a wasm-backed engine.

Framework examples

Next.js example

'use client';

import { useEffect, useState } from 'react';
import { StockfishWasmEngine, EngineName } from '@jalpp/stockfishts';

export default function ChessPage() {
  const [score, setScore] = useState<string>('Loading...');

  useEffect(() => {
    let engine: StockfishWasmEngine | undefined;

    async function run() {
      engine = new StockfishWasmEngine('/stockfish.wasm.js', EngineName.Stockfish18);
      await engine.init();

      const result = await engine.evaluatePositionWithUpdate({
        fen: 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1',
        depth: 10,
      });

      setScore(result.lines[0]?.cp?.toString() ?? 'No evaluation');
      engine.shutdown();
    }

    run().catch(console.error);

    return () => engine?.shutdown();
  }, []);

  return <div>{score}</div>;
}

React example

import { useEffect, useState } from 'react';
import { StockfishWasmEngine, EngineName } from '@jalpp/stockfishts';

export function ChessEvaluator() {
  const [evaluation, setEvaluation] = useState('');

  useEffect(() => {
    const engine = new StockfishWasmEngine('/stockfish.wasm.js', EngineName.Stockfish16);

    engine.init().then(() => {
      return engine.evaluatePositionWithUpdate({
        fen: 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1',
        depth: 8,
      });
    }).then((result) => {
      setEvaluation(result.bestMove ?? 'No move found');
      engine.shutdown();
    });
  }, []);

  return <div>{evaluation}</div>;
}

UseEngine React hook (Next.js + React)


import { useEffect, useState } from 'react';

import {
    EngineName,
    parseEngineName,
    StockfishSimpleEngine,
    StockfishWasmEngine,
    type UciEngine,
} from '@jalpp/stockfishts';

const ENGINE_PATHS: Record<EngineName, string> = {
    [EngineName.Stockfish18]: '/static/engine/stockfish-18/stockfish-18-lite-single.js#/static/engine/stockfish-18/stockfish-18-lite-single.wasm',
    [EngineName.Stockfish17Point]: '/static/engine/stockfish-17/stockfish-17.1-lite-single-03e3232.js#/static/engine/stockfish-17/stockfish-17.1-lite-single-03e3232.wasm',
    [EngineName.Stockfish17]: '/static/engine/stockfish-17/stockfish-17-lite.js#/static/engine/stockfish-17/stockfish-17-lite.wasm',
    [EngineName.Stockfish16]: '/static/engine/stockfish-16.1-lite.js#/static/engine/stockfish-16.1-lite.wasm',
    [EngineName.Stockfish11]: '/static/engine/stockfish-11.js',
};

export const useEngine = (enabled: boolean, engineName: EngineName | undefined) => {
    const [engine, setEngine] = useState<UciEngine>();
    const normalizedEngine = parseEngineName(engineName);

    useEffect(() => {
        if (!enabled || !normalizedEngine) return;

        const engine = pickEngine(normalizedEngine);
        console.log('Initializing engine');

        void engine.init().then(() => {
            console.log('Engine initialized');
            setEngine(engine);
        });

        return () => {
            engine.shutdown();
            setEngine(undefined);
        };
    }, [enabled, normalizedEngine]);

    return engine;
};

const pickEngine = (engine: EngineName): UciEngine => {
    const path = ENGINE_PATHS[engine];

    if (engine === EngineName.Stockfish11) {
        return new StockfishSimpleEngine(path, engine);
    }

    return new StockfishWasmEngine(path, engine);
};

useChessDb React hook

import { useCallback, useEffect, useState } from 'react';
import { ChessDbApi, type ChessDbMove } from '@jalpp/stockfishts';

const cdb = new ChessDbApi();

export function useChessDb(fen: string) {
  const [data, setData] = useState<ChessDbMove[]>([]);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);

  const fetchMoves = useCallback(async (fenToQuery: string) => {
    setLoading(true);
    setError(null);

    const result = await cdb.queryAll(fenToQuery);
    if (result.success) {
      setData(result.data);
    } else {
      setData([]);
      setError(result.error);
      // Ask ChessDB to analyze positions it doesn't know yet.
      if (result.error === 'unknown') {
        void cdb.queue(fenToQuery);
      }
    }

    setLoading(false);
  }, []);

  useEffect(() => {
    void fetchMoves(fen);
  }, [fen, fetchMoves]);

  return { data, loading, error, refetch: () => fetchMoves(fen) };
}

Plain TypeScript example

import { StockfishWasmEngine, EngineName } from '@jalpp/stockfishts';

async function evaluate() {
  const engine = new StockfishWasmEngine('./stockfish.wasm.js', EngineName.Stockfish16);
  await engine.init();

  const result = await engine.evaluatePositionWithUpdate({
    fen: 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1',
    depth: 12,
  });

  console.log(result);
  engine.shutdown();
}

void evaluate();

Custom wasm engine example

import { CustomUciEngine, EngineName } from '@jalpp/stockfishtss';

const engine = new CustomUciEngine('/my-custom-engine.wasm.js', EngineName.Stockfish16, {
  checkWasmSupport: true,
});

await engine.init();
const result = await engine.evaluatePositionWithUpdate({
  fen: 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1',
  depth: 10,
});

console.log(result);
engine.shutdown();

API overview

The package exports:

  • StockfishWasmEngine for Stockfish-compatible wasm engine usage
  • StockfishSimpleEngine for simple worker-based engine usage
  • CustomUciEngine for any UCI-compatible wasm engine that is not limited to Stockfish
  • UciEngine as the base engine abstraction
  • EngineWorker as the worker interface
  • engine constants and result helpers from the engineTypes and parseResults modules
  • ChessDbApi as a typed client for the ChessDB (chessdb.cn) cloud chess database

Public API reference

UciEngine

  • init(): initializes the engine and performs the standard UCI handshake
  • setElo(elo): updates the Elo value requested during initialization
  • stopSearch(): stops the current search immediately
  • evaluatePositionWithUpdate(params): evaluates a position and streams partial updates
  • shutdown(): terminates the underlying worker and resets the engine state
  • isReady(): returns whether initialization has completed

StockfishWasmEngine

  • constructor(pathOrWorker, name): creates a Stockfish-compatible wasm engine from a worker URL or existing worker adapter
  • isSupported(): checks whether the current runtime exposes usable WebAssembly support

StockfishSimpleEngine

  • constructor(pathOrWorker, name): creates a simple wrapper around a worker-style engine without wasm checks

CustomUciEngine

  • constructor(pathOrWorker, name, options): creates a generic wrapper for any UCI-compatible wasm engine
  • isSupported(): checks whether the current runtime exposes usable WebAssembly support

parseEvaluationResults(fen, results, whiteToPlay)

Parses raw UCI engine messages into a PositionEval object with principal variations and evaluation data.

ChessDbApi

A typed fetch-based client for the ChessDB cloud chess database. Every method resolves to a ChessDbResult<T>{ success: true, data: T } on success, or { success: false, error: string } for both network failures and ChessDB's own error responses (e.g. "invalid board", "unknown", "nobestmove") — so callers don't need try/catch for expected outcomes.

  • constructor(options?): creates a client. options.baseUrl overrides the ChessDB endpoint; options.fetchImpl overrides the fetch implementation (useful in Node.js runtimes without a global fetch, or for tests)
  • queryAll(fen, options?): fetches every move ChessDB knows for a position, each with a formatted score, raw eval, win rate, rank and a human-readable quality note
  • queryBest(fen, options?): asks ChessDB for the single best known move
  • queryMove(fen, options?): asks ChessDB for one reasonable move, with some randomness (useful for varied play)
  • querySearchMoves(fen, options?): asks ChessDB for a list of candidate moves worth searching further, e.g. to pass to a local engine as searchmoves
  • queryScore(fen, options?): fetches ChessDB's standalone evaluation score for a position
  • queryPv(fen, options?): fetches ChessDB's principal variation (score, depth and moves) for a position
  • queue(fen): requests background analysis for a position ChessDB doesn't know yet
  • store(fen, move): requests analysis of one particular move from a position
Helper functions
  • getChessDbNoteWord(note): converts ChessDB's raw note character ("!", "*", "?") into a "Best" / "Good" / "Bad" / "unknown" label
  • getSideToMoveFromFen(fen): reads the side to move ('w' or 'b') out of a FEN string
  • normalizeChessDbScore(score, sideToMove): converts a ChessDB score (always from White's perspective) into a score relative to the side to move

Example methods

await engine.init();
await engine.stopSearch();
engine.setElo(3000);
engine.shutdown();

Development

npm install
npm run build

Credits

Special thanks to below authors who helped creating Stockfishts possible

Author

@jalpp

License

MIT