Chrome 119, Firefox 120, and Safari 18.2 made WasmGC practical for Kotlin and Java web apps. Architecture, performance, and tradeoffs. Read now.
What WasmGC Changes for Managed Languages
WebAssembly started as a low-level compile target for C, C++, and Rust. Those languages already manage memory explicitly, so mapping them to linear memory was natural. Java and Kotlin are different: they depend on a garbage collector, object graphs, and runtime types. Without a GC in the Wasm host, toolchains had to ship a collector inside the module, pay for it in size and startup cost, and still fight impedance mismatches with the browser heap.
WasmGC adds typed heap objects and collection to the WebAssembly model itself. Compilers can emit references to host-managed structures instead of emulating a full managed runtime on top of a flat byte buffer. That is why support in Chrome 119, Firefox 120, and Safari 18.2 matters: the missing piece was not “can we compile Kotlin or Java to Wasm,” but “can the browser own the GC so the result is practical for real web apps.”
Architecture: How a Java or Kotlin App Reaches the Browser
A typical pipeline starts with existing application code, compiles it through a WasmGC-aware toolchain, and ships a module that the page loads like any other Wasm binary. The runtime still needs a thin layer for standard library services, interop with JavaScript, and any platform APIs the language expects. The important shift is that object allocation and collection are expressed with WasmGC types rather than a private heap inside linear memory.
Interop is the other architectural hinge. Browser APIs, DOM work, and existing JS libraries live outside the managed Wasm heap. Crossing that boundary has a cost: you marshal data, wrap callbacks, and decide which side owns each object’s lifetime. Good designs keep hot loops and domain models inside the WasmGC world and treat JS as the shell for UI, networking entry points, and third-party widgets—not as the place every object constantly travels through.
- Prefer compiling shared business logic and data models once for WasmGC, not reimplementing them in JS.
- Define a narrow interop surface: DTOs, events, and explicit API calls instead of free-form object sharing.
- Measure cold start and first interaction separately from steady-state throughput; they fail for different reasons.
Performance: Where Wins Appear and Where They Do Not
WasmGC helps most when the workload is allocation-heavy managed code that previously paid for an embedded collector and dense linear-memory bookkeeping. Steady-state numeric work, graph walking, and large in-memory models can run closer to what you expect from a native-style Wasm module, because collection and typing are first-class in the engine.
It does not automatically make every Java or Kotlin web app “as fast as hand-written JS.” DOM-bound UIs still spend time in the browser’s layout and paint paths. Chatty interop can erase gains from better GC. Large modules still download and compile; shipping an entire language runtime and app graph has a weight budget even when GC is host-provided. Performance work is still ordinary engineering: profile the real page, cut boundary crossings, shrink what you ship, and keep interactive code on the critical path lean.
Tradeoffs and Practical Guidance
WasmGC is practical for Kotlin and Java in browsers now that major engines support it, but it is a different product shape than a pure JS SPA. You gain reuse of existing codebases, stronger static typing across larger systems, and a path to share logic with non-web targets. You accept toolchain complexity, longer build pipelines, careful interop design, and debugging that spans JS tools and Wasm-aware inspectors.
Choose WasmGC when you already have substantial Java or Kotlin logic worth keeping, when correctness and structure matter more than minimal bundle size, or when the app’s core is compute and modeling rather than thin UI glue. Prefer conventional web stacks when the product is mostly DOM, marketing pages, or small interactive widgets where the managed runtime adds weight without enough shared logic to justify it. The browsers named above made the platform ready; the remaining decisions are about architecture fit, not whether GC-capable Wasm exists at all.