Custom Godot WebGL Template: Loading Screen, JS Bridge, and the HTML Shell
Custom Godot WebGL Template: Loading Screen, JS Bridge, and the HTML Shell
Honesty note: this is Godot’s documented template system as of 4.x, mapped for a browser-game use case. I ran my Godot H5 test with the default template and hit its two limits (plain loading UI, no easy JS bridge) — the fixes below are the standard documented ones (my Godot context).
TL;DR
- Every Godot web export is an HTML shell + engine files:
index.html(the template),index.wasm,index.pck,index.js. The template is just an HTML page that loads the engine — and you can replace it (file set). - Customizing the template means copying Godot’s default HTML and editing it: export → HTML5 → “HTML Shell” setting points to your
.htmlfile. Common edits: a branded loading screen, progress bar, and post-load callbacks. - JavaScriptBridge is the JS↔GDScript bridge:
JavaScriptBridge.eval()/JavaScriptBridge.get_interface()call JS from GDScript, and you can expose GDScript callbacks to the page. This is how a template can drive game UI, or the page can drive the game (e.g. portal SDK calls). - The template is also your embed contract: when a portal iframes your game, the template’s
startGame/ resize logic decides how it behaves in the iframe (portal reality). Keep the default template structure intact and extend it.
How the template system works
Export → HTML5 → HTML Shell: a file path (default is Godot’s built-in godot.template.html). The engine looks for specific elements/IDs in the shell to attach to; the documented contract:
- The shell HTML contains placeholders the export fills in (engine script tags, canvas).
startGame()(or equivalent) boots the engine when the page is ready.- Additional files in the same directory (e.g.
yourgame.js) are merged into the export.
The safe path: export once with the default shell to get the generated godot.template.html, copy it, edit the copy (loading text, styles, callbacks), and point the export at your copy. Don’t hand-write a shell from scratch — the contract details are easy to get wrong.
What to customize (in priority order)
- Loading screen — replace the default “Loading…” with a branded progress UI; the engine exposes progress callbacks the shell can listen to.
- Pre/post-load hooks — show a “Tap to start” overlay (mobile browsers need a user gesture for audio), fire
startGame()from a button. - Resize behavior — make the shell adapt the canvas to its container/iframe (portals size you; your own page can letterbox).
- JS bridge — call into the page or get called by it.
The JavaScriptBridge pattern (30 seconds)
# In GDScript: call a JS function on the page
JavaScriptBridge.eval("window.myGameBridge('level_complete')")
# Expose GDScript to the page: register a callback the page can call
# (via the template's JS calling the engine's exposed object)
Typical uses: send analytics events to the page’s tracker, trigger a portal SDK call, receive a “pause/resume” signal from the embedding page. On the web export, the bridge is the only sanctioned path between your GDScript and the host page (my JS/SDK reality).
Template + portal iframes (the honest part)
When a portal (CrazyGames/Poki) iframes your export, your template runs inside their page context:
- The template’s loading screen usually doesn’t show (the portal wraps it) — don’t over-invest in branding that only your own hosting shows.
- Iframe-safe behavior matters more: handle resize, don’t depend on top-window access, and use the portal’s SDK for ads/analytics rather than your own JS (portal SDK).
- The custom template earns its keep mainly on your own hosted build — the one you control (where it runs).
Pitfalls
- Hand-writing the shell — copy Godot’s generated template and edit; the contract is subtle.
- Over-branded loading UI — portals mask the template anyway; put the effort where your own hosting shows it.
- JS bridge without checking
JavaScriptBridgeavailability — the class only exists on web exports; guard for other platforms (touch note). - Forgetting resize/iframe behavior — a template that assumes full-window breaks inside an iframe.
- Mixing custom JS with the threaded build — the SAB/COOP/COEP constraints still apply to your shell’s third-party scripts (export warnings).
Bottom line
The Godot web template is just an HTML shell you can customize: copy the generated default, edit the loading screen and hooks, use JavaScriptBridge for page↔game calls, and keep iframe behavior in mind for portal embeds. The template is the web layer of your Godot game — and for the size/performance reality around it, the rest of my Godot posts cover the edges.