Skip to content
Documentation
Skill

/r3f-animation

React Three Fiber animation - useFrame, useAnimations, spring physics, keyframes. Use when animating objects, playing GLTF animations, creating procedural motion, or implementing physics-based movement.

From plugin
claude-code-guide
4.5k79 skills109 agents
Install
$ npx -y skills add zebbern/claude-code-guide --skill r3f-animation --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/r3f-animation

Context preview

The summary Claude sees to decide when to auto-load this skill.

React Three Fiber animation - useFrame, useAnimations, spring physics, keyframes. Use when animating objects, playing GLTF animations, creating procedural motion, or implementing physics-based movement.

SKILL.md

r3f-animation.SKILL.md
name: r3f-animation
description: React Three Fiber animation - useFrame, useAnimations, spring physics, keyframes. Use when animating objects, playing GLTF animations, creating procedural motion, or implementing physics-based movement.

React Three Fiber Animation

Quick Start

import { Canvas, useFrame } from '@react-three/fiber'
import { useRef } from 'react'

function RotatingBox() {
  const meshRef = useRef()

  useFrame((state, delta) => {
    meshRef.current.rotation.x += delta
    meshRef.current.rotation.y += delta * 0.5
  })

  return (
    <mesh ref={meshRef}>
      <boxGeometry />
      <meshStandardMaterial color="hotpink" />
    </mesh>
  )
}

export default function App() {
  return (
    <Canvas>
      <ambientLight />
      <RotatingBox />
    </Canvas>
  )
}

useFrame Hook

The core animation hook in R3F. Runs every frame.

Basic Usage

import { useFrame } from '@react-three/fiber'
import { useRef } from 'react'

function AnimatedMesh() {
  const meshRef = useRef()

  useFrame((state, delta) => {
    // state contains: clock, camera, scene, gl, mouse, etc.
    // delta is time since last frame in seconds

    meshRef.current.rotation.y += delta
  })

  return (
    <mesh ref={meshRef}>
      <boxGeometry />
      <meshStandardMaterial color="orange" />
    </mesh>
  )
}

State Object

useFrame((state, delta, xrFrame) => {
  const {
    clock,           // THREE.Clock
    camera,          // Current camera
    scene,           // Scene
    gl,              // WebGLRenderer
    mouse,           // Normalized mouse position (-1 to 1)
    pointer,         // Same as mouse
    viewport,        // Viewport dimensions
    size,            // Canvas size
    raycaster,       // Raycaster
    get,             // Get current state
    set,             // Set state
    invalidate,      // Request re-render (when frameloop="demand")
  } = state

  // Time-based animation
  const t = clock.getElapsedTime()
  meshRef.current.position.y = Math.sin(t) * 2
})

Render Priority

// Lower numbers run first. Default is 0.
// Use negative for pre-render, positive for post-render

function PreRender() {
  useFrame(() => {
    // Runs before main render
  }, -1)
}

function PostRender() {
  useFrame(() => {
    // Runs after main render
  }, 1)
}

function DefaultRender() {
  useFrame(() => {
    // Runs at default priority (0)
  })
}

Conditional Animation

function ConditionalAnimation({ isAnimating }) {
  const meshRef = useRef()

  useFrame((state, delta) => {
    if (!isAnimating) return
    meshRef.current.rotation.y += delta
  })

  return <mesh ref={meshRef}>...</mesh>
}

GLTF Animations with useAnimations

The recommended way to play animations from GLTF/GLB files.

Basic Usage

import { useGLTF, useAnimations } from '@react-three/drei'
import { useEffect, useRef } from 'react'

function AnimatedModel() {
  const group = useRef()
  const { scene, animations } = useGLTF('/models/character.glb')
  const { actions, names } = useAnimations(animations, group)

  useEffect(() => {
    // Play first animation
    actions[names[0]]?.play()
  }, [actions, names])

  return <primitive ref={group} object={scene} />
}

Animation Control

function Character() {
  const group = useRef()
  const { scene, animations } = useGLTF('/models/character.glb')
  const { actions, mixer } = useAnimations(animations, group)

  useEffect(() => {
    const action = actions['Walk']
    if (action) {
      // Playback control
      action.play()
      action.stop()
      action.reset()
      action.paused = true

      // Speed
      action.timeScale = 1.5  // 1.5x speed
      action.timeScale = -1   // Reverse

      // Loop modes
      action.loop = THREE.LoopOnce
      action.loop = THREE.LoopRepeat
      action.loop = THREE.LoopPingPong
      action.repetitions = 3
      action.clampWhenFinished = true

      // Weight (for blending)
      action.weight = 1
    }
  }, [actions])

  return <primitive ref={group} object={scene} />
}

Crossfade Between Animations

import { useGLTF, useAnimations } from '@react-three/drei'
import { useState, useEffect, useRef } from 'react'

function Character() {
  const group = useRef()
  const { scene, animations } = useGLTF('/models/character.glb')
  const { actions } = useAnimations(animations, group)
  const [currentAnim, setCurrentAnim] = useState('Idle')

  useEffect(() => {
    // Fade out all animations
    Object.values(actions).forEach(action => {
      action?.fadeOut(0.5)
    })

    // Fade in current animation
    actions[currentAnim]?.reset().fadeIn(0.5).play()
  }, [currentAnim, actions])

  return (
    <group ref={group}>
      <primitive object={scene} />
    </group>
  )
}

Animation Events

function AnimatedModel() {
  const group = useRef()
  const { scene, animations } = useGLTF('/models/character.glb')
  const { actions, mixer } = useAnimations(animations, group)

  useEffect(() => {
    // Listen for animation events
    const onFinished = (e) => {
      console.log('Animation finished:', e.action.getClip().name)
    }

    const onLoop = (e) => {
      console.log('Animation looped:', e.action.getClip().name)
    }

    mixer.addEventListener('finished', onFinished)
    mixer.addEventListener('loop', onLoop)

    return () => {
      mixer.removeEventListener('finished', onFinished)
      mixer.removeEventListener('loop', onLoop)
    }
  }, [mixer])

  return <primitive ref={group} object={scene} />
}

Animation Blending

function CharacterController({ speed = 0 }) {
  const group = useRef()
  const { scene, animations } = useGLTF('/models/character.glb')
  const { actions } = useAnimations(animations, group)

  useEffect(() => {
    // Start all animations
    actions['Idle']?.play()
    actions['Walk']?.play()
    actions['Run']?.play()
  }, [actions])

  // Blend based on speed
  useFrame(() =>
Read more
Ships withclaude-code-guide

Claude Code Guide - Setup, Commands, workflows, agents, skills & tips-n-tricks from beginner to power user!

Get the whole plugin

Other skills on claude-code-guide.