Skip to main content 홈 크리에이터 beko2210 firstbrain threejs-animation
threejs-animation Three.js animation - keyframe animation, skeletal animation, morph targets, animation mixing. Use when animating objects, playing GLTF animations, creating procedural motion, or blending animations.
설치로 이동 Skills Marketplace 커뮤니티가 만든 AI 스킬을 발견하고 탐색하세요.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/BEKO2210/Firstbrain --skill threejs-animation명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
Zip 다운로드 다운로드 중... name threejs-animation description Three.js animation - keyframe animation, skeletal animation, morph targets, animation mixing. Use when animating objects, playing GLTF animations, creating procedural motion, or blending animations. type skill created 2026-02-27T00:00:00.000Z domain design-creative category 3d-animation risk unknown source community tags ["skill","design-creative","3d-animation","threejs","animation"]
Three.js Animation
When to Use
You need to animate objects, rigs, morph targets, or imported GLTF animations in Three.js.
The task involves mixers, clips, keyframes, procedural motion, or animation blending.
You are building motion behavior in a Three.js scene rather than just static rendering.
Quick Start
import * as THREE from "three" ;
const timer = new THREE .Timer ();
renderer.setAnimationLoop (() => {
timer.update ();
const delta = timer.getDelta ();
const elapsed = timer.getElapsed ();
mesh.rotation .y += delta;
mesh.position .y = Math .sin (elapsed) * 0.5 ;
renderer.render (scene, camera);
});
Note: THREE.Timer is recommended over THREE.Clock as of r183. Timer pauses when the page is hidden and has a cleaner API. THREE.Clock still works but is considered legacy.
Animation System Overview
Three.js animation system has three main components:
AnimationClip - Container for keyframe data
AnimationMixer - Plays animations on a root object
AnimationAction - Controls playback of a clip
AnimationClip
Stores keyframe animation data.
const times = [ , , ];
values = [ , , ];
track = . (
,
times,
values,
);
clip = . ( , , [track]);
0
1
2
const
0
1
0
const
new
THREE
NumberKeyframeTrack
".position[y]"
const
new
THREE
AnimationClip
"bounce"
2
KeyframeTrack Types
new THREE .NumberKeyframeTrack (".opacity" , times, [1 , 0 ]);
new THREE .NumberKeyframeTrack (".material.opacity" , times, [1 , 0 ]);
new THREE .VectorKeyframeTrack (".position" , times, [
0 ,
0 ,
0 ,
1 ,
2 ,
0 ,
0 ,
0 ,
0 ,
]);
const q1 = new THREE .Quaternion ().setFromEuler (new THREE .Euler (0 , 0 , 0 ));
const q2 = new THREE .Quaternion ().setFromEuler (new THREE .Euler (0 , Math .PI , 0 ));
new THREE .QuaternionKeyframeTrack (
".quaternion" ,
[0 , 1 ],
[q1.x , q1.y , q1.z , q1.w , q2.x , q2.y , q2.z , q2.w ],
);
new THREE .ColorKeyframeTrack (".material.color" , times, [
1 ,
0 ,
0 ,
0 ,
1 ,
0 ,
0 ,
0 ,
1 ,
]);
new THREE .BooleanKeyframeTrack (".visible" , [0 , 0.5 , 1 ], [true , false , true ]);
new THREE .StringKeyframeTrack (
".morphTargetInfluences[smile]" ,
[0 , 1 ],
["0" , "1" ],
);
Interpolation Modes const track = new THREE .VectorKeyframeTrack (".position" , times, values);
track.setInterpolation (THREE .InterpolateLinear );
track.setInterpolation (THREE .InterpolateSmooth );
track.setInterpolation (THREE .InterpolateDiscrete );
BezierInterpolant (r183) Three.js r183 adds THREE.BezierInterpolant for bezier curve interpolation in keyframe tracks, enabling smoother animation curves with tangent control.
AnimationMixer Plays animations on an object and its descendants.
const mixer = new THREE .AnimationMixer (model);
const action = mixer.clipAction (clip);
action.play ();
function animate ( ) {
const delta = clock.getDelta ();
mixer.update (delta);
requestAnimationFrame (animate);
renderer.render (scene, camera);
}
Mixer Events mixer.addEventListener ("finished" , (e ) => {
console .log ("Animation finished:" , e.action .getClip ().name );
});
mixer.addEventListener ("loop" , (e ) => {
console .log ("Animation looped:" , e.action .getClip ().name );
});
AnimationAction Controls playback of an animation clip.
const action = mixer.clipAction (clip);
action.play ();
action.stop ();
action.reset ();
action.halt (fadeOutDuration);
action.isRunning ();
action.isScheduled ();
action.time = 0.5 ;
action.timeScale = 1 ;
action.paused = false ;
action.weight = 1 ;
action.setEffectiveWeight (1 );
action.loop = THREE .LoopRepeat ;
action.loop = THREE .LoopOnce ;
action.loop = THREE .LoopPingPong ;
action.repetitions = 3 ;
action.clampWhenFinished = true ;
action.blendMode = THREE .NormalAnimationBlendMode ;
action.blendMode = THREE .AdditiveAnimationBlendMode ;
Fade In/Out
action.reset ().fadeIn (0.5 ).play ();
action.fadeOut (0.5 );
const action1 = mixer.clipAction (clip1);
const action2 = mixer.clipAction (clip2);
action1.play ();
action1.crossFadeTo (action2, 0.5 , true );
action2.play ();
Loading GLTF Animations Most common source of skeletal animations.
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js" ;
const loader = new GLTFLoader ();
loader.load ("model.glb" , (gltf ) => {
const model = gltf.scene ;
scene.add (model);
const mixer = new THREE .AnimationMixer (model);
const clips = gltf.animations ;
console .log (
"Available animations:" ,
clips.map ((c ) => c.name ),
);
if (clips.length > 0 ) {
const action = mixer.clipAction (clips[0 ]);
action.play ();
}
const walkClip = THREE .AnimationClip .findByName (clips, "Walk" );
if (walkClip) {
mixer.clipAction (walkClip).play ();
}
window .mixer = mixer;
});
function animate ( ) {
const delta = clock.getDelta ();
if (window .mixer ) window .mixer .update (delta);
requestAnimationFrame (animate);
renderer.render (scene, camera);
}
Skeletal Animation
Skeleton and Bones
const skinnedMesh = model.getObjectByProperty ("type" , "SkinnedMesh" );
const skeleton = skinnedMesh.skeleton ;
skeleton.bones .forEach ((bone ) => {
console .log (bone.name , bone.position , bone.rotation );
});
const headBone = skeleton.bones .find ((b ) => b.name === "Head" );
if (headBone) headBone.rotation .y = Math .PI / 4 ;
const helper = new THREE .SkeletonHelper (model);
scene.add (helper);
Programmatic Bone Animation function animate ( ) {
const time = clock.getElapsedTime ();
const headBone = skeleton.bones .find ((b ) => b.name === "Head" );
if (headBone) {
headBone.rotation .y = Math .sin (time) * 0.3 ;
}
mixer.update (clock.getDelta ());
}
Bone Attachments
const weapon = new THREE .Mesh (weaponGeometry, weaponMaterial);
const handBone = skeleton.bones .find ((b ) => b.name === "RightHand" );
if (handBone) handBone.add (weapon);
weapon.position .set (0 , 0 , 0.5 );
weapon.rotation .set (0 , Math .PI / 2 , 0 );
Morph Targets Blend between different mesh shapes.
const geometry = mesh.geometry ;
console .log ("Morph attributes:" , Object .keys (geometry.morphAttributes ));
mesh.morphTargetInfluences ;
mesh.morphTargetDictionary ;
mesh.morphTargetInfluences [0 ] = 0.5 ;
const smileIndex = mesh.morphTargetDictionary ["smile" ];
mesh.morphTargetInfluences [smileIndex] = 1 ;
Animating Morph Targets
function animate ( ) {
const t = clock.getElapsedTime ();
mesh.morphTargetInfluences [0 ] = (Math .sin (t) + 1 ) / 2 ;
}
const track = new THREE .NumberKeyframeTrack (
".morphTargetInfluences[smile]" ,
[0 , 0.5 , 1 ],
[0 , 1 , 0 ],
);
const clip = new THREE .AnimationClip ("smile" , 1 , [track]);
mixer.clipAction (clip).play ();
Animation Blending Mix multiple animations together.
const idleAction = mixer.clipAction (idleClip);
const walkAction = mixer.clipAction (walkClip);
const runAction = mixer.clipAction (runClip);
idleAction.play ();
walkAction.play ();
runAction.play ();
idleAction.setEffectiveWeight (1 );
walkAction.setEffectiveWeight (0 );
runAction.setEffectiveWeight (0 );
function updateAnimations (speed ) {
if (speed < 0.1 ) {
idleAction.setEffectiveWeight (1 );
walkAction.setEffectiveWeight (0 );
runAction.setEffectiveWeight (0 );
} else if (speed < 5 ) {
const t = speed / 5 ;
idleAction.setEffectiveWeight (1 - t);
walkAction.setEffectiveWeight (t);
runAction.setEffectiveWeight (0 );
} else {
const t = Math .min ((speed - 5 ) / 5 , 1 );
idleAction.setEffectiveWeight (0 );
walkAction.setEffectiveWeight (1 - t);
runAction.setEffectiveWeight (t);
}
}
Additive Blending
const baseAction = mixer.clipAction (baseClip);
baseAction.play ();
const additiveAction = mixer.clipAction (additiveClip);
additiveAction.blendMode = THREE .AdditiveAnimationBlendMode ;
additiveAction.play ();
THREE .AnimationUtils .makeClipAdditive (additiveClip);
Animation Utilities import * as THREE from "three" ;
const clip = THREE .AnimationClip .findByName (clips, "Walk" );
const subclip = THREE .AnimationUtils .subclip (clip, "subclip" , 0 , 30 , 30 );
THREE .AnimationUtils .makeClipAdditive (clip);
THREE .AnimationUtils .makeClipAdditive (clip, 0 , referenceClip);
const clone = clip.clone ();
clip.duration ;
clip.optimize ();
clip.resetDuration ();
Procedural Animation Patterns
Smooth Damping
const target = new THREE .Vector3 ();
const current = new THREE .Vector3 ();
const velocity = new THREE .Vector3 ();
function smoothDamp (current, target, velocity, smoothTime, deltaTime ) {
const omega = 2 / smoothTime;
const x = omega * deltaTime;
const exp = 1 / (1 + x + 0.48 * x * x + 0.235 * x * x * x);
const change = current.clone ().sub (target);
const temp = velocity
.clone ()
.add (change.clone ().multiplyScalar (omega))
.multiplyScalar (deltaTime);
velocity.sub (temp.clone ().multiplyScalar (omega)).multiplyScalar (exp);
return target.clone ().add (change.add (temp).multiplyScalar (exp));
}
function animate ( ) {
current.copy (smoothDamp (current, target, velocity, 0.3 , delta));
mesh.position .copy (current);
}
Spring Physics class Spring {
constructor (stiffness = 100 , damping = 10 ) {
this .stiffness = stiffness;
this .damping = damping;
this .position = 0 ;
this .velocity = 0 ;
this .target = 0 ;
}
update (dt ) {
const force = -this .stiffness * (this .position - this .target );
const dampingForce = -this .damping * this .velocity ;
this .velocity += (force + dampingForce) * dt;
this .position += this .velocity * dt;
return this .position ;
}
}
const spring = new Spring (100 , 10 );
spring.target = 1 ;
function animate ( ) {
mesh.position .y = spring.update (delta);
}
Oscillation function animate ( ) {
const t = clock.getElapsedTime ();
mesh.position .y = Math .sin (t * 2 ) * 0.5 ;
mesh.position .y = Math .abs (Math .sin (t * 3 )) * 2 ;
mesh.position .x = Math .cos (t) * 2 ;
mesh.position .z = Math .sin (t) * 2 ;
mesh.position .x = Math .sin (t) * 2 ;
mesh.position .z = Math .sin (t * 2 ) * 1 ;
}
Performance Tips
Share clips : Same AnimationClip can be used on multiple mixers
Optimize clips : Call clip.optimize() to remove redundant keyframes
Disable when off-screen : Stop mixer updates for invisible objects
Use LOD for animations : Simpler rigs for distant characters
Limit active mixers : Each mixer.update() has a cost
mesh.onBeforeRender = () => {
action.paused = false ;
};
mesh.onAfterRender = () => {
if (!isInFrustum (mesh)) {
action.paused = true ;
}
};
const clipCache = new Map ();
function getClip (name ) {
if (!clipCache.has (name)) {
clipCache.set (name, loadClip (name));
}
return clipCache.get (name);
}
See Also
threejs-loaders - Loading animated GLTF models
threejs-fundamentals - Clock and animation loop
threejs-shaders - Vertex animation in shaders
Connections
Domain: [[Design & Kreativitaet]]
Kategorie: [[3D & Animation]]
Navigation: [[Skills Uebersicht]], [[Home]]