WebberUI

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.

Loading preview…
npx shadcn@latest add https://webberui.com/r/ballpit.json

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
<Ballpit />

Installation

npx shadcn@latest add https://webberui.com/r/ballpit.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

On this page