šŸš€ LEVEL UP TO SENIOR:Unlock 500+ Advanced Practical Challenges & Exercises.
šŸŽ“ COURSERA PARTNER:Earn professional Google, Meta, and IBM certificates to supercharge your resume.
HTML MASTER CLASS /// LEARN TAGS /// BUILD STRUCTURE /// SEMANTIC WEB /// HTML MASTER CLASS /// LEARN TAGS ///

Animations in Three.js 3D WebGL

Learn how to use the animation loop in vanilla Three.js and the `useFrame` hook in React Three Fiber.

⚔ Total XP: 0|šŸ’» threejs XP: 0

Skill Matrix

UNLOCK NODES BY LEARNING NEW TAGS.

System Hub

Core Three.js animation concepts.

Quick Quiz //

What is the primary risk of forgetting to use delta time in your animation loop?


šŸš€ LEVEL UP TO SENIOR:Unlock 500+ Advanced Practical Challenges & Exercises.
šŸŽ“ COURSERA PARTNER:Earn professional Google, Meta, and IBM certificates to supercharge your resume.

Listen up. If you're building modern applications, understanding Animations in Three.js 3D WebGL is non-negotiable. This is where simple logic turns into intelligent behavior.

1Building the requestAnimationFrame Render Loop

Static scenes are boring! To make things move, we need a render loop. requestAnimationFrame is your best friend.

The animate() function calls itself every frame via requestAnimationFrame, which asks the browser to run your callback right before the next repaint — typically 60 times per second on standard displays, and it automatically throttles when the tab is inactive to save battery and CPU.

Unlike setInterval or setTimeout, requestAnimationFrame stays synchronized with the browser's actual repaint cycle, so you avoid tearing and wasted work when the page isn't visible. Each call to renderer.render(scene, camera) redraws the entire scene from the current camera's point of view, so the loop must keep running for anything to appear to move.

āœ•
—
+
function animate() {
  requestAnimationFrame(animate);
  renderer.render(scene, camera);
}
animate();
localhost:3000
Browser Preview
WebGL Output
3D Scene rendered. Objects: 4, Draw Calls: Optimized.

2Updating Object Properties Inside the Loop

Inside the render loop, you update properties like rotation before calling render.

Because animate() runs on every frame, incrementing cube.rotation.x and cube.rotation.y by a small fixed amount each pass produces a smooth, continuous spin — Three.js re-evaluates the object's world matrix from these Euler angles before the next render call.

Order matters here: you mutate the object's properties first, then call renderer.render, so the frame you see always reflects the latest state. Using a fixed increment like 0.01 ties rotation speed to frame rate, which is fine for a first demo but will spin faster on high-refresh-rate monitors — a limitation solved later with delta time.

āœ•
—
+
function animate() {
  cube.rotation.x += 0.01;
  cube.rotation.y += 0.01;
  renderer.render(scene, camera);
  requestAnimationFrame(animate);
}
localhost:3000
Browser Preview
WebGL Output
3D Scene rendered. Objects: 4, Draw Calls: Optimized.

3Animating in React Three Fiber with useFrame

If you are using React Three Fiber, you don't write requestAnimationFrame! You use the useFrame hook.

React Three Fiber already owns the render loop internally, so calling requestAnimationFrame yourself would fight the reconciler and likely double-render or desync from React's lifecycle. The useFrame hook lets you tap into that existing loop safely on every frame.

The callback receives state (camera, scene, clock, and more) and delta, the time in seconds since the last frame. Multiplying by delta instead of using a fixed increment keeps the animation speed consistent regardless of the viewer's frame rate. Mutating meshRef.current directly, rather than storing it in React state, avoids triggering a re-render for a value that changes 60 times a second.

āœ•
—
+
import { useFrame } from '@react-three/fiber';

useFrame((state, delta) => {
  meshRef.current.rotation.x += delta;
});
localhost:3000
Browser Preview
WebGL Output
3D Scene rendered. Objects: 4, Draw Calls: Optimized.

4Step-by-Step Breakdown

Static scenes are boring! To make things move, we need a render loop. requestAnimationFrame is your best friend.

Inside the render loop, you update properties like rotation before calling render.

Which browser API is used to synchronize your animation with the screen's refresh rate?

  • →setInterval
  • →setTimeout
  • →requestAnimationFrame

If you are using React Three Fiber, you don't write requestAnimationFrame! You use the useFrame hook.

Level Up šŸš€

Advanced cheat sheets, SEO tricks, and interview prep for this topic.

Browser Support

ChromeSupported

Fully supported.

FirefoxSupported

Fully supported.

SafariSupported

Fully supported.

EdgeSupported

Fully supported.

Accessibility (A11y)

1Reduced Motion Support

Continuous rotation and camera movement can trigger discomfort for users with vestibular disorders, so respect the prefers-reduced-motion media query and pause or slow animations when it's set.

if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) { // skip or simplify animation updates }

SEO Implications

  • 1

    Crawlability of Canvas Content

    A WebGL canvas is opaque to search engine crawlers, so none of the animated 3D content itself is indexable — keep essential text, product details, and navigation in real HTML alongside the canvas instead of relying on the animation to convey information.

Best Practices

Use Delta Time, Not Frame Count

Multiply movement and rotation by the delta time between frames (via THREE.Clock or useFrame's delta argument) so animation speed stays consistent across 30Hz, 60Hz, and 144Hz displays.

Cancel the Loop on Unmount

Store the ID returned by requestAnimationFrame and call cancelAnimationFrame when the component unmounts or the scene is disposed, otherwise the loop keeps running against a destroyed renderer and leaks memory.

Frequent Bugs

THE BUG

Animation speed varies wildly between devices.

THE FIX

This happens when rotation or position increments are hardcoded per frame instead of scaled by delta time; multiply by clock.getDelta() (or the delta argument in useFrame) so motion stays consistent regardless of frame rate.

Real-World Examples

Product Configurator Idle Spin

E-commerce 3D product viewers use a render loop like this to slowly auto-rotate a model when the user isn't interacting, pausing the loop the instant the user grabs OrbitControls so the two animations never fight over the same rotation property.

let idleSpin = true;
function animate() {
  requestAnimationFrame(animate);
  if (idleSpin) model.rotation.y += 0.002;
  renderer.render(scene, camera);
}

Interview Prep

?Frequently Asked Questions

Pascual Vila

Pascual Vila

Frontend Instructor // Code Syllabus

Common Pitfalls & Errors

The Error //

Not reading error messages carefully

Uncaught TypeError: Cannot read properties of undefined (reading 'length') // Solution: Ensure the variable you are calling .length on is initialized as a string or an array, not undefined.

The Solution //

Most of the time, the compiler or interpreter tells you exactly what line caused the crash and why. Read stack traces from the top down to identify the root cause.

The Error //

Hardcoding sensitive credentials

// Wrong const API_KEY = 'sk-123456789'; // Correct const API_KEY = process.env.API_KEY;

The Solution //

Never hardcode API keys, passwords, or secrets in your source code. Use environment variables (.env files) to keep them secure and out of version control.

Continue Learning