Cristhian Villegas
Frontend13 min read1 views

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

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).

Key insight: WasmGC doesn't add a new garbage collector — it lets Wasm modules use the same GC that already powers JavaScript in every browser.

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 struct and array types in the Wasm module's type section
  • Object allocation uses struct.new and array.new instructions
  • 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)
Safari was the last holdout. Safari 18.2 (December 2024) finally shipped WasmGC enabled by default, completing cross-browser coverage. On iOS, all browsers use WebKit under the hood, so WasmGC support on iPhone/iPad depends on iOS version 18.2+.

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:

javascript
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}
Important: Do not rely solely on user-agent sniffing. Feature detection through trial compilation is the only reliable method, especially since Chromium-based browsers may have different feature flags enabled.

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.

kotlin
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:

kotlin
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.

bash
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
Known issue (April 2026): Flutter's Wasm renderer (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.

bash
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, or Span<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-opt to 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:

javascript
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
dart
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:

kotlin
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
JS Promise Integration is particularly exciting — at Phase 4, it's nearly standardized and will allow WasmGC modules to directly 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:

bash
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#'s IDisposable triggered 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:

html
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

Share:
CV

Cristhian Villegas

Software Engineer specializing in Java, Spring Boot, Angular & AWS. Building scalable distributed systems with clean architecture.

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.