A modern, in-browser WebGL 1 & 2 inspector, debugger, and profiler.
Package name:
webgl-developer-tool
WebGL Developer Tool is a modernized fork and major feature expansion of benvanik/WebGL-Inspector (BSD-3-Clause © 2014 Ben Vanik). The original tool was a landmark piece of WebGL tooling that let developers capture a frame, step through every GL call, inspect GPU resources, and replay the frame on a mirror canvas — all from inside the browser, with no extension required.
This project preserves that core idea and extends it into a fuller, modern debugger:
- Ported from RequireJS/AMD to modern ESM + Vite. Zero runtime dependencies.
- Targets both WebGL 1 (primary) and WebGL 2 (UBOs, VAOs, samplers, transform feedback, queries, multisampled renderbuffers, pixel-pack/unpack state).
- Ships two ways: a standalone
<script>embed and a Chrome Manifest V3 extension. - Adds many features the original never had (Console tab, Uniforms tab, shader hot-reload, frame export/import, dockable layouts, theme toggle, clickable source links, and more — see Features).
- 132 unit tests across 10 suites — run with
npm test.
💡 If you used the original WebGL-Inspector, the mental model is identical — hit Capture (F12), browse the trace, click resources to inspect — but almost every tab has been upgraded with richer detail.
- One-click frame capture (F12 or the floating Capture button). Every
gl.*call is recorded with its arguments, duration, error code, and full JS call stack. - 3-panel Trace view — frame thumbnails on the left, call list in the middle, live replay preview + call detail on the right (matches the original WebGL-Inspector layout).
- Resource links —
[Buffer 3],[Program 2],[Texture 5]render as blue clickable links that jump to the resource's detail tab. - Enum resolution — numeric GL constants are rendered by name (
TRIANGLES,TEXTURE_2D,FUNC_ADD), with bitmask decomposition (COLOR_BUFFER_BIT | DEPTH_BUFFER_BIT). - Step controls — restart, step back, step forward, skip to next draw, run to end (with hotkeys F6–F10).
- Draw-call isolation — click the ⊚ button on any draw to re-render the frame with only that draw call visible, preserving FBO dependencies.
- Per-frame shadow-state analysis flags
gl.*calls that set state to its current value (e.g.,useProgram(X)whenXis already bound). - Yellow row highlighting + live counter in the Trace toolbar (
5/36 · 14%). - ×N repeat badges on the first occurrence of each redundant pattern so the worst offenders jump out.
- Toggle off to dim redundant rows when you want to see only the meaningful state changes.
- Red row highlighting on any call where
gl.getError()returned non-zero. - Live Console tab logs every GL error as it happens (not only inside captures), with:
- Level filters (Errors / Warnings / Info) with live counts
- Free-text message search
- Clickable source-location link on every error — parsed from the JS stack, skips inspector-internal frames, points directly at your code (e.g.
…/app.js:293:6) - Repeat-collapse with
×Nbadges
All resource tabs left-align a resource list and auto-select the first entry.
- Textures — thumbnail, parameters (size, format, filters, wrap), full-resolution preview rebuilt via
readPixels, History, Usage-in-frame, Download PNG / Zoom modal / Copy dataURL toolbar, and source-URL section for textures loaded from<img>/<video>/<canvas>/ImageBitmap. - Buffers — target/usage/size with A/E/U type badges at the right end (Array / Element / Uniform / etc.), "Structure (from last draw)" table showing the actual vertex-attribute layout (offset/size/type/stride/normalized) discovered by walking the trace, and stride-aware Contents view with one vertex per row and attribute-group separators.
- Programs — full info block (
LINK_STATUS,VALIDATE_STATUS,DELETE_STATUS,ACTIVE_UNIFORMS,ACTIVE_ATTRIBUTESqueried live from the GPU), uniform table with live values, attribute table, sidebar showsVS:/FS:clickable shader refs. - Shaders — type, compile status + info log, syntax-highlighted GLSL source, editable (see below).
- Uniforms — all uniform calls in the frame grouped by program, with the last value set for each uniform rendered as a proper matrix grid / vec / scalar, clickable call-ordinal links back to the Trace.
- Framebuffers — attachment table (color/depth/stencil) with clickable resource refs, live
checkFramebufferStatus(). - Renderbuffers — size, internal format, samples.
- Edit any shader's source directly in the Shaders tab.
- Apply & Recompile pushes the edit into the running app: re-calls
shaderSource+compileShader, relinks every program that uses the shader, and transparently refreshes cachedWebGLUniformLocationobjects so apps that cache locations at init don't break. - Live-highlighted GLSL preview below the editor.
- Revert to original source.
- Compile errors show inline with the info log; the edit is automatically rolled back on failure.
- State tab with
Initial / Current / Enddropdown — see the GL state at three points in the frame:- Initial — captured at frame start
- Current — snapshot at the currently-selected trace call (uses the replay GL context)
- End — captured at frame end
- Diff highlighting — in Current/End views, every changed row is highlighted green and labeled
(was X)so you can instantly spot what the frame modified. - 12 canonical state categories (Alignment, Blend, Clear, Color, Coverage, Cull, Depth, Draw, Mipmap Hint, Polygon Offset, Scissor, Stencil) + Vertex Attributes + Texture Units.
- Clickable resource values —
CURRENT_PROGRAM = [Program 11]jumps to the Programs tab.
- Stat cards: total calls / draw calls / redundant / CPU time / avg per call.
- Call categories legend with color swatches + count + percentage + total duration per category (Draw, Redundant, Uniform, State changes, Resource binds, Data upload, Texture ops, Queries, Other).
- Stacked category bar for at-a-glance frame breakdown.
- Per-call duration chart colored by category, clickable bars jump to the Trace.
- Top 15 functions by call count table with distribution bars — spot your dominant calls.
- Export the current frame to a self-contained JSON file.
- Includes: all calls + stack traces, every resource with parameters and pixel/buffer data (base64), shader sources, init + end state, 60+ GL parameters, frame screenshot as PNG.
- Import any exported JSON — rebuilt frame appears in the frame selector, fully browsable across all tabs.
- Imported textures reconstruct previews from the stored pixel bytes; buffers show stride-aware structure as before.
- Share bug frames with teammates. Archive reference frames for A/B comparison across code changes.
- Dockable — left / top / bottom / right, detached floating window, Maximize (fills viewport), and Open in new window (separate browser window). Dock choice persists via
localStorage. - Resizable — drag any inner edge.
- Dark / Light theme toggle — CSS-variable-driven; affects only the inspector, not the host page.
- Font-size control —
A− 12px A+, clamped 9–24 px, persists. - Tab visibility menu — toggle switches let you hide tabs you don't use; a master Show all toggle flips everything. Hidden tabs temporarily re-appear if you click a resource link that navigates there.
- Single gear (⚙) menu — Chrome-style popover houses dock / theme / font / tabs / export-import in one place.
- Close button (✕) — hide the inspector window; re-open via F11 or the host "Inspector" button.
- Frame replay on a mirror GL context with per-call stepping (uniform-location invalidation and mirror disposal bugs from the original port have been fixed).
- Real-time GL error capture —
gl.getError()is polled after every call, so errors show up in the Console tab as they happen, not only after a capture. - Always-on JS stack capture — no toggle to forget, clickable source links work out of the box.
- Node.js 20+
- npm
git clone <this-repo>
cd webgl-inspector-next
npm install
npm run dev # Vite dev server on port 5173Then open:
http://localhost:5173/samples/webgl1-basic/— textured rotating cube (WebGL 1)http://localhost:5173/samples/webgl2-basic/— WebGL 2 sample with UBOs, instancing, transform feedback
npm run build:embedThis produces dist/embed/gli.js (+ gli.css). Include it on any page before your WebGL code:
<script type="module" src="path/to/gli.js"></script>
<script>
// Your existing WebGL app — no changes needed.
const canvas = document.getElementById('c');
const gl = canvas.getContext('webgl');
// ...
</script>The inspector injects two floating buttons (Capture + Inspector) into the bottom-right. Press F12 to capture a frame, F11 to toggle the inspector UI.
npm run build:extension- Open
chrome://extensions/and enable Developer mode. - Click Load unpacked and select the built
dist/extension/folder. - Click the extension icon on any page with a WebGL canvas.
npm run build:all # embed + extensionnpm testRuns 10 unit-test suites (132 assertions) covering GL constants, redundancy detection, resource versioning, settings persistence, console bus, frame serializer, and more.
| Command | Description |
|---|---|
npm run dev |
Vite dev server with hot reload |
npm run build:embed |
Standalone IIFE bundle |
npm run build:extension |
Chrome MV3 extension |
npm run build:all |
Both builds |
npm test |
Run unit tests |
npm run gen:info |
Regenerate GLConsts.js / Info.js from Khronos WebGL IDL |
npm run lint |
ESLint |
npm run format |
Prettier |
| Key | Action |
|---|---|
| F12 | Capture current frame |
| F11 | Toggle inspector UI |
| F6 | Step back one call |
| F7 | Skip to next draw call |
| F8 | Step forward one call |
| F9 | Run to end of frame |
| F10 | Restart from frame start |
| Esc | Close modal dialogs (texture zoom, etc.) |
src/core/host/— Instrumentation layer: hooks every GL call, tracks resources (buffers, textures, shaders, programs, FBOs, renderbuffers, VAOs, samplers, queries, syncs, transform feedbacks), captures frames, serializes them.src/core/replay/— Replays captured frames on a mirror context.RedundancyCheckerflags calls that set already-cached state.Controllersupports stepping, draw isolation, and depth visualization.src/core/ui/— Inspector window: Trace, Console, Timeline, State, Textures, Buffers, Programs, Shaders, Uniforms, Framebuffers, Renderbuffers tabs. Dark + light themes (Catppuccin-inspired).src/core/shared/— GL constants (WebGL1+2 + extensions), call metadata, utilities, event bus, settings, console bus.src/extension/— Chrome MV3: background service worker, content-script bridge, page-world injector.src/embed.js— Standalone entry that hooksHTMLCanvasElement.prototype.getContext.scripts/gen-info.js— Node script that regenerates shared constants/metadata from Khronos WebGL IDL.
Bundled samples exercise the inspector against realistic GL workloads. Both ship with the dev server (npm run dev).
A compact WebGL 1 scene that exercises the full pipeline in ~300 lines:
- Custom vertex & fragment shaders with texture sampling and simple diffuse lighting
- Interleaved VBO (position + UV + normal) and indexed draws
- Procedural 64×64 checkerboard texture with mipmap generation
- Two-pass rendering — render to an FBO (framebuffer + color-attachment texture + depth renderbuffer), then blit the FBO texture to the default framebuffer via a fullscreen quad
- Per-frame rotating MVP matrix
Good for testing the Trace / Buffer / Texture / Program / Framebuffer / Renderbuffer tabs, draw-call isolation across render passes, and shader hot-reload.
Demonstrates WebGL 2-specific features:
- Uniform Buffer Objects (UBOs) — shared light/material uniforms across draws
- Vertex Array Objects (VAOs) — no more per-draw
enableVertexAttribArrayspam - Instanced drawing —
drawElementsInstancedwith per-instance attributes - Transform feedback — capture vertex output back into a buffer
- Sampler objects — texture parameter state decoupled from textures
- Occlusion & timer queries —
ANY_SAMPLES_PASSED,TIME_ELAPSED_EXT - Sync fences —
fenceSync+clientWaitSync
Good for testing all the WebGL 2 tabs (Samplers, Queries, TransformFeedback, VAO) and state-tracker completeness.
Ideas we'd like to explore (contributions welcome):
- Per-pixel shader debugging — tap a pixel in the replay preview to see every draw that contributed to it.
- Depth / stencil visualization — toggles in the preview panel to render depth as grayscale or stencil as a heatmap.
- Overdraw heatmap — render the frame with additive white so overdraw spots glow.
- Wireframe / normal visualization — patch the shader mirror on the replay context to render just wireframes or normals without touching the host.
- Network-style waterfall for textures — show when each texture was first uploaded, last modified, GPU memory footprint.
- GPU-time queries — use
EXT_disjoint_timer_query_webgl2to get actual GPU time per draw. - Search across trace — free-text and regex filtering of the call list.
- Persistent breakpoints — pause the running app on a specific
gl.*call or on any error. - Diff two captured frames — show which calls / states / resources changed between runs.
- CSV / CSV-with-binary export — in addition to JSON, export a compact timings CSV for external analysis.
- Firefox / Safari extension ports — currently MV3 is Chromium-only; DevTools panels on other browsers need separate shimming.
- WebGPU parallel — a sibling project that applies the same capture/inspect/replay model to WebGPU command encoders.
- Inspired by and ported from benvanik/WebGL-Inspector (BSD-3-Clause © 2014 Ben Vanik). Huge thanks for the original design — this project wouldn't exist without it.
- This fork is released under the MIT License — you're free to use, modify, and distribute it (including commercially) with minimal restrictions. Attribution to Ben Vanik's original work is preserved in the LICENSE file.
Commit history is intentionally verbose — every commit message explains why the change was made, not just what changed. Useful as a reading path if you want to understand how each feature evolved.