· 2 min read
Solo Composer Series Part 2: Looking at Rust
In this series I will be talking a bit about a personal project I have been working on for the past few months - Solo composer, a music notation application. This post explores porting the application to rust to target the WASM runtime.
I have recently been working on a personal development project to build a music notation application using web technologies. My inital prototype relied heavily on Typescript for much of the heavy lifting. However, while listening to a podcast about Google Earth's stack they described porting their business logic to WASM (compiled from C++) and writing a thin UI layer over the top of this.This grabbed me as an interesting idea that I would like to look into further.
WHAT IS WASM?
WASM, or WebAssembly, is a "low-level assembly-like language with a compact binary format that runs with near-native performance". It lends itself well to performing the complex logic that is required to parse and render scores inside the application. There are a number of languages that have WASM as a compile target, most notibly C++ and rust. I have been hearing great things about Rust and wanted to explore using this further.
WHAT IS RUST?
Rust is a highly performant systems level language with built in memory safety and it has a well supported ecosystem for compiling to WASM. The guarentees that the Rust compiler grants you through it's memory management system allows you to avoid a whole class of memory related bugs such as dangling pointers. Coupled with the fact that Rust is a strongly typed language, this makes for a great developer experience -- the compiler is sophistacted and a lot of effort has gone into clear error messaging.
THE NEW TECH STACK
I have chosen to take inspiration from the Google Earth team and structure the application in a similar fashion. The data layer is almost entirely within the Rust code which is in turn compiled to WASM and imported in the application's bootstrapping step. There is a small amount of very UI specific state which I have kept in the js realm but the rest is entirely within this WASM code.
On top of this WASM data layer sits the react UI layer. This is almost entirely the same code as before. The only difference is instead of interfacing with a javascript state we call methods in the rust code to retrieve the data we need. This is done through the wasm-bindgen utility. Wasm-bindgen provides a simple way of generating an interface between the js realm and the generated WASM code. It generates "glue" code for transfering data (of many different types) back and forth between the two realms.
In my initial prototype the Typescript code was in charge of generating the render instructions. This has now moved entirely into the Rust code. For smaller scores (shorter length, less instruments) the performance gains are negligible. However, larger scores see a much more drastic performance gain, the time to render being cut to roughly a third.
CONCLUSION
This rearchitecturing of the stack has made big impacts on the performance. It has been an interesting journey finding out more about Rust. Rust is a very capable language and when compiled to WASM opens up a lot of oportunitties in the web space.
In future posts I aim to share more about other performance improvements such as tile rendering and Hashmap performance.