# Create Skeletal Animation using Bones

**URL:** <https://discourse.threejs.org/t/create-skeletal-animation-using-bones/13789>\
**Category:** Questions\
**Tags:** skeleton, bones, animation, skeletal-animation\
**Created:** [March 24, 2020, 9:18pm UTC](https://discourse.threejs.org/t/create-skeletal-animation-using-bones/13789 "2020-03-24T21:18:44Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![soham0243](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/soham0243/32/7560_2.png) [@soham0243](https://discourse.threejs.org/u/soham0243)\
**Post date:** [March 24, 2020, 9:18pm UTC](https://discourse.threejs.org/t/create-skeletal-animation-using-bones/13789/1 "2020-03-24T21:18:44Z")

</div>

Hello, I have a model with skeleton but no animation. Instead of creating animation in blender I want to create it directly on three.js ( by rotating bones ).  
For example, I want some bones to rotate like following  
`function rotate_1(){ skeleton.bones[index1].rotate.x += 0.1; skeleton.bones[index2].rotate.y += 0.3; } //similarly, rotate_2(), rotate_3() .... // rotate_1() for 3 seconds, rotate_2() for 5 .... // I want relative timing so that I will able to adjust the speed of animation`  
And then I want to pass it to someting like gltf.animation[0, 1, 2…]. After that either it will be

1. Exported as GLTF  
or,
2. Directly appied to the page  
` mixer.clipAction(gltf.animation[index]); //later action.play();`  
Is it possible in three.js? (By any means)  
Alternatively, if I want to manipulate gltf.animations[index] then is there any way to do this ?

---

<div class="post-metadata">

**Author:** ![Mugen87](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/mugen87/32/5645_2.png) [@Mugen87](https://discourse.threejs.org/u/Mugen87)\
**Post date:** [March 25, 2020, 9:35am UTC](https://discourse.threejs.org/t/create-skeletal-animation-using-bones/13789/2 "2020-03-25T09:35:39Z")

</div>

> [@soham0243](#):
>
> Is it possible in three.js?

Yes, but that means you have to author the keyframe tracks and animation clips by yourself. The problem is that the respective API is not intended for this use case so you really have to know what you are doing.

It’s still best to author animation data in a DCC tool like Blender and then export to `glTF`.

---

<div class="post-metadata">

**Author:** ![soham0243](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/soham0243/32/7560_2.png) [@soham0243](https://discourse.threejs.org/u/soham0243)\
**Post date:** [March 25, 2020, 10:47am UTC](https://discourse.threejs.org/t/create-skeletal-animation-using-bones/13789/3 "2020-03-25T10:47:55Z")

</div>

I’ve found the following in official documentation.  
[Keyframe Track in three.js](https://threejs.org/docs/#api/en/animation/KeyframeTrack)  
But I haven’t found any example of this. Is there any documentation regarding this in anywhere like [threejsfundamentals.org](http://threejsfundamentals.org) ?

---

<div class="post-metadata">

**Author:** ![Mugen87](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/mugen87/32/5645_2.png) [@Mugen87](https://discourse.threejs.org/u/Mugen87)\
**Post date:** [March 25, 2020, 11:21am UTC](https://discourse.threejs.org/t/create-skeletal-animation-using-bones/13789/4 "2020-03-25T11:21:03Z")

</div>

> [@soham0243](#):
>
> Is there any documentation regarding this in anywhere like [threejsfundamentals.org](http://threejsfundamentals.org) ?

No, there is only a small example that shows the setup of some simple tracks:

[https://threejs.org/examples/misc\_animation\_keys](https://threejs.org/examples/misc_animation_keys)

---

<div class="post-metadata">

**Author:** ![soham0243](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/soham0243/32/7560_2.png) [@soham0243](https://discourse.threejs.org/u/soham0243)\
**Post date:** [March 25, 2020, 9:38pm UTC](https://discourse.threejs.org/t/create-skeletal-animation-using-bones/13789/5 "2020-03-25T21:38:28Z")

</div>

I modified the example like following. I want to load a custom model and move the mesh. But it is not working. It is showing something like `root is undefined`

 ![cc](https://canada1.discourse-cdn.com/flex035/uploads/threejs/original/2X/3/3aff4889bf2abc58cebf1756f28304bd33e1bbdd.png)

```
<!DOCTYPE html>
<html lang="en">
	<head>
		<title>three.js webgl - animation - basic</title>
		<meta charset="utf-8">
		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
		<link type="text/css" rel="stylesheet" href="main.css">
	</head>
	<body>

		<div id="info">
			<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> webgl - animation - basic use
		</div><script src="js/three.js"></script><script src="js/stats.min.js"></script>
		<script src="js/GLTFLoader.js"></script>

		<script>

			var stats, clock;
			var scene, camera, renderer, mixer;

			init();
			animate();

			function init() {

				scene = new THREE.Scene();

				//

				camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 1, 1000 );
				camera.position.set( 50, 50, 100 );
				camera.lookAt( scene.position );

				//

				var axesHelper = new THREE.AxesHelper( 10 );
				scene.add( axesHelper );

				scene.add(new THREE.AmbientLight(0xffffff,1));

				//

				//var geometry = new THREE.BoxBufferGeometry( 5, 5, 5 );
				//var material = new THREE.MeshBasicMaterial( { color: 0xffffff, transparent: true } );
				//var mesh = new THREE.Mesh( geometry, material );
				//scene.add( mesh );
				var mesh;
				var loader = new THREE.GLTFLoader();
				loader.load("models/Soldier.glb",function(gltf){
					mesh = gltf.scene;
					mesh.position.set(0,0,0);
					scene.add(mesh);
				});

				// create a keyframe track (i.e. a timed sequence of keyframes) for each animated property
				// Note: the keyframe track type should correspond to the type of the property being animated

				// POSITION
				var positionKF = new THREE.VectorKeyframeTrack( '.position', [0, 1, 2], [0, 0, 0, 30, 0, 0, 0, 0, 0] );

				// SCALE
				var scaleKF = new THREE.VectorKeyframeTrack( '.scale', [0, 1, 2], [1, 1, 1, 2, 2, 2, 1, 1, 1] );

				// ROTATION
				// Rotation should be performed using quaternions, using a THREE.QuaternionKeyframeTrack
				// Interpolating Euler angles (.rotation property) can be problematic and is currently not supported

				// set up rotation about x axis
				var xAxis = new THREE.Vector3( 1, 0, 0 );

				var qInitial = new THREE.Quaternion().setFromAxisAngle( xAxis, 0 );
				var qFinal = new THREE.Quaternion().setFromAxisAngle( xAxis, Math.PI );
				var quaternionKF = new THREE.QuaternionKeyframeTrack( '.quaternion', [0, 1, 2], [qInitial.x, qInitial.y, qInitial.z, qInitial.w, qFinal.x, qFinal.y, qFinal.z, qFinal.w, qInitial.x, qInitial.y, qInitial.z, qInitial.w] );

				// COLOR
				var colorKF = new THREE.ColorKeyframeTrack( '.material.color', [0, 1, 2], [1, 0, 0, 0, 1, 0, 0, 0, 1], THREE.InterpolateDiscrete );

				// OPACITY
				var opacityKF = new THREE.NumberKeyframeTrack( '.material.opacity', [0, 1, 2], [1, 0, 1] );

				// create an animation sequence with the tracks
				// If a negative time value is passed, the duration will be calculated from the times of the passed tracks array
				var clip = new THREE.AnimationClip( 'Action', 3, [scaleKF, positionKF, quaternionKF, colorKF, opacityKF] );

				// setup the THREE.AnimationMixer
				mixer = new THREE.AnimationMixer( mesh );

				// create a ClipAction and set it to play
				var clipAction = mixer.clipAction( clip );
				clipAction.play();

				//

				renderer = new THREE.WebGLRenderer( { antialias: true } );
				renderer.setPixelRatio( window.devicePixelRatio );
				renderer.setSize( window.innerWidth, window.innerHeight );
				document.body.appendChild( renderer.domElement );

				//

				stats = new Stats();
				document.body.appendChild( stats.dom );

				//

				clock = new THREE.Clock();

				//

				window.addEventListener( 'resize', onWindowResize, false );

			}

			function onWindowResize() {

				camera.aspect = window.innerWidth / window.innerHeight;
				camera.updateProjectionMatrix();

				renderer.setSize( window.innerWidth, window.innerHeight );

			}

			function animate() {

				requestAnimationFrame( animate );

				render();

			}

			function render() {

				var delta = clock.getDelta();

				if ( mixer ) {

					mixer.update( delta );

				}

				renderer.render( scene, camera );

				stats.update();

			}

		</script>

	</body>
</html>

```

How can I resolve this issue ? ( I tried with `mesh.children[0]` but it didn’t work )  
It should be something like ` mesh.children[0].material` or `mesh.material` but nothing seems to work.  
Another question, is it something like  
`var quaternionKF = new THREE.QuaternionKeyframeTrack( '.quaternion', [0, 1, 2], [qInitial.x, qInitial.y, qInitial.z, qInitial.w, qFinal.x, qFinal.y, qFinal.z, qFinal.w, qInitial.x, qInitial.y, qInitial.z, qInitial.w] );`  
And then `mixer = new THREE.AnimationMixer(mesh);` means I am modifying `mesh.quaternion` object ?  
I tried with `var quaternionKF = new THREE.QuaternionKeyframeTrack( '.quaternion', [0, 1, 2], [qInitial.x, qInitial.y, qInitial.z, qInitial.w, qFinal.x, qFinal.y, qFinal.z, qFinal.w, qInitial.x, qInitial.y, qInitial.z, qInitial.w] );` then `mixer = new THREE.AnimationMixer(skeleton.bones[8]);`  
I didn’t get any output anyway.  
Reference: I created a CSV file with bone data of the soldier.[soldier\_bone\_data.csv](https://discourse.threejs.org/uploads/short-url/ftH8lOm4OlgR599j0hTVbdYVYEk.csv) (4.0 KB)  
Then I printed `gltf.animations[1]` in console . It showed something like following

 ![custom](https://canada1.discourse-cdn.com/flex035/uploads/threejs/original/2X/9/9a9bb956502d378790afcca90b097958ed82e857.png)  
(I also printed `positionKF` and `scaleKF` in console as well as removed `colorKF` and `opacityKF`)  
Where am I doing wrong ?

---

<div class="post-metadata">

**Author:** ![soham0243](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/soham0243/32/7560_2.png) [@soham0243](https://discourse.threejs.org/u/soham0243)\
**Post date:** [March 26, 2020, 2:23pm UTC](https://discourse.threejs.org/t/create-skeletal-animation-using-bones/13789/6 "2020-03-26T14:23:36Z")

</div>

[ISSUE RESOLVED]  
The follwoing code is working. (I managed to control bones using keyframe track) I don’t know why people like you don’t encourage others to create animation directly in three.js

```
<!DOCTYPE html>
<html lang="en">
	<head>
		<title>three.js webgl - animation - basic</title>
		<meta charset="utf-8">
		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
		<link type="text/css" rel="stylesheet" href="main.css">
	</head>
	<body>

		<div id="info">
			<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> webgl - animation - basic use
		</div><script src="js/three.js"></script><script src="js/stats.min.js"></script>
		<script src="js/GLTFLoader.js"></script>

		<script>

			var stats, clock;
			var scene, camera, renderer, mixer, mesh, skeleton;

			init();
			animate();

			function init() {

				scene = new THREE.Scene();

				//

				camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 1, 1000 );
				camera.position.set( 3, 3, 6 );
				camera.lookAt( scene.position );

				//

				var axesHelper = new THREE.AxesHelper( 10 );
				scene.add( axesHelper );

				scene.add(new THREE.AmbientLight(0xffffff,1));

				//

				//var geometry = new THREE.BoxBufferGeometry( 5, 5, 5 );
				//var material = new THREE.MeshBasicMaterial( { color: 0xffffff, transparent: true } );
				//var mesh = new THREE.Mesh( geometry, material );
				//scene.add( mesh );

				var loader = new THREE.GLTFLoader();
				loader.load("models/Soldier.glb",function(gltf){
					mesh = gltf.scene;
					mesh.children[0].material = new THREE.MeshLambertMaterial();
					mesh.position.set(0,0,0);
					scene.add(mesh);

					skeleton = new THREE.SkeletonHelper( mesh );
					skeleton.visible = true;
					scene.add( skeleton );

					// create a keyframe track (i.e. a timed sequence of keyframes) for each animated property
					// Note: the keyframe track type should correspond to the type of the property being animated

					// POSITION
					/*var positionKF = new THREE.VectorKeyframeTrack( '.position', [0, 1, 2], [0, 0, 0, 30, 0, 0, 0, 0, 0] );

					// SCALE
					var scaleKF = new THREE.VectorKeyframeTrack( '.scale', [0, 1, 2], [1, 1, 1, 2, 2, 2, 1, 1, 1] );

					// ROTATION
					// Rotation should be performed using quaternions, using a THREE.QuaternionKeyframeTrack
					// Interpolating Euler angles (.rotation property) can be problematic and is currently not supported

					// set up rotation about x axis
					var xAxis = new THREE.Vector3( 1, 0, 0 );

					var qInitial = new THREE.Quaternion().setFromAxisAngle( xAxis, 0 );
					var qFinal = new THREE.Quaternion().setFromAxisAngle( xAxis, Math.PI );
					var quaternionKF = new THREE.QuaternionKeyframeTrack( '.quaternion', [0, 1, 2], [qInitial.x, qInitial.y, qInitial.z, qInitial.w, qFinal.x, qFinal.y, qFinal.z, qFinal.w, qInitial.x, qInitial.y, qInitial.z, qInitial.w] );

					// COLOR
					var colorKF = new THREE.ColorKeyframeTrack( '.material.color', [0, 1, 2], [1, 0, 0, 0, 1, 0, 0, 0, 1], THREE.InterpolateDiscrete );

					// OPACITY
					var opacityKF = new THREE.NumberKeyframeTrack( '.material.opacity', [0, 1, 2], [1, 0, 1] );

					// create an animation sequence with the tracks
					// If a negative time value is passed, the duration will be calculated from the times of the passed tracks array
					var clip = new THREE.AnimationClip( 'Action', 3, [scaleKF, positionKF, quaternionKF, colorKF, opacityKF] );*/

					var xAxis = new THREE.Vector3( 1, 0, 0 );

					var qInitial = new THREE.Quaternion().setFromAxisAngle( xAxis, 0 );
					var qFinal = new THREE.Quaternion().setFromAxisAngle( xAxis, Math.PI );
					var quaternionKF = new THREE.QuaternionKeyframeTrack( skeleton.bones[42].name+'.quaternion', [0, 1, 2], [qInitial.x, qInitial.y, qInitial.z, qInitial.w, qFinal.x, qFinal.y, qFinal.z, qFinal.w, qInitial.x, qInitial.y, qInitial.z, qInitial.w] );
					var clip = new THREE.AnimationClip( 'Action', 3, [quaternionKF] );

					// setup the THREE.AnimationMixer
					mixer = new THREE.AnimationMixer( mesh );

					// create a ClipAction and set it to play
					var clipAction = mixer.clipAction( clip );
					clipAction.play();
	
					//
				});

				renderer = new THREE.WebGLRenderer( { antialias: true } );
				renderer.outputEncoding = THREE.sRGBEncoding;
				renderer.setClearColor(new THREE.Color(0xcccccc));
				renderer.setPixelRatio( window.devicePixelRatio );
				renderer.setSize( window.innerWidth, window.innerHeight );
				document.body.appendChild( renderer.domElement );

				//

				stats = new Stats();
				document.body.appendChild( stats.dom );

				//

				clock = new THREE.Clock();

				//

				window.addEventListener( 'resize', onWindowResize, false );

			}

			function onWindowResize() {

				camera.aspect = window.innerWidth / window.innerHeight;
				camera.updateProjectionMatrix();

				renderer.setSize( window.innerWidth, window.innerHeight );

			}

			function animate() {

				requestAnimationFrame( animate );

				render();

			}

			function render() {

				var delta = clock.getDelta();

				if ( mixer ) {

					mixer.update( delta );

				}

				renderer.render( scene, camera );

				stats.update();
				
			}

		</script>

	</body>
</html>
```

---

<div class="post-metadata">

**Author:** ![alromh87](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/alromh87/32/22408_2.png) [@alromh87](https://discourse.threejs.org/u/alromh87)\
**Post date:** [November 24, 2021, 5:21am UTC](https://discourse.threejs.org/t/create-skeletal-animation-using-bones/13789/7 "2021-11-24T05:21:50Z")

</div>

Thanks a lot for your contribution, I was looking exactly for this, since I need programtic animations on the same model. Found your code snippet extremly useful.

I found also that using Euler for the rotation matrix serves better my porpouse since that way is easier to apply multiple rotations to the same joint.

let a = new THREE.Euler( Math.PI, 0, -.15\*Math.PI/2, ‘XYZ’ );  
var qInitial = new THREE.Quaternion().setFromEuler( a );
