// FREE TOOLSView all →
◈Keyword ResearchFreeReal Google + Bing suggestions, grouped by intent.
▣Meta & SERP PreviewsoonSee how your page looks in Google, fix title & description.
◐AI Visibility ChecksoonWhat AI assistants say about your brand.
❖Schema GeneratorsoonCopy-paste JSON-LD for FAQ, Article, Product & more.
← all components
#07
Gravity well
A field of particles orbits a calm center. Move your cursor in and it becomes a gravity source, a black hole that bends their paths, pulls them into a spiral, and stretches them into light-streaks before they fall past the event horizon and respawn on the outer ring. Real inverse-square gravity math on a canvas, 60fps.
ReactCanvasJS
// LIVE DEMO
Become the gravity
Move your cursor. Everything orbits, then falls in.
// THE CODE
React
"use client";
import { useEffect, useRef } from "react";
export default function GravityWell() {
const ref = useRef(null);
useEffect(() => {
const cv = ref.current, ctx = cv.getContext("2d");
if (matchMedia("(prefers-reduced-motion: reduce)").matches) return;
let w, h, raf, cx, cy;
const DPR = Math.min(devicePixelRatio || 1, 2);
const mouse = { x: 0, y: 0, active: false };
let pts = [];
function resize() {
const r = cv.getBoundingClientRect(); w = r.width; h = r.height;
cv.width = w*DPR; cv.height = h*DPR; ctx.setTransform(DPR,0,0,DPR,0,0);
cx = w/2; cy = h/2;
const n = Math.min(150, Math.floor(w*h/5500));
pts = Array.from({ length: n }, () => {
const ang = Math.random()*Math.PI*2, rad = 40 + Math.random()*(Math.min(w,h)*0.42);
const x = cx + Math.cos(ang)*rad, y = cy + Math.sin(ang)*rad, sp = 0.9 + Math.random()*0.5;
return { x, y, px:x, py:y, vx:-Math.sin(ang)*sp, vy:Math.cos(ang)*sp, hue:190 + Math.random()*70 };
});
}
function frame() {
ctx.fillStyle = "rgba(5,8,16,.18)"; ctx.fillRect(0,0,w,h);
ctx.globalCompositeOperation = "lighter";
const gx = mouse.active ? mouse.x : cx, gy = mouse.active ? mouse.y : cy, G = mouse.active ? 2600 : 900;
for (const p of pts) {
p.px = p.x; p.py = p.y;
const dx = gx-p.x, dy = gy-p.y, d2 = Math.max(dx*dx+dy*dy, 140), d = Math.sqrt(d2), a = G/d2;
p.vx += (dx/d)*a; p.vy += (dy/d)*a; p.vx *= .995; p.vy *= .995;
p.x += p.vx; p.y += p.vy;
if (mouse.active && d < 16) {
const ang = Math.random()*Math.PI*2, rad = Math.min(w,h)*0.45;
p.x = cx+Math.cos(ang)*rad; p.y = cy+Math.sin(ang)*rad; p.px = p.x; p.py = p.y;
p.vx = -Math.sin(ang); p.vy = Math.cos(ang);
}
const sp = Math.min(Math.hypot(p.x-p.px, p.y-p.py), 22), al = 0.25 + Math.min(sp/22,1)*0.6;
ctx.strokeStyle = `hsla(${p.hue},95%,65%,${al})`; ctx.lineWidth = 1.6;
ctx.beginPath(); ctx.moveTo(p.px,p.py); ctx.lineTo(p.x,p.y); ctx.stroke();
ctx.fillStyle = `hsla(${p.hue},95%,72%,.9)`;
ctx.beginPath(); ctx.arc(p.x,p.y,1.4,0,7); ctx.fill();
}
const g = ctx.createRadialGradient(gx,gy,0,gx,gy, mouse.active?60:30);
g.addColorStop(0, mouse.active ? "rgba(129,140,248,.5)" : "rgba(56,189,248,.25)");
g.addColorStop(1, "rgba(0,0,0,0)");
ctx.fillStyle = g; ctx.beginPath(); ctx.arc(gx,gy, mouse.active?60:30, 0, 7); ctx.fill();
if (mouse.active) { ctx.globalCompositeOperation = "source-over"; ctx.fillStyle = "#05070d";
ctx.beginPath(); ctx.arc(gx,gy,9,0,7); ctx.fill(); }
ctx.globalCompositeOperation = "source-over";
raf = requestAnimationFrame(frame);
}
const onMove = e => { const r = cv.getBoundingClientRect(); mouse.x = e.clientX-r.left; mouse.y = e.clientY-r.top; mouse.active = true; };
const onLeave = () => mouse.active = false;
resize(); frame();
addEventListener("resize", resize); cv.addEventListener("pointermove", onMove); cv.addEventListener("pointerleave", onLeave);
return () => { cancelAnimationFrame(raf); removeEventListener("resize", resize);
cv.removeEventListener("pointermove", onMove); cv.removeEventListener("pointerleave", onLeave); };
}, []);
return <canvas ref={ref} className="w-full h-[380px] rounded-2xl bg-[#05080f]" />;
}// HOW IT WORKS
- 01Each particle is given a tangential velocity so it naturally orbits the center, like a stable solar system.
- 02Gravity is computed as an inverse-square pull toward the active source (the cursor, or the center when idle), so paths curve realistically.
- 03Particles are drawn as streaks from their previous to current position, so fast motion near the well stretches into light-trails; ones that cross the horizon respawn on the outer ring.
Want a custom component for your product?
Let's build it →