Download PDF version of this article PDF

WebAssembly: Yes, but for What?

The keys to a successful Wasm deployment

Andy Wingo

WebAssembly (Wasm) turns 10 this year, which, in software terms, just about brings it to the age of majority. It has been polished, prepared, explored, and deployed, but in the language of American speculative fiction author William Gibson, we are now as ever in the unevenly distributed future: WebAssembly has found a niche but not yet filled its habitable space. This article attempts to inventory the early Wasm wins and losses and identify winning (and losing!) patterns, and then goes further to extract commonalities between these patterns: What is it that makes for a successful Wasm deployment? These observations are then used to predict the future, suggesting new areas where Wasm will find purchase in the next two to three years.

 

Methodological Confessions

Before diving in, we need a plan. The problem with understanding Wasm is that it is not just a technology: It also has an associated fog of narrative, and we as technologists are the perfect victims of such storytelling. Wasm is catnip to the kind of person who can find beauty in a copy of Benjamin C. Pierce's Types and Programming Languages, with its clean, fully specified, mechanically verified formal semantics. At the same time, its heady fumes entrance the software architects of the world with the promise of a cheap, universal, Lego-like composition of systems from standard pieces.

Both audiences tend to turn their aesthetic predispositions into premonitions, mistaking what should be with what will be. In these 10 years, we have seen claims that WebAssembly will replace JavaScript, Docker, Linux, and several minor deities. How should you situate yourself in this landscape? What is real? How can you know what parts of Wasm are actually valuable in the here and now?

To answer these questions, we adopt an aesthetically unsatisfying device: the market. Which Wasm deployments have stuck around? Where are people happily using Wasm? Which deployments failed? These are all real indicators by people with skin in the game.

The market can also be used to assess bets on the future: Which Wasm niches are the target of current investments? The future is still in the making, so this criterion necessarily reincorporates narrative into the evaluation, but where many companies are all pushing in the same direction, this must be taken as a sign of what is to come.

 

Wasm and the Web: Wins and Losses

You would think Wasm would be a clear success on the web. After all, it's in the name. But WebAssembly on the web is a mixed bag.

To take it from the beginning, Wasm grew out of asm.js, a restricted, statically typeable subset of JS (JavaScript). The emscripten toolchain could compile C and C++ programs to this dialect of JS, and browsers such as Firefox built dedicated compilation pipelines to directly translate such JS programs to native code. The first big splash for asm.js was a set of 3D games based on Unreal Engine 4, which had experimental support for targeting the web via emscripten; this toolchain was later modified to emit Wasm instead of JS for better performance and lower startup latency.

While these early demos were quite striking, they did not translate into massive adoption of Wasm by the gaming industry. The current version of Unreal does not even include a Wasm back end anymore. There are games on the web that use it, but Wasm is in no way an industry focus.

In other domains, this approach of taking a desktop application and retargeting it to the web via Wasm has had some success. Adobe notably provides a web port of its Photoshop image-editing software, as well as other applications in its portfolio: a win for Wasm. The process has not been without hiccups, however. Adobe engineers had to be deeply involved in WebAssembly toolchain development in order to reach acceptable performance, and start-up time on first visit remains a challenge.

On the face of it, the case of the Figma collaborative interaction design web application is similar; Figma is also a graphic-design product written in C++ that targets the web. Unlike Photoshop, however, Figma is a web-only product, so you can expect that if, say, TypeScript works better for Figma's needs, then, over time, new features will be written in TypeScript instead. Indeed, looking at the breakdown of Figma over time would seem to reveal that is what has occurred; Wasm is still used for some per-pixel operations, where its greater control over data layout makes it a win, but the application is composed of relatively more JS than Wasm.

As a component part of a web page, Wasm has had more lasting success. Wasm-compiled SQLite is so successful that it actually replaced a part of the web platform, causing Chrome to remove WebSQL entirely. The Perfetto tracing UI uses Wasm to parse hundreds of megabytes of JSON (JavaScript Object Notation) data into packed indexable trace data, with SQL query support, though the UI itself is still implemented in TypeScript.

These examples of Wasm success all have C++ or Rust as their source language, and that is no accident. Though Wasm is nominally independent of the language in which an application is written, in practice, it has historically been most appropriate for low-level languages. This is partly because these languages use the LLVM (low-level virtual machine)-based toolchain that compiles to Wasm, which has seen much more investment than any other compiler.

