Hover✨ Premium

Collision Detection

Eight coloured spheres generated on initialisation bounce continuously inside their allocated area: every frame detects circle-to-circle collisions and wall bounces, then swaps velocities. Autonomous simulation with no user interaction, loop paused off-screen.

JSCollisionPhysics

Updated

This effect is part of Effect.Labs — 840 vanilla effects, some free, some premium. Hover has 48 effects, including 5 free. Explore the category →

3 usage examples

Full-screen hero background for a tech conference — coloured balls bouncing behind the title and dates — Collision Detection example 1

① Full-screen hero background for a tech conference — coloured balls bouncing behind the title and dates

WhenColoured spheres bounce in the background while the conference title, dates and CTA float in the foreground.
WhyColliding balls evoke the energy of a tech conference without a static image to load; the autonomous animation holds attention without requiring interaction.
SettingsRoot at position:absolute;inset:0 with transparent background; random HSL ball colours; text overlay at z-index:10 with a gradient backdrop to remain readable.
Active nodes widget in an infrastructure dashboard — 5 balls = 5 running services — Collision Detection example 2

② Active nodes widget in an infrastructure dashboard — 5 balls = 5 running services

WhenA compact panel on the right side of the dashboard shows active infrastructure nodes; balls bounce off each other to signal the system is alive.
WhyAn autonomous animation in a monitoring widget replaces a simple blinking indicator: it communicates continuous activity without requiring real-time server events.
Settings5 visible balls (3 extras hidden by data-scenes) recoloured to green (#22c55e) and amber (#f59e0b) for status; 240 px wide container with overflow:hidden.
8 category balls bouncing freely in a creative blog navigation section — Collision Detection example 3

③ 8 category balls bouncing freely in a creative blog navigation section

WhenA navigation section shows blog themes as coloured balls bouncing continuously; the user clicks a ball to filter the feed.
WhyCategories that collide suggest editorial diversity and energy without a static list; the autonomous animation draws the eye without requiring interaction.
Settings8 balls, all labelled by data-scenes via textContent (Design, Code, Audio, Video, UX, Data, AI, 3D); 7px bold white font; full-width area inside a rounded frame.

How it works

The .fx-0387-root wrapper centres the collision area .fx-0387-area, a container that is 100 % wide with a height that follows its parent. At init, eight div.fx-0387-ball elements are created with a random radius (12–22 px), a random HSL colour, and an initial velocity of ±4 px per frame at 60 fps. Their starting positions are drawn randomly within the container.

Each frame, the loop moves every ball by its velocity (scaled by the elapsed time since the previous frame, capped at 50 ms to prevent jumps after a background tab). It checks the frame edges (simple bounce: flip the sign of the matching velocity component) then checks every ball pair for collisions: if the distance between centres is less than the sum of radii, velocities are recalculated along the collision angle.

A ResizeObserver watches .fx-0387-area and re-reads its dimensions on every resize, keeping balls inside the area without recreating them. An IntersectionObserver watches the root: the requestAnimationFrame loop stops when the area leaves the viewport and resumes on return, with no frames computed off-screen. Under reduced motion, the balls are displayed at their initial positions with no loop.

Accessibility

  • Reduced motion: under prefers-reduced-motion: reduce, the loop does not start. The balls are shown at their initial positions, visible against the dark background. The animation carries no informational content.
  • Decorative role: the animation is purely visual. It carries no information accessible by other means. If the balls represent named entities (molecules, projects), those names must be exposed as text beside the animation area.
  • Keyboard and touch: no user interaction is built into this effect — the balls move autonomously. There are no focusable elements or touch handlers.
  • Off-screen: the loop stops automatically when the area leaves the viewport. In a hidden tab, the browser suspends requestAnimationFrame calls on its own.

Browser compatibility

ES5 JavaScript (requestAnimationFrame, IntersectionObserver, matchMedia), no dependency. IntersectionObserver available since Chrome 51, Firefox 55 and Safari 12.1. Not measured in Firefox and WebKit.

Chrome 51+✓ Full (measured)
Firefox 55+✓ Full (not measured)
Safari 12.1+✓ Full (not measured)
Edge 79+✓ Full (Chromium engine)
Mobile iOS 12.2+✓ Full (not measured)
Android Chrome 51+✓ Full (measured on Chromium)

Without JavaScript, the frame stays empty. Without IntersectionObserver, the loop runs off-screen too. Without matchMedia, the animation starts even under reduced motion.

The code

HTML structure to paste into your page (the CSS + JS come with your purchase):

index.html — structure
<div class="fx-0387-root"><div class="physics-container"><div class="fx-0387-area"></div></div></div>

One-time payment, no subscription — the code is yours forever

Customize

Options passed to the API or data-* attributes:

Option / propertyDefaultEffect
Number of balls (JS — loop for (var n = 0; n < 8; n++)) 8 balls 4 balls give a sparser simulation, 16 a very busy frame with frequent collisions.
Size range (JS — var r = 25 + 20 * Math.random()) Radius between 12.5 and 22.5 px Increase the first term for larger spheres, reduce the second for more uniform sizes.
Initial speed (JS — vx: (Math.random() - 0.5) * 4) ±2 px per frame at 60 fps Multiply by 2 to double the speed. Bounces remain correct at any speed.
Colours (JS — 'hsl(' + Math.round(360 * Math.random()) + ',70%,60%)') Random hue, 70 % saturation, 60 % lightness Replace with a fixed list of hex colours for a controlled palette: ['#6366f1','#a855f7','#ec4899'][n % 3].
Area height (CSS — .physics-container { height: 300px }) 300 px (width always follows the container) Increase or reduce the CSS value to match your layout. Width is read dynamically at init and updated on resize.

FAQ

No. The loop detects collisions with all four walls and flips the matching velocity component. Balls stay within the bounds read from clientWidth and clientHeight of .fx-0387-area, updated automatically on resize.
When the distance between two centres is less than the sum of their radii, the collision angle is calculated and velocities are mirrored. It is not perfectly elastic physics, but the visual result is convincing.
Yes: add a click listener to each el at init. The DOM element is accessible in the closure via the balls array.