Orbits by XAWPHAD
by PixelRider35167 lines5.6 KB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Orbits by XAWPHAD</title>
<style>
body { margin: 0; overflow: hidden; background: black; }
canvas { display: block; }
</style>
</head>
<body>
<canvas id="gameCanvas"></canvas>
<script>
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
window.addEventListener('resize', () => {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
});
class Ball {
constructor(x, y, radius, color, vx=0, vy=0, fixed=false) {
this.x = x;
this.y = y;
this.radius = radius;
this.color = color;
this.vx = vx;
this.vy = vy;
this.fixed = fixed;
this.hits = 0;
}
draw() {
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
ctx.fillStyle = this.color;
ctx.fill();
}
update() {
if (this.fixed) return;
if (this.x + this.radius > canvas.width || this.x - this.radius < 0) this.vx *= -1;
if (this.y + this.radius > canvas.height || this.y - this.radius < 0) this.vy *= -1;
this.x += this.vx;
this.y += this.vy;
}
applyGravity(target) {
if (this.fixed) return;
const dx = target.x - this.x;
const dy = target.y - this.y;
const distSq = dx*dx + dy*dy;
const minDist = this.radius + target.radius;
if (distSq < minDist*minDist) return;
const G = 0.05; // gentle gravity
const force = (G * target.radius) / distSq;
const accelFactor = 20 / this.radius; // smaller balls accelerate more
const ax = dx * force * accelFactor;
const ay = dy * force * accelFactor;
const maxAccel = 0.3; // cap acceleration
this.vx += Math.max(Math.min(ax, maxAccel), -maxAccel);
this.vy += Math.max(Math.min(ay, maxAccel), -maxAccel);
}
}
// Helper functions
function rand(min, max) { return Math.random() * (max - min) + min; }
function randColor() { return `hsl(${Math.random() * 360}, 100%, 50%)`; }
let balls = [];
// Center white ball
const centerBall = new Ball(canvas.width/2, canvas.height/2, 40, 'yellow', 0, 0, true);
balls.push(centerBall);
// Click to create new balls (max 10)
canvas.addEventListener('click', (e) => {
const nonFixedBalls = balls.filter(ball => !ball.fixed);
if (nonFixedBalls.length >= 7) return;
const radius = rand(10, 30)/2;
const color = randColor();
const speed = rand(2, 5);
const angle = rand(0, Math.PI*2);
const vx = Math.cos(angle) * speed;
const vy = Math.sin(angle) * speed;
balls.push(new Ball(e.clientX, e.clientY, radius, color, vx, vy));
});
// Collision detection
function handleCollisions() {
for (let i = 0; i < balls.length; i++) {
for (let j = i + 1; j < balls.length; j++) {
const dx = balls[j].x - balls[i].x;
const dy = balls[j].y - balls[i].y;
const dist = Math.sqrt(dx*dx + dy*dy);
if (dist < balls[i].radius + balls[j].radius) {
if (balls[i].fixed && !balls[j].fixed) balls[j].hits = 30;
else if (!balls[i].fixed && balls[j].fixed) balls[i].hits = 30;
else if (!balls[i].fixed && !balls[j].fixed) {
balls[i].hits++;
balls[j].hits++;
const angle = Math.atan2(dy, dx);
const speed1 = Math.sqrt(balls[i].vx**2 + balls[i].vy**2);
const speed2 = Math.sqrt(balls[j].vx**2 + balls[j].vy**2);
const dir1 = Math.atan2(balls[i].vy, balls[i].vx);
const dir2 = Math.atan2(balls[j].vy, balls[j].vx);
const vx1 = speed1 * Math.cos(dir1 - angle);
const vy1 = speed1 * Math.sin(dir1 - angle);
const vx2 = speed2 * Math.cos(dir2 - angle);
const vy2 = speed2 * Math.sin(dir2 - angle);
const finalVx1 = ((balls[i].radius - balls[j].radius) * vx1 + 2 * balls[j].radius * vx2) / (balls[i].radius + balls[j].radius);
const finalVx2 = ((balls[j].radius - balls[i].radius) * vx2 + 2 * balls[i].radius * vx1) / (balls[i].radius + balls[j].radius);
balls[i].vx = Math.cos(angle) * finalVx1 + Math.cos(angle + Math.PI/2) * vy1;
balls[i].vy = Math.sin(angle) * finalVx1 + Math.sin(angle + Math.PI/2) * vy1;
balls[j].vx = Math.cos(angle) * finalVx2 + Math.cos(angle + Math.PI/2) * vy2;
balls[j].vy = Math.sin(angle) * finalVx2 + Math.sin(angle + Math.PI/2) * vy2;
const overlap = 0.5 * (balls[i].radius + balls[j].radius - dist + 1);
balls[i].x -= overlap * (dx / dist);
balls[i].y -= overlap * (dy / dist);
balls[j].x += overlap * (dx / dist);
balls[j].y += overlap * (dy / dist);
}
}
}
}
}
function animate() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
handleCollisions();
balls.forEach(ball => {
if (!ball.fixed) ball.applyGravity(centerBall);
});
balls = balls.filter(ball => ball.fixed || ball.hits < 30);
balls.forEach(ball => {
ball.update();
ball.draw();
});
requestAnimationFrame(animate);
}
animate();
</script>
</body>
</html>
Game Source: Orbits by XAWPHAD
Creator: PixelRider35
Libraries: none
Complexity: moderate (167 lines, 5.6 KB)
The full source code is displayed above on this page.
Remix Instructions
To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: orbits-by-xawphad-pixelrider35" to link back to the original. Then publish at arcadelab.ai/publish.