wg
wg copied to clipboard
Using wasm-bindgen for games
I've noticed while working on the wasm-bindgen
backend for Tetra that there's a few rough edges around the tooling from a game dev perspective. Since wasm-bindgen
seems to be the 'blessed' tool for WASM in Rust, having a tracking issue for some of these papercuts seems like a good idea. Maybe we can work with the WASM WG on some of these?
(Also, since some of said WG have been tagged in - aside from these issues, porting to WASM has gone ridiculously smoothly, kudos for all your hard work!)
Support for binary crates
~~wasm-bindgen
doesn't currently support main
- you have to use the wasm_bindgen(start)
attribute on a public function instead, and that function can't be main
(https://github.com/rustwasm/wasm-bindgen/issues/1630). This means that it's not possible to have a binary crate 'just work' without some glue code.~~
~~My current approach is to have a macro that automatically creates a wasm_main
wrapper, but I'd be interested to hear how other libraries (e.g. Winit) are planning on approaching this.~~
The above info is now out of date - wasm-bindgen
supports binary crates now.
Most likely as a consequence of the above, wasm-pack
is also currently quite heavily focused on usage for libraries that are intended for publishing to NPM. There's an RFC for a --no-pack
mode (https://github.com/rustwasm/wasm-pack/issues/691) that looks like it might work better for our use cases, but it seems to have stalled a little bit.
No cargo web
counterpart
The stdweb
ecosystem has cargo web
, which is a really nice tool that offers a HTTP server, auto-reloading, static file packaging, etc. Unfortunately, it doesn't currently support wasm-bindgen projects (https://github.com/koute/cargo-web/issues/92).
There are templates for projects that call into wasm-pack
from Webpack and offer similar features in that way, but I feel like this is overkill for most gamedev needs - 99% of the time, you just want a HTML page with a canvas on it that you can hook into.
EDIT: ~~Actually, it looks like cargo web
might have added wasm-bindgen support of some kind since I last tried it! Need to give that a proper try.~~ Nope, it was stdweb
that added support, my mistake!
cc @alexcrichton @ashleygwilliams @fitzgen @koute
Okay, I think I get the state of things now (please correct me if I'm wrong!) - you can use stdweb
with wasm-bindgen
or cargo web
as the backend, but you can't use cargo web
with wasm-bindgen
(at least not without manually calling the latter after the former).
Most likely as a consequence of the above,
wasm-pack
is also currently quite heavily focused on usage for libraries that are intended for publishing to NPM.
I wouldn't characterize it this way. It has a publish
command to publish the package to npm, but it is an optional command and doesn't affect anything else.
wasm-bindgen
doesn't currently supportmain
- you have to use thewasm_bindgen(start)
attribute on a public function instead
This is because there fundamentally is no main
on the web. When main
exits, basically nothing else runs. When a wasm_bindgen(start)
function exits, the page has still only just loaded.
HTTP server
We have some ideas around this stuff, but unfortunately haven't gotten around to it yet: https://github.com/rustwasm/rfcs/pull/10
@fitzgen It has a publish command to publish the package to npm, but it is an optional command and doesn't affect anything else.
That's true, but even the wasm-pack build
command is optimized for libraries. You can contrast that with cargo web build
, which is optimized for applications.
Having used both, I (and many others) have found that cargo web
provides a much smoother experience for application developers. Everything Just Works.
There's a lot that wasm-bindgen (and wasm-pack) need to learn from cargo web
and stdweb, because they provide a much nicer, smoother, more Rust-like experience.
This is because there fundamentally is no main on the web.
That's not the reason we don't support bin crates. After all, cargo web
has no problem with supporting bin crates (everything Just Works in the same way that Rust itself does), and wasm_bindgen(start)
is effectively a main
.
The reason we don't support bin crates is because it bloats up the file size by a lot. After that is fixed in rustc
we'll be able to support bin crates.
Maybe it would benefit the rust-gamedev WG if we could try to clarify the following points:
- Whether users should typically use wasm-pack, or instead use wasm-bindgen-cli directly. I remember there were some questions about keeping wasm-bindgen-cli and wasm-bindgen in sync when using the CLI directly.
- How webpack and npm relate to various parts of the wasm toolchain (and how to avoid them if the application prefers not to use them). I think
--web
was (or still is?) the standard way to do this but there were some changes earlier this year. This is slightly related to the first point. - How to handle crates that want to support both stdweb/wasm32-unknown-unknown and wasm-bindgen/wasm32-unknown-unknown
- How to handle non-Rust dependencies even when targeting wasm32-unknown-unknown (i.e. a dependency on a C or C++ library which is still built with Emscripten), even if the answer is to try to avoid doing that. This question has already come up a few times in the Rust community Discord. It's especially confusing for handling resources/assets (for example, we might call the separate Emscripten bundle a regular resource that has to be loaded and executed).
This clarification would be super useful to improve the documentation for gfx-hal's quad example and glow.
To add a small experience report to this discussion. I'm not using wasm-bindgen for games, but am writing an application using it.
It's been a few months since I started it, but I too remember some confusion about how to use the tooling for applications instead of libraries. At some point I had been messing with it for a while and decided to look into stdweb
because its approach seemed much easier to grok, but had to move away from it because some dependencies I wanted to use didn't support using stdweb (which also, as a first-time experience, made me a bit weary of potential ecosystem fragmentation).
I'm not sure if that's still the case with the recent convergence, but at some point I got things to "just work" using wasm-bindgen but without using wasm-pack, and haven't really looked into the pipeline since, so long as things keep working.
My current set-up now runs several tools in succession to build a release binary, including cargo build
, wasm-snip
, wasm-bindgen
and wasm-opt
. It then uses this inline module code to make things work (although I'm investigating an issue running the application in Safari which might be related to this, so this might change).
I have very little experience with the build ecosystem that exists around JavaScript, and didn't want to spend too much time on that for this project, so the goal was to not have to deal with anything related to that, which in the end seems possible, but it took some digging and experimenting to make it all work.
I'm using wasm-pack for my web-based multiplayer game https://SneakySnake.io. A couple thoughts:
- On binary support: this would be better handled with a template project that configures npm, webpack, and wasm-pack appropriately, sets up an auto-scaled canvas with high-dpi support, WebGL context, and calls into
init
andupdate_and_render
functions (or similar) in the wasm. You're better off writing your game as a library, anyways, if you ever want to run it on anything other than the web. I highly recommend watching Handmade Hero to see how Casey does multi-platform support.
If I ever have time to open source any of my work on SneakySnake, maybe I'll make one of these for others to benefit from. But all my code is currently highly specialized to this project's particular needs, i.e. decoupled network/update tick and render tick rates.
- On http server and auto-reload, as pointed out in the OP, this is all covered by an appropriately configured webpack/wasm-pack environment.
I'm strongly in favor of the handmade hero approach, and I think it's one place where we can easily get a win out of this WG.
https://github.com/rustwasm/wasm-bindgen/issues/1630 has been closed, making binary crates Just Work in wasm-bindgen
; once those changes get released I'll see if I can push harder for wasm-pack
to also Just Work on binary crates, which will knock out the first major obstacle.