home / skills / sickn33 / antigravity-awesome-skills / threejs-skills
This skill helps you create high-quality 3D scenes and interactive visuals using Three.js with CDN-r128 best practices.
npx playbooks add skill sickn33/antigravity-awesome-skills --skill threejs-skillsReview the files below or copy the command above to add this skill to your agents.
---
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.
source: https://github.com/CloudAI-X/threejs-skills
risk: safe
---
# 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.
This skill helps you create high-quality 3D scenes, interactive experiences, and visual effects using Three.js. It codifies a reliable, CDN-friendly workflow (r128) for scene setup, animation, interaction, and performance tuning. Use it to rapidly prototype WebGL visuals or to produce production-ready patterns that scale into modern build toolchains.
The skill provides a systematic pattern: import the correct Three.js CDN, initialize scene/camera/renderer, build geometry and materials, add lights when using lit materials, and run an animation loop with requestAnimationFrame. It includes practical interaction patterns (custom orbit-like controls, raycasting for selection, particle systems), responsiveness handling, and resource disposal for performance. Guidance is included for both CDN-based projects and migrating to modular imports in production.
Can I use OrbitControls and other examples with the CDN import?
No. OrbitControls and many example modules are not available on the r128 CDN. Implement simple custom controls or switch to modular imports via npm to use the examples library.
Why is my scene black or nothing renders?
Common causes: forgot to add objects to scene, camera inside objects, using lit materials without lights, or not calling renderer.render() inside the animation loop. Check console errors and validate camera and object positions.