---
title: "How to Create Physics / Bouncing Animations in Framer"
description: "Build physics-based bouncing animations in Framer — real gravity and collision math, a working code override, spring tuning, and a free component."
canonical_url: "https://framerhub.io/blog/physics-bounce-animation-framer"
last_updated: "2027-03-03T00:00:00.000Z"
---

Physics-based bounce — objects that fall under simulated gravity, hit a boundary, and bounce with realistic energy loss — is a step beyond a standard spring animation. A spring settles a single value toward a target; real bounce physics involves accumulating velocity, detecting collisions, and losing energy on impact, closer to an actual simulation than a single animation curve. Here's how the math works and how to build it.

## Spring Animation vs. Real Physics Bounce

It's worth being precise about the distinction, since the two are often conflated:

- **A spring transition** (Framer's built-in spring easing) animates one value toward a target with realistic-feeling overshoot and settle, governed by stiffness and damping parameters. It's excellent for UI motion — a card settling into place, a modal appearing — but it's fundamentally a curve applied to a single transition, not an ongoing simulation.
- **Real physics bounce** involves a continuously updated simulation: gravity accumulates downward velocity every frame, position updates based on that velocity, and when a boundary is hit, velocity reverses and reduces based on a restitution (bounciness) value. This is closer to how an actual physical object behaves over time, including multiple bounces that gradually lose height and eventually settle.

Springs are the right tool for most interactive UI motion. Physics bounce is the right tool when you specifically want objects that fall, hit something, and bounce — a decorative background of bouncing shapes, an interactive drag-and-release object, or a playful loading indicator.

It's worth being clear about this distinction upfront because reaching for a full physics simulation when a spring transition would actually solve the problem is a common source of unnecessary complexity. If what you actually want is "this element settles into place with a bit of natural-feeling overshoot," a spring transition is simpler to implement, cheaper to run, and gives you that exact result directly — save the full gravity-and-collision approach for when you genuinely need ongoing, multi-bounce physical behavior rather than a single settling motion.

## Where the Math Comes From

The core loop below is a simplified version of Euler integration, the same basic numerical approach used in far more sophisticated physics engines, just without the added complexity of handling arbitrary shapes, rotational forces, or friction between surfaces. Gravity is modeled as a constant downward acceleration added to vertical velocity every frame; position is then updated by that velocity every frame; and collisions are detected by simple boundary checks rather than more general shape-intersection math. This simplicity is a feature, not a limitation, for the specific case of one or a few simple round or rectangular objects bouncing within a fixed container — it's accurate enough to look convincing while staying easy to read, tune, and reason about.

## Building the Physics Loop

Here's a working implementation for a single ball bouncing under gravity within a bounded container.

```jsx
import { useRef, useEffect } from "react"
import { addPropertyControls, ControlType } from "framer"

export default function BouncingBall(props) {
  const { color, size, gravity, restitution } = props
  const ballRef = useRef(null)
  const state = useRef({ x: 100, y: 0, vx: 3, vy: 0 })
  const containerRef = useRef(null)

  useEffect(() => {
    let frameId

    function tick() {
      const container = containerRef.current
      const bounds = { width: container.clientWidth, height: container.clientHeight }
      const s = state.current

      s.vy += gravity
      s.x += s.vx
      s.y += s.vy

      if (s.y + size > bounds.height) {
        s.y = bounds.height - size
        s.vy = -s.vy * restitution
      }
      if (s.x <= 0 || s.x + size > bounds.width) {
        s.vx = -s.vx
        s.x = Math.max(0, Math.min(s.x, bounds.width - size))
      }

      if (ballRef.current) {
        ballRef.current.style.transform = `translate(${s.x}px, ${s.y}px)`
      }
      frameId = requestAnimationFrame(tick)
    }

    frameId = requestAnimationFrame(tick)
    return () => cancelAnimationFrame(frameId)
  }, [gravity, restitution, size])

  return (
    <div ref={containerRef} style={{ position: "relative", width: "100%", height: "100%", overflow: "hidden" }}>
      <div
        ref={ballRef}
        style={{
          position: "absolute",
          width: size,
          height: size,
          borderRadius: "50%",
          background: color,
          willChange: "transform",
        }}
      />
    </div>
  )
}

addPropertyControls(BouncingBall, {
  color: { type: ControlType.Color, defaultValue: "#5B5FEF" },
  size: { type: ControlType.Number, defaultValue: 40, min: 10, max: 120 },
  gravity: { type: ControlType.Number, defaultValue: 0.6, min: 0.1, max: 2, step: 0.1 },
  restitution: { type: ControlType.Number, defaultValue: 0.75, min: 0.3, max: 0.98, step: 0.01 },
})
```

The core loop, run every frame via `requestAnimationFrame`, does four things in order: **accumulate velocity from gravity** (`s.vy += gravity`), **update position from velocity** (`s.x/y += s.vx/vy`), **detect boundary collision** (checking if the ball's position exceeds the container's edges), and **respond to collision** by reversing and reducing velocity (`s.vy = -s.vy * restitution`). This four-step pattern — accumulate, update, detect, respond — is the foundation of essentially all simple physics simulations, whether you're bouncing one ball or building something more complex.

## Understanding Restitution (Bounciness)

`restitution` is the single most important tuning value for how the bounce *feels*, and it directly represents how much velocity survives each collision:

- **1.0** — a perfectly elastic bounce that never loses energy, bouncing at the same height forever. This reads as unnatural, since real-world materials always lose some energy to sound, heat, and deformation.
- **0.7-0.85** — a lively, "bouncy ball" feel that settles gradually over several seconds, roughly matching how a rubber ball actually behaves.
- **0.3-0.5** — a heavy, "dead" bounce, like a beanbag or a poorly inflated ball, settling quickly after just one or two bounces.

Match restitution to the personality you want the animation to convey — a playful loading indicator wants the lively end of the range; a "settling into place" UI moment wants something closer to the low end, since you want it to stop moving quickly rather than bouncing around for several seconds.

## Multiple Balls and Ball-to-Ball Collision

The single-ball loop above handles boundary collision cleanly, but extending it to multiple balls that collide with *each other* — not just the container's edges — is a meaningfully harder problem: you need to check every ball against every other ball each frame (a cost that scales with the square of the ball count), calculate collision response based on both objects' mass and velocity, and handle the edge cases of overlapping bodies correctly. For more than a handful of mutually-colliding objects, this is where reaching for a dedicated physics library like Matter.js becomes the practical choice rather than hand-rolling the collision math — Matter.js handles multi-body collision, mass, friction, and constraints correctly and efficiently, at the cost of an added dependency and a steeper learning curve than the simple single-object loop above.

## Drop-In Option: Gravity Bouncing Balls

FramerHub's [Bouncing Balls component](/components/bouncing-balls) implements a full multi-ball physics simulation with gravity, restitution, ball count, size, and color all exposed as property panel controls — and it's free, so it's a zero-risk way to add this effect to a hero background or playful section without hand-rolling and tuning the physics loop yourself. It's part of FramerHub's [component library](/components), available through the [Components plugin](/plugins/framer-components) alongside other interactive and decorative effects.

## Where Physics Bounce Works Best

- **Decorative hero or background elements** — a scatter of softly bouncing shapes adding a playful, alive quality without competing with foreground content.
- **Loading indicators and empty states**, where physics motion fills otherwise static screen time with something more engaging than a standard spinner.
- **Interactive drag-and-release elements**, where an object gently bounces against its container's edges when released, adding tactile feedback to a drag interaction.
- **Playful brand moments** — 404 pages, easter eggs, or brand personality touches where a bit of whimsy is appropriate.

It's a poor fit for professional, information-dense contexts (dashboards, pricing pages, enterprise SaaS marketing) where the playful, physical quality of bouncing objects can undercut a more serious or authoritative brand tone. Match the effect's inherent personality to your brand's, not just its technical impressiveness.

## Performance Notes

A `requestAnimationFrame` loop updating `transform` directly (as in the snippet, via `style.transform` rather than a React state update per frame) is efficient — it avoids triggering a React re-render on every single frame, which would add unnecessary overhead for an animation running 60 times per second. Keep the number of simultaneously simulated objects reasonable (a dozen or so for simple boundary-only collision is comfortable; ball-to-ball collision scales worse and should be tested carefully as object count grows) and prefer `transform` over `top`/`left` for positioning, consistent with every other GPU-composited animation technique covered elsewhere in this series.

## Accessibility

Continuous, unpredictable motion (bouncing objects don't move in a simple, predictable loop the way a fade or slide does) can be more visually taxing for motion-sensitive visitors than a standard transition. Respect `prefers-reduced-motion` by pausing the simulation entirely or replacing it with a static arrangement of the same shapes for visitors who've set that preference — since this is a decorative effect, nothing about the page's actual content or function depends on the physics running.

## Common Mistakes

- **Updating React state every animation frame instead of mutating a DOM style directly.** Driving position through `setState` inside a `requestAnimationFrame` loop forces a re-render 60 times per second, which is far more expensive than directly setting `style.transform` on a ref, as the snippet above does.
- **A restitution value at or above 1.0**, producing an object that bounces forever at the same height rather than gradually settling — almost never the intended result, and worth double-checking if a bounce animation feels "wrong" without an obvious cause.
- **No boundary clamping after a collision response.** Simply reversing velocity without also correcting position (as the snippet does with `s.y = bounds.height - size`) can let an object visually clip slightly outside its container for a frame or two before the reversed velocity pulls it back, producing a small but noticeable glitch at each bounce.
- **Ignoring willChange: "transform".** This CSS hint (included in the snippet) tells the browser to optimize the element for frequent transform changes ahead of time, which can measurably smooth out animation that would otherwise show minor jank on its first few frames.

## FAQ

**What's the difference between a spring animation and real physics bounce?**
A spring animation (Framer's built-in spring transitions) simulates a single object settling toward a target value with realistic overshoot and damping. Real physics bounce involves gravity accumulating velocity over time, collision detection against boundaries or other objects, and energy loss on each bounce — a fundamentally more involved simulation than a single spring curve.

**Can I build gravity and collision in Framer without a physics library?**
For a small number of objects with simple boundary collision, yes — the core loop (accumulate velocity from gravity, update position, detect and respond to boundary collisions) is straightforward to hand-code, as shown in this guide. For many objects colliding with each other, a dedicated physics library like Matter.js is the more practical choice.

**Why do my bouncing objects lose energy and eventually stop?**
This is intentional and realistic — a restitution (bounciness) value below 1.0 means each bounce returns less velocity than the object had before impact, mimicking energy loss to sound, heat, and material deformation in a real bounce. A restitution of exactly 1.0 would bounce forever, which reads as unnatural.

**Should physics animations use requestAnimationFrame or a library?**
For a handful of independently bouncing objects with simple boundary collision, a hand-rolled requestAnimationFrame loop (as shown in this guide) is lightweight and sufficient. For object-to-object collision, complex constraints, or many simultaneous bodies, a physics engine like Matter.js handles the harder math correctly and is worth the added dependency.

**Is a physics-based animation good for interactive UI elements, not just decoration?**
Yes — subtle physics (a button that settles with a slight overshoot, a dragged card that bounces gently against its container's edge) is a well-established technique for making interactive elements feel tactile and responsive, distinct from purely decorative bouncing-ball backgrounds.
