Compressing animation

Hit me with your best tips for reducing that animation data footprint!

Is there any good open source libraries out there that does this?

I did find this Animation Compression Library (ACL) library and it seems promising, but I think it lacks a JavaScript decoder.

I also found an early Draco extension draft but that was a few years old and not maintained.

There are some general points in this thread but I don’t want to start building something that already exists.

I think there is js version
also there is GLTF version so you can try out

1 Like

Cool, thank you!

I think I got it working, just need to evaluate the resulting float array :smiley:

Any idea how ACL compares to zfp?

1 Like

not really

I haven’t compared ACL against zfp or gzip and other lossless compression algorithms. How much you save will greatly depend on your animation data and how easily you can tune the the error metric. Properly tuned, you should save quite a bit compared to raw data or naive packing.

One key difference between ACL and other compression algorithms is that ACL is designed to remain compressed in memory. With something like zfp, you’ll likely need to decompress once ahead of time before you can sample your clip much like you would do with gzip. Decompressing every time you sample would be far too slow. ACL remains compressed and decompression is on demand when you sample. This ensures a very small memory footprint at runtime and no overhead when you load your assets. Decompression is also heavily optimized and will be much faster in ACL 2.0 which will be out in a few months. While it doesn’t yet use WASM SIMD, I hope to add support for it as soon as the proposal is final and major browsers support it properly. That should give further gains as well.

I did measure performance against ThreeJS sampling and it should be comparable for simple characters. For complex characters, ACL should be faster.

I also hope to introduce progressive streaming in ACL 2.0. This will allow you to start playback as soon as your animations are loaded while you continue to stream higher quality data in the background. This is designed to speed up scene loading so you can drop into gameplay as soon as you have enough data and to avoid loading data that is less important on devices that would not benefit from it or that can’t afford it (e.g. mobile, web, etc). I’m not quite sure yet how the API will look like for the JS shim though. All in due time :slight_smile:

If you run into issues, feel free to reach out on github (as you have) or through email. AFAIK the JS NPM module isn’t used much at the moment so there might be a few kinks but the core ACL library is very stable and is used by game studios the world over in simple mobile games all the way to AAA titles.

Note that the JS module is still in ‘beta’ in that it relies on the develop branch of ACL from a few months ago. Once ACL 2.0 comes out, the JS module will be updated to match and will have its first official release. Once this happens, it will be backwards compatible with future ACL versions. Once you upgrade to that version, you’ll need to recompress your animations one last time.

Cheers and thanks for trying it out!


There is a also a proposed glTF extension for Draco animation compression:

@Nicholas_Frechette thank you for sharing. That’s really cool.

I managed to get the npm module working and it’s pretty sweet!

My only gripe right now is how to tune the compression settings but I already emailed you a question about that. If you have time to answer this would be a better place though as more can learn from it.

There seems to be very little information about draco’s animation compression but it is highly likely to require up-front decompression of the whole file before clips can be sampled. Texture packer style compression would also behave the same (most likely).

@reimund I’ll answer your question here.

ACL uses an error metric documented here: Animation Compression: Measuring Accuracy · Nicholas Frechette's Blog

The gist of it is that in order to evaluate the error as close as what you’ll see at runtime, ACL simulates rigid skinning around each bone. You can picture a sphere around each bone and the error is measured on the surface of that sphere. The radius of these spheres is called the ‘shell radius’ or ‘virtual vertex distance’. Each bone can have its own value. The core ACL library assumes that units are centimeters but the JS module abstracts this away and assumes meters are your unit of distance (it’s what glTF uses). This means if your model isn’t stored in meters, you’ll need to change the shell radius and the constant translation threshold to match your units (you’ll scale the existing values to convert them from meters into your units).

Typically 0.1 millimeter is a decent precision threshold and it’s good enough for cinematographic quality (e.g. AAA games). But you have to be careful what shell radius you set for each bone. Because ACL simulates the visual mesh deformation using a fixed distance (rigid shell), the better it approximates your visual mesh, the better the results will be. If the rigid shell is too conservative, you’ll retain more accuracy than you need and the memory footprint will end up higher as a result. If the rigid shell isn’t conservative enough, you will have a lower footprint but you might start to notice visual artifacts. A proper shell radius is important.

If you have access to the visual mesh, you can grab the furthest vertex skinned by each bone and use its distance. That should be optimal. For bones that do not have skinning information (e.g. root, IK, camera, etc) you’ll want to use a fairly high and conservative value. 1 meter is typically good enough. Note that this radius value is in local space of each bone.

If you do not have access to the visual mesh, you can hardcode a reasonable value like 3-10cm. You can see here how I tuned ACL for UE4: acl-ue4-plugin/ at develop · nfrechette/acl-ue4-plugin · GitHub The repo also contains the source code for the plugin which you can use as inspiration. The API isn’t quite the same as the ACL one but they will converge in ACL 2.0 to the one the JS module uses.

For bones that have attachments (e.g hands, prop sockets, etc), you’ll want to use a more conservative value as well. For example, say 3cm is a good estimate for the visual mesh around the hand. This might be fine when nothing is attached to it but if a large 2 meter sword is picked up, all of a sudden the visual mesh of the sword is rigidly skinned with the hand bone.

How close the camera can get to your animated models might also impact your choice of shell distance. The closer the camera can get (e.g. first person), the easier it is to spot compression artifacts from up close. If the camera can never get too close (e.g. tower defense style game), you could use less conservative values as any compression artifacts won’t be as easily visible.

As long as all your models have consistent units and sizes, picking decent defaults should be possible without too much trial and error.

Note that ACL takes the bone hierarchy into account when measuring the resulting error and as such it will handle any amount of complexity gracefully. It also supports any number of root bones and as such is suitable for compressing whole cinematics or simulations with multiple animated models into a single clip (if desired). I try to keep ACL as engine agnostic and flexible as possible. If there is something you need that it can’t do, let me know!