Bevy Version:0.12(outdated!)

As this page is outdated, please refer to Bevy's official migration guides while reading, to cover the differences: 0.12 to 0.13, 0.13 to 0.14. 0.14 to 0.15.

I apologize for the inconvenience. I will update the page as soon as I find the time.


Create a Custom Web Page

If you want full control over your website, such as if you are a web developer and you want to embed your Bevy game into a nice website you made, this page will offer some tips.

wasm-bindgen

This is the "low level" tool for exporting/preparing a Rust WASM binary, so it can be integrated into HTML/JS. It generates the bridge to JavaScript, so that Rust/Bevy can work with the browser.

You will need to run it whenever you rebuild your game, to process the WASM binaries generated by cargo.

You can install it using cargo:

cargo install wasm-bindgen-cli

Now, to build your game, run:

cargo build --release --target wasm32-unknown-unknown
wasm-bindgen --no-typescript --target web \
    --out-dir ./out/ \
    --out-name "mygame" \
    ./target/wasm32-unknown-unknown/release/mygame.wasm

You need to provide the path to the compiled WASM binary in cargo's target directory. It will be renamed according to the --out-name parameter.

./out/ is the directory where it will place the processed files. You will be uploading these files to your server. You need to also put the assets folder there. Bevy will expect to find it alongside the WASM file.

The final list of files for a minimal website will look something like this:

assets/ index.html mygame.js mygame_bg.wasm

In a more compex website, you might want to have the game files be in a subdirectory somewhere, and load them from a HTML file elsewhere.

For the HTML file, you can use this as a starting point:

index.html
<!doctype html>
<html lang="en">

<body style="margin: 0px;">
  <script type="module">
    import init from './mygame.js'

    init().catch((error) => {
      if (!error.message.startsWith("Using exceptions for control flow, don't mind me. This isn't actually an error!")) {
        throw error;
      }
    });
  </script>
</body>

</html>

Note: change mygame.js above to the actual name of the file outputted by wasm-bindgen. It will match the --out-name parameter you provided on the commandline.

This minimal index.html will just display the Bevy game, without giving you much control over the presentation. By default, Bevy will create its own HTML canvas element to render in.

You can optionally run tools like wasm-opt on the final WASM file, to optimize the WASM further for size. Run such tools after wasm-bindgen, not on the original WASM file. Otherwise, wasm-bindgen will panic with an error if you give it a file processed with wasm-opt.

Embedding into a complex web page

You probably want control over how/where the game is displayed, so you can place it on a fancier web page, alongside other content.

IFrame

A simple/hacky way is using an IFrame. The advantage is that you don't need any modifications to the Rust code.

You can create a minimal index.html as was shown previously.

You can then embed that into your larger webpage using a HTML IFrame element:

<iframe id="mygame-iframe" src="wasm/index.html" width="1280" height="720"></iframe>

You can place it wherever you like on your web page and style it however you like using CSS. It is recommended to explicitly specify its dimensions.

Make sure to use the correct path to the HTML file in src. You might want to rename/move it according to your website's needs.

Custom Canvas

A more elegant way to accomplish this is by using your own canvas element. You don't need a separate HTML file.

Create a HTML canvas and give it an ID string of your choice.

<canvas id="mygame-canvas" width="1280" height="720"></canvas>

You can place it wherever you like on your web page and style it however you like using CSS. It is recommended to explicitly specify its dimensions.

On the Rust side, we need to tell Bevy the ID of the canvas element, so it can use our canvas instead of trying to create its own.

fn main() {
    let mut app = App::new();
    app.add_plugins(DefaultPlugins.set(WindowPlugin {
        primary_window: Some(Window {
            // provide the ID selector string here
            canvas: Some("#mygame-canvas".into()),
            // ... any other window properties ...
            ..default()
        }),
        ..default()
    }));
    // ...
    app.run();
}

Unfortunately, this means if you want to rename the ID of the canvas, you will have to make sure to update the Rust code and rebuild/redeploy the game.

General Advice

Bevy WASM binaries are big. Even when [optimized for size][wasm::opt-size], they can be upwards of 30MB (reduced down to 15MB with wasm-opt).

To make your page fast to load, you might want to delay the loading of the WASM. Let the user see and interact with the page before you trigger it.

You could use some JavaScript to detect when the user clicks on the canvas, or have a special button or link to trigger it.

Further, after the WASM loads and your Bevy game is running, your game will probably want to load assets at runtime. Make sure your assets are well-compressed/optimized, so they can load quickly. Try to design your game so that it isn't unresponsive or making the user suffer annoying waits.