Easing

CustomBounce

CustomBounce is a GSAP easing plugin that builds a tunable bounce curve you shape yourself, instead of the one fixed shape you get from the built-in `bounce.out`. You set how bouncy it is with `strength`, whether the element lands back where it started with `endAtStart`, and how long it squashes on impact with `squash`, and CustomBounce hands you back a named ease you can drop into any tween.

Updated July 1, 2026

Mechanics

How CustomBounce works

CustomBounce extends CustomEase, so it works by generating a real easing curve at create time and registering it under a name you choose. You call CustomBounce.create(id, vars) once, then reference that id as a string in the ease of any tween. The vars object is where the tuning lives: strength (0 to 1) sets how many times it bounces, squash adds a beat where the element looks stuck to the floor between bounces, and endAtStart makes it leave and return to its starting value.

That graph is GSAP's built-in bounce.out, and it's a single fixed shape: three decreasing bounces, no squash, always settling at the end. CustomBounce is the version you get to reshape. Turn strength up toward 0.9 and you get many small bounces; drop it toward 0.3 and you get one or two heavy ones. The curve you'd see for a high-strength CustomBounce has more, tighter oscillations packed near the end than the graph above.

script.js

CustomEase is required

CustomBounce is built on top of CustomEase, so you have to register both (gsap.registerPlugin(CustomEase, CustomBounce)). Registering only CustomBounce is the most common reason a myBounce ease silently falls back to power1.out.
When

Use it for

  • A ball, badge, or card that drops in and physically settles with more than one bounce
  • A number counter that lands on its final value and bounces to rest
  • Adding real squash and stretch on impact (the squash option plus the paired squash ease driving scaleX/scaleY)
  • A throw-up-and-land move where the object leaves and returns to the same spot (endAtStart: true)
  • Any bounce where the fixed bounce.out shape feels too generic and you need to dial the weight
Alternatives

Use something else when

  • You want one clean overshoot, not a settling bounce, use back.out(1.7) (no plugin needed, ships with core)
  • You want a springy wobble that oscillates past the target and back, use elastic.out(1, 0.3)
  • The motion is UI feedback (button, toggle, hover), use expo.out at a short duration, a bounce reads as playful, not responsive
  • You need a bespoke curve that isn't a bounce at all, draw it with CustomEase.create() directly
  • The user has prefers-reduced-motion set, drop the bounce entirely rather than shipping a softened version

Bounce is exactly what reduced-motion is for

A settling bounce is large, repeated, non-essential motion, the kind that triggers vestibular discomfort. prefers-reduced-motion passed 50% adoption on mobile sites in 2024, up from 34% in 2022 (Web Almanac 2024, Accessibility chapter), so gate every CustomBounce tween behind gsap.matchMedia() and hand reduced-motion users a plain fade or an instant set.
In production

Where a bounce ease fits in Annnimate components

Both of these components already expose the ease as a data-anm-ease config, so a CustomBounce ease drops straight in without touching the animation logic. Neither ships with CustomBounce by default, they use a lighter single-overshoot spring, and the plugin is the upgrade path when you want a real settling bounce:

  • Popping Text ships with back.out(1.7) so each character pops in with one overshoot. Swap the ease for a strength: 0.5 CustomBounce and the characters settle with a couple of real bounces instead of a single spring
  • Counter counts to its target with power3.inOut. Feed it a CustomBounce ease and the number overshoots and bounces onto its final value, which reads well for stat blocks and score reveals

Tuning tip

Keep strength around 0.4 to 0.6 for UI. Above 0.8 you get a long tail of tiny bounces that looks cartoonish on text and counters. Add squash: 2 only when the element visibly hits a surface, on a floating counter it just looks like a glitch.
Used in components

See it running in production

FAQ

Common questions

Why does my CustomBounce ease not work / fall back to a linear-ish curve?
You almost certainly registered CustomBounce but not CustomEase. CustomBounce extends CustomEase and can't build its curve without it. Register both: gsap.registerPlugin(CustomEase, CustomBounce). Also confirm the string in your tween's ease exactly matches the id you passed to CustomBounce.create(), a typo makes GSAP fall back to the default ease with no error.
When do I need CustomBounce instead of the built-in bounce.out?
Use bounce.out when its default shape looks right, it needs no plugin and no setup. Reach for CustomBounce when you want to change the shape: fewer or more bounces (strength), a squash/stuck beat between bounces (squash), or a bounce that returns to its start (endAtStart). bounce.out gives you exactly one look; CustomBounce gives you a whole family.
What does endAtStart actually do?
endAtStart: true makes the ease end back at the value it started from, so you get an object sitting on the ground, leaping up, and bouncing back down to a stop. It's the config you want for a throw-and-land where the start and end positions are the same, rather than a drop where the element travels from A to B.
How do I get the squash-and-stretch on impact?
Set squash on the create call (2 is a good starting point) and give it a squashID. That registers a SECOND ease. You run your position tween with the bounce ease, and a separate tween on scaleX/scaleY with the squash ease and transformOrigin: "center bottom". The two eases are timed so the element flattens exactly when it hits.
Is CustomBounce free, or is it a Club GSAP plugin?
It's free. Every GSAP plugin, including CustomBounce and CustomEase, has been fully free for any use since GSAP 3.13 (mid-2024), after Webflow acquired GreenSock. You just import and register it like any core plugin, no membership or license key.