Back to UI/UX Design

threejs-fundamentals

Three.js3D graphicsWebGLJavaScriptfrontendscene setupcamerasrenderers
2.4k🕒 2026-01-19Source ↗

Install this skill

npx skills add cloudai-x/threejs-skills

Works across Claude Code, Cursor, Codex, Copilot & Antigravity

Three.js Fundamentals provides the architectural scaffolding for 3D web graphics. It abstracts the complex WebGL API into a readable, object-oriented system centered on a scene graph. By defining a hierarchy of meshes, cameras, and light sources, developers create interactive visual experiences within the browser. The skill covers the standard implementation pattern: initializing a renderer to interface with the GPU, setting up perspective or orthographic cameras to define the viewport, and maintaining an animation loop for state updates. Whether rendering simple primitives or integrating complex environment maps for dynamic reflection, this approach ensures the transformation of mathematical data into visible 3D geometry. It emphasizes proper scene management, handling window resize events to maintain aspect ratios, and optimizing rendering settings for hardware performance.

When to Use This Skill

  • Adding interactive 3D product visualizers to an e-commerce storefront
  • Implementing data-heavy visualizations with complex mesh hierarchies
  • Developing browser-based architectural walkthroughs or 3D prototypes
  • Creating custom geometric UI components for immersive web applications

How to Invoke This Skill

Example prompts that trigger this skill in Claude Code, Cursor, or Antigravity:

  • Create a basic Three.js scene with a rotating cube
  • How do I set up a WebGL renderer for a full-screen canvas?
  • Explain the difference between PerspectiveCamera and OrthographicCamera
  • Show me how to manage object hierarchy using Object3D
  • How can I handle responsive window resizing for my Three.js canvas?

Pro Tips

  • 💡Always consider `renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))` to leverage high-DPI screens effectively without overly stressing lower-end GPUs.
  • 💡Utilize `Object3D` for grouping related meshes, lights, or even other groups; this simplifies transformations (position, rotation, scale) for entire sections of your scene.
  • 💡Start with `AmbientLight` and a simple `DirectionalLight` for basic scene illumination. Progressively add more complex lighting once the fundamental scene structure is stable.

What this skill does

  • Object-oriented scene graph for hierarchical 3D transformations
  • Multi-camera support including perspective, orthographic, and array viewports
  • Integrated shader pipeline for materials, lighting, and environmental mapping
  • Hardware-accelerated rendering with WebGL performance optimization
  • Built-in geometry primitives and transformation control for translation, rotation, and scaling

When not to use it

  • Simple 2D static graphics or basic vector animations best handled by CSS or SVG
  • Performance-critical heavy simulation requiring direct low-level GPU access beyond the standard mesh system

Example workflow

  1. Initialize the scene, camera, and WebGLRenderer instance
  2. Construct geometry and apply materials to create 3D meshes
  3. Define lighting sources and add all objects to the scene graph
  4. Implement the requestAnimationFrame loop for continuous rendering
  5. Add event listeners to update projection matrices during resize events

Prerequisites

  • Basic proficiency in modern JavaScript (ES6+)
  • Fundamental understanding of coordinate systems and linear algebra

Pitfalls & limitations

  • !Forgetting to update the projection matrix after camera aspect ratio changes
  • !Performance bottlenecks caused by frequent, expensive updates like real-time reflection probes
  • !Misunderstanding local versus world coordinate transforms in deep object hierarchies

FAQ

Why is my canvas empty despite adding objects?
Check if your camera is correctly positioned, if the camera is looking at the center (0,0,0), and if your renderer is being called within the animation loop.
Do I need to clean up resources?
Yes. When removing objects from the scene, you should manually dispose of geometries, materials, and textures to free up GPU memory.
How does setPixelRatio affect rendering?
It ensures the canvas renders at the native resolution of high-density displays, preventing blurry images at the cost of additional GPU processing.
Is Three.js compatible with all browsers?
Three.js runs on any browser supporting WebGL, which includes virtually all modern desktop and mobile platforms.

How it compares

Using this skill provides structured boilerplate that handles scene graph traversal and matrix updates correctly, which is significantly more error-prone when writing raw WebGL shaders from scratch.

Source & trust

2.4k stars🕒 Updated 2026-01-19
📄 Full skill instructions — original source: cloudai-x/threejs-skills
# Three.js Fundamentals

## Quick Start

import * as THREE from "three";

// Create scene, camera, renderer
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000,
);
const renderer = new THREE.WebGLRenderer({ antialias: true });

renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
document.body.appendChild(renderer.domElement);

// Add a mesh
const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshStandardMaterial({ color: 0x00ff00 });
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);

// Add light
scene.add(new THREE.AmbientLight(0xffffff, 0.5));
const dirLight = new THREE.DirectionalLight(0xffffff, 1);
dirLight.position.set(5, 5, 5);
scene.add(dirLight);

camera.position.z = 5;

// Animation loop
function animate() {
requestAnimationFrame(animate);
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
renderer.render(scene, camera);
}
animate();

// Handle resize
window.addEventListener("resize", () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});


## Core Classes

### Scene

Container for all 3D objects, lights, and cameras.

const scene = new THREE.Scene();
scene.background = new THREE.Color(0x000000); // Solid color
scene.background = texture; // Skybox texture
scene.background = cubeTexture; // Cubemap
scene.environment = envMap; // Environment map for PBR
scene.fog = new THREE.Fog(0xffffff, 1, 100); // Linear fog
scene.fog = new THREE.FogExp2(0xffffff, 0.02); // Exponential fog


### Cameras

**PerspectiveCamera** - Most common, simulates human eye.

// PerspectiveCamera(fov, aspect, near, far)
const camera = new THREE.PerspectiveCamera(
75, // Field of view (degrees)
window.innerWidth / window.innerHeight, // Aspect ratio
0.1, // Near clipping plane
1000, // Far clipping plane
);

camera.position.set(0, 5, 10);
camera.lookAt(0, 0, 0);
camera.updateProjectionMatrix(); // Call after changing fov, aspect, near, far


**OrthographicCamera** - No perspective distortion, good for 2D/isometric.

// OrthographicCamera(left, right, top, bottom, near, far)
const aspect = window.innerWidth / window.innerHeight;
const frustumSize = 10;
const camera = new THREE.OrthographicCamera(
(frustumSize * aspect) / -2,
(frustumSize * aspect) / 2,
frustumSize / 2,
frustumSize / -2,
0.1,
1000,
);


**ArrayCamera** - Multiple viewports with sub-cameras.

const cameras = [];
for (let i = 0; i < 4; i++) {
const subcamera = new THREE.PerspectiveCamera(40, 1, 0.1, 100);
subcamera.viewport = new THREE.Vector4(
Math.floor(i % 2) * 0.5,
Math.floor(i / 2) * 0.5,
0.5,
0.5,
);
cameras.push(subcamera);
}
const arrayCamera = new THREE.ArrayCamera(cameras);


**CubeCamera** - Renders environment maps for reflections.

const cubeRenderTarget = new THREE.WebGLCubeRenderTarget(256);
const cubeCamera = new THREE.CubeCamera(0.1, 1000, cubeRenderTarget);
scene.add(cubeCamera);

// Use for reflections
material.envMap = cubeRenderTarget.texture;

// Update each frame (expensive!)
cubeCamera.position.copy(reflectiveMesh.position);
cubeCamera.update(renderer, scene);


### WebGLRenderer

const renderer = new THREE.WebGLRenderer({
canvas: document.querySelector("#canvas"), // Optional existing canvas
antialias: true, // Smooth edges
alpha: true, // Transparent background
powerPreference: "high-performance", // GPU hint
preserveDrawingBuffer: true, // For screenshots
});

renderer.setSize(width, height);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));

// Tone mapping
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = 1.0;

// Color space (Three.js r152+)
renderer.outputColorSpace = THREE.SRGBColorSpace;

// Shadows
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;

// Clear color
renderer.setClearColor(0x000000, 1);

// Render
renderer.render(scene, camera);


### Object3D

Base class for all 3D objects. Mesh, Group, Light, Camera all extend Object3D.

const obj = new THREE.Object3D();

// Transform
obj.position.set(x, y, z);
obj.rotation.set(x, y, z); // Euler angles (radians)
obj.quaternion.set(x, y, z, w); // Quaternion rotation
obj.scale.set(x, y, z);

// Local vs World transforms
obj.getWorldPosition(targetVector);
obj.getWorldQuaternion(targetQuaternion);
obj.getWorldDirection(targetVector);

// Hierarchy
obj.add(child);
obj.remove(child);
obj.parent;
obj.children;

// Visibility
obj.visible = false;

// Layers (for selective rendering/raycasting)
obj.layers.set(1);
obj.layers.enable(2);
obj.layers.disable(0);

