Steps to Run a Rust+Bevy Project in the Browser with wasm-bindgen
This article assumes you have basic familiarity with Rust, and want to figure out how to run your Bevy+Rust project in a web browser.
Follow these steps to get your Rust+Bevy project running in the browser using WebAssembly with the wasm-bindgen CLI.
0. Setup
Step 0.0: Create index.html
File
Create an index.html
file in your ./web
directory with the following content:
<html>
<head>
<meta charset="UTF-8" />
<style>
body {
margin: 0;
background: darkgrey;
background-size: 400% 400%;
animation: gradient 15s ease infinite;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
.loader {
border: 16px solid #f3f3f3;
border-radius: 50%;
border-top: 16px solid #3498db;
width: 120px;
height: 120px;
position: absolute;
z-index : -999;
-webkit-animation: spin 2s linear infinite;
animation: spin 2s linear infinite;
}
@-webkit-keyframes spin {
0% { -webkit-transform: rotate(0deg); }
100% { -webkit-transform: rotate(360deg); }
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
</style>
</head>
<body>
<div class="loader"></div>
<script>
// the following function keeps track of all AudioContexts and resumes them on the first user
// interaction with the page. If the function is called and all contexts are already running,
// it will remove itself from all event listeners.
(function () {
// An array of all contexts to resume on the page
const audioContextList = [];
// An array of various user interaction events we should listen for
const userInputEventNames = [
"click",
"contextmenu",
"auxclick",
"dblclick",
"mousedown",
"mouseup",
"pointerup",
"touchend",
"keydown",
"keyup",
];
// A proxy object to intercept AudioContexts and
// add them to the array for tracking and resuming later
self.AudioContext = new Proxy(self.AudioContext, {
construct(target, args) {
const result = new target(...args);
audioContextList.push(result);
return result;
},
});
// To resume all AudioContexts being tracked
function resumeAllContexts(_event) {
let count = 0;
audioContextList.forEach((context) => {
if (context.state !== "running") {
context.resume();
} else {
count++;
}
});
// If all the AudioContexts have now resumed then we unbind all
// the event listeners from the page to prevent unnecessary resume attempts
// Checking count > 0 ensures that the user interaction happens AFTER the game started up
if (count > 0 && count === audioContextList.length) {
userInputEventNames.forEach((eventName) => {
document.removeEventListener(eventName, resumeAllContexts);
});
}
}
// We bind the resume function for each user interaction
// event on the page
userInputEventNames.forEach((eventName) => {
document.addEventListener(eventName, resumeAllContexts);
});
})();
</script>
<script type="module">
import init from '/out/<your-project-name-here>.js'
init();
</script>
</body>
</html>
Remember to replace <your-project-name-here>
with the actual project name as specified in your Cargo.toml
file.
Step 0.1: Install wasm-bindgen CLI
First, install the latest version of the wasm-bindgen CLI by running:
cargo install -f wasm-bindgen-cli
If you run into issues, you may need to install a specific version. For example:
cargo install -f wasm-bindgen-cli --version 0.2.93
This version might be outdated, so it's best to keep things up to date.
Step 0.2: Add WebAssembly Target
In your project directory, add web support by running:
rustup target add wasm32-unknown-unknown
This step is required only once per project.
1. Build and Run the Project
Step 1.1: Build the Project for Web
To compile your project to WebAssembly, run:
cargo build --release --target wasm32-unknown-unknown
This needs to be done every time you want to build your project for the web.
Step 1.2: Generate WebAssembly Bindings
Next, generate the WebAssembly bindings by running:
wasm-bindgen --out-dir ./out/ --target web ./target/wasm32-unknown-unknown/release/<your_project_name>.wasm
Replace <your_project_name>
with the actual project name as specified in your Cargo.toml
file.
Step 1.3: Copy Output Directory
Copy the ./out
directory to ./web/out
by running:
cp -r ./out ./web/out
Step 1.4: Serve the Project Locally
You can now serve your project locally with:
npx serve web
Open localhost
on the specified port to see your Rust+Bevy project running in the browser.
--
Step 2: Deploying
You can deploy your /web folder to a static hosting service such as Vercel, Netlify, Github Pages, AWS S3, or Render - for example.
Make sure you've included your /out directory in the web folder, along with the index.html code.