WebAssembly has learned a great deal since 2017. It now has shared memories, SIMD, exception handling, tail calls, 64-bit memories and garbage collection.1 The result is a much wider range of languages able to target Wasm while keeping the abstractions of their normal runtimes.

Open a browser, though, and try to write console.log("hello").

Nine years of language progress suddenly looks slightly absurd because WebAssembly cannot simply grab console, hand it a string and carry on; it needs a JavaScript bridge with an imported function, access to Wasm memory and decoding into a JavaScript string before the Web API call can happen.1

Mozilla calls this being a second-class language on the Web. The problem is not that Wasm is too slow or too primitive. JavaScript keeps two privileges the browser has not given Wasm directly: natural code loading and direct use of Web APIs.1

Those two gaps in integration are enough to leave a format designed for the Web with the status of a guest on the platform that hosts it.

Two privileges

Loading JavaScript through a script element or an import is part of the ordinary model of the Web: the browser fetches the file, places it in the module graph and takes responsibility for resolution and execution.

For stable Wasm today, more of that work remains in application code: fetch the binary, prepare its imports, call WebAssembly.instantiateStreaming() (or a related API), and obtain the instance before using any of its exports.17

Work on the first gap has reached phase 3 of the WebAssembly proposal process. ES Module Integration specifies both import of Wasm from JavaScript and loading of a module through <script type="module">.23

Mozilla implemented the first half of that route in Firefox during 2026, through source phase imports that let ESM retrieve a WebAssembly.Module.5 Work on the evaluation/instance phase (the part responsible for linking and instantiation) remains on a separate implementation track.6

That distinction matters, because seeing “Wasm ESM” land in a browser does not make a .wasm file behave everywhere like an executable .js module.

And even when loading becomes boring, the larger problem remains.

JS glue

Most of the browser surface, from document and fetch to Canvas, WebGPU and Web Audio, grew around the object model of JavaScript.

Wasm can reach all of them, and heavy applications have done so for years, but access to that surface usually depends on bindings: a layer responsible for translation of values at the boundary between the two worlds.1

Strings make the problem visible. The representation of a string in linear Wasm memory is a run of bytes at an address; on the JavaScript side, console.log expects an engine string. A binding therefore has to read the address and length, create a view of memory, decode the bytes and make the call.1

The return trip has its own protocol: conversion of a JavaScript string may require a call to an allocator inside the Wasm module, encoding to bytes, a write into memory and a decision about ownership of the allocation.1

Tooling hides most of this machinery. Emscripten has embind; on the Rust side, wasm-bindgen is common, because few developers want to hand-write a bridge for every property of the DOM.1

Hiding the glue does not remove the boundary.

45 percent

Mozilla points to an older experiment that remains useful for one reason: it isolates the cost of the boundary.

In 2020, the team used a Rust version of TodoMVC built on the experimental Dodrio framework. One build asked JavaScript to apply each DOM operation; the other relied on an experimental direct binding, moving execution of those DOM operations out of the JS glue layer.1

In that experiment, removal of the JavaScript glue cut the measured time for application of the DOM changes by 45 percent.1

Mozilla chart comparing DOM update time with JavaScript glue and with a direct WebAssembly binding
In the Dodrio experiment cited by Mozilla, removing JavaScript glue reduced the measured phase by 45%. The experiment dates from 2020 and is not a universal 2026 Wasm benchmark.Ryan Hunt / Mozilla Hacks

To be sure, the result says nothing like “WebAssembly is twice as fast as JavaScript”. It comes from one workload on one architecture and generation of browser engine; Mozilla uses it as evidence of cost at the boundary, even when execution on the Wasm side is fast.1

It also explains the odd relationship creative and technical software has with Wasm: as an application touches more of Canvas, DOM, files, audio, GPU APIs or events, its fast computation spends more time communicating with a platform whose native vocabulary remains JavaScript-first.

The wrong gap

It is tempting to assume WebAssembly remains niche because too many capabilities are missing from its low-level language.

