Three.js Fundamentals: The Core Components
Every Three.js application, from a simple cube to a complex game, is built upon three core components: the Scene
, the Camera
, and the Renderer
. This section provides an interactive demonstration of these foundational elements.
Core Logic Explained
Below is the essential JavaScript code. The interactive buttons on the left directly call functions that modify the cube's properties.
{`
// 1. Scene: The container for all objects
const scene = new THREE.Scene();
scene.background = new THREE.Color(0xf0f0f0);
// 2. Camera: Defines the viewpoint
const camera = new THREE.PerspectiveCamera(
75, width / height, 0.1, 1000
);
camera.position.z = 5;
// 3. Renderer: Renders the scene
const renderer = new THREE.WebGLRenderer();
renderer.setSize(width, height);
container.appendChild(renderer.domElement);
// Add lighting
const ambientLight = new THREE.AmbientLight(0xffffff, 0.8);
scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
directionalLight.position.set(5, 5, 5);
scene.add(directionalLight);
// Create an object (Mesh = Geometry + Material)
const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshStandardMaterial({ color: 0x846c5b });
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
// Animation loop
function animate() {
requestAnimationFrame(animate);
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
renderer.render(scene, camera);
}
animate();
`}
Mastering Orbital Controls
OrbitControls
is a powerful tool that gives the user freedom to pan, zoom, and rotate the camera. This section explains the correct setup and provides a troubleshooting guide for common errors.
Correct Implementation
{`
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
// ... After setting up scene, camera, renderer
// Initialize controls
const controls = new OrbitControls(camera, renderer.domElement);
// CRITICAL: Update controls in the animation loop
function animate() {
requestAnimationFrame(animate);
controls.update();
renderer.render(scene, camera);
}
animate();
`}
Troubleshooting Common Errors
Cause: You have set controls.enableDamping = true
but forgotten to call controls.update()
inside your animation loop.
Fix: Add controls.update()
to your animate
function.
Cause: The OrbitControls script has not loaded correctly.
Fix: Check your browser's developer console (F12) for errors. Ensure the path to OrbitControls.js
in your import is correct.
Cause: The default polar angle allows for 360-degree vertical rotation.
Fix: Limit the vertical rotation by setting controls.maxPolarAngle = Math.PI / 2;
Importing 3D Models (.glb/.gltf)
Use the button below to upload a model from your computer. You can use the sliders to interactively adjust its position, rotation, and scale, which are the fundamental transformations you'll apply to any object in your scene.
Model Loader & Transformation Code
Loading models is an asynchronous operation. You use a specific loader and define a callback function that executes once the model has loaded successfully.
{`
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
const loader = new GLTFLoader();
loader.load(
'path/to/model.glb',
function (gltf) {
const model = gltf.scene;
// Transformations applied here
model.position.set(0, -1, 0);
model.scale.set(1.5, 1.5, 1.5);
scene.add(model);
},
undefined,
function (error) {
console.error('An error occurred while loading the model:', error);
}
);
`}
The Physics Engine: Cannon.js
Cannon.js handles the backend calculations for gravity, collisions, and forces, while you use its results to update the positions and rotations of your visible Three.js objects. Click "Drop Ball" to see the collision event.
Connecting Physics to Rendering
The core loop of a physics-based game involves two key steps: first, advance the physics world, then update the visual objects based on the new physics positions.
{`
import * as CANNON from 'cannon-es';
import * as CannonDebugRenderer from 'cannon-es-debugger';
const world = new CANNON.World({ gravity: new CANNON.Vec3(0, -9.82, 0) });
const cannonDebugRenderer = new CannonDebugRenderer.default(scene, world);
// Create the physical ground plane
const groundBody = new CANNON.Body({ mass: 0 });
groundBody.quaternion.setFromEuler(-Math.PI / 2, 0, 0);
world.addBody(groundBody);
// Update both in the animation loop
function animate() {
requestAnimationFrame(animate);
world.fixedStep();
cannonDebugRenderer.update();
// Sync each Three.js mesh with its Cannon.js body
renderer.render(scene, camera);
}
animate();
`}
Buoyancy and Density Simulation
In this simple demonstration, we've created a custom particle system to show the difference between wood and metal. Wood, being less dense, floats on the surface, while metal, being denser, continues to sink.
Simulation Logic
Here's a breakdown of the code that creates the simulation. The animation loop applies a different "physics" rule to each particle once it hits the water plane.
{`
// The main animation loop
function animate() {
requestAnimationFrame(animate);
// Update particle positions and apply physics
for (let i = 0; i < particleCount * 3; i += 3) {
const particleIndex = i / 3;
const currentY = positions[i + 1];
// If the particle is below the water surface
if (currentY < water.position.y) {
if (particleTypes[particleIndex] === 'wood') {
// Wood floats: Stop it from sinking further
positions[i + 1] = water.position.y;
velocities[i + 1] = 0;
} else if (particleTypes[particleIndex] === 'metal') {
// Metal sinks: Continue downward motion with some resistance
velocities[i + 1] *= 0.98;
}
} else {
// In freefall, apply simple gravity
velocities[i + 1] -= 0.005;
}
positions[i] += velocities[i];
positions[i + 1] += velocities[i + 1];
positions[i + 2] += velocities[i + 2];
}
particleGeometry.attributes.position.needsUpdate = true;
renderer.render(scene, camera);
}
animate();
`}
A Game Developer's Debugging Toolkit
When things go wrong in 3D, the errors can be cryptic. A blank screen is a common symptom for many different problems. Click on each error line to understand its likely cause and how to approach fixing it, turning you into a more effective problem solver.
Interactive Debug Console
Analysis & Fix
Click on an error in the console to see a detailed explanation and solution here.
The Easy Way: Google's <model-viewer>
For some use cases, like displaying a single product or a piece of art, a full Three.js scene is overkill. Google's <model-viewer>
is a web component that lets you declaratively add a 3D model to a webpage with minimal code. It's incredibly powerful for simple showcases, offering features like AR placement, animations, and camera controls right out of the box.
Live Example
When to Use Which?
Use <model-viewer>
when:
- You need to display a single, pre-made 3D model.
- The main goal is showcasing the model (e.g., e-commerce, portfolio).
- You want easy integration with Augmented Reality (AR).
- You prefer a declarative HTML approach over writing JavaScript.
Use Three.js
when:
- You are building a game or a highly interactive simulation.
- You need to manage multiple objects, physics, and complex logic.
- You require custom shaders, post-processing effects, or particle systems.
- You need full programmatic control over every aspect of the 3D scene.