Download Game! Currently 105 players. Last logged in:AnakinLouenBylethProcyon

Blitzer's Blog >> 72912

Back to blogs index
Posted: 03 Oct 2026 17:29 [ permalink ]
Here are the three files to run your engine natively in the browser.

Because engine.mjs is completely isomorphic, the browser will import the exact
same file to analyze and compile the AST on the main thread, and the Web
Worker will import it to execute the bytecode.

1. web_worker.mjs (The Browser Adapter)

The browser's Web Worker API uses self.onmessage and self.postMessage instead
of Node's worker_threads. This thin adapter bridges those browser APIs to your
pure engine.

/ web_worker.mjs import { executeVMWorker, StateBuffer } from './engine.mjs';
self.onmessage = (event) => { const { bytecode, sab, gasLimit } = event.data;
const stateBuffer = new StateBuffer(sab); executeVMWorker( bytecode,
stateBuffer, gasLimit, (msg) => self.postMessage(msg) // Callback to send rich
objects back ); }; 

2. index.html (The Main Thread & UI)

This file acts exactly like main.mjs did for Node. It imports the compiler,
allocates the SharedArrayBuffer, spawns the web_worker.mjs thread, and polls
the memory asynchronously using setInterval.

<!-- index.html --> <!DOCTYPE html> <html lang="en"> <head> <meta
charset="UTF-8"> <title>HAUKI-X86 Browser Engine</title> <style> body {
background: #121212; color: #00ff00; font-family: monospace; padding: 2rem; }
pre { background: #000; padding: 1rem; border: 1px solid #333; overflow-x:
auto; } </style> </head> <body> <h2>HAUKI-X86: Isomorphic Edge/Browser VM</h2>
<pre id="terminal">Initializing...</pre> <script type="module"> import {
analyze, compile, StateBuffer } from './engine.mjs'; const log = (msg) => {
document.getElementById('terminal').innerText += '
' + msg; }; const payload = [ "let", [ ["my_struct", ["record", "status",
["str", "processing"], "data", ["array"]]] ], ["do", ["try", ["for", "i", 1,
10, ["do", ["set-idx", "my_struct", "data", ["cons", "i", ["get", "my_struct",
"data"]]], ["if", ["=", "i", 5], ["throw", ["str", "Browser Memory Crash!"]],
null ] ] ], "err", ["set-idx", "my_struct", "status", "err"] ], "my_struct" ]
]; try { log("Analyzing AST statically in browser..."); analyze(payload);
log("Compiling to Bytecode..."); const bytecode = compile(payload); //
Allocate the Lock-Free buffer in browser memory const sab = new
SharedArrayBuffer(16); const stateBuffer = new StateBuffer(sab); // Spawn the
Web Worker as an ES Module const worker = new Worker('./web_worker.mjs', {
type: 'module' }); let finalData = null; worker.onmessage = (event) => {
finalData = event.data; }; log("Dispatching to Web Worker. Polling
SharedArrayBuffer..."); worker.postMessage({ bytecode, sab, gasLimit: 50000
}); const startTime = performance.now(); // Main browser thread polls the
buffer without blocking the UI const pollInterval = setInterval(() => { const
state = stateBuffer.readState(); if (state.status >= 2 || performance.now() -
startTime >= 1000) { clearInterval(pollInterval); worker.terminate(); log(`
Execution ended cleanly. Status: ${state.status}`); log(`Final Output:
${JSON.stringify(finalData, null, 2)}`); } }, 10); } catch (err) {
log(`CRITICAL FAULT: ${err.message}`); } </script> </body> </html> 


3. serve.mjs (The Security Gateway)

Because of the Spectre CPU vulnerabilities (2018), modern browsers like Chrome
and Firefox will block SharedArrayBuffer entirely unless the web server
explicitly flags the page as strictly isolated from other tabs.

You cannot just open index.html from your file system. You need a tiny local
server to append the magic HTTP headers (COOP and COEP).

/ serve.mjs import http from 'http'; import fs from 'fs'; import path from
'path'; const server = http.createServer((req, res) => { const filePath =
req.url === '/' ? './index.html' : `.${req.url}`; const ext =
path.extname(filePath); const mimeTypes = { '.html': 'text/html', '.mjs':
'text/javascript' // Browsers require .mjs to be served as javascript }; //
SPECTRE MITIGATION: Required for SharedArrayBuffer to exist in the browser!
res.setHeader('Cross-Origin-Opener-Policy', 'same-origin'); res.setHeader('Cros
s-Origin-Embedder-Policy', 'require-corp'); fs.readFile(filePath, (err,
content) => { if (err) { res.writeHead(404); res.end('Not found'); } else {
res.writeHead(200, { 'Content-Type': mimeTypes[ext] || 'text/plain' });
res.end(content, 'utf-8'); } }); }); server.listen(8080, '0.0.0.0', () => {
console.log('Browser VM serving at http://localhost:8080'); console.log('Ensure
 you visit via http://localhost:8080 (or your Pi\'s IP) to satisfy SAB
security policies.'); }); 

Running the Browser Engine

Run node serve.mjs.

Open http://localhost:8080 in your web browser.

Watch the UI parse, compile, dispatch, and catch the VM exception directly
inside your browser.