// Traverse hierarchy
obj.traverse((child) => {
if (child.isMesh) child.material.color.set(0xff0000);
});

// Matrix updates
obj.matrixAutoUpdate = true; // Default: auto-update matrices
obj.updateMatrix(); // Manual matrix update
obj.updateMatrixWorld(true); // Update world matrix recursively


### Group

Empty container for organizing objects.

const group = new THREE.Group();
group.add(mesh1);
group.add(mesh2);
scene.add(group);

// Transform entire group
group.position.x = 5;
group.rotation.y = Math.PI / 4;


### Mesh

Combines geometry and material.

const mesh = new THREE.Mesh(geometry, material);

// Multiple materials (one per geometry group)
const mesh = new THREE.Mesh(geometry, [material1, material2]);

// Useful properties
mesh.geometry;
mesh.material;
mesh.castShadow = true;
mesh.receiveShadow = true;

// Frustum culling
mesh.frustumCulled = true; // Default: skip if outside camera view

// Render order
mesh.renderOrder = 10; // Higher = rendered later


## Coordinate System

Three.js uses a **right-handed coordinate system**:

- **+X** points right
- **+Y** points up
- **+Z** points toward viewer (out of screen)

// Axes helper
const axesHelper = new THREE.AxesHelper(5);
scene.add(axesHelper); // Red=X, Green=Y, Blue=Z


## Math Utilities

### Vector3

const v = new THREE.Vector3(x, y, z);
v.set(x, y, z);
v.copy(otherVector);
v.clone();

// Operations (modify in place)
v.add(v2);
v.sub(v2);
v.multiply(v2);
v.multiplyScalar(2);
v.divideScalar(2);
v.normalize();
v.negate();
v.clamp(min, max);
v.lerp(target, alpha);

// Calculations (return new value)
v.length();
v.lengthSq(); // Faster than length()
v.distanceTo(v2);
v.dot(v2);
v.cross(v2); // Modifies v
v.angleTo(v2);

// Transform
v.applyMatrix4(matrix);
v.applyQuaternion(q);
v.project(camera); // World to NDC
v.unproject(camera); // NDC to world


### Matrix4

const m = new THREE.Matrix4();
m.identity();
m.copy(other);
m.clone();

// Build transforms
m.makeTranslation(x, y, z);
m.makeRotationX(theta);
m.makeRotationY(theta);
m.makeRotationZ(theta);
m.makeRotationFromQuaternion(q);
m.makeScale(x, y, z);

// Compose/decompose
m.compose(position, quaternion, scale);
m.decompose(position, quaternion, scale);

// Operations
m.multiply(m2); // m = m * m2
m.premultiply(m2); // m = m2 * m
m.invert();
m.transpose();

// Camera matrices
m.makePerspective(left, right, top, bottom, near, far);
m.makeOrthographic(left, right, top, bottom, near, far);
m.lookAt(eye, target, up);


### Quaternion

const q = new THREE.Quaternion();
q.setFromEuler(euler);
q.setFromAxisAngle(axis, angle);
q.setFromRotationMatrix(matrix);

q.multiply(q2);
q.slerp(target, t); // Spherical interpolation
q.normalize();
q.invert();


### Euler

const euler = new THREE.Euler(x, y, z, "XYZ"); // Order matters!
euler.setFromQuaternion(q);
euler.setFromRotationMatrix(m);

// Rotation orders: 'XYZ', 'YXZ', 'ZXY', 'XZY', 'YZX', 'ZYX'


### Color

const color = new THREE.Color(0xff0000);
const color = new THREE.Color("red");
const color = new THREE.Color("rgb(255, 0, 0)");
const color = new THREE.Color("#ff0000");

color.setHex(0x00ff00);
color.setRGB(r, g, b); // 0-1 range
color.setHSL(h, s, l); // 0-1 range

color.lerp(otherColor, alpha);
color.multiply(otherColor);
color.multiplyScalar(2);


### MathUtils

THREE.MathUtils.clamp(value, min, max);
THREE.MathUtils.lerp(start, end, alpha);
THREE.MathUtils.mapLinear(value, inMin, inMax, outMin, outMax);
THREE.MathUtils.degToRad(degrees);
THREE.MathUtils.radToDeg(radians);
THREE.MathUtils.randFloat(min, max);
THREE.MathUtils.randInt(min, max);
THREE.MathUtils.smoothstep(x, min, max);
THREE.MathUtils.smootherstep(x, min, max);


## Common Patterns

