| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Name | Name | Last commit date | ||
|---|---|---|---|---|
JZ (javascript zero) is a distilled JS subset that compiles to fast, minimal WASM.
| Good for | Not for |
|---|---|
| DSP, audio, synthesis | UI, DOM, frontend state |
| Images, video, pixels | Network, hot I/O, serving HTTP |
| Simulation, physics, games | Dynamic object models and monkey-patching |
| Parsers, codecs, compression | Allocation-heavy, long-lived object graphs |
| Scientific, numeric, edge ML | Security-sensitive cryptography and arbitrary-precision integers |
| Hashing, checksums, RNG | Tiny calls where the JS/WASM boundary dominates |
site / try it / examples / benchmarks / stability contract
Used by: color-space, audiojs
npm install jzimport { compile } from 'jz'
const wasm = compile('export const dist = (x, y) => (x*x + y*y) ** 0.5')
const { instance } = await WebAssembly.instantiate(wasm)
instance.exports.dist(3, 4) // 5Options are passed as jz(source, opts) or compile(source, opts):
| Option | Use |
|---|---|
| modules: { specifier: source } | Static ES imports to bundle. CLI import resolution does this from files automatically. |
| imports: { mod: host } | Host imports import { fn } from "mod". |
| memory | Pass memory: N for owned memory with N initial pages, or memory: jz.memory() / WebAssembly.Memory to share across modules. maxMemory: N caps growth; importMemory: true imports env.memory instead of exporting own. |
| host: 'js' | 'wasi' | 'native' | Runtime-service lowering. Default js; wasi for standalone runtimes; native targets the wasm2c/native lane (same module shape as js, tail calls off). |
| optimize | false/0 off, 1 minimal, true/2 default (all stable passes), 3/'speed' trades size for speed, 'size' for smallest wasm, 'fast' for fastest compile (default passes, final wat optimizer off). (Object form for per-pass overrides is internal/unstable.) |
| define | Compile-time constants injected as top-level bindings, e.g. { DEBUG: false, PORT: 8080 } (numbers, booleans, strings, null, or literal arrays/objects). |
| strict: true | Skip jzify lowering and reject dynamic fallbacks such as obj[k], for-in, and unknown receiver methods. |
| alloc: false | Omit allocator exports (_alloc/_clear) from modules that never marshal heap values. |
| noSimd: true | Disable auto-vectorization. Explicit f32x4 and i32x4 intrinsics still compile. |
| whyNotSimd: true | Report the first operation that prevented each loop from being vectorized. Warnings go to the warnings sink. |
| stencil / outerStrip / toneMap | Structure vectorizers: neighbour-load stencils (b[i] = f(a[i-1], a[i], a[i+1]), 2-D 5-point), strip-mined pixel loops over an inner reduction, and log-tonemap islands — all to f64x2, bit-exact vs scalar. On by default at optimize 2+; pass false to disable one, true to force it at lower levels. |
| noTailCall: true | Use ordinary call frames instead of return_call for engines/tools without the tail-call proposal. |
| noEhAbort: true | Lower internal throws to unreachable in genuinely catch-free modules even when source has a bare throw — drops the exceptions tag for consumers with no wasm-EH (wasm2c, w2c2). |
| sharedMemory: true | Compile against an imported SHARED memory (wasm threads): atomic heap bump; link with new WebAssembly.Memory({ initial, maximum, shared: true }). |
| nativeTimers: true | Emit a blocking timer loop in _start so setTimeout/setInterval fire under standalone runtimes with no host event loop (e.g. the wasmtime CLI). |
| warnings | Mutable sink populated with entries: [{ code, message, fn?, line?, column? }] — heap-growth advisories, simd-why-not reports. |
| randomSeed | Set a number for a reproducible Math.random sequence. The default uses host entropy; true requests entropy explicitly. |
| wat: true | compile() returns WAT text instead of WASM binary. |
| names: true | Emit a WASM name section (function symbols) for profilers/debuggers. |
| profile | Mutable sink for compile-stage timings (entries/totals per phase). |
npm install -g jz
jz program.js # → program.wasm
jz program.js --wat # → program.wat
jz program.js -o out.wasm # custom output (- for stdout)
jz program.js -O3 # optimization: -O0 off, -O1 min, -O2 default, -O3 speed, -Os size
jz program.js --host wasi # standalone WASI output
jz --strict program.js # pure canonical subset (also implied by .jz extension)
jz -e "1 + 2" # eval → 3jz. min JS → WASM compiler
Usage:
jz <file.js> Compile JS to WASM (full JS subset; .jz = strict)
jz --strict <file.js> Strict mode — pure canonical subset, no lowering
jz --jzify <file.js> Transform JS → jz source (auto-derives output file)
jz -e <expression> Evaluate expression
jz --help, -h Show this help
Examples:
jz program.js # → program.wasm
jz program.js --wat # → program.wat
jz program.js -o out.wasm # custom output name
jz program.js -o - # write to stdout
jz program.js -O3 # optimize for speed
jz program.js -Os # optimize for size
jz program.js -D DEBUG=false # inject a compile-time constant
jz program.js --memory 64 # 64 initial pages (4 MB)
jz program.js --host wasi # emit WASI Preview 1 imports
jz --strict program.js # strict mode
jz --jzify lib.js # → lib.jz
jz -e "1 + 2"
Options:
--output, -o <file> Output file (.wat, .wasm, or - for stdout)
-O<n>, --optimize <n> Optimization level: 0 off, 1 minimal, 2 default (all
stable passes), 3 speed. -Os optimizes for size,
-Ofast compiles fastest (default passes, wat optimizer off).
--define, -D <K=V> Inject a compile-time constant (VALUE parsed as JSON,
else string). Repeatable.
--host <js|wasi|native> Runtime-service lowering (default js). 'native' targets
the wasm2c/native-lowering lane (scripts/native/) —
same module shape as 'js', tail calls off (wasm2c
return_call + multi-value codegen bug)
--memory <pages> Initial memory size in 64 KiB pages
--max-memory <pages> Cap memory growth at this many pages (default unbounded)
--import-memory Import env.memory instead of exporting own memory
--no-alloc Omit _alloc/_clear allocator exports (standalone wasm)
--no-simd Disable auto-vectorization (no v128) for non-SIMD engines
--why-not-simd Report, per loop, why the auto-vectorizer declined it
--stencil Force neighbour-load stencil vectorization (a[i±1]) at
levels where it's off (on by default at -O2+)
--outer-strip Force pixel-loop strip-mining over an inner reduction to
f64x2 at levels where it's off (on by default at -O2+)
--no-tail-call Use ordinary call frames instead of return_call
--no-eh-abort Lower internal throws to unreachable even with a bare throw
in source (no wasm-exceptions tag), when no try/catch is reachable
--names Emit wasm name section for profilers/debuggers
--stats Print compile-phase timings to stderr
--strict Pure canonical subset: reject full-JS syntax + dynamic fallbacks
--jzify Transform JS to jz source (no compilation)
--eval, -e Evaluate expression or file
--wat Output WAT text instead of binary
--resolve Resolve bare specifiers via Node.js module resolution
--imports <file> JSON file with host import specs (e.g. {"env":{"fn":{"params":2}}})
--version, -v Show version number
chladni |
robot motion |
hydrogen |
See all examples.
What JS is supported?┌────────────────────────────────────────────────────────────────────────┐
│ ┌────────────────────────────────────────────────────────────────────┐ │
│ │ jz strict │ │
│ │ let/const arrows rest destructuring import/export │ │
│ │ if/else for/while/do-while/of break/continue │ │
│ │ try/catch/finally throw │ │
│ │ numbers strings booleans arrays objects template literals │ │
│ │ Math Number String Array Object JSON RegExp Symbol │ │
│ │ ArrayBuffer DataView typed arrays Map Set Atomics │ │
│ │ Float16Array base64/hex codecs TextEncoder timers Date │ │
│ │ crypto randomness URLSearchParams structuredClone Set algebra │ │
│ │ WASI file I/O │ │
│ └────────────────────────────────────────────────────────────────────┘ │
│ jz default (jzify) │
│ var function arguments switch │
│ class new this extends super private/static fields │
│ generators iterator helpers async/await Promise for await │
│ loose equality instanceof WeakMap WeakSet │
└────────────────────────────────────────────────────────────────────────┘
not supported
eval Function with Proxy Reflect
property descriptors getters/setters live prototypes
dynamic import DOM Intl Temporal Node APIsOrdinary code already carries useful type evidence: let x = 0.5, Float32Array, an array index, a loop counter. JZ infers it instead of turning the file into another language. Ambiguous values fall back to a slower dynamic path; the handful of shapes where that path is still wrong are tracked openly — as KNOWN-WRONG/KNOWN-FAIL pins, the kernel-oracle PENDING_FIX tier, or .work ledger entries — rather than hidden.
Can I use npm packages and ES modules?Packages compile when their source fits the JZ language. Pure numeric or algorithmic source may include async functions and promises; code using the DOM or Node APIs does not; those services cross as host imports.
Standard import/export syntax bundles into one WASM module at compile time; there is no runtime module resolution.
const { exports } = jz(
'import { add } from "./math.js"; export const f = (a, b) => add(a, b)',
{ modules: { './math.js': 'export const add = (a, b) => a + b' } }
)Import from a named module in the compiled source, then provide that module through { imports }: a JavaScript function, constant, or whole namespace. Functions become WASM imports; numeric constants fold. Numbers pass directly; strings, arrays, and objects cross through memory.*.
// custom function
jz(
'import { log } from "host"; export const f = x => { log(x); return x }',
{ imports: { host: { log: console.log } } }
)
// whole namespace
jz(
'import { sin, PI } from "math"; export const f = () => sin(PI / 2)',
{ imports: { math: Math } }
)
// globalThis works too
jz(
'import { parseInt } from "window"; export const f = () => parseInt("42")',
{ imports: { window: globalThis } }
)As a tagged template, jz inserts interpolated values at compile time. Numbers and booleans inline directly; strings, arrays, and objects become JZ literals:
jz`export let f = () => ${'hello'}.length` // 5
jz`export let f = () => ${[10, 20, 30]}[1]` // 20
jz`export let f = () => ${{name: 'jz', count: 3}}.count` // 3
const scale = (x) => x * 10
jz`export let f = (n) => ${scale}(n) + 1` // f(2) → 21, host-calledInterpolated functions become host calls. Non-serializable values such as host objects and class instances use post-instantiation getters.
How do I pass numbers, strings, arrays, and objects between JS and WASM?Numbers cross as f64 or i32. Heap values use tagged pointers; null, undefined, and booleans use atom tags. Both use the same i64 NaN-box carrier, represented as BigInt in JavaScript; i64 preserves the NaN payload in JSC and Safari, which canonicalize f64 NaNs at the boundary. The carrier, _alloc/_clear, and the jz:i64exp custom section form the current raw ABI; they are regression-tested in test/abi.js but are not cross-release-stable yet. Pin the exact JZ version when persisting prebuilt binaries or writing a raw host; use the wrapper API for values and live compile/instantiate flows. See the stability contract.
The wrapped exports returned by jz() or jz/interop's instantiate() marshal arguments, decode results, and convert WASM throws to Error objects:
const { exports } = jz`
export const greet = s => s.length
export const dist = p => (p.x * p.x + p.y * p.y) ** 0.5
export const point = (x, y) => ({ x, y })
export const rgb = c => [c, c * 0.5, c * 0.2]
export const sum = a => { let n = 0; for (const x of a) n += x; return n }
`
exports.greet('hello') // 5
exports.dist({ x: 3, y: 4 }) // 5
exports.point(3, 4) // { x: 3, y: 4 }
exports.rgb(100) // [100, 50, 20]
exports.sum(new Float64Array([1, 2, 3])) // 6For raw instance.exports calls, memory.String, .Array, typed-array methods, and .Object allocate on the WASM heap and return a pointer; memory.read(ptr) decodes a raw result. Keys passed to memory.Object() must match a compiled schema; key order does not matter. Numeric arrays of at most eight elements return as WASM multi-values.
What ships at runtime, and where does it run?The compiler runs at build time or synchronously in a browser/Worker. The resulting module runs in browsers, Workers, Node, Deno, Bun, and standalone WASM engines.
A module imports env, wasi_snapshot_preview1, or neither, according to what the source actually uses.
How does memory work?Heap-using modules use a growing bump allocator with no free list or garbage collector. Allocations are discarded in batches:
for (let i = 0; i < 1000; i++) {
const result = exports.process(100) // allocates on the WASM heap
memory.reset() // drop the whole batch
}memory.reset() invalidates every previous pointer. Scalar modules without heap values omit the allocator.
For threads, sharedMemory: true compiles against shared WebAssembly.Memory, with Atomics.* lowering to WASM thread operations; jz.pool(src, { threads }) runs one kernel across worker threads. Shared typed arrays and scalars cross; strings and objects stay thread-local.
jz.memory() creates memory shared by multiple compiled modules. Schemas accumulate, so one module can consume an object created by another:
const memory = jz.memory()
const a = jz('export const make = () => ({ x: 10, y: 20 })', { memory })
const b = jz('export const read = o => o.x + o.y', { memory })
b.exports.read(a.exports.make()) // 30Pass an existing WebAssembly.Memory to jz.memory(memory) to wrap it.
Is it fast?JZ leads V8 and AssemblyScript by geometric mean on the covered corpus and targets near-native speed. The release gate is stricter than an average: JZ must be the fastest WASM on every case. Per-case numbers, missing target coverage, and every measured loss stay visible on the bench page; a rival win is a bug to close, not an exception to hide.
How small is the output?A heap-free numeric program emits no memory, allocator, or startup function; an empty program emits an empty module. Runtime helpers and standard-library kernels are included only when reachable.
In the published benchmark corpus, size-optimized JZ modules are 1.02× the size of AssemblyScript modules by geometric mean. AssemblyScript's ports use unchecked array access while JZ retains JavaScript out-of-bounds guards. Most JZ modules in the corpus are single-digit kilobytes.
The compiler stays in the build step; these sizes are what ships.
Which optimizations are applied?At the default optimize: 2, JZ applies:
optimize: 3 / 'speed' accepts additional code size for throughput; optimize: 'size' disables unrolling and SIMD.
How do I inspect or debug output?Float loop counters, plain arrays, and loop-carried dependencies are common reasons a kernel remains slower or scalar.
How does JZ compare with Porffor, AssemblyScript, scriptc etc.?Yes. npm run test:self compiles JZ's parser, jzify pass, compiler, optimizer, and encoder into dist/jz.wasm. That WASM-hosted compiler then compiles real programs, whose output is instantiated and checked against the native compiler.
dist/jz.wasm is a self-host test artifact, not a runtime shipped with compiled programs.
Can JZ compile to native?JZ emits WASM, which wasm2c or w2c2 can translate to C and a C compiler can turn into a native executable:
JS → JZ → WASM → wasm2c → C → clang → native--host native targets this lane directly (same module shape as js, tail calls off for wasm2c). The native benchmark lane lowers with wasm2c, removes its C optimizer barriers, hoists the guard-page-backed memory base, and builds with native CPU tuning plus LTO. It does not require wasm-opt. A host harness and wasm2c runtime must be linked, so this is currently a toolchain rather than a one-command JZ target. See the native pipeline.
Is JZ production-ready?JZ is experimental and pre-1.0. The supported language and WASM ABI may change; pin a version and re-test upgrades. The exact v1 commitments and deliberately non-JavaScript machine semantics are listed in the stability contract. CI runs the core suite, selected test262 language and built-in tests, benchmark checks, and a self-host build.
Adoption is ejectable: remove the JZ build step and the source remains JavaScript.
| Back | FazBrowse Home | New Git URL |