useSceneGate
A heavy scene requires four distinct runtime decisions: should it exist yet, should it be drawing, how much fidelity should it render, and is its graphics context still alive. A page that answers only the first leaves a WebGL canvas burning battery and frames for a visitor who scrolled past ten seconds ago.
Six scenes, one context loss
A driver reset or a GPU switch does not take one canvas. It takes all of them, silently.
Nothing in these cards handles the loss. Each one reports it and is keyed on generation. Remove the key and they stay black.
The useSceneGate hook makes all four lifecycle decisions and reports them through a typed state snapshot. Applying them is your responsibility: nothing here directly touches a renderer, pixel ratio, or post-processing pass, giving you full control over what "full" and "reduced" look like in your application.
The hook composes useSafeToMount (headroom and core floor verification) and useAdaptiveQuality (device floor + render pressure attribution) with proximity intersection observing and WebGL context-loss recovery.
When a user scrolls past a scene, the gate enters "idle" state: the render loop pauses while GPU textures, compiled shaders, and buffers remain intact in memory. Once the user scrolls back, resuming rendering takes a single subscriber tick rather than a catastrophic multi-frame context rebuild.
◆Interactive Lifecycle & WebGL Context Playground
Quick start
Attach the returned ref to your scene container and conditionally render your WebGL canvas with the generation key.
The generation counter increments when WebGL context loss occurs, automatically remounting a fresh canvas.
The Seven Scene Lifecycle States
The complete state machine governing scene lifecycle, visibility, quality, and context restoration.
| State | Mounted | Quality | Description |
|---|---|---|---|
| "dormant" | false | null | Too far from viewport to warrant initialization. |
| "warming" | false | null | Within preload proximity threshold; waiting for frame headroom. |
| "active" | true | "full" | Visible and running at full visual fidelity. |
| "constrained" | true | "reduced" | Running with reduced geometry, resolution, or post-processing. |
| "idle" | true | "full" | "reduced" | Started but currently off-screen. Render loop paused; assets retained. |
| "recovering" | true | "reduced" | WebGL context lost. Increments generation to re-create canvas. |
| "poster" | false | null | Hardware floor or reduced-motion disables 3D scene entirely. |
API Reference
Syntax
Monitors viewport proximity, main-thread headroom, render pressure, and WebGL context health.
Parameters
| Parameters.field | Type | Default | Description |
|---|---|---|---|
| options.cost | "light" | "normal" | "heavy" | "heavy" | Complexity tier passed through to useSafeToMount to determine required headroom. |
| options.preload | number | 200 | Distance in pixels before the viewport to transition from dormant to warming. |
| options.label | string | undefined | Diagnostic label for DevTools and telemetry inspectors. |
| options.content | boolean | false | For a section the reader came for, like a chart, rather than decoration. It still waits to be near, for the scroll to settle and for frame headroom, then mounts in the constrained state even under reduced motion or on a device below the floor, where a scene would get its poster. Needs 4.2.0 or newer. |
Return Value
Contains ref, state, mounted, quality, generation, cause, and reason.
Properties & State
| Properties.field | Type | Description |
|---|---|---|
| scene.ref | RefObject<T | null> | Hybrid ref attached to the container element whose position determines proximity. |
| scene.state | SceneState | "dormant" | "warming" | "active" | "constrained" | "idle" | "recovering" | "poster" |
| scene.mounted | boolean | Whether the scene should exist in the DOM (true for active, constrained, idle, recovering). |
| scene.quality | "full" | "reduced" | null | Fidelity setting when mounted; null when dormant, warming, or poster. |
| scene.generation | number | Context recovery counter. Pass to <Canvas key={scene.generation}>. |
| scene.cause | SceneCause | "ok" | "not-near" | "waiting-for-headroom" | "off-screen" | "render-bound" | "frame-rate" | "device-floor" | "reduced-motion" | "context-lost" |
| scene.reason | string | Human-readable diagnostic string explaining the current verdict. |
WebGL Context Loss Recovery
Automatic recovery mechanism for driver crashes, backgrounded tabs, or GPU context eviction.
Lifecycle & Invariant Guarantees
- →Zero unmount thrashing: Once a scene starts, scrolling off-screen transitions to idle rather than tearing down WebGL contexts.
- →One reset is one rebuild: A driver reset takes every canvas on the page at once. Losses arriving together are treated as the same reset, so generation increments once and the page rebuilds once, however many scenes were affected. Requires 4.0.3 or newer. From 4.2.0 a replacement lost moments after that rebuild counts as a new failure and rebuilds again, at most three times in a row.
- →A forgotten reportHealthy costs a warning, not the scene: If nothing reports healthy within 5 seconds of a rebuild, the runtime assumes it worked, leaves recovering and warns once, naming the call to add. Before, the next loss was swallowed and the canvas stayed black. Needs 4.2.0 or newer.
- →Automatic context healing: Recovers seamlessly from WebGL context losses by incrementing generation without requiring page reloads.
- →Attribution-accurate quality: Only degrades quality when rendering is the bottleneck; ignores unrelated main-thread CPU blocking. From 4.2.0 the worst tier reduces quality too, where it used to fall through to full.
- →Hydration safety: Starts dormant/poster during SSR and initial hydration, preventing hydration mismatch errors.
Architectural Principles of Scene Gating
Unmounting a WebGL scene throws away the graphics context, vertex buffers, uploaded textures, and compiled shader programs. Coming back forces the browser and GPU driver to re-compile and re-upload everything from scratch at the exact moment the user is looking. On mobile devices, this causes severe multi-second stutter. The gate pauses the render loop in "idle" without dropping memory.
If third-party analytics scripts or JSON parsing blocks the main thread, reducing particle counts or shader resolution does not speed up the CPU; it only makes the page uglier while remaining just as slow. The gate uses frame pressure attribution to degrade only when "render" is the diagnosed bottleneck.
◆Production Examples
Battle-tested production patterns ready to copy directly into your codebase.
Render an accessible, lightweight WebP still poster until the scene is near and the thread is calm.
When NOT to use this
Rules & Gotchas
- →Attach the ref to the element whose visibility decides display: On sticky or pinned sequences, observe the sticky child rather than the tall parent scroll container.
- →The ref target may be rendered conditionally: Hybrid refs safely re-arm when Suspense fallbacks or dynamic loaders swap in the real DOM element.
- →Branch on state and cause, never on reason: reason is a human-readable string subject to phrasing changes. state, quality, and cause are strictly typed.
- →Always pass generation to the Canvas key: This ensures clean automatic recovery if the WebGL context is lost due to GPU driver reset or memory reclamation.
Related Motion Components
See it live in the lab: /lab/context-loss