Create Smooth 3D Paths with Three.js CatmullRomCurve3

Generating smooth, curved trajectories through a set of coordinates is a common requirement in 3D web development, whether for camera animations or character movement. This article provides a step-by-step guide on how to use CatmullRomCurve3 in Three.js to define 3D waypoints, generate a smooth mathematical curve through them, and visualize or animate objects along that path.

1. Define Your Waypoints

The first step is to define an array of 3D coordinates using THREE.Vector3. These points act as the anchors that your smooth path must pass through.

import * as THREE from 'three';

const waypoints = [
    new THREE.Vector3(-10, 0, 10),
    new THREE.Vector3(-5, 5, 5),
    new THREE.Vector3(0, 0, 0),
    new THREE.Vector3(5, -5, -5),
    new THREE.Vector3(10, 0, -10)
];

2. Create the CatmullRomCurve3 Instance

Pass your array of waypoints into the THREE.CatmullRomCurve3 constructor. This class interpolates a smooth spline through all the specified points.

// Create the 3D curve
const curve = new THREE.CatmullRomCurve3(waypoints);

// Optional configurations:
// curve.closed = true; // Makes the path loop back to the start
// curve.curveType = 'centripetal'; // Options: 'centripetal', 'chordal', or 'catmullrom'

Using the default configuration is usually sufficient, but setting closed: true is ideal for continuous circular tracks or patrol paths.

3. Visualize the Curve

To see the generated path in your scene, you need to extract a series of detailed points from the curve, assign them to a geometry, and render them using a line material.

// Divide the curve into 50 segments to get 51 points for a smooth visual line
const points = curve.getPoints(50); 

// Create geometry from the points
const geometry = new THREE.BufferGeometry().setFromPoints(points);

// Create a material for the line
const material = new THREE.LineBasicMaterial({ color: 0xff0000 });

// Create the final object to add to the scene
const curveObject = new THREE.Line(geometry, material);
scene.add(curveObject);

4. Animate an Object Along the Path

To move a 3D object (like a camera or a mesh) smoothly along the generated path, use the .getPointAt(t) method in your animation loop. This method takes a value t between 0 (start of the curve) and 1 (end of the curve).

const mesh = new THREE.Mesh(
    new THREE.BoxGeometry(1, 1, 1),
    new THREE.MeshBasicMaterial({ color: 0x00ff00 })
);
scene.add(mesh);

let progress = 0; // Starts at 0 (0%) and goes to 1 (100%)
const speed = 0.002; // Controls the movement speed

function animate() {
    requestAnimationFrame(animate);

    // Increment progress and loop it
    progress += speed;
    if (progress > 1) progress = 0;

    // 1. Get the current position on the curve
    const position = curve.getPointAt(progress);
    mesh.position.copy(position);

    // 2. Optional: Make the object look ahead along the curve
    const lookAtTarget = curve.getPointAt(Math.min(progress + 0.01, 1));
    mesh.lookAt(lookAtTarget);

    renderer.render(scene, camera);
}

animate();