Whitepaper · May 2026
js² (js squared) is a direct ahead-of-time compiler from JavaScript and TypeScript to WebAssembly GC. Its core architectural claim is that JavaScript can be compiled into Wasm-native artifacts without shipping a bundled JavaScript engine inside the deployed module. The deeper claim is that this materially changes module size, runtime overhead, and the feasibility of building small, linkable, swappable Wasm modules [1] [4] [19].
Most JavaScript-on-Wasm systems either ship an interpreter inside WebAssembly or narrow the language into a subset or replacement dialect. js² (js squared) takes a different path: direct ahead-of-time compilation to WebAssembly GC with the long-term goal of full ECMAScript language compatibility, with TypeScript source support layered on top. The intended endpoint is not a custom JavaScript sub- or superset. Test262 is the current public measurement baseline, not the limit of the product contract. That matters not only for language purity, but for ecosystem fit: broad compatibility with JavaScript is a cornerstone of compatibility with the wider JavaScript ecosystem, including real-world npm packages and existing application code. This whitepaper grounds those compatibility claims in the ECMAScript specification and Test262 [2] [3] and explains why that architectural choice matters, what deployment profile it enables, what tradeoffs remain, and where the approach is already concrete today.
The central claim is not only that JavaScript can be compiled without embedding a runtime. It is that this changes the economics of deployment: modules can be orders of magnitude smaller than interpreter-bundling approaches and, on representative published standalone Wasmtime benchmark paths, can remove large interpreter or engine overhead from hot runtime execution.
Most attempts to run JavaScript in WebAssembly follow one of two paths. The first path bundles a JavaScript engine into a Wasm module. That preserves mature semantics, but it also preserves the cost of shipping and initializing the engine. The second path avoids bundling an engine by narrowing the language into a constrained subset, a TypeScript-like dialect, or a new language designed to compile more easily to Wasm.
QuickJS-based approaches sit squarely in the first category. They are still runtime interpreters packaged inside Wasm: the engine is bundled, and user code executes through that interpreter at runtime [7]. That also places them in the slowest performance tier, because interpretation remains on the hot path.
Full-fledged engine approaches land in a different tier, but they run into a different ceiling. Modern JavaScript engines are designed around native tiered execution, especially JIT compilation, not around ahead-of-time compilation into standalone Wasm artifacts. Once the engine itself is shipped as Wasm, those native JIT tiers are generally unavailable, so the fastest optimization paths of the engine do not come along for the ride [8] [10].
SpiderMonkey-based approaches therefore try to recover some of that lost ground through preinitialization and ahead-of-time baseline specialization, for example with tools such as Wizer and weval. That can improve startup and execution behavior, but it does not change the core architecture: a full JavaScript engine is still shipped inside the module, and the engine is still being adapted to an AOT deployment model it was not originally designed for [8] [9] [10] [20].
Both paths solve real problems, but they also impose real constraints. Interpreter-in-Wasm approaches inherit large artifacts and runtime overhead. Subset approaches reduce the deployment cost, but they change the developer contract by asking teams to write a different language than mainstream JavaScript.
js² is built around a third position: JavaScript should be compiled directly to WebAssembly GC, without embedding a JavaScript engine, and without redefining the language into a smaller substitute.
The project is organized around four design commitments.
| Commitment | Meaning |
|---|---|
| Direct AOT compilation | Compile program logic directly into Wasm GC binaries instead of dispatching through an embedded interpreter. |
| No embedded JS engine | Remove the runtime tax common in interpreter-in-Wasm and bundled-engine stacks, from high-hundreds-of-kilobytes for interpreters to megabytes for full-fledged JS engines. |
| Full ECMAScript direction | Treat ECMAScript compatibility as the strategic target, measured publicly through Test262 progress, with TypeScript source support layered above it. |
| Wasm-native deployment model | Produce artifacts that fit Wasm runtimes, plugin systems, and serverless environments rather than behaving like hidden JavaScript runtimes. |
This is also an ecosystem issue, not just a language-design issue. If the language surface is narrowed too aggressively, or extended into a custom superset that is not specified ECMAScript, compatibility with mainstream application code, existing libraries, and npm packages erodes quickly. Full-language compatibility is therefore a cornerstone of ecosystem compatibility.
Targeting WebAssembly GC is the technical basis that makes direct JavaScript compilation plausible
without recreating a garbage-collected runtime in linear memory. Wasm GC provides host-managed
garbage collection, nominal types, struct and array support, subtyping, and interop through
references such as externref and i31ref
[1]
[4].
Without Wasm GC, direct JavaScript compilation typically falls back to manual object models in linear memory or to bundled runtime machinery. Wasm GC does not make JavaScript semantics easy, but it gives the compiler the right runtime substrate to express a dynamic, garbage-collected language without embedding another garbage-collected runtime inside the output artifact.
At a high level, the compiler pipeline is straightforward:
JavaScript / TypeScript source -> parse and type-check via the TypeScript compiler API -> collect imports and declarations -> lower expressions and statements into a WasmModule IR -> emit a Wasm GC binary -> optionally optimize the result
Parsing and type analysis are handled through the TypeScript compiler API. Code generation lowers the typed AST into a Wasm module IR, registering imports, declarations, structs, arrays, globals, and function bodies before binary emission. Optional optimization can then run through Binaryen.
This is not an interpreter specialization pipeline and not a bytecode container format. The module contains compiled program logic.
The compiler currently spans two execution models. In JS-host mode, modules can import selected helpers from a JavaScript environment where some behavior is still easier or not yet fully lowered into Wasm. In parallel, the project also has a standalone and WASI-oriented direction, where the goal is to depend on Wasm-native facilities instead of a JavaScript embedding environment.
More generally, the host should be understood as a platform surface, not just as “some leftover JavaScript runtime”. In a browser, that surface is the Web Platform and its Web APIs. In Node.js, it includes the environment and standard-library APIs exposed by the runtime and underlying native system. Those platform capabilities are part of the surrounding execution environment and do not need to be eliminated simply because application logic is being compiled to Wasm.
The JS-host path is therefore intentional for interoperability, not only a temporary fallback. It is the right mode when compiled modules need to integrate with platform APIs or with existing JavaScript code that already runs in that environment and is not itself meant to be compiled to Wasm. In that sense, JS-host mode is part compatibility bridge and part integration surface.
This split is important. The end state is not "Wasm that still secretly depends on a browser-like runtime." The point is to move as much behavior as possible into compiled output and explicit Wasm-native integration. The deeper goal is to shrink the implicit host environment and enlarge the Wasm closed world over time. What should remain outside the compiled module is not an ambient JavaScript runtime context, but an explicit and bounded API surface such as Web APIs, Node APIs, or WASI. Those API surfaces can then, in principle, be implemented by any compatible host. Standalone support is meaningful and growing, but it is not yet the primary public conformance path today.
This aligns with the direction of WinterCG and its successor standardization work in Ecma TC55 / WinterTC: a provider-spanning effort to define a common, web-aligned API surface for server-side and edge JavaScript runtimes, including worker-like serverless hosts such as Cloudflare Workers and Deno Deploy [21]. js² does not need to replace that host surface. The relevant point is that a compiled module can target explicit, portable host APIs instead of assuming that each deployment unit must carry a full JavaScript engine to get a familiar serverless programming model.
This is not just theoretical. Wasmer's Edge.js is a recent example of a system that re-exposes Node.js workloads through a WebAssembly-based execution model rather than treating a conventional Node process as the only way to provide the Node environment. That reinforces the broader point: the platform surface can remain, while the ambient runtime context behind it can change. See the official Wasmer Edge.js announcement [11].
The main reason to pursue direct compilation is deployment, not aesthetic purity. When an application is compiled directly to Wasm GC without bundling an engine, the resulting module can be materially better suited for edge runtimes, plugin systems, embedded applications, multi-language hosts, and modular systems where compatible components should be swappable without rebuilding the full application.
This changes the artifact shape in practical ways:
That size tax also matters at the granularity of composition. If the engine is embedded directly into each deployment unit, then the cost is effectively paid per module, not just per application. It can be amortized only if the engine is factored out into an imported or otherwise shared dependency. That is a meaningful optimization path, but it changes the packaging model less than it changes the size accounting.
The current public standalone Wasmtime benchmark surface is easiest to read as a packaging and
runtime comparison. The Wasmtime rows use precompiled Wasmtime artifacts
(wasmtime compile / --allow-precompiled) with runtime JIT compilation
disabled. That is intentional: it approximates a serverless deployment shape where code is
prepared before the request path, and the measured cost is packaging, instantiation, startup, and
execution without on-demand runtime compilation. The benchmark programs are deliberately small,
behavior-oriented kernels: iterative numeric looping, recursive calls, array allocation/fill/summation,
object allocation and field access churn, and string concatenation plus character-code hashing.
| Deployment pattern | What it represents | Size model | Cold start | Runtime |
|---|---|---|---|---|
| Direct js² AOT WasmGC | Compiled WasmGC output, no JavaScript engine in the module | 0.34 kB | 13.7 ms | 21.7 ms |
| Dynamic imported-interpreter module | Small module calling a shared interpreter/runtime plugin | 2.98 kB per module + about 1.2 MB shared runtime/plugin |
24.7 ms | 272 ms |
| Preinitialized/specialized bundled JS engine | Bundled engine with ComponentizeJS/Wizer/weval-style preinitialization and specialization | 14.4 MB | 24.7 ms | 297 ms |
In the same benchmark set, native JavaScript in Node/V8 with JIT enabled is 12.4 ms on the runtime metric and 20.8 ms on cold start. That remains the native runtime baseline to beat on hot execution.
The benchmark is a public standalone datapoint, not the scope of the compiler. Current compatibility work is also pushing against popular npm packages and real application code, while Test262 remains the public conformance baseline.
The runtime data should be read narrowly. Native JavaScript with JIT remains the runtime reference point, and the current standalone path still has known gaps. The useful claim is not universal speedup. It is that removing the embedded interpreter or engine changes module size by orders of magnitude, improves cold start against the fresh Node/V8 process baseline in this benchmark set, and can remove large standalone Wasmtime runtime overhead where direct compilation is already mature.
This is also what makes modular composition more practical. If each deployment unit does not need to carry an engine, it becomes realistic to ship and link smaller Wasm modules instead of collapsing everything into one large runtime container just to amortize the engine cost. If an engine can be shared as an imported dependency, that can reduce duplicated size across many modules, but it still leaves execution centered around the imported engine rather than around directly compiled module logic. With stable interfaces, compatible modules can also be swapped independently, which pushes dependency injection and platform integration to the module boundary instead of the source-bundle boundary.
That modularity is also a security property. Smaller runtime-free modules make it more realistic to adopt a Component-Model-style or otherwise shared-nothing composition model, where modules communicate through explicit interfaces instead of through one shared ambient runtime. In that shape, smaller modules do not just improve packaging; they reduce the amount of state, capability, and implicit trust bundled into each deployment unit [5] [6] [12].
The security implications matter as well. In-process JavaScript execution is structurally difficult to harden, because code often shares one mutable object graph, one prototype universe, and one ambient runtime context. In practice, once untrusted or compromised code runs in that shared environment, it is often difficult to prevent it from reaching or influencing unrelated parts of the program unless heavier isolation mechanisms such as iframes, workers, separate processes, or comparable boundaries are used [15] [16] [17] [18].
Today, JavaScript deployment is often tied to a large runtime and a package-heavy execution model. Public npm ecosystem incidents have shown how quickly that can widen the blast radius of supply-chain failures. A compiled, sandboxed Wasm artifact model does not eliminate software supply-chain risk, but it can reduce the amount of runtime machinery, shared mutable context, and ambient capability that each deployment unit carries by default [14] [12] [13].
In other words, the goal is not merely “JavaScript in Wasm.” The goal is JavaScript with a meaningfully different packaging, execution, security, and composition profile.
The principal risk in any direct JavaScript compiler is semantic coverage. JavaScript is large, specification-heavy, and full of edge cases that are observable by user code. js² treats compatibility as a public engineering problem rather than a hidden claim.
The project tracks ECMAScript compatibility through Test262, the standard conformance suite for JavaScript implementations [2] [3]. Conformance is measured along two independent paths (see §5), and both are reported publicly: the JS-host path (default target, host imports allowed) is at 73.2% Test262 compliance (31,933 of 43,621 official conformance tests passing; report generated 2026-08-11), and the standalone / host-free path (pure WasmGC, no JS host) is at 66.9% (29,195 of 43,621), measured host-free on the same official denominator. These are distinct metrics on different targets and are never summed; the JS-host figure is the headline number, and the standalone gap is where the current effort concentrates (§5.2, §12.2).
That figure should be interpreted correctly. It does not mean the compiler is finished or suitable for arbitrary npm workloads today. It means there is already a public, measurable conformance baseline that can improve in a disciplined way toward the broader goal of real ecosystem compatibility.
The public evaluation surface already includes a landing page, a browser playground, benchmark reporting, and compatibility reporting. That matters because it makes progress inspectable through actual outputs rather than through private claims.
The project is not trying to prove that a small curated subset compiles well. The long-term value is the steady expansion toward mainstream ECMAScript semantics, with TypeScript source support layered above that contract, while preserving the deployment advantages of direct compilation. In practice, ecosystem compatibility flows from language compatibility: the closer the compiler gets to full ECMAScript semantics, the more credible it becomes as a path for existing application code and npm packages rather than only for greenfield demos.
Clarity about non-goals matters.
Some behaviors are intrinsically hostile to ahead-of-time compilation or require explicit host boundaries, including dynamic code evaluation, runtime module loading semantics, and deeply host-observable engine behavior. The right response is not to pretend those constraints do not exist. It is to design a compiler surface that handles them explicitly while still making the broad language tractable.
There are four broad architectures in the JavaScript-to-Wasm space: bundled engines plus AOT specialization, interpreter-only bundling, direct AOT to core Wasm, and direct AOT to Wasm GC. js² occupies the last category.
As of mid-2026, we are not aware of another AOT JavaScript-to-Wasm approach that both aims to implement the full ECMAScript standard and already has production-ready garbage collection. The closest direct-AOT efforts are important, but publicly visible projects either remain experimental, target a narrower language surface, or use core Wasm / linear-memory strategies rather than host-provided WasmGC.
The nearest Wasm-GC direct-compilation analogues we track are JAWSM, a JavaScript-to-Wasm prototype, and Wasmnizer-ts, a TypeScript-subset-to-WasmGC research compiler from the WAMR / Web DevKit ecosystem. They are useful signals that direct JS/TS-to-WasmGC compilation is an active research direction, but we currently treat them as prototype/subset comparators rather than production competitors for full ECMAScript coverage.
| Dimension | Interpreter in Wasm | Custom sub- or superset | Direct AOT to Wasm GC (js²) |
|---|---|---|---|
| Ships a JS engine | Yes | No | No |
| Targets full ECMAScript compatibility over time | Usually inherits engine behavior | Usually no | Yes |
| Garbage collection model | Engine-owned GC inside the bundled runtime | Usually custom or language-specific | Host-provided WasmGC |
| Deployment profile | Heavier artifacts, runtime overhead | Lean, but language adoption friction | Lean artifacts with mainstream-language ambition |
| Main risk | runtime bulk | developer adoption and compatibility ceiling | compiler and conformance complexity |
The project deliberately accepts the last category of risk. It chooses compiler complexity over runtime bulk and over language substitution.
The approach is especially relevant where JavaScript demand exists but shipping a JavaScript engine is the wrong deployment decision.
A useful whitepaper cannot ignore the hard parts.
Conformance is still incomplete. At 73.2% Test262 compliance on the JS-host path — and 66.9% on the standalone, host-free path — the compiler is credible but far from complete, and the standalone gap in particular is a primary focus (§5.2, §12.2).
Wasm GC support is required. The architecture depends on runtimes with Wasm GC support. That is increasingly practical, but it remains a real deployment constraint.
Some host fallbacks still exist. The system already compiles substantial behavior directly, but some operations still use host-assisted paths. The long-term direction is to shrink those boundaries, not normalize them.
The hardest part is semantic closure. The challenge is not emitting Wasm bytes. It is matching JavaScript behavior closely enough that mainstream code can move through the compiler without requiring a language rewrite.
The highest-value next steps are not ambiguous.
js² exists because “JavaScript in Wasm” is not the same thing as “JavaScript compiled to Wasm.” Bundling an interpreter inside Wasm can deliver strong compatibility, but it inherits the size and runtime costs of shipping the engine. Narrowing or extending the language can simplify compilation, but it changes the developer contract. js² takes the harder route: compile mainstream ECMAScript semantics directly to WebAssembly GC without embedding a runtime, with TypeScript source support layered on top rather than used as a different language contract.
That route is still in progress, and it is not trivial. But it produces a distinct and strategically valuable outcome if it succeeds: Wasm-native artifacts, many-times smaller modules than interpreter-bundling approaches, substantially lower hot-path runtime overhead on representative standalone benchmark paths, reduced runtime and supply-chain attack surface, more portable deployment across Wasm-capable environments without Node.js as the target runtime, more composable and swappable modules when interfaces are stable, stronger isolation boundaries, and a credible TypeScript story for Wasm platforms that do not want to ship a JS engine.