A Quick Introduction to Wasm
Most modern browsers already support WebAssembly (Wasm) functionality, a relatively new technology that allows high-performance applications to run in the web environment. WebAssembly is widely adopted because it enables code written in low-level languages like C++ to run at near-native speed on the web.
Common web applications that utilise Wasm include various gaming platforms like Unity, graphics-intensive applications like Figma, and even some productivity tools. These applications benefit from Wasm's ability to execute tasks more quickly and efficiently than traditional JavaScript, leading to a smoother, more responsive user experience on the web.
So what is WasmGC?
WebAssembly Garbage Collection (WasmGC) represents a significant enhancement in the realm of WebAssembly (Wasm), particularly for garbage-collected programming languages like Dart, Kotlin, PHP, or Java. These languages, unlike those requiring manual memory management (e.g., C, C++, Rust), typically include garbage collection as a standard feature.
Garbage collection, in essence, is a method to reclaim memory that was previously allocated by a program but is no longer in use. This makes the process of coding software much easier and efficient for developers because they no longer need to manually free the memory they have used.
The introduction of WasmGC brings multiple benefits. For developers, it streamlines the process of porting higher-level languages to the Wasm runtime. Traditionally, porting involved recompiling the main virtual machine code of a language to Wasm, along with implementing backends for just-in-time (JIT) or ahead-of-time (AOT) compilation specific to the Wasm architecture. This approach, while functional, required significant effort to adapt to Wasm's low-level architecture.
So how does this affect the performance of Flutter on the web?
Image courtesy Kevin Moore @ Wasm I/O 2023
The current state of play
Dart and flutter already provide a great experience on the web due to Dart’s native ability to compile to JavaScript. You can see this in action with products such as Rive, which provide a great experience regardless of whether you’re on the web or native desktop - all of which are produced using Dart and flutter.
At the moment Flutter apps on the web ship with a WASM version of the Skia rendererer called CanvasKit. This high performance renderer is the same as used in the Chrome browser and allows for more engaging graphical features. It’s hardware-accelerated, and it’s the same renderer as used on Flutter’s mobile experiences, so it’s a really consistent stack.
The actual Flutter framework and application code are written in garbage-collected Dart however, and need to be translated to Javascript in the current stable Flutter distribution. This still works great, and browsers are fantastic at optimising and JIT compiling this JS portion. But without the features of WasmGC it hasn’t been possible to move the framework and application code to Wasm and ship the whole app as one Wasm package.
Image courtesy Kevin Moore @ Wasm I/O 2023
And now 100% WebAssembly
However, the advent of WasmGC unlocks new potential. Imagine the entire Flutter app, framework and all, compactly packaged and shipped as a single, unified Wasm bundle. This could mean not just smoother performance, but a single and optimised binary download for users when loading the app.
How does Dart compile to WASM-GC
This is quite a technical section so honestly, if you’re not too bothered about the nitty gritty you can probably skip this part, but for closet geeks like myself I find it really interesting!
1. Common Dart front end
The first stage of the compilation is performed using the dart2wasm tool. It shares a common front end which is also used by the dart compilers and static analysers. It provides for parsing, type checking and lower level language features.
2. Type Flow Analysis
The next stage of the process is the type flow analysis. This is shared with the dart native AOT compiler, and provides for AST-level optimisations such as constant propagation and tree shaking.
3. Representations Phase
The next phase is the representations phase. This is where Dart types are translated into what the Wasm interpreter would consider a type. For example, a dart string would be converted to Wasm’s string representation, complex types can be boxed and unboxed and more optimisations are performed during this phase.
4. Code Generation
The following phase is code generation. This is where the actual Wasm instructions that do the work on the browser are generated. This is akin to machine code on a native platform, and there’s is no intermediate representation (IR) needed, as with other compilers such as LLVM. More optimisations are performed here, using information from the previous type flow analysis step.
5. Binaryen
Finally the process utilises another component common to other parts of the Wasm ecosystem called Binaryen. This is a stand-alone Wasm-to-Wasm optimiser, and is also used by languages such as Kotlin as part of their own WasmGC stack.
Most of the above stages are actually shared components and only the Representations and Code Generation components needed to be updated for DartGC support. These are themselves also written in Dart and open source, so you can see for yourself the capabilities of the Dart language on GitHub if you wish.
So what does this mean for my existing Dart app or my plans for a new all-screen Desktop, web and mobile experience?
With the Skia renderer we already have a great and performant solution for our current apps. Foresight Mobile develop several combined web and mobile apps using this stack including Ryze and Planpoint.
But going forward it’s great to hear that early next year we’ll have the full Flutter stack running natively in the browser, and early indications suggest that this will be 2 to 3 times the speed and capability of the current JS stack. This not only provides for amazing game-like experiences on the web but also improves web performance on low-spec browsers and older devices.
Curious to Discover More?
As you can see above, the Flutter development stack provides for some great cost savings, high performance, and cross-platform capabilities that are game-changers for new products considering their path on mobile, web or desktop. All without compromising on quality or maintainability.
Eager to see how we can transform your project? Please get in touch for a friendly chat about your requirements and to discuss the benefits and cost savings we can offer.