Skip to content

About

国际象棋 AI 网页版 · Stockfish 18(WASM/NNUE)全部跑在浏览器本地 · 六档难度 · 复盘分析与评估曲线 · PGN 导入导出 · ECO 开局识别(3810 条)· 纯前端零后端,静态托管即可部署

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

Chess Master (International Chess · Stockfish 18 WASM · Six Difficulty Levels · Pure Frontend)

中文文档 | English

Play it now: https://justa-cai.github.io/chess-master/

The 7.3 MB engine is fetched lazily the first time the AI has to move, then cached by the browser — later visits and reloads load it from cache.

A pure frontend, zero-backend International Chess game: Stockfish 18 compiled to WebAssembly runs entirely in your browser (NNUE neural-network evaluation), behind six difficulty levels from beginner to grandmaster. No server, no account, no installation.


1. Difficulty Levels

The app offers six difficulty levels, all of them served by the same Stockfish build. This is possible because the official Stockfish WASM build — unlike the Pikafish build the sibling 02.chess project has to work around — still ships the engine's own strength-limiting options:

option name Skill Level       type spin  default 20   min 0    max 20
option name UCI_LimitStrength type check default false
option name UCI_Elo           type spin  default 1320 min 1320 max 3190
option name MultiPV           type spin  default 1    min 1    max 256

So difficulty is a two-stage knob:

  1. Native weakening — UCI_LimitStrength + UCI_Elo covers roughly 1320–3190 Elo.
  2. Our own sampling — because UCI_Elo bottoms out at 1320 (too strong for a beginner), the lower levels additionally ask the engine for several candidate moves via MultiPV and sample among them with a centipawn softmax temperature plus a blunder probability.
Level Name Target Elo Search MultiPV Temperature Blunder rate
1 Beginner 1320 depth 2, ≤1.2 s 6 800 35 %
2 Novice 1320 depth 4, ≤1.5 s 5 500 12 %
3 Intermediate 1500 depth 6, ≤2.0 s 4 200 2 %
4 Advanced 1900 depth 9, ≤3.0 s 3 100 0
5 Master 2400 depth 12, ≤4.0 s 1 0 0
6 Grandmaster unlimited ≤3.0 s 1 0 0

The depth and movetime limits are both passed to go, and Stockfish stops at whichever comes first — so a low level never searches too deep, and every level has a hard time ceiling.

UCI_LimitStrength and Skill Level are two independent mechanisms and are never mixed. That is not a guess: it was measured. On the same middlegame position, go depth 12 with MultiPV 1:

Configuration bestmove nodes
default (Skill 20, no limit) d2d3 47,397
LimitStrength + Elo 1320 f3g5 185,368
LimitStrength + Elo 1400 d2d3 91,067
LimitStrength + Elo 2400 d2d3 99,326

Note the first row: Skill Level already defaults to 20, yet the Elo 1320 row still plays visibly worse (f3g5 is a planless move). In other words UCI_LimitStrength derives its own strength during search and ignores Skill Level. Each level therefore uses exactly one of the two mechanisms — the Elo one, throughout. The finding is recorded in the header comment of js/difficulty.js so the experiment does not have to be repeated.

Centipawn loss caps keep low levels "weak but coherent". A candidate more than LOSS_CAP centipawns behind the best move is discarded before sampling (400 / 300 / 200 / 120 cp for levels 1–4; 600 cp in blunder mode). A level-1 opponent will hang a pawn, but it will not give away a queen or miss a mate-in-one — that would read as a bug, not as an easy opponent.

The level numbers are initial values awaiting calibration: they must be confirmed by engine-vs-engine matches across adjacent levels, with colours swapped, and the results written back into the comment table in js/difficulty.js.


