A plugin for the Bevy Remote Protocol that exposes remote methods to Wasm/JavaScript.
Check the Bevy Remote Playground at https://splo.github.io/bevy-remote-playground.
It is a web app that loads a Bevy Wasm module built on a bevy-remote-wasm-enabled app and provides a UI to explore the exposed methods and their results.
-
Add
bevy_remoteto your dependencies, andbevy_remote_wasmonly forwasmtargets.[dependencies] bevy = "0.18" bevy_remote = { version = "0.18", default-features = false } [target.'cfg(target_family = "wasm")'.dependencies] bevy_remote_wasm = "0.1"
Don't depend on
bevy/bevy_remote(bevy = { version = "0.18", features = ["bevy_remote"] }) because it enables the defaulthttptransport, which does not compile on Wasm target (until this fix is merged and released).This is why it is recommended to depend on the separate crate
bevy_remotewith no default features (sobevy_remote/httpis not enabled). -
Add
RemotePluginto your app, and addRemoteWasmPluginonly when compiling for Wasm.use bevy::prelude::*; use bevy_remote::RemotePlugin; fn main() { let mut app = App::new(); app.add_plugins((DefaultPlugins, RemotePlugin::default())); #[cfg(target_family = "wasm")] app.add_plugins(bevy_remote_wasm::RemoteWasmPlugin); app.run(); }
-
Build for the
wasm32-unknown-unknowntarget, then generate the JS bindings withwasm-bindgendirectly (wasm-bindgen --target web --out-dir <output_dir> target/wasm32-unknown-unknown/debug/<crate_name>.wasm) or through a tool such aswasm-packor Trunk. -
In JavaScript, load the generated JS module, call
init(), then awaitgetBridge():import init, { getBridge } from "/example.js"; await init(); const bridge = await getBridge(); const response = await bridge.main["rpc.discover"](); console.log(response.info.version);
The generated module path and
initfunction depend on your build tool, but the initialization order does not: initialize the Wasm module first, then await the bridge.getBridge()may be called as soon as the JS module loads, but it resolves only after Bevy publishes the bridge during app startup, which, depending on your setup, may take some time.The
wasm-bindgenoutput also includes TypeScript declarations for the root bridge (BrpBridge) and the default remote methods (BuiltInBrpBridge):import type { BuiltInBrpBridge } from "./example.js"; const bridge = await getBridge() as BuiltInBrpBridge; const response = await bridge.main['world.query']({ data: { option: 'all' } }); // Enjoy docs and autocompletion for built-in methods!
-
Serve the generated HTML, JS, and
.wasmfiles together from a web server or bundler that supports Wasm imports.
A minimal example can be found in examples/minimal.rs for the smallest Bevy setup and apps/get-version/index.html for an inline HTML module example.
The plugin builds a bridge object with app-scoped method maps based on the methods registered with bevy_remote::RemotePlugin, more specifically through the bevy_remote::RemoteMethods resource. Each time this resource is updated, the bridge is re-published with the new methods. The getBridge() function must be called again to get the updated bridge.
For Bevy 0.18, the bridge exposes only the main app. Method keys inside that namespace use the BRP method names directly, so call them with bracket notation such as bridge.main["world.query"] and bridge.main["world.list_components+watch"].
- Instant methods (run once, return a result):
// Call the instant method with params but without callback.
// Returns the result wrapped in a Promise.
const result = await bridge.main['world.query']({ data: { option: 'all' } });
console.log(result);
// Call the instant method with params and a callback that will be called with the result.
// Returns `undefined` wrapped in a Promise.
await bridge.main['world.query']({ data: { option: 'all' } }, (result) => console.log(result));- Watching methods (stream results):
// Call the watching method with params and a callback that will be called on each result.
// Returns a closer function wrapped in a Promise.
const close = await bridge.main['world.list_components+watch']({ entity: 123 }, (result) => console.log(result));
// Stop the stream.
close();| Bevy | Bevy Remote Wasm |
|---|---|
0.18 |
0.1 |
This project is built with:
- Rust and Cargo for the core crate.
- Bevy and the Bevy Remote Protocol for the runtime API.
- WebAssembly and wasm-bindgen for the Wasm to JavaScript bridge.
- TypeScript and npm for the generated declaration files and the companion types package.
- just for common development tasks.
Some just recipes depend on external tools and will fail fast if they are not installed.
Use the Justfile for the usual development workflow:
- Validate the whole repository with
just check. This runs Rust linting, formatting checks, a crate publish dry-run, and the TypeScript package check. - Format the Rust code with
just format. This usescargo +nightly fmt, so a nightly toolchain is required for formatting. - Run the Wasm tests with
just test. Tests run on thewasm32-unknown-unknowntarget withwasm-bindgen-test-runner. - Discover available demos with
just list-examplesandjust list-apps. - Build and serve demos locally with
just run-example. By default this builds theminimalexample with theget-versionapp and serves it onhttp://localhost:8080. - Build demo artifacts without serving them with
just build-example. Generated browser assets are written undertarget/dist/<app>.
The repository is split into a few small parts:
src/contains the coreRemoteWasmPluginimplementation and the Wasm bridge exported to JavaScript.build.rscombines the TypeScript declaration files into a single output thatwasm-bindgenembeds in the generated bindings.examples/contains Rust-side Bevy examples such as the minimal app and the 3D demo.apps/contains browser-facing HTML and JavaScript apps that load those examples and exercise the bridge.bevy-remote-wasm-types/contains the type-only NPM package and its declaration files.
A good contribution flow is: update the Rust bridge in src/, keep the embedded TypeScript surface in sync and run one of the example apps locally. Validate with just check before submitting a pull request.
A GitHub Actions CI workflow runs on pushes to main and on pull requests.
Dual-licensed under Apache-2.0 and MIT. See LICENSE-APACHE and LICENSE-MIT.