Blog / JavaScript

Physics Animations: Gravity, Springs and Bounce

Bring your interfaces to life with real physics-based animations. Gravity, springs, bouncing, pendulum and elastic easing: the complete guide to natural and satisfying motion.

Introduction

Physics-based animations create natural and satisfying movements that make your interfaces more intuitive and engaging. Unlike linear CSS transitions, physics simulations produce organic and predictable behaviors that users recognize instinctively.

In this tutorial, we will explore 5 essential physics animation techniques: bounce, springs, gravity, pendulum and elastic easing. Each technique is accompanied by an interactive demo and the complete code.

💡
Good to know

Physics animations rely mainly on requestAnimationFrame and custom cubic-bezier curves. These APIs are supported by all modern browsers.

1. Bounce Effect

The bounce effect is the most recognizable physics animation. It uses a specific Bezier curve to simulate a bouncing object, with natural deceleration at each contact with the ground.

bounce.css
.bounce-ball {
  width: 50px;
  height: 50px;
  background: linear-gradient(135deg, #6366f1, #d946ef);
  border-radius: 50%;

  /* Custom bounce curve */
  animation: bounce 1s
    cubic-bezier(0.36, 0, 0.66, -0.56)
    infinite alternate;
}

@keyframes bounce {
  to {
    transform: translateY(-150px);
  }
}

How it works

The key to the bounce effect lies in the cubic-bezier curve:

  • cubic-bezier(0.36, 0, 0.66, -0.56): the negative value of the 4th parameter creates an overshoot that simulates the bounce
  • infinite alternate: the animation loops by alternating direction, creating a natural back-and-forth movement
  • translateY: the vertical displacement simulates the effect of gravity

2. Spring Animation

The spring animation combines displacement and deformation for an organic effect. The scale applies a squish that mimics a soft object attached to a spring.

spring.css
/* Elastic cubic-bezier for spring effect */
.spring-ball {
  width: 50px;
  height: 50px;
  background: linear-gradient(135deg, #6366f1, #d946ef);
  border-radius: 50%;

  animation: spring 0.6s
    cubic-bezier(0.68, -0.55, 0.265, 1.55)
    infinite alternate;
}

@keyframes spring {
  to {
    /* Deplacement + deformation */
    transform:
      translateY(-100px)
      scale(1.1, 0.9);
  }
}
🎨
Design tip

The value cubic-bezier(0.68, -0.55, 0.265, 1.55) is often called "easeInOutBack". Negative values and values greater than 1 create the elastic overshoot characteristic of springs.

3. Gravity Simulation

For a truly realistic gravity simulation, JavaScript must be used. Velocity and position are calculated at each frame by applying a gravity constant, with a bounce coefficient for damping.

gravity.js
const ball = document.querySelector('.gravity-ball');

let y = 0;
let velocity = 0;
const gravity = 0.5;      // Gravity force
const bounceCoef = 0.7;  // Bounce coefficient
const floor = 150;       // Floor position

function animate() {
  // Apply gravity to velocity
  velocity += gravity;
  y += velocity;

  // Collision with floor
  if (y >= floor) {
    y = floor;
    velocity *= -bounceCoef; // Inverser + amortir
  }

  // Stop when energy is depleted
  if (Math.abs(velocity) < 0.5 && y >= floor - 1) {
    y = floor;
    return;
  }

  ball.style.top = y + 'px';
  requestAnimationFrame(animate);
}

animate();

Key parameters

  • gravity (0.5): the downward acceleration at each frame. The higher the value, the faster the fall
  • bounceCoef (0.7): determines how much energy is retained after each bounce. 1.0 = perfect bounce, 0.0 = no bounce
  • floor (150): the Y position of the floor in pixels. The ball bounces when it reaches this limit
⚠️
Beware of the infinite loop

Without a stop condition, the requestAnimationFrame loop runs indefinitely. Always add a minimum velocity threshold to stop the animation when the object is "at rest".

4. Pendulum Effect

The pendulum is a classic of physics simulation. It can be achieved in pure CSS by combining transform-origin and an animated rotation with sinusoidal easing.

pendulum.css
.pendulum-container {
  position: relative;
  width: 100px;
  height: 200px;
}

.pendulum-string {
  position: absolute;
  top: 0;
  left: 50%;
  width: 2px;
  height: 150px;
  background: #a1a1aa;

  /* Pivot at top */
  transform-origin: top center;
  animation: swing 2s ease-in-out infinite;
}

.pendulum-ball {
  position: absolute;
  bottom: -25px;
  left: 50%;
  transform: translateX(-50%);
  width: 50px;
  height: 50px;
  border-radius: 50%;
}

@keyframes swing {
  0%, 100% {
    transform: rotate(30deg);
  }
  50% {
    transform: rotate(-30deg);
  }
}

The ease-in-out easing is essential for simulating the natural slowing of the pendulum at the extremes of its motion, and acceleration at the center.

5. Elastic Easing

Elastic easing is perfect for user interactions: buttons, menus, modals. The elastic overshoot gives a satisfying visual feedback that makes the interface more lively and responsive.

elastic-button.css
.elastic-btn {
  padding: 16px 32px;
  background: #6366f1;
  color: white;
  border: none;
  border-radius: 12px;
  cursor: pointer;
  font-weight: 600;

  /* Elastic easing */
  transition: transform 0.3s
    cubic-bezier(0.68, -0.55, 0.265, 1.55);
}

.elastic-btn:hover {
  transform: scale(1.1);
}

.elastic-btn:active {
  transform: scale(0.95);
}

You can also create a more advanced elastic easing in JavaScript for custom animations:

elastic-easing.js
// Custom elastic easing function
function elasticOut(t) {
  const p = 0.3;
  return Math.pow(2, -10 * t)
    * Math.sin((t - p / 4)
    * (2 * Math.PI) / p) + 1;
}

// Usage with a manual animation
function animateElastic(element, from, to, duration) {
  const start = performance.now();

  function tick(now) {
    const t = Math.min((now - start) / duration, 1);
    const progress = elasticOut(t);
    const value = from + (to - from) * progress;

    element.style.transform =
      `scale(${value})`;

    if (t < 1) requestAnimationFrame(tick);
  }

  requestAnimationFrame(tick);
}

Best practices

Before concluding, here are some recommendations for using physics animations effectively:

Performance

  • Use transform and opacity only for animations. These properties are composited by the GPU and do not trigger reflow
  • Limit simultaneous active JavaScript simulations. Each requestAnimationFrame consumes resources
  • Add will-change: transform on animated elements to warn the browser
  • Prefer CSS when possible: CSS animations are often more performant than their JavaScript equivalent

Design and UX

  • Keep durations short: 200-500ms for micro-interactions, 500ms-1s for decorative animations
  • Stay consistent: use the same type of easing throughout your interface
  • Don't overload: one or two physics animations per page is enough

Accessibility

accessibility.css
/* Disable animations for sensitive users */
@media (prefers-reduced-motion: reduce) {
  .bounce-ball,
  .spring-ball,
  .pendulum-string {
    animation: none;
  }

  .elastic-btn {
    transition: none;
  }
}
⚠️
Think about accessibility

Always verify that prefers-reduced-motion is respected. For JavaScript simulations, test this media query with window.matchMedia and disable animations accordingly.

Conclusion

Physics animations transform a static interface into a lively and intuitive experience. By mastering custom cubic-bezier curves and JavaScript simulations with requestAnimationFrame, you can create interactions that feel natural and satisfying.

The five techniques presented here cover the most common use cases: bounce for notifications, spring for element transitions, gravity for realistic simulations, pendulum for decorative animations, and elastic easing for micro-interactions.

Don't hesitate to experiment with values and parameters to find the perfect balance between realism and performance for your project.

🎨
Go further

Find dozens of ready-to-use animation effects in our effects library, with one-click copyable code.