# stats-gl - WebGL/WebGPU Performance Monitor Real-time FPS, CPU, GPU timing for Three.js, native WebGL2/WebGPU, and Web Workers. ## Install ```bash npm install stats-gl ``` ## 1. Three.js (WebGL) ```js import Stats from 'stats-gl'; import * as THREE from 'three'; const stats = new Stats({ trackGPU: true }); document.body.appendChild(stats.dom); const renderer = new THREE.WebGLRenderer(); stats.init(renderer); function animate() { renderer.render(scene, camera); stats.update(); } renderer.setAnimationLoop(animate); ``` ## 2. Three.js (WebGPU) ```js import Stats from 'stats-gl'; import * as THREE from 'three'; const stats = new Stats({ trackGPU: true, trackCPT: true }); document.body.appendChild(stats.dom); const renderer = new THREE.WebGPURenderer(); stats.init(renderer); async function animate() { await renderer.renderAsync(scene, camera); stats.update(); } renderer.setAnimationLoop(animate); ``` ## 3. Native WebGL2 ```js import Stats from 'stats-gl'; const stats = new Stats({ trackGPU: true }); const canvas = document.querySelector('#canvas'); stats.init(canvas); document.body.appendChild(stats.dom); function animate() { stats.begin(); gl.clear(gl.COLOR_BUFFER_BIT); gl.drawArrays(gl.TRIANGLES, 0, 3); stats.end(); stats.update(); requestAnimationFrame(animate); } animate(); ``` ## 4. Native WebGPU ```js import Stats from 'stats-gl'; const adapter = await navigator.gpu.requestAdapter(); const device = await adapter.requestDevice({ requiredFeatures: ['timestamp-query'] }); const context = canvas.getContext('webgpu'); const stats = new Stats({ trackGPU: true }); stats.init(device); // Pass GPUDevice document.body.appendChild(stats.dom); function animate() { stats.begin(); const encoder = device.createCommandEncoder(); const pass = encoder.beginRenderPass({ colorAttachments: [...], timestampWrites: stats.getTimestampWrites() // GPU timing }); // ... draw calls ... pass.end(); stats.end(encoder); // Resolve timestamps device.queue.submit([encoder.finish()]); stats.update(); requestAnimationFrame(animate); } animate(); ``` ## 5. Web Worker + OffscreenCanvas ### Main Thread ```js import Stats from 'stats-gl'; const stats = new Stats({ trackGPU: true }); document.body.appendChild(stats.dom); const canvas = document.getElementById('canvas'); const offscreen = canvas.transferControlToOffscreen(); const worker = new Worker('worker.js', { type: 'module' }); worker.postMessage({ type: 'init', canvas: offscreen }, [offscreen]); worker.onmessage = (e) => { if (e.data.type === 'stats') { stats.setData(e.data); } }; function loop() { stats.update(); requestAnimationFrame(loop); } loop(); ``` ### Worker ```js import { StatsProfiler } from 'stats-gl'; const profiler = new StatsProfiler({ trackGPU: true }); self.onmessage = async (e) => { if (e.data.type === 'init') { const gl = e.data.canvas.getContext('webgl2'); await profiler.init(gl); requestAnimationFrame(loop); } }; function loop() { profiler.begin(); // render... profiler.end(); profiler.update(); self.postMessage({ type: 'stats', ...profiler.getData() }); requestAnimationFrame(loop); } ``` ## 6. Texture Preview Panels ```js import Stats from 'stats-gl'; const stats = new Stats({ trackGPU: true }); stats.init(renderer); // Add texture panel stats.addTexturePanel('GBuffer'); // Set source (Three.js RenderTarget) const rt = new THREE.WebGLRenderTarget(1024, 1024); stats.setTexture('GBuffer', rt); // In animate - auto-updates at graphsPerSecond rate function animate() { renderer.setRenderTarget(rt); renderer.render(scene, camera); renderer.setRenderTarget(null); renderer.render(scene, camera); stats.update(); } ``` ## 7. Worker Texture Transfer ### Worker ```js const bitmap = await profiler.captureTexture(renderTarget, 'gbuffer'); self.postMessage( { type: 'texture', name: 'GBuffer', bitmap, width: 1024, height: 1024 }, [bitmap] ); ``` ### Main Thread ```js stats.addTexturePanel('GBuffer'); worker.onmessage = (e) => { if (e.data.type === 'texture') { stats.setTextureBitmap(e.data.name, e.data.bitmap, e.data.width, e.data.height); } }; ``` ## 8. TSL Node Capture (WebGPU) Capture TSL nodes for live preview. Works with MRT and PostProcessing. ### Main Thread ```js import { statsGL } from 'stats-gl/addons/StatsGLNode.js'; import { addMethodChaining } from 'three/tsl'; addMethodChaining('toStatsGL', statsGL); // Register nodes - panels created automatically diffuseNode.toStatsGL('Diffuse', stats); normalNode.toStatsGL('Normal', stats); depthNode.toStatsGL('Depth', stats, (n) => linearize(n)); // with transform ``` ### Worker ```js import { flushCaptures } from 'stats-gl/addons/StatsGLNode.js'; // Register (no stats instance in worker) diffuseNode.toStatsGL('Diffuse'); normalNode.toStatsGL('Normal'); // After render, flush and transfer const captures = await flushCaptures(renderer); for (const { name, bitmap } of captures) { self.postMessage({ type: 'texture', name, bitmap }, [bitmap]); } ``` ## Options | Option | Default | Description | |--------|---------|-------------| | trackFPS | true | FPS/CPU panels | | trackGPU | false | GPU timing | | trackHz | false | Refresh rate | | trackCPT | false | Compute timing (WebGPU) | | logsPerSecond | 4 | Text update rate | | graphsPerSecond | 30 | Graph update rate | | samplesLog | 40 | Text averaging samples | | samplesGraph | 10 | Graph averaging samples | | precision | 2 | Decimal places | | minimal | false | Click-to-cycle mode | | horizontal | true | Panel layout | | mode | 0 | Initial panel | ## API ### Stats (default export) ```js stats.init(renderer) // Init with Three.js renderer, canvas, or GPUDevice stats.begin() // Start timing stats.end(encoder?) // End timing (pass encoder for native WebGPU) stats.update() // Update display stats.getTimestampWrites() // Get timestampWrites for native WebGPU render pass stats.setData({ fps, cpu, gpu }) // External data (workers) stats.addPanel(panel) // Custom panel stats.addTexturePanel(name) // Texture preview stats.setTexture(name, target) // Set texture source stats.setTextureBitmap(name, bmp) // Set ImageBitmap stats.dispose() // Cleanup ``` ### StatsProfiler (headless for workers) ```js profiler.init(gl | device) // Init with GL context or GPUDevice profiler.begin() // Start timing profiler.end(encoder?) // End timing (pass encoder for native WebGPU) profiler.update() // Process data profiler.getTimestampWrites() // Get timestampWrites for native WebGPU profiler.getData() // { fps, cpu, gpu, gpuCompute } profiler.captureTexture(src, id) // Capture to ImageBitmap profiler.dispose() // Cleanup ``` ### Exports ```js import Stats, { StatsProfiler, PanelTexture, TextureCaptureWebGL, TextureCaptureWebGPU, StatsGLCapture } from 'stats-gl'; // TSL Node addon (WebGPU only, works in main thread and workers) import { statsGL, flushCaptures } from 'stats-gl/addons/StatsGLNode.js'; ```