Still, shipping C++ to the browser via Wasm has its limits. Web developers prefer JavaScript or TypeScript or some other web-first language, and if you are building an organization to create a web experience, generally speaking, you would rather pull from the deeper pool of JS developers than the more rarified C++/Wasm skill set. But there also are technical reasons that JS can work better than Wasm on the web.

One of these reasons is a known limitation of early versions of Wasm: GC (garbage collection). Popular languages such as Python, Ruby, and Java have automatic memory management in the form of a garbage collector, but Wasm's only way to represent data was as bytes in a big array (linear memory). You could compile in a simple garbage collector that runs over that linear memory, but it would not be efficient; for example, Wasm still has no shared-memory multithreading, so parallel and concurrent GC are off the table. Implementers of languages needing GC instead preferred to generate JS, which could use the world-class memory manager built into the browser.

This state of affairs has now changed with new developments in the Wasm platform. Since last year, all browsers have extended their Wasm implementations to allow source languages to use the Wasm runtime's own garbage collector. In theory, this should allow more efficient interaction with browser capabilities such as the DOM (Document Object Model) and WebGPU. It is still early, so a market-based assessment of the long-term success of this new WasmGC extension is not yet possible, but a survey of the field can shed some light.

From a toolchain perspective, it seems that most compilers that target JS have been moving toward Wasm instead, as is the case, for example, with Scala or OCaml. There are new toolchains that skipped JS completely, such as the Hoot Scheme compiler, or the Graal ahead-of-time Java compiler. Finally, there are a couple of application development frameworks that can now target the web in addition to mobile, thanks to WasmGC (Kotlin/Compose and Dart/Flutter).

As far as applications go, the most prominent WasmGC deployment is Google Sheets. This spreadsheet app used to evaluate per-cell spreadsheet formulae using Java code, compiled to JS; Sheets has now fully switched to WasmGC instead. There are precious few other prominent examples, however.

The social factors favoring JavaScript and TypeScript for web development will persist for the foreseeable future, but the technical foundations are now laid for other language communities to target the web on more-or-less equal footing.

 

Wasm without the Web

It's in the name, yes, but WebAssembly doesn't actually have anything to do with the web. The core specification stands alone. There is a companion specification for how to instantiate a Wasm program from JavaScript and how to pass control and data back and forth between JS and Wasm, but it is more of a language binding than an essential part of Wasm.

Though browser vendors drove the initial development of Wasm, since then its set of stakeholders has widened to include nonbrowser use cases, which currently breaks down into three areas:

• Ad-hoc composition/plug-ins

• Lightweight virtualization

• The component model, a sweeping new vision of how to compose systems from pieces

As an example of ad-hoc composition, consider the RLBox tool, used by Firefox to link to third-party libraries written in unsafe C or C++, such as the Expat XML parser. RLBox compiles these libraries to Wasm, then compiles the Wasm version back to C. Using Wasm as an intermediate target isolates Expat's use of memory from the rest of Firefox, preventing a class of security vulnerabilities when there is a bug in Expat. Compiling the Wasm back to C allows Firefox to build and link against sandboxed Expat directly, without needing a Wasm runtime and with low overhead. Granted, this is a specific application, but it hints at the expressive possibilities afforded by the Wasm abstraction.

Another ad-hoc use case is wherever you might want extensibility via a little virtual machine. For example, the TrueType font format has long embedded a bespoke VM used in font shaping; the HarfBuzz shaping library extends this idea by allowing OpenType fonts to embed more expressive shapers implemented in WebAssembly. In this domain, you quickly run into issues of how to define the interface between host (application) and guest (plug-in/extension); the ad-hoc approach tends to be at a lower level than you would like, as the set of operand types to an exported function is limited to integers and floating-point numbers, at least for linear-memory Wasm.

Lightweight virtualization also has an ad-hoc character but a broader scope in that it targets whole applications rather than their parts.

For example, WALI (WebAssembly Linux Interface) is a toolchain-driven virtualization solution that compiles C, C++, and Rust applications, and applications to Wasm. Its libc redirects system calls to an ad-hoc set of imported Wasm functions, and its runtime hypervisor lowers guest Wasm code to native code, linking in implementations for the imported system calls. Though the Wizard compiler used in WALI produces less-optimized binaries than a native toolchain targeting Docker, by avoiding the fixed costs of container instantiation, it has lower total overhead for short-running tasks (approximately 0.5 seconds or less). Granted, WALI is still at the research stage, but it indicates the shape of the possible.

