YaneuraOu shogi engine — WebAssembly packages and native Windows binaries.
What's new in this release: the engine base moves to YaneuraOu V9.60 (upstream), native Windows
binaries are published alongside the WASM packages for the first time, and
the set is trimmed to the variants that are actually useful for play and
analysis — NNUE (KP256 / HalfKP128 / HalfKP256 / HalfKP768), the SFNN NAGISA_V3
network, and the DfPn mate solver. That means fourteen WASM packages across
browser, Node.js and Cloudflare Workers (where the eval fits), plus six Windows
tarballs covering every 64-bit CPU target. The Material / Mobility / HumanLike
variants are no longer published; their sources remain in the repository. The
loader API is unchanged from 8.50.0 apart from the new progressBin option
described below.
-
USI_Hashnow defaults to 16 MB on WASM builds (native stays at 1024 MB).
The transposition table is sized from this default while theThreadsoption
is registered — before anysetoptioncan reach the engine — and 1024 MB
exceedsMAXIMUM_MEMORYfor every WASM package, so the engine called
exit(EXIT_FAILURE)during startup. Builds published between the V9.60 merge
and this fix could not start at all; the builds themselves succeeded, so CI
stayed green. If you want a larger table — in particular for the DfPnmate-*
packages, which search deeper the more hash they get — raise it explicitly with
setoption name USI_Hash value 256. -
New: HalfKP_128x2_32_32 (
pthread-halfkp128,node-halfkp128). Half the
first layer of the standard HalfKP256 network, so the eval is ~31 MB instead of
~62 MB. There is no cfworkers variant — loading the eval overruns the 128 MB
Workers heap. -
New: the NAGISA_V3 network is supported (
pthread-halfka1024,node-halfka1024).
This is an SFNN HalfKA_hm2 1024x2-15-64 net with 9 layer stacks. Unlike every
other supported network it keeps its layer-stack selection coefficients in a
separateprogress.binrather than insidenn.bin, so both files must be
supplied: the loaders take a newprogressBinoption alongsideevalBin, and
the engine reads the coefficients through theLS_PROGRESS_COEFFUSI option
(defaulting toprogress.binunderEvalDir). -
LS_BUCKET_MODEselects the layer-stack bucket rule, defaulting to
progress8kpabsto match NAGISA_V3's own default — progress alone picks
buckets 0–7 and the 9th layer stack is never used. Set it toprogress8ekto
route mutual-entering-king positions to that 9th stack instead. The two rules
differ only in entering-king positions, and both produce identical node counts
and moves on native and WASM.This distinction matters because both rules pass the eval file's hash check.
Picking the wrong one loads without any error or warning and quietly selects
different weights, so a mismatch is only visible by comparing search output. -
FV_SCALEnow defaults to 28 on SFNN builds (16 elsewhere). Together with
the two options above this matches theeval_options.txtthat NAGISA_V3 ships
next to its weights, so the network runs correctly out of the box. There is no
halfka1024-cfworkersvariant: the 75 MB eval does not fit the 128 MB Workers heap. -
Heap ceiling raised to 4 GB on every pthread and Node package (the wasm32
maximum), up from 1–2 GB.MAXIMUM_MEMORYis an address-space reservation, not
an upfront allocation, so this costs nothing until the memory is actually used. -
USI_Hash's ceiling is now derived from the heap rather than hard-coded to
2048 MB on every WASM build. It is half the heap ceiling: 2048 MB on the
pthread/Node packages, 64 MB on the cfworkers ones. Previously a cfworkers
build would happily acceptUSI_Hash 2048and then die allocating it; now the
option range only offers values the build can actually satisfy. Native builds
are unaffected. -
Fixed: NNUE evaluation was silently wrong on plain-NEON builds — the
published macOS (APPLEM1) and Android (arm64-v8a) binaries. The
sparse-input layer loaded its weights in the scrambled layout whenever
USE_NEON >= 8, but only reads that layout back whenUSE_NEON_DOTPRODis
set; without dotprod it falls back to the dense kernel, so the load and the
compute disagreed. The plain-NEON kernels also treated theuint8input as
signed, wrapping any value of 128 or above negative. Neither failed loudly:
the engine started, searched, and returned plausible-looking scores.
On aarch64 at depth 16 the NEON build reported 12,017,078 nodes / cp 87 where
scalar and WASM both give 743,902 / cp 94. All four networks now agree node
for node across scalar, NEON and WASM. WASM builds were never affected. -
WebAssembly support restored on the V9.6x engine model: the NNUE weights stay
in the dense layout (upstream's scrambled/sparse layout is AVX-only), and the
explicit affine layers stay dense under WASM too. -
Edge variants build with pthreads fully off rather than
pthread-capable-but-single-threaded. -
Fork engine options carried over:
FullTimeMode(stop shrinking the think time
on stable positions), hidden USI options settable only viasetoption, and a
tolerant NNUE header-version check. Seedocs/fork_engine_options.md. -
Node variants remain pinned to emscripten 3.1.43 — the only toolchain
verified to drive a pthread-backed YaneuraOu cleanly under Node (newer
emscriptens stall, seedocs/wasm_client_usage.mdfor the matrix). The rest
build on 5.0.5.
Each .tar.gz contains the package's package.json (version bumped to this release), README.md, SPEC.md, the TypeScript loader (dist/index.js, dist/index.d.ts), and the engine bundle (dist/yaneuraou.js, dist/yaneuraou.wasm, .wasm.br). Node variants additionally ship dist/yaneuraou.worker.js (the emscripten 3.1.43 classic pthread worker) and dist/worker_shim.{js,d.ts} (the worker_threads-side glue the loader uses to bridge .worker.js into a fake web-worker scope).
Packages
Browser / Cloudflare Workers
| Package | Engine | Eval data | Threading | Initial / Max memory |
|---|---|---|---|---|
@ultemica/yaneuraou-wasm-kp256-cfworkers |
NNUE KP256 | external (~873 KB) | single-thread | 64 MB / 128 MB |
@ultemica/yaneuraou-wasm-mate-cfworkers |
Mate (DfPn) | none | single-thread | 64 MB / 128 MB |
@ultemica/yaneuraou-wasm-pthread-kp256 |
NNUE KP256 | external (~873 KB) | pthread | 128 MB / 4 GB |
@ultemica/yaneuraou-wasm-mate-pthread |
Mate (DfPn) | none | pthread | 128 MB / 4 GB |
@ultemica/yaneuraou-wasm-pthread-halfkp128 |
NNUE HalfKP_128x2_32_32 | external (~31 MB) | pthread | 256 MB / 4 GB |
@ultemica/yaneuraou-wasm-pthread-halfkp256 |
NNUE HalfKP_256x2_32_32 | external (~62 MB) | pthread | 256 MB / 4 GB |
@ultemica/yaneuraou-wasm-pthread-halfkp768 |
NNUE HalfKP_768x2_16_64 | external (~184 MB) | pthread | 256 MB / 4 GB |
@ultemica/yaneuraou-wasm-pthread-halfka1024 |
SFNN HalfKA_hm2 1024x2-15-64 | external (~75 MB + progress.bin) | pthread | 256 MB / 4 GB |
The
pthread-*packages run in the browser and needSharedArrayBuffer, so the page must be served cross-origin isolated (Cross-Origin-Opener-Policy: same-origin+Cross-Origin-Embedder-Policy: require-corp). Where you cannot set those headers, use a*-cfworkerspackage.
Cloudflare Workers cannot host pthread builds — use the cfworkers variant there. HalfKP cfworkers variant does not exist (eval exceeds Workers memory budget).
Node.js variants
Six packages built with EM_ENVIRONMENT=node, EM_PTHREAD=1, and EM_EXPORTED_RUNTIME_METHODS=['FS','ccall','callMain']. Pinned to emscripten 3.1.43 — the only toolchain currently verified to drive a multi-threaded YaneuraOu correctly under Node (3.1.44–3.1.73 stall on ESM workers, 3.1.74+ stall on INCOMING_MODULE_JS_API; both unresolved). See docs/wasm_client_usage.md for the full compatibility matrix.
| Package | Engine | Eval data | Threading | Initial / Max memory |
|---|---|---|---|---|
@ultemica/yaneuraou-wasm-node-kp256 |
NNUE KP256 | external (~873 KB) | pthread | 128 MB / 4 GB |
@ultemica/yaneuraou-wasm-node-mate |
Mate (DfPn) | none | pthread | 128 MB / 4 GB |
@ultemica/yaneuraou-wasm-node-halfkp128 |
NNUE HalfKP_128x2_32_32 | external (~31 MB) | pthread | 256 MB / 4 GB |
@ultemica/yaneuraou-wasm-node-halfkp256 |
NNUE HalfKP_256x2_32_32 | external (~62 MB) | pthread | 256 MB / 4 GB |
@ultemica/yaneuraou-wasm-node-halfkp768 |
NNUE HalfKP_768x2_16_64 | external (~184 MB) | pthread | 256 MB / 4 GB |
@ultemica/yaneuraou-wasm-node-halfka1024 |
SFNN HalfKA_hm2 1024x2-15-64 | external (~75 MB + progress.bin) | pthread | 256 MB / 4 GB |
Quickstart (Node 18+):
import fs from "node:fs/promises";
import { createRequire } from "node:module";
import { createEngine } from "@ultemica/yaneuraou-wasm-node-kp256";
import YaneuraOuFactory from "@ultemica/yaneuraou-wasm-node-kp256/engine";
const require = createRequire(import.meta.url);
const wasmPath = require.resolve("@ultemica/yaneuraou-wasm-node-kp256/wasm");
const enginePath = require.resolve("@ultemica/yaneuraou-wasm-node-kp256/engine");
const wasmBinary = await fs.readFile(wasmPath);
const evalBin = await fs.readFile("./eval/nn.bin"); // suishopetite for KP256
const engine = await createEngine({
factory: YaneuraOuFactory,
enginePath,
wasmBinary,
evalBin,
threads: 4,
usiHash: 64,
});
const result = await engine.eval({
sfen: "lnsgkgsnl/1r5b1/ppppppppp/9/9/9/PPPPPPPPP/1B5R1/LNSGKGSNL b - 1",
byoyomi: 2_000,
});
console.log(result.bestmove, result.score); // -> "7g7f", { kind: "cp", value: ... }
engine.dispose();Requires Node.js 18 or later. Browsers cannot load these binaries (built with
EM_ENVIRONMENT=node, notweb,worker); use thepthread-*packages there. Cloudflare Workers / V8 Isolate environments can't host pthread builds at all; use the*-cfworkerspackages there.
Windows (native)
Also attached: six yaneuraou-windows-*.tar.gz files, one per
network, built with MinGW for 64-bit Windows. Each holds that engine compiled
for every CPU target — AVX512VNNI, AVX512, AVX2, SSE42, ZEN2, ZEN3 — so download the network you want and pick the
.exe matching your machine.
Which CPU build: AVX2 covers any Haswell-or-later Intel and most AMD. Use
SSE42 on older hardware, ZEN2 / ZEN3 on Ryzen, and the AVX512* builds
only where AVX-512 is genuinely present — they will not start otherwise.
The network names match the WASM packages above (halfkp256 is the standard
Suisho5-compatible network), and they take the same eval files; see below.
Performance — how close to native?
WASM is not the slow option. Measured on one machine with the same eval and the
same search parameters, WASM reaches about 92% of a native SIMD build.
| Build | nps | vs native NEON |
|---|---|---|
| native clang-14 (NEON) | 764k | 102% |
| native gcc (NEON) | 748k | 100% |
| wasm32 pthread | 691k | 92% |
| wasm32 (no SIMD) | 585k | 78% |
| native clang-14 (scalar) | 320k | 43% |
| native gcc (scalar) | 203k | 27% |
Threads=1 / go nodes 1000000 / KP256 + suishopetite / median of 3 runs.
Absolute numbers are machine-dependent — only the ratios carry over.
- WASM SIMD is doing real work. Turning it off drops 691k to 585k (-15%).
WASM still beats both native scalar builds by more than 2x. - Threads scale. On Node, 1 to 4 threads goes 686k to 2,916k (4.25x) —
superlinear thanks to the shared transposition table. - Browser and Node are within noise. 99% at 1 thread, 92% at 4 threads;
SharedArrayBuffersynchronisation costs the browser a little.
Search results are bit-identical across every WASM variant (32/64-bit,
SIMD on/off, browser/Node/edge) down to the node count, so switching runtimes
does not change what the engine plays.
Full conditions and raw data: docs/reports/2026-07-28_wasm_v96x_performance.md.
Required: NNUE eval file (NNUE builds only)
The WASM bundles do not embed an eval function. Fetch nn.bin separately, write it into MEMFS with FS.writeFile, and point the EvalDir USI option at it.
| Eval | Engine | Size | URL |
|---|---|---|---|
| suisho5 nn.bin | HalfKP_256x2_32_32 | ~62 MB | https://github.com/mizar/YaneuraOu/releases/download/resource/suisho5_20211123.halfkp.nnue.cpp.gz |
| AobaNNUE nn.bin | HalfKP_768x2_16_64 | ~184 MB | https://github.com/yssaya/AobaNNUE/releases |
| suishopetite nn.bin | KP256 | ~873 KB | https://github.com/mizar/YaneuraOu/releases/download/resource/suishopetite_20211123.k_p.nnue.cpp.gz |
| NAGISA_V3 nn.bin + progress.bin | SFNN HalfKA_hm2 1024x2-15-64 | ~75 MB | https://github.com/keinoda/YaneuraOu/releases/tag/nagisa-v3.1 |
suisho5 / suishopetite ship as
.cpp.gzin embedded C++ array form — convert them back to.binwith the inverse ofscript/eval_bin_to_cpp_literal.py. AobaNNUE is distributed as a plainnn.bin.
NAGISA_V3 has no standalone eval download —
eval/nn.binandeval/progress.binare bundled inside the platform archives on that release page (any of them will do; the eval is identical across all three). It is the only supported network that needs two files: passprogress.binvia the loader'sprogressBinoption, or place it next tonn.binunderEvalDir. Without it the layer-stack bucket cannot be computed.FV_SCALEalready defaults to 28 on these builds (16 elsewhere), matching theeval_options.txtshipped alongside the network — no manual setting needed.
KP256, HalfKP_256x2_32_32 and HalfKP_768x2_16_64 evals are mutually incompatible. The Mate engine needs no eval. The HalfKP eval (62 MB) OOMs the 128 MB cfworkers heap, so it only works on the pthread variants.
Optional: opening book
Load a book through the BookDir / BookFile USI options.
| Book | Positions | Size | URL |
|---|---|---|---|
| 100T-shock | ~40,000 | 4.7 MB | https://github.com/yaneurao/YaneuraOu/releases/download/BOOK-100T-Shock/100T-shock-book.zip |
| 700T-shock | ~400,000 | 32 MB | https://github.com/yaneurao/YaneuraOu/releases/download/BOOK-700T-Shock/700T-shock-book.zip |
| New petabook 2.33M | ~2,330,000 | 76 MB (.7z) | https://github.com/yaneurao/YaneuraOu/releases/download/new_petabook233/new_petabook_20250505c.7z |
700T-shock (32 MB) OOMs the 128 MB cfworkers heap — on cfworkers, stick to 100T-shock. The petabook strains the heap even on pthread variants.