CSS Physics Simulation: How to Create a

  • Use Cubic-Bezier: Achieve realistic physics by moving beyond simple keyframes. Cubic-bezier curves allow fine-tuning acceleration and deceleration, mimicking natural forces like gravity.
  • Simulate Impact: Implement squash and stretch principles by adjusting both position and scale simultaneously. This adds weight and realism to the bounce.
  • Combine Concepts: True physics animation combines controlled easing (the bounce) with dimensional changes (the squash/stretch) for maximum visual impact.
  • Understand the Limitations: For complex, multi-object interactions, JavaScript libraries are necessary, but CSS provides powerful tools for single-element physics.

Understanding Physics in CSS: Why Keyframes Aren't Enough

CSS keyframes are excellent for basic, predictable animations. They define an element's state at specific points in time. However, they treat motion as a series of discrete steps. This method lacks the fluidity of real-world physics.

Real-world motion is governed by principles like gravity, inertia, and elasticity. When a ball bounces, its speed changes constantly. It slows down approaching the ground, accelerates rapidly during impact, and slows again while rising. Standard keyframe timing cannot model this dynamic acceleration.

To simulate physics, you must manipulate the easing. Easing controls the rate of change of an animation over time. Instead of relying on linear timing, use the cubic-bezier function. This function allows developers to define an arbitrary acceleration curve, creating a precise simulation of physical forces like gravity.

The Foundation: Setting Up the Basic Bounce Animation

A basic bounce animation requires setting an element's initial state, defining the fall, and then defining the bounce cycle. Start by giving the element an initial height and a defined animation duration.

The core technique involves animating the element's transform property, specifically using `translateY`. Animating `transform` is hardware-accelerated. This ensures smooth performance across devices. We define keyframes for the fall and the subsequent rise.

Consider this basic example. This animation will look mechanical because it lacks realistic timing.

/* HTML */
/* CSS */ .bouncing-ball { width: 50px; height: 50px; background-color: dodgerblue; /* Initial state */ transform: translateY(0); animation: basicBounce 1s infinite alternate; } @keyframes basicBounce { 0% { transform: translateY(0); } 100% { transform: translateY(150px); } }

The first draft of the bounce follows the keyframes rigidly. It lacks the natural settling motion. This is where sophisticated easing becomes necessary.

Mastering the Bounce: Using Cubic-Bezier for Realistic Easing

The bounce curve is the most critical component. Use `cubic-bezier(x1, y1, x2, y2)` to model the forces acting on the object. Do not use simple timing. Calculate a curve that mimics the deceleration and acceleration of a falling object.

For a realistic bounce, the curve must be weighted toward the end of the animation cycle. The object should slow significantly before hitting the ground, and then it should rebound with diminishing energy. A poorly chosen curve makes the bounce feel robotic.

To apply advanced easing, apply the `animation-timing-function` property to the element. You can apply a custom curve to the entire animation. This allows the object to accelerate on the way down, decelerate rapidly upon impact, and then follow a predictable, diminishing arc on the way up. This level of control moves the animation from mere movement to simulated physical action.

/* CSS Enhancement */
.bouncing-ball {
    /* ... other styles ... */
    animation: realisticBounce 1s infinite alternate;
    /* Apply the timing function to the element, not inside keyframes */
    animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1.5);
}

@keyframes realisticBounce {
    0% {
        transform: translateY(0);
    }
    50% {
        /* Adjusting the curve for the fall */
        transform: translateY(150px);
    }
    100% {
        transform: translateY(150px);
    }
}

Advanced Polish: Implementing Squash and Stretch for Impact

A bounce that only manipulates position feels flat. To add weight and volume, implement the classic animation principle of squash and stretch. This principle dictates that an object must visibly deform when subjected to force.

When the ball hits the ground, it must "squash." This means decreasing its vertical scale (`scaleY`) and increasing its horizontal scale (`scaleX`). The object compresses. This visual compression conveys impact and weight.

As the object prepares to leave the ground, it must "stretch." This involves increasing the scale in the direction of movement, giving the illusion of stored energy. By animating both the `transform: translateY()` and the `transform: scale()` properties simultaneously, you transform a simple bounce into a dynamic, satisfying physical event. This combination of precise easing and dimensional transformation is the hallmark of advanced web animation.

/* CSS Implementation with Scale */
.bouncing-ball {
    /* ... other styles ... */
    animation: fullBounce 1s infinite alternate;
    animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1.5);
}

@keyframes fullBounce {
    0% {
        /* Start at normal size and position */
        transform: translateY(0) scale(1, 1);
    }
    50% {
        /* Impact: Squashed (smaller Y, wider X) and at max fall position */
        transform: translateY(150px) scale(1.1, 0.9);
    }
    100% {
        /* Rebound: Stretched (larger Y, narrower X) and back to start position */
        transform: translateY(0) scale(0.9, 1.1);
    }
}
Try it free
Put your animation in motion
Convert your HTML animation to MP4 with AI captions, AI voiceover, and 500k CC0 music tracks. 3 free watermarked renders — no credit card required.
Convert your animation →
3 free renders · $5/month after · Cancel anytime