Physics Engine Demo
HTML
<div class="ui">
Click to spawn balls<br>
Move mouse to interact
</div>
<canvas id="canvas"></canvas>
CSS
body {
margin: 0;
padding: 0;
overflow: hidden;
background-color: #1a1a1a;
font-family: sans-serif;
}
canvas {
display: block;
}
.ui {
position: absolute;
top: 20px;
left: 20px;
color: rgba(255, 255, 255, 0.5);
pointer-events: none;
user-select: none;
}
JavaScript
/**
* PHYSICS ENGINE CONSTANTS
*/
const GRAVITY = 0.25;
const FRICTION = 0.99; // Air resistance
const WALL_BOUNCE = 0.7; // Energy kept after hitting a wall
const SUB_STEPS = 8; // Physics sub-steps for stability
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
let width, height;
let balls = [];
const mouse = { x: -1000, y: -1000, radius: 150 };
// Resize handler
function resize() {
width = window.innerWidth;
height = window.innerHeight;
canvas.width = width;
canvas.height = height;
}
window.addEventListener('resize', resize);
resize();
/**
* BALL CLASS
*/
class Ball {
constructor(x, y, radius, color, mass) {
this.x = x;
this.y = y;
this.radius = radius;
this.mass = mass || radius; // Mass proportional to size
this.color = color;
this.vx = (Math.random() - 0.5) * 10;
this.vy = (Math.random() - 0.5) * 10;
}
draw() {
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
ctx.fillStyle = this.color;
ctx.fill();
// Shine effect
ctx.beginPath();
ctx.arc(this.x - this.radius * 0.3, this.y - this.radius * 0.3, this.radius * 0.1, 0, Math.PI * 2);
ctx.fillStyle = 'rgba(255,255,255,0.5)';
ctx.fill();
}
update() {
// Apply gravity
this.vy += GRAVITY;
// Apply air friction
this.vx *= FRICTION;
this.vy *= FRICTION;
// Update position
this.x += this.vx;
this.y += this.vy;
// Wall Collisions
if (this.x + this.radius > width) {
this.x = width - this.radius;
this.vx *= -WALL_BOUNCE;
} else if (this.x - this.radius < 0) {
this.x = this.radius;
this.vx *= -WALL_BOUNCE;
}
if (this.y + this.radius > height) {
...