---
date: 2026-6-20
---

<!-- markdownlint-disable MD001 -->

# Animate

![gsap example](https://media3.giphy.com/media/v1.Y2lkPTc5MGI3NjExZGMyN3U3c3lhOTZiYmI1bGp4M3g3cWNmcHBjMzNteDAzdHoybmt4aiZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/SbpNmgAOYJRnoOcPtm/giphy.gif)

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](https://gsap.com) 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.

```javascript
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.

```javascript
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:

```javascript
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.

```javascript
// 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:

```javascript
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):

```javascript
gsap.to(material.color, {
    value: '#00ff88',
    duration: 1,
});
```

### timelines — sequencing animations

You can sequence multiple Three.js animations with GSAP timelines:

```javascript
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.
