Using Rust and WebAssembly without JavaScript
Before we begin, lets make it very clear that this is NOT a post to bash JavaScript language. If you are looking for childish language fights you won’t find it here.
What this post is really about is a way to compile Rust into WebAssembly, and be able to run it without the JavaScript ecosystem. You see, I have no issues with the language, but the ecosystem… that’s a different beast, and one that I’m more than happy to avoid. So let’s get started.-
You will need:
- Rust, obviously
- wasm-pack to compile the source.
- <optional>wasm-opt for optimizing the binary.
- <optional>Python 3 as a test http server.
Begin by making sure Rust, Cargo and wasm-pack are installed. We will use a nice game as example. We will NOT use npm nor any other JavaScript tool.
Important note on wasm-opt: it is only needed if you are _not_ using a 64 bit operating system. If you are running on ARM, or x86, you will need to install binaryen manually, since they don’t provide pre-compiled binaries. Don’t download the x86 packages, the binaries inside are x64 anyway. Check this bug for details: https://github.com/rustwasm/wasm-pack/issues/974
The project I’m using is a fork from the project on this blogpost: https://rhmoller.dev/posts/2020/my-experience-with-rust-and-websys/
I found it to be complex enough to show what can be done without JavaScript. You need two joysticks to control the two characters, and everything is done with Rust and web-sys. Clone it from here:
Eventually there will be some JavaScript glue code, but we are not touching it.
Let’s just go to the details and finish this post. Open build.sh and lets focus on the important steps
1) wasm-pack compiles the Rust code from the demo I copied. The — web argument is important to be able to import the module
wasm-pack build — target web2) We copy the static files needed to run the webassembly blob
mkdir -p demo_server/web_app
cp -r pkg/* static/* demo_server/web_app3) Remove the binary we just copied
rm demo_server/web_app/rust_webpack_template_bg.wasm4) Manually call wasm-opt and re-generate the binary
wasm-opt pkg/rust_webpack_template_bg.wasm -o demo_server/web_app/rust_webpack_template_bg.wasm -O4 -all -ffm — enable-simd
Now we have all needed files to run a server and the game inside the web_app folder. To do this I added a test python server. Run python3 demo_server/server.py and open http://localhost:8080 to see it in action.
In summary, what we did different is:
- Import the wasm-pack generated JavaScript glue code as a module from the index.html file.
- This glue code in turn calls the game Rust code.
- Rust renders the pixels into a canvas HTML element. This is the game I did NOT write, credit goes to Rene Hangstrup Møller who wrote that excellent demo, and shared it.
- The optional wasm-opt call manually optimizes the binary, which is a handy workaround since wasm-pack bug mentioned above is not correctly calling it in all platforms.
- Finally, we use a test server written in Python 3, and avoid installing the JavaScript ecosystem.
I hope this helps someone else, and of course, I have to thank Rene Hangstrup Møller for posting the original code and sharing it for us to learn.