Debug inspector
Usage guide of debug.inspector: what the panel shows, who may open it, settings and defaults, cost, the gripforge.debug/1 API for pages and agents, the debug.open hook, kit modes.
Debug inspector of your game (debug.inspector) #
One debug panel for every game: frame time, GPU, rendering counters, wireframe, collision boxes, the scene tree, and the debug modes your kits add. The same data is available as JSON for a page and for agents. Readable offline: gripforge docs debug-inspector, or the MCP tool gripforge_docs (topic debug-inspector).
What it is for #
- Frame: frames per second, mean frame time, p50 / p95 / p99, the worst frame, frames over 33 ms (a window of the last 240 frames), and where the time of a frame goes.
- GPU: the backend (
webgpu,webgl2,vulkan,dx12,dx11,metal,opengl,gles), the adapter, its vendor and type, and whether it is a software rasteriser (the measures then say nothing about a real GPU). - Rendering: draw calls, drawn meshes, triangles, materials, lights, shadow casters, textures and their bytes, entities, memory.
- Modes: wireframe, collision boxes (the
sizeof every entity), the bounds of every drawn mesh. - Scene tree: every scene and its nodes and meshes, within a depth and node budget. A node can be hidden, outlined or selected (components, transform, mesh, the kit entity it draws).
- Kit modes: the modes other kits register (paths of the AI, display optimisation, and so on).
Turn it on #
debug.inspector is installed in every new game and open to everyone by default; existing games receive it at their next opening. Press F3 (on a Mac: fn + F3, or the fallback Ctrl + Alt + D). Nothing is loaded, measured, allocated or drawn until the key is pressed.
gripforge_gamekit_install { project_id: "gkp_…", id: "debug.inspector" }
gripforge_gamekit_configure { project_id: "gkp_…", id: "debug.inspector",
config: { "access": { "players": false }, "toggleKey": "F4" } }
gripforge_gamekit_configure { project_id: "gkp_…", id: "debug.inspector", config: { "enabled": false } } # off for the game
Removing the kit has the same effect as enabled: false: no key, no panel, no page or agent API. It requires input.actions and game.engine. The full reference is gripforge kit api debug.inspector.
Settings and defaults #
| Key | Default | Meaning |
|---|---|---|
enabled | true | the developer's switch; false turns the whole inspector off |
toggleKey | F3 | the key that opens and closes it; null = no key (then only ?debug=1 or the API). Free in every control scheme |
access.team | true | the owner, the members of the workspace and a local build (engine editor, desktop export run by its developer) |
access.players | true | public players (false: the team only) |
access.query | debug | ?debug=1 opens it at start for those allowed |
refreshFrames | 10 | frames between two snapshots while open |
treeDepth, treeMaxNodes | 6, 2000 | budget of the scene tree |
startModes | all false | wireframe, colliders, bounds on at start |
panel | { side: "right", width: 380, layer: "top" } | where the panel sits |
modeFrames | 15 | frames between two calls of an open kit mode |
The key goes through input.actions like every key. It is registered only when the inspector is enabled and the viewer is allowed, and it is never shown in the panel nor listed in a Controls screen.
Cost #
Closed and unread, the inspector costs one key test per frame. On the web nothing of the panel is loaded (code, markup, styles, passport requests) until the first press; the first press brings it in with one dynamic import. Open, the engine measures every frame within fixed bounds (a ring of 240 frame times, counts every 30 frames), rebuilds the boxes every 6 frames and the snapshot every refreshFrames. The scene tree is built only when asked, the wireframe within 600 000 edges.
The API (gripforge.debug/1) #
One contract on every engine. A snapshot is { schema, engine, open, sampling, frame, timing, gpu, render, modes, selection, hidden, outlined, tree?, details?, tick?, extensions, notes }. Ops are sent alone, in a list, or as { ops: [...] }:
| Op | Fields | Does |
|---|---|---|
snapshot | answers the snapshot (keeps the measures for 10 s when the panel is closed) | |
open, close | opens or closes the panel | |
mode | wireframe, colliders, bounds (booleans, any subset) | turns modes on or off |
tree | root?, depth?, maxNodes? | builds the scene tree once |
select | ids | selects nodes (outlined, details of the first) |
hide | id, hidden | hides or shows a node |
outline | id, on | outlines a node |
reset | shows every hidden node and removes every outline | |
extension | id, on | turns a kit mode on or off |
TICK (section of the F3 panel, field tick of the snapshot): the game logic tick, separate from the render frame and the physics step. It gives the requested and played rate (hz), the tick counter, ticks per frame, logic milliseconds per tick, the time dropped by catch-up, the interpolation alpha and the configured periods (tick.systems). The 15/20/30/60 buttons change the rate live (next tick boundary, nothing reset; in multiplayer only the authority).
Ways to reach it:
- Agents: MCP
gripforge_debug_snapshotandgripforge_debug_set; CLIgripforge debug snapshot <project>andgripforge debug set <project> '<ops>'. They act on a game open in a browser by a member of the team, with the same rights as the panel. - The page:
gripforgePresenter.debug(json)in the frame of the native player (synchronous: the last snapshot; the ops are applied at the next frame), and the runtime protocol operationsdebug_snapshot/debug_set. - A kit: the service
debug.inspector(allowed(),isOpen(),open(by),close(),toggle(by),set(ops),view(),modes(),addMode(mode),setMode(id, on),scenes()).
Hooks, events, registry #
| What | Name | Detail |
|---|---|---|
| Hook | debug.open | payload { by, viewer } (by: key, query, api; viewer: owner, member, local, player); cancellable. A game or a mod refuses it (a ranked match); the inspector stays closed and debug.refused follows |
| Events | debug.opened, debug.closed, debug.mode, debug.refused | |
| Registry | debug_modes | the kit modes; a game or a mod adds one (moddable, extends allowed) |
| Moddable config | enabled, toggleKey, access, refreshFrames, treeDepth, treeMaxNodes, startModes, panel, modeFrames | a mod changes them like the game does |
Add a mode #
A debug mode is data: { id: "<kit>.<name>", kit, label, kind, call, args?, on? }. kind is overlay (the call answers what to draw, { lines | paths | marks | boxes, color } in world units), info (rows for the panel) or scene (nodes listed beside the scene tree, for a kit with its own notion of scene). While a mode is on and the panel open, the inspector calls it every modeFrames frames and hands the answer to the engine. A kit adds modes with addMode(mode) when it mounts:
{ "id": "ai.navigation.paths", "kit": "ai.navigation", "label": "AI paths", "kind": "overlay", "call": "ai.navigation.debugPaths" }
Engines #
The measures and the drawing come from one Rust core, adapted by each engine with its own tools. The web and native players implement the full contract. Godot, Unity and Unreal follow the same contract.
When it does not do what you expect #
| Symptom | Cause and fix |
|---|---|
| F3 does nothing on a Mac | the F-keys are media keys: hold fn (fn + F3), or use the fallback Ctrl + Alt + D (same rule), or ?debug=1 |
| F3 does nothing | the kit is removed or enabled: false; the viewer is refused by access (players: false) or by a debug.open tap; toggleKey was changed or is null |
| The GPU line says software | the measures come from a software rasteriser; measure on a machine with a real GPU |
| The scene tree is cut | raise treeMaxNodes / treeDepth, or send a tree op with a narrower root |
| A kit mode is missing from the list | the kit did not register it with addMode, or it is not in debug_modes |
Turning a Worker off (comparison) #
?workers=0 turns every Worker off; ?worker.render=0, ?worker.physics=0 or ?worker.<module id>=0 (e.g. worker.physics.rapier=0) one only; =1 asks it (it still falls back with a reason if impossible). ?playerWorker=0 still works. The same keys in localStorage: gripforge.workers, gripforge.worker.<id>; the address wins. F3 and the snapshot (renderThread, physicsThread) then read main (disabled by option (...)).