live - 60fps 1920 x 1080
<!DOCTYPE html>
<html>
<head>
<title>Dot Grid Spotlight</title>
<style>
:root {
--bg-color: #0a0a0f;
--dot-color: #2a2a3a;
--glow-color: #6366f1;
}
* { margin: 0; padding: 0; }
body { height: 100vh; background: var(--bg-color); overflow: hidden; }
canvas { display: block; }
</style>
</head>
<body>
<canvas id="c"></canvas>
<script>
const canvas = document.getElementById('c');
const ctx = canvas.getContext('2d');
// Read customizable colors from CSS variables
const css = getComputedStyle(document.documentElement);
const dotColor = css.getPropertyValue('--dot-color').trim() || '#2a2a3a';
const glowColor = css.getPropertyValue('--glow-color').trim() || '#6366f1';
function hexToRgb(h) {
h = h.replace('#', '');
if (h.length === 3) h = h.split('').map(c => c + c).join('');
const n = parseInt(h, 16);
return [(n >> 16) & 255, (n >> 8) & 255, n & 255];
}
const glow = hexToRgb(glowColor);
let w, h, dots, mouse = { x: -9999, y: -9999 }, hasMouse = false;
const GAP = 34, RADIUS = 1.6, SPOTLIGHT = 160;
function build() {
w = canvas.width = window.innerWidth;
h = canvas.height = window.innerHeight;
dots = [];
const cols = Math.ceil(w / GAP) + 1;
const rows = Math.ceil(h / GAP) + 1;
for (let i = 0; i < cols; i++)
for (let j = 0; j < rows; j++)
dots.push({ x: i * GAP, y: j * GAP });
}
window.addEventListener('mousemove', (e) => { mouse.x = e.clientX; mouse.y = e.clientY; hasMouse = true; });
window.addEventListener('mouseleave', () => { hasMouse = false; });
window.addEventListener('resize', build);
build();
let t = 0;
function draw() {
t += 0.006;
ctx.clearRect(0, 0, w, h);
// When there's no cursor, drift the spotlight so previews stay alive
let mx = mouse.x, my = mouse.y;
if (!hasMouse) {
mx = w * (0.5 + 0.32 * Math.cos(t));
my = h * (0.5 + 0.28 * Math.sin(t * 1.3));
}
for (const d of dots) {
const dx = d.x - mx, dy = d.y - my;
const dist = Math.sqrt(dx * dx + dy * dy);
const inten = Math.max(0, 1 - dist / SPOTLIGHT);
if (inten > 0.01) {
ctx.beginPath();
ctx.arc(d.x, d.y, RADIUS + inten * 2.2, 0, Math.PI * 2);
ctx.fillStyle = `rgba(${glow[0]},${glow[1]},${glow[2]},${0.15 + inten * 0.85})`;
ctx.fill();
} else {
ctx.beginPath();
ctx.arc(d.x, d.y, RADIUS, 0, Math.PI * 2);
ctx.fillStyle = dotColor;
ctx.fill();
}
}
requestAnimationFrame(draw);
}
draw();
</script>
</body>
</html> About this animation
A subtle dot grid that lights up around your cursor with a glowing spotlight — the clean, tactile background popularized by Linear and shadcn.
Related