Version 1.2, Web Support


Version 1.2 is available, now playable on the web! It is also no longer using SDL, instead it is using wgpu and winit for graphics and windowing, respectively.

It was a bit of a journey to get to web support, but it was made a lot easier by Rust’s native support for web targets and the fact that many of the libraries I use explicitly support web. I did have some desktop specific code for accessibility and file loading that required disabling or re-writing to work on web, but the hardest part was getting scripting and thread support working.

Getting scripts working required switching from Lua to JavaScript, since the libraries for the former wouldn’t support building for web without a significant amount of work. After a bit of searching, I found an interpreter for JS written in Rust, with support for embedding and explicit support for the wasm32 (web) target. I evaluated some other scripting languages like Rune and Rhai, but felt like they would be too unfamiliar to anyone trying to write in them and they had some sharp edges which I didn’t like. This does mean that on web I’m running a JS interpreter in WebAssembly in a browser which natively supports JS - which I find amusing, but it was the most pragmatic solution to sharing code for desktop and web.

Thread support required pulling in a library which emulates threads using workers and SharedArrayBuffers along with building using the nightly version of the Rust compiler. This made things a little more tricky to test locally, but once I got support setup for COOP and COEP security headers while serving local files it was quick to iterate and test things out.

Support for web also means that sound doesn’t play right away when loading the game anymore (background music now only starts after the first keyboard input), since web browsers (rightly) don’t allow auto-playing audio except in response to user interaction on a page. This seems like an acceptable trade-off though.

I also had to change how I initialize the main window. Previously, I was initializing wgpu in the application resume handler for winit using a call to block_on to handle the async nature of initialization. This approach didn’t work on the web however, requiring me to switch to initializing the window prior to running the event loop. It’s somewhat concerning that winit has marked the create_window function on event loop as deprecated with no clear replacement for this use case, but it works for now.

The initial version of web loading was too slow, since it needed to download the entire 80+MB before it could render anything to the screen. In order to fix this, I split the assets out from the binary and set it up so they’re loaded by fetching the resource after the application has started. Doing so required pulling in the miniquad library, which has support for cross platform file loading - including web. I am unaware of any other libraries with this functionality.

To get miniquad to load, I needed to patch the files generated by wasm-bindgen to reference a custom env.js file (adapted from miniquad’s gl.js) which specified the exports miniquad needs in order to load. I also needed to specify the wasm memory in env.js and pass it in to the generated initialization function rather than relying on the generated memory allocation function.

The end result was about 2x faster loading on the web. Improving the speed even further required moving most of the fonts out of the binary into the assets folder (keeping just the most basic fallback font). Now the binary is about 14.25MB and loads relatively quickly.

Get Starlight

Download NowName your own price

Leave a comment

Log in with itch.io to leave a comment.