- Add date_added to all 950+ skills for complete tracking - Update version to 6.5.0 in package.json and README - Regenerate all indexes and catalog - Sync all generated files Features from merged PR #150: - Stars/Upvotes system for community-driven discovery - Auto-update mechanism via START_APP.bat - Interactive Prompt Builder - Date tracking badges - Smart auto-categorization All skills validated and indexed. Made-with: Cursor
656 lines
17 KiB
Markdown
656 lines
17 KiB
Markdown
---
|
|
name: threejs-skills
|
|
description: "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."
|
|
risk: safe
|
|
source: "https://github.com/CloudAI-X/threejs-skills"
|
|
date_added: "2026-02-27"
|
|
---
|
|
|
|
# Three.js Skills
|
|
|
|
Systematically create high-quality 3D scenes and interactive experiences using Three.js best practices.
|
|
|
|
## When to Use
|
|
|
|
- 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
|
|
|
|
## Core Setup Pattern
|
|
|
|
### 1. Essential Three.js Imports
|
|
|
|
Always use the correct CDN version (r128):
|
|
|
|
```javascript
|
|
import * as THREE from "https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js";
|
|
```
|
|
|
|
**CRITICAL**: Do NOT use example imports like `THREE.OrbitControls` - they won't work on the CDN.
|
|
|
|
### 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 requestAnimationFrame for smooth rendering:
|
|
|
|
```javascript
|
|
function animate() {
|
|
requestAnimationFrame(animate);
|
|
|
|
// Update object transformations here
|
|
mesh.rotation.x += 0.01;
|
|
mesh.rotation.y += 0.01;
|
|
|
|
renderer.render(scene, camera);
|
|
}
|
|
animate();
|
|
```
|
|
|
|
## 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
|
|
|
|
**IMPORTANT**: Do NOT use `CapsuleGeometry` (introduced in r142, not available in r128)
|
|
|
|
**Alternatives for capsules:**
|
|
|
|
- Combine `CylinderGeometry` + 2 `SphereGeometry`
|
|
- Use `SphereGeometry` with adjusted parameters
|
|
- Create custom geometry with vertices
|
|
|
|
### 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);
|
|
});
|
|
```
|
|
|
|
## Common Patterns
|
|
|
|
### Rotating Object
|
|
|
|
```javascript
|
|
function animate() {
|
|
requestAnimationFrame(animate);
|
|
mesh.rotation.x += 0.01;
|
|
mesh.rotation.y += 0.01;
|
|
renderer.render(scene, camera);
|
|
}
|
|
```
|
|
|
|
### Custom Camera Controls (OrbitControls Alternative)
|
|
|
|
Since `THREE.OrbitControls` isn't available on CDN, implement custom controls:
|
|
|
|
```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,
|
|
});
|
|
```
|
|
|
|
## 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 `THREE.OrbitControls` - not available on CDN
|
|
- ❌ Using `THREE.CapsuleGeometry` - requires r142+
|
|
- ❌ 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
|
|
|
|
## Example Workflow
|
|
|
|
User: "Create an interactive 3D sphere that responds to mouse movement"
|
|
|
|
1. **Setup**: Import Three.js (r128), 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 ✓
|
|
|
|
## 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
|
|
|
|
## 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.outputEncoding = THREE.sRGBEncoding;
|
|
|
|
// 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
|
|
|
|
While advanced post-processing may not be available in r128 CDN, basic effects can be achieved with shaders and render targets.
|
|
|
|
### Group Objects
|
|
|
|
```javascript
|
|
const group = new THREE.Group();
|
|
group.add(mesh1);
|
|
group.add(mesh2);
|
|
group.rotation.y = Math.PI / 4;
|
|
scene.add(group);
|
|
```
|
|
|
|
## Summary
|
|
|
|
Three.js artifacts require systematic setup:
|
|
|
|
1. Import correct CDN version (r128)
|
|
2. Initialize scene, camera, renderer
|
|
3. Create geometry + material = mesh
|
|
4. Add lighting if using lit materials
|
|
5. Implement animation loop
|
|
6. Handle window resize
|
|
7. Avoid r128 incompatible features
|
|
|
|
Follow these patterns for reliable, performant 3D experiences.
|
|
|
|
## Modern Three.js & Production Practices
|
|
|
|
While this skill focuses on CDN-based Three.js (r128) for artifact compatibility, here's what you'd do in production environments:
|
|
|
|
### Modular Imports with Build Tools
|
|
|
|
```javascript
|
|
// In production with npm/vite/webpack:
|
|
import * as THREE from "three";
|
|
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
|
|
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
|
|
import { EffectComposer } from "three/examples/jsm/postprocessing/EffectComposer";
|
|
```
|
|
|
|
**Benefits:**
|
|
|
|
- Tree-shaking (smaller bundle sizes)
|
|
- Access to full example library (OrbitControls, loaders, etc.)
|
|
- Latest Three.js features (r150+)
|
|
- TypeScript support
|
|
|
|
### 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
|
|
|
|
**CDN Approach (Current Skill):**
|
|
|
|
- Quick prototypes and demos
|
|
- Educational content
|
|
- Artifacts and embedded experiences
|
|
- No build step required
|
|
|
|
**Production Build Approach:**
|
|
|
|
- Client projects and portfolios
|
|
- Complex applications
|
|
- Need latest features (r150+)
|
|
- Performance-critical applications
|
|
- Team collaboration with version control
|
|
|
|
### Recommended Production Stack
|
|
|
|
```
|
|
Three.js (latest) + Vite/Webpack
|
|
├── GSAP (animations)
|
|
├── React Three Fiber (optional - React integration)
|
|
├── Drei (helper components)
|
|
├── Leva (debug GUI)
|
|
└── Post-processing effects
|
|
```
|
|
|
|
This skill provides CDN-compatible foundations. In production, you'd layer on these modern tools for professional results.
|