threlte logo
@threlte/extras

useGltfAnimations

A convenience hook to use gltf animations loaded by a <GLTF> component or by the useGltf hook.

<script lang="ts">
  import Scene from './Scene.svelte'
  import { Button, Folder, Pane } from 'svelte-tweakpane-ui'
  import { Canvas } from '@threlte/core'

  let scene = $state()
  let animating = $state(false)

  const actions = $derived(scene?.actions)
  const action = $derived($actions?.['Take 001'])

  // start animating as soon as the action is ready
  $effect(() => {
    action?.play()
    animating = true
  })
</script>

<Pane
  position="fixed"
  title="littlest tokyo"
>
  <Folder title="animation">
    <Button
      disabled={animating}
      on:click={() => {
        action?.play()
        animating = true
      }}
      title="play"
    />
    <Button
      disabled={!animating}
      on:click={() => {
        action?.stop()
        animating = false
      }}
      title="stop"
    />
    <Button
      disabled={!animating}
      on:click={() => {
        action?.reset()
      }}
      title="reset"
    />
  </Folder>
</Pane>

<div>
  <Canvas>
    <Scene bind:this={scene} />
  </Canvas>
</div>

<style>
  div {
    height: 100%;
  }
</style>
<script lang="ts">
  import { Environment, OrbitControls, useDraco, useGltf, useGltfAnimations } from '@threlte/extras'
  import { T } from '@threlte/core'

  const dracoLoader = useDraco()
  const gltf = useGltf('/models/LittlestTokyo.glb', { dracoLoader })
  export const { actions, mixer } = useGltfAnimations<'Take 001'>(gltf)
</script>

<T.PerspectiveCamera
  makeDefault
  position={[600, 200, -600]}
  near={10}
  far={10_000}
>
  <OrbitControls
    autoRotate
    autoRotateSpeed={0.2}
    enableDamping
    enableZoom={false}
    target={[-60, -75, 0]}
  />
</T.PerspectiveCamera>

<Environment
  url="/textures/equirectangular/hdr/industrial_sunset_puresky_1k.hdr"
  isBackground
/>

{#await gltf then { scene }}
  <T is={scene} />
{/await}

Model: Littlest Tokyo by Glen Fox, CC Attribution.

Examples

With the <GLTF> component

Without any arguments, useGltfAnimations returns a store which can be bound to the <GLTF> component.

<script lang="ts">
  import { GLTF, useGltfAnimations } from '@threlte/extras'

  const { gltf, actions, mixer } = useGltfAnimations<'All Animations'>()
  mixer.timeScale = 0.5

  export const triggerAnimation = () => {
    $actions['All Animations']?.play()
  }
</script>

<GLTF
  url="/path/to/model.glb"
  bind:gltf={$gltf}
/>

With the useGltf hook

For cases where you want to reuse parts of the GLTF such as materials, nodes, or the embedded camera, useGltfAnimations accepts a writable store as its first argument. useGltf returns a store that can be directly passed to useGltfAnimations.

<script lang="ts">
  import { T } from '@threlte/core'
  import { useGltfAnimations, useGltf } from '@threlte/extras'

  const gltf = useGltf('/path/to/model.glb')
  const { actions, mixer } = useGltfAnimations<'All Animations'>(gltf)

  $effect(() => {
    $actions['All Animations']?.play()
  })
</script>

{#await gltf then { scene }}
  <T is={scene} />
{/await}

Applying Animations to a Different Root

useGltfAnimations’s second optional argument allows you to apply the animations to a root other than the GLTF scene.

<script>
  import { useGltfAnimations, useGltf } from '@threlte/extras'
  import { Group } from 'three'

  const gltf = useGltf('/path/to/model.glb')

  const group = new Group()

  const { root } = useGltfAnimations(gltf, group)
  // $root === group
</script>

{#await gltf then { scene }}
  <T is={group}>
    <T is={scene} />
  </T>
{/await}

You can also set the root store without passing in the second argument

<script>
  import { useGltfAnimations, useGltf } from '@threlte/extras'
  import { Group } from 'three'

  const gltf = useGltf('/path/to/model.glb')

  const { root } = useGltfAnimations(gltf)
</script>

{#await gltf then { scene }}
  <T.Group bind:ref={$root}>
    <T is={scene} />
  </T.Group>
{/await}