WASM Effort

Hiya, to coordinate the WASM effort, it would be good to have a “living document” so we can track what is happening.

This post is a Wiki. Please edit this post if there is any relevant information to add / amend.



Please keep the WASM Rush project up to date with the tasks that you are working on. Feel free to expand a task into multiple sub tasks and update the board.

Tag issues with:

  • feat: WASM support

and put it in the WASM Rush project.

  • Please assign yourself to an issue so we know you are working on it, and unassign yourself when you are putting it down / pausing it, so someone else may pick it up.

    Note: There is nothing wrong with going halfway then stopping. Every effort counts, even if it simply demonstrates a particular approach isn’t feasible.

I would encourage people to tag-team if / when possible: “This is what I tried, this is what needs to be done next. Feel free to push to the branch.”

Ideas to work on, when the project is able to take it:

  • [ ] Network: Send requests over WebRTC? Or talk to Javascript to use QUIC?

Progress summary

The fifth post in this thread summarizes the initial efforts which informed the base of this work.

Week 1: Baseline

  • Amethyst compiles to WASM.
  • Repository forks / branches created.
  • Contribution instructions.
  • Basic CI.

Week 2: Discovery

  • Many limitations discovered around:
    • winit event loop and WASM event loop requirements
    • web worker threading requirements
    • audio loading requirements
    • texture loading requirements
  • Assets load from HTTP source

Week 3: 90% Of The Time We Don’t Crash

  • UI pass works
  • WASM app doesn’t crash 90% of the time from double borrow mut in winit.

Week 4: Sounds Good

  • :paintbrush: GL depth buffer fix – rendering is corrected in more cases.
  • :musical_note: Audio now plays

Week 5: Tidying

