Three.js r186
Three.js 0.186.x (r186) code, assets and tooling without pre-r186 APIs, with owned GPU resources, bounded per-frame work and package-manager-owned dependencies; any renderer, runtime or app shape. Rule IDs TJ-V/T/A/L/F/G/P/X/E/S/Q; NEED TO blocks.
Set this profile
ccpp cpo profile set threejsInside Claude Code, prefix the command with ! to run it in the session. Downloading a profile needs a Builder or Team plan; listing them does not.
Policies
93 policies, grouped by what they guard.
Error handling 1
Failures are propagated or handled, never swallowed or turned into fake success.
tj-l12-swallowed-failuresTJ-L12/S08 · Three.js loading, initialization and compilation failures are not swallowed (empty catch, .catch(() => {}))
Performance & resource bounds 6
Timeouts, bounded retries and fan-out, reused clients, closed resources, no N+1 queries.
tj-f09-frame-allocationsTJ-F09/F10/L13/P08/X11 · Per-frame callbacks do no unguarded allocation, GPU object construction or recompilation triggers
tj-f11-frame-callsTJ-F10/F11/G06/G16/P19/S05/X13 · Per-frame callbacks do no unguarded loading, compilation, PMREM generation, vertex baking, layout reads, logging, scene scans or sync readback
tj-l02-remove-without-disposeTJ-L02/L04 · Code that removes or clears scene objects it built releases their GPU resources (remove/clear/null do not dispose; material.dispose() keeps its textures)
tj-l06-recreated-gpu-objectsTJ-L06/L13 · Resize and re-setup handlers resize or reuse renderers and render targets instead of constructing new ones without disposing the old
tj-l10-decoder-loadersTJ-L10/X07/X08 · KTX2/DRACO loaders are configured once (transcoder path, detectSupport), reused across assets and disposed when done
tj-l14-teardownTJ-L09/L14/S09 · A teardown that exists stops the render loop and releases the renderer, controls, timer and animation mixers the module created
Architecture & boundaries 4
Declared layers and dependency directions, no import cycles, small modules, explicit dependencies.
tj-a06-host-globalsTJ-A06 · Reusable Three.js modules receive their canvas/container and size from the host (no document.body append or window sizing outside entry modules)
tj-s02-global-handlesTJ-S02 · Renderer, scene, camera and loaders are not published as mutable globals (window.x / globalThis.x)
tj-s06-function-lengthTJ-S06 · With a selected budget (maxFunctionLines), functions in Three.js modules stay within it (warn; off by default)
tj-s07-renderer-internalsTJ-A07/S07 · No patching of three internals (private renderer fields, ShaderChunk/ShaderLib overrides, prototype monkey patches, raw GL state)
Versions & deprecated APIs 26
Code matches the installed version, without removed, renamed or deprecated APIs.
tj-v01-installed-versionTJ-V01/V02/V04 · At session start, Claude learns the resolved three (and @types/three) version and whether this r186 profile applies
tj-v03-post-r186-apisTJ-V03 · No r187+ API in r186 code (Light.registerNode light registration)
tj-v05-commonjs-requireTJ-V05 · New code imports three as an ES module (require('three') is deprecated in r186 and warns at run time)
tj-v05-core-exportsTJ-V05/V08/V09/V10/V41 · Imports from three and three/webgpu name exports the resolved package has (no Geometry, *Encoding, *BufferGeometry, default import…)
tj-v05-module-pathsTJ-V05/V06/V08/V11 · three subpath imports exist in the resolved package (no examples/js, three/nodes, removed addons, removed global builds)
tj-v05-tsl-exportsTJ-V05/V42 · TSL imports from three/tsl name functions the resolved package exports (no tslFn, timerLocal, cond, uniforms, rangeFog, blend renames…)
tj-v06-html-modulesTJ-V04/V06 · HTML pages load three as modules from one revision: no removed three(.min).js builds, an import map for bare imports, core and addons on the same version
tj-v07-addons-aliasTJ-V07 · New addon imports use the public three/addons/ alias rather than three/examples/jsm/
tj-v09-removed-membersTJ-V09/V10/V14/V34/V35/V37/V41 · No removed three members or options (outputEncoding, useLegacyLights, addAttribute, updateRange, waitForGPU, reverseDepthBuffer, TextGeometry height…)
tj-v11-deprecated-importsTJ-V11/V16/V17/V18/V31/V42/V45 · New code avoids three exports r186 deprecates (Clock, PostProcessing, PCFSoftShadowMap, Source, RGBELoader, PI2, cache, label…)
tj-v12-deprecated-membersTJ-V12/V25/V27/V32/V33/V34 · New code avoids deprecated three members (renderAsync/clearAsync/…, setDecoderConfig, detectSupportAsync, createShapes, GTAO distance settings…)
tj-v12-lsp-deprecatedTJ-V11/V12/V45 · No new call or member resolves to a declaration three marks @deprecated (language server: Matrix3 transforms, PassNode.setResolution, PMREM async…)
tj-v13-webgpu-initTJ-V13 · WebGPURenderer is initialized (await init() or setAnimationLoop) before hasFeature/compute/render/initTexture/detectSupport, and init failures are handled
tj-v15-webgpu-disposeTJ-V15 · WebGPURenderer.dispose() is awaited or returned when teardown completes asynchronously (WebGLRenderer disposal stays synchronous)
tj-v19-dispose-superTJ-V19 · A class extending a three Object3D type that overrides dispose() calls super.dispose() (r186 Object3D.dispose dispatches the dispose event)
tj-v21-triangles-in-placeTJ-V21 · toTrianglesDrawMode() mutates its geometry in place since r186: a second name for the result is a shared, changed geometry
tj-v22-async-resultsTJ-V22/V33 · Promise-returning three APIs (SimplifyModifier.modify, EXR/KTX2 parse, parseAsync, loadAsync, fromCubeRenderTarget) are awaited or chained, not used as values
tj-v23-light-probe-gridTJ-V23 · WebGLRenderer code uses r186's LightProbeGridWebGL / LightProbeGridHelperWebGL (LightProbeGrid is now the WebGPU class)
tj-v24-sky-up-uniformTJ-V24 · Sky / SkyMesh code does not write the removed up uniform (+Y up is assumed since r186)
tj-v29-loader-return-valueTJ-V29 · FileLoader.load() and ImageBitmapLoader.load() results come from onLoad or loadAsync (their return value is undefined since r184)
tj-v30-fbx-z-upTJ-V30 · No blanket Z-up correction on FBX imports (FBXLoader converts Z-up assets itself since r184)
tj-v36-copy-signaturesTJ-V36 · copyTextureToTexture / copyFramebufferToTexture use the r186 argument order (textures first, positions after)
tj-v38-batched-instancesTJ-V38 · BatchedMesh geometries become visible through addInstance(geometryId), not addGeometry alone
tj-v39-transform-controls-helperTJ-V39 · TransformControls is shown by adding controls.getHelper() to the scene (the controls are not an Object3D since r169)
tj-v40-controls-connectTJ-V40 · Controls.connect() receives the DOM element to listen to (no undefined fallback since r175)
tj-v44-position-localTJ-V44 · A positionNode built from positionLocal on skinned or morphed meshes is a deliberate choice (positionGeometry keeps skinning/morphing)
3D rendering 35
Materials, shaders, cameras, render loops and color spaces of real-time 3D scenes.
tj-a02-webgpu-glsl-materialsTJ-A02/A03 · WebGPURenderer code customizes shading with node materials/TSL and post-processes with RenderPipeline (no ShaderMaterial, onBeforeCompile, EffectComposer)
tj-a04-node-materials-webglTJ-A04 · Node materials rendered by WebGLRenderer go through the r186 WebGLNodesHandler (renderer.setNodesHandler)
tj-a08-stencil-bufferTJ-A08 · Stencil-based materials render with a stencil buffer enabled on the renderer created in the same module (off by default since r163)
tj-a09-tsl-js-operatorsTJ-A09 · TSL nodes are combined with node methods (add/mul/If/select), not JavaScript operators or if/ternaries that run once on the CPU
tj-a10-shader-uniformsTJ-A10 · ShaderMaterial uniforms are { value } objects, updated through uniform.value (no raw numbers/vectors, no replacing a uniform)
tj-a11-glsl-versionTJ-A11 · Custom GLSL 3 shaders set glslVersion: GLSL3 and do not embed their own #version directive
tj-e03-render-target-restoreTJ-E03 · Reusable passes (functions receiving the renderer) that switch the render target restores the previous one (getRenderTarget) instead of assuming it was null
tj-e05-pipeline-output-updateTJ-E05 · Replacing RenderPipeline.outputNode after setup sets pipeline.needsUpdate = true
tj-f01-nested-frame-loopsTJ-F01 · A callback given to setAnimationLoop does not also schedule itself with requestAnimationFrame (one scheduling owner per surface)
tj-f02-xr-animation-loopTJ-F02 · XR rendering is scheduled by renderer.setAnimationLoop (window requestAnimationFrame does not run inside an XR session)
tj-f05-time-unitsTJ-F05 · AnimationMixer.update and OrbitControls.update receive a delta in seconds, not a millisecond timestamp or an elapsed time
tj-f06-timer-updateTJ-F06 · A Timer the module creates and reads is advanced with timer.update() (core Timer never advances on its own)
tj-f08-damping-updateTJ-F08 · Controls with damping or auto-rotation enabled are updated every frame (controls.update) while those features run
tj-g03-attribute-uploadsTJ-G03 · Buffer attributes written in per-frame callbacks are flagged for upload (attribute.needsUpdate = true)
tj-g05-instance-boundsTJ-G05 · Instances moved every frame keep valid bounds for culling and picking (computeBoundingSphere, or a justified frustumCulled = false)
tj-g07-attribute-item-sizeTJ-G07 · Literal vertex data matches its attribute itemSize (array length divisible by itemSize)
tj-g08-instance-uploadsTJ-G08 · setMatrixAt / setColorAt / setMorphAt in frame callbacks and event handlers are followed by the matching needsUpdate flag
tj-g13-merge-nullTJ-G13 · The result of mergeGeometries / mergeAttributes is checked for null before it replaces geometry
tj-g14-traverse-mutationTJ-G14 · traverse() callbacks do not add or remove scene objects while iterating (collect, then mutate)
tj-g15-manual-matricesTJ-G15 · Objects with matrixAutoUpdate = false recompute their local matrix after transform changes and flag matrixWorldNeedsUpdate after direct matrix edits
tj-p01-pixel-ratioTJ-P01 · The drawing buffer's pixel ratio is bounded (no raw devicePixelRatio: cost grows with its square)
tj-p02-projection-updateTJ-P02 · Changing a camera's aspect, fov, near/far, zoom or frustum is followed by updateProjectionMatrix() in the same function
tj-p03-zero-size-aspectTJ-P03 · Aspect ratios computed from element sizes guard a zero height (hidden, collapsed or detached surfaces)
tj-p04-depth-rangeTJ-P04 · Literal perspective cameras use a valid depth range (0 < near < far) and, with a selected budget, a bounded far/near ratio
tj-p06-costly-renderer-flagsTJ-P05/P06 · Costly renderer options (preserveDrawingBuffer, MSAA under post-processing, logarithmicDepthBuffer) come with a stated purpose
tj-p09-program-cache-keyTJ-P09 · onBeforeCompile code that varies the shader with closure values declares customProgramCacheKey
tj-p13-premultiplied-blendingTJ-P13 · Materials using MultiplyBlending or SubtractiveBlending set premultipliedAlpha: true (required since r178)
tj-p14-shadow-budgetTJ-P14/P15 · Shadows are enabled per object, not for a whole traversal, and shadow maps stay within the project's selected budget
tj-p17-pickingTJ-P17/P18 · Pointer picking computes coordinates from the canvas rectangle and does not raycast the whole scene recursively on every move
tj-x01-color-texture-spaceTJ-X01 · Color textures loaded with TextureLoader and used as map/emissiveMap get colorSpace = SRGBColorSpace (TextureLoader leaves NoColorSpace)
tj-x02-data-texture-srgbTJ-X02 · Data maps (normal, roughness, metalness, AO, displacement, bump, alpha…) are not tagged SRGBColorSpace
tj-x03-double-color-conversionTJ-X03 · Hex/CSS colors are not converted to linear a second time (ColorManagement already converts in setters)
tj-x04-output-conversionTJ-X04/X05 · Post-processing applies tone mapping and output color conversion exactly once (OutputPass for EffectComposer; outputColorTransform off when renderOutput is manual)
tj-x10-render-target-sizeTJ-X10 · Render targets are resized with setSize(), not by assigning width/height
tj-x12-texture-samplingTJ-X09/X12 · Textures assigned to glTF materials keep glTF orientation (flipY = false), and max anisotropy is not applied to every texture
Dependencies & toolchain 15
Packages change through the package manager, lockfiles stay consistent and the project's tools are used.
tj-t01-manual-dependency-editTJ-T01 · Dependencies change through the package manager (add/remove/update), never by editing package.json by hand
tj-t01-new-manifestTJ-T01/T02 · A new package.json comes from the package-manager initializer (or a generator), and gets its dependencies from the package manager
tj-t01-package-manager-callRecords the tool calls that run a package manager (they may rewrite package.json and lockfiles; TJ-T01/T08)
tj-t02-hand-written-manifestTJ-T02 · A new package.json without dependencies is initialized with the package manager
tj-t03-version-lookupsTJ-T03 · Registry version lookups are not a step before installing
tj-t03-version-pinsTJ-T03 · Packages are installed by name: no guessed exact version, no --save-exact without a reason
tj-t04-package-managerTJ-T04 · Dependencies are installed with the project's package manager (packageManager field, lockfile)
tj-t04-second-lockfileTJ-T04 · No second lockfile next to another package manager's lockfile
tj-t05-locked-installsTJ-T05 · CI and container builds restore dependencies from the lockfile (npm ci, --frozen-lockfile, --immutable)
tj-t06-vendored-copyTJ-T06/S07 · Project sources do not contain copied three builds or addon modules (license header of Three.js Authors)
tj-t06-vendored-downloadTJ-T06 · three builds and addons are not downloaded into the project by hand (curl/wget); the package manager or an import map provides them
tj-t08-hand-edited-lockfileTJ-T08 · Lockfiles are written by the package manager only (no hand edit, no script rewriting them)
tj-t08-stale-lockfileTJ-T08 · A turn that changes package.json dependencies also updates the lockfile next to it (once per stop)
tj-v01-install-specsTJ-V01/T03 · Installing three or @types/three at a version outside this profile's release line needs the user's confirmation
tj-v01-resolved-versionTJ-V01 · After an install that named three, the resolved three is still on this profile's release line
Completion evidence 6
A turn ends with its checks run and passing; claims of success need proof after the last edit.
tj-q01-evidence-ledgerRecords, per session, Claude's edits (file tools and shell writes) and the typecheck, lint and test runs that succeeded after them (TJ-Q01)
tj-q01-new-type-errorsTJ-Q01 · A turn adds no TypeScript errors to the changed files or their importers (language server, once per stop)
tj-q01-required-checksTJ-Q01 · A turn that changed Three.js code ends only after the configured checks (default: typecheck) succeeded on the final files (once per stop)
tj-q03-measurement-ledgerRecords that the turn ran a measurement (benchmark, profiler, performance trace) for TJ-Q03
tj-q03-performance-claimsTJ-Q03 · A turn that changed Three.js code does not claim a measured performance gain without a measurement in the turn (once per stop)
tj-turn-baselineRecords the work tree when a prompt starts: the baseline of the Three.js end-of-turn checks (TJ-T08, Q01)