threejs-fundamentals

Essential Three.js scene setup, cameras, renderers, and object hierarchies for 3D web graphics. Covers scene creation, three camera types (Perspective, Orthographic, Array), renderer configuration with tone mapping and shadow support, and the Object3D hierarchy system Includes right-handed coordinate system, transform operations (position, rotation, scale, quaternions), and local vs. world space conversions Provides math utilities for Vector3, Matrix4, Quaternion, Euler angles, and Color with common operations like lerp, normalize, and matrix composition Demonstrates responsive canvas handling, animation loops with delta time, proper resource cleanup, and performance optimization patterns like frustum culling and LOD

INSTALLATION
npx skills add https://github.com/cloudai-x/threejs-skills --skill threejs-fundamentals
Run in your project or agent environment. Adjust flags if your CLI version differs.

SKILL.md

$2a

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

- **Limit draw calls**: Merge geometries, use instancing, atlas textures

- **Frustum culling**: Enabled by default, ensure bounding boxes are correct

- **LOD (Level of Detail)**: Use `THREE.LOD` for distance-based mesh switching

- **Object pooling**: Reuse objects instead of creating/destroying

- **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);

BrowserAct

Let your agent run on any real-world website

Bypass CAPTCHA & anti-bot for free. Start local, scale to cloud.

Explore BrowserAct Skills →

Stop writing automation&scrapers

Install the CLI. Run your first Skill in 30 seconds. Scale when you're ready.

Start free
free · no credit card