In a similar vein, there are a number of IoT (Internet-of-things) projects whose firmware runs Wasm instead of native binaries, allowing for in-the-field upgrades with the robustness guarantees offered by Wasm's memory separation. Many of these use the WAMR (WebAssembly Micro Runtime), but as the effort needed to get a basic Wasm implementation off the ground is not high, there is inevitably some fragmentation in this area.

Finally, there is the component model. Let's approach the problem by observing that in some ways, Wasm is too expressive on its own. It is missing the equivalent of an ABI (application binary interface) that indicates how to map high-level concepts such as records or strings to the low-level scalars defined by core Wasm. Such an ABI would also have to deal with ownership and life cycles. On what common ground and with what machinery can a string be transmitted from the linear memory of one Wasm module to the linear memory of another? How do modules get instantiated, activated, and shut down? At some point you have to choose a set of abstractions and restrictions that allow independently developed pieces to work together in a system.

Such are the goals of the component model. It defines a higher-level concept of a component, building on Wasm modules, but with richer types, a defined life cycle, and a specific model of shared-nothing composition and communication. The component model working group also develops a set of open source toolchains for linking and running Wasm components. The aim is to build systems with the fine-grained interoperation of shared libraries, specified in a way to allow isolation but without requiring the overhead of an operating system process or a container.

The component model supports plug-in-like use cases. For example, Shopify allows its customers to provide Wasm code that can compute discounts based on the contents of a user's shopping cart. It also supplies an SDK (software development kit) that lets users provide source code and have the platform compile it on their behalf.

 

Cloud, Cloud, Cloud

The most intense area of component-model work, however, has by far been "in the cloud;" somewhere in the union of Kubernetes and edge compute, Microsoft, Fastly, Fermyon, Cosmonic, and F5 all have component-model?based offerings that they would like you to try. We aren't here to sell their products but rather to understand why they would be interested in the component model as a technology. The answer has nuances, but their main differentiator is that Wasm starts fast.

To appreciate this point, imagine a web service that spins up a virtual machine to handle each incoming HTTP request. It would work, but it would be slow; for example, Amazon's presumably state-of-the-art Firecracker virtualization host takes about 100ms to start a VM, and that is before handing off control to the actual request handler. Simple math says that would limit throughput to well under 10 queries per second per core.

For this reason, many FaaS (function-as-a-service) platforms keep their service instances alive for a time, reusing them for multiple requests; still, when scaling up, a FaaS platform will need to spin up more instances, which will incur that 100ms-or-more cold-start latency penalty. This leads to higher overall tail latencies just as demand spikes, which is a recipe for cascading failures. To counter this failure mode, some services will be tempted to keep instances in reserve, but that is expensive and wasteful. What's more, reserving idle instances rules out the use of edge compute. The cost of this is bad enough in the core datacenter, but the larger number of edge nodes makes the cost impossible to bear.

Wasm is the technology that can enable useful edge compute by reducing cold-start times to a millisecond or less—just the time to page in an instance's linear memory and call its HTTP handler, if that's what you're building. A number of optimizations can be applied to Wasm modules, such as resuming snapshots taken after initialization. Some platforms use virtual memory tricks to make hot start even faster, on the order of tens of microseconds. And of course, the advantages of fast cold start are not limited to edge deployment.

The component model has enabled innovation in the cloud space by offering a shared vocabulary, a shared set of expectations, and a shared open source ecosystem that organizations can reuse and build on. Still, Wasm on the component-model cloud remains a frontier technology—not yet a clear win, but it certainly has a chance.

Patterns of Success

Let's summarize. WebAssembly does well at retargeting big C and C++ desktop applications to the web. It also wins at getting C and C++ components onto the web—for example, file format handlers, per-pixel processors, or embedded libraries such as SQLite. It hasn't won in games, though—perhaps there are other industry factors at play. For user-facing web interfaces, JavaScript and TypeScript are still better suited than Wasm, or rather C/C++/Rust compiled to Wasm; for Kotlin, Java, and other languages that use Wasm's garbage collector, it is still early.

For plug-in and extension use cases, Wasm can be a good option if the host needs isolation from the guest, as is the case with Firefox and Expat. Wasm's minimalism, however, means that even concepts such as strings or records need to be represented as scalar numbers or as bytes in an array. To bridge the semantic gap between API and ABI, you need conventions, and it seems the component model is the most likely project to succeed in this space.

