Recently we announced our decision to shift from our proprietary BRAWL bytecode to embrace WebAssembly (WASM). WASM is becoming a global standard, and standardization matters when building technology that needs to be secure, interoperable, and future-proof.
We are proud to share that we’ve not only made a transition decision - we’ve done it by building our own WASM run-time from scratch, purpose-built for resource constrained microcontrollers.
A Extremely Lightweight Run-time
When we announced our shift to WebAssembly, we faced a clear choice: utilize an existing open source engine, or start from scratch. We chose the harder path, since we like challenges and also want to fully understand the implementation - in doing so, we created something that is uniquely suited for microcontroller environments.
Why Build Our Own Run-time?
Most WebAssembly engines are designed with desktops or high-end embedded Linux systems in mind. They assume megabytes of memory, operating systems and in some cases, JIT compilation. They typically do not focus on a bare-metal microcontroller with only a few kilobytes of SRAM.
By writing our run-time from the ground up in C, we could:
- control every byte of the memory usage on the device
- control the opcode surface - focus on WebAssembly MVP (1.0)
- avoid hidden assumptions - we understand every line of code!
- guarantee determinism - essential for IoT and safety-critical environments
How Big Is The WASM Run-time?
Our run-time is surprisingly small given what it implements:
- ~12,000 lines of C - cleanly documented source
- ~46 KB flash overhead for the run-time - compared to a trivial Arduino sketch
- ~14 KB RAM overhead - adjustable based on requirements
That means a fully compliant WASM virtual machine can fit comfortably in the flash and SRAM of many microcontrollers on the market, leaving space for actual applications. We also chose an Arduino UNO R4 WiFi to demonstrate our run-time as it is a typical microcontroller that is available on the market.
How Reliable Is The WASM Run-time?
Code size alone is meaningless without validation and compliance. That is why testing and ensuring compliance with the WebAssembly MVP (1.0) standard was a major focus:
- almost 12,000 test cases
- the majority are derived from WAST, the official test suite
- also added hundreds of handcrafted edge cases
We can confidently claim our run-time is 100% compliant to WebAssembly MVP (1.0) specification.
DEMO: Arduino UNO R4 WiFi
We like to show things that work, proof is in the pudding as they say - we utilized the Arduino UNO R4 WiFi, powered by the Renesas RA4M1 microcontroller, specifically to demonstrate the capabilities of the WebAssembly run-time - providing host functions to create a demo that actually does something visual.
- MCU: Arm® Cortex®-M4F @ 48 MHz
- flash: 256 KB
- SRAM: 32 KB
- Onboard LED Matrix: 12x8
Arduino Sketches: LEDBounce_I32.ino and LEDBounce_F32.ino for Arduino UNO R4 WiFi.
We re-created a nice demo we saw on hackster.io demonstrating balls bouncing around the Arduino UNO R4 WiFi's LED Matrix. We have implemented two variants, one using I32 (fixed point 16:16) and the other F32 (floats) - depending on whether the target microcontroller has an FPU or not.
What It Means in Practice
On Arduino, there’s no operating system, no file-systems - just a microcontroller and simple peripherals. We kept our run-time MVP-pure (no custom opcodes) and exposed device features via set of hardware specific host functions (WASM imports), keeping the VM small and portable, while interacting directly with hardware like the Arduino UNO R4’s 12x8 LED matrix.
As an example, the WASM target for Arduino UNO R4 WiFi provides these host functions:
;; LED matrix lifecycle (import "brawl:device/matrix" "begin" (func $begin)) (import "brawl:device/matrix" "on" (func $on (param i32))) (import "brawl:device/matrix" "off" (func $off (param i32))) (import "brawl:device/matrix" "autoscroll" (func $autoscroll (param i32))) (import "brawl:device/matrix" "clear" (func $clear)) (import "brawl:device/matrix" "play" (func $play (param i32))) (import "brawl:device/matrix" "next" (func $next)) (import "brawl:device/matrix" "sequenceDone" (func $sequenceDone (result i32))) ;; pixel/sequence I/O (import "brawl:device/matrix" "loadFrame" (func $loadFrame (param i32 i32 i32))) (import "brawl:device/matrix" "loadPixels" (func $loadPixels (param i32 i32 i32))) (import "brawl:device/matrix" "loadSequence" (func $loadSequence (param i32 i32))) (import "brawl:device/matrix" "renderFrame" (func $renderFrame (param i32)))
The run-time resolves the host functions (WASM imports) by name (namespace + function), validates the types at load time, and binds them to real functionality on the device. A WASM call is just a direct, bounds-checked jump into the host device, just as if it was a native application.
The Developer Experience
From a developer’s perspective, working with our run-time is straightforward: you write your logic in WebAssembly text format (WAT), compile it to standard WASM bytecode, and then let our run-time execute it on the device. Instead of coding directly against Arduino libraries, you target a small set of imported host functions that abstract the hardware (like the LED matrix).
WebAssembly also opens up the opportunity for developers to use high level languages such as C, Rust, JavaScript, Go and many more - while being able to compile into a universal bytecode that can potentially be deployed anywhere bringing a true "write one, run anywhere" experience.
This means the same WASM module can run unmodified on any board that provides those imports. Looking ahead, we are extending our lifecycle management platform to support WASM as a first-class deployment format. Just like with our native microcontroller updates today, developers will soon be able to deliver and update WASM bytecode over the air - securely, seamlessly, and at scale.
Recognizing Our Interns
While this run-time has been project led internally, it would not have reached this milestone as quickly without the help of two talented interns who joined us through the Absolute Internship program.
Luis Daniel Filorio Luna and Maria Renee Ramos Valdez holding Arduino plug-and-make kits
"Luis Daniel Filorio Luna" and "Maria Renee Ramos Valdez" - Computer Science and Engineering students at "Instituto Tecnológico de Estudios Superiores de Monterrey" have worked hands-on with our team and assisted in building our test framework and producing real examples to make this project a reality.
They have directly shaped the reliability of our WASM run-time. For us, internships are more than a learning opportunity for students; they are a way to bring new energy, ideas, and perspectives into our every day-to-day work. This project is living proof of that.
We also want to recognize Absolute Internship for the professionalism and global reach they bring. By managing the logistics and matching us with interns who had the right skills and mindset, they enabled us to focus on innovation while ensuring the program ran smoothly.
This collaboration is something we’re proud of, and we look forward to continuing it in future projects.