Rewriting my mobile game in Rust targeting WASM

Background

In 2012, a friend and I wrote a mobile game called Panda Doodle. It was a puzzle game in which the player had to draw connections between doodles by matching/mixing the correct colors while looking for the shortest paths that required the least amount of paint. We were two programmers, but we also got help from an artist who contributed with very nice drawings which made the game look great!

What about Rust?

Well, in the beginning of 2020 I was starting to get curious about Rust. What’s up with all this hype around it? I read the Rust Book which was a nice introduction, but in my experience I usually come to grasp something much better by trying to use it. So I started to look for opportunities to do that.

WASM and the idea

As part of my initial exploration around Rust, I came across rustwasm and wasm-bindgen which are neat tools that allow building rust-generated WebAssembly and easily interacting with the browser Javascript APIs. I also remember coming across RustyVolley which is a simple game, but looked pretty good and ran super smoothly on my browser.

  • Translating C++ into Rust would probably give me insights on what exactly is different between these languages. By understanding changes forced by Rust, I would probably even become a better C++ programmer by getting a clearer picture of some of its pitfalls.
  • I get my game back!
  • And by targeting browsers this time the game will last… forever? I bet someone thought this when they were building Flash games back in the day! haha. Still the HTML5 APIs seem pretty solid and in much better shape than Flash ever was, so betting that they’re here to stay for a long while seems reasonable.
  • I also get to bypass the app stores and don’t have to deal with their annoyances. They can’t kick me out anymore! 😉 (to be fair, it wasn’t their fault that the SDK I used disappeared)

What I wanted/expected from my Rust code

Before starting this project, I already had in mind a few things that I wanted to explore about Rust with it:

  • Minimize as much as possible changes to the API of my ad-hoc game engine and to the structure of the game code as well. This would probably help me make the code translation faster, but the main reason to do it was actually to stress the limits of what can be done with Rust. For example, Rust is not an OOP language (it doesn’t have the concepts of classes or inheritance), so I wanted to know how much trouble I would get into by trying to replicate a framework that relies a lot on OOP (like the UI framework of my ad-hoc engine).
  • Be ergonomic and expressive. This meant that even if I had already solved a specific problem, I might come back to refactor it to try to push a bit to make the code less verbose, or to use a different design pattern that would make that code simpler to read/write (even if that meant sacrificing a bit of performance).

Getting Started

The main question I had to answer at the start was whether the game would run well enough in the browser of a mobile device. This was the biggest risk threatening the project. So my initial goal was to build a prototype to exercise the scene of the game that had most animations and rendering calls.

Writing some Rust code

As I started writing some real code, my lack of experience with Rust started to get in the way. My original C++ code used many patterns which aren’t straightforward for a beginner to replicate in Rust. Even if these patterns would look more straightforward for a C++ beginner programmer, I’ve got to admit a bunch of them were risky and very fragile. A C++ beginner can probably get a lot more done faster than a Rust beginner, but they are also more likely to create a lot more crap, bugs, crashes, etc. Rust was pushing me towards more robust software.

Rust Drawbacks

Relative to other languages, Rust is a hard language to learn (if not the hardest I’ve ever learnt). It took me quite some time to become productive. When I was learning Go about 4 years ago, I remember being productive almost off the bat. Comparing to Go is not a fair comparison though: if your application can afford garbage collection, you should probably pick a language with garbage collection in the first place anyway (it’s too much overhead otherwise, which explains a lot of why Rust is more complex).

Ah iOS Safari…

I need to pause here to add a rant about what actually was the most frustrating part of this project. I think we need to ask ourselves “Why is iOS Safari… so bad?”. Many times I reached a project milestone and tried running a test on an iPhone I found something was broken. Sure, no fullscreen API support, no vibration API support, it’s tricky to perform pixel perfect rendering on canvas2d… each of these individually might not look so bad, but they start stacking up.

Conclusion

15000 lines of Rust later, many of them written between my newborn son’s naps while babysitting him overnight, the reimplementation of the game is done! If you want to check it out, you can play it at https://pandadoodle.lucamoller.com/ (the experience is better on touch screens). You can even install it as a PWA on Android which is pretty neat and then it can run offline. And if you’d like to take a look at the resulting code, I’ve shared it on github.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store