webdevpuneet.com

CustomBounce Ball — Free GSAP Squash & Stretch Snippet

CustomBounce Ball · Animations · Plain HTML, CSS & JS · Live preview

CategoryAnimations

Copy & paste

Source Code

<div class="cbb-wrap">
  <div class="cbb-stage">
    <div class="cbb-ball" id="cbbBall"></div>
    <div class="cbb-shadow" id="cbbShadow"></div>
    <div class="cbb-floor"></div>
  </div>
  <div class="cbb-bar">
    <button class="cbb-btn" data-strength="0.3">Soft</button>
    <button class="cbb-btn is-active" data-strength="0.6">Bouncy</button>
    <button class="cbb-btn" data-strength="0.8">Superball</button>
  </div>
  <p class="cbb-hint">Squash & stretch ride a matched companion ease.</p>
</div>

Extend with AI

Take This Snippet Further With AI

The AI prompt for this one is for Patreon supporters ($3 a month), along with a site with no ads. Supporters enter the passcode once and it is remembered in this browser.

· Become a supporter

What's included

Features

Paired ease generation
Position and squash curves from one call.
Contact-perfect squash
Deformation lands on every impact.
Restitution control
strength shapes rebound energy.
Floor-anchored origin
Squash flattens against the ground.
Strength-scaled time
Bouncier presets settle over longer runs.
Shared named eases
The shadow reuses the ball's curve.
Interrupt-safe replays
Kill, reset, re-create, play.
CustomEase family
Built on SVG-path-defined easing.

About this UI Snippet

CustomBounce Ball — Animation-Principle Bouncing With Paired Eases

Screenshot of the CustomBounce Ball snippet rendered live

GSAP's built-in bounce.out moves a value like a bouncing ball — but a *real* ball also squashes on impact and stretches in flight, Disney's most famous animation principle. Faking that by hand means computing exactly when each bounce contacts the floor and syncing scale keyframes to those instants. CustomBounce (free on the CDN since 3.13) does the sync for you: one call generates a position ease *and* a matched squash ease whose deformations land precisely on every floor contact.

One create() call, two synchronized eases

CustomBounce.create('ball', { strength: 0.6, squash: 3 }) registers two named eases: 'ball', the bounce trajectory for y, and 'ball-squash', a companion curve that spikes exactly at the trajectory's contact points. Two separate tweens — one moving y, one animating scaleX/scaleY — run over the *same duration* with their respective eases, and because both curves were generated from the same bounce math, deformation and impact can never drift apart. That co-generation is the entire plugin; hand-synced versions break the moment you retune the bounce.

strength is restitution

The strength parameter (0–1) is effectively the coefficient of restitution: how much energy survives each bounce. Soft (0.3) dies in a couple of low rebounds; Superball (0.8) keeps rebounding tall and long. The demo scales total duration with strength (1.6 + strength) because bouncier balls genuinely take longer to settle — a fixed duration would compress the superball's extra bounces into frantic jitter.

squash: 3 and the transform origin

squash sets how much of the ease's time is spent deformed relative to airborne travel. The squash tween drives scaleX: 1.5, scaleY: 0.55 — wider and flatter — with transformOrigin: '50% 100%' so the ball flattens *against the floor* rather than around its own center (center-origin squash makes the ball appear to levitate mid-impact, the most common squash-and-stretch mistake).

The shadow rides the same position ease

The contact shadow grows and darkens using the *same* 'ball' ease on scale and opacity — so it inflates as the ball approaches and shrinks as it rebounds, in perfect sync, with zero additional timing logic. Reusing a registered ease across properties is the quiet superpower of named CustomEases.

Replays are kill-set-play

Each button re-creates the ease with a new strength (re-registering under the same name replaces it), kills running tweens, resets transforms, and replays — the standard idiom for parameterized animations, keeping every run interrupt-safe.

Where CustomBounce sits in the ease family

It's built on CustomEase (SVG-path-defined easing curves — also loaded here as a dependency) alongside CustomWiggle. Bounce trajectories are painful to draw by hand as paths; CustomBounce is essentially a curve *generator* for the physics-shaped subset. For arbitrary hand-drawn feels, drop to raw CustomEase; for oscillation, see custom wiggle icons.

Customizing it

Tweak squash (1 subtle, 4 cartoon), drop from a click position, or apply the paired-eases idea to UI: a dropping modal or badge with landing squash reads delightfully physical. Related motion: ease shopping in gsap ease gallery, simulated momentum in physics props pucks, gravity arcs in physics 2d burst, and the CSS-keyframe breathing animation.

Step by step

How to Use

  1. 1
    Add the GSAP CDNsgsap, CustomEase, and CustomBounce (order matters).
  2. 2
    Paste HTML, CSS, and JSThe ball drops immediately with the Bouncy preset.
  3. 3
    Watch an impactThe ball squashes flat exactly at floor contact.
  4. 4
    Try SuperballHigher strength: taller rebounds, longer settle.
  5. 5
    Watch the shadowIt grows and darkens on the same ease.
  6. 6
    Tune squashRaise it toward 4 for cartoon-grade deformation.

Real-world uses

Common Use Cases

Notification arrivals
Drop a badge in with impact squash beside a notification bell, using paired position and squash curves from one call.
Modal entrances
Land a confirm dialog with weight, using a softer strength preset so the bounce suits a serious message.
Game HUD pickups
Bounce coins or items into a heads-up display, near physics balls that show the same idea with a real engine.
Empty-state mascots
Bring a character into an empty state with squash against the ground, anchored so deformation flattens at the floor.
Ease education
Compare against stock curves in the GSAP ease gallery, or look at custom wiggle icons for oscillation from the same plugin family.
Related: FLIP Technique List Reorder Animation
See the FLIP Technique List Reorder Animation for a related animations pattern worth pairing with this one.

Got questions?

Frequently Asked Questions

The companion squash ease. bounce.out only shapes a value's trajectory; CustomBounce.create generates that trajectory plus a second registered ease ('name-squash') whose spikes align exactly with each floor contact. Run position and scale tweens over the same duration with the paired eases and squash-and-stretch synchronizes to impacts automatically — the hand-sync this replaces breaks on every retune.

strength (0–1) is restitution — how much energy each rebound keeps: 0.3 dies quickly in low bounces, 0.8 keeps rebounding tall. squash sets how much relative time the ease allocates to deformation at contact versus airborne travel: 1 reads as firm rubber, 3–4 as cartoon. They're independent, so a soft ball can still squash dramatically.

Squash must flatten against the floor: with origin at 50% 100%, scaleY compresses the ball downward onto the contact line while scaleX widens it there. Center-origin squash shrinks toward the middle, visually lifting the ball's underside off the floor mid-impact — the tell-tale mistake in most squash-and-stretch attempts.

It tweens with the same registered 'ball' ease over the same duration — as y approaches the floor the ease's progress drives shadow scale and opacity up, and rebounds pull both back. Named eases are reusable across any tween and property, so one generated curve coordinates ball, squash companion, and shadow.

Each preset needs a different strength, and CustomBounce.create registering under the same name simply replaces the previous curves. The replay then follows the kill-set-play idiom — kill running tweens, reset transforms, start fresh — which keeps mid-animation button mashing safe and means the shadow and squash always pair with the current preset's trajectory.

Register CustomEase and CustomBounce at module scope and create the named ease there too if strength is fixed; parameterized versions re-create inside the play function as here. Trigger from handlers against refs, and killTweensOf the ball in the unmount cleanup (useEffect return, onUnmounted, ngOnDestroy). The stage is simple absolute positioning that maps directly to Tailwind utilities.