Skip to content

Three.js - Animations

Static 3D scenes are interesting, but animation is what truly brings them to life. In Three.js, animation is achieved by creating a render loop that updates the scene and re-renders it on every frame, creating the illusion of motion.

The Render Loop with requestAnimationFrame

Section titled “The Render Loop with requestAnimationFrame”

The standard, browser-native way to create an efficient animation loop is with the window.requestAnimationFrame() method. It tells the browser that you wish to perform an animation and requests that the browser calls a specified function to update an animation before the next repaint. This is much more efficient than using setInterval, as it allows the browser to optimize rendering, pause animations in inactive tabs, and sync with the display’s refresh rate.

function tick() {
// Code to update objects in the scene goes here
// Render the scene
renderer.render(scene, camera);
// Schedule the next frame
window.requestAnimationFrame(tick);
}
// Start the loop
tick();

A common mistake is to update an object’s properties by a fixed amount on each frame, like cube.rotation.y += 0.01;. This creates a problem: the animation will run faster on high-refresh-rate monitors (e.g., 144Hz) and slower on standard ones (60Hz).

To solve this, we must use the time elapsed between frames to calculate our updates. Three.js provides a handy THREE.Clock utility for this.

import * as THREE from 'three';
const clock = new THREE.Clock();
function tick() {
// Get the time elapsed since the last frame
const deltaTime = clock.getDelta();
// Get the total time elapsed since the clock started
const elapsedTime = clock.getElapsedTime();
// Update object rotation based on elapsed time for smooth, consistent speed
// cube.rotation.y = elapsedTime * Math.PI; // Rotates 180 degrees per second
// Update object position in a circle
cube.position.x = Math.cos(elapsedTime) * 5;
cube.position.z = Math.sin(elapsedTime) * 5;
renderer.render(scene, camera);
window.requestAnimationFrame(tick);
}
tick();

Advanced Animation with Tweening Libraries

Section titled “Advanced Animation with Tweening Libraries”

For more complex, timeline-based animations (e.g., moving an object from point A to B over 2 seconds with a specific easing effect), a dedicated tweening library is invaluable. While the original tutorial used Tween.js, a highly popular and powerful industry standard is GSAP (GreenSock Animation Platform). It’s known for its performance, flexibility, and excellent documentation.

Let’s see how to integrate GSAP into our project.

npm install gsap

GSAP can directly animate the properties of any JavaScript object, including Three.js objects like mesh.position, mesh.rotation, or material.color.

import gsap from 'gsap';
// Animate the cube's Y position over 1 second
gsap.to(cube.position, { duration: 1, delay: 0.5, y: 5 });
// Animate back down with an elastic easing effect
gsap.to(cube.position, { duration: 1.5, delay: 1.5, y: 0, ease: 'elastic.out(1, 0.3)' });

One of GSAP’s key advantages is that you don’t need to call an update function inside your render loop. It manages its own internal ticker, making for very clean and declarative animation code.

  • Performance: Avoid creating new objects (new THREE.Vector3()) or performing heavy calculations inside the render loop. Do as much setup as possible outside the loop.
  • Debugging: Use console.log(elapsedTime) to check timing or the stats.js library to monitor your scene’s frame rate (FPS).
  • Control: Use libraries like GSAP for complex sequences, but leverage the tick function with THREE.Clock for continuous, procedural animations like waves, floating, or constant rotation.
  • User Experience: Always provide a way for users to pause or reduce motion if they are sensitive to it. Consider using the (prefers-reduced-motion) CSS media query to disable non-essential animations by default.

Mini-Project Idea: An Animated Solar System

Section titled “Mini-Project Idea: An Animated Solar System”

A great way to practice these concepts is to build a simple solar system.

  1. Create a sphere for the sun and several smaller spheres for planets.
  2. In the tick function, use elapsedTime with Math.sin() and Math.cos() to make the planets orbit the sun at different speeds and distances.
  3. Make the planets rotate on their own axes using planet.rotation.y = elapsedTime * speed;.
  4. Challenge: Use GSAP to animate the camera moving from a wide view to focus on a specific planet when the user clicks on it.