The Web Animations API (WAAPI) is an animation API that's available in every browser.
WAAPI's most interesting feature for developers is that it lets us animate values like transform and opacity completely off the main JavaScript thread. This means that when a user's CPU is busy with work, animations remain smooth.
The downside is that, out of the box, it's missing many features that users of JavaScript animation libraries take for granted. Think interruption, default unit types, and most importantly, custom easing curves and spring animations.
Unlike GSAP, with its myriad of easing functions, or Motion, with its dynamic spring animations, WAAPI is limited to its handful of CSS easing functions.
Or is it? In this post we're going to show you how we can add support for these easing functions and springs to WAAPI, thanks to Motion's 2.3kb animate function. We'll also explain a little bit about the magic behind this, the linear() easing function.
animate()
Motion's animate function is built on WAAPI to leverage its hardware accelerated animations, but extends it with a greatly improved developer experience.
Most interestingly for us, it also supports custom easing functions and springs. This means that you can pass regular JavaScript functions:
Even though pow2 is a JavaScript function, this animation will run completely on the GPU, off the main thread and free from jank.
GSAP easing functions
GSAP has been knocking around for over a quarter of a century, so many users will be used to its easing functions. The good news is that this method works exactly the same with GSAP.
Basic easings with parseEase
GSAP offers all of its easings via string identifiers like "power2.out". We can access the functions behind these identifiers using parseEase.

You can even use easing functions accepting parameters this way:

Easing packs
GSAP also offers a number of optional easing functions like rough, which can create jittery and unpredictable motion:

You can import and use ExpoScaleEase and others from the EasePack in the same way.
Custom easing
GSAP includes a powerful CustomEase function, which can take any SVG path definition within the 0,0 and 1,1 coordinate space, and turn that into an easing function.

CustomEase accepts cubic bezier values, but animate() already accepts bezier curves via a simple array syntax: { ease: [.34,0,.27,.95] }
Bonus: Spring animations
As GSAP is known for its wide selection of easing functions, Motion is known for its spring animations.
While the full 18kb animate() includes these by default, you can also provide spring explicitly to the mini animate() for just a few extra kb.
How does this work?
These custom easing functions and the spring generator are all JavaScript functions, so how do these off-thread, hardware accelerated animations even work?
Over the last couple years, browsers have added support for the new linear() easing function. linear() can accept a number of points, and interpolate between these points linearly.
For instance, linear(0, 1) is identical to linear - one point at the start, one at the end, and a straight line between them.

By adding more points, we can add variations in speed. For instance, linear(0, 0.25, 1) looks like this:

By adding even more points, we can create paths that more closely resemble smooth curves.
As you can imagine, a curve like this is impossible to write by hand, which is why we have to generate it in some way.
We can do this in one of two ways. We can pre-build the curve, or we can dynamically generate it. There are two primary benefits of doing the latter.
Developer experience (DX)
linear() has awful DX at every stage of the development lifecycle: Creation, reading, editing.
Pre-building a curve can go some way to improving the creative experience. Some external tools, like the Motion Studio page or linear() generator, will let you generate and then copy/paste a linear() curve directly into your code.

However, all of these tools involve you going back and forth between an external website.
This is improved somewhat with the Motion Studio MCP, which sits in your code editor and lets your LLM generate CSS linear() curves using Motion's spring generator and bounce easing function. In a future iteration of the Motion Studio Extension you'll be able to do this via the GUI, too.
But once you've created a curve, there's the problem of maintenance. When you see a curve like this:
What does it do? How is it meant to feel? And if you knew it was a spring, how do you edit it? These are essentially opaque, inscrutable tokens.
Motion Studio will soon bake in comments that describe the generated spring so they remain readable and editable.
But until then, compare the linear() curve above to:
Or:
These are immediately recognisable and tweakable.
Additionally, it's not just tweaking the curve that requires a new linear() generation. Changing the duration does, too.
A linear() curve is a series of points with straight lines in-between. More points = smoother curve. But the number of points required for a smooth curve is a constant per second, therefore an animation of 0.6 seconds requires twice as many points as a 0.3 second animation. Which means changing duration requires a return trip to your curve editor.
This also brings us on to the second benefit of dynamic curve generation, the filesize.
Filesize
The accepted wisdom in some quarters is that JS libraries are unnecessary bloat. Therefore, if we can accept the negative DX, some would say they're still worth pre-baking in order to save kb.
Not always. This very bouncy spring requires a linear() path that's 2.3kb - the same size as animate() itself. It's quite an extreme curve, around 9 seconds long. But it gives you an idea of how quickly these curves can accumulate, especially across a large codebase. Whereas animate() will only ever be 2.3kb and always output a curve that's the correct resolution for your animation duration.
Conclusion
WAAPI is incredibly valuable. It provides us animations that can run smoothly even when the site is busy. But until recently, it meant giving up the expressive array of easing functions found in libraries like GSAP, or Motion's expressive spring animations.
In this post we've seen we can have the best of both worlds thanks to Motion's animate() function.
Finally, if you're eager to dive deeper on linear() easing curves, there's no better resource than this excellent post by Josh W Comeau.






