Every Three.js application has the same core structure: a Scene (the 3D world), a Camera (what you see), and a Renderer (draws it to the screen). You add objects to the scene, position the camera, and call renderer.render(scene, camera) every frame.
Objects in Three.js are called Meshes — a combination of Geometry (the shape) and Material (the surface appearance). In this tutorial, we use a BoxGeometry (cube shape) with a MeshStandardMaterial (physically-based surface that responds to light).
COMPLETE SOURCE CODE
Copy this code into an HTML file and open it in your browser to see it in action:
HTML / Three.js
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>My First Three.js Scene - Spinning Cube</title>
<style>
/* Remove default margins and fill the entire window */
body { margin: 0; overflow: hidden; }
canvas { display: block; }
</style>
</head>
<body>
<!-- Load Three.js from CDN -->
<script src="https://cdn.jsdelivr.net/npm/three@latest/build/three.min.js"></script>
<script>
// ============================================================
// STEP 1: CREATE THE SCENE
// The scene is the 3D world that holds all your objects
// ============================================================
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x1a1a2e); // Dark blue background
// ============================================================
// STEP 2: CREATE THE CAMERA
// The camera defines what the viewer sees
// PerspectiveCamera(fieldOfView, aspectRatio, 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.z = 4; // Move camera back so we can see the cube
// ============================================================
// STEP 3: CREATE THE RENDERER
// The renderer draws the scene to a canvas element
// ============================================================
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(window.devicePixelRatio);
document.body.appendChild(renderer.domElement); // Add canvas to page
// ============================================================
// STEP 4: CREATE A CUBE
// A Mesh = Geometry (shape) + Material (surface)
// ============================================================
const geometry = new THREE.BoxGeometry(1.5, 1.5, 1.5); // Width, Height, Depth
const material = new THREE.MeshStandardMaterial({
color: 0x00d4ff, // Cyan color
metalness: 0.3, // Slightly metallic
roughness: 0.4, // Slightly smooth
});
const cube = new THREE.Mesh(geometry, material);
scene.add(cube); // Add the cube to the scene
// ============================================================
// STEP 5: ADD LIGHTING
// Without light, MeshStandardMaterial appears black!
// ============================================================
// Point light - like a light bulb in 3D space
const pointLight = new THREE.PointLight(0xffffff, 1.5, 50);
pointLight.position.set(3, 3, 5); // Position the light
scene.add(pointLight);
// Ambient light - soft fill light everywhere (no shadows)
const ambientLight = new THREE.AmbientLight(0xffffff, 0.3);
scene.add(ambientLight);
// ============================================================
// STEP 6: ANIMATION LOOP
// This function runs ~60 times per second
// ============================================================
function animate() {
requestAnimationFrame(animate); // Schedule next frame
// Rotate the cube a little each frame
cube.rotation.x += 0.01; // Rotate around X axis
cube.rotation.y += 0.015; // Rotate around Y axis
// Draw the scene from the camera's perspective
renderer.render(scene, camera);
}
// ============================================================
// STEP 7: HANDLE WINDOW RESIZE
// Keep the scene looking correct if the window size changes
// ============================================================
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
// Start the animation!
animate();
</script>
</body>
</html>
Code Breakdown
Let's walk through each section of the code to understand exactly what's happening.
Step 1: The Scene
The THREE.Scene() is the container for everything in your 3D world. Think of it as an empty room — you'll add objects, lights, and cameras to it. The scene.background sets the color you see behind all objects (like painting the walls of the room).
Step 2: The Camera
PerspectiveCamera simulates how human eyes see — objects farther away appear smaller. The four parameters are:
Field of View (75°) — how wide the camera sees. Higher = more fisheye.
Aspect Ratio — width/height of the viewport. Keeps things from looking stretched.
Near (0.1) — anything closer than this is invisible.
Far (1000) — anything farther than this is invisible.
We position the camera at z = 4 so it's looking at the origin (0,0,0) where our cube will be, from 4 units away.
Step 3: The Renderer
WebGLRenderer creates a <canvas> element and uses WebGL to draw 3D graphics on it. antialias: true smooths jagged edges. We set it to fill the full window and append it to the page body.
Step 4: The Cube (Mesh)
A Mesh is the fundamental visible object in Three.js. It combines:
Geometry — the shape. BoxGeometry(1.5, 1.5, 1.5) creates a cube 1.5 units on each side.
Material — the surface. MeshStandardMaterial is physically-based, meaning it responds realistically to light. The metalness and roughness properties control how shiny it looks.
Step 5: Lighting
Without light, MeshStandardMaterial renders as pure black. We add two lights:
PointLight — emits light in all directions from a single point (like a light bulb). Creates highlights and shadows on the cube's faces.
AmbientLight — adds a soft, even light everywhere so no face is completely dark. Think of it as indirect light bouncing off walls.
Step 6: The Animation Loop
requestAnimationFrame(animate) tells the browser to call our animate function before the next screen repaint (~60 times per second). Each frame, we rotate the cube slightly and re-render. This creates smooth, continuous animation. Without this loop, you'd see a single static frame.
Step 7: Resize Handling
If the user resizes their browser window, we need to update the camera's aspect ratio and the renderer's size. Without this, the scene would look stretched or cropped after a resize.
Next Steps
Now that you have a working 3D scene, try these experiments:
Change the cube's color by modifying the color value (use hex like 0xff6600 for orange)
Replace BoxGeometry with SphereGeometry(1, 32, 32) to make a sphere
Add a second cube at a different position: cube2.position.set(3, 0, 0)
Make the rotation speed respond to mouse position
Try MeshBasicMaterial instead — it doesn't need light (flat color, no shading)