WebAssembly port of the strong chess engine Stockfish. See it in action on https://lichess.org/analysis.
This port is branched from SF_classical
.
See https://github.com/hi-ogawa/Stockfish for a WebAssembly port with NNUE support.
Uses the latest WebAssembly threading proposal. Requires these HTTP headers on the top level response:
Cross-Origin-Embedder-Policy: require-corp
Cross-Origin-Opener-Policy: same-origin
And the following header on the included files:
Cross-Origin-Embedder-Policy: require-corp
If headers are not configured correctly on the top level response,
wasmThreadsSupported()
as defined below will return false
. If headers are
not configured correctly on the included files, something like
pthread sent an error! undefined:undefined: undefined
may be logged to the
console.
- Since Chromium 79: Full support.
- Chromium 74: Supports treading, but cannot allocate additional memory. The default allocation suffices for up to 2 threads and 16 MB hash.
- Chromium 70 to 73: Needs flag
chrome://flags/#enable-webassembly-threads
or Origin Trial.
- Since Firefox 79: Full support.
- Firefox 72 to 78: Structured cloning can no longer be enabled with flags, except on nightlies.
- Firefox 71: Requires
javascript.options.shared_memory
anddom.postMessage.sharedArrayBuffer.withCOOP_COEP
to be enabled inabout:flags
. - Firefox 68 to 70: Requires
javascript.options.shared_memory
to be enabled inabout:flags
No support.
See stockfish.js for a more portable but single-threaded version.
function wasmThreadsSupported() {
// WebAssembly 1.0
const source = Uint8Array.of(0x0, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00);
if (
typeof WebAssembly !== "object" ||
typeof WebAssembly.validate !== "function"
)
return false;
if (!WebAssembly.validate(source)) return false;
// SharedArrayBuffer
if (typeof SharedArrayBuffer !== "function") return false;
// Atomics
if (typeof Atomics !== "object") return false;
// Shared memory
const mem = new WebAssembly.Memory({ shared: true, initial: 8, maximum: 16 });
if (!(mem.buffer instanceof SharedArrayBuffer)) return false;
// Structured cloning
try {
// You have to make sure nobody cares about these messages!
window.postMessage(mem, "*");
} catch (e) {
return false;
}
// Growable shared memory (optional)
try {
mem.grow(8);
} catch (e) {
return false;
}
return true;
}
- Hashtable: 1024 MB. You may want to check
navigator.deviceMemory
before allocating. - Threads: 32. You may want to check
navigator.hardwareConcurrency
. May be capped lower (e.g.,dom.workers.maxPerDomain
in Firefox). - Can hang when UCI protocol is misused. (Do not send invalid commands or positions. While the engine is searching, do not change options or start additional searches).
- No NNUE support.
- No Syzygy tablebase support.
Assuming em++ (^2.0.13
) is available:
npm run-script prepare
Requires stockfish.js
, stockfish.wasm
and stockfish.worker.js
(total size ~400K, ~150K gzipped) to be served from the same directory.
<script src="stockfish.js"></script>
<script>
Stockfish().then((sf) => {
sf.addMessageListener((line) => {
console.log(line);
});
sf.postMessage("uci");
});
</script>
Or from recent node (v14.4.0 tested) with flags
--experimental-wasm-threads --experimental-wasm-bulk-memory
:
const Stockfish = require("stockfish.wasm"); // the module, not the file
Stockfish().then((sf) => {
// ...
});
Thanks to the Stockfish team for sharing the engine under the GPL3.