Start with a function you already have.
Wrap it in a jz template and it comes back as a WebAssembly module with that function exported. No annotations, no second language.
import jz from 'jz'
const { exports } = jz`
export const dist = (x, y) =>
(x*x + y*y) ** 0.5
`
console.log(exports.dist(3, 4)) // 5
The tag compiles and instantiates once, so keep the module around: every call to exports.dist from here runs WebAssembly. Notice what the source never said – that x and y are numbers. JZ read that off the arithmetic.
npm install jz
Or open the playground first, with nothing to install.
Give it a whole loop
A two-line function shows the shape of it. Loops are where it earns its keep: crossing into WASM costs something each time, so hand it a whole array rather than one number.
import jz from 'jz'
const { exports, memory } = jz`
export const sum = a => {
let total = 0
for (const x of a) total += x
return total
}
`
const values = new Float64Array([1, 2, 3])
console.log(exports.sum(values)) // 6
memory.reset()
Arrays, strings and objects cross the boundary for you. What they allocate stays on the heap until you say otherwise, so finish the batch, then call memory.reset(): it invalidates every pointer and view handed out before it. If state has to survive between calls, read the memory contract first.
Your fallback is the same file
Already have a numeric kernel? Leave it where it is. Export its functions and compile the file in your build:
npx jz kernel.js -o kernel.wasm
Ship the .wasm and load it with instantiate from jz/interop; the compiler stays in your build. The .js you compiled from is still there, still running as JavaScript. API and CLI reference →
Start with one kernel and run your existing tests against both versions. JZ is experimental and covers a subset of JavaScript, so measure the whole call, transfer included, before you trust a speedup.