44 KiB
Three.js and React Three Fiber grading-rubric coverage
React Doctor statically diagnoses code defects. It does not claim that source inspection can replace the rubric's Playwright probes, temporal evidence, prompt profile, or craft review. This map keeps that boundary explicit so adding rubric coverage does not turn intentional fixed cameras, flat styles, static scenes, or repeated formations into false positives.
The rule pairs below use Three.js import provenance and closed, locally provable object graphs. They abstain when spreads, callbacks, custom components, imported scene members, shader materials, or helper mutations can change the result.
Source-request coverage
The original request included a core Three.js/R3F implementation checklist and four explicit GPU performance directives before the grading rubric. Those requirements are part of this coverage contract rather than implicit background.
Core scene and implementation checklist
| Requested pattern | Three.js static coverage | React Three Fiber static coverage | Remaining evidence boundary |
|---|---|---|---|
| Scene, camera, renderer, DOM attachment | three-valid-perspective-camera, three-valid-orthographic-camera, three-require-renderer-dom-attachment, and three-require-renderer-size |
r3f-valid-perspective-camera, r3f-valid-orthographic-camera, and managed Canvas ownership |
Required central output and successful boot remain runtime probes. Instantiating a Scene is not required when a valid externally owned scene is rendered. |
| Responsive resize without distortion | three-require-camera-aspect-on-resize, three-effect-composer-require-size-on-resize, and three-no-unconditional-renderer-resize-in-animation-loop |
r3f-no-manual-canvas-resize; Canvas owns renderer sizing and camera updates |
CSS layout, embedded canvases, and the observed post-resize image require a runtime resize trial. |
| Geometry, meshes, and attributes | three-valid-buffer-attribute-array-length, three-valid-buffer-attribute-item-size, three-no-normalized-float-buffer-attribute, update-flag rules, and cleanup rules |
Paired buffer validation, update-flag, ownership, and inline-resource rules | Whether a custom shape visually matches the brief is craft evidence. |
| Materials, PBR properties, and textures | Opacity, transparency, PBR/physical-property, ignored-property, color-space, wrapping, UV, normal, and environment rules | Paired JSX/material contracts with authoritative-prop and spread handling | Material attractiveness, variation, texture scale, and shader-specific semantics require pixels or craft review. |
| Asset loading, loading state, and failure handling | three-require-loader-error-handling, three-require-ktx2-detect-support, and worker-loader cleanup |
r3f-prefer-use-loader and r3f-no-null-loader-input; R3F loader hooks integrate with externally owned React Suspense and error boundaries |
Network completion, decoder failures, and externally placed loading/error boundaries require runtime exercise or owning-component context. |
| Effective lighting | Light-property validators plus three-require-lighting-for-pbr and three-require-environment-for-metal |
Paired closed-Canvas lighting and environment rules |
Form separation, mood, and dynamic/imported lighting remain pixel and craft evidence. |
| Shadows | three-require-shadows-enabled, three-no-shadows-on-unsupported-light, three-valid-shadow-map-size, and three-limit-shadowed-point-lights |
Paired shadow contracts | Visible shadow quality and prompt applicability require rendered evidence. |
| Animation loop and frame-rate independence | three-prefer-set-animation-loop, three-require-render-in-animation-loop, three-require-frame-delta, and animation-loop safety rules |
useFrame priority, delta, async, dependency-array, recursion, and hot-loop rules |
Smoothness and equivalent evolution under throttling require temporal probes. |
| Orbit controls, picking, and pointer interaction | three-require-controls-update, three-require-controls-cleanup, three-valid-raycaster-range, and pointer hot-path rules |
r3f-valid-raycaster-range, pointer-event mutation/capture rules, and pointer hot-path rules |
Whether controls exist when requested, gesture separation, coordinate locality, and selection correctness require prompt-aware input trials. |
| Modular structure | General React Doctor architecture and component-size rules apply; Three.js resource ownership is separated into focused lifecycle rules | The same architecture rules apply to components and hooks | File/module decomposition is not a universal Three.js correctness contract and must not be inferred from scene complexity alone. |
| Performance and disposal | Instancing, GPU-animation, allocation, compile, clone, resize, readback, material-recompile, update-flag, and owned-resource cleanup families | Paired useFrame, instancing, GPU-animation, managed-resource, loader-cache, root-unmount, and cleanup families |
Absolute FPS, draw-call budgets, and dynamic GPU memory growth remain runtime measurements. |
Explicit GPU and instancing directives
| Directive | Three.js rule | React Three Fiber rule | Reporting boundary |
|---|---|---|---|
Use InstancedMesh for many copies of shared geometry and material |
three-prefer-instanced-mesh |
r3f-prefer-instanced-mesh |
Reports only provably repeated rendered meshes with stable shared resources and no incompatible per-object semantics. |
| Favor GPU animation for repeated instance transforms | three-prefer-gpu-instanced-animation |
r3f-prefer-gpu-instanced-animation |
Reports repeated per-instance matrix/color writes in a recognized frame loop; one-time setup and open helpers abstain. |
| Animate repeated vertex positions in shaders or GPU state instead of rewriting buffers each frame | three-prefer-gpu-position-animation |
r3f-prefer-gpu-position-animation |
Reports repeated position-buffer entry mutations in a recognized animation loop or useFrame; bulk one-time initialization and unrelated attributes stay quiet. |
| Compute particle position and transform evolution on the GPU where possible | three-prefer-gpu-position-animation and three-prefer-gpu-instanced-animation |
r3f-prefer-gpu-position-animation and r3f-prefer-gpu-instanced-animation |
Particle vocabulary alone never reports. The detector requires the harmful CPU-per-frame mutation shape, so static or low-cardinality systems are not penalized. |
These boundaries follow the current Three.js guidance for WebGLRenderer.setAnimationLoop, InstancedMesh, and BufferAttribute updates, together with R3F's managed Canvas, useFrame and useLoader, automatic disposal, and instancing guidance.
Added static contracts
| Contract | Three.js | React Three Fiber | Static reporting boundary |
|---|---|---|---|
| Normal-mapped custom geometry requires normals | three-require-lit-material-normals |
r3f-require-lit-material-normals |
A closed BufferGeometry defines position, does not define or compute normal, and is paired with a built-in lit material that applies a non-null normalMap. Unlit, shader, unmapped, and open materials are excluded. |
| Mapped custom geometry requires UVs | three-require-uv-for-texture-map |
r3f-require-uv-for-texture-map |
A closed BufferGeometry defines position, has none of uv, uv1, uv2, or uv3, and is paired with a built-in material that has a non-null UV-sampled map. |
| Strong metal requires an environment | three-require-environment-for-metal |
r3f-require-environment-for-metal |
A closed rendered scene or inline Canvas contains a standard or physical material with statically known metalness > 0.5, no material environment map, and no scene environment. Direct lights do not satisfy metal reflections. |
| PBR requires a lighting source | three-require-lighting-for-pbr |
r3f-require-lighting-for-pbr |
A closed rendered scene or inline Canvas contains a standard or physical material and no direct light, scene environment, material environment map, light map, or intentional emissive source. |
These contracts follow the Three.js definitions of BufferGeometry attributes, texture UV channels, and environment lighting for MeshStandardMaterial. Since Three.js r183, renderers fall back to derivative-based flat shading when ordinary lit geometry has no normal attribute, so the normals rules intentionally report only the normal-map case where that fallback does not apply.
Grading-protocol boundary
The rubric also specifies how a grader combines evidence. Those instructions are not application source patterns and therefore do not become lint diagnostics.
| Rubric protocol | React Doctor responsibility | External grader responsibility |
|---|---|---|
| Keep the probe floor and craft review separate | Report source diagnostics with their normal rule identity, category, severity, and confidence boundary | Report Plane 1 and Plane 2 side by side and never convert diagnostic count into craft credit or sum the planes into one rubric score |
| Set prompt applicability before grading | Avoid inferring fixed-camera, flat-style, static-scene, interaction, occupancy, or visual-ambition requirements from implementation vocabulary | Declare one prompt profile, eligible probes, eligible craft weight, and evidence modality before inspecting competing outputs |
| Grade runtime failures on calibrated share | Keep each static diagnostic no broader than a locally proven defect and abstain on visibility, share, and intent ambiguity | Measure visible/runtime share, calibrate thresholds against a reference, and report not measurable when confidence is insufficient |
| Exercise multiple poses, time, input, and recovery paths | Diagnose statically provable setup, lifecycle, hot-loop, and resource-ownership defects | Run boot, grazing, wide, and rotated poses; idle soak; normal and rapid input; cancellation; resize; pause/resume; and privileged-API accept/reject/retry flows |
| Apply hard and craft gates before scores | Do not claim that a clean source scan proves a rendered experience booted, completed, or achieved spatial integrity | Apply page/boot hard gates and the eligible experience-completeness and spatial-integrity craft gates before reporting scores |
| Preserve every passing probe as a regression | Keep rule tests, fuzz regressions, open-source eval evidence, pull-request parity, and the complete repository test suite green | Re-run the full probe.mjs battery for each build round instead of selecting only probes related to the latest source change |
Use probe.mjs through public runtime evidence |
Do not require app-specific debug APIs or pretend lint can observe pixels, browser diagnostics, GPU counters, or temporal state | Serve the built app and run the Playwright battery through __THREE_DEVTOOLS__, pixels, browser diagnostics, and synthetic input, using lite mode when its documented perturbation requires it |
Plane 1A: core discriminators
| Rubric probe | Static coverage | Disposition |
|---|---|---|
| Page errors | None | Runtime-only hard gate. Source cannot establish whether a page throws over the complete exercise path. |
| Console diagnostics | Shader validation, WebGPU compatibility, render-target, texture, and material rules catch provable precursors | Runtime console capture remains authoritative for driver, framebuffer, texture-unit, and deprecation diagnostics. |
| Boot and render | three-require-render-in-animation-loop, three-require-renderer-dom-attachment, three-require-renderer-size, and R3F Canvas rules |
Framebuffer activity and required central output remain runtime-only. |
| Unprompted-interaction gate | None | Requires synthetic input before and after a documented entry flow. |
| Privileged-input acquisition and recovery | None | Accept, rejection, retry, and browser permission state require runtime exercise. |
| Input to response locality | three-valid-raycaster-range and r3f-valid-raycaster-range cover invalid ray intervals only |
Pointer/response centroids and coordinate transforms require rendered input trials. |
| Input cross-contamination | None | Requires causal comparison of distinct gestures. |
| Input lifecycle | Controls and root cleanup rules cover owned resources, not held-state cancellation | Blur, pointercancel, visibility, and lock-loss behavior require runtime state observation. |
| Saturation bound | Numeric material, fog, camera, light, and buffer validators cover local invalid values | Simulation amplitude, NaN pixels, and freeze signatures require stressed runtime input. |
| Time-step independence | three-require-frame-delta and r3f-require-frame-delta |
Static rules catch frame-step mutation that ignores the supplied delta; replay at multiple frame rates remains the behavioral oracle. |
| Metal without environment | three-require-environment-for-metal and r3f-require-environment-for-metal |
Static coverage is limited to closed scenes and known metalness. Runtime share grading covers dynamic and imported scenes. |
| Interaction jank | Allocation, clone, compile, readback, resize, state-update, and material-recompile loop rules catch high-confidence causes | Long Animation Frame duration and the calibrated 200 ms threshold are runtime-only. |
Plane 1B: scoped graded checks
| Rubric probe | Static coverage | Disposition |
|---|---|---|
| Transparency sorting risk | three-require-transparent-for-opacity and r3f-require-transparent-for-opacity validate opacity/transparent consistency |
Visibility, world-space classification, cutout intent, ordering, and problematic share require runtime evidence. A per-material sorting warning would violate the rubric's scope. |
| Color-texture semantics | three-valid-texture-color-space and r3f-valid-texture-color-space |
Reports only statically classified color and data map roles; procedural or unknown semantics abstain. |
| Mipmap filters | None | HUD exemption, world-space use, sampled visibility, and the bad-share threshold require runtime texture/material association. |
| Shading normals | three-require-lit-material-normals and r3f-require-lit-material-normals |
Current Three.js supplies a flat-shading fallback for ordinary lit geometry. The static rules report only normal-mapped geometry where normals remain required; shader, unlit, open, and externally mutated geometry abstains. |
| Z-fighting | None | Dolly flips must be measured against idle framebuffer churn from multiple poses. |
| Light rotation or lights-out cue | Lighting configuration rules validate provable setup errors | Pixel response relative to idle noise requires runtime rotation or light toggling. |
| Input release | None | Eligibility requires a causal motion pretest, followed by held, released, and ambient speed measurements. |
| Exposure and clipping | Numeric light/material validators catch impossible local parameters | Global and grid luminance/clipping shares require framebuffer pixels. |
| Return to rest | None | Transient intent and the idle-noise envelope require prompt applicability and temporal evidence. |
| Motion continuity | Frame-delta and GPU-animation rules cover implementation hazards | Entity tracking, declared teleports, cuts, wraps, respawns, and velocity discontinuities require runtime state. |
| Bounds containment | Camera and raycaster range validators cover invalid numeric ranges only | Intended containing volume and visible transitions require scene semantics and tracked transforms. |
| Idle motion churn | None | Static-by-design applicability and framebuffer churn require prompt and temporal evidence. |
| Camera temporal stability | Camera validators, projection-update rules, and controls-update rules cover setup defects | Primary-camera selection, intentional shake/cuts, and transform frequencies require runtime evidence. |
| Control-gain envelope | None | Useful macro range and smallest intended interaction scale depend on FOV, target size, playable range, and input trials. |
| Repeated-entity occupancy | Instancing preference, instance-buffer update, and GPU-instanced-animation rules cover implementation quality | Formations, stacks, transforms, visible overlap, and interpenetration require prompt scope and runtime instance matrices. |
| Resize and aspect | three-require-renderer-size, three-require-camera-aspect-on-resize, three-effect-composer-require-size-on-resize, three-no-unconditional-renderer-resize-in-animation-loop, and r3f-no-manual-canvas-resize |
Runtime resize remains useful for CSS/layout and embedded-canvas behavior that source cannot prove. |
| Texture-load completion | three-require-loader-error-handling, r3f-prefer-use-loader, r3f-no-null-loader-input, and KTX2 support checks |
Network completion, decoding, and visible use still require runtime observation. |
| Resource leak | Renderer, root, controls, geometry, material, texture, render-target, animation-mixer, postprocessing, GPU-computation, and worker-loader cleanup rules | Static rules cover locally owned resources; soak testing remains authoritative for dynamic ownership and GPU memory. |
| Wrap versus repeat | three-texture-repeat-requires-wrapping, r3f-texture-repeat-requires-wrapping, and three-require-texture-update-after-wrapping-change |
Explicit, local wrapping contracts are fully static; visual tiling quality is not graded. |
| Normal-map sanity | Normal, UV, color-space, and PBR-property rules cover provable prerequisites | Tangent orientation, handedness, strength, and visible inversion require pixels or model semantics. |
| UV under map | three-require-uv-for-texture-map and r3f-require-uv-for-texture-map |
Modern uv through uv3 channels are accepted; shaders and open geometry abstain. |
| Lights for PBR | three-require-lighting-for-pbr and r3f-require-lighting-for-pbr |
Closed graphs only; dynamic/imported lighting is left to the runtime probe. |
Plane 1C: report-only context
| Rubric measurement | Related static diagnostics | Why it remains report-only |
|---|---|---|
| Frame budget, draw calls, and triangles | Instancing and hot-loop allocation/compile/readback rules suggest structural improvements | Absolute counts combine ambition, scene scale, hardware, and host contention. |
Render calls per requestAnimationFrame |
Render-loop presence, render-target reset, and composer ordering rules validate correctness | Shadows, minimaps, postprocessing, and simulation passes may legitimately render more than once. |
| Untextured surface share | Basic-material ignored-property rules catch configuration mistakes | Flat, procedural, and stylized surfaces may be intentional. |
| Shadow contrast ratio | Shadow enablement, supported-light, map-size, and shadowed-point-light rules cover configuration | Art direction and lighting conditions determine desirable contrast. |
| Depth range | Perspective and orthographic camera validators catch invalid clipping planes | A broad far / near ratio may be required by world scale. |
| Chromatic or tonal variance | Texture color-space and exposure-related parameter checks cover technical misuse | Monochrome and high-variance palettes can both be deliberate. |
| Spatial spawn entropy | Instancing rules make repeated entities cheaper but do not grade arrangement | Formations may be intentional; promote only through a prompt-conditioned runtime assertion. |
aoMap or uv1 presence |
UV-under-map rules accept uv, uv1, uv2, and uv3 |
Three.js texture.channel selects the channel, so one named attribute is not universally required. |
| Output color space | Texture semantics and three-effect-composer-output-pass-last cover common pipeline errors |
Linear renderer output followed by an sRGB output pass can be correct. |
Plane 1D: prompt-conditioned checks
All checks in this tier remain prompt-profiled runtime assertions. Static rules must not infer the intended mapping, direction, amplitude, distribution, palette, or timing from implementation vocabulary alone.
| Prompt-conditioned claim | Static support | Required evidence |
|---|---|---|
| Camera-relative W-angle and per-key travel directions | Frame-delta rules catch frame-coupled movement | Synthetic key trials and tracked camera/actor transforms. |
| Diagonal normalization | None | Compare cardinal and diagonal displacement under the declared control scheme. |
| Pointer versus keyboard expectations | Pointer hot-path and mutation rules catch unsafe implementations | Exercise both modalities according to the prompt profile. |
| Aim, reticle, selection, and hit-volume congruence | Raycaster range rules catch invalid intervals | Compare visible target, pointer/reticle, hit point, and response. |
| Texture coverage or surface-depth demands | Texture/UV/material rules validate technical prerequisites | Pixel, depth, or geometry evidence tied to the requested regions. |
| Behavior causality | None | A/B input intervention and response delta. |
| Locality claims | None | Trigger at distributed locations and track the effect over time. |
| Named decay and settling windows | None | Temporal variance or state measurements over the requested window. |
| Camera shake, recoil, follow, or stabilization amplitude | Camera setup and frame-delta rules catch local defects | Camera-transform trials calibrated to the task. |
| Encounter distribution, separation, cooldown, and progression | Instancing/update rules cover implementation hazards | Tracked spatial and temporal event samples. |
| Adaptive-system causality | None | Controlled player-performance intervention and directional response. |
| Relational color, contrast, and composition | Color-space rules validate texture interpretation | Region-to-region pixel comparisons derived from the brief. |
Plane 2: craft review
| Craft criterion | Static contribution | Why craft evidence remains necessary |
|---|---|---|
| Experience completeness | Renderer, loader, animation, and lifecycle rules catch missing technical wiring | Requested outcomes, reachability, depth, and end-to-end confirmation require exercising the experience. |
| Spatial and interaction integrity | Raycaster, geometry, camera, and buffer rules catch local invalid contracts | Collision, hit volumes, occlusion, tunneling, and impact agreement require runtime geometry and interaction evidence. |
| Encounter diversity and pacing | Instancing and update rules cover repeated-entity implementation | Enjoyment, escalation, separation, repeat control, and stage identity are prompt- and time-dependent. |
| Perceptual calibration and readability | Material, texture, light, fog, camera, and opacity rules catch invalid values | Subject separation, compositing, affordance clarity, and effect amplitude require representative pixels. |
| Animation and temporal craft | Delta, mixer-update, GPU-animation, and buffer-update rules catch implementation defects | Articulation, personality, continuity, secondary motion, and settling quality require video or interaction. |
| Camera and control feel | Camera, projection, controls-update, and frame-delta rules catch setup defects | Comfort, smoothing, collision response, recoil, gain, and precision require hands-on or temporal evidence. |
| Input conventions and tuning | Pointer hot-path rules catch jank-prone state and allocation work | Direction, discoverability, remapping, gain, and lifecycle require prompt-aware input trials. |
| Model fidelity | Geometry attribute validators prevent malformed buffers | Silhouette, proportion, defining parts, style, and viewing-distance detail are visual judgments. |
| Material treatment | PBR ranges, physical properties, texture semantics, UVs, normals, opacity, lighting, and environment rules cover technical contracts | Differentiation, variation, scale, attractiveness, and stylistic coherence remain visual judgments. |
| World composition | Camera/fog ranges catch invalid numeric setup | Hierarchy, depth, landmarks, negative space, and dressing require scene-level visual review. |
| Lighting and atmosphere | Light validity, PBR lighting, metal environment, shadows, and exposure-related parameter rules catch technical defects | Mood, form separation, palette, atmosphere, and reactive-light taste require pixels and prompt intent. |
| Scene hygiene | Cleanup, z-adjacent depth configuration, resource, and motion implementation rules reduce common defects | Floating, clipping, flicker, popping, overlap, grounding, and rare paths require full sweeps and soak. |
| HUD and UI craft | General React accessibility and interaction rules still apply to DOM UI | Required readouts, hierarchy, styling, feedback, pause, and recovery flows require prompt-aware review. |
| Frame stability | Hot-loop allocation, compile, readback, resize, state, clone, instancing, and GPU-animation rules target high-confidence causes | Representative and worst-case frame pacing must be measured on the running build. |
Intentionally rejected universal lint rules
- Transparency sorting, mipmap choice, z-fighting, exposure, motion continuity, containment, occupancy, idle churn, camera stability, and absolute performance depend on visibility, share, time, pixels, or prompt intent.
- Texture repetition, untextured share, spawn entropy, output color space, scene complexity, and fixed-camera or static-scene choices are not defects without a task-specific contract.
- Runtime and craft rows must be reported as not observed or not measurable when the required evidence is unavailable. Absence of a static diagnostic is never a passing craft score.