New this week:

  • Input bindings are loaded from server. (#2214)

  • amethyst_test updated to work with winit 0.22.0 event loop. (#2245)

    This means if you want to run tests on CI with the GL rendering backend, it is now possible – set up XVFB and run tests through that.

Week 6 Update: Fin Ack

New this week:

These were based on the End-to-end issue discovery issue.


Please add yourself to the list.

Who What are you happy / keen to work on When are you actively available
@jojolepro parallel/specs/general compilation/wasm GOL example(mozilla contract thingy) Feb 28 to March 8, 5h/d
@azriel91 happy to work on most things, less on graphics / math / 3d stuff March 16th to April 24th, 20h/w
@ishitatsuyuki audio first, maybe rendering/windowing later Until March 25th

@azriel91 I think it would be good to make this public. I wanted to reply to this fellow here, but it would be good to be able to show the community this doc I think:

1 Like

And now he wants to help, so I definitely think this should be public. :slight_smile:


Yeaps! Have made it public – the more help we can get, the better :v:


WASM commits

Jojolepro – amethyst:wasm


  • Top down approach – try to get amethyst to compile for wasm with minimal features, then incrementally enable crates.
  • Partial update of winit to 0.21.
  • Places crates / code behind feature toggles.
  • f6dd55e4

    • removes backtrace from amethyst_error

    • places the following crates under a feature flag in Cargo.toml:

      • amethyst_controls
      • amethyst_input
      • amethyst_ui
      • amethyst_utils
      • amethyst_window
      • winit
      • failure
  • 2c9a78f2: adds #[cfg(feature = "renderer")] in main amethyst crate.

  • ebeec5fb

    • Bumps winit to 0.21 and updates features.

    • Puts the following behind feature flags:

      • amethyst_audio
      • amethyst_ui
    • Removes #[cfg(feature = "renderer")] from src/app.rs:30

Semtexv – amethyst:rendy-all (based on #2040)


  • Updates winit to 0.21, including Event and screen logical / physical size changes.
  • Updates rendy to 0.5.1.
  • Updates most (all?) examples to properly run with winit's new event loop mechanism.
Interesting diff
/// # Examples
/// ~~~no_run
/// let event_loop = EventLoop::new();
/// let mut game = Application::new(assets_dir, Example::new(), game_data)?;
/// game.initialize();
/// event_loop.run(move |event, _, control_flow| {
///     #[cfg(feature = "profiler")]
///     profile_scope!("run_event_loop");
///     log::trace!("main loop run");
///     game.run_winit_loop(event, control_flow)
/// })
/// ~~~

Omni-viral – amethyst:gl (PR #1877)


  • The first attempt that compiled and run.

  • Updates winit to 0.20.0-alpha2.

  • Disables the following crates:

    • amethyst_audio
    • amethyst_network

    Also for amethyst_gltf:

    • mikktspace
  • Removes usage of rayon::ThreadPool.

  • Changes shader compilation script, and recompiles all shaders.

Notable diffs
  • amethyst/Cargo.toml

    [target.'cfg(target_arch = "wasm32")'.dev-dependencies]
    console_log = "0.1"
    console_error_panic_hook = "0.1"
    wasm-bindgen = "0.2"
    # ..
    gfx-backend-dx12 = { git = "https://github.com/omni-viral/gfx", rev = "8a81717ce157e659bda04d740c1eaabee0d3a66e" }
    gfx-backend-empty = { git = "https://github.com/omni-viral/gfx", rev = "8a81717ce157e659bda04d740c1eaabee0d3a66e" }
    gfx-backend-gl = { git = "https://github.com/omni-viral/gfx", rev = "8a81717ce157e659bda04d740c1eaabee0d3a66e" }
    gfx-backend-metal = { git = "https://github.com/omni-viral/gfx", rev = "8a81717ce157e659bda04d740c1eaabee0d3a66e" }
    gfx-backend-vulkan = { git = "https://github.com/omni-viral/gfx", rev = "8a81717ce157e659bda04d740c1eaabee0d3a66e" }
    gfx-hal = { git = "https://github.com/omni-viral/gfx", rev = "8a81717ce157e659bda04d740c1eaabee0d3a66e" }
    winit = { git = "https://github.com/omni-viral/winit", rev = "e9b72797e0d57d64cabd5a92d0ec310fda3817e8" }
    spirv_cross = { git = "https://github.com/omni-viral/spirv_cross", rev = "b27d886dad4dd9f00c0f7e8434ecfc7620b702be" }
    rendy = { git = "https://github.com/omni-viral/rendy", rev = "d15cc1bf360cf0ef9962972a8c829e81e196fc39" }
  • unsafe impl Send/Sync for winit::..::EventLoop and Window. See struct St<T>.

Jojolepro – web_worker


  • Allows rayon::ThreadPool to be used by using a custom constructor.

Jaynus – rendy:jaynus-fixes


  • Bumps gfx-hal to latest git master as of march 11, 2020
  • Compiles rendy/rendy examples to WASM using wasm-bindgen

Heya all, I’ve created a Github Project board where you may pick up something to work on.
I’ve also written a Contribution guide to make it easier to get started.
Other links are in the guide / the first post in this thread.

Yes, the wasm branch of amethyst does compile to a wasm pkg:

# in amethyst
$ wasm-pack build -- --features "wasm gl"
# ..
[INFO]: :-) Done in 37.87s
[INFO]: :-) Your wasm pkg is ready to publish at ./pkg.

Update! @chemicstry managed to get (single-threaded) pong_wasm running:

There’s structural changes coming for the application event loop, so we’ll be figuring that out, and creating more issues which hopefully are easy to pick up and be worked on in parallel.

1 Like

Hey, yep, I’ve got the pong running. Although paddles are rendered every other frame and ball sprite doesn’t look that great. This required numerous hacks, but I’ve learnt what still needs to be done for proper wasm support so we can split that into separate issues.

  • https://github.com/jojolepro/web_worker is a great abstraction that allows using web workers as a rayon ThreadPool, however there is one big wasm limitation. The main javascript thread cannot use Atomic.wait and that means no Mutex, RWLock or thread join in it. The only way to synchronise with the main thread is postMessage from within worker. Maybe some kind of main thread synchronisation could be implemented in the web_worker library? The biggest blocker here is shred dispatcher, because it has to wait for all threads to finish executing (https://github.com/amethyst/shred/blob/master/src/dispatch/dispatcher.rs#L60). The simple solution for now is to use single-threaded shred (parallel asset loading still works). Proper solution is a bit tricky because dispatcher is deep in the call stack and either the whole stack has to be made async and be executed by a custom runtime that reacts to worker messages or split all functions in the stack into 2 (pre and post).
  • Continuing async problems, webgl rendering has to be done in requestAnimationFrame callback, otherwise screen tearing and other nasty effects happen. Something to watch https://github.com/rust-windowing/winit/issues/1305. Also, if we do ECS dispatching asynchronously, we need to do that in between requestAnimationFrame callbacks, so that rendering is executed synchronously.
  • cpal causes crash with new winit on windows. There is a patch but it’s not merged: https://github.com/rust-windowing/winit/issues/1255
  • create_surface causes a crash on GL in https://github.com/amethyst/amethyst/blob/wasm/amethyst_rendy/src/plugins.rs#L97. I’m not an expert of rendy, but I believe that only one surface is available in GL and it is created when initializing rendy. Easy fix: put surface into resources here https://github.com/amethyst/amethyst/blob/wasm/amethyst_rendy/src/bundle.rs#L110 and take it out in plugin instead of creating new one. Or rewrite function signatures so surface is propagated to plugins.
  • FrameLimiter is not used for wasm and should be disabled for wasm (browser controls rendering speed by requestAnimationFrame callbacks). Either add conditional compilation everywhere it’s used or just make FrameLimiter::wait empty on wasm.
  • spirv_cross is used to make shader compilation cross-platform. However, wasm implementation is a hacky workaround. Instead of compiling C++ into rust, C++ is compiled into wasm with emscripten and then rust bindings call javascript functions. The immediate problem of this is that spirv_cross_wrapper_glsl.wasm and spirv_cross_wrapper_glsl.js files have to be present in the final distribution and be served by HTTP. https://github.com/grovesNL/spirv_cross/issues/97
  • Similar problem with web workers from https://github.com/jojolepro/web_worker. Web worker entry point has to be a javascript file and final distributions will have to provide worker.js file. Probably part of the larger bundling problem.
  • https://github.com/jojolepro/web_worker has to be compiled on rust nightly according to https://github.com/rustwasm/wasm-bindgen/blob/master/examples/raytrace-parallel/build.sh. Can only be compiled with web or no-modules targets because webpack does not support wasm atomics. Also a bundling problem.
  • winit has problems with window events and panics with double borrows

Week 3 Update: 90% Of The Time We Don’t Crash


New this week:

  • UI pass works
  • WASM app doesn’t crash 90% of the time from double borrow mut in winit.


Good starter issues:


Week 4 Update: Sounds Good

New this week:

  • :paintbrush: GL depth buffer fix – rendering is corrected in more cases.
  • :musical_note: Audio now plays


Here are the less curly issues available for this week :v::


Week 5 Update: Tidying

New this week:

  • Input bindings are loaded from server. (#2214)

  • amethyst_test updated to work with winit 0.22.0 event loop. (#2245)

    This means if you want to run tests on CI with the GL rendering backend, it is now possible – set up XVFB and run tests through that.

Not much to see visually this time, but promise the next one will be better :v:

New issues this week:

  • Colour correction: #2246
  • UI coordinate correction: #2247

and added potential solution to explore for parallel dispatch (rough exploratory steps are written):

  • Get parallel shred working: #2191

Week 6 Update: Fin Ack

New this week:

These were based on the End-to-end issue discovery issue.

The corrected UI coordinates and network play can be seen in this video:

House keeping

Github projects are meant to be long lived; whereas milestones are the categorization tool for for time scoping, so I renamed the “WASM Rush” project to “WASM”, and placed all the completed issues under the “WASM Rush” milestone.

The final item to do is the project report. I’ve updated all other issues to be in then newly created “WASM Usability 1” milestone.


Wow @azriel91 that’s great. Good job everyone, and thanks for the regular updates on progress!

1 Like

Presented at the local Rust meetup :crab: