WebAssembly Garbage Collection (WasmGC): Full Browser Support in 2026

What Is WebAssembly Garbage Collection (WasmGC)?
WebAssembly (Wasm) was originally designed as a compilation target for languages like C, C++, and Rust — languages that manage memory manually. But what about languages with automatic memory management, like Java, Kotlin, Dart, or Go? Before WasmGC, these languages had to ship their entire garbage collector as part of the compiled Wasm binary, resulting in bloated file sizes, poor integration with browser developer tools, and duplicated work that the browser's own VM already handles natively.
The WasmGC proposal (formally part of Wasm 3.0, standardized on September 17, 2025) introduces first-class garbage-collected types — struct and array — directly into the WebAssembly type system. Instead of GC-managed languages compiling their own memory allocator and collector into linear memory, they now delegate object allocation and collection to the host VM's built-in garbage collector (V8, SpiderMonkey, or JavaScriptCore).
Before WasmGC: The Linear Memory Approach
Before WasmGC, compiling a GC language to Wasm looked like this:
- The language's runtime (including its garbage collector) was compiled to Wasm and shipped as part of the binary
- All objects lived in Wasm's linear memory — a flat byte array managed manually
- The embedded GC had to walk linear memory to find live objects, with no help from the browser VM
- Interacting with JavaScript objects required complex, expensive "glue code"
- Browser DevTools couldn't inspect GC-managed objects inside linear memory
After WasmGC: Native VM Integration
With WasmGC, the picture changes dramatically:
- Languages define
structandarraytypes in the Wasm module's type section - Object allocation uses
struct.newandarray.newinstructions - The browser's GC automatically tracks and collects these objects — no embedded GC needed
- GC objects can seamlessly reference JavaScript objects and vice versa
- DevTools can inspect WasmGC objects natively
Browser Support in 2026: Full Cross-Browser Coverage
As of April 2026, WasmGC has reached Baseline Newly Available status (since December 11, 2024), meaning all major browser engines support it. Global coverage stands at approximately 87.9% of users according to caniuse.com.
| Browser | First Version with WasmGC | Ship Date | Engine |
|---|---|---|---|
| Chrome | 119 | October 2023 | V8 |
| Edge | 119 | October 2023 | V8 (Chromium) |
| Firefox | 120 | November 2023 | SpiderMonkey |
| Opera | 105 | Late 2023 | V8 (Chromium) |
| Safari (macOS & iOS) | 18.2 | December 2024 | JavaScriptCore |
| Samsung Internet | 25 | 2024 | V8 (Chromium) |
| Opera Mobile | 80 | 2025 | V8 (Chromium) |
Feature Detection: Checking WasmGC Support at Runtime
Before relying on WasmGC, you should verify that the user's browser supports it. The most reliable method is to attempt instantiation of a minimal Wasm module that uses GC types:
1/**
2 * Detect WasmGC support by trying to compile a minimal module
3 * that declares a GC struct type.
4 *
5 * The binary below defines:
6 * (module
7 * (type (struct (field i32)))
8 * )
9 */
10async function supportsWasmGC() {
11 try {
12 const bytes = new Uint8Array([
13 0x00, 0x61, 0x73, 0x6d, // magic: \0asm
14 0x01, 0x00, 0x00, 0x00, // version: 1
15 0x01, // Type section id
16 0x07, // Section size: 7 bytes
17 0x01, // 1 type
18 0x5f, // struct type constructor
19 0x01, // 1 field
20 0x00, // field mutability: immutable
21 0x7f, // field type: i32
22 ]);
23 await WebAssembly.compile(new WebAssembly.Module(bytes));
24 return true;
25 } catch {
26 return false;
27 }
28}
29
30// Usage
31const hasGC = await supportsWasmGC();
32if (hasGC) {
33 console.log('WasmGC is supported! Loading optimized module...');
34 // Load WasmGC-compiled module
35} else {
36 console.log('WasmGC not supported. Falling back to JS or linear-memory Wasm...');
37 // Load fallback
38}
Languages Targeting WasmGC
WasmGC unlocks the web as a first-class target for garbage-collected languages. Here's the current state of each major language ecosystem:
Kotlin/Wasm — Beta (Stable Expected Late 2026)
Kotlin/Wasm is arguably the most mature WasmGC target. It graduated from Alpha to Beta in September 2025, and Compose Multiplatform for Web reached Beta at the same time. JetBrains has been a driving force behind WasmGC adoption.
1// greeting.kt — A simple Kotlin/Wasm example
2// Compile target: wasmJs (uses WasmGC under the hood)
3
4fun greet(name: String): String {
5 val greeting = buildString {
6 append("Hello, ")
7 append(name)
8 append("! This runs on WasmGC ")
9 append("with native browser garbage collection.")
10 }
11 return greeting
12}
13
14// Data classes map to WasmGC struct types
15data class User(val name: String, val score: Int)
16
17fun processUsers(users: List<User>): List<User> {
18 // Collections, lambdas, and data classes all work
19 // Objects are allocated as WasmGC structs, not in linear memory
20 return users
21 .filter { it.score > 50 }
22 .sortedByDescending { it.score }
23}
To set up a Kotlin/Wasm project with Gradle:
1// build.gradle.kts
2plugins {
3 kotlin("multiplatform") version "2.1.20"
4}
5
6kotlin {
7 wasmJs {
8 browser {
9 commonWebpackConfig {
10 outputFileName = "app.wasm.js"
11 }
12 }
13 binaries.executable()
14 }
15
16 sourceSets {
17 val wasmJsMain by getting {
18 dependencies {
19 implementation("org.jetbrains.kotlinx:kotlinx-browser:0.3")
20 }
21 }
22 }
23}
24
25// Build command:
26// ./gradlew wasmJsBrowserProductionWebpack
Dart / Flutter Web — Stable WasmGC Support
Flutter shipped stable WebAssembly support in Flutter 3.38 (November 2025), with impressive benchmarks: approximately 40% faster load times and 30% less memory usage compared to the JavaScript compilation target.
1# Create a new Flutter project targeting web with Wasm
2flutter create my_wasm_app
3cd my_wasm_app
4
5# Build for web using WebAssembly (WasmGC)
6flutter build web --wasm
7
8# The output in build/web/ will contain:
9# main.dart.wasm — WasmGC-compiled Dart code
10# main.dart.mjs — JS bootstrap/glue code
11# index.html — Entry point with fallback logic
12
13# For development with hot reload:
14flutter run -d chrome --wasm
skwasm) has compatibility issues with Firefox and Safari due to browser-specific bugs. Chrome/Chromium is the most reliable target. Flutter automatically falls back to the CanvasKit (JS) renderer on unsupported browsers. Track the Safari issue at WebKit Bug 267291.
Java — GraalVM WebAssembly Backend
Oracle GraalVM for Java 25 introduced a WebAssembly backend that compiles Java bytecode to WasmGC modules. This is a landmark achievement — Java can now run natively in browsers without applets, plugins, or heavy runtime embedding.
Google has been a pioneer here: Google Sheets uses J2Wasm (an internal tool) to compile its Java-based calculation engine to WasmGC, achieving 2x faster performance than the equivalent JavaScript implementation.
1# Compile Java to WasmGC using GraalVM (JDK 25+)
2# Requires Oracle GraalVM with the wasm component
3
4# Install the Wasm component
5gu install wasm
6
7# Compile a Java application to WasmGC
8native-image --target=wasm32-wasi \
9 -o app.wasm \
10 -H:+WasmGC \
11 com.example.Main
12
13# The resulting app.wasm uses WasmGC struct/array types
14# and delegates memory management to the browser's GC
C# / .NET / Blazor — NOT Adopting WasmGC
In a surprising but technically well-reasoned decision, Microsoft has stated that .NET will not adopt WasmGC v1. The fundamental incompatibilities include:
- WasmGC v1 cannot support interior pointers,
ref, orSpan<T> - No finalization, resurrection, weak references, or dependent handles
- No concurrent GC support
- C/C++ interop code cannot manipulate WasmGC objects outside linear memory
Blazor WebAssembly continues to bundle the full .NET runtime (with its own GC) compiled to linear-memory Wasm. Microsoft says they will "continue to monitor the evolution of the post-v1 WasmGC spec."
Go — No Active Plans
Despite community interest (94+ upvotes on GitHub issue #63904), the Go team has labeled WasmGC support as "Unplanned". Go currently compiles to Wasm using linear memory with a bundled GC, resulting in larger binaries (~10-15MB minimum). This remains an area of community frustration.
Other Languages
| Language | WasmGC Status | Notes |
|---|---|---|
| OCaml | Active development | wasocaml compiler targeting WasmGC |
| Scheme | Working prototype | Hoot compiler (Spritely/Guile) |
| Scala | Experimental | Via Scala.js → Wasm bridge or direct targeting |
Technical Benefits: Why WasmGC Matters
1. Dramatically Smaller Binaries
By offloading garbage collection to the browser, WasmGC modules don't need to include a GC implementation. The difference is striking:
| Benchmark | WasmGC (Java) Binary | Linear Memory (C/Rust) Binary | Reduction |
|---|---|---|---|
| Fannkuch | 2.3 KB | 6.1 – 9.6 KB | ~62-76% |
For real-world applications, the savings are even more pronounced because you're eliminating not just the GC but also the memory allocator (malloc/free implementations) and all the bookkeeping code.
2. Better Performance
WasmGC objects have fixed, known types and structures at compile time. This means the browser's JIT compiler can generate more efficient machine code:
- No deoptimization risk — unlike dynamic JavaScript objects, WasmGC struct field accesses are always type-safe
- No shadow stack overhead — linear-memory GC implementations require a shadow stack to walk the call stack for root scanning; WasmGC eliminates this entirely
- V8 speculative inlining — delivered approximately 30% speedup on Google Sheets' WasmGC workload
- Binaryen optimization — applying
wasm-optto WasmGC modules yields an average 1.9x speedup
3. Seamless JavaScript Interop
WasmGC objects live on the same heap as JavaScript objects. The browser's GC can trace references between them, eliminating the need for complex bridge/barrier code:
1// JavaScript interop with a WasmGC module
2const instance = await WebAssembly.instantiateStreaming(
3 fetch('app.wasm'),
4 {
5 imports: {
6 // JS functions can receive WasmGC references directly
7 logUser(userRef) {
8 // The VM handles the reference — no serialization needed
9 console.log('Processing user:', userRef);
10 }
11 }
12 }
13);
14
15// WasmGC-exported functions return GC-managed references
16const result = instance.exports.createUser('Alice', 100);
17// 'result' is a WasmGC struct reference — the browser's GC tracks it
4. Better DevTools Integration
Because WasmGC objects are first-class citizens of the browser's GC, developer tools can:
- Inspect WasmGC object fields and types in the Memory tab
- Track GC allocations and collections
- Profile memory usage accurately
- Detect memory leaks involving WasmGC objects
Real-World Impact: Flutter Web and Kotlin Multiplatform
Flutter Web with WasmGC
Flutter's adoption of WasmGC represents one of the largest real-world deployments of this technology:
- ~40% faster initial load compared to JavaScript compilation
- ~30% less memory usage at runtime
- Flutter 3.38 (November 2025) marked stable Wasm support
- Flutter 3.35 began preparing developers for Wasm as the default compilation target
1// Flutter Web with WasmGC — no code changes needed!
2// The same Dart code compiles to both JS and WasmGC
3
4import 'package:flutter/material.dart';
5
6void main() {
7 runApp(const MyApp());
8}
9
10class MyApp extends StatelessWidget {
11 const MyApp({super.key});
12
13 @override
14 Widget build(BuildContext context) {
15 return MaterialApp(
16 title: 'Flutter WasmGC Demo',
17 home: Scaffold(
18 appBar: AppBar(title: const Text('Running on WasmGC!')),
19 body: const Center(
20 child: Text(
21 'This widget tree is managed by the browser GC',
22 style: TextStyle(fontSize: 24),
23 ),
24 ),
25 ),
26 );
27 }
28}
Kotlin Multiplatform for Web
Compose Multiplatform for Web (Beta, September 2025) allows developers to share UI code across Android, iOS, Desktop, and Web — with the web target compiled to WasmGC:
1// Compose Multiplatform — same code, multiple targets including WasmGC
2@Composable
3fun App() {
4 var count by remember { mutableStateOf(0) }
5
6 MaterialTheme {
7 Column(
8 modifier = Modifier.fillMaxSize(),
9 horizontalAlignment = Alignment.CenterHorizontally,
10 verticalArrangement = Arrangement.Center
11 ) {
12 Text("Count: $count", style = MaterialTheme.typography.headlineMedium)
13 Button(onClick = { count++ }) {
14 Text("Increment")
15 }
16 }
17 }
18}
19
20// When compiled to wasmJs target, this entire UI
21// runs as WasmGC code in the browser
Google Sheets: The Proof at Scale
Perhaps the most compelling validation of WasmGC is Google's migration of the Sheets calculation engine from JavaScript to WasmGC (compiled from Java via J2Wasm):
- Initial result: Unoptimized WasmGC was 2x slower than JavaScript
- After V8 and Binaryen optimizations: WasmGC became 2x faster than JavaScript
- That's a 4x improvement from the initial unoptimized compilation to the final optimized version
- Demonstrates that WasmGC is production-ready at Google scale
WasmGC Post-MVP: What's Coming Next
WasmGC as shipped in Wasm 3.0 is the "MVP" (Minimum Viable Product). Several proposals are in progress to extend its capabilities:
| Proposal | Phase | What It Adds |
|---|---|---|
| JS Promise Integration | Phase 4 (Standardize) | Seamless async/await between JS and Wasm — no callback wrappers needed |
| Stack Switching | Phase 3 (Implementation) | Enables coroutines, green threads, and efficient async in Wasm |
| JS String Builtins | Shipped (Wasm 3.0) | Native string operations without JS glue code — already available |
| Shared-Everything Threads | Phase 1 | Share GC objects across threads — critical for .NET compatibility |
| More Array Constructors | Phase 1 | Additional ways to create and manipulate WasmGC arrays |
await JavaScript Promises without the complex trampoline code currently required.
How to Optimize WasmGC Modules with Binaryen
Binaryen is the essential optimization toolkit for WebAssembly. Its wasm-opt tool can significantly improve WasmGC module performance:
1# Install Binaryen
2npm install -g binaryen
3
4# Optimize a WasmGC module (aggressive optimization)
5wasm-opt -O3 --enable-gc --enable-reference-types \
6 input.wasm -o optimized.wasm
7
8# With additional WasmGC-specific passes
9wasm-opt -O3 \
10 --enable-gc \
11 --enable-reference-types \
12 --enable-strings \
13 --type-merging \
14 --type-ssa \
15 --global-refining \
16 --cast-all \
17 --gufa-optimizing \
18 input.wasm -o optimized.wasm
19
20# Check the size difference
21ls -la input.wasm optimized.wasm
22
23# Expected: 1.5-2x smaller after optimization
24# Expected: 1.9x average runtime speedup (based on V8 team benchmarks)
Limitations and Considerations
Despite its transformative potential, WasmGC has important limitations to be aware of:
Current Limitations
- No interior pointers: You cannot create a reference to a field inside a struct — only to the struct itself. This is why .NET can't adopt WasmGC v1.
- No finalization: There's no equivalent of Java's
finalize()or C#'sIDisposabletriggered by GC. Resources must be explicitly managed. - No weak references in Wasm: While JavaScript has
WeakRef, WasmGC v1 doesn't expose weak reference capabilities. - No concurrent GC control: Wasm modules cannot hint or control when GC runs — it's entirely up to the host VM.
- Browser-specific bugs: Flutter's Wasm renderer has issues on Safari and Firefox as of April 2026, despite WasmGC itself working correctly.
When NOT to Use WasmGC
- C/C++/Rust code: These languages manage memory manually and should continue using linear-memory Wasm
- .NET/Blazor: Fundamental incompatibilities make WasmGC impractical for the .NET runtime
- Real-time applications requiring GC pause control: You cannot tune the GC for latency-sensitive workloads
Getting Started: A Complete Example
Here's a practical example of setting up a Kotlin/Wasm project that uses WasmGC, with a fallback for unsupported browsers:
1<!DOCTYPE html>
2<html lang="en">
3<head>
4 <meta charset="UTF-8">
5 <title>WasmGC Application</title>
6</head>
7<body>
8 <div id="root"></div>
9 <script type="module">
10 // Feature detection
11 async function supportsWasmGC() {
12 try {
13 const bytes = new Uint8Array([
14 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00,
15 0x01, 0x07, 0x01, 0x5f, 0x01, 0x00, 0x7f,
16 ]);
17 await WebAssembly.compile(new WebAssembly.Module(bytes));
18 return true;
19 } catch {
20 return false;
21 }
22 }
23
24 if (await supportsWasmGC()) {
25 // Load WasmGC-compiled application
26 const { default: init } = await import('./app.wasm.js');
27 await init();
28 console.log('Running with WasmGC - native browser GC');
29 } else {
30 // Fallback to JavaScript version
31 const { default: init } = await import('./app.js');
32 await init();
33 console.log('Running JavaScript fallback');
34 }
35 </script>
36</body>
37</html>
References and Further Reading
- Can I Use — WasmGC Browser Compatibility Table
- Chrome Blog — A new way to bring garbage collected programming languages efficiently to WebAssembly
- V8 Blog — WasmGC Porting Guide
- WebAssembly.org — Wasm 3.0 Announcement
- WebAssembly Proposals Repository
- Kotlin/Wasm Documentation
- Flutter Web — WebAssembly Compilation
- Binaryen — WebAssembly Toolchain Optimizer
- .NET Runtime — WasmGC Discussion (Why .NET Won't Adopt It)
- Go — WasmGC Tracking Issue
Comments
Sign in to leave a comment
No comments yet. Be the first!
Related Articles
Stay updated
Get notified when I publish new articles. No spam, unsubscribe anytime.