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

maia3-js

v0.1.0

Published

JavaScript inference for Maia3 (Chessformer) chess models — human-like move prediction with per-candidate WDL.

Readme

maia3-js

JavaScript inference for Maia3 (Chessformer) — predicts human-like chess moves and per-move WDL evaluations.

ESM-only. Node + browser. ONNX Runtime under the hood.

Install

# Node
npm install maia3-js onnxruntime-node

# Browser
npm install maia3-js onnxruntime-web

onnxruntime-node and onnxruntime-web are optional peer dependencies — install whichever runtime you need.

Quickstart (Node)

import { Maia3, STARTPOS_FEN } from "maia3-js";

const maia = new Maia3({ variant: "5m" });
await maia.load();

const result = await maia.predict({
  fen: STARTPOS_FEN,
  selfElo: 1500,
});

console.log(result.bestMove);          // e.g. "e2e4"
console.log(result.winProbability);    // e.g. 0.52
for (const c of result.candidates) {
  console.log(c.uci, c.probability.toFixed(3), c.wdl);
}

Quickstart (Browser)

import { Maia3, STARTPOS_FEN } from "maia3-js/web";

const maia = new Maia3({
  variant: "5m",
  onProgress: (loaded, total) => console.log(loaded / total),
});
await maia.load();

const r = await maia.predict({ fen: STARTPOS_FEN, selfElo: 1500 });

5m is bundled with the npm package (~21 MB). 23m and 79m are downloaded on first use from Hugging Face and cached locally (Node) or in Cache Storage (browser).

History

Maia3 conditions on up to 8 prior board positions. Supply this via one of two equivalent inputs:

// Option A: explicit prior FENs (oldest first, exclude the current position)
await maia.predict({
  fen: currentFen,
  priorFens: [prevFen, prevPrevFen, ...],
  selfElo: 1500,
});

// Option B: UCI moves applied to a base FEN (defaults to startpos)
await maia.predict({
  fen: currentFen,
  priorMoves: ["e2e4", "e7e5", "g1f3", "b8c6"],
  selfElo: 1500,
});

When no history is supplied the current position is used and silently padded to 8 — matches the default UCI engine behavior in the Python reference (use_uci_history=False).

Passing both priorFens and priorMoves throws.

API

class Maia3 {
  constructor(options?: {
    variant?: "5m" | "23m" | "79m";   // default "5m"
    modelPath?: string;                // node: explicit path
    modelDir?: string;                 // node: cache dir
    url?: string;                      // web: override URL
    modelBytes?: ArrayBuffer;          // web: pre-fetched bytes
    onProgress?: (loaded: number, total: number) => void; // web
    executionProviders?: string[];     // pass-through to ORT
    temperature?: number;              // default 0 (argmax bestMove)
    topP?: number;                     // default 1.0
    topK?: number;                     // default 5
  });

  load(): Promise<void>;
  isLoaded(): boolean;
  predict(input: PredictInput): Promise<PredictionResult>;
  close(): Promise<void>;
}

interface PredictInput {
  fen: string;
  selfElo: number;
  oppoElo?: number;        // defaults to selfElo
  priorFens?: string[];    // exclusive with priorMoves
  priorMoves?: string[];
  baseFen?: string;        // for priorMoves; defaults to startpos
  temperature?: number;
  topP?: number;
  topK?: number;
}

interface PredictionResult {
  bestMove: string;                   // UCI of selected move
  moves: Map<string, number>;         // legal UCI → policy probability
  candidates: MoveCandidate[];        // top-K with per-move WDL
  wdl: { win: number; draw: number; loss: number }; // side-to-move POV
  winProbability: number;             // wdl.win + 0.5 * wdl.draw
}

interface MoveCandidate {
  uci: string;
  probability: number;
  wdl: { win: number; draw: number; loss: number };
  winProbability: number;
}

Sampling

  • temperature === 0 (default): bestMove is argmax of the masked policy.
  • temperature > 0: bestMove is sampled from the policy with optional nucleus filter (topP < 1). Matches the Python sample_from_logits routine.

Per-candidate WDL

For each of the top-topK candidates, maia3-js runs an additional forward pass with swapped Elos to score the position after the candidate move. WDL is reported in the original player's frame (inverted from the post-move side-to-move).

Variants

| Alias | Repo | Parameters | Bundled | |--------|---------------------------------|-----------:|---------| | 5m | UofTCSSLab/Maia3-5M | ~5 M | yes | | 23m | UofTCSSLab/Maia3-23M | ~23 M | no | | 79m | UofTCSSLab/Maia3-79M | ~79 M | no |

ONNX checkpoints are hosted at huggingface.co/cemoss17/maia3-onnx.

Parity with Python

Test fixtures are generated from the official Maia3 PyTorch implementation. The JS port matches it within:

  • Encoding: bit-exact (1e-6) for the (64, 96) token tensor.
  • Raw logits: 1e-3 absolute on Top-20 logits.
  • Policy probabilities: 5% absolute.
  • WDL: 5% absolute (current position and per-candidate).

npm run validate exercises this against 13 curated positions and 200 random parity positions in one go.

Re-exporting weights

If you need to export a custom checkpoint or the 23m / 79m variants yourself, install the maia3 Python package and run:

.venv/bin/python scripts/export-onnx.py \
  --variant 5m \
  --output models/maia3_5m.onnx \
  --validate

The --validate flag runs a 32-position parity check between the PyTorch model and the exported ONNX (atol=1e-4).

Limitations

  • inferBatch currently runs sequentially under the hood — the dynamo exporter baked a single-batch reshape into the graph. K=5 candidate scoring is still fast (~5×10ms on CPU), but true batched inference will land in a future export.
  • Sampling uses Math.random() — no seed parameter in v0.1.
  • Only the policy / value heads are exported. The Maia3 ponder head (predicted clock time) is dropped.

License

MIT.