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

@og-nav/expo-stockfish

v0.1.0

Published

Stockfish chess engine for React Native + Expo (iOS)

Readme

@og-nav/expo-stockfish

Expo native module wrapping the Stockfish 17 C++ chess engine for iOS. Runs Stockfish with NNUE neural networks on-device via a four-layer bridge (Swift -> ObjC++ -> C++ -> vendored Stockfish source), exposing a clean React hook and low-level UCI API.

import { useStockfish, useStockfishInfo } from "@og-nav/expo-stockfish";

const { isReady, bestMove, setPosition, search, stop } = useStockfish();
const info = useStockfishInfo(); // streaming info, call in a leaf component

Install

npm install @og-nav/expo-stockfish
# or
pnpm add @og-nav/expo-stockfish

Then download the NNUE neural network files (~88 MB):

npx expo-stockfish download-nnue

The NNUE files are not included in the npm package to keep it small. They are bundled into the iOS app as a resource bundle at build time.

Peer dependencies

The module requires Expo and React Native but does not bundle them:

npx expo install expo react react-native

Quickstart

React hook (recommended)

import { Chessboard, type ChessboardRef } from "@og-nav/expo-chessboard";
import { Chess } from "chess.ts";
import { useStockfish } from "@og-nav/expo-stockfish";
import { useRef, useState, useCallback, useEffect } from "react";

export function PlayScreen() {
  const [chess] = useState(() => new Chess());
  const boardRef = useRef<ChessboardRef>(null);

  const { isReady, bestMove, setPosition, search, stop } = useStockfish({
    skill: 10,       // 0-20 (default 20)
    throttleMs: 150,  // throttle info-line state updates
  });

  // React to engine's best move
  useEffect(() => {
    if (bestMove) {
      boardRef.current?.animateMove(bestMove.from, bestMove.to, bestMove.promotion);
    }
  }, [bestMove]);

  const onPlayerMove = useCallback(() => {
    if (chess.turn() === "b" && !chess.gameOver()) {
      stop();
      setPosition(chess.fen());
      search({ depth: 15 });
    }
  }, [chess, stop, setPosition, search]);

  if (!isReady) return <Text>Loading Stockfish...</Text>;

  return (
    <Chessboard
      ref={boardRef}
      chess={chess}
      boardSize={360}
      playerSide="white"
      onMove={onPlayerMove}
    />
  );
}

One-shot helper

For cases where you just need a single best move:

import { findBestMove } from "@og-nav/expo-stockfish";

const move = await findBestMove(
  "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq - 0 1",
  { depth: 18, skill: 10 }
);
console.log(move.uci); // "e7e5"

Low-level API

Full control over the UCI protocol:

import {
  startEngine,
  stopEngine,
  sendCommand,
  addOutputListener,
  isRunning,
  getProcessStats,
} from "@og-nav/expo-stockfish";

await startEngine();

const sub = addOutputListener((line) => {
  console.log("engine:", line);
});

sendCommand("position startpos moves e2e4");
sendCommand("go depth 20");

// Later:
sendCommand("stop");
await stopEngine();
sub.remove();

API Reference

useStockfish(options?)

React hook that manages the engine lifecycle.

Options: | Option | Type | Default | Description | |--------|------|---------|-------------| | autoStart | boolean | true | Start engine on mount | | threads | number | 1 | Number of search threads | | hashMB | number | 32 | Hash table size in MB | | skill | number | 20 | Skill Level (0-20) | | throttleMs | number | 100 | Throttle info-line state updates | | onError | (err: Error) => void | — | Error callback |

Returns: | Field | Type | Description | |-------|------|-------------| | isReady | boolean | Engine initialized and ready | | bestMove | BestMove \| null | Latest bestmove result | | analyze | (opts?) => Promise<AnalyzeResult> | Promise-returning search (idiomatic for game loops) | | setPosition | (fen: string) => void | Send position command | | search | (opts?) => void | Start search (depth/movetime/infinite) | | stop | () => void | Stop current search | | sendRaw | (cmd: string) => void | Send raw UCI command | | start | () => Promise<void> | Manually start engine | | shutdown | () => Promise<void> | Manually stop engine |

