Had it been today, Chrome would have just pushed NaCl and PNaCl no matter what, and then everyone would complain why Safari and Firefox aren't keeping up with "Web" standards.
I really thought, for a time, that we'd be doing everything in the browser. And in a way that's increasingly true, but it all just feels worse than ever. I like WASM and I want to like WASM but the rate of maturity within the ecosystem is incredibly abysmal.
What's worse is that we should all be running our untrustworthy AI tools and their outputs in precisely such a sandbox, and companies are selling the reverse: hosted sandboxes, hosted JS-based VMs.
I guess that was always the problem: there was never any money in a client-side sandbox.
"Our submission is in TALx86, a strongly typed functional language that encourages an explicit continuation-passing style and supports mutually recursive modules. We were encouraged to use this language when we learned that the competition would allow us to run our program on an interpreter implemented in hardware. We are grateful to the Intel Corporation for developing this interpreter."
Here’s an example of Sudoku running in WebAssembly (it was vibe coded in Zig) and then rendered to canvas. The interface between the wasm module and the browser is function calls for keyboard and mouse events, and then another that renders to a pixel buffer to copy to the canvas.
And this approach also works for simple forms, such as a URL input that gets turned into a QR code. Again the interface is simple, here converted a URL into SVG markup. As you type in the input we call the WebAssembly render function again.
The QR example works fine though
I'm using a brand-new MacBook Pro with a high-end M5 processor, and this site is extremely unresponsive for me. Huge latency between clicking and getting feedback.
It also breaks accessibility.
The QR code use case seems far more reasonable to me, you're generating a static image.
WASM is called WEB assembly but it can't access the Web API's without paying tax to the JS tyrant in between.
https://hacks.mozilla.org/2026/02/making-webassembly-a-first...
In practice, whenever you need more than a singlethreaded app with http/serial port, the "run everywhere" breaks.
Don't get me wrong I love WASM but we're not there yet
Wasm components & wasi have a lot of promise here. Until now though browsers have been ignoring all this; Firefox just started taking a more active interest. https://hacks.mozilla.org/2026/02/making-webassembly-a-first...
I feel like some of the Google-sourced standards are the laziest, least-webby ones out there. There are some good ones that come from the Chrome team, but man the real stinkers are _always_ a lazy Google engineer trying to ship a half-baked clone of something native in the browser because they need it for something or other internally.
The original NaCl was a 'validated subset' of native CPU machine code (e.g. actual x86 machine code with some instructions and instruction sequences disallowed which would allow to escape the sandbox).
The next iteration was P(ortable)-NaCl which replaced the native machine code with a subset of LLVM bitcode, which was then compiled at load time. Unfortunately with this step NaCl lost most of its advantages. Startup time was atrocious because it was basically the second half of the LLVM compilation pipeline (from LLVM-IR to machine code). LLVM-IR also isn't actually great as CPU-agnostic bytecode.
WASM was designed from the ground up as CPU agnostic bytecode that's also much easier and faster to validate.
The only major advantage of PNaCl vs early WASM was that PNaCl supported shared-memory threading right from the start (this is still knee-capped in WASM because of the COOP/COEP response header requirement).
...apart from Emscripten => asm.js => WASM, and Google's NaCl/PNaCl there was also a system by Adobe (Flascc/Alchemy(?) I forgot all the names this went through) to compile C and C++ code into Adobe Flash bytecode.
I have an ancient blogpost from 2012 which compares the three (and where I have been flabbergasted by how well Emscripten actually worked - and this was even before asm.js - the linked demo is unfortunately no longer up):
Yep, that's the name. There was a brief period in late 2000s when Adobe was pushing hard to make Flash embedded into web ecosystem. They made Air as a way to package Web or Flash code into a desktop app. Essentially it was Electron-before-Electron. Alchemy was a part of this grand plan to be able to integrate existing native libraries with Flash code. The plan was like you said to compile to Flash bytecode, and AFAIK it never went further than a tech demo.
This whole ecosystem turned into a slow train-wreck over approximately 5-year period. Adobe really saw themselves as future stewards of web technology. They donated their ActionScript VM and a JIT to Mozilla and hoped that Firefox would become the first browser with fast JavaScript engine. Google developed Chrome and V8 in secret and managed to release their fast browser early. Microsoft and Yahoo sabotaged adoption of ActionScript dialect as at the next JavaScript. And at the same time Apple went fully anti-plugins, and with the rise of iPhone both Flash and silverlight died off.
Years later Java folks tried to build their own version of Alchemy as part of GraalVM. The project was called Sulong and was using Graal to execute and JIT LLMV bitcode. TruffleRuby was supposed to be a primary early beneficiary to be able to compile and run Ruby native extensions. This was during the period of a race between several JIT solutions in hopes to become "the next Ruby", and Truffle team (along with IBM's OMR) lost the race first to MJIT and then to YJIT. Graal itself seems to loosing steam, because their multi-language VM never got enough adoption among Java, Node, or Ruby people, and the VM itself tended to use too much RAM in era when RAM became premium in the cloud.
Presumably that is because PNaCl predated spectre (?)
Crossbridge was its third and most recent name.
Pepper.js is another interesting project from around this time that didn't pan out.
Pepper was a pun on Native Client (since NaCl = salt). Pepper Plugin API (PPAPI) was Google's more secure version of NPAPI (Netscape Plugin API). Flash Player was essentially the only thing using NPAPI/PPAPI by the end of its life.
Out of curiosity, does that mean that NaCL (without P) only ran on x86? Or were there different subsets for different architectures?
The DevOps infrastructure Kubernetes runbook AI inference router API people (DIK-AROUnders for short) always want an abstract technical solution that increases both their budget and their distance from the end user's actual application. Like the more money they get to dick around with meaningless technical cathedrals, the better. They're only bent out of shape that they couldn't parlay that into a sweet crypto scheme. In the real world, the line between what users actually want and what DIK-AROUnders call inauthentic activity is quite blurred.
To me, the fact that AI agents can browse websites and make payments and read my email and pretend to be me or other people is a huge part of their value proposition. People want to get out of the sandbox! There are many meanings to the words security and privacy.
IIRC a big reason it didn't end up working was because NaCl was such a "big" technology and asm.js such a "small" one that asm.js was able to reach production-ready first despite starting work several years later.
(I will note that Apple seems to have upped WebKit investment this decade since their regulatory problems started in earnest - so it's possible this would end differently today)
Users were migrating to us _from_ desktop applications. Collaboration was the key differentiator, but a less well known reason was that improved performance, including but not limited to the support of large design systems, was also a commonly cited reason among paying customers for migrating to Figma.
Desktop or collaborative is a false dichotomy. Desktop or performance is too.
I get why you did what you did. It makes sense. But don’t think there aren’t people out here who HATE everything being shoved on the web with no desktop option.
No, electron and PWA don’t count.
Jokes aside, Figma's stack is super inspiring, and y'all's articles on sync engines heavily inspired my work on LegendKeeper. I appreciate the work you do!
https://www.destroyallsoftware.com/talks/the-birth-and-death...
(And to those who haven't encountered this before, I strongly recommend a watch. It may be the greatest tech talk of all time, for certain values of greatest.)
Its IR design is horrendeous.
And at some point we're going to have a period or war and our psychological attachments to old programming paradigms will be released so that we can move on to a more advanced way of doing things (but that won't stop your bank from running YavaScript for at least another 85 years).
12 years on, it’s shocking how much of his fiction became reality.
[0] https://www.destroyallsoftware.com/talks/the-birth-and-death...
https://webglinsights.github.io/
It was fun to see the rise of asm.js, which was a precursor to Web Assembly. Some of the early demos were so cool to see; Unreal Engine running in the browser. :) Bitter sweet to see the sun set here, but it did lead to much better things.
(compiling legacy code with legacy versions of Emscripten is quite frustrating, almost as bad as updating your JS code to be compatible with accumulated changes in the Emscripten ABI)
Nowadays most browser JavaScript VMs converged to very similar designs and optimizations, so even without Odin asm.js code would run pretty fast anyway.
But wasm is too isolated from javascript. From my limited use of it, I was considering trying to compile to asmjs instead.
But I wasn't sure that emscripten still fully supported it.
You can't call most web apis from wasm.
But more important for what i was trying to do, you can't zero copy buffers from js to wasm.
Everything is a trade off. The isolation is a good thing, but also a bad thing.
asmjs is going to be strictly more limited in interacting with JS than wasm. You're basically limited to simple number values and array buffers. Whereas wasm now a days has GC types and can hold onto JS value using externref.
> But more important for what i was trying to do, you can't zero copy buffers from js to wasm
I'm pretty sure you can't do that with asmjs either. There is a proposal for zero-copy buffers with wasm: https://github.com/WebAssembly/memory-control/blob/main/prop...
Had WASM not been adopted we would have SIMD in JS ( probably via asm.js) by now. Because we didn't, JS just cannot compete with WASM in many computationally heavy workflows. We'd also have general purpose JS to Asm.js compilation, with few API restrictions, making writing it much easier.
WASM is that evolution of (strict mode) asm.js. The two really aren't all that different from what they can and can't do.
But since asm.js is just (a subset of) javascript, I assumed I could just pass ArrayBuffers around.
With wasm, I could pass a Uint8Array out of it. If I wanted to pass it in, I had to call malloc from the javascript side to allocate in the wasm heap. But since I already had an arraybuffer (from a file upload), that meant an extra copy.
> You can't call most web apis from wasm.
You can't from strict asm.js either, since it only supports numbers (no JS strings or objects) and manages the C heap in an ArrayBuffer object just like WASM.
> But more important for what i was trying to do, you can't zero copy buffers from js to wasm.
Same problem as above, you need to call out into "real" Javascript from asm.js and you can't map other ArrayBuffers directly into the 'asm.js heap' either, a copy is needed. The "Javascript FFI" really isn't much different between asm.js and WASM.
AFAIK as soon as you'd start mixing idiomatic JS and asm.js, you lose the "special sauce", you only got the special asm.js treatment in browsers when putting a "use asm" at the top of a source file, and that would prevent using regular JS features in the same file.
> An asm.js module can take up to three optional parameters, providing access to external JavaScript code and data:
> - a standard library object, providing access to a limited subset of the JavaScript standard libraries;
> - a foreign function interface (FFI), providing access to custom external JavaScript functions; and
> - a heap buffer, providing a single ArrayBuffer to act as the asm.js heap.
From http://asmjs.org/spec/latest/#introduction:~:text=External%2...
In asm.js you have to treat JS functions and objects as special extern values, just like in WASM.
asm.js - when validated and optimized - is closer to WASM serialized to a JS-like syntax than actual JS.
this was such a crazy project. remember when we compiled our c++ to wasm over 10 years ago, wait, this works?! web seemed to move so fast then.