WebAssembly in the Browser#
Compile Jac's native (na) subset to WebAssembly and run it in the browser at native speed -- with Jac's own wasm linker, no emscripten, no wasm-ld, no toolchain to install.
What you'll do:
- Compile a
.na.jacmodule to a.wasmbinary with one command - Load and call it from JavaScript
- See how
nablocks integrate into aweb-staticapp, where the compiler does steps 1-2 for you
Time: ~15 minutes
1. Write a native module#
The na codespace is the statically-compiled subset of Jac (native pathway reference). Create sum.na.jac:
2. Compile to WebAssembly#
Wasm module written to sum.wasm (541 bytes).
Instantiate in a browser/Node with an `env` import object.
That's a complete, standards-compliant wasm module (MVP profile) in half a kilobyte. Jac assembled and linked it itself -- the same jac binary that compiles to Python bytecode carries an LLVM backend and its own wasm linker.
3. Call it from JavaScript#
Every :pub function becomes a wasm export. Load it the standard way:
<script type="module">
const { instance } = await WebAssembly.instantiateStreaming(
fetch("sum.wasm"),
{ env: {} } // runtime imports (I/O like print lands here)
);
instance.exports.__jac_glob_init(); // initialize module globals once
console.log(instance.exports.add(2n, 3n)); // 5n
</script>
Two things to know, both visible in that snippet:
- Jac
intis 64-bit, so integer parameters and returns cross the boundary as JavaScriptBigInt-- calladd(2n, 3n), notadd(2, 3).floatcrosses as a plainnumber. - Call
__jac_glob_init()once after instantiation to initialize module globals. If your module does I/O (e.g.print), supply the corresponding functions on theenvimport object; a pure-computation module needs nothing.
4. The integrated path: na blocks in a web app#
Hand-loading wasm is the mechanics; in a real app you don't do any of it. In a web-static project, an na block in your app module is compiled to wasm and wired to your client code by the build:
Put hot-path code in an na block and UI in cl; jac start (or jac build --client web) compiles cl → JavaScript and na → wasm into .jac/client/dist/, and serves them together.
For a full worked example of the pattern -- a game loop running as wasm, rendered through a JavaScript shim -- see the raylib shooter in the repo (jac/examples/raylib_shooter/web): main.jac holds the na game and the cl page, and raylib_shim.cl.jac supplies the wasm module's imports as WebGL/DOM functions.
Where to go next#
- Native pathway reference -- the
nasubset, targets, optimization levels, shared libraries jac guide jac-native-wasm-- the bundled quick reference (also available to AI agents)- Build a Chess Engine -- the native pathway compiled to a host binary instead
- Full-stack web apps -- where in-browser native fits the bigger picture