Solve Anubis proof-of-work challenges natively in the browser with WebGPU, falling back to an optimized pure-JS solver on CPU.
This project was inspired by anubis_offload and is a native recreation of the same idea. Big thanks to David Buchanan for the original work. Note that anubis_offload is still perfect if you want to offload the solve to another device on your network, e.g. from a slow laptop to a fast workstation with a GPU.
Install anubis_webgpu.user.js with Tampermonkey/Violentmonkey. That's it.
Sustained hash rates (2 s at difficulty 16, so a solution is never found):
| Backend | Firefox 156.0a1 (20260816214904) | Chromium/Chrome/Electron/Edge/... |
|---|---|---|
| WebGPU | 3255.7 MH/s | 5547.2 MH/s |
| CPU x1 (JS) | 4.6 MH/s | 5.8 MH/s |
| CPU x32 (JS) | 62.1 MH/s | 82.1 MH/s |
| stock Anubis page | ~0.5 MH/s | ~0.6 MH/s |
Measured on a Ryzen 7950X and a Radeon RX 9070 XT. Your numbers will differ, the ratios are what matter.
Notes:
fastPoll solve option submits empty command buffers while a readback is pending, cutting the wait to a few ms. It is off by default, no need to burn CPU, and ~100 ms is fine for solving a challenge; to turn it on, set FAST_POLL = true in src/hook.ts and rebuild. It will improve once the mentioned bug is fixed.Anubis computes PoW in Web Workers, hashing data + nonce with WebCrypto per attempt. The userscript hooks Worker.postMessage, intercepts messages that look like Anubis PoW challenges, and solves them itself:
On any failure the original messages are replayed to the page's own workers, so stock Anubis still works.
Progress is reported the same way the stock worker does (numeric messages), so the Anubis progress bar keeps working. The stock "Speed:" readout only refreshes after one second, longer than a solve takes, so the hook updates its text in place with the real rate. On pages without it, a small corner badge is shown instead.
Both solvers re-hash only the final SHA-256 block per attempt (midstate trick) and use the octal-nonce encoding from anubis_offload: the nonce string is "1" + 18 octal digits of the counter, built branchlessly, and happens to be a valid decimal integer. Every result is verified against a full JS SHA-256 before being handed to the page.
The CPU solver (src/cpu.ts) additionally memoizes everything the fast-moving nonce digits can't reach: compression rounds before the first dynamic message word are computed once per carry event instead of per hash, and the Davies-Meyer feed-forward is elided down to the two output words the difficulty mask tests. The per-hash rounds are an unrolled kernel (emitted by src/kernelgen.ts) with the message schedule fused into 16 locals, shaped as a 16-round loop - the best single compromise between V8 and SpiderMonkey, which disagree about how much unrolling they like.
TypeScript sources live in src/; anubis_webgpu.user.js is a committed build artifact for convenience.
npm install
npm run build
npm test # correctness vs node:crypto
npm run check # tsc --noEmit
WebGPU needs Chrome 113+ or Firefox 141+ and a secure context (https).
2 commits
TypeScript
51.1%
JavaScript
48.9%
Solve Anubis proof-of-work challenges natively in the browser with WebGPU, falling back to an optimized pure-JS solver on CPU.
This project was inspired by anubis_offload and is a native recreation of the same idea. Big thanks to David Buchanan for the original work. Note that anubis_offload is still perfect if you want to offload the solve to another device on your network, e.g. from a slow laptop to a fast workstation with a GPU.
Install anubis_webgpu.user.js with Tampermonkey/Violentmonkey. That's it.
Sustained hash rates (2 s at difficulty 16, so a solution is never found):
| Backend | Firefox 156.0a1 (20260816214904) | Chromium/Chrome/Electron/Edge/... |
|---|---|---|
| WebGPU | 3255.7 MH/s | 5547.2 MH/s |
| CPU x1 (JS) | 4.6 MH/s | 5.8 MH/s |
| CPU x32 (JS) | 62.1 MH/s | 82.1 MH/s |
| stock Anubis page | ~0.5 MH/s | ~0.6 MH/s |
Measured on a Ryzen 7950X and a Radeon RX 9070 XT. Your numbers will differ, the ratios are what matter.
Notes:
fastPoll solve option submits empty command buffers while a readback is pending, cutting the wait to a few ms. It is off by default, no need to burn CPU, and ~100 ms is fine for solving a challenge; to turn it on, set FAST_POLL = true in src/hook.ts and rebuild. It will improve once the mentioned bug is fixed.Anubis computes PoW in Web Workers, hashing data + nonce with WebCrypto per attempt. The userscript hooks Worker.postMessage, intercepts messages that look like Anubis PoW challenges, and solves them itself:
On any failure the original messages are replayed to the page's own workers, so stock Anubis still works.
Progress is reported the same way the stock worker does (numeric messages), so the Anubis progress bar keeps working. The stock "Speed:" readout only refreshes after one second, longer than a solve takes, so the hook updates its text in place with the real rate. On pages without it, a small corner badge is shown instead.
Both solvers re-hash only the final SHA-256 block per attempt (midstate trick) and use the octal-nonce encoding from anubis_offload: the nonce string is "1" + 18 octal digits of the counter, built branchlessly, and happens to be a valid decimal integer. Every result is verified against a full JS SHA-256 before being handed to the page.
The CPU solver (src/cpu.ts) additionally memoizes everything the fast-moving nonce digits can't reach: compression rounds before the first dynamic message word are computed once per carry event instead of per hash, and the Davies-Meyer feed-forward is elided down to the two output words the difficulty mask tests. The per-hash rounds are an unrolled kernel (emitted by src/kernelgen.ts) with the message schedule fused into 16 locals, shaped as a 16-round loop - the best single compromise between V8 and SpiderMonkey, which disagree about how much unrolling they like.
TypeScript sources live in src/; anubis_webgpu.user.js is a committed build artifact for convenience.
npm install
npm run build
npm test # correctness vs node:crypto
npm run check # tsc --noEmit
WebGPU needs Chrome 113+ or Firefox 141+ and a secure context (https).
2 commits
TypeScript
51.1%
JavaScript
48.9%