Making Of: Godot Web Player

Published 25 November 2019

Mikael Hernvall

3 minute read

making of godot web player

The web player is an easy-to-use, drag-n-drop tool for Godot gamedevs to try out their games in the browser.

We made it because the path from exporting a game to finally seeing it in action in a browser is not as straightforward as we'd like. Note that this process will be easier in 3.2, where the Play in Browser button is finally working (thanks faless!).

In the future, it will also be used as an entrypoint for gamedevs who want to share their games on the Web platform by uploading them to Gotm.

How a Godot HTML5 Export Works

To make the web player work, we made some changes to Godot's source. In particular, we changed how the Godot HTML5 export bootstraps your game in the browser. So, before explaining our changes, let's first dive into the anatomy of a Godot HTML5 export.

When you export your game using the HTML5 export preset, you get essentially three things:

  1. The entire Godot engine compiled to WebAssembly, in the .wasm file.
  2. JavaScript glue code for bootstrapping the engine in the browser and loading your game, in the .html and .js files.
  3. Your actual game, in the .pck file.

By default, the bootstrapper immediately starts working on page load. It fetches the .wasm from disk and instantiates it, then starts up the engine with your .pck passed as ordinary command-line arguments (--main-pack <pck>).

Hit the Snooze

Our problem is that we don't have a .pck on page load. This means we have to defer the bootstrapper. There are various ways to accomplish this. Our initial idea was to let the bootstrapper start up the engine, but pause the engine before it loads the .pck. Which is more akin to how our games work.

This, however, is needlessly complex and would require changes in the engine itself. We needed something more quick and dirty. Instead, we completely defer the bootstrapper and do not start it until a .pck is provided. We simply let the bootstrapper snooze.

So, when you enter the page... nothing happens. However, as soon as you drop a .pck into the browser, the bootstrapper kicks in with the provided .pck injected into its bootup sequence.

Since the bootup can take a while, some parts of it are cached. This reduces the boot time for subsequent .pck-drops on the same page. That's what makes the quick transitions between games possible in the video below.

Why You Have to Specify Your PCK's Version

For the most part, your .pck is pretty compatible across Godot versions. Unfortunately, the GDScript bytecode is not, not even within major versions such as 3.1 and 3.1.1. Since a .pck does not contain information about its minor version, the web player cannot reliably deduce it by looking at the .pck. That's why you have to help it a bit.

As of writing, the web player uses the 3.2.beta1 version of the Godot engine. For simplicity's sake, it uses this version only. However, that means we have to translate the bytecode to the right version before feeding it to the engine.

Luckily, this was not as cumbersome as one might think, thanks to the open-source decompilation tool Godot RE Tools maintained by bruvzg. There you can find a record of bytecode changes, which is really helpful as a starting point.

The cause of incompatibility between the versions 3.1, 3.1.1 and 3.2 is that built-in GDScript functions have been added. In bytecode these are identified by integers in an enumerable.

Unfortunately, new functions are not added to the end of this enumerable. The effect of this is that the new function takes someone else's identifier and shift the identifiers of all functions below it. For example, when a 3.1.1-compiled script calls lerp when run in a 3.2 engine, lerp_angle is called instead. Not good!

The web player fixes this by remapping the function identifiers to the 3.2 bytecode version before feeding it to the engine. If you export your game with script encryption enabled, however, it currently won't work.

If you are worried that your scripts may get stolen you shouldn't worry. Everything is local and nothing leaves your computer. In the translation process, your scripts are never decompiled either, since it only needs to make changes in the bytecode.

By the Grace of Porta-Packs

Update 2020-01-02: This is not 100% true. Use the HTML5 export preset always. Some features (e.g. VRAM compression) might not work if not using the HTML5 preset.

An interesting detail is that the web player also works for exports to other platforms. We've tested it on Linux, Windows, Android and HTML5 exports. This works because your actual game is contained within the .pck.

In other words, it doesn't matter what platform you export your game to. On Android, though, the .pck is buried in the .apk archive, so you'll have to dig around a bit to find it.