thedivergentai/godot-platform-web
Expert blueprint for HTML5/web export on Compatibility (WebGL 2.0): JavaScriptBridge, localStorage wrapper, custom loading shells, COOP/COEP hosts, relative paths, beforeunload, visibility pause, and size optimization. WebGPU is out of scope. Keywords: web, HTML5, WebGL, Compatibility, JavaScriptBridge, localStorage, COOP, COEP, canvas, browser API.
npx skills add https://github.com/thedivergentai/GD-Agentic-Skills --skill godot-platform-web
HTML5 export on Compatibility / WebGL 2.0. Browser storage, JS bridges, and host headers — not Forward+/WebGPU fantasy.
> Out of scope / future: WebGPU is not a supported Godot 4.x web renderer. Do not design production paths around it; ship Compatibility (WebGL 2.0) + VRAM compression.
localStorage / IndexedDB via JavaScriptBridge).await / threaded workers where available.SharedArrayBuffer need cross-origin isolation.visibilitychange.SharedArrayBuffer (see exporting-for-web docs).index.html / PCK/WASM URLs must be relative so /game/ subpaths work.beforeunload — Wire web_navigation_guard.gd when unsaved progress exists.> MANDATORY: For saves, load web_local_storage_wrapper.gd — do not paste JavaScriptBridge.eval("localStorage.setItem...") string recipes.
Quota-safe localStorage via get_interface + JSON (no eval string interpolation).
Two-way JS↔GD with create_callback (keep callback refs alive).
Canvas resize to browser viewport.
Suppress context menu / spacebar scroll defaults.
Remote PCK/resource fetch patterns.
Async clipboard via Navigator API.
Pause engine/audio on tab hide.
beforeunload unsaved-progress guard.
window.open with noopener.
VRAM/draw stats to JS console.
scripts/create_callback bridge (keep refs alive).> CAUTION: Never persist via JavaScriptBridge.eval("localStorage.setItem('%s')" % data) — injection/escaping bugs. Use web_local_storage_wrapper.gd.
pwa_update_available → pwa_update() when pwa_needs_update().PWA lifecycle, JSON-RPC bridge, localStorage anti-patterns, size knobs — references/web-elite-patterns.md.
<!-- 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>
if OS.has_feature("web"):
# Web-only: storage wrapper, visibility pause, navigation guard
pass
[rendering]
textures/vram_compression/import_s3tc_bptc=true
textures/vram_compression/import_etc2_astc=true
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()
> 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.
SharedArrayBuffer.eval, get_interface, and create_callback contracts for two-way GD↔JS bridges.Engine/startGame progress callbacks used by custom loading screens.user:///FileAccess persistence is unreliable in browser sandboxes versus localStorage/IndexedDB.visibilitychange and beforeunload guards.web), Compatibility renderer defaults, and display stretch settings every HTML5 export branch depends on.localStorage wrappers must not invent ad hoc.Take thedivergentai/godot-platform-web from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.