Godot
The Godot (web export) connector. Godot 4 compiles to WebAssembly and renders into a
<canvas>, so it is built on the web-export foundation and
works in two tiers: a JS-only tier (no engine code — pointer heatmaps, FPS, JS
errors) and a bridged tier (a thin copy-in shim adds camera pose, world-space
picks, and replay).
Install
Section titled “Install”npm install @uptimizr/godotimport { trackGodot } from "@uptimizr/godot";
const { client, bridge } = trackGodot({ projectId: "your-project", endpoint: "https://collect.example.com", canvas: () => document.querySelector("#godot-canvas"),});
// later, on teardownawait client.stop("manual");trackGodot creates the client, registers the JS-only tier collector, exposes the
engine bridge (default window.__uptimizr_godot__), and starts the session with
Godot’s connector provenance. The JS-only tier captures immediately; wire the
engine-side shim to bridge to add camera pose, picks, and replay.
Engine-side bridge
Section titled “Engine-side bridge”The bridged tier needs a thin copy-in shim — a JavaScriptBridge autoload that
samples the active Camera3D and calls the bridge each frame. It ships with the package as
a copy-in asset (not an npm dependency), in both GDScript and C#:
- Copy
UptimizrGodot.gd(orUptimizrGodot.csfor .NET projects) into your Godot 4 project. - Register it as a singleton: Project → Project Settings → Globals → Autoload, add the
script with node name
UptimizrGodot, and enable it.
On the next Web export the autoload finds window.__uptimizr_godot__ (exposed by
trackGodot), asserts the bridge protocol version, and starts pushing camera pose, FPS, and
left-click raycast picks automatically. Off the Web export it guards on
OS.has_feature("web") and is a no-op, so it is safe to leave enabled in every build.
For world-space object engagement and replay completeness, mark nodes with
add_to_group("uptimizr_tracked") and call UptimizrGodot.push_scene_proxy() once after
your scene is built. The full contract, options, and coordinate notes live in the package’s
bridge/README.md.
Verification status
Section titled “Verification status”| Tier | Status | Verified by |
|---|---|---|
| JS-only | Stable | Unit tests + the web-export Playwright round trip. |
| Bridged | Verified | An automated headless Godot 4.7.2 Web export driven by Playwright in CI: the real WASM build boots with the shipped UptimizrGodot.gd autoload, and the test asserts camera_sample (Z negated), mesh_interaction (a raycast pick naming the node), frame_perf, and the scene proxy reach the collector. |
Reference integration
Section titled “Reference integration”examples/godot-web-export
is the reference project the CI proof exports: a minimal Godot 4 scene with the autoload
registered, a Camera3D, and named pickable StaticBody3D props (Crate, Orb) that opt
into the scene proxy from main.gd. Its copy of the shim is checked byte-for-byte against
the package source, so the test always exercises the asset you copy in. Reproduce it locally
from the repo root:
pnpm godot:fetch # pinned headless editor + web_nothreads_release template (~85 MB)pnpm godot:export # headless --export-release Web → examples/godot-web-export/distpnpm test:e2e:godot # boots the export in the playground and asserts the round tripThe sample uses the nothreads Web template (variant/thread_support=false), so the
export runs without SharedArrayBuffer and the host page needs no COOP/COEP headers —
the simplest deployment for self-hosters.
Coordinate frame
Section titled “Coordinate frame”Godot’s native world frame is right-handed, y-up, meters, so the connector negates
Z to reach the canonical wire frame (left-handed, y-up). The engine-side shim does no
coordinate math. The session records Godot’s native frame in
connector.coordinateSystem.
Capture
Section titled “Capture”JS-only tier: pointer move/click → screen heatmaps, FPS / long frames → performance, JS errors. Bridged tier: camera pose → view-direction heatmap, world-space picks → object engagement, scene proxy, and replay.
Privacy
Section titled “Privacy”No client-side persistent IDs and no PII by default (ADR 0003). client.stop() tears
down every listener, timer, and animation-frame callback.