This excalibur game developers to easily inspect their games while making them!
Now live in browsers!
Debug
- Performance issues
- Actor graphics
- Actor geometry
- Colliders
- Scenes
- Toggle Debug Draw
If you want to develop locally
- Install node.js
- Run
npm installin the root directory - Run
npm run start:chromethis will start a parcel dev server, or runnpm run build:chrometo produce a prod bundle - Open
chrome://extensions/and click "Load unpacked"
- Select the
dist-chromedirectory in theexcalibur-extensionproject
If you want to develop locally
- Install node.js
- Run
npm installin the root directory - Run
npm run start:firefoxthis will start a parcel dev server, or runnpm run build:firefoxto produce a prod bundle - Open
about:debugging#/runtime/this-firefoxand click "Load Temporary Add-on..."
- Select the manifest.json file inside the
dist-firefoxdirectory.
- Install node.js
- Run
npm installin the root directory - Run
npm build:chrometo produce the final build artifact indist-chrome- Zip the files in the directory, not the
dist-chromedirectory - Upload the zip to chrome
- Zip the files in the directory, not the
- Run
npm build:firefoxto product the final build artifact indist-firefoxwhich can be zipped and uploaded- Zip the files in the directory, not the
dist-firefoxdirectory - Upload the zip to mozilla
- Zip the files in the directory, not the
The message protocol (src/protocol.ts) is the fixed seam. Three layers carry it,
and each seam has two interchangeable implementations — one for the browser
extension, one for the embedded build:
flowchart LR
APP["<b><app-main></b><br/>Lit panel UI<br/><i>src/components/</i>"]
CONN["<b>createConnection()</b><br/>command switch · 200ms heartbeat poll<br/>frame reconcile · pickerOpSeq · 3-strike tolerance<br/><i>src/driver/connection.ts</i>"]
FNS["<b>page functions</b> (closure-free, serializable)<br/>detect · inject · clock · picker<br/>entities · scene · materials<br/><i>src/page/</i>"]
ENGINE[("window.___EXCALIBUR_DEVTOOL<br/>Excalibur Engine")]
APP <-- "PanelTransport" --> CONN
CONN <-- "PageExecutor" --> FNS
FNS <--> ENGINE
| Seam | Extension build | Embedded build |
|---|---|---|
PanelTransport |
chrome runtime Port (src/panel-transport/extension.ts) |
in-memory pair (src/embedded/local-driver.ts) |
PageExecutor |
chrome.scripting.executeScript, world MAIN (src/background.ts) |
direct function call (src/embedded/local-driver.ts) |
| Panel UI lives in | the devtools panel page | a same-origin dock iframe (src/embedded/host.ts) |
| Connection runs in | the background service worker | the game page itself |
The runtime flow is identical in both builds — only the plugs differ:
sequenceDiagram
participant Panel as app-main (panel)
participant Conn as createConnection (driver)
participant Exec as PageExecutor
participant Game as Excalibur Engine
Panel->>Conn: ex-debug:hello (tabId stamped by the transport)
Conn-->>Panel: ex-debug:init
Panel->>Conn: ex-debug:update-debug (settings push)
loop every 200ms
Conn->>Exec: execAll(detectExcalibur)
Exec->>Game: find window.___EXCALIBUR_DEVTOOL (all frames)
Conn->>Exec: exec(inject, settings)
Exec->>Game: apply debug settings, serialize game state
Conn-->>Panel: ex-debug:heartbeat (instances, selectedFrameId, data)
end
Panel->>Conn: ex-debug:command (dispatch: ex-debug:kill, actorId)
Conn->>Exec: exec(kill, [actorId])
Exec->>Game: actor.kill()
Note over Panel,Conn: on-demand replies (material-detail,<br/>entity-graphics) post back the same way
The panel can also run directly inside a game page, firebug-style — no browser extension, no devtools window. Build it with:
npm run build:embedded→dist-embedded/ex-devtools.js(self-contained)
Drop it into any page running an Excalibur game (the engine already exposes itself to the devtools — no game-side code needed):
<script type="module" src="ex-devtools.js"></script>A floating Ex button appears bottom-right and opens a resizable dock with
the full panel (inspector, debug draw, materials, graphs, entity picker).
window.ExDevtools exposes open(), close(), toggle(), and destroy();
appending ?ex-devtools=open to the URL opens the dock on load.
For development, npm run start:embedded serves a harness page with a demo
game and the embedded devtools on top.
npm run test:unit— node-level Vitest specs (settings schema/store/utils, formatters, GLSL tokenizer, and a serialization tripwire that proves everysrc/page/function is still self-contained).npm run test:browser— Vitest browser mode (headless Chromium via Playwright; runnpx playwright install chromiumonce): boots a real Excalibur game and drives the embedded panel against it end to end.npm run test— both.
PR's welcome
-
Global audio settings
- List sounds that are playing
- Mute sounds
-
Excalibur Action Window Pop-up
-
Support firefox with manifest v3 https://extensionworkshop.com/documentation/develop/porting-a-google-chrome-extension/
-
Show "no excalibur detected" if something isn't in the extension
-
Make sure lite mode looks okay
-
List active event listeners on stuff for debugging!
-
Remember debug settings across refreshes/closing
-
Show entities as a tree view so child/parent relationships are clear
-
Screen settings
- Override camera with a click an drag
- Zoom the camera!
- Change Resolution
- Change DisplayMode
- Adjust antialiasing settings
- Display Content Area
- Expose Color Blind Testing Tools
-
Physics settings
- Switch between solvers Arcade/Realistic
- Max fps
- Fixed step fps
- Interpolation
- Gravity
- Iterations
-
Show loaded resources
-
Entity inspector side panel with component value detials
- When you hover over an actor/entity in the game it highlights in the dev tools!
-
System JS info?
