If you explicitly list which functions you want to export from your WebAssembly module, the linker will remove all the unused code, in the same way that "tree-shaking" works for JS bundlers.
In my experience, a WebAssembly module (even with all symbols exported) is smaller than the equivalent native library. The bytecode is denser.
WebAssembly modules tend to be larger than JavaScript because AOT-compiled languages don't care as much about code size--they assume you only download the program/library once. In particular, LLVM (which I believe is the only mainstream WebAssembly-emitting backend) loves inlining everything.
Judicious use of `-Oz`, stripping debug info, and other standard code size techniques really help here. The app developer does have to care about code size, of course.
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.
you don't even need `<!doctype html>`. I'm sure it's easy to look up when that was added/recommended, but i've never used it when i do a 94 html page/site like this. html head title /title /head body /body /html
'sit
The header image in the article is a 2400x1600 PNG that is 500KB large, apparently due to subtle dithering making it hard to compress. Converting it to a visually-identical .avif (quality 90, 12-bit color depth) takes it all the way down to 15KB.
For an image which isn’t even interesting or illustrative of the issue. Hero images on blog posts have run their course, I can’t remember the last time I felt anything positive by encountering one. They make the page load longer, force us to scroll to start reading, and are forgotten as soon as they go out of sight.
I'm guessing it could be even smaller if it was designed as a SVG file. Although the glow effect with the fading colors would probably need to be simplified.
Yup, the CloudFlare CAPTCHAs are a nuisance, but they're so much better than the Google reCAPTCHAs they replaced. Those things must be designed to make you give up.
Ah, I didn't realize that always happened. I thought it was only if you did something that might have OS specific rendering characteristics (text-draws, etc).
> The patent for the Cherry MX design finally expired in 2014.
OK, but did it really? I've seen this claim pop up occasionally, but nobody ever points to the patent in question. A quick Google search for "cherry mx patent 2014" shows the oldest result as being a Reddit comment from 2017 (https://www.reddit.com/r/hardware/comments/6am47a/comment/dh...):
> The patent expired in 2014. Many people have been paying the same price for mechanical keyboards with cheaper Chinese MX switches without knowing.
> For 20 years, Cherry’s patent on mechanical switches made it the only player around. That patent’s expiration around 2014, though, released the floodgates and allowed countless copycats and switches with varying levels of modification to the cross-stem design to pour in.
However, what seems to be the actual Cherry MX switch patent (US4467160A) was filed in 1983 and expired all the way back in 2003. So what exactly expired in 2014?
We've been paying less and actively avoiding lower quality Cherry branded MX compatible switches.
There is no reason to ever pay Cherry's pricing when Kalih and Gateron completely control that market now.
Also, the patent they're discussing, afaik, isn't MX specific, but more a specific thing in how Cherry's switches work (including the MX). Kalih and Gateron both built their businesses on making patent-avoiding designs that are superior.
For a much more substantive article on a similar topic, see Dan Luu's blog post which draws an analogy to talent scouting in baseball: https://danluu.com/talent/
Am I missing something? The LGPL only applies to the library itself--you can dynamically link to it from proprietary code. So in this hypothetical scenario, someone could just write a terminal emulator (or IDE, or what have you) that dynamically links to libghostty and put as much telemetry in it as they wanted, couldn't they?
No, you're not but I did! So GPL then. Maybe I just wanted to make up a scenario. I'm not sure why MIT/BSD have become so popular, they have their place but I don't think they have any place in software infrastructure.
>> I truly don’t understand nor follow what you mean? What makes MIT/BAD bad for software infra?
You should have access to the source for your infra as a user of it. MIT/BSD allow people to deploy without offering source, so they can do whatever they want with it and then you get to use it. TiVoisation is more than possible among many other user-hostile options. "But you can always get the source!" is not a valid response - you can't get the source for this thing that's running right now.
Freedom to deny other peoples freedom is no part of "Free Software" but it goes on all the time with "Open Source".
But I would hesitate to blame this on the original OSS author’s license choice - the vendor can always provide source even without the force of GPL. Heck, rather making it GPL means almost no hosting company will provide it.
I was going to wait to post this until I've finished the CLI and documentation, but this seems like a relevant time to plug my web font subsetting/self-hosting tool: https://glypht.valadaptive.dev/
It lets you pick from the Google Fonts catalog, and comes with various options for further reducing the fonts' sizes if you're as obsessed with webpage size as I am.
Completely different use case, where it assumes you have the font downloaded already, and every new regen of your static site you regenerate the font for what’s required to render it.
This looks really cool! When I was optimizing my SSG website, the big win was self-hosting the fonts. The fonts are still the biggest part of the payload on some of the pages, though, so I'm wondering how small I can make them. I think I'll have to give this tool a shot!
This is fantastic! I was recently trying to prepare a number of fonts for web use and there was a lot of friction -- each part of the process needed a different tool, each with its own weird quirks. Glypht looks to have everything in one place without any extraneous stuff or legacy cruft. I didn't even know it was possible to make a subset of weights for variable fonts. I'll be making thorough use of this, thank you!
Fair play, that is awesome, and just what I wanted.
I do have a minor constructive criticism, as someone that does not use Apple, I found the '+' to actually select a Google Font to be far from intuitive. I was looking for a big button in the bottom right with 'Select' (or some other label).
Other than that, bookmarked, kettle on and fired up to get my fonts optimised. Thank you.
Emscripten provides a libc implementation based on musl, and so does wasi-libc (https://github.com/WebAssembly/wasi-libc).
If you explicitly list which functions you want to export from your WebAssembly module, the linker will remove all the unused code, in the same way that "tree-shaking" works for JS bundlers.
In my experience, a WebAssembly module (even with all symbols exported) is smaller than the equivalent native library. The bytecode is denser.
WebAssembly modules tend to be larger than JavaScript because AOT-compiled languages don't care as much about code size--they assume you only download the program/library once. In particular, LLVM (which I believe is the only mainstream WebAssembly-emitting backend) loves inlining everything.
Judicious use of `-Oz`, stripping debug info, and other standard code size techniques really help here. The app developer does have to care about code size, of course.