Open-source project
BabylonJS/Spector.js avatar
BabylonJS/Spector.js

Spector.js captures a whole WebGL frame

Explore and Troubleshoot your WebGL scenes with ease.

1,626 stars191 forksTypeScriptMIT

At a glance

What is it?
Spector.js is an MIT-licensed WebGL and WebGL2 frame capture tool from the BabylonJS team, usable as a browser extension, a UMD module or a script tag. It records the full command list with the state at each call, which is what makes a rendering bug reproducible, and its extension path into OffscreenCanvas and workers is where its limits are clearest.
Who is it for?
Use Spector.js when you need to see the exact WebGL calls behind a frame and you are working in the browser or in a worker you control. Do not adopt it as a permanent part of a production bundle, since the package is a debugging tool and the README's own examples wire it straight into your entry point.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What a capture contains: commands, state and context

Spector.js records one frame of WebGL activity and turns it into something you can read. The README describes the result as your entire commands list with their associated visual states and context information.

That distinction is the whole point. A browser's built-in WebGL inspector, the one Spector's author says he was a fan of, shows the current state of your context. Spector shows what was called, in what order, and what the context looked like at each call. When a draw call produces the wrong pixels because a uniform was not rebound, only the second view tells you.

The origin story in the README matters for scope. The author describes being a core member of the BabylonJS team, needing to debug a WebGL context, losing the tool he relied on when Babylon introduced WebGL2, and deciding to build one compatible with all WebGL and WebGL2 engines rather than just his own. That is why it is engine-agnostic: the repository topics list webgl, webgl2, webvr, inspector and debugger, and the summary line says it supports all engines and vanilla script usage.

Spector.js is aimed at WebGL developers across engines.

Three ways in: extension, UMD module, script tag

Spector can be used in three configurations, and picking the wrong one costs you time.

The browser extension is the fastest route and requires no code. There are store listings for Chrome and Firefox, plus documentation on how to load the extension from source in the repository.

For code integration, the library is transpiled as a UMD module, so it works as a script reference or through a bundler. The npm package is `spectorjs`:

bash
npm install spectorjs

The README's webpack walkthrough creates a sample directory, installs webpack and webpack-cli plus spectorjs as dev dependencies, and writes an `entry.js` with three lines:

javascript
var SPECTOR = require("spectorjs");

var spector = new SPECTOR.Spector();
spector.displayUI();

Bundling is then a single command:

bash
npx webpack entry.js 

As a plain script, the README offers three sources: the jsDelivr CDN at a versioned URL, the `dist` folder in the repository if you self-host, and the `node_modules/spectorjs/dist` copy if you installed from npm. All three resolve to `spector.bundle.js`, and the reference tag is:

html
<script type="text/javascript" src="https://spectorcdn.babylonjs.com/spector.bundle.js"></script>

captureCanvas, spyCanvases and the onCapture callback

Instantiating the inspector and showing its UI is two calls:

javascript
var spector = new SPECTOR.Spector();
spector.displayUI();

The third method is the one to build tooling on. `captureCanvas` takes a canvas element and captures its frame, and `onCapture` is an event you subscribe to in order to receive the result as JSON:

javascript
spector.onCapture.add((capture) => {
    var myEvent = new CustomEvent("SpectorOnCaptureEvent", { detail: { captureString: JSON.stringify(capture) } });
    document.dispatchEvent(myEvent);
});

var canvas = document.getElementById("renderCanvas");
spector.captureCanvas(canvas);

The README's example serialises the capture and re-dispatches it as a DOM CustomEvent, which is the pattern to copy if you want to hand a capture to another part of your page or to an automated test rather than just look at it.

`spector.spyCanvases()` is the fourth entry point and the easiest to overlook. The README says it enables complete tracking of the gl calls even before capturing, which gives access to texture inputs and memory consumption information. In other words, it is a continuous instrumentation mode rather than a one-shot snapshot, and that is what you want when the bug is about what was uploaded rather than what was drawn.

There is also a custom data hook: adding a field named `__SPECTOR_Metadata` to your WebGLObj attaches your own data to the capture.

OffscreenCanvas support is split into three modes with different reliability

The table of contents gives OffscreenCanvas its own section with three subsections, and the differences between them are the honest signal about how mature that support is.

There is main-thread OffscreenCanvas, worker OffscreenCanvas described as manual API and recommended, and auto-injection described as best-effort. That labelling is not cosmetic. Manual API means you wire the capture up yourself and get predictable behaviour. Auto-injection means the tool patches canvas creation for you and may or may not attach cleanly, which is exactly the sort of thing that fails on the one browser version you care about.

The repository reflects this. The sample directory includes `workerAutoInject.html` alongside `index.html`, so there is a runnable case for the worker path, and the package publishes two bundles rather than one: `dist/spector.bundle.js` and `dist/spector.worker.bundle.js`. You load the worker bundle in the worker and the main bundle in the page.

For a worker-based renderer, use the manual API with the worker bundle. Do not start with auto-injection.

MCP server and the toolchain around the bundle

The repository now includes an `mcp/` directory and an MCP Server section in the table of contents, which means the capture can be driven from an agent tool rather than only from the UI.

The rest of the toolchain is visible in package.json. The build is webpack: `build:bundle` runs webpack in prod mode against tools/webpack.config.js, and `build:types` runs a bundling script for declaration files. The full build chains tslint, the bundle, the types, two copy steps that push the bundle into an `extensions/` folder, and a concat step that wraps the bundle with a header and footer to produce `spector.bundle.func.js`.

