PluginBench
Skill
Pass
Audit score 90

threejs-fundamentals

cloudai-x/threejs-skills

Set up 3D scenes, cameras, renderers, and object hierarchies in Three.js.

What is threejs-fundamentals?

Three.js Fundamentals provides core classes and patterns for building 3D scenes: Scene, Camera (Perspective/Orthographic/Array/Cube), WebGLRenderer, Object3D hierarchy, and coordinate systems. Use this when initializing a 3D project, configuring cameras and rendering, managing object transforms, or organizing scene structure.

  • Create and configure Scene, PerspectiveCamera, OrthographicCamera, and WebGLRenderer
  • Manage Object3D hierarchy with transforms (position, rotation, scale, quaternion)
  • Handle camera types including ArrayCamera for multi-viewport and CubeCamera for reflections
  • Configure renderer settings: tone mapping, color space, shadows, antialiasing, pixel ratio
  • Work with coordinate systems, layers, visibility, and frustum culling
  • Use Vector3, Matrix4, and Quaternion math utilities for transforms and calculations

How to install threejs-fundamentals

npx skills add https://github.com/cloudai-x/threejs-skills --skill threejs-fundamentals
Prerequisites
  • Three.js library (import * as THREE from 'three')
  • A canvas element or browser window for rendering
  • Basic JavaScript knowledge
Claude Code
Cursor
Windsurf
Cline

How to use threejs-fundamentals

  1. 1.Import THREE from the three.js library
  2. 2.Create a Scene, PerspectiveCamera (or other camera type), and WebGLRenderer
  3. 3.Set renderer size and append to DOM
  4. 4.Add geometries, materials, and meshes to the scene
  5. 5.Add lights (AmbientLight, DirectionalLight) for illumination
  6. 6.Position camera and set up animation loop with requestAnimationFrame
  7. 7.Handle window resize events to update camera aspect and renderer size

Use cases

Good for
  • Initialize a basic 3D scene with camera, renderer, and animated mesh
  • Set up multi-viewport rendering with ArrayCamera for split-screen or dashboard layouts
  • Create reflective surfaces using CubeCamera for environment mapping
  • Organize complex scenes with Group and Object3D hierarchy
  • Handle window resize and maintain camera aspect ratio
Who it's for
  • 3D web developers building scenes from scratch
  • Game developers setting up level or scene infrastructure
  • Visualization engineers creating interactive 3D dashboards
  • Anyone learning Three.js fundamentals

threejs-fundamentals FAQ

What's the difference between PerspectiveCamera and OrthographicCamera?

PerspectiveCamera simulates human vision with perspective distortion (objects farther away appear smaller). OrthographicCamera has no perspective distortion and is useful for 2D, isometric, or technical views.

When should I use a Group vs adding objects directly to the scene?

Use Group to organize related objects and apply transforms to multiple objects at once. For example, group wheels and body together to rotate a car as one unit.

What does updateProjectionMatrix() do?

Call this after changing camera properties like fov, aspect, near, or far. It recalculates the camera's projection matrix so changes take effect.

How do I handle high-DPI displays?

Set renderer pixel ratio with renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)) to match device resolution while capping at 2 for performance.

What's the right-handed coordinate system in Three.js?

+X points right, +Y points up, +Z points toward the viewer. Use AxesHelper to visualize: Red=X, Green=Y, Blue=Z.

Full instructions (SKILL.md)

Source of truth, from cloudai-x/threejs-skills.


name: threejs-fundamentals description: Three.js scene setup, cameras, renderer, Object3D hierarchy, coordinate systems. Use when setting up 3D scenes, creating cameras, configuring renderers, managing object hierarchies, or working with transforms.

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