NEVER Do (Expert Web Rules)
Persistence & Storage
- NEVER use FileAccess alone for persistent web saves β Prefer web_local_storage_wrapper.gd (
localStorage/ IndexedDB viaJavaScriptBridge). - NEVER assume localStorage is permanent β Implement cloud-save fallback for production.
Rendering & Logic
- NEVER use the Forward+ renderer for web β Use Compatibility (WebGL 2.0).
- NEVER block the browser event loop β Long sync work β "Kill the Page." Use
await/ threaded workers where available. - NEVER ignore COOP/COEP β Threads/
SharedArrayBufferneed cross-origin isolation.
UX & Security
- NEVER forget tab focus loss β Pause audio on
visibilitychange. - NEVER trigger Fullscreen/Mouse Lock without a click β Must be inside a user gesture.
- NEVER use absolute paths in HTML shells β Relative paths for subdirectory hosting.
Host checklist (procedure)
- HTTPS β Required for many browser APIs (clipboard, some storage policies, secure contexts).
- COOP / COEP β Serve isolation headers when enabling threads /
SharedArrayBuffer(see exporting-for-web docs). - Relative shell paths β Custom
index.html/ PCK/WASM URLs must be relative so/game/subpaths work. beforeunloadβ Wire web_navigation_guard.gd when unsaved progress exists.- Compatibility renderer + texture compression β Desktop browsers: S3TC/BPTC as appropriate; keep particle/draw budgets low.
Available Scripts
MANDATORY: For saves, load web_local_storage_wrapper.gd β do not paste
JavaScriptBridge.eval("localStorage.setItem...")string recipes.
web_local_storage_wrapper.gd
Quota-safe localStorage via get_interface + JSON (no eval string interpolation).
web_javascript_bridge_callback.gd
Two-way JSβGD with create_callback (keep callback refs alive).
web_responsive_canvas_adaptor.gd
Canvas resize to browser viewport.
web_browser_input_guard.gd
Suppress context menu / spacebar scroll defaults.
web_resource_lazy_loader.gd
Remote PCK/resource fetch patterns.
web_clipboard_interface.gd
Async clipboard via Navigator API.
web_visibility_auto_pause.gd
Pause engine/audio on tab hide.
web_navigation_guard.gd
beforeunload unsaved-progress guard.
web_external_url_opener.gd
window.open with noopener.
web_performance_profiler.gd
VRAM/draw stats to JS console.
Also in scripts/
- platform_web_patterns.gd β Misc web feature gates.
- web_bridge_sync.gd β Structured bridge sync helper.
- web_json_rpc_bridge.gd β JSON-RPC
create_callbackbridge (keep refs alive).
Expert WHY (critical)
CAUTION: Never persist via
JavaScriptBridge.eval("localStorage.setItem('%s')" % data)β injection/escaping bugs. Use web_local_storage_wrapper.gd.
- PWA updates β
pwa_update_availableβpwa_update()whenpwa_needs_update(). - WebGPU β not a Godot 4.x web renderer; ship Compatibility (WebGL 2.0).
- JSON-RPC host page β structured bidirectional bridge: web_json_rpc_bridge.gd.
Deep dive (load on demand)
PWA lifecycle, JSON-RPC bridge, localStorage anti-patterns, size knobs β references/web-elite-patterns.md.
Loading shell (custom HTML)
<!-- index.html custom loading β keep asset URLs relative -->
<div id="loading-screen">
<div class="progress-bar"><div id="progress" style="width: 0%"></div></div>
<p id="status-text">Loading...</p>
</div>
<script>
const engine = new Engine(CONFIG);
engine.startGame({
onProgress: function(current, total) {
const percent = Math.floor((current / total) * 100);
document.getElementById('progress').style.width = percent + '%';
document.getElementById('status-text').innerText = `Loading ${percent}%`;
}
}).then(() => {
document.getElementById('loading-screen').style.display = 'none';
});
</script>Feature gate
if OS.has_feature("web"):
# Web-only: storage wrapper, visibility pause, navigation guard
passSize / perf knobs
[rendering]
textures/vram_compression/import_s3tc_bptc=true
textures/vram_compression/import_etc2_astc=true- Target ~60 FPS mid-range browsers; cut particles, draw calls, huge textures.
- Keep download under a practical budget (~50MB) via exclude filters on docs/source.
PWA update hook
func _ready() -> void:
if OS.has_feature("web"):
JavaScriptBridge.pwa_update_available.connect(_on_pwa_update)
func _on_pwa_update() -> void:
if JavaScriptBridge.pwa_needs_update():
JavaScriptBridge.pwa_update()Reference
Progressive disclosure: open Official Documentation links only when researching a specific API; load Related Skills when routing work to a peer domain β do not preload the whole lattice.
Official Documentation
- Exporting for the Web β Export presets, Compatibility/WebGL limits, and why COOP/COEP isolation is required for threads/
SharedArrayBuffer. - Web export β Platform overview for HTML5 hosting constraints before wiring browser-only APIs.
- The JavaScriptBridge singleton β
eval,get_interface, andcreate_callbackcontracts for two-way GDβJS bridges. - Custom HTML page for Web export β Relative asset paths and custom loading shells so builds work under subdirectories.
- HTML5 shell class reference β
Engine/startGameprogress callbacks used by custom loading screens. - JavaScriptBridge β PWA update signals plus clipboard/download helpers that must keep JS callback refs alive.
- Introduction to the 3 rendering methods β Why web exports should ship Compatibility (WebGL 2.0) instead of Forward+.
- File paths in Godot projects β Why
user:///FileAccesspersistence is unreliable in browser sandboxes versuslocalStorage/IndexedDB. - Saving games β Serialize/version save payloads before wrapping them in browser storage APIs.
- Multiple resolutions β Stretch/aspect/content scale paired with HTML canvas resize policy for responsive web viewports.
- Handling quit requests β Pause/resume lifecycle that maps to tab
visibilitychangeandbeforeunloadguards. - GPU optimization β Draw-call, texture, and particle budgets that dominate WebGL frame time.
Related Skills
Prerequisites
- godot-project-foundations β Feature tags (
web), Compatibility renderer defaults, and display stretch settings every HTML5 export branch depends on. - godot-input-handling β InputEvent ownership before suppressing browser defaults (context menu, spacebar scroll) or remapping canvas focus.
- godot-save-load-systems β Versioned save ownership and cloud-fallback hooks that
localStoragewrappers must not invent ad hoc.
Complements
- godot-platform-mobile β Touch/lifecycle patterns that overlap when the same build is played in mobile browsers.
- godot-platform-desktop β Keep native desktop paths healthy when one project dual-targets desktop + web.
- godot-audio-systems β Bus/voice control used when tab visibility must pause playback to avoid background audio.
- godot-performance-optimization β Profiling and CPU/GPU cuts when WebGL budgets still miss 60 FPS after Compatibility + compression.
- godot-ui-containers β Control layout that stays readable as the browser canvas resizes across desktop and phone web.
- godot-autoload-architecture β Singleton homes for JavaScriptBridge managers, storage wrappers, and always-on visibility guards.
- godot-multiplayer-networking β WebSocket/WebRTC peers that replace LAN assumptions once the game is browser-hosted.
- godot-resource-data-patterns β PCK/resource packing contracts consumed by remote lazy-load fetch flows on web.
Downstream / consumers
- godot-export-builds β CI presets, artifact hosting, and size gates after browser APIs and Compatibility settings are locked in.
Master
- godot-master β Library router and mirrored module entry for discovering this platform skill beside sibling domains.