Streaming info lines are exposed separately via useStockfishInfo() so consumers can subscribe in a leaf component. This keeps the ~50/sec info stream from re-rendering the parent subtree (e.g. a chessboard).

useStockfishInfo()

React hook that subscribes to the shared info store and returns the latest SearchInfo | null. Call it in the component that actually renders the info strip, not in a parent that also renders the board:

function EngineInfoBar() {
  const info = useStockfishInfo();
  if (!info) return null;
  return <Text>depth {info.depth} · {info.nps} nps</Text>;
}

For imperative reads (e.g. polling from a timer), use getInfoSnapshot() instead — it returns the current snapshot without subscribing.

findBestMove(fen, options?)

One-shot async helper. Starts the engine if needed, sends position + search, resolves on bestmove. 60-second safety timeout.

Parsers

import { parseInfoLine, parseBestMove, parseUCIMove } from "@og-nav/expo-stockfish";

const info = parseInfoLine("info depth 20 nodes 1234567 nps 500000 pv e2e4 e7e5");
// { depth: 20, nodes: 1234567, nps: 500000, pv: ["e2e4", "e7e5"], ... }

const best = parseBestMove("bestmove e2e4 ponder e7e5");
// { from: "e2", to: "e4", uci: "e2e4", ponder: "e7e5" }

const move = parseUCIMove("e7e8q");
// { from: "e7", to: "e8", promotion: "q" }

getProcessStats()

Returns current process stats (thermal state, memory). Useful for monitoring engine performance on device.

const stats = getProcessStats();
// { thermalState: 0, residentBytes: 123456789, virtualBytes: 987654321 }

NNUE Setup

The Stockfish 17 neural networks (~88 MB total) are required for full engine strength. They are excluded from the npm package to keep it under npm's size limit.

# Download NNUE files
npx expo-stockfish download-nnue

# Force re-download
npx expo-stockfish download-nnue --force

The files are placed in cpp/Stockfish/src/ and bundled into the iOS app as StockfishNNUE.bundle at build time.

If the automatic download fails, you can download manually:

curl -L -o cpp/Stockfish/src/nn-5227780996d3.nnue \
  https://tests.stockfishchess.org/api/nn/nn-5227780996d3.nnue

curl -L -o cpp/Stockfish/src/nn-37f18f62d772.nnue \
  https://tests.stockfishchess.org/api/nn/nn-37f18f62d772.nnue

Example App

The example/ directory contains a full Expo app with two tabs:

  • Play — Play against Stockfish using @og-nav/expo-chessboard for the board
  • Test — Device-side test harness (position runner, bench, mate-in-N suite, leak detector)
cd example
npm install
npx expo-stockfish download-nnue  # from parent dir
npx pod-install
npx expo run:ios

Architecture

Four-layer bridge from JS to C++:

JS/TS (useStockfish hook)
  -> ExpoStockfishModule.swift (Expo Modules definition)
    -> StockfishBridge.mm (ObjC++ singleton)
      -> StockfishEngine.cpp (C++ wrapper)
        -> Vendored Stockfish 17 source

The C++ layer replaces std::cin/std::cout with custom streambufs to route UCI protocol through the bridge. Engine output is dispatched to the main thread for safe React state updates.

Known Limitations

  • iOS only. Android module is a stub.
  • Single engine instance. The bridge is a singleton — only one Stockfish process at a time.
  • Stockfish 17. Upgrade to SF 18 is planned for a future release.
  • No std::cin/std::cout while engine runs. The bridge redirects these streams.

License

GPL-3.0-or-later (required by Stockfish's license).