Why I use WebAssembly
20 points by runxiyu
20 points by runxiyu
I like the idea of WebAssembly, but I don’t see how it’s meaningfully better as an “engine of application state” than JavaScript except in certain niche cases. The article mentions “you already have a desktop app and you want to ship a web version” but I’m very skeptical that even that case is a good one for WASM–if you’re going to have to port everything that touches the platform API (e.g., the filesystem, the platform UI libraries, etc) to the web, are you really better off using WASM (which has to pass through a JavaScript shim to use the web APIs anyway) than rewriting in TypeScript or similar? Maybe the answer is “yes” if your application has a lot of client-side code that is not UI logic (for example, Figma or AutoCAD) but those seem to be pretty rare as far as I can tell.
Similarly, I’m curious about WASM as a container replacement, but I don’t buy the “if WASM existed in 2012 we wouldn’t have needed Docker” argument if only because WASM does not solve for packaging an entire system, which means it’s only useful for apps that can be packaged as a single WASM artifact and I don’t think that limited vision was going to be forceful enough to change the industry the way containers did (for those of us who were around back then, it was a huge shift to get people to think in terms of containers even with the promise that they could package their existing apps into container images–I can’t imagine the professionals of 2012 wrapping their heads around a new orchestration model that wouldn’t accommodate the apps they had to support at the time).
Even as a container replacement today, I’m not quite sure what it offers me. It seems like it offers a simpler sandboxing model, but one which imposes a larger runtime performance/efficiency cost than that of containers (someone please correct me if I’m being too charitable toward containers). I’ve heard claims that it also reduces the size of the artifacts, but container images can be pretty tiny–I’ve shipped 2MB images before. I’m really not sure why I would prefer WASM over containers, even as someone who prefers Go which seems like it should be an almost ideal language for WASM?
The article mentions “you already have a desktop app and you want to ship a web version” but I’m very skeptical that even that case is a good one for WASM […]
It depends on the application, but for cases beyond simple CRUD data entry there can be a lot of non-UI logic involved. Assuming the existing source code is well-factored enough to keep OS-specific I/O away from the business logic, WebAssembly can let you reuse the existing code without worries about introducing new bugs in a rewrite between very different languages.
WebAssembly is also applicable to more places than the web, it can be used a sort of architecture-independent shared library when you don’t need the full performance of native code. For example last year I wanted to add xz
decoding to https://github.com/bazel-contrib/publish-to-bcr, but there was no way in hell I was going to write an LZMA decoder in JavaScript. Being able to compile liblzma
into a WebAssembly blob let me reuse the existing reference library (in C) without having to care about which platform(s) the code might run on.
Implementation PR: https://github.com/bazel-contrib/publish-to-bcr/pull/173
Amusingly there was a bug in that PR, but it was on the JavaScript side: https://github.com/bazel-contrib/publish-to-bcr/pull/178 – pretty sure if I had actually attempted to write lzma.ts
it would have been an absolute disaster.
Similarly, I’m curious about WASM as a container replacement, but I don’t buy the “if WASM existed in 2012 we wouldn’t have needed Docker” argument
Advocating for WebAssembly as a replacement for Docker (etc) has always struck me as a category error. You could imagine using WebAssembly (plus WASI, or some similar POSIX-ish API) to produce platform-independent binaries, and I think there might be value in that, but you still need a format to package the 50 MiB of binaries along with the 5000 MiB of static assets that those binaries load at runtime.
The value of container images has always been as a more efficient way to distribute chroot filesystem tarballs, which has no relation to the instruction set. If a single-file architecture-neutral binary was sufficient then people wouldn’t need container images to deploy Java applications.
XZ is a good niche because it just operates on byte strings, but for complex data structures, WASM will be painful. You will also have to take care to manage memory carefully so JacaScript and your WASM application don’t try to free the same memory. The costs are a lot higher than people think, even for a well-factored application code base.
The value of container images has always been as a more efficient way to distribute chroot filesystem tarballs, which has no relation to the instruction set. If a single-file architecture-neutral binary was sufficient then people wouldn’t need container images to deploy Java applications.
That’s my point—an instruction set doesn’t obviate Docker because Docker is much more than an instruction set (indeed, Docker is effectively agnostic to instruction sets).
XZ is a good niche because it just operates on byte strings, but for complex data structures, WASM will be painful.
All complex data structures are fundamentally just blobs of bytes, and sending them to/from WebAssembly isn’t different from any other FFI boundary. You’ll have a struct
of some sort that defines the data layout, then the host peeks/pokes memory to enact the data transfer.
For JavaScript <-> WebAssembly the obvious choice for structured data would be JSON, but Protocol Buffers or Idol might be better for high-throughput cases.
You will also have to take care to manage memory carefully so JacaScript and your WASM application don’t try to free the same memory.
I might be misunderstanding, but I don’t think this problem can exist as stated. WebAssembly can’t free memory within the JavaScript heap, and any allocator used within the WebAssembly memory would be opaque to JavaScript.
The costs are a lot higher than people think, even for a well-factored application code base.
Still not seeing how this is true. A normal programmer (not Fabrice Bellard) could spend years porting QEMU to TypeScript, or I could point Clang at it and get a working qemu.asm
in an afternoon[0]. Most native applications are a thin-ish shell of UI on top of a giant pile of business logic, and being able to reduce the porting effort to just the UI means that it’s not really any more difficult than porting from Windows to macOS.
[0] Not a hypothetical example: https://ktock.github.io/qemu-wasm-demo/alpine-x86_64.html
All complex data structures are fundamentally just blobs of bytes, and sending them to/from WebAssembly isn’t different from any other FFI boundary. You’ll have a struct of some sort that defines the data layout, then the host peeks/pokes memory to enact the data transfer.
I agree that it’s not different from any other FFI boundary, but in no FFI boundary are complex types “fundamentally just blobs of bytes”; they almost always contain pointers into the heap and those pointers must be dereferenced properly and the memory pointed to must be managed properly. And this is to say nothing of ABI concerns. FFI is expensive.
WebAssembly can’t free memory within the JavaScript heap, and any allocator used within the WebAssembly memory would be opaque to JavaScript.
I’m not familiar with the particulars of WASM, so maybe that specific example is impossible, but I’m not sure what’s stopping WASM from vending out a pointer to JavaScript and then freeing it while JS thinks it’s valid. Maybe in a world where every language is using WASM’s GC, but I don’t think that’s going to happen any time soon (as far as I know, no one has ever made a one-size-fits-all GC).
Still not seeing how this is true. A normal programmer (not Fabrice Bellard) could spend years porting QEMU to TypeScript, or I could point Clang at it and get a working qemu.asm in an afternoon[0].
I’m not sure why you think porting qemu rebuts my claim that the costs of using WASM are higher than people think? Qemu is pretty aggressively optimized for portability and importantly lacks any GUI at all. I don’t think WASM is inappropriate in all cases, and that all applications should be ported to TS/JS, but rather that rewriting GUI applications in TS/JS is probably a much lighter lift for most applications than trying to take the existing code base and port it to WASM. Not for nothing, your Qemu example doesn’t work at all in my browser (Firefox).
What about libraries that don’t need to touch a bunch of platform APIs, and just provide useful computations to applications? Even if you rewrite your UI logic, you might want to be able to use the same libraries. For example, icu4x is written in Rust but is meant to be a go-to Unicode library for every language, and it’s available for use in JS/TS via WASM.
Also, some UI toolkits natively support both desktop and web via wasm, so you mightn’t need to change all that much, depending on what technology you’re using.
Also also, rewrites hecking suck. No one wants to do them, for good reason.
What about libraries that don’t need to touch a bunch of platform APIs, and just provide useful computations to applications?
Probably the most promising use case, but it still seems limited to specific use cases with simple data structures (e.g., strings, vectors, dataframes, etc).
Also, some UI toolkits natively support both desktop and web via wasm, so you mightn’t need to change all that much, depending on what technology you’re using.
Changing the UI toolkit is itself a pretty enormous change for most applications, never mind the rest of the platform (file system, network, etc). If you have a codebase that does a really good job of abstracting away the platform, then maybe you could use it, but such codebases are rare and there’s not a lot of benefit versus just building your UI once with TypeScript and WebView and getting support on all platforms for free and without FFI headaches.
Also also, rewrites hecking suck. No one wants to do them, for good reason.
Agreed, but if you’re trying to add a web target to a non-web desktop application, you’re already going to be rewriting 90% of your application unless your app does a lot of platform agnostic client-side stuff (like an image converter or a CAD program or similar). I think people wildly overestimate what WASM brings to the table in terms of making the web available to other programming languages. I don’t think you’re going to see many native Windows or macOS apps ported to the web any time soon. For the foreseeable future, the smoothest path to crossplatform applications is going to look a lot more like a TypeScript PWA than anything WASM.
Changing the UI toolkit is itself a pretty enormous change for most applications, never mind the rest of the platform (file system, network, etc).
Oh, I meant, you might already be using such a toolkit. Perhaps you chose it specifically because it’d allow future portability to the web. If you’re not using such a toolkit, then yeah, a rewrite would look more attractive.
For the foreseeable future, the smoothest path to crossplatform applications is going to look a lot more like a TypeScript PWA than anything WASM.
There is one thing you’re not considering: some of us are allergic to JS/TS. I’m only mostly joking. If I wanted to start making a webapp next week, there’s a 50% chance I’d do it in Rust with one of the web GUI frameworks available (e.g., Leptos, Dioxus, Sycamore, etc). Just because I’d rather get intimate with the borrow checker than write JS.
Oh, I meant, you might already be using such a toolkit. Perhaps you chose it specifically because it’d allow future portability to the web. If you’re not using such a toolkit, then yeah, a rewrite would look more attractive.
That makes sense, and thanks for clarifying. I imagine that there are relatively few such applications, which is what I was talking about with respect to WASM’s niche appeal.
There is one thing you’re not considering: some of us are allergic to JS/TS. I’m only mostly joking. If I wanted to start making a webapp next week, there’s a 50% chance I’d do it in Rust with one of the web GUI frameworks available (e.g., Leptos, Dioxus, Sycamore, etc). Just because I’d rather get intimate with the borrow checker than write JS.
I agree with you about wanting to avoid JS, but if you want to build anything serious in a cross-platform fashion, I strongly suspect typical web tech is the path of least resistance (also, TypeScript addresses almost all of my grievances with JavaScript, at least as it pertains to comparisons with WASM). I am curious about the Rust frameworks—are they enjoying any success among real world, load-bearing applications yet?
Interesting to bet on a web version of a DAW with no plugin support. Seems like a lot of work when the precedent (downloading programs) is very low friction.
I do wish the term “cross-platform” didn’t even exist. Web tech wants so badly to fill the void of a desperately needed general app runtime, and it has been insane following advancements in web browsers over the years. It seems like only yesterday I was struggling to make basics work across IE6 and friends, and now I have things like flex/grid, css variables, 3D rendering, MIDI, worker threads, WebAssembly… it’s wild. Feels like we get closer to the dream every day.
It would great if their project results in a wasm-based plugin standard. Platform-specific plugins has always been a little bit of a downer.