| name | 3d-interactive-ui |
| description | Expert in creating detailed, interactive 3D visualizations using Three.js. Use when user requests 3D, three-dimensional, 3D visualization, WebGL, or 3D interactive experiences. Provides comprehensive guidance for physics simulations, 3D models, lighting, cameras, and interactive controls. |
3D Interactive UI Skill
Overview
This skill provides expert guidance for creating high-quality, interactive 3D visualizations using Three.js. It covers everything from basic scene setup to advanced rendering techniques, ensuring generated 3D applications are performant, interactive, and visually stunning.
Three.js Setup
Required CDN Links
Always include these scripts in the HTML <head> or before closing </body>:
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/OrbitControls.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.9/dat.gui.min.js"></script>
Scene Initialization Pattern
const scene = new THREE.Scene();
scene.background = new THREE.Color(0xf0f0f0);
const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
camera.position.set(5, 5, 5);
camera.lookAt(0, 0, 0);
const renderer = new THREE.WebGLRenderer({
antialias: true,
alpha: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
document.body.appendChild(renderer.domElement);
const controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.05;
controls.minDistance = 2;
controls.maxDistance = 50;
controls.enablePan = true;
controls.enableZoom = true;
Core 3D Concepts
Geometry and Meshes
- BoxGeometry:
new THREE.BoxGeometry(width, height, depth)
- SphereGeometry:
new THREE.SphereGeometry(radius, widthSegments, heightSegments)
- PlaneGeometry:
new THREE.PlaneGeometry(width, height)
- CylinderGeometry:
new THREE.CylinderGeometry(radiusTop, radiusBottom, height)
- TorusGeometry:
new THREE.TorusGeometry(radius, tube, radialSegments, tubularSegments)
- BufferGeometry: Use for custom/complex geometries (more efficient)
Materials
- MeshStandardMaterial: Physically-based rendering (PBR), responds to lights
- MeshPhongMaterial: Shiny, responds to lights
- MeshLambertMaterial: Matte, responds to lights
- MeshBasicMaterial: Flat color, ignores lights
- MeshPhysicalMaterial: Advanced PBR with clearcoat, sheen, etc.
Best Practice: Use MeshStandardMaterial for realistic 3D objects. Always set metalness and roughness properties.
Lighting Setup
Essential Light Configuration:
const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);
scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
directionalLight.position.set(5, 10, 5);
directionalLight.castShadow = true;
directionalLight.shadow.mapSize.width = 2048;
directionalLight.shadow.mapSize.height = 2048;
directionalLight.shadow.camera.near = 0.5;
directionalLight.shadow.camera.far = 50;
scene.add(directionalLight);
const pointLight = new THREE.PointLight(0xffffff, 1, 100);
pointLight.position.set(-5, 5, -5);
pointLight.castShadow = true;
scene.add(pointLight);
const hemisphereLight = new THREE.HemisphereLight(0xffffbb, 0x080820, 0.5);
scene.add(hemisphereLight);
Lighting Best Practices:
- Always include ambient light (30-60% intensity)
- Use directional light as main light source
- Add point/spot lights for accent lighting
- Enable shadows on lights and meshes for depth
- Adjust shadow map size based on scene complexity
Interactive Controls
OrbitControls Setup
const controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.05;
controls.screenSpacePanning = false;
controls.minDistance = 1;
controls.maxDistance = 100;
controls.maxPolarAngle = Math.PI;
function animate() {
requestAnimationFrame(animate);
controls.update();
renderer.render(scene, camera);
}
animate();
GUI Controls (dat.GUI)
const gui = new dat.GUI();
const params = {
rotationSpeed: 0.01,
scale: 1,
color: 0xff0000
};
gui.add(params, 'rotationSpeed', 0, 0.1);
gui.add(params, 'scale', 0.1, 2);
gui.addColor(params, 'color').onChange((value) => {
mesh.material.color.setHex(value);
});
Custom Tailwind GUI Panels
Instead of dat.GUI, create custom control panels using Tailwind CSS:
<div class="fixed top-4 right-4 bg-white/90 backdrop-blur-sm rounded-xl p-4 shadow-lg">
<h3 class="font-bold mb-2">Controls</h3>
<div class="space-y-2">
<label class="block text-sm">Rotation Speed</label>
<input type="range" id="rotationSpeed" min="0" max="0.1" step="0.01"
class="w-full" value="0.01">
</div>
</div>
Performance Optimization
Efficient Rendering
- Use BufferGeometry: Always prefer BufferGeometry over Geometry for better performance
- Frustum Culling: Automatically handled by Three.js (objects outside camera view aren't rendered)
- Instancing: Use
InstancedMesh for repeated objects (trees, particles, etc.)
- Level of Detail (LOD): Use
THREE.LOD for objects at different distances
- Texture Optimization: Use compressed textures, appropriate sizes (power of 2)
- Reduce Draw Calls: Combine geometries, use texture atlases
Animation Loop Best Practices
let clock = new THREE.Clock();
function animate() {
requestAnimationFrame(animate);
const delta = clock.getDelta();
controls.update();
if (mesh) {
mesh.rotation.y += rotationSpeed * delta;
}
renderer.render(scene, camera);
}
animate();
Memory Management
geometry.dispose();
material.dispose();
texture.dispose();
scene.traverse((object) => {
if (object instanceof THREE.Mesh) {
object.geometry.dispose();
if (object.material instanceof Array) {
object.material.forEach(m => m.dispose());
} else {
object.material.dispose();
}
}
});
Common 3D Patterns
Particle Systems
const particleCount = 1000;
const geometry = new THREE.BufferGeometry();
const positions = new Float32Array(particleCount * 3);
for (let i = 0; i < particleCount * 3; i++) {
positions[i] = (Math.random() - 0.5) * 10;
}
geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
const material = new THREE.PointsMaterial({
color: 0xffffff,
size: 0.05,
transparent: true,
opacity: 0.8
});
const particles = new THREE.Points(geometry, material);
scene.add(particles);
Physics Simulations
For gravity, collisions, and physics:
- Use simple Euler integration for basic physics
- Calculate forces:
force = mass * acceleration
- Update velocity:
velocity += force * deltaTime
- Update position:
position += velocity * deltaTime
3D Data Visualization
- Use
THREE.BarChart patterns with BoxGeometry
- Color-code bars by value
- Add interactive labels on hover
- Animate transitions when data changes
Procedural 3D Generation
- Use mathematical functions to generate geometry
- Create terrain with Perlin noise
- Generate organic shapes with parametric equations
- Use
THREE.ParametricGeometry for custom surfaces
Code Structure
Organized Three.js Code Pattern
class Scene3D {
constructor() {
this.scene = new THREE.Scene();
this.camera = null;
this.renderer = null;
this.controls = null;
this.objects = [];
}
init() {
this.setupRenderer();
this.setupCamera();
this.setupLights();
this.setupControls();
this.createObjects();
this.animate();
}
setupRenderer() {
this.renderer = new THREE.WebGLRenderer({ antialias: true });
this.renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(this.renderer.domElement);
}
setupCamera() {
this.camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
this.camera.position.set(5, 5, 5);
}
setupLights() {
}
setupControls() {
this.controls = new THREE.OrbitControls(this.camera, this.renderer.domElement);
this.controls.enableDamping = true;
}
createObjects() {
}
animate() {
requestAnimationFrame(() => this.animate());
this.controls.update();
this.renderer.render(this.scene, this.camera);
}
}
document.addEventListener('DOMContentLoaded', () => {
const scene = new Scene3D();
scene.init();
});
Responsive Design
Handle Window Resize
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
Mobile Optimization
- Reduce particle counts on mobile
- Lower shadow map resolution
- Disable expensive post-processing effects
- Use simpler materials (MeshLambertMaterial instead of MeshStandardMaterial)
Best Practices Summary
- Always use WebGLRenderer with antialiasing enabled
- Enable shadows for depth perception (directional light + shadow casting)
- Use OrbitControls for camera interaction
- Implement proper lighting (ambient + directional + accent lights)
- Use requestAnimationFrame for smooth animations
- Handle window resize events
- Dispose of resources when removing objects
- Use delta time in animations for frame-rate independence
- Optimize geometry with BufferGeometry and instancing
- Add GUI controls for interactive parameter adjustment
Example: Complete 3D Scene Template
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x222222);
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 5, 10);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
document.body.appendChild(renderer.domElement);
const controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
directionalLight.position.set(5, 10, 5);
directionalLight.castShadow = true;
scene.add(directionalLight);
const geometry = new THREE.BoxGeometry(2, 2, 2);
const material = new THREE.MeshStandardMaterial({
color: 0x00ff00,
metalness: 0.5,
roughness: 0.5
});
const cube = new THREE.Mesh(geometry, material);
cube.castShadow = true;
cube.receiveShadow = true;
scene.add(cube);
const planeGeometry = new THREE.PlaneGeometry(20, 20);
const planeMaterial = new THREE.MeshStandardMaterial({ color: 0xcccccc });
const plane = new THREE.Mesh(planeGeometry, planeMaterial);
plane.rotation.x = -Math.PI / 2;
plane.receiveShadow = true;
scene.add(plane);
function animate() {
requestAnimationFrame(animate);
controls.update();
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
renderer.render(scene, camera);
}
animate();
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
Critical Requirements for Generated Code
When generating 3D applications, ALWAYS:
- Include Three.js and OrbitControls CDN links
- Set up scene, camera, and renderer with proper configuration
- Add ambient + directional lighting with shadows enabled
- Implement OrbitControls for camera interaction
- Use requestAnimationFrame for animation loop
- Handle window resize events
- Use MeshStandardMaterial for realistic rendering
- Enable shadow casting/receiving on objects
- Add GUI controls (dat.GUI or custom Tailwind panels) for parameter adjustment
- Structure code cleanly with clear separation of concerns
- Include error handling for WebGL context loss
- Optimize for performance (BufferGeometry, efficient rendering)