I second this; the tooling is somehow still not there after 10 years.
- The main toolchain for compiling existing C codebases to WebAssembly is Emscripten. It still hasn't escaped its tech-demo origins, and it's a rats' nest of compiler flags and janky polyfills. There are at least 3 half-finished implementations of everything. It doesn't follow semver, so every point release tends to have some breaking changes.
- The "modern" toolchain, wasi-sdk, is much more barebones. It's getting to the point of being usable, but I can't use it myself because it ships a precompiled libc and libc++ that use `-O3`, whereas Emscripten recompiles and caches the sysroot and uses `-Oz` if I tell it to. This increases the code size, which is already quite large.
- LLVM is still not very good at emitting optimized WebAssembly bytecode.
- Engines are still not very good at compiling WebAssembly bytecode to optimized machine code.
- Debug info, as you mentioned, is a total mess.
- Rust's WebAssembly tooling is on life support. The rustwasm GitHub organization was "sunset" in mid-2025 after years of inactivity.
- There is still no official way to import WebAssembly modules from JavaScript in a cross-platform manner, in the year of our lord 2026. If you're deploying to the browser and using Vite or raw ES modules, you can use `WebAssembly.instantiateStreaming(fetch(new URL('./foo.wasm', import.meta.url)))` and eat the top-level await. Vite recognizes the `new URL('...', import.meta.url)` pattern and will include the asset in the build output, but most other bundlers (e.g. Rollup and esbuild) do not. If you're on Node, you can't do this, because `fetch` does not work for local files. Most people just give up and embed the WebAssembly binary as a huge Base64 string, which increases the filesize by 33% and greatly reduces the compression ratio.
- If you want multithreaded WebAssembly, you need to set the COOP/COEP headers in order to gain access to `SharedArrayBuffer`. GitHub Pages still doesn't let you do this, although it's the third-most-upvoted feature request. There's a janky workaround that installs a service worker. All bets are off on how that workaround interacts with PWAs.
If the tooling situation had advanced past "tech demo" in the past 8 years since WebAssembly first shipped, a lot more people would be using it.
> LLVM is still not very good at emitting optimized WebAssembly bytecode.
Like I said in the other comment, I find it incredibly weird that wasm-opt can still squeeze like 10% better code (as in, both smaller binary and somehow faster code) on top of what LLVM does. And it hasn't changed much within the last 5 years.
And in general, the tooling ecosystem is doing... weirdly. Rust is doing badly yeah, but for example there was also a long stretch of time (I think it's solved now?) when you couldn't pass a .wasm with bulk-memory or other extensions to webpack, as its builtin wasm parser (why was it parsing the binary anyway?) didn't recognize new opcodes.