asopi tech
asopi techIndie Developer
A Map of WASM Runtimes and Frameworks (2026) — Who Actually Implements the Raw APIs?

[July 2026 edition]

A Map of WASM Runtimes and Frameworks (2026) — Who Actually Implements the Raw APIs?

Published: Jul 24, 2026
Reading time: ~14 min

In the previous article, we mapped how WASM, WebGL, and WebGPU have converged on a division of roles rather than replacement (Part 1). This sequel goes one layer down and draws a map of the runtimes, tools, and frameworks that actually implement and abstract WASM’s “raw APIs.” This is the state of things as of July 2026.

The WASM Ecosystem Is Not Monolithic

The implementations around WASM — Wasmer and the rest — are not a single “WASM framework.” They split into layers.

Applications / plugins / serverless


Upper frameworks       Spin / wasmCloud / Extism / Wasmer Edge


Component Model / WASI / language bindings   WIT / wit-bindgen / cargo-component / jco / wasm-tools


WASM runtimes / embedding APIs    Wasmtime / Wasmer / WasmEdge / wazero / WAMR


Execution engines       Cranelift / LLVM / Singlepass / Interpreter / AOT


WebAssembly Core Specification

The browser side is a separate lineage.

Rust / C++ / C / Go, etc.

wasm-bindgen / Emscripten / TinyGo

WebAssembly JavaScript API

V8 / SpiderMonkey / JavaScriptCore

WASM Core

So the first step in a technology decision is to separate what you actually want to do: run a WASM binary; embed execution into a host app; implement WASI; use the Component Model; call browser APIs from WASM; build a plugin system; build a serverless/edge platform. That is the starting point.

What “Raw API” Refers To

There are at least four kinds of “raw API” around WASM. Conflating them leads to bad choices.

  • WASM Core API — loads, validates, compiles, and instantiates modules and manipulates functions and memory. It is built from concepts like Engine, Store, Module, Instance, Memory, Table, Global, Function, and Linker.
  • WASI API — the host provides WASM’s external facilities: files, standard I/O, environment variables, clocks, random numbers, sockets. It is not a single API but a set of interfaces still being standardized.
  • Component Model API — passes strings, lists, records, enums, and resources — not just integers and pointers — between modules. You define it in WIT and convert to each language’s types via the Canonical ABI.
  • Web API bindings — in the browser, WASM cannot call the DOM or WebGPU directly; you need a bridge to the JavaScript API (wasm-bindgen/web-sys, Emscripten, TinyGo’s syscall/js, and so on).

In the browser, the Core API is exposed as a JavaScript API:

core-api.js
const module = await WebAssembly.compile(bytes);
const instance = await WebAssembly.instantiate(module, imports);
const result = instance.exports.add(1, 2);

Out-of-browser runtimes expose an equivalent embedding API to Rust, C, C++, Go, Python, and others.

Major WASM Runtimes

Wasmtime — the Center of Standards Tracking

Wasmtime is a standards-focused runtime developed by the Bytecode Alliance. It supports JIT and AOT execution of both Core WASM and Component Model components, and embeds into a host as a Rust library, not just a CLI. Its execution engines are Cranelift and Winch, and it ships WASI Preview 1/2 and the Component Model.

wasmtime-embed.rs
let engine = wasmtime::Engine::default();
let module = wasmtime::Module::from_file(&engine, "plugin.wasm")?;
let mut store = wasmtime::Store::new(&engine, ());
let instance = wasmtime::Instance::new(&mut store, &module, &[])?;

Its strengths are the speed of standards tracking, its lead on Component Model support, integration with WASI Preview 2, affinity with Rust hosts, and easy limiting of CPU time, memory, and instance count. It suits embedding WASM into Rust apps, standards-compliant plugin platforms, and serverless execution. Note its bias: rather than running large numbers of POSIX apps as-is, it leans toward structuring apps around WASI and the Component Model.

Wasmer — an Integrated Ecosystem from Runtime to Hosting

