Playing Built-in Animations
Load and play the built-in rooomAvatars animations on your avatar model in three.js or Babylon.js.
By the end, you have a built-in rooomAvatars animation playing on an exported avatar model in three.js or Babylon.js.
Prerequisites
- A rooomAvatars API token — see Authentication
- An avatar ID from a completed configurator session — see the Editor embedding guide
- A web project using either three.js or Babylon.js
How it works
Avatar models (GET /model/:id.glb) and built-in animations (GET /animations/:gender/:name.glb) share a Mixamo-compatible skeleton. The animation clip references bones by name, and matching bone names exist on the avatar model.
You have two ways to get animations onto your avatar:
- Bake them into the model download — pass the
animationsquery parameter onGET /model/:id.glb(e.g.?animations=locomotionor?animations=all). The animations ship inside the same GLB as the avatar — simplest if you always need the same clips. - Download animations separately (this tutorial) — request the avatar without baked animations (the default) and load each animation GLB on demand. Keeps the initial download small and lets you swap clips at runtime without re-downloading the avatar.
INFO
Always download the animation matching your avatar's gender (masculine or feminine). The skeletons differ slightly in proportions and joint orientation — mixing them produces visible distortion.
Steps
1. List available animations
Call GET /animations to discover which animation names ship with rooomAvatars:
curl "https://api.avatars.rooom.com/animations"The response is grouped by gender:
{
"status": "ok",
"data": {
"masculine": ["locomotion_idle", "locomotion_walk", "locomotion_run"],
"feminine": ["locomotion_idle", "locomotion_walk", "locomotion_run"]
}
}2. Play the animation
Pick one of the engine examples below.
import * as THREE from 'three';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
// AVATAR_ID is the registered avatar ID.
const loader = new GLTFLoader();
const [avatarGltf, animGltf] = await Promise.all([
loader.loadAsync(`https://api.avatars.rooom.com/model/${AVATAR_ID}.glb`),
loader.loadAsync('https://api.avatars.rooom.com/animations/masculine/locomotion_idle.glb'),
]);
scene.add(avatarGltf.scene);
const mixer = new THREE.AnimationMixer(avatarGltf.scene);
mixer.clipAction(animGltf.animations[0]).play();
const clock = new THREE.Clock();
renderer.setAnimationLoop(() => {
mixer.update(clock.getDelta());
renderer.render(scene, camera);
});import { SceneLoader } from '@babylonjs/core';
import '@babylonjs/loaders/glTF';
// AVATAR_ID is the registered avatar ID.
await SceneLoader.ImportMeshAsync('', 'https://api.avatars.rooom.com', `/model/${AVATAR_ID}.glb`, scene);
const animContainer = await SceneLoader.LoadAssetContainerAsync(
'https://api.avatars.rooom.com',
'/animations/masculine/locomotion_idle.glb',
scene,
);
const idle = animContainer.animationGroups[0].clone('idle', (source) =>
scene.getTransformNodeByName(source.name) ?? scene.getBoneByName(source.name),
);
animContainer.dispose();
idle.play(true);In three.js, create an AnimationMixer on the avatar scene and play the clip from the animation file. In Babylon.js, load the animation into an AssetContainer and clone the animation group with a target converter that maps source nodes or bones by name.
Troubleshooting
- If the avatar deforms during playback, verify that the animation gender matches the avatar gender.
- If no animation plays, check that the requested animation name exists in the
GET /animationsresponse. - For custom motion from Mixamo or other sources, see Using Mixamo Animations.