That was far more convincing in its early years. Since those early releases, the core has accumulated SIMD, exceptions, reference types, multiple returns, tail calls, GC, memory64 and many smaller extensions.1 Important work remains, including stack switching and better threading, but Mozilla's point is that the language is mature enough to shift attention toward Web integration.1

That change in diagnosis matters. Addition of another Wasm instruction will not make MDN readable from Rust, remove wasm-bindgen or turn document.createElement into a native API of every source language.

The bottleneck has become social and architectural as much as technical.

Maintainers of rustc or LLVM generally want to emit a standardized artifact, not maintain a second implicit backend for generation of browser-specific JavaScript. Responsibility for that Web layer therefore falls on third-party toolchain distributions and integration projects in the Rust, C++, C# and other ecosystems.1

Web documentation also remains, reasonably, JavaScript-first, which means a developer using another language often has to understand the JS form of a Web API before learning the translation exposed by the binding layer of that ecosystem.1

Components

Mozilla's preferred direction is the WebAssembly Component Model.14

A component goes beyond packaging of several .wasm files. It describes a high-level interface around a Wasm program, using WIT as the language of that interface, a Canonical ABI, and rules for exchange of richer values across languages and runtimes.4

That is the missing layer between production of Wasm by a compiler and use of the Web by the resulting program.

In Mozilla's imagined browser, WIT interfaces could be derived from the WebIDL definitions already used for Web APIs. A Rust program might import std:web/console, call a function with an ordinary string of the language, and rely on the browser to provide the native binding.1

The component could load as a module, and Mozilla shows the ideal syntax directly as <script type="module" src="component.wasm">, with no companion JavaScript file.1

Diagram showing WebAssembly integration glue generated by each language today, compared with browser-provided Component Model bindings.The proposed gain is organizational as much as technical: stop rebuilding Web integration in JavaScript inside every toolchain and share a standardized interface supplied by the platform. IRZ synthesis from Mozilla and the WebAssembly Component Model

If that works, the change is larger than deleting a few lines of JS. A standard compiler could emit a self-contained artifact with a portable interface; the browser could link it; multiple languages could share the same Web API descriptions; and the boundary would stop being a different integration project in every ecosystem.14

Not yet

That attractive future remains several standards steps away from becoming an ordinary Firefox feature.

Mozilla explicitly says that interactions between Components and the Web platform have not been fully designed, the tooling is under active development and its example std:web/console interface does not exist today.1

The Component Model is a real WebAssembly Community Group effort, and its repository now publishes WASI Developer Preview milestones covering WIT, linking, resources and newer asynchronous/concurrent capabilities.4 That work already demonstrates use of the architecture outside browsers, while native binding of Components to the Web platform remains part of Mozilla's proposed future.

Components can run in browsers today through Jco and a polyfill, precisely because the target native platform integration has not landed.1

Even the smaller ESM effort shows the gap between progress and completion. Mozilla landed source-phase Wasm support in Firefox during 2026 while evaluation/instance phase support remains separate work.56 The ESM proposal itself remains at phase 3.23

The <script> in this story is therefore more than a distant metaphor: the proposal explicitly wants Wasm to gain that ordinary Web syntax, although implementation across browsers remains unfinished.

The real gain

WebAssembly has already proved that a page can host 3D engines, codecs, creative software, scientific libraries and code originally built for other platforms.

The next leap looks less impressive on a benchmark slide: it is about making those programs stop asking JavaScript for permission to exist as Web programs.

That could remove part of the build tooling, runtime glue and boundary cost. The deeper change may be educational: developers of Rust, C++, C# or another language could learn Web APIs as APIs of their platform, rather than first learning the JavaScript incarnation and only afterward the translation chosen by their ecosystem.1

Since 2017, Wasm has moved toward native capabilities one feature at a time. In 2026 Mozilla is asking almost the opposite question: perhaps the next move is toward the Web itself, this time as a matter of status rather than speed.