Wasmer is more than a runtime: it is an integrated ecosystem including package management, a browser SDK, and cloud/edge execution. You can choose among Cranelift, LLVM, Singlepass, and interpreter backends.

Its defining feature is the proprietary extension WASIX. It fills gaps in standard WASI with more POSIX-like facilities — threads, sockets, processes, signals, terminal. That improves practicality, but departs from full compatibility across standard WASI runtimes.

WASI  ─ minimal, capability-oriented standard API
WASIX ─ threads / processes / sockets / signals / terminal (more POSIX-like)

There is also a JS SDK that runs WASI/WASIX apps in the browser; the official examples wire Bash and Unix utilities to xterm.js in-browser. Wasmer Pack and WAI generate packages too, but the center of the standard ecosystem is WIT and the Component Model, so WAI should be treated as Wasmer’s own line, with compatibility and migration paths checked individually. If standard WASI compatibility is the top priority, Wasmtime is the clearer choice; if practical Unix-compatible features matter more, Wasmer/WASIX is strong.

WasmEdge — Cloud-Native and Edge AI

WasmEdge is a CNCF Sandbox project focused on cloud-native, edge, AI inference, and server-side WASM. It has SDKs for C/C++/Rust/Python/Node.js and leans on adding runtime features as plugins; official plugins include WASI-Crypto and WASI-NN.

WASM guest
  ├─ wasi-nn / wasi-crypto / sockets / custom host API

WasmEdge plug-in

TensorFlow / PyTorch / OpenVINO / host library

For AI inference it connects to host-side inference runtimes through WASI-NN — rather than compiling the whole network into WASM, WASM calls the host’s inference engine. It is used in Docker’s WASM stack, distributing WASM binaries as OCI images. It suits edge AI, WASI-NN, OCI/Docker integration, and Nginx/Proxy-Wasm. It states a direction on the Component Model, but that may not be as consistently central as in Wasmtime, so confirm the WIT features, WASI Preview 2, async, and resource-type support you need.

wazero — a Pure-Go Runtime with No CGO

wazero is a runtime implemented purely in Go, with no native dependencies. With no CGO and no C libraries, it embeds statically into Go binaries, cross-compiles easily, and integrates interruption and control via Go’s context.

wazero-embed.go
runtime := wazero.NewRuntime(ctx)
defer runtime.Close(ctx)

module, err := runtime.Instantiate(ctx, wasmBytes)
if err != nil {
    return err
}
result, err := module.ExportedFunction("add").Call(ctx, 1, 2)

It excels at plugin features for Go servers, CLIs, gateways/proxies/control planes, CGO-forbidden environments, and single-binary distribution. If you must have the most advanced Component Model features, Wasmtime leads; but for safely and concisely embedding Core WASM and WASI into Go, wazero is the first choice.

WAMR / wasm3 — Embedded and Ultra-Small Environments

WAMR (WebAssembly Micro Runtime) is the Bytecode Alliance’s lightweight runtime for embedded, IoT, TEE, and edge. You can choose classic/fast interpreter, AOT, or JIT; official material puts the interpreter at roughly 85 KB and the AOT part around 50 KB depending on configuration. It suits microcontrollers, IoT gateways, embedded Linux, TEE, and firmware extensions.

wasm3 is an even smaller interpreter-type runtime with no JIT, running in low-memory environments. Use wasm3 for the ultra-small and simple; use WAMR when embedded but needing multiple execution modes. Note that wasm3 warrants care regarding update cadence and support for the latest proposals, WASI, and the Component Model.

Chicory / GraalWasm — WASM on the JVM

Chicory is a JVM-native runtime implemented in 100% Java, running WASM with no JNI or native libraries. It integrates easily with Maven/Gradle and suits plugin mechanisms for Java business systems and JVM servers. Its value is pure Java, ease of distribution, and safe embedding, rather than a high-performance JIT or bleeding-edge Component Model.

GraalWasm is a WASM implementation on GraalVM, running WASM from Java/Kotlin via the Polyglot API. It fits when you already use GraalVM or want a polyglot configuration.

In-Browser WASM Implementations

