Published on October 14, 2026
We tried SortyNow on WebAssembly and rolled back (for now)
We compiled our Flutter app to WebAssembly, shipped it and pulled it. What --wasm really changes, which browsers support it and why SortyNow still runs on CanvasKit today.
flutter · webassembly · web · sortynow
SortyNow is our draw app: a wheel, quick draw, secret Santa, team maker and pair matcher. It's built with Flutter, published on Google Play, and it also runs right here on the web, inside kitusoft.com.
A few months ago we tried compiling the web version with Flutter's --wasm option. We shipped it, found a visible problem and went back to the previous build. This is the full story, with what the official sources say and what we saw ourselves.
First, clearing up a misunderstanding
You often read that Flutter's normal web build is "JavaScript" and the new one is "WebAssembly". It's more nuanced:
- The default build turns your Dart code into JavaScript (with
dart2js) and draws the interface with CanvasKit, which is the Skia graphics engine compiled to WebAssembly. In other words, WebAssembly was already there. - The
--wasmbuild also compiles your own Dart code to WebAssembly (withdart2wasm) and uses skwasm, a more compact version of Skia, also in WebAssembly.
Even with --wasm, Flutter still produces the JavaScript version: if the browser lacks what's needed, the app starts with that version automatically. There's no need to maintain two builds by hand.
Which browsers can use it
The WebAssembly build needs a browser with WasmGC (garbage collection in WebAssembly). According to Flutter's documentation:
- Chrome, Edge and other Chromium browsers: yes, from version 119.
- Firefox: announced WasmGC in version 120, but it currently doesn't work with Flutter due to a known limitation.
- Safari: now supports WasmGC, but a bug blocks Flutter's renderer.
- iPhone and iPad: no browser, because on iOS they all use Safari's engine.
In practice, a large share of visitors will keep getting the JavaScript version. It's a key fact before deciding.
What we learned by shipping it
1. The base path matters
SortyNow lives in a subfolder (/apps/sortynow/), not at the root of the domain. Flutter generates an index.html with <base href="/">, so the browser looks for its files at the root and can't find them. The fix is to build with the path:
flutter build web --wasm --base-href /apps/sortynow/
The path must start and end with /. The same applies to the build without --wasm.
2. The isolation headers are about threads
For skwasm to use several threads, the server must send Cross-Origin-Opener-Policy: same-origin and Cross-Origin-Embedder-Policy (require-corp or credentialless). Without them, skwasm runs on a single thread.
We chose not to enable them: applied to the site, they could block resources from other domains (such as Firebase or external images). For a draw app, one thread is enough.
3. Passing the language from the site
kitusoft.com is in four languages and we wanted SortyNow to open in the page's language, not the system's. We solved it by passing the language in the iframe URL (?lang=en) and reading it when the app starts:
Locale? _localeFromQuery() {
final lang = Uri.base.queryParameters['lang'];
if (lang == null || lang.isEmpty) return null;
final code = lang.toLowerCase().split(RegExp(r'[-_]')).first;
const supported = {'es', 'en', 'fr', 'de'};
return supported.contains(code) ? Locale(code) : null;
}
On Android that parameter doesn't exist, so the function returns null and the app uses the phone's language.
4. The confetti disappeared
With the WebAssembly version, the winner dialog appeared, but the confetti animation didn't show. In SortyNow that moment is part of the experience, so we went back to the default build, where it works fine.
That said, the confetti package is listed on pub.dev as WebAssembly-compatible, and we found no public reports of this problem. We don't know yet whether the cause is the package, skwasm or our own code. What we do know is that, for our app and at that time, it wasn't ready.
So, is it worth it?
The WebAssembly build is where Flutter is heading, and trying it costs one command. Our advice:
- Try it if your app is public and you want better performance in Chrome and Edge.
- Check carefully everything visual (animations, effects, custom painting) in a supported browser before shipping.
- Don't expect it to reach everyone: on Safari, on iPhone and currently on Firefox, your users will get the JavaScript version.
- Build with
--base-hrefif your app doesn't live at the root of the domain.
We'll try again once we can isolate the confetti issue. When we do, we'll write about it here.
Sources
- Flutter: Support for WebAssembly (Wasm).
- Flutter: Web app initialization.
- Flutter: Hosting a Flutter app at a non-root location.
- pub.dev: confetti package.