That last file is the browser extension build, which is why the same source produces both the extension and the standalone page version.

Testing is three-layered. The `test` script runs jest unit tests, then Playwright tests, then Playwright again against a separate integration config. A playwright.config.ts and a jest.config.js sit at the repository root. Testing a graphics capture tool is inherently awkward because the thing you assert on is a pixel buffer, and running the same assertions in a unit pass, a browser pass and an integration pass is a reasonable answer to that.

Lint is tslint with a tslint.json config, which is dated but still what the scripts invoke.

Version 0.9.35 in package.json, v0.9.33 as the newest tag

The versioning here is worth pausing on, because the three numbers a reader encounters do not agree.

package.json says version 0.9.35. The README's CDN link points at `[email protected]`, matching that. But the latest GitHub release is v0.9.33, tagged on 2026-09-14, and the release before it is v0.9.27 from 2021-09-26. There is also v0.9.26 from 2021-07-02.

So the repository is being maintained at a steady trickle, with the last push on 2026-10-12, while the tags are sparse and the npm version runs ahead of them.

The 0.9.x line itself has been in place for years, which cuts both ways. It means the API is settled and unlikely to move under you, which is what you want from a debugging tool. It also means the package sits below 1.0, so semver promises nothing about breaking changes.

One naming detail catches people: the npm package is `spectorjs` with no dot and no capital S, while the repository, the docs and the homepage all use Spector.js.

When Spector is the wrong tool

The first boundary is obvious but worth stating: this is for WebGL and WebGL2. If your rendering goes through WebGPU, or through a canvas abstraction that never touches a WebGL context, there is nothing here to capture. The topics list is explicit that this is WebGL.

The second is that a capture is a snapshot, and the problem you have may not be in one frame. `spyCanvases` gives continuous tracking, but a full frame captured under instrumentation is large, and serialising it to JSON and moving it around your page is not free. If your issue is a slow first frame or a memory leak that accumulates over thousands of frames, a per-frame capture tool answers the wrong question.

The third is the production concern. Every example in the README wires Spector into your entry point. That is fine for a development build and wrong for a shipped one, since the inspector is a debugging surface with a UI and no security boundary around what it exposes. Keep it behind a development-only conditional in your own build.

The fourth is engine-agnosticism as a double edge. Supporting all engines means no engine-specific knowledge. Spector focuses on WebGL calls and their context. If your bug is a scene graph or material configuration issue rather than a GL state issue, the wrong tool is a different category of tool.

Against the built-in inspector and against recording instead

There are two other approaches worth comparing honestly.

The browser's own WebGL context is inspectable without any tool. Every modern browser ships a WebGL panel showing current state, and it costs nothing and is always present. What it cannot do is history. Spector's value over it is the per-call state sequence, and the README is candid that the author built it after losing his preferred tool when Babylon moved to WebGL2.

The other approach is not a debugger at all. Video recording of the canvas, or capturing screenshots frame by frame, tells you what the output looked like without telling you why. That is the right tool when the question is visual regression, and Spector is the wrong one because it does not compare renders.

A third is an automated graphics test harness, which asserts on output and fails a build. Spector is not that either, though its onCapture JSON is usable as the input to one, which is the most interesting composition available here.

So the honest summary: use the built-in inspector for current state, Spector for the call history behind one frame, and a capture-and-compare harness for regressions across many frames.

Editorial conclusion

Use Spector.js when you need to see the exact WebGL calls behind a frame and you are working in the browser or in a worker you control. Do not adopt it as a permanent part of a production bundle, since the package is a debugging tool and the README's own examples wire it straight into your entry point. Verify first which version you get, because the README's CDN link points at 0.9.35 while the latest GitHub release tag is v0.9.33 from 2026-09-14, with the previous release dating from 2021.

Frequently asked questions

How do I install Spector.js?

Run npm install spectorjs, then reference it either through a bundler with var SPECTOR = require("spectorjs"), or with a script tag pointing at spector.bundle.js from the jsDelivr CDN, the dist folder in the repository, or node_modules/spectorjs/dist.

What does Spector.js capture?

It captures a frame's complete commands list with the visual state and context information at each call, rather than only the current state of the WebGL context as a browser's built-in inspector shows.

How do I use Spector.js with OffscreenCanvas in a worker?

The README documents three modes: main-thread OffscreenCanvas, worker OffscreenCanvas via the manual API which it recommends, and auto-injection which it labels best-effort. The worker path uses a separate bundle, dist/spector.worker.bundle.js, with the main bundle in the page.

What is the difference between captureCanvas and spyCanvases?

captureCanvas captures a single frame from a given canvas and delivers the result through the onCapture event. spyCanvases enables complete tracking of the GL calls even before capturing, which gives access to texture inputs and memory consumption information.

Which version of Spector.js is current?

package.json and the README's CDN link both reference 0.9.35, while the most recent GitHub release tag is v0.9.33 dated 2026-09-14. The previous release tag is v0.9.27 from 2021-09-26, so the npm version runs ahead of the tags.

What licence is Spector.js released under?

It is released under the MIT licence, declared in package.json with LICENSE.txt at the repository root. This is a licensing note and not legal advice.

Official sources

  1. BabylonJS/Spector.js on GitHub
  2. License: MIT
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/babylonjs-spector-js.svg)](https://hysenlabs.com/projects/babylonjs-spector-js)

Was this useful?