In the browser, you do not use Wasmtime or Wasmer as the execution engine. WASM Core is implemented directly by the browser’s JavaScript engine.

Chrome / Edge → V8
Firefox       → SpiderMonkey
Safari        → JavaScriptCore

Apps see the common WebAssembly JavaScript API, beneath which JIT/baseline/optimizing compilers run. But while these implement the WASM Core Spec, WASI is not a browser-standard API. To use WASI in the browser you need a polyfill or virtual environment: the Wasmer JS SDK, a WASI shim, a Node-compatibility layer, a virtual filesystem, a Worker-based runtime, or host imports implemented in JS.

Foundational Tools for Producing and Transforming Binaries

  • WABT (WebAssembly Binary Toolkit) — the lowest-level tools for Core WASM: wat2wasm, wasm2wat, wasm-validate, wasm-objdump, wasm-strip. Used for spec validation, debugging, and binary analysis.
  • Binaryen — an optimization/transformation base centered on wasm-opt: dead code elimination, inlining, constant folding, size optimization, feature lowering. Used inside many toolchains, including Emscripten.
  • wasm-tools — the Bytecode Alliance’s Rust tools handling both Core WASM and the Component Model. Its Component Model commands — component new/component wit/validate — are the important part. If WABT is the foundational tool for Core WASM, wasm-tools is the foundational tool for the Component Model era.
wasm-tools.sh
wasm-tools component new module.wasm -o component.wasm
wasm-tools component wit component.wasm
wasm-tools validate component.wasm

Implementations Around the Component Model

WIT — the Interface Definition Language for Components

WIT defines typed interfaces without exposing linear-memory offsets or pointers.

package asopi:storage;

interface blob-store {
  resource blob {
    size: func() -> u64;
    read: func(offset: u64, length: u64) -> list<u8>;
  }
  open: func(name: string) -> result<blob, string>;
}
Core WASM ABI      : pointer + length + integer
Component Model    : string + list + record + resource + result