2. Highlights

  • Single engine, no second download. Levels 1–6 are all Stockfish. The whole engine is two files totalling 7.3 MB (stockfish-18-lite-single.wasm 7.30 MB + stockfish-18-lite-single.js 21 KB), fetched once and cached. Compare the sibling 02.chess, which needs a 49 MB neural-network weights file on top of its engine.
  • Runs on GitHub Pages with no special headers. The engine is the lite single-threaded build — Threads reports a max of 1, so SharedArrayBuffer is not used and COOP/COEP cross-origin isolation headers are not required. That is why this project has no coi-serviceworker.js, unlike 02.chess.
  • Answering your own moves, not just playing them. The sidebar table records the real search depth, node count, NPS, elapsed time and evaluation of every move from the engine's actual UCI output. When sampling picks a move other than the engine's first choice, the row is explicitly marked "suboptimal". Fields that are genuinely unavailable show -; nothing is invented.
  • Review analysis with an evaluation curve. The engine re-scores the whole game move by move at full strength, labels each move (best / good / inaccuracy / mistake / blunder) and draws a win-probability curve. Loss is computed as V_i + V_{i+1} from the engine's own scores — plies the engine did not reach are shown as gaps, not as zeros.
  • PGN import / export with SAN notation, and ECO opening recognition for 3,810 named openings (longest-prefix matching over the UCI move sequence).
  • Complete rule handling — castling, en passant, promotion (a four-way picker: queen, rook, bishop, knight — never silently defaulted), threefold repetition, the fifty-move rule and insufficient material. All of it lives in js/rules.js on top of chess.js; the UI never re-implements a rule.
  • Three modes — human vs AI (either colour), human vs human on one machine, and engine vs engine, so you can watch two levels play each other.
  • Undo takes back two plies in human-vs-AI (the AI's move and yours) and rebuilds the sidebar log to match.
  • No main-thread jank — the engine runs in a dedicated Web Worker.

3. Local Development

The project ships a static development server, server.py, bound to port 6325 by default:

python3 server.py
# then open http://127.0.0.1:6325/

Unlike 02.chess's server, this one does not inject COOP/COEP headers — the single-threaded engine does not need them. It only does the two things that are genuinely necessary:

  1. serves *.wasm with the correct application/wasm MIME type, so the browser does not fall back to the slow path;
  2. sends long-lived cache headers for .wasm / .nnue / font files and no-store for everything else, so edits show up on refresh.

Any static server works, e.g. python3 -m http.server 8000.

Rebuilding the opening book

node scripts/build_openings.mjs
# reads third-party/chess-openings/{a..e}.tsv, writes data/openings.json (3,810 openings)

4. Deployment (GitHub Pages)

The site is 100 % static — no build step, no dependency install, no server. Push the repository and point GitHub Pages at the branch root:

git init
git add -A
git commit -m "Chess Master: Stockfish 18 WASM, six difficulty levels, pure frontend"
git branch -M main
git remote add origin git@github.com:<user>/chess-master.git
git push -u origin main

Then Settings → Pages → Source: Deploy from a branch → main / / (root).

Two details that matter:

  • .nojekyll is committed at the repo root. It stops GitHub Pages from running Jekyll over the site, which would otherwise slow the build down and silently skip files it treats as special.
  • No coi-serviceworker.js is needed. The engine is single-threaded, so the Cross-Origin-Opener-Policy / Cross-Origin-Embedder-Policy headers GitHub Pages cannot set are simply not required. (This is the one place where this project is meaningfully simpler than 02.chess.)
  • All asset paths are resolved at runtime — js/engines/bridge.js derives the engine URL from its own script URL and js/openings.js derives the data URL from import.meta.url — so the site works correctly both at a domain root and under a project sub-path such as /chess-master/.

5. Project Layout

index.html                    page shell; loads difficulty.js + bridge.js as classic
                              scripts, then app.js as an ES module
js/
  difficulty.js               single source of truth for difficulty (classic script → global)
  rules.js                    chess.js wrapper — the only place rules are implemented
  openings.js                 ECO opening recognition (data/openings.json)
  analysis.js                 review analysis + SVG evaluation curve
  app.js                      the single orchestrator: game state, engine scheduling, PGN, review
  ui/
    board.js                  chessground integration + promotion picker
    panel.js                  sidebar rendering
    menu.js                   menu pages, dialogs, toasts
  engines/
    bridge.js                 engine facade: Worker lifecycle + UCI protocol + request pairing
    stockfish/                Stockfish 18 lite single-threaded WASM (unmodified)
  vendor/
    chessjs/                  chess.js 1.4.0 (unmodified)
    chessground/              chessground 9.2.1 + brown/cburnett styles (unmodified)
data/openings.json            compiled opening book
third-party/chess-openings/   CC0 source TSVs for the book
styles/                       design tokens → base → components → main
scripts/build_openings.mjs    TSV → JSON compiler
specs/                        prd.md, ui.md, project_tree.md
server.py                     local static dev server (port 6325)
NOTICE.md                     third-party components and licenses
LICENSE                       GNU GPL v3.0

6. License

GNU General Public License v3.0 — see LICENSE.

The project is GPL-3.0 rather than something more permissive because its core engine, Stockfish, is GPL-3.0 and is compiled to WebAssembly and distributed with the site; the GPL's copyleft propagates to the whole distributed artifact.

Third-party components and their licenses are listed in NOTICE.md. In short:

Component License Modified?
Stockfish 18 lite single-threaded WASM (stockfish@18.0.8) GPL-3.0 no — byte-identical to the npm package
chess.js 1.4.0 BSD-2-Clause no
chessground 9.2.1 GPL-3.0-or-later no
cburnett piece set / brown board (inside chessground's CSS) CC BY-SA 3.0 no
lichess-org/chess-openings CC0-1.0 compiled to JSON

If you redistribute this project, keep these notices and the corresponding license texts.

About

国际象棋 AI 网页版 · Stockfish 18(WASM/NNUE)全部跑在浏览器本地 · 六档难度 · 复盘分析与评估曲线 · PGN 导入导出 · ECO 开局识别(3810 条)· 纯前端零后端,静态托管即可部署

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages