cloudai-x/threejs-skills/threejs-fundamentals

Trust tier 3
  • community
Source check pending

Checking that the SKILL.md file is still in the repository.

Three.js scene setup: scenes and fog, cameras, the renderer and tone mapping, the animation loop, object hierarchies, transforms, core math and cleanup, on WebGL or with WebGPU and TSL. Use when creating a scene from scratch, configuring a camera or renderer, handling resize, or arranging objects.

Install

npx vskill@latest install cloudai-x/threejs-skills/threejs-fundamentals

SKILL.md

Three.js Fundamentals

Targets three.js r186. Examples use WebGLRenderer; the WebGPU and TSL section uses WebGPURenderer.

Quick Start

javascript
import * as THREE from "three";
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x111318);
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 1.5, 5);
const cube = new THREE.Mesh(
new THREE.BoxGeometry(1, 1, 1),
new THREE.MeshStandardMaterial({ color: 0x3a86ff }),
);
scene.add(cube);
const light = new THREE.DirectionalLight(0xffffff, 2);
light.position.set(3, 4, 5); // shines from its position toward the origin
scene.add(light);
scene.add(new THREE.AmbientLight(0xffffff, 0.4));
const timer = new THREE.Timer();
timer.connect(document); // freezes the delta while the tab is hidden
function animate() {
timer.update(); // advance the timer first thing each frame
cube.rotation.y += timer.getDelta(); // seconds since the previous frame
renderer.render(scene, camera);
}
renderer.setAnimationLoop(animate);
window.addEventListener("resize", () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});

Without a bundler, the browser loads three.js as ES modules through an import map; this is the map for the WebGLRenderer examples above:

html
<script type="importmap">
{
"imports": {
"three": "https://cdn.jsdelivr.net/npm/three@0.186.0/build/three.module.js",
"three/addons/": "https://cdn.jsdelivr.net/npm/three@0.186.0/examples/jsm/"
}
}
</script>

For WebGPURenderer work, map all four keys — three.tsl.js imports three/webgpu, so the map fails without that entry:

html
<script type="importmap">
{
"imports": {
"three": "https://cdn.jsdelivr.net/npm/three@0.186.0/build/three.webgpu.js",
"three/webgpu": "https://cdn.jsdelivr.net/npm/three@0.186.0/build/three.webgpu.js",
"three/tsl": "https://cdn.jsdelivr.net/npm/three@0.186.0/build/three.tsl.js",
"three/addons/": "https://cdn.jsdelivr.net/npm/three@0.186.0/examples/jsm/"
}
}
</script>

Scene

javascript
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x111318); // solid color
scene.fog = new THREE.Fog(0x111318, 1, 100); // linear fog: near, far
scene.fog = new THREE.FogExp2(0x111318, 0.02); // exponential fog: density (pick one)
  • scene.background also accepts a CubeTexture, or a Texture whose mapping is THREE.EquirectangularReflectionMapping; a plain unmapped texture is drawn as a flat screen-filling quad, not a skybox.
  • scene.environment lights MeshStandardMaterial and MeshPhysicalMaterial on both renderers, and MeshLambertMaterial and MeshPhongMaterial on WebGLRenderer only. scene.environmentIntensity scales it — material.envMapIntensity scales only material.envMap. See threejs-loaders for HDR environments.

Cameras

PerspectiveCamera
javascript
// PerspectiveCamera(fov in degrees, aspect, near, far)
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 5, 10);
camera.lookAt(0, 0, 0); // rotates the camera to face the target
camera.fov = 60;
camera.updateProjectionMatrix(); // required after changing fov, aspect, near or far
OrthographicCamera
javascript
// OrthographicCamera(left, right, top, bottom, near, far); frustum size in world units
const aspect = window.innerWidth / window.innerHeight;
const frustumHeight = 10;
const camera = new THREE.OrthographicCamera(
(-frustumHeight * aspect) / 2,
(frustumHeight * aspect) / 2,
frustumHeight / 2,
-frustumHeight / 2,
0.1,
1000,
);
// on resize: recompute left and right from the new aspect, then call camera.updateProjectionMatrix()
ArrayCamera
javascript
// Renders the scene once per sub-camera, each into its own viewport.
// Viewport units under WebGLRenderer are drawing-buffer pixels (CSS size × pixel ratio);
// WebGPURenderer multiplies CSS pixels by the pixel ratio itself.
const width = window.innerWidth;
const height = window.innerHeight;
const dpr = renderer.getPixelRatio();
const cameras = [];
for (let i = 0; i < 4; i++) {
const subCamera = new THREE.PerspectiveCamera(75, width / height, 0.1, 100);
subCamera.viewport = new THREE.Vector4(
((i % 2) * width * dpr) / 2,
(Math.floor(i / 2) * height * dpr) / 2,
(width * dpr) / 2,
(height * dpr) / 2,
);
subCamera.position.set(-2 + (i % 2) * 4, 1.5, 6);
subCamera.lookAt(0, 0, 0);
subCamera.updateMatrixWorld(); // sub-cameras are not scene-graph children; update them when they move
cameras.push(subCamera);
}
const arrayCamera = new THREE.ArrayCamera(cameras);
arrayCamera.position.set(0, 1.5, 6); // WebGLRenderer culls every viewport with this camera's frustum (fov 50, aspect 1 by default); WebGPURenderer uses each sub-camera's frustum
renderer.render(scene, arrayCamera);
CubeCamera
javascript
const cubeRenderTarget = new THREE.WebGLCubeRenderTarget(256);
const cubeCamera = new THREE.CubeCamera(0.1, 1000, cubeRenderTarget);
scene.add(cubeCamera);
const chrome = new THREE.Mesh(
new THREE.SphereGeometry(1, 32, 16),
new THREE.MeshStandardMaterial({ color: 0xffffff, metalness: 1, roughness: 0 }),
);
chrome.material.envMap = cubeRenderTarget.texture;
scene.add(chrome);
// every frame: hide the reflective mesh, or it is drawn into the map it samples
chrome.visible = false;
cubeCamera.position.copy(chrome.position);
cubeCamera.update(renderer, scene);
chrome.visible = true;

With WebGPURenderer, build the target with CubeRenderTarget from three/webgpu instead of WebGLCubeRenderTarget.

WebGLRenderer

javascript
const renderer = new THREE.WebGLRenderer({
canvas: document.querySelector("#canvas"), // reuse an existing canvas; omit to create one
antialias: true, // MSAA
alpha: true, // let the page background show through the canvas
stencil: true, // stencil is off by default; pass this for stencil techniques
powerPreference: "high-performance",
preserveDrawingBuffer: true, // keep the drawing buffer for readPixels / screenshots
});
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)); // cap the fill rate on high-DPI displays
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.toneMapping = THREE.ACESFilmicToneMapping; // or AgXToneMapping, NeutralToneMapping; NoToneMapping is the default
renderer.toneMappingExposure = 1.0;
// renderer.outputColorSpace is SRGBColorSpace by default; leave it alone
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFShadowMap; // the default, already soft; VSMShadowMap and BasicShadowMap are the alternatives
renderer.setClearColor(0x000000, 0); // clear alpha 0 keeps the alpha: true canvas transparent
renderer.render(scene, camera);

Objects

Object3D

Base class of Mesh, Group, Light and Camera.

javascript
const obj = new THREE.Object3D();
const child = new THREE.Object3D();
obj.position.set(1, 2, 3); // translation
obj.rotation.set(0, Math.PI / 4, 0); // Euler angles in radians; kept in sync with quaternion
obj.quaternion.setFromAxisAngle(new THREE.Vector3(0, 1, 0), Math.PI / 4); // the same rotation as a quaternion
obj.scale.set(1, 1, 1);
obj.getWorldPosition(new THREE.Vector3()); // world-space queries take a target vector to write into
obj.getWorldQuaternion(new THREE.Quaternion());
obj.getWorldDirection(new THREE.Vector3()); // the +Z axis in world space; cameras return their view direction (−Z)
obj.add(child); // reparents child; also removes it from its previous parent
obj.attach(child); // reparents and keeps child's world transform; add() keeps the local one
obj.remove(child);
obj.visible = false; // hides the whole subtree
obj.layers.enable(1); // ADD layer 1; set(1) would replace the mask and leave layer 0
obj.layers.disable(1);
obj.layers.test(camera.layers); // true when the object shares a layer with the camera
obj.traverse((descendant) => {
if (descendant.isMesh) descendant.castShadow = true; // visits obj itself, then all descendants
});
// matrix updates run automatically while matrixAutoUpdate is true (the default)
obj.matrixAutoUpdate = false;
obj.updateMatrix(); // composes obj.matrix and flags the world matrix dirty
// when writing obj.matrix directly instead, flag the world matrix yourself:
obj.matrixWorldNeedsUpdate = true;
Group and Mesh
javascript
const group = new THREE.Group(); // an Object3D that exists to hold children
const ball = new THREE.Mesh(new THREE.SphereGeometry(0.5), new THREE.MeshStandardMaterial());
group.add(ball);
group.rotation.y = Math.PI / 4; // transforms the whole subtree
scene.add(group);
javascript
const mesh = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1), new THREE.MeshStandardMaterial());
scene.add(mesh);
// one material per geometry group; BoxGeometry has six (+x, −x, +y, −y, +z, −z)
// a group whose materialIndex has no material is not drawn
const faceColors = [0xff0000, 0x00ff00, 0x0000ff, 0xffff00, 0xff00ff, 0x00ffff];
const dice = new THREE.Mesh(
new THREE.BoxGeometry(1, 1, 1),
faceColors.map((color) => new THREE.MeshStandardMaterial({ color })),
);
mesh.castShadow = true; // this object casts shadows
mesh.receiveShadow = true; // this object shows shadows
mesh.frustumCulled = true; // default: skipped when its bounding sphere leaves the camera frustum
mesh.renderOrder = 10; // renders later within the same transparency bucket

