Real-world examples showing how to use EasyRig in different scenarios. Copy and adapt for your game.
Load a character model, apply animations, and integrate with a Three.js scene. The simplest possible setup to get started.
import * as THREE from 'three'
// basic scene setup
const scene = new THREE.Scene()
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000)
const renderer = new THREE.WebGLRenderer()
renderer.setSize(window.innerWidth, window.innerHeight)
document.body.appendChild(renderer.domElement)
// initialize EasyRig
EasyRig.init({ debug: true })
// load character
const player = await EasyRig.load('player', '/models/character.glb')
// load animations
await player.applyAnimation('/animations/idle.glb', 'idle')
await player.applyAnimation('/animations/walk.glb', 'walk')
// add to scene
scene.add(player.getModel())
// play idle
player.play('idle')
// render loop
const clock = new THREE.Clock()
function animate() {
requestAnimationFrame(animate)
const delta = clock.getDelta()
player.update(delta)
renderer.render(scene, camera)
}
animate()
Smooth transitions between idle, walk, and run based on player input. Shows how to handle multiple animations and crossfading.
// load all animations
await player.applyAnimation('/anims/idle.glb', 'idle')
await player.applyAnimation('/anims/walk.glb', 'walk')
await player.applyAnimation('/anims/run.glb', 'run')
await player.applyAnimation('/anims/jump.glb', 'jump')
let currentState = 'idle'
let velocity = 0
function updateAnimation() {
let targetState = 'idle'
if (keys.space && isGrounded) {
targetState = 'jump'
} else if (velocity > 5) {
targetState = 'run'
} else if (velocity > 0.1) {
targetState = 'walk'
}
if (targetState !== currentState) {
player.crossFade(targetState, 0.3)
currentState = targetState
}
}
function update(delta) {
// calculate velocity from input
velocity = Math.sqrt(moveX * moveX + moveZ * moveZ)
// update animation based on state
updateAnimation()
player.update(delta)
}
Enable physics ragdoll on character death or impact. Shows integration with Cannon.js physics engine.
import * as CANNON from 'cannon-es'
// setup physics world
const world = new CANNON.World()
world.gravity.set(0, -9.82, 0)
world.broadphase = new CANNON.SAPBroadphase(world)
world.solver.iterations = 10
// add ground plane
const groundShape = new CANNON.Plane()
const groundBody = new CANNON.Body({ mass: 0 })
groundBody.addShape(groundShape)
groundBody.quaternion.setFromEuler(-Math.PI / 2, 0, 0)
world.addBody(groundBody)
// enable ragdoll on character
player.enableRagdoll(world)
// activate on hit
function onCharacterHit(impactPoint, impactForce) {
// switch from animation to physics
player.activateRagdoll({
x: impactForce.x * 5,
y: impactForce.y * 5,
z: impactForce.z * 5
})
// apply extra force to specific bone
player.applyForce('Head', {
x: impactForce.x * 10,
y: impactForce.y * 10,
z: impactForce.z * 10
})
// recover after delay
setTimeout(() => {
player.deactivateRagdoll()
player.play('getup')
}, 3000)
}
// update loop
function update(delta) {
world.step(1 / 60, delta, 3)
player.update(delta)
}
Use IK for foot placement on uneven terrain and hand positions for grabbing objects. Shows practical IK usage.
// enable IK system
player.enableIK()
// foot placement on terrain
function updateFootPlacement() {
const model = player.getModel()
const pos = model.position
// get terrain height under each foot
const leftFootY = terrain.getHeightAt(pos.x - 0.1, pos.z)
const rightFootY = terrain.getHeightAt(pos.x + 0.1, pos.z)
// set IK targets
player.setIKTarget('leftLeg', [
pos.x - 0.1,
leftFootY,
pos.z
])
player.setIKTarget('rightLeg', [
pos.x + 0.1,
rightFootY,
pos.z
])
}
// hand IK for grabbing object
function grabObject(object) {
const targetPos = object.position
// move hand to object
player.setIKTarget('rightArm', [
targetPos.x,
targetPos.y,
targetPos.z
])
// attach object to hand bone
setTimeout(() => {
const handBone = player.getBone('RightHand')
handBone.add(object)
}, 500)
}
// update in render loop
function update(delta) {
updateFootPlacement()
player.update(delta)
}
Efficiently manage multiple characters in a scene using the clone feature. Shows performance optimization techniques.
// load base character with animations
const base = await EasyRig.load('base', '/models/npc.glb')
await base.applyAnimation('/anims/idle.glb', 'idle')
await base.applyAnimation('/anims/walk.glb', 'walk')
// clone for each NPC (shares animations)
const npcs = []
for (let i = 0; i < 50; i++) {
const npc = base.clone(`npc_${i}`)
// position
const model = npc.getModel()
model.position.set(
Math.random() * 100 - 50,
0,
Math.random() * 100 - 50
)
scene.add(model)
// random animation
npc.play(Math.random() > 0.5 ? 'idle' : 'walk')
npcs.push(npc)
}
// optimized update - only update visible characters
function update(delta) {
npcs.forEach(npc => {
const model = npc.getModel()
// distance culling
const dist = camera.position.distanceTo(model.position)
if (dist < 50) {
npc.update(delta)
} else {
// pause far away characters
npc.pause()
}
})
}