Default index.html
This is the default index.html
that bevy build web
and bevy run web
uses to load your application. You may customize index.html
by creating a web/index.html
to override the default. The default is provided below, so you can copy it instead of writing your own from scratch:
Warning
The default index.html
has the following line:
import init from "./build/bevy_app.js";
You will need to replace bevy_app
with the name of your compiled binary. This is usually your crate's name, but it can be customized in Cargo.toml
with the [[bin]]
table, so be careful!
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<style>
/* Styles for the loading screen */
:root {
--web-bg-color: #2b2c2f;
--web-color: white;
}
* {
margin: 0;
padding: 0;
border: 0;
}
html,
body {
width: 100%;
height: 100%;
}
.center {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
body {
background-color: var(--web-bg-color);
color: var(--web-color);
}
.spinner {
width: 128px;
height: 128px;
border: 64px solid transparent;
border-bottom-color: #ececec;
border-right-color: #b2b2b2;
border-top-color: #787878;
border-radius: 50%;
box-sizing: border-box;
animation: spin 1.2s linear infinite;
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
</style>
</head>
<body class="center">
<noscript>JavaScript support is required to run this app</noscript>
<div id="loading-screen" class="center">
<span class="spinner"></span>
</div>
<script type="module">
// Automatically restart the audio context after user interaction
// Needs to be executed _before_ the game is loaded
// Taken from https://developer.chrome.com/blog/web-audio-autoplay/#moving-forward
(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
if (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">
// Starting the game
// When this file is used as the default `index.html`, the CLI will automatically replace
// `bevy_app.js` with the name of the generated JS entrypoint. If you copy this file and
// customize it, you will need to manually change the name. For more information, please see
// <https://thebevyflock.github.io/bevy_cli/cli/web/default-index-html.html>!
import init from "./build/bevy_app.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>
<script type="module">
// Hide loading screen when the game starts.
const loading_screen = document.getElementById("loading-screen");
const observer = new MutationObserver((records) => {
for (const record of records) {
for (const addedNode of record.addedNodes) {
if (addedNode instanceof HTMLCanvasElement) {
if (addedNode.innerText.trim().length === 0) {
// Add compatibility note
addedNode.innerText =
"Canvas support is required to run this app";
}
// A new canvas has been created, which means that the game has been loaded
// Hide the loading screen!
loading_screen.style.display = "none";
observer.disconnect();
return;
}
}
}
});
observer.observe(document.body, {
subtree: false,
childList: true,
attributes: false,
characterData: false,
});
</script>
</body>
</html>