### Proper Cleanup

function dispose() {
// Dispose geometries
mesh.geometry.dispose();

// Dispose materials
if (Array.isArray(mesh.material)) {
mesh.material.forEach((m) => m.dispose());
} else {
mesh.material.dispose();
}

// Dispose textures
texture.dispose();

// Remove from scene
scene.remove(mesh);

// Dispose renderer
renderer.dispose();
}


### Clock for Animation

const clock = new THREE.Clock();

function animate() {
const delta = clock.getDelta(); // Time since last frame (seconds)
const elapsed = clock.getElapsedTime(); // Total time (seconds)

mesh.rotation.y += delta * 0.5; // Consistent speed regardless of framerate

requestAnimationFrame(animate);
renderer.render(scene, camera);
}


### Responsive Canvas

function onWindowResize() {
const width = window.innerWidth;
const height = window.innerHeight;

camera.aspect = width / height;
camera.updateProjectionMatrix();

renderer.setSize(width, height);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
}
window.addEventListener("resize", onWindowResize);


### Loading Manager

const manager = new THREE.LoadingManager();

manager.onStart = (url, loaded, total) => console.log("Started loading");
manager.onLoad = () => console.log("All loaded");
manager.onProgress = (url, loaded, total) => console.log(${loaded}/${total});
manager.onError = (url) => console.error(Error loading ${url});

const textureLoader = new THREE.TextureLoader(manager);
const gltfLoader = new GLTFLoader(manager);


## Performance Tips

1. **Limit draw calls**: Merge geometries, use instancing, atlas textures
2. **Frustum culling**: Enabled by default, ensure bounding boxes are correct
3. **LOD (Level of Detail)**: Use THREE.LOD for distance-based mesh switching
4. **Object pooling**: Reuse objects instead of creating/destroying
5. **Avoid getWorldPosition in loops**: Cache results

// Merge static geometries
import { mergeGeometries } from "three/examples/jsm/utils/BufferGeometryUtils.js";
const merged = mergeGeometries([geo1, geo2, geo3]);

// LOD
const lod = new THREE.LOD();
lod.addLevel(highDetailMesh, 0);
lod.addLevel(medDetailMesh, 50);
lod.addLevel(lowDetailMesh, 100);
scene.add(lod);


## See Also

- threejs-geometry - Geometry creation and manipulation
- threejs-materials - Material types and properties
- threejs-lighting - Light types and shadows

How to Use This Skill Unit

Option A: Project-Specific (Recommended)

  1. Click "Download" above
  2. In your project, create the directory: .agent/skills/threejs-fundamentals/
  3. Save the file as SKILL.md
  4. The agent will automatically discover the skill based on its description.

Option B: Global Installation (All Agents)

Save the file to these locations to make it available across all projects:

  • Claude Code: ~/.claude/skills/cloudai-x/threejs-skills/threejs-fundamentals/SKILL.md
  • Cursor: ~/.cursor/skills/cloudai-x/threejs-skills/threejs-fundamentals/SKILL.md
  • Antigravity: ~/.gemini/antigravity/skills/cloudai-x/threejs-skills/threejs-fundamentals/SKILL.md

🚀 Install with CLI:
npx skills add cloudai-x/threejs-skills

Read the Master Guide: Mastering Agent Skills

Recommended Rules

View more rules

Recommended Workflows

View more workflows

Recommended MCP Servers

View more MCP servers

Take It Further

Maximize your productivity with these powerful resources

📋

Define Your Standards

Set up coding standards to ensure this workflow produces consistent, high-quality results.

Browse Rules Library
📖

Master Workflows

Learn how to create custom workflows, use Turbo Mode, and build your automation library.

Complete Guide

How to use this Skill in Claude Code & Cursor

For Claude Code (CLI)

To use this skill in Claude Code, copy the rule content into your project's custom instructions or follow our Add-Skill CLI guide. This ensures Claude follows your standards during every code generation.

For Cursor & Windsurf

For Cursor or Windsurf, individual skills are best used in the "Rules for AI" section. This specific unit helps the agent avoid ui/ux design issues, leading to cleaner, more efficient code.

Why the skill format matters: the standardized Agent Skills format lets your AI agent load detailed instructions only when they are relevant, keeping your prompt clean while improving results.

Source & attribution

This skill is categorized under UI/UX Design and is published by CloudAI-X, maintained in cloudai-x/threejs-skills.

← Browse All Agent Skills
Sponsored AI assistant. Recommendations may be paid.