How can I activate the Babylon Inspector when my game is running in a Web Worker?

I’m adjusting my game to run in a WebWorker using OffscreenCanvas as per the docs.

I’ve just gone to use the Babylon inspector (v2) and hit a wall.

I’ve tried things like running:

import("@babylonjs/inspector").then(({ ShowInspector }) => {
//etc
}

inside the WebWorker but that throws a Uncaught (in promise) ReferenceError: global is not defined

What are my options?

  • the perf from WebWorker is much needed
  • it’s non-trivial to build what I’m doing in a way that could easily run in both web worker ‘mode’ and on the UI thread for debugging

Let me add @ryantrem to the thread as he is the inspector daddy

Looking…

Short version: the Inspector can’t run inside a worker, and that part isn’t fixable. But there is a partial option, and you found a real bug along the way.

About that error

I checked, and global is not defined isn’t coming from the Inspector itself. Every reference to global in the shipped @babylonjs/inspector bundle is typeof-guarded, so it can’t throw on its own. It’s most likely coming out of your own build, from one of the peer dependencies (React / Fluent UI) being processed for a worker target. global is a Node-ism that bundlers normally shim for main-thread builds, and that shim doesn’t always get applied to worker bundles.

That said, it’s a bit moot, because it’s the first error rather than the real blocker. If you patched it you’d immediately hit document is not defined. The Inspector is a React app: it creates elements and mounts into document.body. A worker has no DOM, so there’s nowhere for it to render.

Why the obvious workaround doesn’t work either

The natural follow-up is “can the UI run on the main thread and just reach into the worker?” Unfortunately not. The worker and the main thread are separate JS heaps, and the only channels between them are postMessage (structured clone, which copies values and drops prototypes, methods and getters), transferables, and SharedArrayBuffer. The Inspector reads properties off your live objects synchronously and patches their accessors to observe changes, and none of that survives a copy. Atomics.wait is also disallowed on the main thread, so a synchronous bridge isn’t possible either. That’s a browser constraint rather than a Babylon one.

What you can do today: the Reflector

There’s an existing feature for roughly this shape of problem. Your scene opens a WebSocket to a small relay, the Sandbox connects to the same relay, and the Sandbox opens the Inspector on the scene it receives. The scene-side class is DOM-free, so it runs fine in a worker:

import { Reflector } from "@babylonjs/core/Misc/reflector";

new Reflector(scene, "localhost", 1234);

Then open https://sandbox.babylonjs.com/?reflector.

The important caveat: this is a one-time, read-only snapshot. It serializes the scene once when the connection is established and sends it over. There are no live updates afterwards, nothing you change in the Inspector goes back to your worker, and anything that doesn’t survive SceneSerializer is lost. It’s good for inspecting your scene graph, materials and settings; it is not a live editing session.

The bug

While checking this for you I found the Reflector relay has been broken for about four years. It was attaching its message handler to the WebSocket server instead of to the individual client socket, so every payload was silently dropped, and there was a second crash behind that one. Fix is up here: Fix Reflector bridge relay dropping and crashing on client messages by ryantrem · Pull Request #18800 · BabylonJS/Babylon.js · GitHub

I tested it against your exact setup, a Babylon scene in a worker with an OffscreenCanvas and no DOM, and it works once the relay is fixed. The relay itself isn’t published to npm, so for now you’d run it out of the repo (packages/tools/reflector, npm run build then npm run start), or write the equivalent few-line ws relay yourself.

Really appreciate the work ryantrem!

I’ve ended up doing all the work required to make running on the main thread and a web worker a one liner so that I can have access to the full inspector.

For anyone else in the future this meant switching all my UI systems to communicating with the game via a MessageChannel - this meant changing a lot of systems which previously just grabbed the BabylonEngine directly to be async serializable message based which is a fair chunk of more boilerplate, but I prioritise debuggability and didn’t want to give up the perf of rendering on another thread so for me it was worth it.

Would love to see what you’ve done if you have any code you are willing to share!