| name | threejs-fundamentals |
| description | Three.js scene setup, cameras, renderer, Object3D hierarchy, coordinate systems, transforms, and math utilities. Use when setting up 3D scenes, creating cameras, configuring renderers, managing object hierarchies, or working with Vector3, Matrix4, Quaternion, or colour math. Adapted from CloudAI-X/threejs-skills (MIT). |
| license | MIT |
| compatibility | Portable reference skill for agents that support markdown skills or prompt files. Works best alongside project Three.js source files and browser DevTools. |
| disable-model-invocation | true |
| metadata | {"owner":"game-delivery","version":"2.0.0","language":"en-GB","category":"web-rendering","upstream_references":["https://github.com/CloudAI-X/threejs-skills (MIT — see NOTICE.md)"],"tags":["three-js","scene","camera","renderer","object3d","transforms","math","fundamentals"],"intents":["scene-setup","camera-configuration","renderer-configuration","hierarchy-design","transform-maths","cleanup"],"output_types":["code-example","api-reference","configuration-snippet"]} |
Three.js Fundamentals
Quick Start
import * as THREE from 'three';
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);
const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshStandardMaterial({ : });
cube = .(geometry, material);
scene.(cube);
scene.( .(, ));
dirLight = .(, );
dirLight..(, , );
scene.(dirLight);
camera.. = ;
() {
(animate);
cube.. += ;
cube.. += ;
renderer.(scene, camera);
}
();
.(, {
camera. = . / .;
camera.();
renderer.(., .);
});
Core Classes
Scene
Container for all 3D objects, lights, and cameras.
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x000000);
scene.background = texture;
scene.background = cubeTexture;
scene.environment = envMap;
scene.fog = new THREE.Fog(0xffffff, 1, 100);
scene.fog = new THREE.FogExp2(0xffffff, 0.02);
Cameras
PerspectiveCamera — most common, simulates the human eye.
const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000,
);
camera.position.set(0, 5, 10);
camera.lookAt(0, 0, 0);
camera.updateProjectionMatrix();
OrthographicCamera — no perspective distortion; good for 2D / isometric.
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 (split-screen, minimap).
const cameras = [];
for (let i = 0; i < 4; i++) {
const sub = new THREE.PerspectiveCamera(40, 1, 0.1, 100);
sub.viewport = new THREE.Vector4(
Math.floor(i % 2) * 0.5,
Math.floor(i / 2) * 0.5,
0.5, 0.5,
);
cameras.push(sub);
}
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);
material.envMap = cubeRenderTarget.texture;
cubeCamera.position.copy(reflectiveMesh.position);
cubeCamera.update(renderer, scene);
WebGLRenderer
const renderer = new THREE.WebGLRenderer({
canvas: document.querySelector('#canvas'),
antialias: true,
alpha: true,
powerPreference: 'high-performance',
preserveDrawingBuffer: true,
});
renderer.setSize(width, height);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = 1.0;
renderer.outputColorSpace = THREE.SRGBColorSpace;
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
renderer.setClearColor(0x000000, 1);
renderer.render(scene, camera);
Object3D
Base class for all 3D objects — Mesh, Group, Light, Camera all extend Object3D.
const obj = new THREE.Object3D();
obj.position.set(x, y, z);
obj.rotation.set(x, y, z);
obj.quaternion.set(x, y, z, w);
obj.scale.set(x, y, z);
obj.getWorldPosition(targetVector);
obj.getWorldQuaternion(targetQuaternion);
obj.getWorldDirection(targetVector);
obj.add(child);
obj.remove(child);
obj.visible = false;
obj.layers.set(1);
obj.layers.enable(2);
obj.traverse(child => {
if (child.isMesh) child.material.color.set(0xff0000);
});
obj.matrixAutoUpdate = true;
obj.updateMatrix();
obj.updateMatrixWorld(true);
Group
Empty container for organising objects.
const group = new THREE.Group();
group.add(mesh1);
group.add(mesh2);
scene.add(group);
group.position.x = 5;
group.rotation.y = Math.PI / 4;
Mesh
Combines geometry and material.
const mesh = new THREE.Mesh(geometry, material);
const mesh = new THREE.Mesh(geometry, [mat1, mat2]);
mesh.castShadow = true;
mesh.receiveShadow = true;
mesh.frustumCulled = true;
mesh.renderOrder = 10;
Coordinate System
Three.js uses a right-handed coordinate system:
- +X → right
- +Y → up
- +Z → toward viewer (out of screen)
const axesHelper = new THREE.AxesHelper(5);
scene.add(axesHelper);
Math Utilities
Vector3
const v = new THREE.Vector3(x, y, z);
v.set(x, y, z);
v.copy(other);
v.clone();
v.add(v2);
v.sub(v2);
v.multiplyScalar(2);
v.divideScalar(2);
v.normalize();
v.negate();
v.clamp(min, max);
v.lerp(target, alpha);
v.length();
v.lengthSq();
v.distanceTo(v2);
v.dot(v2);
v.cross(v2);
v.angleTo(v2);
v.applyMatrix4(matrix);
v.applyQuaternion(q);
v.project(camera);
v.unproject(camera);
Matrix4
const m = new THREE.Matrix4();
m.identity();
m.copy(other);
m.makeTranslation(x, y, z);
m.makeRotationY(theta);
m.makeRotationFromQuaternion(q);
m.makeScale(x, y, z);
m.compose(position, quaternion, scale);
m.decompose(position, quaternion, scale);
m.multiply(m2);
m.premultiply(m2);
m.invert();
m.transpose();
m.makePerspective(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);
q.normalize();
q.invert();
Euler
const euler = new THREE.Euler(x, y, z, 'XYZ');
euler.setFromQuaternion(q);
euler.setFromRotationMatrix(m);
Color
const colour = new THREE.Color(0xff0000);
const colour = new THREE.Color('red');
const colour = new THREE.Color('#ff0000');
colour.setHex(0x00ff00);
colour.setRGB(r, g, b);
colour.setHSL(h, s, l);
colour.lerp(other, alpha);
colour.multiply(other);
colour.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 disposeMesh(mesh) {
mesh.geometry.dispose();
const mats = Array.isArray(mesh.material) ? mesh.material : [mesh.material];
mats.forEach(m => {
Object.values(m).forEach(v => v?.isTexture && v.dispose());
m.dispose();
});
scene.remove(mesh);
}
renderer.dispose();
Clock for Animation
const clock = new THREE.Clock();
function animate() {
const delta = clock.getDelta();
const elapsed = clock.getElapsedTime();
mesh.rotation.y += 0.5 * delta;
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
Responsive Canvas
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
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('Loading started');
manager.onLoad = () => console.log('All loaded');
manager.onProgress = (url, loaded, total) => console.log(`${loaded}/${total}`);
manager.onError = url => console.error(`Error: ${url}`);
const textureLoader = new THREE.TextureLoader(manager);
const gltfLoader = new GLTFLoader(manager);
Performance Tips
- Limit draw calls — merge static geometries, use instancing, atlas textures
- Frustum culling — enabled by default; ensure bounding boxes are correct
- LOD — use
THREE.LOD for distance-based mesh switching
- Object pooling — reuse objects instead of creating and destroying them
- Avoid
getWorldPosition in hot loops — cache results
const lod = new THREE.LOD();
lod.addLevel(highDetailMesh, 0);
lod.addLevel(medDetailMesh, 50);
lod.addLevel(lowDetailMesh, 100);
scene.add(lod);
import { mergeGeometries } from 'three/addons/utils/BufferGeometryUtils.js';
const merged = mergeGeometries([geo1, geo2, geo3]);
const mesh = new THREE.Mesh(merged, sharedMat);
See Also
threejs-geometry — geometry creation and manipulation
threejs-materials — material types and properties
threejs-lighting — light types and shadows
threejs-animation — AnimationMixer and keyframe animation
three-js-best-practices — performance rules and memory disposal