Key Takeaways
- WebAssemly, while being actively developed and still rough around the edges, is mature enough to port a complex application such as a desktop game editor to the web
- Benefits of porting desktop software to WebAssembly includes the cross-platform delivery of the ported software, possibly reaching a larger target audience
- Front-end frameworks and tooling allow for a streamlined UI development with a shorter feedback cycle vs. native desktop applications
- The web additionally offers new possibilities such as deep-linking, and the access to a rich UI ecosystem, with the potential to improve considerably the user experience
- Developers must pay special attention to memory management, testing, and typing to mitigate porting issues
Florian Rival, software engineer at Google and creator of the GDevelop game editor, recently presented a talk at ReactiveConf 2019 in Prague, in which he discussed the lessons learnt from porting a native desktop game editor to the browser with WebAssembly.
InfoQ asked Rival about the technical challenges encountered, the benefits derived from the port, and tips for developers thinking about porting desktop applications with WebAssembly.
InfoQ: Let’s start with describing GDevelop. What is the target audience and what is the value proposition of GDevelop for that audience? What pain points does it solve for game creators?
Florian Rival: The idea with GDevelop is making game creation accessible to anyone, from beginners to seasoned game developers. GDevelop allows you to create the logic of your game using visual events, composed of conditions and actions. You can also build your game objects by composing pre-defined and customizable behaviors.
This means that the entry barrier of learning the syntax and idioms of a programming language is removed. For people that are not developers, it’s a way to quickly get up and running with an intuitive interface. Lots of people love sandbox games. GDevelop is a sandbox - but what you can do with it is unlimited.
For seasoned game developers, you get an integrated development environment that makes creation of games quite fast and efficient (events are transpiled to "real" code) and extensible.
Finally, for a team, it’s a way to involve everybody (developers, artists, project managers, game designers ...) around a common interface and foster the creativity of everyone.
Of course, education and workshops related to game creation can greatly benefit from using GDevelop with students. You can start hacking on a game in a few seconds, teach programming concepts through events and even go further by creating extensions.
InfoQ: What motivated you to port the game editor to WebAssembly? What does porting to the web give you that you cannot have or is hard to have on desktop?
Rival: The editor used to be a native C++ desktop application. While working OK, it was just OK and not great. It was starting to fail on a few points: support of macOS and Linux was hard to achieve properly (crashes in the UI toolkit were frequent) and working on the UI was quite slow (due to the long "make a change > compile > run > repeat" feedback loop) and limited to outdated UI components. This would also frighten new contributors.
I knew, after working quite a bit with React (and related frameworks like React Native to make mobile apps), that the component paradigm (associated to the "UI as a function of the state") was a really powerful and scalable way to make interfaces.
I was missing this development speed and the great architecture it enables on the native side!
More generally, I started to wonder if web technologies could help in making a better app.
That is at this point that I turned to WebAssembly (using Emscripten), as a convenient way of porting this large existing C++ codebase to browsers (and JS engines in general like Node.js).
My idea would be that moving to WebAssembly for the core and JavaScript + React (or another front-end framework) for the interface, I would have an app portable to almost every existing platform, working consistently across devices and I would unlock new things that I could not even dream of before. Like having an app that you can launch in a few seconds to try it, or an app that can run unchanged on phones and tablets as well.
InfoQ: Talking about the port, you mentioned in the talk that you started with the following architecture:
This being a game editor, I understand that two key modules are a rich, interactive UI handling the editing part, and a more transactional, algorithmic part linked to the rule engines and game code generation. How does that translate into the new architecture when porting to WebAssembly?
Rival: Porting to WebAssembly started by clearly separating, in the original C++ codebase, the core of the app (the "business logic", which are in my case the classes describing the structure of a game and the classes manipulating them, including the game code generation) from the UI. This is this "core" part that is compiled to WebAssembly - almost unchanged apart from a few abstractions around the file system.
Once you have this codebase running in the browser, the next step was to start exposing part of it to JavaScript by writing bindings. (What you use depends on your language. In my case for C++ with Emscripten, I used WebIDL to write the bindings).
I could then finally start to write a new interface, using React, that is using this code - just like another library.
InfoQ: What are the key challenges you encountered? What tips do you have for developers seeking to similarly port existing desktop software to the web with WebAssembly?
Rival: A familiarity with the existing codebase is of course a huge advantage to begin with. It’s a good idea to see how the existing native code is running and is being used if it’s a library.
A big challenge is around memory management. While JavaScript objects are garbage-collected, the objects that you create in the "WebAssembly world" are not. So if you call a function to create an object that is stored in the WebAssembly memory, and then later drop the reference to it in JavaScript, you just made a memory leak. Be sure to delete what you create - until Emscripten/wasm gets some garbage collector.
Another challenge is around properly using the WebAssembly compiled library. If you pass a parameter of the wrong type, or try to call from JavaScript a method of an object that you deleted from memory in your C++ code, then you’re in the uncharted territory of the undefined behavior (in C++) and will likely get a "crash" inside your wasm module. And it’s unlikely you can recover from it (while JavaScript can be a bit more forgiving and you can hope to continue working if an exception was uncaught in a non-critical part of the app).
Typing in JavaScript (using Flow and Typescript) can help - unfortunately I don’t have yet a way to automatically translate C++ types to Flow or Typescript types.
Debugging can be harder too. I recommend to have an extensive test set for your wasm library, to sanity-check that everything is working properly. This will help in case you’re moving the implementation to another language and also is a great living documentation of how to use properly the library.
InfoQ: How did you end up packaging/distributing your web application and why?
Rival: As having access to the file system was still an essential component of the app and that people prefer to be able to work on games offline, I packaged the whole application as an Electron application that can work on Windows/macOS/Linux. It’s working really well on all platforms and while Electron can be a bit heavy in terms of packaging size, I’m OK with it as users are downloading a full-fledged game editor.
This being said, having an autonomous web app was important, so the app can be tried and used online. There are some limitations compared to the desktop app that I’m progressively removing, when the web offers equivalent APIs, or reworking to use alternatives.
InfoQ: How long did it take to get a minimally viable ported editor (UI-wise and performance-wise) and how easy would you say the experience was?
Rival: There were certainly up and downs while doing this. Once I got a working version of the core inside browsers, a few friends and I started working on a new web-based UI for the app. It took us only a few weeks to get something working. I restarted it from scratch later - but our prototype was a proof that browsers could support the app and run it properly.
I did have to rework the bindings though at some points, and upgrading to newer versions of Emscripten was not always easy. I’m still not on the latest version :) (but it’s just a matter of investing some time to do it).
It’s certainly not easy but the rough edges that were at the start are getting better (Emscripten compilation used to be super long - this is not the case anymore).
InfoQ: I noticed with interest that you mentioned that the ported web application is better than the original native app. Can you elaborate on the specific improvements brought about by the web application?
Rival: Interestingly, startup time was improved and I think could be improved again.
What I’m the most satisfied with is the possibility to make this kind of link.
Click there and in a few seconds (more or less according to your internet speed and how powerful is your device), you have a working game editor, you can modify the game, run a preview, start hacking on it. It was unbelievable to be able to reach this previously.
In terms of development experience, working on the C++ part of the app is mostly the same as before, but the UI is really faster to develop on. Tools like Storybook allow to develop components of the app in isolation - without even running the app. We went from a feedback loop of a few minutes to a few seconds when developing on the UI. Storybook also allows you to visualize unusual states of the components of your app, like error states, and be confident after making changes that everything is working. I see them as non-automated but still super useful test cases.
Quirks between web browsers are unavoidable but in general the support for multiple platforms is much easier to achieve than with the older cross-platform UI toolkits that I tried in C++.
While the interface of the app is not made of native widgets, we can still use some (especially if you’re using Electron, which lets you take advantage of things like native context menus and native menu bars).
The clean, components-oriented architecture that is enabled by using React is a huge advantage in terms of modularity and keeping a clean codebase - which is improving development efficiency again. In the end, this translates to a better interface - because your clean architecture translates to well-designed, consistent components. For example, responsiveness is easier to achieve consistently with components. It was hard to imagine doing a responsive UI with the old interface.
InfoQ: What do you see next in terms of future features for the editor?
Rival: Tons of things! The community is always suggesting new things :) The interface is being always improved to strike the right balance between intuitiveness and completeness. I’d like to create a more complete ecosystem and community around the software - again I have tons of ideas to make game creation more accessible and faster for literally anyone. The game engine can be improved with new extensions bringing new capabilities or enhancing the performance by reworking some performance sensitive parts in … WebAssembly!
GDevelop is an open-source, cross-platform game engine available through its GitHub repository.
ReactiveConf is a yearly conference targeted at developers with talks addressing the latest technologies and trends in software development. ReactiveConf 2019 took place Oct. 30-Nov. 1, 2019, and is the fifth installment of ReactiveConf.
About the Interviewee
Florian Rival is a software engineer at Google. He is the author of GDevelop, an open-source game making software based on WebAssembly and JavaScript. He also made Lil BUB’s Hello Earth, an 8-bit game starring the internet sensation cat, available on desktop and mobile, 100% made with GDevelop. More generally, he is fond of innovative approaches to push the limits of what we can do in apps, mobile apps and games.