Examples
A small gallery of three.js patterns you can lift into your own project: a particle field, an orbit-controllable model viewer, an interactive raycaster, and a tween-driven camera path. Each is short, self-contained, and shows the API working together.
Particles, model viewer, raycasting, camera path
EXAMPLE
import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(innerWidth, innerHeight);
renderer.outputColorSpace = THREE.SRGBColorSpace;
renderer.toneMapping = THREE.ACESFilmicToneMapping;
document.body.appendChild(renderer.domElement);
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 0.1, 200);
camera.position.set(0, 1.5, 5);
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
// 1) Particle field — 10k points scattered in a sphere
const positions = new Float32Array(10_000 * 3);
for (let i = 0; i < positions.length; i++) positions[i] = (Math.random() - .5) * 20;
const particles = new THREE.Points(
new THREE.BufferGeometry().setAttribute('position', new THREE.BufferAttribute(positions, 3)),
new THREE.PointsMaterial({ size: 0.04, color: 0x88aaff, sizeAttenuation: true })
);
scene.add(particles);
// 2) Model viewer — drop a GLB into ./public/model.glb
const loader = new GLTFLoader();
let model;
loader.load('/model.glb', (gltf) => {
model = gltf.scene;
model.position.set(0, 0, 0);
scene.add(model);
}, undefined, (err) => console.error(err));
scene.add(new THREE.AmbientLight(0xffffff, 0.5));
const dir = new THREE.DirectionalLight(0xffffff, 1.2);
dir.position.set(5, 10, 5); scene.add(dir);
// 3) Raycaster — click-to-highlight any object under the cursor
const raycaster = new THREE.Raycaster();
const pointer = new THREE.Vector2();
let highlighted = null;
addEventListener('pointermove', (e) => {
pointer.x = (e.clientX / innerWidth) * 2 - 1;
pointer.y = -(e.clientY / innerHeight) * 2 + 1;
});
function updateHighlight() {
raycaster.setFromCamera(pointer, camera);
const hits = raycaster.intersectObjects(scene.children, true);
const hit = hits[0]?.object ?? null;
if (hit !== highlighted) {
if (highlighted) highlighted.material?.emissive?.setHex(0x000000);
highlighted = hit;
if (highlighted?.material?.emissive) highlighted.material.emissive.setHex(0x222288);
}
}
// 4) Camera path with a Catmull-Rom curve — smooth fly-through
const path = new THREE.CatmullRomCurve3([
new THREE.Vector3( 5, 2, 5),
new THREE.Vector3( 0, 3, -3),
new THREE.Vector3(-5, 2, 5),
new THREE.Vector3( 0, 6, 0),
], true);
let t = 0;
function flyThrough(dt) {
t = (t + dt * 0.04) % 1;
const p = path.getPointAt(t);
const look = path.getPointAt((t + 0.05) % 1);
camera.position.lerp(p, 0.05);
camera.lookAt(look);
}
let last = performance.now();
function flyButton() { /* toggle here */ }
renderer.setAnimationLoop((now) => {
const dt = (now - last) / 1000; last = now;
particles.rotation.y += dt * 0.05;
if (model) model.rotation.y += dt * 0.2;
updateHighlight();
// flyThrough(dt); // uncomment for camera path
controls.update();
renderer.render(scene, camera);
});
Why it matters
Set renderer.outputColorSpace = THREE.SRGBColorSpace and renderer.toneMapping = ACESFilmicToneMapping at startup. They make every material, light, and texture in three.js look correct out of the box; without them, lighting looks washed-out and exported PBR materials never look right.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Discussion
Loading…