How Browser Mining Works: Workers, WASM & Stratum
Four layers stand between a visitor's tab and a mining pool's ledger. Here's how each one works — and why the browser is a surprisingly viable place to hash.
The Architecture at a Glance
Browser mining isn't a single technology. It's a stack of four browser APIs working in concert: Web Workers for parallel threading, WebAssembly for near-native hashing speed, WebSocket for persistent pool connections, and the Stratum protocol for work assignment and share submission. Each layer solves a specific problem that would otherwise make browser-based compute impossible.
Here's the simplified code flow from page load to share submission:
Layer 1: Web Workers — Parallel Threading
The browser's main thread owns the DOM. If you run CPU-intensive hashing on it, the page freezes — every click, scroll, and paint blocks until the hash loop yields. The Worker API solves this by spawning OS-level threads that run in isolated contexts with zero DOM access.
Earnify queries navigator.hardwareConcurrency and spawns n−1 workers, reserving one core for the UI thread. On an 8-core machine, that's 7 dedicated hashing threads — each running its own WASM instance, each computing nonces independently against the same job header.
Workers communicate with the main thread via postMessage. The main thread broadcasts new jobs from the Stratum connection; workers post back found shares. This message-passing boundary is the only coordination point — otherwise each worker runs autonomously.
Worker Spawn Pseudo-code
Layer 2: WASM — Compiling Native Code for the Browser
Mining algorithms like MinotaurX are written in C and Rust — not JavaScript. JavaScript's JIT compiler can optimize numeric loops, but it can't approach the throughput of ahead-of-time compiled native code. WebAssembly bridges this gap.
The build pipeline compiles the C/Rust hashing implementation into .wasm bytecode via emscripten or wasm-pack. The browser's WASM engine then compiles this bytecode to the host machine's native instruction set at load time. The result: ~70% of native execution speed — fast enough to make browser mining economically viable.
Why 70% and not 100%? The gap comes from three sources: (1) WASM lacks SIMD parity with native AVX2/SSE instructions on some browsers, (2) the message-passing overhead between workers and the main thread introduces minor latency, and (3) browser engines impose a slight compilation overhead at module instantiation. For memory-hard algorithms like MinotaurX, the 70% figure holds consistently across Chrome, Firefox, and Edge.
Each Web Worker instantiates its own WebAssembly.Module — there's no shared WASM memory across workers by design. This isolation prevents one thread's state from corrupting another's, which matters when each worker is computing nonces over different byte ranges. Memory per worker is roughly 2MB for MinotaurX — negligible on any device manufactured after 2018.
WASM Initialization Pseudo-code
Layer 3: WebSocket — Persistent Stratum Connection
Mining pools use the Stratum protocol — a JSON-RPC-over-TCP protocol designed for low-latency work distribution. Browsers can't open raw TCP sockets, but they can open WebSocket connections. Mining proxies (like stratum-proxy or pool-hosted WS endpoints) bridge the gap, exposing Stratum over a WebSocket transport.
The WebSocket connection persists for the entire mining session. Unlike HTTP polling, there's no per-request handshake overhead — the TCP connection stays open, and both the browser and pool can push messages at will. This is critical for mining because pools emit new jobs frequently (every few seconds on average), and delays between job issuance and worker receipt directly reduce hashrate efficiency.
The connection lifecycle follows four Stratum messages:
- mining.subscribe — The client registers with the pool, receiving a session ID and initial difficulty parameters.
- mining.authorize — The client authenticates with a wallet address. The pool associates all submitted shares with this identity.
- mining.notify — The pool pushes new jobs. Each job contains a block header, merkle root components, a target difficulty, and a job ID. This message arrives every time the pool needs miners to switch to a new block candidate.
- mining.submit — When a worker finds a nonce producing a hash below the target difficulty, the main thread packages the result and submits it to the pool. If the pool accepts the share, the miner's hashrate contribution is credited.
Stratum Session Pseudo-code
Layer 4: Share Submission & Pool Credit
A "share" is a hash output that meets the pool's difficulty target — not necessarily the network's full difficulty. Pools set a lower threshold to ensure miners find shares frequently (every few seconds), which gives the pool a continuous signal of each miner's hashrate. The pool then credits the miner proportionally.
When a worker's WASM hash function produces a result below the target, the main thread wraps it into a mining.submit message and sends it over the WebSocket. The pool validates the share cryptographically — it re-runs the hash with the submitted nonce and verifies the output. If valid, the share counts toward the miner's credited work.
The submission flow is fire-and-forget from the worker's perspective: as soon as a share is posted, the worker resumes hashing against the same job. There's no ACK-wait cycle that would stall the hashing loop. The pool's acceptance or rejection arrives asynchronously and is logged but doesn't interrupt mining.
Putting It All Together
The full cycle from page load to credited share looks like this:
- Page loads — main thread reads configuration (pool URL, wallet, algorithm).
- Workers spawn — n−1 Web Workers are created, each loading the WASM hashing module.
- Stratum handshake — WebSocket connects, subscribes, and authorizes with the pool.
- Job arrives — pool pushes a
mining.notifywith header bytes and target difficulty. - Workers hash — each worker runs the WASM hash in a tight loop over its nonce range at ~70% native speed.
- Share found — a worker posts the valid nonce back to the main thread via
postMessage. - Share submitted — main thread sends
mining.submitover the WebSocket. Pool validates and credits. - Repeat — workers continue hashing against the current job until a new
mining.notifyarrives.
No server required. No backend. No database. The browser is the entire mining client — the only external dependency is the pool's WebSocket endpoint, which is provided by the mining pool itself. For a deeper look at which algorithms map to this architecture and how they differ in browser performance, see the algorithm comparison breakdown.
Read the Source
Every layer described above is implemented in the open-source Earnify codebase. The Worker orchestration, WASM build pipeline, and Stratum client are all there — production-grade, MIT-licensed, and ready to fork or audit.
github.com/romannnoodesl/earnify.cc
Deploy Browser Mining in 5 Minutes
Workers, WASM, and Stratum — wired up and ready. Single script tag, open source, 10% fee.
Get Started with Earnify