{"id":"69cdce12-bf04-4082-92b9-e1d584a0aa49","entityType":"agent","slug":"clawhub-openlark-threejs-3d","name":"Comprehensive Three.js 3D graphics reference","canonicalUrl":"https://www.xpersona.co/agent/clawhub-openlark-threejs-3d","canonicalPath":"/agent/clawhub-openlark-threejs-3d","generatedAt":"2026-10-11T10:50:13.944Z","source":"CLAWHUB","claimStatus":"UNCLAIMED","verificationTier":"NONE","summary":{"evidence":{"source":"editorial-content","verified":true,"confidence":"high","updatedAt":"2026-10-11T08:03:17.540Z","emptyReason":null},"description":"Detailed Three.js 3D graphics reference covering scene setup, cameras, geometries, materials, lights, animations, controls, loaders, math utilities, and debu... Skill: Comprehensive Three.js 3D graphics reference Owner: openlark Summary: Detailed Three.js 3D graphics reference covering scene setup, cameras, geometries, materials, lights, animations, controls, loaders, math utilities, and debu... Tags: latest:1.0.0 Version history: v1.0.0 | 2026-05-22T01:32:25.889Z | user - Initial release of the threejs-3d skill. - Provides a comprehensive Three.js 3D graphics reference for","descriptionLabel":"Technical summary","evidenceSummary":"Capability contract not published. No trust telemetry is available yet. 1.1K downloads reported by the source. Last updated 10/11/2026.","installCommand":"clawhub skill install s1727wv2g20pc729snzcm4nf8183hy72:threejs-3d","sourceUrl":"https://clawhub.ai/openlark/threejs-3d","homepage":"https://clawhub.ai/openlark/skills/threejs-3d","primaryLinks":[{"label":"View on ClawHub","url":"https://clawhub.ai/openlark/threejs-3d","kind":"source"},{"label":"Homepage","url":"https://clawhub.ai/openlark/skills/threejs-3d","kind":"homepage"}],"safetyScore":84,"overallRank":62,"popularityScore":61,"trustScore":null,"claimedByName":null,"isOwner":false,"seoDescription":"Detailed Three.js 3D graphics reference covering scene setup, cameras, geometries, materials, lights, animations, controls, loaders, math utilities, and debu..."},"coverage":{"evidence":{"source":"public-profile","verified":false,"confidence":"medium","updatedAt":"2026-10-11T08:03:17.540Z","emptyReason":null},"protocols":[{"protocol":"OPENCLEW","label":"OpenClaw","status":"self-declared","notes":"Declared in the public agent profile."}],"capabilities":[],"verifiedCount":0,"selfDeclaredCount":1,"capabilityMatrix":{"rows":[{"key":"OPENCLEW","type":"protocol","support":"unknown","confidenceSource":"profile","notes":"Listed on profile"}],"flattenedTokens":"protocol:OPENCLEW|unknown|profile"}},"adoption":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-11T08:03:17.540Z","emptyReason":null},"stars":null,"forks":null,"downloads":1118,"packageName":null,"latestVersion":"1.0.0","tractionLabel":"1.1K downloads"},"release":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-11T08:03:17.528Z","emptyReason":null},"lastUpdatedAt":"2026-10-11T08:03:17.540Z","lastCrawledAt":"2026-10-11T08:03:17.528Z","lastIndexedAt":null,"nextCrawlAt":"2026-10-12T08:03:17.528Z","lastVerifiedAt":null,"highlights":[{"version":"1.0.0","createdAt":"2026-05-22T01:32:25.889Z","changelog":"- Initial release of the threejs-3d skill. - Provides a comprehensive Three.js 3D graphics reference for web apps, games, and visualizations. - Covers key topics: scene setup, cameras, geometries, materials, lights, shadows, post-processing, controls, animation, loaders, textures, node materials, math utilities, and debugging. - Includes a quick start code example and modular, concise reference files for each topic. - Designed for practical usage with relevant code patterns and gotchas highlighted.","fileCount":17,"zipByteSize":28653}]},"execution":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No published capability contract is available yet."},"installCommand":"clawhub skill install s1727wv2g20pc729snzcm4nf8183hy72:threejs-3d","setupComplexity":"low","setupSteps":["Setup complexity is LOW. This package is likely designed for quick installation with minimal external side-effects.","Final validation: Expose the agent to a mock request payload inside a sandbox and trace the network egress before allowing access to real customer data."],"contract":{"contractStatus":"missing","authModes":[],"requires":[],"forbidden":[],"supportsMcp":false,"supportsA2a":false,"supportsStreaming":false,"inputSchemaRef":null,"outputSchemaRef":null,"dataRegion":null,"contractUpdatedAt":null,"sourceUpdatedAt":null,"freshnessSeconds":null},"invocationGuide":{"preferredApi":{"snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-openlark-threejs-3d/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-openlark-threejs-3d/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-openlark-threejs-3d/trust"},"curlExamples":["curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-openlark-threejs-3d/snapshot\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-openlark-threejs-3d/contract\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-openlark-threejs-3d/trust\""],"jsonRequestTemplate":{"query":"summarize this repo","constraints":{"maxLatencyMs":2000,"protocolPreference":["OPENCLEW"]}},"jsonResponseTemplate":{"ok":true,"result":{"summary":"...","confidence":0.9},"meta":{"source":"CLAWHUB","generatedAt":"2026-10-11T10:50:13.944Z"}},"retryPolicy":{"maxAttempts":3,"backoffMs":[500,1500,3500],"retryableConditions":["HTTP_429","HTTP_503","NETWORK_TIMEOUT"]}},"endpoints":{"dossierUrl":"https://www.xpersona.co/api/v1/agents/clawhub-openlark-threejs-3d/dossier","snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-openlark-threejs-3d/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-openlark-threejs-3d/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-openlark-threejs-3d/trust"}},"reliability":{"evidence":{"source":"runtime-metrics","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No trust, reliability, or runtime telemetry is available."},"trust":{"status":"unavailable","handshakeStatus":"UNKNOWN","verificationFreshnessHours":null,"reputationScore":null,"p95LatencyMs":null,"successRate30d":null,"fallbackRate":null,"attempts30d":null,"trustUpdatedAt":null,"trustConfidence":"unknown","sourceUpdatedAt":null,"freshnessSeconds":null},"decisionGuardrails":{"doNotUseIf":["Contract metadata is missing or unavailable for deterministic execution."],"safeUseWhen":[],"riskFlags":["missing_or_unavailable_contract","trust_data_unavailable","schema_references_missing"],"operationalConfidence":"low"},"executionMetrics":{"observedLatencyMsP50":null,"observedLatencyMsP95":null,"estimatedCostUsd":null,"uptime30d":null,"rateLimitRpm":null,"rateLimitBurst":null,"lastVerifiedAt":null,"verificationSource":null},"runtimeMetrics":{"successRate":null,"avgLatencyMs":null,"avgCostUsd":null,"hallucinationRate":null,"retryRate":null,"disputeRate":null,"p50Latency":null,"p95Latency":null,"lastUpdated":null}},"benchmarks":{"evidence":{"source":"no-benchmark-data","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No benchmark suites or observed failure patterns are available."},"suites":[],"failurePatterns":[]},"artifacts":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"high","updatedAt":"2026-10-11T08:03:17.540Z","emptyReason":null},"readme":"Skill: Comprehensive Three.js 3D graphics reference\n\nOwner: openlark\n\nSummary: Detailed Three.js 3D graphics reference covering scene setup, cameras, geometries, materials, lights, animations, controls, loaders, math utilities, and debu...\n\nTags: latest:1.0.0\n\nVersion history:\n\nv1.0.0 | 2026-05-22T01:32:25.889Z | user\n\n- Initial release of the threejs-3d skill.\n- Provides a comprehensive Three.js 3D graphics reference for web apps, games, and visualizations.\n- Covers key topics: scene setup, cameras, geometries, materials, lights, shadows, post-processing, controls, animation, loaders, textures, node materials, math utilities, and debugging.\n- Includes a quick start code example and modular, concise reference files for each topic.\n- Designed for practical usage with relevant code patterns and gotchas highlighted.\n\nArchive index:\n\nArchive v1.0.0: 17 files, 28653 bytes\n\nFiles: references/animation.md (2881b), references/cameras.md (2019b), references/controls.md (4169b), references/geometries.md (3386b), references/getting-started.md (4649b), references/helpers.md (3821b), references/lights.md (3884b), references/loaders.md (4277b), references/materials.md (4707b), references/math.md (3482b), references/nodes.md (3626b), references/objects.md (4709b), references/renderers.md (6543b), references/textures.md (3127b), skill-card.md (2818b), SKILL.md (3509b), _meta.json (129b)\n\nFile v1.0.0:SKILL.md\n\n---\nname: threejs-3d\ndescription: Comprehensive Three.js 3D graphics reference. Use when building 3D web apps, games, or visualizations with Three.js. Covers: scene setup, cameras, geometries, materials, lights, shadows, post-processing, controls, animation, loaders, textures, TSL/node materials, math utils, and debugging.\n---\n\n# Three.js 3D\n\n## Quick Start\n\n```js\nimport * as THREE from 'three';\nconst scene = new THREE.Scene();\nconst camera = new THREE.PerspectiveCamera(75, innerWidth/innerHeight, 0.1, 1000);\nconst renderer = new THREE.WebGLRenderer({antialias: true});\nrenderer.setSize(innerWidth, innerHeight);\ndocument.body.appendChild(renderer.domElement);\nscene.add(new THREE.Mesh(\n  new THREE.BoxGeometry(1,1,1),\n  new THREE.MeshStandardMaterial({color: 0x44aa88})\n));\nscene.add(new THREE.DirectionalLight(0xffffff, 3));\nscene.add(new THREE.AmbientLight(0x404040, 0.5));\ncamera.position.z = 5;\nfunction animate() { requestAnimationFrame(animate); renderer.render(scene, camera); }\nanimate();\n```\n\n## Import\n```bash\nnpm install three\n```\n```js\nimport * as THREE from 'three';\nimport { OrbitControls } from 'three/addons/controls/OrbitControls.js';\nimport { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';\n```\n\n## Key Patterns\n\n### Object3D (everything extends this)\n```js\nobj.position.set(x,y,z); obj.rotation.set(x,y,z); obj.scale.set(x,y,z); // Euler rad\nobj.quaternion.set(x,y,z,w);       // No gimbal lock\nobj.lookAt(target);                // -Z → target, +Y up\nparent.add(child); parent.remove(child); parent.attach(child); // Reparent w/o visual change\nobj.getWorldPosition(v); obj.localToWorld(v); obj.worldToLocal(v);\n```\n\n### Color\n```js\nnew THREE.Color(0xff0000); new THREE.Color('#ff0000'); new THREE.Color(1,0,0);\ncolor.setHSL(hue, saturation, lightness); // 0–1\n```\n\n### Dispose (always!)\n```js\ngeometry.dispose(); material.dispose(); texture.dispose(); renderTarget.dispose();\n```\n\n## Reference Files\n\nRead the relevant file based on the task — each is concise and domain-focused:\n\n| File | Topic |\n|---|---|\n| [getting-started.md](references/getting-started.md) | Responsive design, multi-canvas, render-on-demand, fog, disposal |\n| [cameras.md](references/cameras.md) | PerspectiveCamera, OrthographicCamera, CubeCamera, gotchas |\n| [geometries.md](references/geometries.md) | All built-in primitives, BufferGeometry, merge, vertex colors |\n| [materials.md](references/materials.md) | Material selection guide, properties, transparency fixes, ShaderMaterial |\n| [lights.md](references/lights.md) | Light types, three-point lighting, environment maps, skyboxes |\n| [objects.md](references/objects.md) | Object3D API, Mesh, InstancedMesh, SkinnedMesh, LOD, scene graph |\n| [textures.md](references/textures.md) | Properties, types, color space, KTX2, memory management |\n| [loaders.md](references/loaders.md) | GLTF/OBJ/FBX, Draco, KTX2, loading manager, gotchas |\n| [animation.md](references/animation.md) | AnimationMixer/Action, skeletal, morph targets, crossfading |\n| [controls.md](references/controls.md) | OrbitControls, TransformControls, HTML labels, sprites/facades |\n| [math.md](references/math.md) | Vector/Matrix/Quat/Color/Raycaster patterns + gotchas |\n| [renderers.md](references/renderers.md) | WebGL/WebGPU setup, post-processing, shadows, render targets, GPU picking |\n| [nodes.md](references/nodes.md) | TSL node materials, compute shaders |\n| [helpers.md](references/helpers.md) | Debug helpers, scene graph dump, GLSL debugging, lil-gui patterns |\n\nFile v1.0.0:_meta.json\n\n{\n  \"ownerId\": \"kn75qrtb885pznwsjwsh18dvf1813bv0\",\n  \"slug\": \"threejs-3d\",\n  \"version\": \"1.0.0\",\n  \"publishedAt\": 1779413545889\n}\n\nFile v1.0.0:references/animation.md\n\n# Animation\n\n## AnimationMixer (the player)\n\n```js\nconst mixer = new THREE.AnimationMixer(rootObject);\n\n// In render loop:\nconst delta = clock.getDelta();\nmixer.update(delta);\n\n// Properties\nmixer.timeScale = 1;      // Speed multiplier\nmixer.time;               // Current time\n```\n\n## AnimationAction (controls playback)\n\n```js\nconst action = mixer.clipAction(animationClip);\n\n// Playback\naction.play();\naction.pause();\naction.stop();\naction.reset();\naction.halt(duration);    // Gradual stop\n\n// Properties\naction.time;              // Current time in clip\naction.timeScale = 1;     // Speed (negative = reverse)\naction.weight = 1;        // Blend weight 0–1\naction.loop = THREE.LoopRepeat | THREE.LoopOnce | THREE.LoopPingPong;\naction.repetitions = Infinity;\naction.paused = false;\naction.enabled = true;\naction.clampWhenFinished = false;    // Hold last frame\naction.zeroSlopeAtStart = true;     // Smooth start\naction.zeroSlopeAtEnd = true;       // Smooth end\naction.blendMode = THREE.NormalAnimationBlendMode | THREE.AdditiveAnimationBlendMode;\n\n// Cross-fading\naction.crossFadeTo(otherAction, duration, warp);\naction.fadeIn(duration);\naction.fadeOut(duration);\naction.startAt(startTime);\n```\n\n## Animation State Machine\n```js\nconst actions = { idle: mixer.clipAction(idleClip), walk: mixer.clipAction(walkClip) };\nlet current = actions.idle;\ncurrent.play();\n\nfunction switchTo(name, dur = 0.3) {\n  const prev = current;\n  current = actions[name];\n  current.reset().play();\n  prev.crossFadeTo(current, dur, true);\n}\n```\n\n## Skeletal Animation (from glTF)\n```js\nloader.load('character.glb', gltf => {\n  const mixer = new THREE.AnimationMixer(gltf.scene);\n  gltf.animations.forEach(c => mixer.clipAction(c).play());\n  // In loop: mixer.update(delta);\n  // Access bones: gltf.scene.traverse(c => { if(c.isSkinnedMesh) console.log(c.skeleton.bones); });\n});\n```\n\n## Morph Target Animation\n```js\nmesh.morphTargetInfluences[0] = 0.5;  // Blend first morph target 50%\n\n// Generate clips from morph sequences:\nconst clips = THREE.AnimationClip.CreateClipsFromMorphTargetSequences(meshes, fps, false);\n```\n\n## AnimationClip (keyframe data)\n```js\nconst clip = new THREE.AnimationClip(name, duration, tracks);\nclip.tracks;              // KeyframeTrack[]\nclip.duration;            // Seconds\n\n// Tracks:\nnew THREE.VectorKeyframeTrack('.position', times, values);\nnew THREE.QuaternionKeyframeTrack('.quaternion', times, values);\nnew THREE.NumberKeyframeTrack('.material.opacity', times, values);\nnew THREE.ColorKeyframeTrack('.material.color', times, values);\n```\n\n## Addons\n```js\nimport { CCDIKSolver } from 'three/addons/animation/CCDIKSolver.js';       // Inverse kinematics\nimport { AnimationClipCreator } from 'three/addons/animation/AnimationClipCreator.js';\n// CreateRotationAnimation(period, axis), CreateScaleAxisAnimation, CreateShakeAnimation, CreatePulsationAnimation\n```\n\nFile v1.0.0:references/cameras.md\n\n# Cameras\n\n## PerspectiveCamera (3D view)\n```js\nconst cam = new THREE.PerspectiveCamera(fov, aspect, near, far);\n// fov: vertical field of view in degrees (not radians!)\n// aspect: width/height\n// near/far: clipping planes\n\ncam.zoom = 1;                   // Zoom factor (>1 = zoom in)\n// After changing: cam.updateProjectionMatrix()\n\n// Stereo / split-screen view offset:\ncam.setViewOffset(fullW, fullH, x, y, w, h);\ncam.clearViewOffset();\n```\n\n## OrthographicCamera (parallel projection)\n```js\nconst aspect = innerWidth / innerHeight;\nconst size = 10;\nconst cam = new THREE.OrthographicCamera(\n  -size * aspect / 2, size * aspect / 2,  // left, right\n  size / 2, -size / 2,                     // top, bottom\n  0.1, 1000                                // near, far\n);\n\n// 2D pixel-perfect (top-left origin):\nconst cam = new THREE.OrthographicCamera(0, canvasW, 0, canvasH, -1, 1);\n```\n\n## CubeCamera (dynamic cubemap/environment)\n```js\nconst cubeRT = new THREE.WebGLCubeRenderTarget(256);\nconst cubeCam = new THREE.CubeCamera(near, far, cubeRT);\n// In loop: obj.visible=false; cubeCam.update(renderer, scene); obj.visible=true;\n// Use cubeRT.texture as envMap\n```\n\n## StereoCamera (VR, used internally by WebXR)\n```js\nconst stereo = new THREE.StereoCamera();\nstereo.eyeSep = 0.064;           // IPD in meters\nstereo.update(camera);\n// stereo.cameraL / cameraR for each eye\n```\n\n## ArrayCamera (multiple cameras, single scene)\n```js\nnew THREE.ArrayCamera([cam1, cam2]);\n```\n\n## Camera Helper\n```js\nconst helper = new THREE.CameraHelper(camera);\nscene.add(helper);\nhelper.update();\n```\n\n## Gotchas\n- **fov** is degrees (unique in Three.js where everything else uses radians)\n- `lookAt` points **-Z** axis at target — set `camera.up.set(0,0,1)` before for top-down views\n- `near` should be as large as possible to maximize depth buffer precision\n- For huge scenes with z-fighting: `renderer.logarithmicDepthBuffer = true` (WebGL only)\n- `updateProjectionMatrix()` must be called after changing fov/aspect/near/far/zoom\n\nFile v1.0.0:references/controls.md\n\n# Controls & Labels\n\n## OrbitControls (most common)\n\n```js\nimport { OrbitControls } from 'three/addons/controls/OrbitControls.js';\nconst controls = new OrbitControls(camera, renderer.domElement);\n\n// Setup\ncontrols.target.set(0, 0, 0);\ncontrols.enableDamping = true;     // Inertia (requires controls.update() in loop)\ncontrols.dampingFactor = 0.05;\n\n// Limits\ncontrols.minDistance = 1;  controls.maxDistance = Infinity;\ncontrols.minPolarAngle = 0; controls.maxPolarAngle = Math.PI;        // Vertical\ncontrols.minAzimuthAngle = -Infinity; controls.maxAzimuthAngle = Infinity; // Horizontal\ncontrols.enableZoom = true;  controls.zoomSpeed = 1.0;\ncontrols.enableRotate = true; controls.rotateSpeed = 0.5;\ncontrols.enablePan = true;    controls.panSpeed = 0.5;\ncontrols.autoRotate = false;  controls.autoRotateSpeed = 2.0;\n\n// State\ncontrols.saveState();\ncontrols.reset();    // Restore saved state\ncontrols.dispose();\n\n// Button mapping\ncontrols.mouseButtons = { LEFT: THREE.MOUSE.ROTATE, MIDDLE: THREE.MOUSE.DOLLY, RIGHT: THREE.MOUSE.PAN };\ncontrols.touches = { ONE: THREE.TOUCH.ROTATE, TWO: THREE.TOUCH.DOLLY_PAN };\n```\n\n## Other Controls\n```js\nimport { TrackballControls } from 'three/addons/controls/TrackballControls.js';    // Free rotation\nimport { MapControls } from 'three/addons/controls/MapControls.js';                // Map-style (same API as Orbit)\nimport { FlyControls } from 'three/addons/controls/FlyControls.js';                // WASD flight\nimport { FirstPersonControls } from 'three/addons/controls/FirstPersonControls.js'; // FPS\nimport { PointerLockControls } from 'three/addons/controls/PointerLockControls.js'; // FPS + pointer lock\nimport { ArcballControls } from 'three/addons/controls/ArcballControls.js';        // Advanced with gizmos\nimport { TransformControls } from 'three/addons/controls/TransformControls.js';    // Move/rotate/scale gizmo\nimport { DragControls } from 'three/addons/controls/DragControls.js';              // Drag objects on plane\n```\n\n## TransformControls (editor gizmo)\n```js\nconst gizmo = new TransformControls(camera, renderer.domElement);\ngizmo.attach(targetObject);\ngizmo.setMode('translate' | 'rotate' | 'scale');\ngizmo.space = 'world' | 'local';\nscene.add(gizmo);  // Add to scene to render\n\n// Disable orbit during transform:\ngizmo.addEventListener('dragging-changed', e => { orbitControls.enabled = !e.value; });\n```\n\n## DragControls\n```js\nconst drag = new DragControls(objects, camera, renderer.domElement);\ndrag.addEventListener('dragstart', e => { orbitControls.enabled = false; });\ndrag.addEventListener('dragend', e => { orbitControls.enabled = true; });\n```\n\n## HTML Labels Aligned to 3D\n\nPosition DOM elements over 3D points via `Vector3.project()`:\n\n```js\n// Container: <div id=\"c\"><canvas/><div id=\"labels\"/></div>\n// CSS: #c{position:relative} #labels{position:absolute;left:0;top:0}\n// Labels: #labels>div{position:absolute;left:0;top:0;white-space:nowrap;text-shadow:...}\n\nconst tempV = new THREE.Vector3();\nfunction updateLabel(obj, elem, camera, canvas) {\n  obj.getWorldPosition(tempV);\n  tempV.project(camera);\n\n  if (Math.abs(tempV.z) > 1) { elem.style.display = 'none'; return; }\n  elem.style.display = '';\n\n  const x = (tempV.x * 0.5 + 0.5) * canvas.clientWidth;\n  const y = (tempV.y * -0.5 + 0.5) * canvas.clientHeight;\n  elem.style.transform = `translate(-50%,-50%) translate(${x}px,${y}px)`;\n  elem.style.zIndex = ((-tempV.z * 0.5 + 0.5) * 100000) | 0;\n}\n```\n\n**Occlusion check:** Raycaster from camera through projected point — hide if not first hit.\n**Sphere backface cull:** `dot(relativePos, cameraToPoint) < 0.2` → hide label.\n\n## Sprites (billboards — always face camera)\n```js\nconst mat = new THREE.SpriteMaterial({ map: texture, transparent: true });\nconst sprite = new THREE.Sprite(mat);\nsprite.position.set(x, y, z);\nsprite.scale.set(w, h, 1);\nsprite.center.set(0.5, 0.5);\n```\n\n### Facade (render 3D object to sprite texture)\n```js\nconst rt = new THREE.WebGLRenderTarget(size, size);\nconst cam = new THREE.PerspectiveCamera(fov, 1, 0.1, 1000);\n// Position cam to frame object, render to rt, use rt.texture as SpriteMaterial.map\n// Saves draw calls for distant objects\n```\n\nFile v1.0.0:references/geometries.md\n\n# Geometries\n\n## Built-in Primitives\n\n| Geometry | Constructor | Default |\n|---|---|---|\n| Box | `BoxGeometry(w, h, d, segW, segH, segD)` | `(1,1,1,1,1,1)` |\n| Sphere | `SphereGeometry(r, segW, segH, phiS, phiL, thetaS, thetaL)` | `(1,32,16,0,2π,0,π)` |\n| Plane | `PlaneGeometry(w, h, segW, segH)` | `(1,1,1,1)` |\n| Circle | `CircleGeometry(r, seg, thetaS, thetaL)` | `(1,32,0,2π)` |\n| Cylinder | `CylinderGeometry(rTop, rBot, h, segR, segH, open)` | `(1,1,1,32,1,false)` |\n| Cone | `ConeGeometry(r, h, segR, segH, open)` | `(1,1,32,1,false)` |\n| Capsule | `CapsuleGeometry(r, len, capSeg, radSeg)` | `(1,1,4,8)` |\n| Ring | `RingGeometry(inR, outR, segT, segP, tS, tL)` | `(0.5,1,32,1,0,2π)` |\n| Torus | `TorusGeometry(R, r, segR, segT, arc)` | `(1,0.4,12,48,2π)` |\n| TorusKnot | `TorusKnotGeometry(R, r, segT, segR, p, q)` | `(1,0.4,64,8,2,3)` |\n| Tube | `TubeGeometry(path, seg, r, segR, closed)` | path is Curve |\n| Lathe | `LatheGeometry(points, seg, phiS, phiL)` | spin points around Y |\n| Shape | `ShapeGeometry(shapes, seg)` | 2D shapes |\n| Extrude | `ExtrudeGeometry(shapes, opts)` | depth + bevel |\n\n**Polyhedra:** `IcosahedronGeometry(r, detail)`, `DodecahedronGeometry(r, detail)`, `OctahedronGeometry(r, detail)`, `TetrahedronGeometry(r, detail)`, `PolyhedronGeometry(verts, indices, r, detail)`\n\n**Wireframe:** `EdgesGeometry(geo, thresholdAngle)` — boundary edges only | `WireframeGeometry(geo)` — all edges\n\n## BufferGeometry (custom)\n\n```js\nconst geo = new THREE.BufferGeometry();\nconst verts = new Float32Array([...]);                // Flat array [x,y,z, x,y,z, ...]\ngeo.setAttribute('position', new THREE.BufferAttribute(verts, 3));\ngeo.setIndex([0,1,2, 2,1,3, ...]);                    // Indexed triangles (optional)\ngeo.computeVertexNormals();                            // Auto normals if not provided\ngeo.computeBoundingBox(); geo.computeBoundingSphere();\n\n// Named attributes: position, normal, uv, color, uv2\n// Each is a BufferAttribute(typedArray, componentsPerVertex)\n\n// Transform geometry in place:\ngeo.translate(x,y,z); geo.scale(x,y,z);\ngeo.rotateX(r); geo.rotateY(r); geo.rotateZ(r);\ngeo.center();\ngeo.applyMatrix4(matrix);\ngeo.applyQuaternion(q);\n\n// Dynamic update:\nconst posAttr = new THREE.BufferAttribute(positions, 3);\nposAttr.setUsage(THREE.DynamicDrawUsage);\ngeo.setAttribute('position', posAttr);\n// In loop: modify positions array, then posAttr.needsUpdate = true;\n```\n\n## Optimize: Merge Geometries (1 draw call)\n```js\nimport { mergeGeometries } from 'three/addons/utils/BufferGeometryUtils.js';\nconst merged = mergeGeometries(geos, false);\n\n// For per-object colors in merged geometry:\nconst colors = new Uint8Array(3 * numVerts);\nconst rgb = [r*255, g*255, b*255];\nfor (let i=0; i<colors.length; i++) colors[i] = rgb[i%3];\ngeo.setAttribute('color', new THREE.BufferAttribute(colors, 3, true));\n// Then: new THREE.MeshBasicMaterial({ vertexColors: true })\n```\n\n## Sphere Positioning Helper\n```js\n// Efficiently place N objects on sphere surface:\nconst lonH = new THREE.Object3D(), latH = new THREE.Object3D(), posH = new THREE.Object3D();\nlonH.add(latH); latH.add(posH); posH.position.z = radius;\n// For each point: lonH.rotation.y = long; latH.rotation.x = lat; posH.updateWorldMatrix(true,false);\n// mesh.applyMatrix4(posH.matrixWorld);\n```\n\n## Disposal\n```js\ngeometry.dispose();\n// Also disposes underlying buffer attributes\n```\n\nFile v1.0.0:references/getting-started.md\n\n# Getting Started — Advanced Patterns\n\n## Minimal Scene\n\n```js\nimport * as THREE from 'three';\nconst scene = new THREE.Scene();\nconst camera = new THREE.PerspectiveCamera(75, innerWidth/innerHeight, 0.1, 1000);\nconst renderer = new THREE.WebGLRenderer({antialias: true});\nrenderer.setSize(innerWidth, innerHeight);\nrenderer.setPixelRatio(Math.min(devicePixelRatio, 2));\ndocument.body.appendChild(renderer.domElement);\n\nconst mesh = new THREE.Mesh(\n  new THREE.BoxGeometry(1,1,1),\n  new THREE.MeshStandardMaterial({color: 0x44aa88})\n);\nscene.add(mesh);\nscene.add(new THREE.DirectionalLight(0xffffff, 3));\nscene.add(new THREE.AmbientLight(0x404040, 0.5));\ncamera.position.z = 5;\n\nfunction animate() {\n  requestAnimationFrame(animate);\n  mesh.rotation.x += 0.01; mesh.rotation.y += 0.01;\n  renderer.render(scene, camera);\n}\nanimate();\n```\n\n## Installation\n```js\n// npm\nimport * as THREE from 'three';\n\n// Addons\nimport { OrbitControls } from 'three/addons/controls/OrbitControls.js';\nimport { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';\n```\n\n## Responsive Design\n```js\nfunction resizeRendererToDisplaySize(renderer) {\n  const canvas = renderer.domElement;\n  const w = canvas.clientWidth, h = canvas.clientHeight;\n  const need = canvas.width !== w || canvas.height !== h;\n  if (need) renderer.setSize(w, h, false);\n  return need;\n}\n\n// In render loop:\nif (resizeRendererToDisplaySize(renderer)) {\n  camera.aspect = canvas.clientWidth / canvas.clientHeight;\n  camera.updateProjectionMatrix();\n}\n// Pass `false` to setSize — let CSS control display size, match drawingBuffer to CSS\n```\n\n### HD-DPI\n```js\nconst pr = window.devicePixelRatio;\nconst w = Math.floor(canvas.clientWidth * pr);\nconst h = Math.floor(canvas.clientHeight * pr);\nif (canvas.width !== w || canvas.height !== h) renderer.setSize(w, h, false);\n\n// Cap resolution to prevent GPU overload:\nconst maxPixels = 3840 * 2160;\nconst scale = w*h > maxPixels ? Math.sqrt(maxPixels/(w*h)) : 1;\nrenderer.setSize(Math.floor(w*scale), Math.floor(h*scale), false);\n```\n\n## Rendering on Demand (no continuous loop)\n```js\nlet renderRequested = false;\nfunction requestRenderIfNotRequested() {\n  if (!renderRequested) { renderRequested = true; requestAnimationFrame(render); }\n}\nfunction render() { renderRequested = false; /* ... render ... */ }\nrender(); // Initial\ncontrols.addEventListener('change', requestRenderIfNotRequested);\nwindow.addEventListener('resize', requestRenderIfNotRequested);\n// For damped controls: call controls.update() inside render()\n```\n\n## Multiple Canvases (single WebGL context)\nBrowser limits WebGL contexts (~8). Use one canvas with scissor/viewport per \"virtual\" canvas:\n\n```js\nconst sceneElems = [];\nfunction addScene(elem, fn) { sceneElems.push({elem, fn}); }\n\nfunction render(time) {\n  renderer.setScissorTest(false); renderer.clear(true, true); renderer.setScissorTest(true);\n  renderer.domElement.style.transform = `translateY(${scrollY}px)`; // scroll sync\n\n  for (const {elem, fn} of sceneElems) {\n    const r = elem.getBoundingClientRect();\n    if (r.bottom < 0 || r.top > innerHeight || r.right < 0 || r.left > innerWidth) continue;\n    const y = renderer.domElement.clientHeight - r.bottom;\n    renderer.setScissor(r.left, y, r.width, r.height);\n    renderer.setViewport(r.left, y, r.width, r.height);\n    fn(time, r);\n  }\n  requestAnimationFrame(render);\n}\n```\n\n## Fog\n```js\nscene.fog = new THREE.Fog(color, near, far);       // Linear (most common)\nscene.fog = new THREE.FogExp2(color, density);     // Exponential (realistic)\nscene.background = new THREE.Color(fogColor);       // Must match fog color!\n// Per-material exclusion:\nmaterial.fog = false;  // For interiors viewed from inside\n```\n\n## Clock / Timer\n```js\nconst clock = new THREE.Clock();\n// In loop: const delta = clock.getDelta(); // seconds, auto-start\n// Timer (new): timer.update(timestamp); timer.getDelta(); timer.getElapsed();\n```\n\n## Disposal (prevent memory leaks)\n```js\ngeometry.dispose();\nmaterial.dispose();\ntexture.dispose();\nrenderTarget.dispose();\n\n// Dispose materials includes textures:\nfunction disposeMaterial(mat) {\n  for (const k of Object.keys(mat)) {\n    if (mat[k]?.isTexture) mat[k].dispose();\n  }\n  mat.dispose();\n}\n// Full scene:\nscene.traverse(obj => {\n  if (obj.geometry) obj.geometry.dispose();\n  if (obj.material) { /* disposeMaterial */ }\n});\nrenderer.dispose();\n```\n\n## Capacibilities / WebXR\n```js\nimport { WebGL } from 'three/addons/capabilities/WebGL.js';\nif (!WebGL.isWebGL2Available()) document.body.appendChild(WebGL.getErrorMessage());\n\n// VR:\nrenderer.xr.enabled = true;\nrenderer.setAnimationLoop(() => renderer.render(scene, camera));\n```\n\nFile v1.0.0:references/helpers.md\n\n# Helpers & Debugging\n\n## Visualization Helpers\n```js\nscene.add(new THREE.AxesHelper(5));                           // RGB axes\nscene.add(new THREE.GridHelper(10, 10));                      // XZ grid\nscene.add(new THREE.BoxHelper(mesh, 0xffff00));               // Wireframe bounds\nscene.add(new THREE.Box3Helper(box3, 0xffff00));              // Visualize Box3\nscene.add(new THREE.CameraHelper(camera));                    // Frustum\nscene.add(new THREE.DirectionalLightHelper(light, 0.5));\nscene.add(new THREE.PointLightHelper(light, 0.5));\nscene.add(new THREE.SpotLightHelper(light));\nscene.add(new THREE.SkeletonHelper(skinnedMesh));\nconst arrow = new THREE.ArrowHelper(dir, origin, length, color); // Debug vectors\n```\n\nAll helpers need `.update()` after their target changes.\n\n## Stats (FPS Monitor)\n```js\nimport Stats from 'three/addons/libs/stats.module.js';\nconst stats = new Stats(); stats.showPanel(0);\ndocument.body.appendChild(stats.dom);\n// In loop: stats.begin(); renderer.render(...); stats.end();\n```\n\n## Debugging\n\n### Nothing Renders? Check:\n1. Camera near/far too tight? → temporarily `near=0.001, far=1000000`\n2. Object behind camera? → add OrbitControls\n3. Needs lights? → try `MeshBasicMaterial` first\n4. NaN in transforms? → `console.log(mesh.matrixWorld)`\n5. `requestAnimationFrame` at **end** of render (errors stop loop → bugs visible)\n6. Unit mismatch: Three.js assumes 1 unit = 1 meter\n\n### On-Screen Logger (per-frame self-clearing)\n```js\nclass ClearingLogger {\n  constructor(elem) { this.elem = elem; this.lines = []; }\n  log(...args) { this.lines.push(args.join(' ')); }\n  render() { this.elem.textContent = this.lines.join('\\n'); this.lines = []; }\n}\n```\n\n### Dump Scene Graph\n```js\nfunction dumpObject(obj, lines=[], isLast=true, prefix='') {\n  const m = isLast ? '└─' : '├─';\n  lines.push(`${prefix}${m}${obj.name||'*no-name*'} [${obj.type}]`);\n  const p = prefix + (isLast?'  ':'│ ');\n  obj.children.forEach((c,i)=>dumpObject(c,lines,i===obj.children.length-1,p));\n  return lines;\n}\nconsole.log(dumpObject(scene).join('\\n'));\n```\n\n### GLSL Shader Debugging\n1. **Solid color test:** `gl_FragColor = vec4(1,0,0,1);` → if object appears, bug is in fragment shader\n2. **Visualize inputs:**\n   ```glsl\n   gl_FragColor = vec4(vNormal * 0.5 + 0.5, 1);   // Normals → RGB\n   gl_FragColor = vec4(fract(vUv), 0, 1);           // UVs → RG\n   ```\n3. **Shader Editor** extension (Chrome) for live shader editing\n4. **Simplify:** Can you draw with MeshBasicMaterial? Add shader changes incrementally.\n\n### NaN Sources\n- `SplineCurve` (2D) fed to `Vector3.set()` → z=NaN → use `CatmullRomCurve3`\n- Division by zero in custom shaders\n- Uninitialized `Vector3` components\n\n### Memory Leak Check\n```js\nsetInterval(() => {\n  console.log('geometries:', renderer.info.memory.geometries, 'textures:', renderer.info.memory.textures);\n}, 2000);\n// Always dispose: geo.dispose(), mat.dispose(), tex.dispose(), rt.dispose()\n```\n\n### Debug UI (lil-gui)\n```js\nimport GUI from 'lil-gui';\nconst gui = new GUI();\ngui.addColor(params, 'color').onChange(v => material.color.set(v));\ngui.add(params, 'speed', 0, 0.1);\ngui.add(mesh.position, 'x', -5, 5);\ngui.add(mesh, 'visible');\n```\n**lil-gui helpers for Three.js:**\n- `ColorGUIHelper` — bridges hex string ↔ THREE.Color\n- `DegRadHelper` — bridges degrees ↔ radians\n- `MinMaxGUIHelper` — enforces near<far, etc.\n\n## Renderer Capabilities\n```js\nrenderer.capabilities.maxTextureSize;         // Max texture resolution\nrenderer.capabilities.maxTextures;            // Texture units\nrenderer.capabilities.isWebGL2;               // WebGL 2.0 available\n\n// Check WebGL/WebGPU support:\nimport { WebGL } from 'three/addons/capabilities/WebGL.js';\nif (!WebGL.isWebGL2Available()) document.body.appendChild(WebGL.getErrorMessage());\n```\n\nFile v1.0.0:references/lights.md\n\n# Lights & Environments\n\n## Light Types\n\n| Light | Constructor | Shadow | Notes |\n|---|---|---|---|\n| AmbientLight | `(color, intensity)` | No | Uniform omnidirectional |\n| HemisphereLight | `(skyColor, groundColor, intensity)` | No | Sky/ground gradient |\n| DirectionalLight | `(color, intensity)` | Yes (ortho) | Parallel rays (sun) |\n| PointLight | `(color, intensity, distance, decay)` | Yes (cubemap, 6x render!) | Omnidirectional from point |\n| SpotLight | `(color, intensity, distance, angle, penumbra, decay)` | Yes (persp) | Cone with falloff |\n| RectAreaLight | `(color, intensity, width, height)` | No | Realistic soft, needs `RectAreaLightUniformsLib.init()` |\n| IESSpotLight | `(color, intensity, distance, angle, penumbra, decay)` | No | IES profile via `iesMap` texture |\n| ProjectorLight | `(color, intensity, distance)` | No | Projects texture like video projector |\n\nAll lights extend `Object3D` — use `position`/`rotation`, `lookAt` (except RectAreaLight uses rotation).\n\n## DirectionalLight (most common for shadows)\n```js\nconst light = new THREE.DirectionalLight(0xffffff, 3); // color, intensity\nlight.position.set(2, 5, 3);\nlight.target.position.set(0, 0, 0); scene.add(light.target);\nscene.add(light);\nscene.add(new THREE.DirectionalLightHelper(light, 0.5));\n```\n\n## SpotLight\n```js\nconst light = new THREE.SpotLight(0xffffff, 50, 0, Math.PI/6, 0.3, 2);\n// intensity, distance (0=infinite), angle, penumbra (0–1), decay\nlight.target.position.set(0, 0, 0); scene.add(light.target);\n```\n\n## PointLight\n```js\nnew THREE.PointLight(color, intensity, distance, decay);\n// Shadows: 6 renders per light (cubemap) — very expensive; use sparingly\n```\n\n## RectAreaLight\n```js\nimport { RectAreaLightUniformsLib } from 'three/addons/lights/RectAreaLightUniformsLib.js';\nRectAreaLightUniformsLib.init();  // Required before use\nconst light = new THREE.RectAreaLight(color, 5, 12, 4); // w, h\nlight.rotation.x = -Math.PI/2;  // Use rotation, not target\n```\n\n## Lighting Patterns\n\n**Three-point:**\n```js\nconst key = new THREE.DirectionalLight(0xffffff, 3); key.position.set(2,5,3);\nconst fill = new THREE.DirectionalLight(0x8888ff, 1); fill.position.set(-2,3,-1);\nconst rim = new THREE.DirectionalLight(0xff8888, 1.5); rim.position.set(0,2,-3);\nscene.add(new THREE.AmbientLight(0x404040, 0.5));\n```\n\n## Environment Maps (PBR)\n```js\nconst pmrem = new THREE.PMREMGenerator(renderer);\npmrem.compileCubemapShader(); // Pre-compile\n\n// From equirectangular HDR\nconst tex = new RGBELoader().load('env.hdr', t => {\n  scene.environment = pmrem.fromEquirectangular(t).texture;\n  scene.background = t; // Optional\n  t.dispose();\n});\n\n// From cubemap\nconst cubeTex = new THREE.CubeTextureLoader().load(['px.jpg',...]);\nscene.environment = pmrem.fromCubemap(cubeTex).texture;\n\n// Bake current scene as environment (for reflections)\nscene.environment = pmrem.fromScene(scene, 0.04).texture;\n```\n\n## Backgrounds\n```js\n// Solid color\nscene.background = new THREE.Color(0x222222);\n\n// Cubemap skybox\nscene.background = new THREE.CubeTextureLoader().load(['px.jpg','nx.jpg','py.jpg','ny.jpg','pz.jpg','nz.jpg']);\n\n// Equirectangular\nloader.load('env.jpg', t => { t.mapping = THREE.EquirectangularReflectionMapping; scene.background = t; });\n\n// Aspect-correct texture background (needs update in loop):\nconst canvasAsp = canvas.clientWidth / canvas.clientHeight;\nconst imgAsp = t.image?.width / t.image?.height || 1;\nconst asp = imgAsp / canvasAsp;\nt.repeat.set(asp>1 ? 1/asp : 1, asp>1 ? 1 : asp);\nt.offset.set(asp>1 ? (1-1/asp)/2 : 0, asp>1 ? 0 : (1-asp)/2);\n```\n\n## Light Helpers\n```js\nscene.add(new THREE.DirectionalLightHelper(light, 0.5));\nscene.add(new THREE.PointLightHelper(light, 0.5));\nscene.add(new THREE.SpotLightHelper(light));\nscene.add(new THREE.HemisphereLightHelper(light, 0.5));\n// For shadow camera frustum: scene.add(new THREE.CameraHelper(light.shadow.camera));\n```\n\nFile v1.0.0:references/loaders.md\n\n# Loaders\n\n## LoadingManager (track all loads)\n```js\nconst manager = new THREE.LoadingManager();\nmanager.onLoad = () => console.log('All done');\nmanager.onProgress = (url, loaded, total) => { /* progress bar */ };\nmanager.onError = (url) => console.error('Failed:', url);\n\nconst loader = new THREE.TextureLoader(manager); // Pass to any loader\n```\n\n## Texture Loaders\n```js\n// Basic\nconst tex = new THREE.TextureLoader().load('tex.jpg');\ntex.colorSpace = THREE.SRGBColorSpace;          // Color textures\ntex.colorSpace = THREE.LinearSRGBColorSpace;    // Normal/roughness/metal/AO maps\n\n// Cubemap\nnew THREE.CubeTextureLoader().setPath('skybox/')\n  .load(['px.jpg','nx.jpg','py.jpg','ny.jpg','pz.jpg','nz.jpg']);\n\n// HDR/EXR environment maps\nimport { RGBELoader } from 'three/addons/loaders/RGBELoader.js';\nimport { EXRLoader } from 'three/addons/loaders/EXRLoader.js';\nnew RGBELoader().setDataType(THREE.HalfFloatType).load('env.hdr', tex => {\n  tex.mapping = THREE.EquirectangularReflectionMapping;\n  scene.environment = tex;\n});\n```\n\n## GLTFLoader (primary model format)\n\n```js\nimport { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';\nimport { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js';\nimport { KTX2Loader } from 'three/addons/loaders/KTX2Loader.js';\n\nconst loader = new GLTFLoader();\n\n// Draco (mesh compression)\nconst dracoLoader = new DRACOLoader();\ndracoLoader.setDecoderPath('https://www.gstatic.com/draco/versioned/decoders/1.5.7/');\nloader.setDRACOLoader(dracoLoader);\n\n// KTX2 (texture compression)\nconst ktx2Loader = new KTX2Loader();\nktx2Loader.setTranscoderPath('https://unpkg.com/three@0.170.0/examples/jsm/libs/basis/');\nktx2Loader.detectSupport(renderer);\nloader.setKTX2Loader(ktx2Loader);\n\nloader.load('model.glb', gltf => {\n  scene.add(gltf.scene);\n  // gltf.animations, gltf.cameras, gltf.asset\n  // Play all: gltf.animations.forEach(c => new THREE.AnimationMixer(gltf.scene).clipAction(c).play());\n});\n```\n\n### glTF Gotchas\n- `.glb` = binary (single file, compact), `.gltf` = JSON + separate assets\n- **Artists should clear transforms** before export — scaling/rotating nodes breaks runtime positioning\n- Remove unnecessary wrapper nodes (orientation_matrix, model_correction_matrix) from export\n- For animated models with broken origins: reparent into fresh Object3D at world position\n```js\nroot.updateMatrixWorld();\nfor (const child of loadedGroup.children.slice()) {\n  const wrapper = new THREE.Object3D();\n  child.getWorldPosition(wrapper.position);\n  child.position.set(0, 0, 0);\n  wrapper.add(child);\n  scene.add(wrapper);\n}\n```\n\n## OBJ Loader\n\n```js\nimport { OBJLoader } from 'three/addons/loaders/OBJLoader.js';\nimport { MTLLoader } from 'three/addons/loaders/MTLLoader.js';\n\nnew MTLLoader().load('model.mtl', mtl => {\n  mtl.preload();\n  Object.values(mtl.materials).forEach(m => m.side = THREE.DoubleSide); // Common fix\n  const objLoader = new OBJLoader().setMaterials(mtl);\n  objLoader.load('model.obj', root => scene.add(root));\n});\n```\n\n### OBJ/MTL Common Fixes\n- `map_Bump` → `norm` in .mtl for normal maps\n- Convert TGA textures to JPG/PNG (TGA unsupported, very large)\n- Auto-frame after load: `const box = new THREE.Box3().setFromObject(root);` then adjust camera\n\n## Other Model Loaders\n```js\nimport { FBXLoader } from 'three/addons/loaders/FBXLoader.js';\nimport { STLLoader } from 'three/addons/loaders/STLLoader.js';\nimport { ColladaLoader } from 'three/addons/loaders/ColladaLoader.js';\nimport { PLYLoader } from 'three/addons/loaders/PLYLoader.js';\nimport { PCDLoader } from 'three/addons/loaders/PCDLoader.js';  // Point clouds\n```\n\n## SVG / Font / Audio\n```js\nimport { SVGLoader } from 'three/addons/loaders/SVGLoader.js';\nimport { FontLoader } from 'three/addons/loaders/FontLoader.js';\nimport { TTFLoader } from 'three/addons/loaders/TTFLoader.js';\nnew THREE.AudioLoader().load('sound.mp3', buffer => { audio.setBuffer(buffer); audio.play(); });\n```\n\n## Serialization\n```js\nnew THREE.BufferGeometryLoader().load('geo.json', geo => {});\nnew THREE.ObjectLoader().load('obj.json', obj => {});\nnew THREE.MaterialLoader().load('mat.json', mat => {});\n```\n\n## Cache\n```js\nTHREE.Cache.enabled = true;  // Default on\nTHREE.Cache.clear();         // Clear all\nTHREE.Cache.remove(key);     // Remove specific\n```\n\nFile v1.0.0:references/materials.md\n\n# Materials\n\n## Quick Selection Guide\n\n| Need | Use | Notes |\n|---|---|---|\n| Unlit / wireframe / background | `MeshBasicMaterial` | No lights, fastest |\n| Diffuse only | `MeshLambertMaterial` | Per-vertex, fast |\n| Specular highlights | `MeshPhongMaterial` | `shininess` (0–150) |\n| Toon/cel shading | `MeshToonMaterial` | Needs `gradientMap` (3-pixel texture) |\n| PBR (standard) | `MeshStandardMaterial` | `roughness` + `metalness` (0–1) |\n| PBR + clearcoat/glass/aniso | `MeshPhysicalMaterial` | Adds `clearcoat`, `transmission`, `sheen`, `iridescence`, `anisotropy` |\n| MatCap (view-dependent) | `MeshMatcapMaterial` | No lights needed, uses `matcap` texture |\n| Normals debug | `MeshNormalMaterial` | RGB = XYZ normals |\n| Depth visualization | `MeshDepthMaterial` | Near=black, far=white |\n| Shadow receiver only | `ShadowMaterial` | `opacity` controls shadow darkness |\n| Custom GLSL | `ShaderMaterial` | Full control, must declare uniforms |\n| Custom GLSL (no builtins) | `RawShaderMaterial` | Must declare ALL uniforms |\n| Particles | `PointsMaterial` | `size`, `sizeAttenuation`, `map` (sprite sheet) |\n| Lines | `LineBasicMaterial` | `linewidth` often ignored (WebGL limit) |\n| Dashed lines | `LineDashedMaterial` | Requires `geometry.computeLineDistances()` |\n| Sprites (billboards) | `SpriteMaterial` | Always faces camera |\n\n**Speed ranking:** Basic → Lambert → Phong → Standard → Physical\n\n## Key Material Properties\n\n```js\nmaterial.side = THREE.FrontSide | THREE.BackSide | THREE.DoubleSide;\nmaterial.transparent = true;     // Enable opacity/alpha\nmaterial.opacity = 0.5;\nmaterial.alphaTest = 0.5;       // Discard fragments below threshold (no depth issues)\nmaterial.blending = THREE.NormalBlending | THREE.AdditiveBlending | THREE.MultiplyBlending;\nmaterial.depthWrite = true;      // false for transparent to avoid z-fighting\nmaterial.depthTest = true;\nmaterial.wireframe = false;\nmaterial.flatShading = false;\nmaterial.fog = true;             // false for interior objects\nmaterial.clippingPlanes = [plane1, plane2];\nmaterial.clipIntersection = false;\n\n// Textures on Standard/Phong:\nmaterial.map;                     // Diffuse/albedo\nmaterial.normalMap;               // RGB normal\nmaterial.roughnessMap;            // Grayscale (Standard only)\nmaterial.metalnessMap;            // Grayscale (Standard only)\nmaterial.aoMap;                   // Ambient occlusion\nmaterial.emissiveMap;\nmaterial.bumpMap;                 // Grayscale\nmaterial.displacementMap;\nmaterial.envMap;                  // Environment reflection\nmaterial.alphaMap;\n\n// Physical material extras:\nmaterial.clearcoat;               // 0–1\nmaterial.clearcoatRoughness;      // 0–1\nmaterial.transmission;            // 0–1 (glass)\nmaterial.thickness;               // Volume thickness\nmaterial.ior;                     // Index of refraction (default 1.5)\nmaterial.sheen;                   // 0–1 (fabric)\nmaterial.iridescence;             // 0–1 (thin film)\nmaterial.anisotropy;              // 0–1 (brushed metal)\n```\n\n## Texture Color Space (critical!)\n\n```js\n// Color textures → sRGB\ntexture.colorSpace = THREE.SRGBColorSpace;\n\n// Data textures (normal, roughness, metalness, AO, displacement) → Linear\ntexture.colorSpace = THREE.LinearSRGBColorSpace;\n```\n\n## Transparency Gotchas\n\n**Problem:** Transparent objects at object level are sorted back→front, but individual triangles within an object are NOT sorted → back faces disappear.\n\n**Fix 1 — Double render (convex objects):**\n```js\n[THREE.BackSide, THREE.FrontSide].forEach(side => {\n  scene.add(new THREE.Mesh(geo, new THREE.MeshPhongMaterial({opacity:0.5, transparent:true, side})));\n});\n```\n\n**Fix 2 — alphaTest (textured objects):**\n```js\nmaterial.alphaTest = 0.5;  // Discard below threshold → no depth sorting needed\n// Best for: leaves, grass, decals, text\n```\n\n**Fix 3 — Split intersecting planes** into non-overlapping halves with shared parent Object3D.\n\n## Multi-Material (per-group)\n```js\nconst mesh = new THREE.Mesh(geometry, [mat0, mat1, mat2]);\n// geometry.groups: [{start, count, materialIndex}, ...]\n// BoxGeometry has 6 groups (one per face), Cylinder has 3, Cone has 2\n```\n\n## ShaderMaterial Pattern\n```js\nnew THREE.ShaderMaterial({\n  uniforms: { time: {value:0}, uColor: {value: new THREE.Color()} },\n  vertexShader: `varying vec2 vUv; void main() { vUv=uv; gl_Position=projectionMatrix*modelViewMatrix*vec4(position,1.0); }`,\n  fragmentShader: `uniform float time; uniform vec3 uColor; varying vec2 vUv; void main(){gl_FragColor=vec4(uColor,1.0);}`,\n});\n// builtin uniforms: projectionMatrix, modelViewMatrix, normalMatrix, cameraPosition, viewMatrix\n// builtin attributes: position, normal, uv\n```\n\nFile v1.0.0:references/math.md\n\n# Math Utilities\n\nThree.js math classes are standard. Key notes:\n\n## Common Patterns\n\n```js\n// position/rotation/scale\nobj.position.set(x, y, z);\nobj.rotation.set(x, y, z);         // Euler, radians\nobj.quaternion.set(x, y, z, w);    // Avoids gimbal lock\nobj.scale.set(x, y, z);\nobj.lookAt(target);                // Points -Z at target, +Y as up\n```\n\n## Matrix4 Composition\n```js\n// Compose/decompose transforms\nconst m = new THREE.Matrix4();\nm.compose(position, quaternion, scale);\nm.decompose(position, quaternion, scale);\nm.makePerspective(left, right, top, bottom, near, far); // for shadow cameras\nm.makeRotationFromQuaternion(q);  // Quaternion → Matrix\n```\n\n## Quaternion\n```js\nq.setFromAxisAngle(axis, angle);   // Axis-angle rotation\nq.slerp(qb, t);                    // Spherical interpolation (no gimbal lock)\nq.setFromUnitVectors(from, to);    // Rotation between two directions\nq.rotateTowards(target, step);     // Step-limited rotation\n```\n\n## Color\n```js\nnew THREE.Color(0xff0000);        // Hex\nnew THREE.Color('#ff0000');       // CSS\nnew THREE.Color('rgb(255,0,0)');  // CSS\nnew THREE.Color(1, 0, 0);         // RGB 0-1\ncolor.setHSL(h, s, l);            // h=0..1, s=0..1, l=0..1\n\n// Color space conversion (for proper PBR rendering)\ncolor.convertSRGBToLinear();\ncolor.convertLinearToSRGB();\n```\n\n## Raycaster\n\n```js\nconst raycaster = new THREE.Raycaster();\n\n// Mouse → ray:\nconst mouse = new THREE.Vector2();\nmouse.x = (e.clientX / window.innerWidth) * 2 - 1;\nmouse.y = -(e.clientY / window.innerHeight) * 2 + 1; // flip Y\nraycaster.setFromCamera(mouse, camera);\nconst hits = raycaster.intersectObjects(scene.children, true);\n\n// Each hit: { distance, point, face, faceIndex, object, uv, normal, instanceId }\n\n// Intersection with specific object types:\nraycaster.params.Points.threshold = 1;  // point size tolerance\nraycaster.params.Line.threshold = 1;    // line width tolerance\n```\n\n## Box3 / Sphere (Bounding Volumes)\n\n```js\n// Auto-compute bounding box of hierarchy\nconst box = new THREE.Box3().setFromObject(root);\nconst size = box.getSize(new THREE.Vector3());\nconst center = box.getCenter(new THREE.Vector3());\n\n// Frustum culling check\nconst frustum = new THREE.Frustum();\nfrustum.setFromProjectionMatrix(\n  new THREE.Matrix4().multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse)\n);\nfrustum.intersectsObject(obj);     // true if visible\nfrustum.containsPoint(point);\n```\n\n## MathUtils\n\n```js\nTHREE.MathUtils.clamp(v, min, max);\nTHREE.MathUtils.lerp(a, b, t);\nTHREE.MathUtils.mapLinear(x, a1, a2, b1, b2);\nTHREE.MathUtils.smoothstep(x, min, max);\nTHREE.MathUtils.degToRad(d);     // degrees → radians\nTHREE.MathUtils.radToDeg(r);     // radians → degrees\nTHREE.MathUtils.damp(x, y, lambda, dt);  // Smooth damping\nTHREE.MathUtils.randFloat(low, high);\nTHREE.MathUtils.randFloatSpread(range);\nTHREE.MathUtils.generateUUID();\nTHREE.MathUtils.DEG2RAD;         // Math.PI / 180\nTHREE.MathUtils.RAD2DEG;         // 180 / Math.PI\n```\n\n## Gotchas\n\n- **all angles are radians** except PerspectiveCamera.fov (degrees)\n- **Euler order** defaults to `'XYZ'` — change with `new Euler(x,y,z,'YXZ')` for FPS-style\n- **Color.setRGB/setHSL** takes 0–1, not 0–255\n- **Vector3.project(camera)** returns NDC: x,y in [-1,1], z in [-1,1] (not [0,1]!)\n- **lookAt** points -Z axis at target with +Y up — adjust with `camera.up.set()` before\n- **SplineCurve** is 2D — feeding into Vector3.set gives NaN on z → use CatmullRomCurve3 for 3D","readmeExcerpt":"Skill: Comprehensive Three.js 3D graphics reference Owner: openlark Summary: Detailed Three.js 3D graphics reference covering scene setup, cameras, geometries, materials, lights, animations, controls, loaders, math utilities, and debu... Tags: latest:1.0.0 Version history: v1.0.0 | 2026-05-22T01:32:25.889Z | user - Initial release of the threejs-3d skill. - Provides a comprehensive Three.js 3D graphics reference for ","codeSnippets":[],"executableExamples":[{"language":"js","snippet":"import * as THREE from 'three';\nconst scene = new THREE.Scene();\nconst camera = new THREE.PerspectiveCamera(75, innerWidth/innerHeight, 0.1, 1000);\nconst renderer = new THREE.WebGLRenderer({antialias: true});\nrenderer.setSize(innerWidth, innerHeight);\ndocument.body.appendChild(renderer.domElement);\nscene.add(new THREE.Mesh(\n  new THREE.BoxGeometry(1,1,1),\n  new THREE.MeshStandardMaterial({color: 0x44aa88})\n));\nscene.add(new THREE.DirectionalLight(0xffffff, 3));\nscene.add(new THREE.AmbientLight(0x404040, 0.5));\ncamera.position.z = 5;\nfunction animate() { requestAnimationFrame(animate); renderer.render(scene, camera); }\nanimate();"},{"language":"bash","snippet":"npm install three"},{"language":"js","snippet":"import * as THREE from 'three';\nimport { OrbitControls } from 'three/addons/controls/OrbitControls.js';\nimport { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';"},{"language":"js","snippet":"obj.position.set(x,y,z); obj.rotation.set(x,y,z); obj.scale.set(x,y,z); // Euler rad\nobj.quaternion.set(x,y,z,w);       // No gimbal lock\nobj.lookAt(target);                // -Z → target, +Y up\nparent.add(child); parent.remove(child); parent.attach(child); // Reparent w/o visual change\nobj.getWorldPosition(v); obj.localToWorld(v); obj.worldToLocal(v);"},{"language":"js","snippet":"new THREE.Color(0xff0000); new THREE.Color('#ff0000'); new THREE.Color(1,0,0);\ncolor.setHSL(hue, saturation, lightness); // 0–1"},{"language":"js","snippet":"geometry.dispose(); material.dispose(); texture.dispose(); renderTarget.dispose();"}],"parameters":null,"dependencies":[],"permissions":[],"extractedFiles":[{"path":"SKILL.md","content":"---\nname: threejs-3d\ndescription: Comprehensive Three.js 3D graphics reference. Use when building 3D web apps, games, or visualizations with Three.js. Covers: scene setup, cameras, geometries, materials, lights, shadows, post-processing, controls, animation, loaders, textures, TSL/node materials, math utils, and debugging.\n---\n\n# Three.js 3D\n\n## Quick Start\n\n```js\nimport * as THREE from 'three';\nconst scene = new THREE.Scene();\nconst camera = new THREE.PerspectiveCamera(75, innerWidth/innerHeight, 0.1, 1000);\nconst renderer = new THREE.WebGLRenderer({antialias: true});\nrenderer.setSize(innerWidth, innerHeight);\ndocument.body.appendChild(renderer.domElement);\nscene.add(new THREE.Mesh(\n  new THREE.BoxGeometry(1,1,1),\n  new THREE.MeshStandardMaterial({color: 0x44aa88})\n));\nscene.add(new THREE.DirectionalLight(0xffffff, 3));\nscene.add(new THREE.AmbientLight(0x404040, 0.5));\ncamera.position.z = 5;\nfunction animate() { requestAnimationFrame(animate); renderer.render(scene, camera); }\nanimate();\n```\n\n## Import\n```bash\nnpm install three\n```\n```js\nimport * as THREE from 'three';\nimport { OrbitControls } from 'three/addons/controls/OrbitControls.js';\nimport { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';\n```\n\n## Key Patterns\n\n### Object3D (everything extends this)\n```js\nobj.position.set(x,y,z); obj.rotation.set(x,y,z); obj.scale.set(x,y,z); // Euler rad\nobj.quaternion.set(x,y,z,w);       // No gimbal lock\nobj.lookAt(target);                // -Z → target, +Y up\nparent.add(child); parent.remove(child); parent.attach(child); // Reparent w/o visual change\nobj.getWorldPosition(v); obj.localToWorld(v); obj.worldToLocal(v);\n```\n\n### Color\n```js\nnew THREE.Color(0xff0000); new THREE.Color('#ff0000'); new THREE.Color(1,0,0);\ncolor.setHSL(hue, saturation, lightness); // 0–1\n```\n\n### Dispose (always!)\n```js\ngeometry.dispose(); material.dispose(); texture.dispose(); renderTarget.dispose();\n```\n\n## Reference Files\n\nRead the relevant file based on the task — each is concise and domain-focused:\n\n| File | Topic |\n|---|---|\n| [getting-started.md](references/getting-started.md) | Responsive design, multi-canvas, render-on-demand, fog, disposal |\n| [cameras.md](references/cameras.md) | PerspectiveCamera, OrthographicCamera, CubeCamera, gotchas |\n| [geometries.md](references/geometries.md) | All built-in primitives, BufferGeometry, merge, vertex colors |\n| [materials.md](references/materials.md) | Material selection guide, properties, transparency fixes, ShaderMaterial |\n| [lights.md](references/lights.md) | Light types, three-point lighting, environment maps, skyboxes |\n| [objects.md](references/objects.md) | Object3D API, Mesh, InstancedMesh, SkinnedMesh, LOD, scene graph |\n| [textures.md](references/textures.md) | Properties, types, color space, KTX2, memory management |\n| [loaders.md](references/loaders.md) | GLTF/OBJ/FBX, Draco, KTX2, loading manager, gotchas |\n| [animation.md](references/animation.md) | AnimationMixer/Action, skeletal, morph targets, crossfadi"},{"path":"_meta.json","content":"{\n  \"ownerId\": \"kn75qrtb885pznwsjwsh18dvf1813bv0\",\n  \"slug\": \"threejs-3d\",\n  \"version\": \"1.0.0\",\n  \"publishedAt\": 1779413545889\n}"},{"path":"references/animation.md","content":"# Animation\n\n## AnimationMixer (the player)\n\n```js\nconst mixer = new THREE.AnimationMixer(rootObject);\n\n// In render loop:\nconst delta = clock.getDelta();\nmixer.update(delta);\n\n// Properties\nmixer.timeScale = 1;      // Speed multiplier\nmixer.time;               // Current time\n```\n\n## AnimationAction (controls playback)\n\n```js\nconst action = mixer.clipAction(animationClip);\n\n// Playback\naction.play();\naction.pause();\naction.stop();\naction.reset();\naction.halt(duration);    // Gradual stop\n\n// Properties\naction.time;              // Current time in clip\naction.timeScale = 1;     // Speed (negative = reverse)\naction.weight = 1;        // Blend weight 0–1\naction.loop = THREE.LoopRepeat | THREE.LoopOnce | THREE.LoopPingPong;\naction.repetitions = Infinity;\naction.paused = false;\naction.enabled = true;\naction.clampWhenFinished = false;    // Hold last frame\naction.zeroSlopeAtStart = true;     // Smooth start\naction.zeroSlopeAtEnd = true;       // Smooth end\naction.blendMode = THREE.NormalAnimationBlendMode | THREE.AdditiveAnimationBlendMode;\n\n// Cross-fading\naction.crossFadeTo(otherAction, duration, warp);\naction.fadeIn(duration);\naction.fadeOut(duration);\naction.startAt(startTime);\n```\n\n## Animation State Machine\n```js\nconst actions = { idle: mixer.clipAction(idleClip), walk: mixer.clipAction(walkClip) };\nlet current = actions.idle;\ncurrent.play();\n\nfunction switchTo(name, dur = 0.3) {\n  const prev = current;\n  current = actions[name];\n  current.reset().play();\n  prev.crossFadeTo(current, dur, true);\n}\n```\n\n## Skeletal Animation (from glTF)\n```js\nloader.load('character.glb', gltf => {\n  const mixer = new THREE.AnimationMixer(gltf.scene);\n  gltf.animations.forEach(c => mixer.clipAction(c).play());\n  // In loop: mixer.update(delta);\n  // Access bones: gltf.scene.traverse(c => { if(c.isSkinnedMesh) console.log(c.skeleton.bones); });\n});\n```\n\n## Morph Target Animation\n```js\nmesh.morphTargetInfluences[0] = 0.5;  // Blend first morph target 50%\n\n// Generate clips from morph sequences:\nconst clips = THREE.AnimationClip.CreateClipsFromMorphTargetSequences(meshes, fps, false);\n```\n\n## AnimationClip (keyframe data)\n```js\nconst clip = new THREE.AnimationClip(name, duration, tracks);\nclip.tracks;              // KeyframeTrack[]\nclip.duration;            // Seconds\n\n// Tracks:\nnew THREE.VectorKeyframeTrack('.position', times, values);\nnew THREE.QuaternionKeyframeTrack('.quaternion', times, values);\nnew THREE.NumberKeyframeTrack('.material.opacity', times, values);\nnew THREE.ColorKeyframeTrack('.material.color', times, values);\n```\n\n## Addons\n```js\nimport { CCDIKSolver } from 'three/addons/animation/CCDIKSolver.js';       // Inverse kinematics\nimport { AnimationClipCreator } from 'three/addons/animation/AnimationClipCreator.js';\n// CreateRotationAnimation(period, axis), CreateScaleAxisAnimation, CreateShakeAnimation, CreatePulsationAnimation\n```"},{"path":"references/cameras.md","content":"# Cameras\n\n## PerspectiveCamera (3D view)\n```js\nconst cam = new THREE.PerspectiveCamera(fov, aspect, near, far);\n// fov: vertical field of view in degrees (not radians!)\n// aspect: width/height\n// near/far: clipping planes\n\ncam.zoom = 1;                   // Zoom factor (>1 = zoom in)\n// After changing: cam.updateProjectionMatrix()\n\n// Stereo / split-screen view offset:\ncam.setViewOffset(fullW, fullH, x, y, w, h);\ncam.clearViewOffset();\n```\n\n## OrthographicCamera (parallel projection)\n```js\nconst aspect = innerWidth / innerHeight;\nconst size = 10;\nconst cam = new THREE.OrthographicCamera(\n  -size * aspect / 2, size * aspect / 2,  // left, right\n  size / 2, -size / 2,                     // top, bottom\n  0.1, 1000                                // near, far\n);\n\n// 2D pixel-perfect (top-left origin):\nconst cam = new THREE.OrthographicCamera(0, canvasW, 0, canvasH, -1, 1);\n```\n\n## CubeCamera (dynamic cubemap/environment)\n```js\nconst cubeRT = new THREE.WebGLCubeRenderTarget(256);\nconst cubeCam = new THREE.CubeCamera(near, far, cubeRT);\n// In loop: obj.visible=false; cubeCam.update(renderer, scene); obj.visible=true;\n// Use cubeRT.texture as envMap\n```\n\n## StereoCamera (VR, used internally by WebXR)\n```js\nconst stereo = new THREE.StereoCamera();\nstereo.eyeSep = 0.064;           // IPD in meters\nstereo.update(camera);\n// stereo.cameraL / cameraR for each eye\n```\n\n## ArrayCamera (multiple cameras, single scene)\n```js\nnew THREE.ArrayCamera([cam1, cam2]);\n```\n\n## Camera Helper\n```js\nconst helper = new THREE.CameraHelper(camera);\nscene.add(helper);\nhelper.update();\n```\n\n## Gotchas\n- **fov** is degrees (unique in Three.js where everything else uses radians)\n- `lookAt` points **-Z** axis at target — set `camera.up.set(0,0,1)` before for top-down views\n- `near` should be as large as possible to maximize depth buffer precision\n- For huge scenes with z-fighting: `renderer.logarithmicDepthBuffer = true` (WebGL only)\n- `updateProjectionMatrix()` must be called after changing fov/aspect/near/far/zoom"},{"path":"references/controls.md","content":"# Controls & Labels\n\n## OrbitControls (most common)\n\n```js\nimport { OrbitControls } from 'three/addons/controls/OrbitControls.js';\nconst controls = new OrbitControls(camera, renderer.domElement);\n\n// Setup\ncontrols.target.set(0, 0, 0);\ncontrols.enableDamping = true;     // Inertia (requires controls.update() in loop)\ncontrols.dampingFactor = 0.05;\n\n// Limits\ncontrols.minDistance = 1;  controls.maxDistance = Infinity;\ncontrols.minPolarAngle = 0; controls.maxPolarAngle = Math.PI;        // Vertical\ncontrols.minAzimuthAngle = -Infinity; controls.maxAzimuthAngle = Infinity; // Horizontal\ncontrols.enableZoom = true;  controls.zoomSpeed = 1.0;\ncontrols.enableRotate = true; controls.rotateSpeed = 0.5;\ncontrols.enablePan = true;    controls.panSpeed = 0.5;\ncontrols.autoRotate = false;  controls.autoRotateSpeed = 2.0;\n\n// State\ncontrols.saveState();\ncontrols.reset();    // Restore saved state\ncontrols.dispose();\n\n// Button mapping\ncontrols.mouseButtons = { LEFT: THREE.MOUSE.ROTATE, MIDDLE: THREE.MOUSE.DOLLY, RIGHT: THREE.MOUSE.PAN };\ncontrols.touches = { ONE: THREE.TOUCH.ROTATE, TWO: THREE.TOUCH.DOLLY_PAN };\n```\n\n## Other Controls\n```js\nimport { TrackballControls } from 'three/addons/controls/TrackballControls.js';    // Free rotation\nimport { MapControls } from 'three/addons/controls/MapControls.js';                // Map-style (same API as Orbit)\nimport { FlyControls } from 'three/addons/controls/FlyControls.js';                // WASD flight\nimport { FirstPersonControls } from 'three/addons/controls/FirstPersonControls.js'; // FPS\nimport { PointerLockControls } from 'three/addons/controls/PointerLockControls.js'; // FPS + pointer lock\nimport { ArcballControls } from 'three/addons/controls/ArcballControls.js';        // Advanced with gizmos\nimport { TransformControls } from 'three/addons/controls/TransformControls.js';    // Move/rotate/scale gizmo\nimport { DragControls } from 'three/addons/controls/DragControls.js';              // Drag objects on plane\n```\n\n## TransformControls (editor gizmo)\n```js\nconst gizmo = new TransformControls(camera, renderer.domElement);\ngizmo.attach(targetObject);\ngizmo.setMode('translate' | 'rotate' | 'scale');\ngizmo.space = 'world' | 'local';\nscene.add(gizmo);  // Add to scene to render\n\n// Disable orbit during transform:\ngizmo.addEventListener('dragging-changed', e => { orbitControls.enabled = !e.value; });\n```\n\n## DragControls\n```js\nconst drag = new DragControls(objects, camera, renderer.domElement);\ndrag.addEventListener('dragstart', e => { orbitControls.enabled = false; });\ndrag.addEventListener('dragend', e => { orbitControls.enabled = true; });\n```\n\n## HTML Labels Aligned to 3D\n\nPosition DOM elements over 3D points via `Vector3.project()`:\n\n```js\n// Container: <div id=\"c\"><canvas/><div id=\"labels\"/></div>\n// CSS: #c{position:relative} #labels{position:absolute;left:0;top:0}\n// Labels: #labels>div{position:absolute;left:0;top:0;white-space:nowrap;text-shadow:...}\n\nconst tempV = new THREE.Vector3();\nfunction updateLabe"}],"languages":[],"docsSourceLabel":"CLAWHUB","editorialOverview":"Detailed Three.js 3D graphics reference covering scene setup, cameras, geometries, materials, lights, animations, controls, loaders, math utilities, and debu... Skill: Comprehensive Three.js 3D graphics reference Owner: openlark Summary: Detailed Three.js 3D graphics reference covering scene setup, cameras, geometries, materials, lights, animations, controls, loaders, math utilities, and debu... Tags: latest:1.0.0 Version history: v1.0.0 | 2026-05-22T01:32:25.889Z | user - Initial release of the threejs-3d skill. - Provides a comprehensive Three.js 3D graphics reference for","editorialQuality":{"score":100,"threshold":65,"status":"ready","wordCount":825,"uniquenessScore":60,"reasons":[]}},"media":{"evidence":{"source":"no-media","verified":false,"confidence":"low","updatedAt":"2026-10-11T08:03:17.540Z","emptyReason":"No screenshots, media assets, or demo links are available."},"primaryImageUrl":null,"mediaAssetCount":0,"assets":[],"demoUrl":null},"ownerResources":{"evidence":{"source":"unclaimed","verified":false,"confidence":"low","updatedAt":"2026-10-11T08:03:17.540Z","emptyReason":"This page has not been claimed by the agent owner."},"hasCustomPage":false,"customPageUpdatedAt":null,"customLinks":[],"structuredLinks":{"docsUrl":null,"demoUrl":null,"supportUrl":null,"pricingUrl":null,"statusUrl":null},"customPage":null},"relatedAgents":{"evidence":{"source":"protocol-neighbors","verified":false,"confidence":"medium","updatedAt":"2026-10-11T10:50:13.944Z","emptyReason":null},"items":[{"id":"8ebccd8e-3863-4187-8355-c3f14e1f9edf","entityType":"agent","canonicalPath":"/agent/iofficeai-aionui","slug":"iofficeai-aionui","name":"AionUi","description":"Free, local, open-source 24/7 Cowork app and OpenClaw for Gemini CLI, Claude Code, Codex, OpenCode, Qwen Code, Goose CLI, Auggie, and more | 🌟 Star if you like it!","url":"https://github.com/iOfficeAI/AionUi","homepage":"https://www.aionui.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-10-09T19:11:12.944Z","createdAt":"2026-02-25T03:38:16.584Z","downloads":null},{"id":"b917f68a-ebff-438e-84f8-3f4b2494c0bc","entityType":"agent","canonicalPath":"/agent/activepieces-activepieces","slug":"activepieces-activepieces","name":"activepieces","description":"AI Agents & MCPs & AI Workflow Automation • (~400 MCP servers for AI agents) • AI Automation / AI Agent with MCPs • AI Workflows & AI Agents • MCPs for AI Agents","url":"https://github.com/activepieces/activepieces","homepage":"https://www.activepieces.com","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-15T02:22:12.426Z","createdAt":"2026-02-25T03:38:12.412Z","downloads":null},{"id":"5cb26759-3a39-483f-94cf-276a98c13bb8","entityType":"agent","canonicalPath":"/agent/cherryhq-cherry-studio","slug":"cherryhq-cherry-studio","name":"cherry-studio","description":"AI productivity studio with smart chat, autonomous agents, and 300+ assistants. Unified access to frontier LLMs","url":"https://github.com/CherryHQ/cherry-studio","homepage":"https://cherry-ai.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-11T14:38:40.986Z","createdAt":"2026-02-25T03:38:19.379Z","downloads":null},{"id":"6f6582d0-5d76-4f0f-b81d-86520247950b","entityType":"agent","canonicalPath":"/agent/copilotkit-copilotkit","slug":"copilotkit-copilotkit","name":"CopilotKit","description":"The Frontend for Agents & Generative UI. React + Angular","url":"https://github.com/CopilotKit/CopilotKit","homepage":"https://docs.copilotkit.ai","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-03-25T09:50:57.846Z","createdAt":"2026-02-25T03:39:14.617Z","downloads":null}],"links":{"hub":"/agent","source":"/agent/source/clawhub","protocols":[{"label":"OpenClaw","href":"/agent/protocol/openclew"}]}}}