Seeing a slight twitch when the same animation is looped

Hi all. I asked a previous question a while back about Lerping/smoothing animation transitions and got a great answer very quickly. I think the transition from one animation to another looks great in my project.

However, I notice when my character loops the same animation (most notably the walk animation), there seems to be a quick “twitch” when the walk animation stops and starts over again.

My model and animations are from Mixamo, but they are downloaded with the default animation loop that is repeats evenly. The same exact model looks fine in other engines that I have tried. Oddly enough I experience this twitch issue with both BabylonJS and ThreeJS (BabylonJS is way cooler BTW :smiley:).

I was going to attach some code here or point to a screencast, but I actually see the exact same issue in the documentation examples shown here:

Is it only me or can you also see the twitch when walking? It’s very subtle, but more noticeable in that playground link when the walking slider is raised up. It causes an almost “robotic” look to the walk.

cc @PatrickRyan, it may be related to how the animation has been created.

Can you provide the file you used (the one from the PG is a .babylon file) and in which engine, so that we can test on our end?

@swim81, I believe there is a missing frame of the walk cycle so the loop is not exact. Can you share the fbx of the mixamo animation you downloaded? I would like to take it into Maya and look at the animation cycle.

Hi @PatrickRyan and @Evgeni_Popov

Thanks for the reply.

I have uploaded the ninja model here:

It could definitely be an issue with mixamo. But I have loaded this model with both Panda3d as well as Godot and didn’t notice the same issue.

Thanks again…

@swim81, I’m a little confused. The file you linked looks like an idle animation of the character just breathing. I opened up the timeline to see if it was a compilation file with several animations in it, but it only seems to be a short idle pose. Did you want me to evaluate this one or was there a walk pose that you are having trouble with?

Hi @PatrickRyan they should be there?

I am using this object in my code:

this.moved = {
    Idle: true,
    Punch: false,
    Run: false,
    StrafeLeft: false,
    StrafeRight: false,
    Walk: false,
    WalkBack: false,

And you can see the animations in this screenshot in the console:

Thanks for looking though anyways.

@PatrickRyan I do see several animations in the sandbox.

@swim81 I tested the file with Threejs, Playcanvas and model-viewer and they all show the walk animation as Babylon, with a hiccup when looping.

[…] I’ve just tested in Godot (4.1.1), and there’s also a hiccup when looping. Admittedly, I don’t know Godot, so I simply loaded the file and ran the walking animation. Perhaps there are some parameters that need to be activated to suppress the hiccups?

Just a shot in the dark: graph editor - How to create a seamless animation loop in Blender? - Blender Stack Exchange When I had a similar issue, the “make linear” solution did it for me.

Oh weird I didn’t notice it in Godot. I will check again though!

@swim81, my apologies, I assumed this was the original download from Mixamo which would have all the animations in the timeline and didn’t look at the NLA to see this was the asset authored with the NLA. I am not a normal blender user, but from what I see, the pop isn’t a missing frame in the animation but rather a mismatch with keyframe tangents. I noticed some strange data when looking at the curves normalized so that I could see anomalies.

This is the upper left leg Y quaternion component for the 24 frames of the walk cycle. Note that the slope of the interpolation between frames 1 and 2 and between frames 23 and 24 do not match, which means there most likely would be a visible pop in the animation as the curve trajectory changes when cycling. The pop would be magnified by the actual delta in value. Note here that they are normalized so it may not be large.

Additionally, if we look at the Y location data of the left upper leg, we see an even bigger mismatch between frame 1 and frame 24:

It appears that the key at frame 0 matches the value at frame 24, but that frame is not exported as the range in the NLA is set from 1 to 24. I think this points to the problem being that the pose at the start and end of the clip are not identical which is causing the animation artifact. It would be worth going back to the source animation and making sure that it wasn’t clipped - maybe the original had two cycles of the walk and one was clipped out for this version which may explain the poses not matching. I would guess at this point this is the most logical source of the issue.


Always impressed by what you do @PatrickRyan !!!

Stumbling around in the Blender interface taking far too long to figure out where Blender has stashed the data? :rofl:

you can just unlock the strips and adjust them to zero , i had a look at the file and did this , but then also just had to shorten the idle by one frame or it has a freeze frame

drag into sandbox and test :

Yeah as it turns out I actually see the twitch in Godot while looking closely. I am not sure why I don’t see it in Panda3d though… although I am Lerping myself there.

This question may or may not make sense… Can the current animation be smoothed/lerped with itself?

@swim81, there are no features in our animation system to lerp looped frames of animation. Our system supports blending, but that is blending across different animation clips. However, I would also posit that using an artificial interpolation of several animation frame to smooth a loop is far more expensive than ensuring your animation data loops correctly from the source. Interpolating multiple frames to figure out the final position of a bone from which a skinning matrix is applied to determine the final position of vertices in the mesh 60x per second adds up. For blending between two animations like idle to walk, it’s a necessary cost that makes sense. For cleaning up a loop pose, it’s another layer of blend that needs to be there for any looping animation where the loop pose does not match, adding extra expense to determining the final position of your vertices.