Hover✨ Premium

Gravity Drag

Three coloured balls float in a frame and can be grabbed with the mouse or a finger: release them and gravity pulls them back to the floor with bounce and friction. Flick them sharply to watch their trajectory, or set them down gently to stack them.

JSPhysicsDrag

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

Animated background — indie game studio landing page — Gravity Drag example 1

① Animated background — indie game studio landing page

WhenThe visitor lands on the studio's homepage; three coloured balls labelled PLAY, BUILD, WIN fall in the background while they read the pitch. They can grab them with the mouse if curiosity takes over.
WhyA lively, interactive but non-intrusive background sets the studio's world without competing with the main message — the text stays prominent thanks to the opaque gradient at the top.
SettingsZone position: absolute inset:0, height 360 px. Balls in purple, gold and cyan gradients to match the studio palette. gd380-label hidden (display:none); root background transparent.
Priority sorting — SaaS project management backlog — Gravity Drag example 2

② Priority sorting — SaaS project management backlog

WhenIn the tool, each urgent task is a ball (URGENT, NORMAL, LATER). The user drags and releases them to stack them; the physics translates the energy put into prioritising.
WhyThe physical metaphor makes sorting intuitive: throwing a ball up signals urgency, burying it at the bottom means it can wait. Direct interaction is more satisfying than a list drag-and-drop.
SettingsHeader bar 48 px; zone below. Red, orange, blue balls at 50 px. Ghost column markers (High/Medium/Low) with pointer-events:none to guide without blocking interaction.
Coffee mini-game — mobile app for a coffee chain — Gravity Drag example 3

③ Coffee mini-game — mobile app for a coffee chain

WhenIn the app, a mini-game invites users to throw coffee beans into a cup before placing their order. They grab a ball, swing it, and release with a flick.
WhyA quick game before ordering builds brand attachment and increases time in the app. The throw physics make the gesture satisfying even with a poor aim.
SettingsCoffee palette (warm browns, background #100702). Play zone above a dashed target materialised by a cup emoji. Balls styled as coffee beans (radial brown gradient).

How it works

Each ball is a div.gd380-ball positioned absolutely inside the .gd380-zone (300 px tall). Its state properties (x, y, vx, vy, isDragging) are kept in a JavaScript array. The requestAnimationFrame loop updates each ball on every frame with a step normalised to 60 fps: f = dt / 16.67.

When a ball is not grabbed: gravity adds 0.5 × f to vy; if y + 50 exceeds the zone's height, the ball bounces (vy × −0.65) and loses some horizontal speed (vx × 0.85). If it exits through a side, vx is reversed. When grabbed (mousedown or touchstart), its position follows the cursor or finger in the zone's coordinate system, with the grab offset stored; on release (mouseup/touchend), the velocity is derived from the position difference over the last 20 ms to allow flicking.

Accessibility

  • Touch: touchstart, touchmove and touchend events let you grab and throw the balls with a finger on mobile and tablet. preventDefault() blocks page scrolling while a ball is held.
  • Keyboard: the effect does not offer keyboard navigation. The balls are not buttons and do not receive focus. If accessible use is required, add invisible <button> elements that apply a preset impulse to each ball via Enter.
  • Reduced motion: under prefers-reduced-motion: reduce, the loop does not start; the balls stay at their initial positions, visible and readable, without falling or bouncing.
  • Decorative role: the balls carry the words DRAG, ME, UP as a visual call to action. If your context requires different accessible text, add an aria-label on .gd380-zone.

Browser compatibility

Pure DOM and CSS, no dependency. IntersectionObserver available since Chrome 51, Firefox 55, Safari 12.1. Physics via requestAnimationFrame normalised to elapsed time: same behaviour at 60 Hz and 120 Hz. Not verified on real Safari; expected full.

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

Without JavaScript, the three balls show at their initial positions, static. Without IntersectionObserver, the loop also runs off screen.

The code

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

index.html — structure
<div class="fx-0380-root">
  <div class="gd380-zone">
    <div class="gd380-ball ball-1" data-ball="0" style="left:50px;top:60px">DRAG</div>
    <div class="gd380-ball ball-2" data-ball="1" style="left:150px;top:90px">ME</div>
    <div class="gd380-ball ball-3" data-ball="2" style="left:250px;top:50px">UP</div>
  </div>
  <span class="gd380-label" aria-hidden="true">Grab and throw the balls</span>
</div>

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

Customize

Options passed to the API or data-* attributes:

Option / propertyDefaultEffect
Gravity (JS — vy += 0.5 * f) 0.5 px/frame² (normalised to 60 fps) 0.2 gives a lunar fall; 1.5 a fast drop. On a 120 Hz screen, normalisation maintains the same feel as at 60 Hz.
Bounce (JS — vy *= -0.65) 65% of speed retained 0.8 gives very elastic bounces; 0.3 a quick stop. 1.0 is physically impossible and creates an infinite cycle.
Floor friction (JS — vx *= 0.85) 85% of horizontal speed retained 0.98 gives a long slide; 0.5 stops the ball almost instantly.
Ball size (CSS — .gd380-ball { width, height }) 50 px Also adjust floor collisions (y + 50 > zoneH) to the actual ball height.
Colours (CSS — .gd380-ball.ball-1, .ball-2, .ball-3) indigo, violet, pink gradient Replace the background with your brand colours. The box-shadow follows if you keep it on a dark background.

FAQ

Yes. Add div.gd380-ball.ball-4 elements inside .gd380-zone with an initial left/top style, and add the colour in the CSS. The script automatically iterates over all .gd380-ball elements with querySelectorAll. Beyond 6 balls, collisions between them are not handled: they pass through each other.
Yes. The effect simulates gravity, bouncing off walls and the floor, but not ball-to-ball collisions. For circular collisions, compute the distance between each pair of balls on every frame and reverse their velocities when the distance falls below the sum of their radii.
Drag the ball quickly in the desired direction before releasing. The script measures the position difference over the last 20 milliseconds to derive the throw velocity. The faster the gesture, the faster the ball travels.