Animate

You can animate things in Three.js the same way you animate anything else — by changing numbers over time. Three.js has its own built-in animation system, but I tend to use the GSAP library instead. Here's why.
GSAP can animate any numeric property on any JavaScript object. Three.js objects are just JavaScript objects with numeric properties — so GSAP can animate them.
import gsap from 'gsap';
import * as THREE from 'three';
// GSAP can tween any object's numeric properties
const obj = { x: 0, y: 0 };
gsap.to(obj, { x: 10, y: 20, duration: 1 });
You apply this to Three.js objects the same way — you create a plain object as a "target," tween its properties, then apply them back to the Three.js object in an onUpdate callback.
animating position
You can't directly tween mesh.position because it's a Vector3 with methods, not plain properties — so you create a plain JS proxy object.
import gsap from 'gsap';
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
// Create a plain JS object to tween
const target = { x: 0, y: 0, z: 0 };
gsap.to(target, {
x: 5,
y: 3,
z: -2,
duration: 2,
ease: 'power2.out',
onUpdate: () => {
// this is where the magic happens
mesh.position.set(target.x, target.y, target.z);
},
});
animating rotation
rotation is a Euler, also not plain properties — same proxy approach:
const target = { x: 0, y: 0 };
gsap.to(target, {
x: Math.PI * 2, // full rotation
y: Math.PI,
duration: 3,
ease: 'power1.inOut',
onUpdate: () => {
mesh.rotation.x = target.x;
mesh.rotation.y = target.y;
},
});
animating ShaderMaterial uniforms
This is even simpler because uniforms are plain objects.
// overlayMaterial.uniforms.uAlpha is { value: 1.0 }
gsap.to(overlayMaterial.uniforms.uAlpha, {
duration: 3,
value: 0, // GSAP tweens the `.value` property directly
});
No onUpdate needed — GSAP tweens the uniform's value property directly, and Three.js picks it up on the next render.
animating color
Three.js Color objects work with GSAP's built-in color interpolation:
const material = new THREE.MeshStandardMaterial({ color: 0xff0000 });
gsap.to(material.color, {
r: 0, // 0-1 range
g: 0,
b: 1, // result: blue
duration: 1,
});
Or with hex strings (GSAP converts them):
gsap.to(material.color, {
value: '#00ff88',
duration: 1,
});
timelines — sequencing animations
You can sequence multiple Three.js animations with GSAP timelines:
const startPos = { x: player.position.x, z: player.position.z };
const timeline = gsap.timeline();
timeline.to(startPos, {
x: 5,
z: 3,
duration: 0.3,
ease: 'back.out',
onUpdate: () => {
player.position.x = startPos.x;
player.position.z = startPos.z;
},
});
// You can chain more tweens with .to(), .fromTo(), etc.
// timeline.to(...)
// timeline.fromTo(...)
good tips to know
- Always kill old tweens before creating new ones on the same target —
if (cameraTween) cameraTween.kill(); - Clean up when you navigate away —
gsap.killTweensOf('*');kills all GSAP tweens globally, preventing memory leaks. - Use
gsap.timeline()for multi-step sequences (move → rotate → fade) instead of nesting callbacks. - Eases work the same —
power2.out,back.out(1.7),elastic.out(1, 0.3)all apply to any numeric value.
