Docs
Otter ships as a pip package. Load it, configure parameters, and query predictions — fully local, no server required.
# install
pip install otter-chessQuickstart
Initialize the model, pass it a position (FEN), game history list, rating level, time control format, and remaining clock fraction to receive skill-conditioned and time-aware predictions.
from otter_chess import OtterModel # Initializing OtterModel loads the cached model weights automatically model = OtterModel(device="cpu") result = model.predict( fen="r1bqkbnr/pppp1ppp/2n5/4p3/4P3/5N2/PPPP1PPP/RNBQKB1R w KQkq - 2 3", player_elo=1600, # target active rating bracket opponent_elo=1500, # target opponent rating bracket history_moves=["e2e4", "e7e5", "g1f3", "b8c6"], # preceding moves time_control="600+0", # rapid base + increment format time_remaining=480 # clock time in seconds (fraction auto-calculated) ) print(result["win_probability"]) # Win evaluation between -1 and +1 print(result["moves"][0]["move"]) # e.g., "f1b5" (Ruy Lopez)
Model card
Otter is a human chess AI model of 15.3 million parameters trained on 6.1 billion positions from 117 million rapid games on Lichess spanning club to titled play. Rather than optimizing for the strongest move, it's conditioned on a target rating band and predicts the move a player at that level would plausibly make — including known blind spots for that band. Full methodology is in the paper linked on the home page.
Loads model configuration and initializes checkpoint parameters on the target hardware device.
.safetensors or .pt). If None, queries standard cache paths or downloads weights."cpu" or "cuda" (or other hardware backends).model.safetensors release on Hugging Face.Runs the model inference pipeline to output move distributions conditioned on skill and temporal pressure.
["e2e4", "e7e5"])."180+2" for Blitz, "600+0" for Rapid).0.0 and 1.0.clock_fraction is calculated automatically using the base time control seconds.Returns
A dictionary containing:
"fen": The board FEN string analyzed."win_probability": The model's evaluation of the position between-1.0(losing) and+1.0(winning)."moves": A list of predicted UCI moves and probabilities sorted from most likely to least likely."aux_predictions": Decoded properties from the auxiliary head (moving piece, captured piece, checks probability, from/to squares, and confidences).
WebGPU / WASM runtime
For browser-based or client-side JavaScript applications, you can execute the exported ONNX model directly on the client using onnxruntime-web. This allows for fully local execution using WebGPU (or WebAssembly as a fallback) without sending game positions to a server.
To load and run the model in your JavaScript code:
import * as ort from 'onnxruntime-web'; // Create an inference session with WebGPU acceleration const session = await ort.InferenceSession.create('/path/to/policy_model.onnx', { executionProviders: ['webgpu', 'wasm'] }); // Feed active elo, opponent elo, board state, move history, etc. const feeds = { board: new ort.Tensor('float32', boardData, [1, 18, 8, 8]), history_ids: new ort.Tensor('int64', historyIds, [1, 20]), active_elo: new ort.Tensor('int64', [activeEloBucket], [1]), ... // and other required inputs }; const results = await session.run(feeds);
Input / output formats
Positions in, moves out — FEN for board state, UCI for moves (e.g. e2e4, e7e8q for promotion). Both APIs also accept and return PGN move lists if you'd rather work with full game history.