add comprehensive Three.js skill with interaction, polish, and production patterns
Systematic guide covering r128 CDN setup, raycasting/custom controls, visual polish (shadows, environment maps, tone mapping), and modern production practices (GSAP, scroll interactions, build tools). Follows test-fixing skill structure with step-by-step workflows and troubleshooting.
This commit is contained in:
@@ -1,22 +1,637 @@
|
||||
---
|
||||
name: threejs-skills
|
||||
description: "Three.js skills for creating 3D elements and interactive experiences"
|
||||
source: "https://github.com/CloudAI-X/threejs-skills"
|
||||
risk: safe
|
||||
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.
|
||||
---
|
||||
|
||||
# Threejs Skills
|
||||
# Three.js Skills
|
||||
|
||||
## Overview
|
||||
Systematically create high-quality 3D scenes and interactive experiences using Three.js best practices.
|
||||
|
||||
Three.js skills for creating 3D elements and interactive experiences
|
||||
## When to Use
|
||||
|
||||
## When to Use This Skill
|
||||
- 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
|
||||
|
||||
Use this skill when you need to work with three.js skills for creating 3d elements and interactive experiences.
|
||||
## Core Setup Pattern
|
||||
|
||||
## Instructions
|
||||
### 1. Essential Three.js Imports
|
||||
|
||||
This skill provides guidance and patterns for three.js skills for creating 3d elements and interactive experiences.
|
||||
Always use the correct CDN version (r128):
|
||||
|
||||
For more information, see the [source repository](https://github.com/CloudAI-X/threejs-skills).
|
||||
```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.
|
||||
|
||||
Reference in New Issue
Block a user