<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<style>
html, body {
margin: 0;
padding: 0;
overflow: hidden;
background: black;
height: 100%;
width: 100%;
}
canvas {
display: block;
position: absolute;
top: 0;
left: 0;
}
</style>
</head>
<body>
<canvas id="starfield"></canvas>
<script>
const canvas = document.getElementById("starfield");
const ctx = canvas.getContext("2d");
let stars = [];
const starCount = 300;
const interactiveFraction = 0.25; // 25% of stars interact with mouse
let mouse = { x: null, y: null };
const gravityRadius = 200;
const orbitRadius = 60;
const ambientSpeedLimit = 0.05;
const maxSpeed = 1.5; // NEW: cap for velocity magnitude
function resizeCanvas() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
}
function createStars() {
stars = [];
for (let i = 0; i < starCount; i++) {
const interactive = i < starCount * interactiveFraction;
const x = Math.random() * canvas.width;
const y = Math.random() * canvas.height;
stars.push({
x,
y,
vx: (Math.random() - 0.5) * ambientSpeedLimit,
vy: (Math.random() - 0.5) * ambientSpeedLimit,
radius: Math.random() * 1.5 + 0.5,
angle: Math.random() * Math.PI * 2,
speed: Math.random() * 0.5 + 0.2,
opacity: Math.random(),
opacitySpeed: Math.random() * 0.02 + 0.005,
orbiting: false,
interactive: interactive
});
}
}
function clampVelocity(vx, vy, max) {
const speed = Math.sqrt(vx * vx + vy * vy);
if (speed > max) {
const scale = max / speed;
return { vx: vx * scale, vy: vy * scale };
}
return { vx, vy };
}
function animateStars() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (let star of stars) {
const dx = mouse.x - star.x;
const dy = mouse.y - star.y;
const distance = Math.sqrt(dx * dx + dy * dy);
// Gravitational or orbital only for interactive stars
if (star.interactive) {
if (mouse.x !== null && distance < orbitRadius) {
star.orbiting = true;
} else {
star.orbiting = false;
}
if (star.orbiting) {
star.angle += 0.05;
star.x = mouse.x + Math.cos(star.angle) * orbitRadius;
star.y = mouse.y + Math.sin(star.angle) * orbitRadius;
star.vx = 0;
star.vy = 0;
} else if (mouse.x !== null && distance < gravityRadius) {
const force = 1 / (distance * 0.05);
const ax = (dx / distance) * force;
const ay = (dy / distance) * force;
star.vx += ax;
star.vy += ay;
// Clamp speed
const clamped = clampVelocity(star.vx, star.vy, maxSpeed);
star.vx = clamped.vx;
star.vy = clamped.vy;
}
}
// Movement
if (!star.orbiting) {
star.vx *= 0.98;
star.vy *= 0.98;
star.x += star.vx;
star.y += star.vy;
}
// Bounce at edges
if (star.x < 0 || star.x > canvas.width) star.vx *= -1;
if (star.y < 0 || star.y > canvas.height) star.vy *= -1;
// Twinkling
star.opacity += star.opacitySpeed;
if (star.opacity >= 1 || star.opacity <= 0) {
star.opacitySpeed *= -1;
}
// Draw
ctx.beginPath();
ctx.arc(star.x, star.y, star.radius, 0, Math.PI * 2);
ctx.fillStyle = `rgba(255, 255, 255, ${star.opacity})`;
ctx.fill();
}
requestAnimationFrame(animateStars);
}
window.addEventListener('resize', () => {
resizeCanvas();
createStars();
});
window.addEventListener('mousemove', (e) => {
mouse.x = e.clientX;
mouse.y = e.clientY;
});
window.addEventListener('mouseout', () => {
mouse.x = null;
mouse.y = null;
});
resizeCanvas();
createStars();
animateStars();
</script>
</body>
</html>
/* Add your styles here */
// Add your code here