--- name: threejs-skills description: "Three.js Skills workflow skill. Use this skill when the user needs Create 3D scenes, interactive experiences, and visual effects using Three.js. Use when user requests 3D graphics, WebGL experiences, 3D visualizations, animations, or interactive 3D elements and the operator should preserve the upstream workflow, copied support files, and provenance before merging or handing off." version: "0.0.1" category: frontend tags: ["threejs-skills", "create", "scenes", "interactive", "experiences", "and", "visual", "effects"] complexity: advanced risk: caution tools: ["codex-cli", "claude-code", "cursor", "gemini-cli", "opencode"] source: community author: "sickn33" date_added: "2026-04-15" date_updated: "2026-04-25" --- # Three.js Skills ## Overview This public intake copy packages `plugins/antigravity-awesome-skills-claude/skills/threejs-skills` from `https://github.com/sickn33/antigravity-awesome-skills` into the native Omni Skills editorial shape without hiding its origin. Use it when the operator needs the upstream workflow, support files, and repository context to stay intact while the public validator and private enhancer continue their normal downstream flow. This intake keeps the copied upstream files intact and uses the `external_source` block in `metadata.json` plus `ORIGIN.md` as the provenance anchor for review. # Three.js Skills Systematically create high-quality 3D scenes and interactive experiences using Three.js best practices. Imported source sections that did not map cleanly to the public headings are still preserved below or in the support files. Notable imported sections: Common Patterns, Advanced Techniques, Modern Three.js Practices (r183), Limitations. ## When to Use This Skill Use this section as the trigger filter. It should make the activation boundary explicit before the operator loads files, runs commands, or opens a pull request. - Requests 3D visualizations or graphics ("create a 3D model", "show in 3D") - Wants interactive 3D experiences ("rotating cube", "explorable scene") - Needs WebGL or canvas-based rendering - Asks for animations, particles, or visual effects - Mentions Three.js, WebGL, or 3D rendering - Wants to visualize data in 3D space ## Operating Table | Situation | Start here | Why it matters | | --- | --- | --- | | First-time use | `metadata.json` | Confirms repository, branch, commit, and imported path through the `external_source` block before touching the copied workflow | | Provenance review | `ORIGIN.md` | Gives reviewers a plain-language audit trail for the imported source | | Workflow execution | `SKILL.md` | Starts with the smallest copied file that materially changes execution | | Supporting context | `SKILL.md` | Adds the next most relevant copied source file without loading the entire package | | Handoff decision | `## Related Skills` | Helps the operator switch to a stronger native skill when the task drifts | ## Workflow This workflow is intentionally editorial and operational at the same time. It keeps the imported source useful to the operator while still satisfying the public intake standards that feed the downstream enhancer flow. 1. What objects need to be rendered 2. Camera position and field of view 3. Lighting setup required 4. Interaction model (static, rotating, user-controlled) 5. BoxGeometry - cubes, rectangular prisms 6. SphereGeometry - spheres, planets 7. CylinderGeometry - cylinders, tubes ### Imported Workflow Notes #### Imported: Core Setup Pattern ### 1. Essential Three.js Imports Use ES module import maps for modern Three.js (r183+): ```html ``` For production with npm/vite/webpack: ```javascript import * as THREE from "three"; import { OrbitControls } from "three/addons/controls/OrbitControls.js"; ``` ### 2. Scene Initialization Every Three.js artifact needs these core components: ```javascript // Scene - contains all 3D objects const scene = new THREE.Scene(); // Camera - defines viewing perspective const camera = new THREE.PerspectiveCamera( 75, // Field of view window.innerWidth / window.innerHeight, // Aspect ratio 0.1, // Near clipping plane 1000, // Far clipping plane ); camera.position.z = 5; // Renderer - draws the scene const renderer = new THREE.WebGLRenderer({ antialias: true }); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement); ``` ### 3. Animation Loop Use `renderer.setAnimationLoop()` (preferred) or `requestAnimationFrame`: ```javascript // Preferred: setAnimationLoop (handles WebXR compatibility) renderer.setAnimationLoop(() => { mesh.rotation.x += 0.01; mesh.rotation.y += 0.01; renderer.render(scene, camera); }); // Alternative: manual requestAnimationFrame function animate() { requestAnimationFrame(animate); mesh.rotation.x += 0.01; mesh.rotation.y += 0.01; renderer.render(scene, camera); } animate(); ``` #### Imported: Systematic Development Process ### 1. Define the Scene Start by identifying: - **What objects** need to be rendered - **Camera position** and field of view - **Lighting setup** required - **Interaction model** (static, rotating, user-controlled) ### 2. Build Geometry Choose appropriate geometry types: **Basic Shapes:** - `BoxGeometry` - cubes, rectangular prisms - `SphereGeometry` - spheres, planets - `CylinderGeometry` - cylinders, tubes - `PlaneGeometry` - flat surfaces, ground planes - `TorusGeometry` - donuts, rings **CapsuleGeometry** is available (stable since r142): ```javascript new THREE.CapsuleGeometry(0.5, 1, 4, 8); // radius, length, capSegments, radialSegments ``` ### 3. Apply Materials Choose materials based on visual needs: **Common Materials:** - `MeshBasicMaterial` - unlit, flat colors (no lighting needed) - `MeshStandardMaterial` - physically-based, realistic (needs lighting) - `MeshPhongMaterial` - shiny surfaces with specular highlights - `MeshLambertMaterial` - matte surfaces, diffuse reflection ```javascript const material = new THREE.MeshStandardMaterial({ color: 0x00ff00, metalness: 0.5, roughness: 0.5, }); ``` ### 4. Add Lighting **If using lit materials** (Standard, Phong, Lambert), add lights: ```javascript // Ambient light - general illumination const ambientLight = new THREE.AmbientLight(0xffffff, 0.5); scene.add(ambientLight); // Directional light - like sunlight const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8); directionalLight.position.set(5, 5, 5); scene.add(directionalLight); ``` **Skip lighting** if using `MeshBasicMaterial` - it's unlit by design. ### 5. Handle Responsiveness Always add window resize handling: ```javascript window.addEventListener("resize", () => { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); }); ``` #### Imported: Example Workflow User: "Create an interactive 3D sphere that responds to mouse movement" 1. **Setup**: Import Three.js, create scene/camera/renderer 2. **Geometry**: Create `SphereGeometry(1, 32, 32)` for smooth sphere 3. **Material**: Use `MeshStandardMaterial` for realistic look 4. **Lighting**: Add ambient + directional lights 5. **Interaction**: Track mouse position, update camera 6. **Animation**: Rotate sphere, render continuously 7. **Responsive**: Add window resize handler 8. **Result**: Smooth, interactive 3D sphere ✓ #### Imported: Summary Three.js artifacts require systematic setup: 1. Import Three.js via import maps or build tools 2. Initialize scene, camera, renderer 3. Create geometry + material = mesh 4. Add lighting if using lit materials 5. Implement animation loop (prefer `setAnimationLoop`) 6. Handle window resize 7. Set `renderer.outputColorSpace = THREE.SRGBColorSpace` Follow these patterns for reliable, performant 3D experiences. #### Imported: Common Patterns ### Rotating Object ```javascript function animate() { requestAnimationFrame(animate); mesh.rotation.x += 0.01; mesh.rotation.y += 0.01; renderer.render(scene, camera); } ``` ### OrbitControls With import maps or build tools, OrbitControls works directly: ```javascript import { OrbitControls } from "three/addons/controls/OrbitControls.js"; const controls = new OrbitControls(camera, renderer.domElement); controls.enableDamping = true; // Update in animation loop renderer.setAnimationLoop(() => { controls.update(); renderer.render(scene, camera); }); ``` ### Custom Camera Controls (Alternative) For lightweight custom controls without importing OrbitControls: ```javascript let isDragging = false; let previousMousePosition = { x: 0, y: 0 }; renderer.domElement.addEventListener("mousedown", () => { isDragging = true; }); renderer.domElement.addEventListener("mouseup", () => { isDragging = false; }); renderer.domElement.addEventListener("mousemove", (event) => { if (isDragging) { const deltaX = event.clientX - previousMousePosition.x; const deltaY = event.clientY - previousMousePosition.y; // Rotate camera around scene const rotationSpeed = 0.005; camera.position.x += deltaX * rotationSpeed; camera.position.y -= deltaY * rotationSpeed; camera.lookAt(scene.position); } previousMousePosition = { x: event.clientX, y: event.clientY }; }); // Zoom with mouse wheel renderer.domElement.addEventListener("wheel", (event) => { event.preventDefault(); camera.position.z += event.deltaY * 0.01; camera.position.z = Math.max(2, Math.min(20, camera.position.z)); // Clamp }); ``` ### Raycasting for Object Selection Detect mouse clicks and hovers on 3D objects: ```javascript const raycaster = new THREE.Raycaster(); const mouse = new THREE.Vector2(); const clickableObjects = []; // Array of meshes that can be clicked // Update mouse position window.addEventListener("mousemove", (event) => { mouse.x = (event.clientX / window.innerWidth) * 2 - 1; mouse.y = -(event.clientY / window.innerHeight) * 2 + 1; }); // Detect clicks window.addEventListener("click", () => { raycaster.setFromCamera(mouse, camera); const intersects = raycaster.intersectObjects(clickableObjects); if (intersects.length > 0) { const clickedObject = intersects[0].object; // Handle click - change color, scale, etc. clickedObject.material.color.set(0xff0000); } }); // Hover effect in animation loop function animate() { requestAnimationFrame(animate); raycaster.setFromCamera(mouse, camera); const intersects = raycaster.intersectObjects(clickableObjects); // Reset all objects clickableObjects.forEach((obj) => { obj.scale.set(1, 1, 1); }); // Highlight hovered object if (intersects.length > 0) { intersects[0].object.scale.set(1.2, 1.2, 1.2); document.body.style.cursor = "pointer"; } else { document.body.style.cursor = "default"; } renderer.render(scene, camera); } ``` ### Particle System ```javascript const particlesGeometry = new THREE.BufferGeometry(); const particlesCount = 1000; const posArray = new Float32Array(particlesCount * 3); for (let i = 0; i < particlesCount * 3; i++) { posArray[i] = (Math.random() - 0.5) * 10; } particlesGeometry.setAttribute( "position", new THREE.BufferAttribute(posArray, 3), ); const particlesMaterial = new THREE.PointsMaterial({ size: 0.02, color: 0xffffff, }); const particlesMesh = new THREE.Points(particlesGeometry, particlesMaterial); scene.add(particlesMesh); ``` ### User Interaction (Mouse Movement) ```javascript let mouseX = 0; let mouseY = 0; document.addEventListener("mousemove", (event) => { mouseX = (event.clientX / window.innerWidth) * 2 - 1; mouseY = -(event.clientY / window.innerHeight) * 2 + 1; }); function animate() { requestAnimationFrame(animate); camera.position.x = mouseX * 2; camera.position.y = mouseY * 2; camera.lookAt(scene.position); renderer.render(scene, camera); } ``` ### Loading Textures ```javascript const textureLoader = new THREE.TextureLoader(); const texture = textureLoader.load("texture-url.jpg"); const material = new THREE.MeshStandardMaterial({ map: texture, }); ``` ## Examples ### Example 1: Ask for the upstream workflow directly ```text Use @threejs-skills to handle . Start from the copied upstream workflow, load only the files that change the outcome, and keep provenance visible in the answer. ``` **Explanation:** This is the safest starting point when the operator needs the imported workflow, but not the entire repository. ### Example 2: Ask for a provenance-grounded review ```text Review @threejs-skills against metadata.json and ORIGIN.md, then explain which copied upstream files you would load first and why. ``` **Explanation:** Use this before review or troubleshooting when you need a precise, auditable explanation of origin and file selection. ### Example 3: Narrow the copied support files before execution ```text Use @threejs-skills for . Load only the copied references, examples, or scripts that change the outcome, and name the files explicitly before proceeding. ``` **Explanation:** This keeps the skill aligned with progressive disclosure instead of loading the whole copied package by default. ### Example 4: Build a reviewer packet ```text Review @threejs-skills using the copied upstream files plus provenance, then summarize any gaps before merge. ``` **Explanation:** This is useful when the PR is waiting for human review and you want a repeatable audit packet. ## Best Practices Treat the generated public skill as a reviewable packaging layer around the upstream repository. The goal is to keep provenance explicit and load only the copied source material that materially improves execution. - Reuse geometries and materials when creating multiple similar objects - Use BufferGeometry for custom shapes (more efficient) - Limit particle counts to maintain 60fps (start with 1000-5000) - Dispose of resources when removing objects: - Always set antialias: true on renderer for smooth edges - Use appropriate camera FOV (45-75 degrees typical) - Position lights thoughtfully - avoid overlapping multiple bright lights ### Imported Operating Notes #### Imported: Best Practices ### Performance - **Reuse geometries and materials** when creating multiple similar objects - **Use `BufferGeometry`** for custom shapes (more efficient) - **Limit particle counts** to maintain 60fps (start with 1000-5000) - **Dispose of resources** when removing objects: ```javascript geometry.dispose(); material.dispose(); texture.dispose(); ``` ### Visual Quality - Always set `antialias: true` on renderer for smooth edges - Use appropriate camera FOV (45-75 degrees typical) - Position lights thoughtfully - avoid overlapping multiple bright lights - Add ambient + directional lighting for realistic scenes ### Code Organization - Initialize scene, camera, renderer at the top - Group related objects (e.g., all particles in one group) - Keep animation logic in the animate function - Separate object creation into functions for complex scenes ### Common Pitfalls to Avoid - ❌ Using `outputEncoding` instead of `outputColorSpace` (renamed in r152) - ❌ Forgetting to add objects to scene with `scene.add()` - ❌ Using lit materials without adding lights - ❌ Not handling window resize - ❌ Forgetting to call `renderer.render()` in animation loop - ❌ Using `THREE.Clock` without considering `THREE.Timer` (recommended in r183) ## Troubleshooting ### Problem: The operator skipped the imported context and answered too generically **Symptoms:** The result ignores the upstream workflow in `plugins/antigravity-awesome-skills-claude/skills/threejs-skills`, fails to mention provenance, or does not use any copied source files at all. **Solution:** Re-open `metadata.json`, `ORIGIN.md`, and the most relevant copied upstream files. Check the `external_source` block first, then restate the provenance before continuing. ### Problem: The imported workflow feels incomplete during review **Symptoms:** Reviewers can see the generated `SKILL.md`, but they cannot quickly tell which references, examples, or scripts matter for the current task. **Solution:** Point at the exact copied references, examples, scripts, or assets that justify the path you took. If the gap is still real, record it in the PR instead of hiding it. ### Problem: The task drifted into a different specialization **Symptoms:** The imported skill starts in the right place, but the work turns into debugging, architecture, design, security, or release orchestration that a native skill handles better. **Solution:** Use the related skills section to hand off deliberately. Keep the imported provenance visible so the next skill inherits the right context instead of starting blind. ### Imported Troubleshooting Notes #### Imported: Troubleshooting **Black screen / Nothing renders:** - Check if objects added to scene - Verify camera position isn't inside objects - Ensure renderer.render() is called - Add lights if using lit materials **Poor performance:** - Reduce particle count - Lower geometry detail (segments) - Reuse materials/geometries - Check browser console for errors **Objects not visible:** - Check object position vs camera position - Verify material has visible color/properties - Ensure camera far plane includes objects - Add lighting if needed ## Related Skills - `@00-andruia-consultant` - Use when the work is better handled by that native specialization after this imported skill establishes context. - `@00-andruia-consultant-v2` - Use when the work is better handled by that native specialization after this imported skill establishes context. - `@10-andruia-skill-smith` - Use when the work is better handled by that native specialization after this imported skill establishes context. - `@10-andruia-skill-smith-v2` - Use when the work is better handled by that native specialization after this imported skill establishes context. ## Additional Resources Use this support matrix and the linked files below as the operator packet for this imported skill. They should reflect real copied source material, not generic scaffolding. | Resource family | What it gives the reviewer | Example path | | --- | --- | --- | | `references` | copied reference notes, guides, or background material from upstream | `references/n/a` | | `examples` | worked examples or reusable prompts copied from upstream | `examples/n/a` | | `scripts` | upstream helper scripts that change execution or validation | `scripts/n/a` | | `agents` | routing or delegation notes that are genuinely part of the imported package | `agents/n/a` | | `assets` | supporting assets or schemas copied from the source package | `assets/n/a` | ### Imported Reference Notes #### Imported: Advanced Techniques ### Visual Polish for Portfolio-Grade Rendering **Shadows:** ```javascript // Enable shadows on renderer renderer.shadowMap.enabled = true; renderer.shadowMap.type = THREE.PCFSoftShadowMap; // Soft shadows // Light that casts shadows const directionalLight = new THREE.DirectionalLight(0xffffff, 1); directionalLight.position.set(5, 10, 5); directionalLight.castShadow = true; // Configure shadow quality directionalLight.shadow.mapSize.width = 2048; directionalLight.shadow.mapSize.height = 2048; directionalLight.shadow.camera.near = 0.5; directionalLight.shadow.camera.far = 50; scene.add(directionalLight); // Objects cast and receive shadows mesh.castShadow = true; mesh.receiveShadow = true; // Ground plane receives shadows const groundGeometry = new THREE.PlaneGeometry(20, 20); const groundMaterial = new THREE.MeshStandardMaterial({ color: 0x808080 }); const ground = new THREE.Mesh(groundGeometry, groundMaterial); ground.rotation.x = -Math.PI / 2; ground.receiveShadow = true; scene.add(ground); ``` **Environment Maps & Reflections:** ```javascript // Create environment map from cubemap const loader = new THREE.CubeTextureLoader(); const envMap = loader.load([ "px.jpg", "nx.jpg", // positive x, negative x "py.jpg", "ny.jpg", // positive y, negative y "pz.jpg", "nz.jpg", // positive z, negative z ]); scene.environment = envMap; // Affects all PBR materials scene.background = envMap; // Optional: use as skybox // Or apply to specific materials const material = new THREE.MeshStandardMaterial({ metalness: 1.0, roughness: 0.1, envMap: envMap, }); ``` **Tone Mapping & Output Encoding:** ```javascript // Improve color accuracy and HDR rendering renderer.toneMapping = THREE.ACESFilmicToneMapping; renderer.toneMappingExposure = 1.0; renderer.outputColorSpace = THREE.SRGBColorSpace; // Was outputEncoding in older versions // Makes colors more vibrant and realistic ``` **Fog for Depth:** ```javascript // Linear fog scene.fog = new THREE.Fog(0xcccccc, 10, 50); // color, near, far // Or exponential fog (more realistic) scene.fog = new THREE.FogExp2(0xcccccc, 0.02); // color, density ``` ### Custom Geometry from Vertices ```javascript const geometry = new THREE.BufferGeometry(); const vertices = new Float32Array([-1, -1, 0, 1, -1, 0, 1, 1, 0]); geometry.setAttribute("position", new THREE.BufferAttribute(vertices, 3)); ``` ### Post-Processing Effects Post-processing effects are available via import maps or build tools. See `threejs-postprocessing` skill for EffectComposer, bloom, DOF, and more. ### Group Objects ```javascript const group = new THREE.Group(); group.add(mesh1); group.add(mesh2); group.rotation.y = Math.PI / 4; scene.add(group); ``` #### Imported: Modern Three.js Practices (r183) ### Modular Imports ```javascript // With npm/vite/webpack: import * as THREE from "three"; import { OrbitControls } from "three/addons/controls/OrbitControls.js"; import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js"; import { EffectComposer } from "three/addons/postprocessing/EffectComposer.js"; ``` ### WebGPU Renderer (Alternative) Three.js r183 includes a WebGPU renderer as an alternative to WebGL: ```javascript import { WebGPURenderer } from "three/addons/renderers/webgpu/WebGPURenderer.js"; const renderer = new WebGPURenderer({ antialias: true }); await renderer.init(); renderer.setSize(window.innerWidth, window.innerHeight); ``` WebGPU uses TSL (Three.js Shading Language) instead of GLSL for custom shaders. See `threejs-shaders` for details. ### Timer (r183 Recommended) `THREE.Timer` is recommended over `THREE.Clock` as of r183: ```javascript const timer = new THREE.Timer(); renderer.setAnimationLoop(() => { timer.update(); const delta = timer.getDelta(); const elapsed = timer.getElapsed(); mesh.rotation.y += delta; renderer.render(scene, camera); }); ``` **Benefits over Clock:** - Not affected by page visibility (pauses when tab is hidden) - Cleaner API design - Better integration with `setAnimationLoop` ### Animation Libraries (GSAP Integration) ```javascript // Smooth timeline-based animations import gsap from "gsap"; // Instead of manual animation loops: gsap.to(mesh.position, { x: 5, duration: 2, ease: "power2.inOut", }); // Complex sequences: const timeline = gsap.timeline(); timeline .to(mesh.rotation, { y: Math.PI * 2, duration: 2 }) .to(mesh.scale, { x: 2, y: 2, z: 2, duration: 1 }, "-=1"); ``` **Why GSAP:** - Professional easing functions - Timeline control (pause, reverse, scrub) - Better than manual lerping for complex animations ### Scroll-Based Interactions ```javascript // Sync 3D animations with page scroll let scrollY = window.scrollY; window.addEventListener("scroll", () => { scrollY = window.scrollY; }); function animate() { requestAnimationFrame(animate); // Rotate based on scroll position mesh.rotation.y = scrollY * 0.001; // Move camera through scene camera.position.y = -(scrollY / window.innerHeight) * 10; renderer.render(scene, camera); } ``` **Advanced scroll libraries:** - ScrollTrigger (GSAP plugin) - Locomotive Scroll - Lenis smooth scroll ### Performance Optimization in Production ```javascript // Level of Detail (LOD) const lod = new THREE.LOD(); lod.addLevel(highDetailMesh, 0); // Close up lod.addLevel(mediumDetailMesh, 10); // Medium distance lod.addLevel(lowDetailMesh, 50); // Far away scene.add(lod); // Instanced meshes for many identical objects const geometry = new THREE.BoxGeometry(); const material = new THREE.MeshStandardMaterial(); const instancedMesh = new THREE.InstancedMesh(geometry, material, 1000); // Set transforms for each instance const matrix = new THREE.Matrix4(); for (let i = 0; i < 1000; i++) { matrix.setPosition( Math.random() * 100, Math.random() * 100, Math.random() * 100, ); instancedMesh.setMatrixAt(i, matrix); } ``` ### Modern Loading Patterns ```javascript // In production, load 3D models: import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader"; const loader = new GLTFLoader(); loader.load("model.gltf", (gltf) => { scene.add(gltf.scene); // Traverse and setup materials gltf.scene.traverse((child) => { if (child.isMesh) { child.castShadow = true; child.receiveShadow = true; } }); }); ``` ### When to Use What **Import Map Approach:** - Quick prototypes and demos - Educational content - Artifacts and embedded experiences - No build step required **Production Build Approach:** - Client projects and portfolios - Complex applications - Performance-critical applications - Team collaboration with version control ### Recommended Production Stack ``` Three.js r183 + Vite ├── GSAP (animations) ├── React Three Fiber (optional - React integration) ├── Drei (helper components) ├── Leva (debug GUI) └── Post-processing effects ``` #### Imported: Limitations - Use this skill only when the task clearly matches the scope described above. - Do not treat the output as a substitute for environment-specific validation, testing, or expert review. - Stop and ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.