Generation and Transformation Tools

  • wit-bindgen — generates guest/host code from WIT definitions (Rust, C, C++, Go, Java, C#, JavaScript, Python, etc.). Crucially, it is not an execution engine but a call-code generator. Wasmtime “runs”; wit-bindgen “generates the calling code.”
  • cargo-component — a Cargo subcommand for building Rust projects for the Component Model: WIT dependency management, binding generation, Core-WASM-to-Component conversion, metadata, and WASI Preview 2 builds. It is the de facto standard tool for using the Component Model from Rust.
  • jco — a Component Model toolchain for JavaScript. Because browsers and Node.js lack a common API to natively run components, jco transpile converts a component into Core WASM plus ES modules (JS glue). It can also generate TypeScript definitions from WIT types.
  • componentize-py — converts Python code into a WASM component. It is large because it bundles a Python interpreter, but it lets you implement WIT interfaces as Python functions.

Language-Specific Bindings

Rust — wasm-bindgen / web-sys / js-sys

wasm-bindgen is a high-level Rust-JavaScript binding generator; a build produces the WASM binary, JS glue, and TypeScript declarations. web-sys is a Web API binding generated from WebIDL, giving access to the DOM, WebGL, WebGPU, Fetch, Workers, and more. js-sys is a low-level binding to JS built-in types like Array and Promise. wasm-pack bundles the build/test/npm-publish workflow (the old Working Group site now states maintenance has ended, so check each tool’s current status in its repo).

wasm-bindgen.rs
#[wasm_bindgen]
pub fn greet(name: &str) -> String {
    format!("Hello, {name}")
}

C/C++ — Emscripten

Emscripten is an LLVM-based C/C++ toolchain — not merely a compiler but “an OS-compatibility/runtime layer for the web.” It provides emscripten.h/html5.h, WebGL/WebGPU APIs, Fetch, WebSocket, Wasm Workers, pthreads, Audio Worklets, the File System API, and Embind. The low-level functions in html5.h are thin bindings that call the corresponding Web API directly.

Supporting implementations include WasmFS (moving the virtual filesystem to the WASM side, aimed at multithread performance) and Wasm Workers (using Web Workers and SharedArrayBuffer from C/C++).

Go — TinyGo

TinyGo is the primary way to compile Go into small WASM binaries. Its targets are wasm (browser + JS API) and wasi/wasip1 (WASI runtimes). The standard Go compiler can also produce WASM, but TinyGo can win on runtime size and startup cost (at the cost of differences in Go compatibility, reflection, and GC behavior).

Upper Frameworks

  • Extism — a framework for using WASM as an application plugin format. It provides host-language SDKs and guest-language PDKs, manifests, memory passing, host functions, and sandbox control. It builds a common plugin model on top of runtimes like Wasmtime, so instead of managing the raw Wasmtime API yourself you call Plugin.call("transform", input). Useful when you might change host language later.
  • Spin — Fermyon’s server-side/serverless framework using WASM components. Triggered by HTTP/Redis/Queue events, it provides host interfaces like outbound HTTP, key-value, and database. Fast cold starts and language independence are its hallmarks; it is an upper framework for web apps, not a “run any WASM” runtime.
  • wasmCloud — a framework treating WASM components as the units of a distributed app. It separates business logic (components) from infrastructure (Capability Providers: S3/NATS/PostgreSQL/HTTP). Less a Kubernetes-container replacement than a base for executing and wiring distributed components and capabilities.
  • Proxy-Wasm — an ABI for extending proxies like Envoy/NGINX and service meshes: request transformation, auth, rate limiting, logging, telemetry, security policy. It is a domain-specific host ABI on a separate track from the Component Model.

GPU Raw-API Implementations — Dawn and wgpu

When touching the GPU from WASM, the WebGPU side also has a “spec implementation” and “wrappers.”

Dawn is the WebGPU implementation used in Chromium. It implements webgpu.h and provides a C API mapping almost one-to-one to the WebGPU IDL. It includes native WebGPU, C headers, a C++ wrapper, a client-server wire protocol (Dawn Wire), the WGSL compiler Tint, and a validation layer. To isolate GPU work from the browser process, Chromium uses a client-server configuration via Dawn Wire. It suits using the WebGPU raw API from C/C++, needing a Chromium-compatible implementation, or building your own browser/renderer.

wgpu is a Rust WebGPU implementation/abstraction. Where Dawn is a C API close to the spec, wgpu provides a safe API matched to Rust’s type system and ownership. Its pieces are wgpu-core (validation/state management), wgpu-hal (low-level access to Vulkan/Metal/D3D12), naga (shader translation/validation), and wgpu-types. The same Rust code calls the browser’s navigator.gpu on the wasm32 target and Vulkan/Metal/D3D12 in a native build.

Same Rust code
  ├─ wasm32 → navigator.gpu
  └─ native → Vulkan / Metal / D3D12

The shader compilers Tint (WGSL → HLSL/MSL/SPIR-V) and Naga (WGSL/SPIR-V/GLSL ↔ various) are not app frameworks but foundational parts used inside WebGPU implementations or shader toolchains.

For WebGL raw APIs, there are two routes: Emscripten translating OpenGL ES to WebGL (porting SDL/GLFW/OpenGL ES code with relatively few changes), and Rust’s web-sys providing thin raw bindings like WebGl2RenderingContext. On top of the latter sit glow, three-d, rend3, the Bevy renderer, wgpu, and so on. Note that desktop OpenGL and WebGL are not identical: no immediate mode, no geometry/compute shaders, some unsupported texture formats, and constraints on synchronous APIs.

Runtime Comparison

ImplementationMain languageExecutionWASIComponent ModelPrimary use
WasmtimeRustJIT / AOTStrongMost advancedStandards, servers, embedding
WasmerRustJIT / AOT / multi-backendWASI + WASIXIncludes its own lineGeneral, browser, edge
WasmEdgeC++Interpreter / AOTStrongIn progressEdge, AI, OCI
wazeroGoCompiler / InterpreterSupportedLimitedGo embedding
WAMRCInterpreter / JIT / AOTSupportedLimitedEmbedded, IoT
wasm3CInterpreterBasicWeakUltra-small
ChicoryJavaInterpreter / in-JVMExpandingDevelopingPure-Java embedding
GraalWasmJava / GraalInterpreter / JITCheck neededLimitedGraalVM integration
V8C++JITNoneCore-focusedChrome, Node
SpiderMonkeyC++ / RustJITNoneCore-focusedFirefox
JavaScriptCoreC++JITNoneCore-focusedSafari

Arranged by “what each layer hides”:

LayerImplementationsWhat it hides
Core runtimeWasmtime / Wasmer / WasmEdgeBinary validation, JIT, memory, instances
Embedding SDKwazero / Chicory / Wasmtime APIHost-language integration
WASI implementationwasmtime-wasi / WASIX / WasmEdge WASIFiles, clocks, random, networking
Component toolingWIT / wit-bindgen / cargo-component / jcoCanonical ABI, type conversion
Browser bindingswasm-bindgen / web-sys / EmscriptenJS/Web API boundary
Plugin baseExtismABI, config, host functions
ServerlessSpinHTTP, events, deploy
Distributed executionwasmCloudCapabilities, providers, placement
Proxy extensionsProxy-WasmHTTP proxy ABI
GPU implementationDawn / wgpuD3D12 / Metal / Vulkan differences
Shader translationTint / NagaWGSL / HLSL / MSL / SPIR-V differences

How to Choose in Practice

  • Center on standard WASI/Component Model → Wasmtime + WASI Preview 2 + WIT + wit-bindgen + cargo-component + wasm-tools. Closest to the spec, minimal proprietary-ABI dependence.
  • Run POSIX-like existing apps → Wasmer + WASIX + Wasmer JS SDK/Runtime. But WASIX-dependent parts become a porting cost to other runtimes.
  • Add plugins to a Go app → wazero + custom host functions. If you also need multi-language support, Extism.
  • Embed into a Java app → Chicory to avoid native dependencies; GraalWasm on a GraalVM base.
  • Embedded/IoT → WAMR; wasm3 if a smaller, simpler interpreter suffices.
  • Connect Rust to Web APIs in the browser → wasm-bindgen + web-sys + js-sys.
  • Port a C/C++ app to the web → Emscripten + WasmFS + Wasm Workers + WebGL/WebGPU bindings.
  • Share GPU code between browser and native → Rust + wgpu + Naga. To get close to the WebGPU spec in C/C++, Dawn + webgpu.h + Tint.

Conclusion — Keep Raw APIs Behind an Adapter Layer

The WASM ecosystem has three broad streams.

  1. The standard-component stream — Wasmtime / WASI Preview 2 / Component Model / WIT / wit-bindgen / cargo-component. Evolving WASM from a “standalone binary” into a “typed software component.”
  2. The practical OS-compatibility stream — Wasmer / WASIX / Wasmer JS SDK / Wasmer Edge. Getting existing Unix apps into WASM quickly to run in browsers and at the edge.
  3. The domain-specialized stream — WasmEdge (edge/AI), wazero (Go), WAMR (embedded), Chicory (JVM), Spin (serverless), wasmCloud (distributed), Extism (plugins), Proxy-Wasm (network).

If you value long-term compatibility most, center on Core WASM, WASI Preview 2, the Component Model, and WIT, and confine runtime-specific features to an adapter layer. Wasmer’s WASIX and WasmEdge’s proprietary plugins are useful, but once the application body depends on them directly, you can no longer swap runtimes.

Application domain
  │  WIT interface

Runtime adapter
  ├─ Wasmtime adapter
  ├─ Wasmer adapter
  ├─ WasmEdge adapter
  └─ Browser adapter

Runtime-specific API

In short: use raw APIs only up to the adapter layer, and separate the application body with WIT (or your own typed interface). That is the basic design for safely using today’s WASM implementations. Next time (Part 3), we organize the “WASM languages” that run on top of these runtimes and tools — centered on MoonBit.

References