Three.jsGSAPAnimationJavaScript

Three.js Animation: GSAP vs AnimationMixer

Choose between GSAP and Three.js AnimationMixer for model animation: use GSAP for interaction-driven part transforms and AnimationMixer for baked glTF clips.

·Updated ·12 min read·Counting...
Three.js Animation: GSAP vs AnimationMixer

In Three.js model animation, two approaches are easy to confuse. You can directly animate a model part with GSAP, or you can play animation clips that are already baked into the model file with AnimationMixer. Both can move things, but they fit different jobs.

Short rule:

  • Use GSAP for interaction-driven local movement.
  • Use AnimationMixer for complete animation clips from the model.

Designer and developer boundary

3D models may arrive in two forms:

  • The designer baked animations into the model, such as character actions, camera movement, or a mechanical sequence.
  • The model contains separate named parts, and the developer controls movement based on UI state or interaction.

Preserve that boundary. If the model already contains a complete animation clip, do not rebuild the whole thing with hand-written tweens unless there is a clear reason.

Use GSAP for local interactive parts

For a car door, drawer, knob, switch, or product part controlled by a button, GSAP is often direct and readable.

import gsap from "gsap"

let doorLF
let isDoorOpen = false

function toggleDoor() {
  if (!doorLF) return

  gsap.to(doorLF.rotation, {
    y: isDoorOpen ? 0 : Math.PI / 2,
    duration: 1,
    ease: "power2.out",
  })

  isDoorOpen = !isDoorOpen
}

The key step is getting the named object:

doorLF = car.getObjectByName("Door_LF")

The name must match the model structure. Ask the 3D designer to keep stable, meaningful object names.

Why GSAP works well here

GSAP is a good fit when animation follows:

  • button clicks
  • sliders
  • scroll progress
  • drag state
  • UI toggles
  • small part transforms

You can map interaction state directly to object rotation, position, or scale.

Use AnimationMixer for baked clips

If the glTF file includes complete animation tracks, use Three.js AnimationMixer:

import * as THREE from "three"

let mixer

loader.load("path/to/model.glb", (gltf) => {
  const model = gltf.scene
  scene.add(model)

  mixer = new THREE.AnimationMixer(model)

  const clip = gltf.animations[0]
  const action = mixer.clipAction(clip)

  action.play()
})

function animate() {
  requestAnimationFrame(animate)

  const delta = clock.getDelta()
  if (mixer) mixer.update(delta)

  renderer.render(scene, camera)
}

AnimationMixer keeps the animation data from the DCC tool. It is the right choice for complete authored timelines such as walk cycles, machinery sequences, character actions, and camera moves.

Controlling mixer actions

Actions can be paused, resumed, faded, or time-scaled:

action.paused = true
action.timeScale = 2
action.fadeIn(0.2)
action.fadeOut(0.2)

For scrubbed progress, you can also control action time carefully, but make sure mixer updates and state changes do not fight each other.

Choosing between them

Scenario Better fit
Click button to open one part GSAP
Scroll maps a product part from closed to open GSAP or hybrid
Play a designer-authored character action AnimationMixer
Pause, resume, or speed up a full model clip AnimationMixer
Combine UI state with a baked clip hybrid, with a clear ownership rule

Hybrid approaches are fine, but define ownership. Do not let GSAP and AnimationMixer both write to the same transform at the same time.

Common mistakes

  • Losing object names during export.
  • Rebuilding authored clips with code tweens.
  • Forgetting to call mixer.update(delta) in the render loop.
  • Using fixed 0.01 update steps instead of frame delta.
  • Letting two animation systems control the same object.
  • Treating animation as separate from loading, state, and interaction design.

Subscribe to FreeMac

Weekly picks: free Mac software reviews, trusted source updates, alternatives, and low-friction guides.