WebGL
WebGL on a live page runs on hardware you don't control: contexts get evicted and lost, GPUs are blocklisted or switched off, displays run at 120Hz and fractional scales, and server rendering imports the module where there is no window. So the fallback is part of the product, the loop runs only while something visible changes, and the cost per pixel decides the resolution. Sources: [flornkm, webgl-components] for widgets and the GPU probe (MIT), [React Doctor, improve-threejs] for the Three.js and R3F audit (Modified MIT), and [webdown-a, 3d-site] for 3D launch pages, each corrected against the Khronos WebGL spec, MDN, threejs.org and the R3F docs where they disagree. Whether a canvas is the right tool at all is in web-performance BROWSER.md; WebGL in code-made video (drawn from seek(t), rendered in software) is in seek-engine SHADERS.md, and the rules that differ are listed in WIDGETS.md.
Apply only the routes and checks for the selected job. Read a linked skill's named file from its installed folder when available, or from the linked source. Report an unavailable required reference instead of inventing it. [skill-authoring, DISTRIBUTION.md#composing-skills]
| The job | Read |
|---|---|
A shader widget rendered many times per page: the live page vs video split, one shared context, the frame loop, fragment cost, glass and dispersion optics, transparency, DPR, texture cache, image decoding, context loss and restore, failIfMajorPerformanceCaveat, fallbacks, "use client" and SSR, reduced motion and ARIA, deterministic identity, verifying without eyes, nonblank-canvas proof and test-suite WebGL disabling |
WIDGETS.md |
Auditing a Three.js or R3F app: severity by render loop, the six-step workflow, P1 to P3 findings, the corrected args rule, disposal and renderer.info, the ten-row visual rubric and its causes, outputColorSpace, resize and setPixelRatio, PerformanceMonitor |
THREEJS-AND-R3F.md |
A scroll-driven 3D launch page: where the owner's landing page differs, content and scene systems, failure layers, data schemas, shell and nav, visual system, the scene contract, GLTFLoader, DRACOLoader, KTX2Loader.detectSupport, HDRLoader, budgets, quality tiers, asset manifest, glTF export checklist, the blueprint, canvas checks |
LAUNCH-SITES.md |
| Which branch this machine takes (strict context, software rasterizer, context cap, blit orientation), as JSON | scripts/gpu-probe.html |
| Licence notices for the copied script and text | scripts/LICENSE-webgl-components.txt, LICENSE-react-doctor.txt |
Select the branch before applying its checks: repeated shader widgets use WIDGETS.md, a Three.js/R3F scene audit uses THREEJS-AND-R3F.md, and a 3D launch page uses LAUNCH-SITES.md. Widget-specific shared-context, fallback-prop and probe requirements do not automatically become requirements for every scene audit. [flornkm, webgl-components; React Doctor, improve-threejs; webdown-a, 3d-site]
The rules
- For repeated shader widgets, one WebGL context and one rAF loop for every instance on the page. Blit from the shared canvas into cheap 2D canvases within the same task as the draw. [flornkm, webgl-components] (WIDGETS.md)
- Draw only while something visible changes. Gate instances on
IntersectionObserver, pause when the tab is hidden, cap slow ambient drift at 30fps, and useframeloop="demand"withinvalidate()for an R3F scene that changes only on input. [flornkm, webgl-components]; [React Doctor, improve-threejs] (WIDGETS.md, THREEJS-AND-R3F.md) - Rank by where code runs. Allocation or
setStateinsideuseFrameor a rAF callback outranks a bigger mistake in a settings panel. [React Doctor, improve-threejs] (THREEJS-AND-R3F.md) - Let the shader's cost decide the pixel ratio, after the shader is final; for a three.js scene, size the buffer yourself and cap its pixel count instead of
setPixelRatio(window.devicePixelRatio), which the manual calls "strongly NOT RECOMMENDED". [flornkm, webgl-components]; https://github.com/mrdoob/three.js/blob/dev/manual/pages/responsive.html (WIDGETS.md, THREEJS-AND-R3F.md) - For the shader-widget branch, refuse software rendering and ship the fallback as a product surface:
failIfMajorPerformanceCaveat: true, afallbackprop in every example, aforceFallbackdemo, and a positioned clipping frame. [flornkm, webgl-components] (WIDGETS.md) - Handle context loss both ways: call
preventDefault()onwebglcontextlostonly if you rebuild every resource onwebglcontextrestored; otherwise stay on the fallback. https://registry.khronos.org/webgl/specs/latest/1.0/ (WIDGETS.md) - Dispose what the framework does not. three.js frees nothing on removal; R3F disposes JSX objects on unmount but not
<primitive>objects; watchrenderer.info.memory, not only the JS heap. https://github.com/mrdoob/three.js/blob/dev/manual/pages/how-to-dispose-of-objects.html, https://r3f.docs.pmnd.rs/api/objects (THREEJS-AND-R3F.md) - Mark colour textures sRGB and data textures not; the renderer's
outputColorSpacealready defaults toSRGBColorSpace. https://threejs.org/docs/pages/WebGLRenderer.html (THREEJS-AND-R3F.md) - Keep content out of the canvas. On a launch page the content system works without the scene, without JavaScript and with WebGL off, and an asset error breaks only its own scene. [webdown-a, 3d-site] (LAUNCH-SITES.md)
- Load through cached three.js loaders: one
DRACOLoader,KTX2Loader.detectSupport(renderer)before the first texture,HDRLoader(not the renamedRGBELoader) for HDR environments. https://threejs.org/docs/pages/KTX2Loader.html, https://threejs.org/docs/pages/HDRLoader.html (LAUNCH-SITES.md) - Reduced motion keeps the look and stops the motion: one still frame at full fidelity for a widget; no orbiting, scroll-scrubbed or shader-heavy transitions for a scene. [flornkm, webgl-components]; [webdown-a, 3d-site] (WIDGETS.md, LAUNCH-SITES.md)
- No visual finding without evidence: a screenshot, frame capture or reproduced observation, or a label saying it was inferred from source. [React Doctor, improve-threejs] (THREEJS-AND-R3F.md)
Prove it
Apply the selected branch's acceptance checks to the surface changed, and report checks not performed as unverified. For the shader-widget branch, scripts/gpu-probe.html reports the branch you expect on each target machine; Chrome with --disable-gpu shows every fallback clipped to its frame and the content readable; a forced context loss flips to the fallback (and back, if you restore); tab-hidden CPU sits near zero and an idle demand-loop scene renders no frames; renderer.info.memory stays flat across mount and unmount; the canvas has non-black, non-transparent pixels after load at desktop and mobile sizes; reduced motion shows a still, full-fidelity frame; a production build serves the page; and every file's own Prove it checks pass for the parts you touched.