# I cannot stop AnimationClip on last frame \[RESOLVED\]

**URL:** <https://discourse.threejs.org/t/i-cannot-stop-animationclip-on-last-frame-resolved/4712>\
**Category:** Questions\
**Tags:** animation\
**Created:** [October 28, 2018, 10:43am UTC](https://discourse.threejs.org/t/i-cannot-stop-animationclip-on-last-frame-resolved/4712 "2018-10-28T10:43:11Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![fancyrainbow](https://avatars.discourse-cdn.com/v4/letter/f/d07c76/32.png) [@fancyrainbow](https://discourse.threejs.org/u/fancyrainbow)\
**Post date:** [October 28, 2018, 10:43am UTC](https://discourse.threejs.org/t/i-cannot-stop-animationclip-on-last-frame-resolved/4712/1 "2018-10-28T10:43:11Z")

</div>

Hello all,

I’m having a tough time with this method, AnimationClip.CreateFromMorphTargetSequence…

I loaded a GLB with some animations. I want two of them to loop only once and stop at last frame.  
However, as talked [here](https://github.com/mrdoob/three.js/issues/8446), the loader copy the first frame of the animations on the last, to make loops look smoother. Someone came up with a solution [here](https://github.com/mrdoob/three.js/pull/8637/commits/9d223623114a5473af120a7f6a90d74e01d63205), but I cannot use it the right way apparently…

I’m supposed to make a new AnimationClip from the old one (the one with the last frame I don’t want), with AnimationClip.CreateFromMorphTargetSequence, with “noLoop” parameter set to true. I tried but it doesn’t work.

I made a [live exemple, with some anotations in the code](http://www.thefancyrainbow.com/liveSample/)

```
<!DOCTYPE html>
<html>
<head>
<title>Live exemple of my problem</title>
<meta charset="utf-8">
<style>
	#world {
		position: fixed;
		margin:0;
		top:0;
		left:0;
		width: 100vw;
		height: 100vh;
		overflow: hidden;
	}

	#menu {
		position: fixed;
		margin: 0;
		padding: 15px;
		top: 25vh;
		width: 30vw;
		height: 50vh;
		background-color: #ffcce6;
		text-align: center;
		z-index: 1;
	}

	#menu>button {
		margin: 10px;
	}
</style>
</head>

<body>
<div id="world"></div>

<div id="menu">
	<p>Click on this button to play the three animationAction.</p>
	<button onclick="onButtonClick()">open the cap</button>
	<p>Two of them (keyRise and capRise,) are supposed to loop only once
	and stop at the last frame. For each of them, I set the clampWhenFinished
	property to true, but to no apparent effect.</p>
	<p>I found an Issue <a href="https://github.com/mrdoob/three.js/issues/8446">here</a>
	about my problem</p>
	<p>Apparently the loader is at fault, because it copy the first fram as the last
	to make loops look smoother. So in my case I'm supposed to use
	<a href="https://github.com/mrdoob/three.js/pull/8637/commits/9d223623114a5473af120a7f6a90d74e01d63205">CreateFromMorphTargetSequence</a>
	, but I'm not sure how.</p>
	<p>You will find more information about this in my code in the GLTFloader</p>
</div>

<script src="Three.js"></script>
<script src="GLTFLoader.js"></script>
<script src="stats.min.js"></script>
<script src="OrbitControls.js"></script>

<script>
	

window.addEventListener("load", function() {
	main();
}, false);

var scene, camera, renderer, stats, clock, loader;
var loader, keyMixer, capMixer, keyRotation, keyRise, capRise;

function onButtonClick() {

keyRotation.play();
keyRise.play();
capRise.play();

keyRise.clampWhenFinished = true;
capRise.clampWhenFinished = true;

keyRise.loop = THREE.LoopOnce;
capRise.loop = THREE.LoopOnce;
};

function main() {

	scene = new THREE.Scene();
	scene.background = new THREE.Color(0x39ac73);

	camera = new THREE.PerspectiveCamera(35, window.innerWidth/window.innerHeight, 0.5, 100);
	camera.position.set(0, 0.3, 1.5);
	camera.lookAt(0, 0.3, 0);

	renderer = new THREE.WebGLRenderer({antialias:true});
	renderer.setSize(window.innerWidth, window.innerHeight);
	document.getElementById("world").appendChild(renderer.domElement);

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

	addShadowedLight( -10, 6, -10, 0xffffff, 0.5);
	addShadowedLight( 1, 1, 1, 0xffffff, 0.5 );
	addShadowedLight( 0.5, 1, -1, 0xffaa00, 0.1 );

	clock = new THREE.Clock();
	clock.start();

	// GLB importation //

	loader = new THREE.GLTFLoader();

	loader.load("PineappleAndKey.glb", function(glb) {

		var animations = glb.animations;

		var pot = glb.scene.getObjectByName("pot");
		var cap = glb.scene.getObjectByName("couvercle");
		var key = glb.scene.getObjectByName("clef");

		var group = new THREE.Group();
		group.add(pot, cap, key);

		scene.add(group);
		group.position.z += 0.8;

		capMixer = new THREE.AnimationMixer(cap);
		keyMixer = new THREE.AnimationMixer(key);

		keyRotation = keyMixer.clipAction(
			THREE.AnimationClip.findByName( animations, 'rotationClef' ));
		keyRise = keyMixer.clipAction(
			THREE.AnimationClip.findByName( animations, 'elevationClef' ));

		capRise = capMixer.clipAction(
			THREE.AnimationClip.findByName( animations, 'ouverturePot' ));

		// Here is what I should write if I follow the process described in the 
		// pull request whose link I give in the text (here is the link again :

		// https://github.com/mrdoob/three.js/pull/8637/commits/9d223623114a5473af120a7f6a90d74e01d63205).

		// To try it, use the following code and remove
		// the last capRise declaration just above.

			//	var clipCapRise = THREE.AnimationClip.findByName( animations, 'ouverturePot');

			//	var clipCapRiseNoLoop = THREE.AnimationClip.CreateFromMorphTargetSequence(
			//	'ouverturePot', clipCapRise.tracks[0], 60, true);

			//	capRise = capMixer.clipAction(clipCapRiseNoLoop);

		// When I try this, the animation does not work at all.
		// I'm sure there is something wrong with my use of CreateFromMorphTargetSequence

	},

	function ( xhr ) {
			console.log( ( xhr.loaded / xhr.total * 100 ) + '% loaded' );
	},

	function ( error ) {
		console.log( 'An error happened' );
	}

	);

	/////////////////////

	loop();

	function loop() {
		requestAnimationFrame(loop);

		stats.update();

		if (keyMixer && clock) {
			var t = clock.getDelta();
			keyMixer.update(t);
			capMixer.update(t);
		};

		renderer.render(scene, camera);
	};

	function addShadowedLight( x, y, z, color, intensity ) {
		var directionalLight = new THREE.DirectionalLight( color, intensity );

		directionalLight.position.set( x, y, z );
		directionalLight.castShadow = true;

		var d = 1;
		directionalLight.shadow.camera.left = -d;
		directionalLight.shadow.camera.right = d;
		directionalLight.shadow.camera.top = d;
		directionalLight.shadow.camera.bottom = -d;
		directionalLight.shadow.camera.near = 0.1;
		directionalLight.shadow.camera.far = 30;
		directionalLight.shadow.mapSize.width = 1048;
		directionalLight.shadow.mapSize.height = 1048;
		directionalLight.shadow.bias = -0;

		scene.add( directionalLight );
	};

};
</script>

</body>
</html>

```

The GLB file is here :  
[PineappleAndKey.glb](https://canada1.discourse-cdn.com/flex035/uploads/threejs/original/2X/d/d50051c09edbc780cc796e7289b69839f430d8f5.glb) (67.6 KB)

I would be so glad if you helped me wrap my head around this method…

---

<div class="post-metadata">

**Author:** ![donmccurdy](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/donmccurdy/32/33209_2.png) [@donmccurdy](https://discourse.threejs.org/u/donmccurdy)\
**Post date:** [October 29, 2018, 2:22am UTC](https://discourse.threejs.org/t/i-cannot-stop-animationclip-on-last-frame-resolved/4712/2 "2018-10-29T02:22:03Z")

</div>

I don’t think you’ll need `CreateFromMorphTargetSequence` with GLB models, was there something in the docs that suggested that particular method? Try `action.clampWhenFinished = true` instead:

```javascript

var mixer = new THREE.AnimationMixer( gltf.scene );
var clip = gltf.animations[0];
var action = mixer.clipAction( clip );
action.clampWhenFinished = true;
action.play();

```

[https://threejs.org/docs/#api/en/animation/AnimationAction.clampWhenFinished](https://threejs.org/docs/#api/en/animation/AnimationAction.clampWhenFinished)

---

<div class="post-metadata">

**Author:** ![fancyrainbow](https://avatars.discourse-cdn.com/v4/letter/f/d07c76/32.png) [@fancyrainbow](https://discourse.threejs.org/u/fancyrainbow)\
**Post date:** [October 29, 2018, 2:51am UTC](https://discourse.threejs.org/t/i-cannot-stop-animationclip-on-last-frame-resolved/4712/3 "2018-10-29T02:51:18Z")

</div>

I did use action.clampWhenFinished = true on my function onButtonClick(), but to no apparent effect.

In the thread that I found there is someone having the same problem, and they found out that the GLTF loader copy the first frame of animations to the last to make loops look smooth (which is a good idea I guess). But in my case, when the action stops on the last frame it looks like it came back to the first.

The solution they offered was to use AnimationClip.CreateFromMorphTargetSequence, to “remove the last frame” of the clip. But I’m not sure what is the MorphTargetSequence that I must pass as argument and where to find it in the loaded glb.

---

<div class="post-metadata">

**Author:** ![donmccurdy](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/donmccurdy/32/33209_2.png) [@donmccurdy](https://discourse.threejs.org/u/donmccurdy)\
**Post date:** [October 29, 2018, 3:32am UTC](https://discourse.threejs.org/t/i-cannot-stop-animationclip-on-last-frame-resolved/4712/4 "2018-10-29T03:32:01Z")

</div>

> [@fancyrainbow](#):
>
> they found out that the GLTF loader copy the first frame of animations to the last to make loops look smooth

Which thread do you mean? THREE.GLTFLoader does not do this… testing the first animation (‘ouverturePot’) in your model, the following works for me:

```javascript
action.clampWhenFinished = true;
action.loop = THREE.LoopOnce;
action.play();

```

… the pineapple stops at the top of its animation path:

 ![41%20PM](https://canada1.discourse-cdn.com/flex035/uploads/threejs/original/2X/5/509430e087d5c00a71c26a2da17c369a242a4b9f.jpeg)

---

<div class="post-metadata">

**Author:** ![fancyrainbow](https://avatars.discourse-cdn.com/v4/letter/f/d07c76/32.png) [@fancyrainbow](https://discourse.threejs.org/u/fancyrainbow)\
**Post date:** [October 29, 2018, 3:36am UTC](https://discourse.threejs.org/t/i-cannot-stop-animationclip-on-last-frame-resolved/4712/5 "2018-10-29T03:36:47Z")

</div>

I meant [this thread](https://github.com/mrdoob/three.js/issues/8446), but if it works for you I must have made a mistake somewhere, I guess it’s because I wrote in the wrong order :

```
action.play();
action.clampWhenFinished = true;
action.loop = THREE.LoopOnce;

```

I’m going to make a try with another order

---

<div class="post-metadata">

**Author:** ![donmccurdy](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/donmccurdy/32/33209_2.png) [@donmccurdy](https://discourse.threejs.org/u/donmccurdy)\
**Post date:** [October 29, 2018, 3:40am UTC](https://discourse.threejs.org/t/i-cannot-stop-animationclip-on-last-frame-resolved/4712/6 "2018-10-29T03:40:13Z")

</div>

That is referring to either JSONLoader or ObjectLoader, I think… but in any case, your code looks very close to mine so I’m surprised it isn’t working. Perhaps the order is the difference. 🤔

---

<div class="post-metadata">

**Author:** ![fancyrainbow](https://avatars.discourse-cdn.com/v4/letter/f/d07c76/32.png) [@fancyrainbow](https://discourse.threejs.org/u/fancyrainbow)\
**Post date:** [October 29, 2018, 7:39am UTC](https://discourse.threejs.org/t/i-cannot-stop-animationclip-on-last-frame-resolved/4712/7 "2018-10-29T07:39:17Z")

</div>

I definitely cannot get it to work… The cap and the key still go back to their initial positions.  
I tried to log the local action times, and I found that it doesn’t come back to 0, though it appears so.

Could you copy here the working code you made please, to compare ?

I updated the [live example](http://thefancyrainbow.com/liveSample/)  
Here is my new code :

```
<!DOCTYPE html>
<html>
<head>
<title>Live exemple of my problem</title>
<meta charset="utf-8">
<style>
	#world {
		position: fixed;
		margin:0;
		top:0;
		left:0;
		width: 100vw;
		height: 100vh;
		overflow: hidden;
	}

	#menu {
		position: fixed;
		margin: 0;
		padding: 15px;
		top: 25vh;
		width: 30vw;
		height: 50vh;
		background-color: #ffcce6;
		text-align: center;
		z-index: 1;
	}

	#menu>button {
		margin: 10px;
	}
</style>
</head>

<body>
<div id="world"></div>

<div id="menu">
	<p>Click on this button to play the three animationAction.</p>
	<button onclick="onButtonClick()">open the cap</button>
	<p>Two of them (keyRise and capRise,) are supposed to loop only once
	and stop at the last frame. For each of them, I set the clampWhenFinished
	property to true, but to no apparent effect.</p>
	<p>I found an Issue <a href="https://github.com/mrdoob/three.js/issues/8446">here</a>
	about my problem</p>
</div>

<script src="Three.js"></script>
<script src="GLTFLoader.js"></script>
<script src="stats.min.js"></script>
<script src="OrbitControls.js"></script>

<script>
	

window.addEventListener("load", function() {
	main();
}, false);

var scene, camera, renderer, stats, clock, loader;
var loader, keyMixer, capMixer, keyRotation, keyRise, capRise;

function onButtonClick() {

keyRise.clampWhenFinished = true;
capRise.clampWhenFinished = true;

keyRise.loop = THREE.LoopOnce;
capRise.loop = THREE.LoopOnce;

keyRotation.play();
keyRise.play();
capRise.play();

};

function main() {

	scene = new THREE.Scene();
	scene.background = new THREE.Color(0x39ac73);

	camera = new THREE.PerspectiveCamera(35, window.innerWidth/window.innerHeight, 0.5, 100);
	camera.position.set(0, 0.3, 1.5);
	camera.lookAt(0, 0.3, 0);

	renderer = new THREE.WebGLRenderer({antialias:true});
	renderer.setSize(window.innerWidth, window.innerHeight);
	document.getElementById("world").appendChild(renderer.domElement);

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

	addShadowedLight( -10, 6, -10, 0xffffff, 0.5);
	addShadowedLight( 1, 1, 1, 0xffffff, 0.5 );
	addShadowedLight( 0.5, 1, -1, 0xffaa00, 0.1 );

	clock = new THREE.Clock();
	clock.start();

	// GLB importation //

	loader = new THREE.GLTFLoader();

	loader.load("PineappleAndKey.glb", function(glb) {

		var pot = glb.scene.getObjectByName("pot");
		var cap = glb.scene.getObjectByName("couvercle");
		var key = glb.scene.getObjectByName("clef");

		var animations = glb.animations;

		var keyRotationClip = THREE.AnimationClip.findByName( animations, 'rotationClef' )
		var keyRiseClip = THREE.AnimationClip.findByName( animations, 'elevationClef' )
		var capRiseClip = THREE.AnimationClip.findByName( animations, 'ouverturePot' )

		capMixer = new THREE.AnimationMixer(cap);
		keyMixer = new THREE.AnimationMixer(key);

		keyRotation = keyMixer.clipAction(keyRotationClip);
		keyRise = keyMixer.clipAction(keyRiseClip);
		capRise = capMixer.clipAction(capRiseClip);

		var group = new THREE.Group();
		group.add(pot, cap, key);

		scene.add(group);
		group.position.z += 0.8;
	},

	function ( xhr ) {
			console.log( ( xhr.loaded / xhr.total * 100 ) + '% loaded' );
	},

	function ( error ) {
		console.log( 'An error happened' );
	}

	);

	/////////////////////

	loop();

	function loop() {
		requestAnimationFrame(loop);

		stats.update();

		if (keyMixer && clock) {
			var t = clock.getDelta();
			keyMixer.update(t);
			capMixer.update(t);

			console.log("keyRise.time = " + keyRise.time + " ; capRise.time = " + capRise.time);
		};

		renderer.render(scene, camera);
	};

	function addShadowedLight( x, y, z, color, intensity ) {
		var directionalLight = new THREE.DirectionalLight( color, intensity );

		directionalLight.position.set( x, y, z );
		directionalLight.castShadow = true;

		var d = 1;
		directionalLight.shadow.camera.left = -d;
		directionalLight.shadow.camera.right = d;
		directionalLight.shadow.camera.top = d;
		directionalLight.shadow.camera.bottom = -d;
		directionalLight.shadow.camera.near = 0.1;
		directionalLight.shadow.camera.far = 30;
		directionalLight.shadow.mapSize.width = 1048;
		directionalLight.shadow.mapSize.height = 1048;
		directionalLight.shadow.bias = -0;

		scene.add( directionalLight );
	};

};
</script>

</body>
</html>
```

---

<div class="post-metadata">

**Author:** ![donmccurdy](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/donmccurdy/32/33209_2.png) [@donmccurdy](https://discourse.threejs.org/u/donmccurdy)\
**Post date:** [October 31, 2018, 6:33pm UTC](https://discourse.threejs.org/t/i-cannot-stop-animationclip-on-last-frame-resolved/4712/8 "2018-10-31T18:33:15Z")

</div>

This turns out to be an issue from an older version of three.js. If you update from r91 to r96+, including a corresponding version of GLTFLoader, you should be OK:

![F0E64A4F-7F1E-4D33-AE67-CF9CD3305B07-80425-0001109DDCB8620D](https://cdck-file-uploads-canada1.s3.dualstack.ca-central-1.amazonaws.com/flex035/uploads/threejs/original/2X/d/d282984826338d90f7943cd0454fb2ad3db6fd9f.gif)

---

<div class="post-metadata">

**Author:** ![fancyrainbow](https://avatars.discourse-cdn.com/v4/letter/f/d07c76/32.png) [@fancyrainbow](https://discourse.threejs.org/u/fancyrainbow)\
**Post date:** [November 1, 2018, 2:25pm UTC](https://discourse.threejs.org/t/i-cannot-stop-animationclip-on-last-frame-resolved/4712/9 "2018-11-01T14:25:55Z")

</div>

OK it works, thanks a lot for you help !

---

<div class="post-metadata">

**Author:** ![OSource](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/osource/32/21864_2.png) [@OSource](https://discourse.threejs.org/u/OSource)\
**Post date:** [March 18, 2022, 12:52pm UTC](https://discourse.threejs.org/t/i-cannot-stop-animationclip-on-last-frame-resolved/4712/10 "2022-03-18T12:52:59Z")

</div>

this bug occurs in 135, using action.clampWhenFinished = true;  
stops it too early

> [@fancyrainbow](#):
>
> `nce;`

---

<div class="post-metadata">

**Author:** ![Shane\_Brumback](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/shane_brumback/32/65134_2.png) [@Shane\_Brumback](https://discourse.threejs.org/u/Shane_Brumback)\
**Post date:** [March 21, 2022, 11:33pm UTC](https://discourse.threejs.org/t/i-cannot-stop-animationclip-on-last-frame-resolved/4712/12 "2022-03-21T23:33:19Z")

</div>

Thanks this thread helped me out a lot. I am working with an enemy bot gltf model on my prototype first person shooter. The robot has a single track animation with many actions. I had to split the track up into sub clips with the following code then applied clampwhenfinished.

var EnemyHeavyBotFallBackClip = THREE.AnimationUtils.subclip(gltf.animations[0], “Take\_001”, 1300, 1355);  
actionEnemyHeavyBotFallBackMixer = mixer.clipAction(EnemyHeavyBotFallBackClip);  
actionEnemyHeavyBotFallBackMixer.clampWhenFinished = true;  
actionEnemyHeavyBotFallBackMixer.setLoop(THREE.LoopOnce);  
actionEnemyHeavyBotFallBackMixer.play();

[https://canada1.discourse-cdn.com/flex035/uploads/threejs/original/3X/e/e/ee51a2a452ac2c09066ea4ec8ab62c11e4e8bf5f.mp4](https://canada1.discourse-cdn.com/flex035/uploads/threejs/original/3X/e/e/ee51a2a452ac2c09066ea4ec8ab62c11e4e8bf5f.mp4)