The cloud Wasm space is a bit of a Wild West, with multiple players scrambling to pitch their visions of how to capture the value provided by Wasm's fast cold-start characteristics. Still, for those users who are prepared to rearchitect their services to suit the shape of cheap, transient, ephemeral Wasm compute, there may indeed be "gold in them thar hills."

More generally, Wasm is an abstraction boundary that offers an intermediate point between isolation and rapprochement; a host can have robust memory safety guarantees with respect to the behavior of guests, but without needing a process boundary in between. Its interfaces are legible to tools, allowing a range of early and late binding strategies to be applied to the same artifact: for example, how system-call interception can be programmatically inserted into a WALI-compiled application with no dynamic dispatch overhead, or how separately compiled components can be statically linked and optimized together. And, there is fast cold start.

 

The Future

What should we build next? Let's put aside the problem of retargeting legacy C++ code, as there is only so much C++ to go around. Considering the patterns of Wasm wins, you can extract a pattern-pattern: Wasm works well where isolation is needed between different program parts written by different people. (If two parts are the responsibility of the same team, they generally don't need isolation.) This applies to some plug-ins, to virtualization—to any sort of system/user-space divide. The more critical the host, the more important isolation becomes. Firefox uses RLBox on Expat because web browsers have high security needs, but many users can't justify the investment.

Therefore, one way to look for a new Wasm habitat is to identify existing extensible security-critical programs. The operating system kernel is an obvious candidate; you can only think that a Bluetooth driver should be isolated from other kernel data structures. So, writing kernel drivers would be a good use of Wasm. Of course, the Linux kernel already has eBPF (extended Berkeley Packet Filter), but it is not capable enough to write full drivers, and if Wasm made it into the kernel, it could logically take over many eBPF use cases.

Or to take another security boundary, there could even be a Wasm operating system in which the Wasm module replaces the process. This could have MMU (memory management unit) advantages, as different Wasm modules are prevented by construction from accessing each other's memories; there need be no system call or task-switching overhead at all. This is a research matter, obviously, but it's compelling all the same.

A final example might be our strange new AI future in which we would like for untrusted, highly fallible third parties to perform tasks on our behalf on sensitive personal data. As a lightweight compute platform that is limited to using only the capabilities explicitly granted to it, Wasm can allow for confident and confidential computing, even for untrusted code.

 

Andy Wingo is a 20-year industry veteran with a specialization in compilers. He is a contributor to the JavaScript and WebAssembly implementations in Firefox and Chrome, co-maintains the widely deployed Guile implementation of Scheme, and can generally be found wherever there is an intermediate representation that needs munging. He lives in France with his family, in the greater Geneva area.

 

Copyright © 2025 held by owner/author. Publication rights licensed to ACM.

acmqueue

Originally published in Queue vol. 23, no. 3
Comment on this article in the ACM Digital Library





More related articles:

Shylaja Nukala, Vivek Rau - Why SRE Documents Matter
SRE (site reliability engineering) is a job function, a mindset, and a set of engineering approaches for making web products and services run reliably. SREs operate at the intersection of software development and systems engineering to solve operational problems and engineer solutions to design, build, and run large-scale distributed systems scalably, reliably, and efficiently. A mature SRE team likely has well-defined bodies of documentation associated with many SRE functions.


Taylor Savage - Componentizing the Web
There is no task in software engineering today quite as herculean as web development. A typical specification for a web application might read: The app must work across a wide variety of browsers. It must run animations at 60 fps. It must be immediately responsive to touch. It must conform to a specific set of design principles and specs. It must work on just about every screen size imaginable, from TVs and 30-inch monitors to mobile phones and watch faces. It must be well-engineered and maintainable in the long term.


Arie van Deursen - Beyond Page Objects: Testing Web Applications with State Objects
End-to-end testing of Web applications typically involves tricky interactions with Web pages by means of a framework such as Selenium WebDriver. The recommended method for hiding such Web-page intricacies is to use page objects, but there are questions to answer first: Which page objects should you create when testing Web applications? What actions should you include in a page object? Which test scenarios should you specify, given your page objects?


Rich Harris - Dismantling the Barriers to Entry
A war is being waged in the world of web development. On one side is a vanguard of toolmakers and tool users, who thrive on the destruction of bad old ideas ("old," in this milieu, meaning anything that debuted on Hacker News more than a month ago) and raucous debates about transpilers and suchlike.





© ACM, Inc. All Rights Reserved.