Coordinate System

Right-handed, +Y up: +X right, +Y up, +Z toward the viewer. A camera at the origin with no rotation looks down −Z.

javascript
const axesHelper = new THREE.AxesHelper(5); // red X, green Y, blue Z; 5 = axis length
scene.add(axesHelper);

Math Utilities

Vector3
javascript
const a = new THREE.Vector3(1, 0, 0);
const b = new THREE.Vector3(0, 1, 0);
const c = a.clone(); // new vector with the same values
a.copy(b); // a becomes b
a.add(b); // in place: a = a + b
a.sub(b);
a.multiplyScalar(2);
a.divideScalar(2);
a.lerp(b, 0.5); // move halfway toward b
a.normalize(); // unit length
a.negate();
a.cross(b); // cross product, written into a (right-hand rule)
const len = a.length();
const lenSq = a.lengthSq(); // compare lengths without a square root
const dist = a.distanceTo(b);
const dot = a.dot(b); // > 0 same half-space, < 0 opposite, 0 perpendicular
const angle = a.angleTo(b); // radians
a.applyQuaternion(new THREE.Quaternion());
a.applyMatrix4(new THREE.Matrix4());
a.project(camera); // world position to normalized device coordinates
a.unproject(camera); // normalized device coordinates back to world
Matrix4, Quaternion, Euler
javascript
const m = new THREE.Matrix4();
m.identity();
m.makeTranslation(1, 2, 3);
m.makeRotationY(Math.PI / 4);
m.makeScale(2, 2, 2);
const position = new THREE.Vector3();
const quaternion = new THREE.Quaternion();
const scale = new THREE.Vector3(1, 1, 1);
m.compose(position, quaternion, scale); // build from transform components
m.decompose(position, quaternion, scale); // read them back
m.multiply(new THREE.Matrix4()); // m = m * other (other applied first)
m.premultiply(new THREE.Matrix4()); // m = other * m
m.invert();
m.lookAt(new THREE.Vector3(0, 0, 1), new THREE.Vector3(), new THREE.Vector3(0, 1, 0)); // eye, target, up
const q = new THREE.Quaternion();
q.setFromEuler(new THREE.Euler(0, Math.PI / 2, 0, "YXZ")); // Euler order matters, XYZ is the default
q.setFromAxisAngle(new THREE.Vector3(0, 1, 0), Math.PI / 4);
q.setFromRotationMatrix(m);
q.slerp(new THREE.Quaternion(), 0.5); // spherical interpolation, written into q
q.invert();
Color

This is the start of the file. Read the full SKILL.md on GitHub.

Security

  • Security scanPassed, 100 of 100Pattern checks on the skill's files.
  • Model reviewPassed

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

Repository stars
1,848
Repository forks
204
7-day trend score
38

Stars and forks are for the whole GitHub repository, not this skill alone. Refreshed 16 Mar 2026, 7 months ago.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/cloudai-x/threejs-skills/threejs-fundamentals/badge)](https://verified-skill.com/skills/cloudai-x/threejs-skills/threejs-fundamentals)

Something wrong with this skill or its page? Tell us.