Backgrounds

Ballpit

An interactive physics ballpit — gravity, walls, and ball-to-ball collisions all simulated live on canvas 2D.

A hand-written physics ballpit on canvas 2D: balls fall under gravity, bounce off the walls, and collide with each other, and moving the mouse pushes the nearby balls aside.

Bring this into your project

Run this command in your project folder. The CLI adds the source and dependencies; then follow the usage example below.

npx webberui@latest add ballpit.en

New project or installation trouble? Start here →

Playground

Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.

40
12
34
900
0.82
Active edited usageDefault props
<Ballpit/>

The complete example includes the preview layout and data. Install the component first, then paste this file into your project.

Installation

npx webberui@latest add https://webberui.com/r/ballpit.en.json

Or, once registries are configured in components.json, install it as @webberui/ballpit.

Usage

The ballpit fills its parent container, so put it inside a bounded box with an explicit height and overflow-hidden:

import { Ballpit } from "@/components/ui/ballpit";

<div className="relative h-[360px] w-full overflow-hidden rounded-xl">
  <Ballpit count={48} />
</div>

Props

PropTypeDefaultDescription
countnumber40Number of balls
minSizenumber12Lower bound of the ball radius (px)
maxSizenumber34Upper bound of the ball radius (px)
gravitynumber900Gravitational acceleration (px/s²); set 0 for weightless floating
restitutionnumber0.82Fraction of velocity retained after a collision (0~1)
colorsstring[]5 built-in colorsBall palette, applied by cycling through the list
interactivebooleantrueWhether the mouse pushes nearby balls away
pointerRadiusnumber110Radius over which the mouse's push acts (px)

Accessibility

  • When the user has "reduce motion" enabled at the system level, the ballpit only lays the balls out at their starting positions and holds still
  • The canvas is a purely decorative layer, marked aria-hidden and taking no pointer events, so it interferes with neither the keyboard nor a screen reader
  • The component is self-contained within its parent's dimensions, scaling with the container through a ResizeObserver and cleaning up the animation loop on teardown