Skip to content

Instantly share code, notes, and snippets.

@HarryStevens
Last active Nov 20, 2020
Embed
What would you like to do?
Pendulum Party
license: gpl-3.0
<!DOCTYPE html>
<html>
<head>
<style>
body {
margin: 0;
}
</style>
</head>
<body>
<canvas></canvas>
<script>
const vecmath = {
// Add vector w to vector v
add: (v, w) => {
let out = [];
for (let i = 0; i < v.length; i++){
out[i] = v[i] + w[i];
}
return out;
},
// Translate position of vector v by an angle in radians and a distance in pixels
trans: (v, ang, dist) => [v[0] + dist * Math.cos(ang), v[1] + dist * Math.sin(ang)]
};
// The Pendulum class
class Pendulum {
constructor(){
this._acceleration = 0;
this._angle = Math.PI / 4;
this._damping = 1.0;
this._gravity = 0.4;
this._length = 200;
this._origin = [innerWidth / 2, 0];
this._velocity = 0;
this._position = vecmath.trans(this._origin, this._angle, this._length);
return this;
}
acceleration(num){
return num ? (this._acceleration = num, this) : this._acceleration;
}
angle(num){
return num ? (this._angle = num, this) : this._angle;
}
damping(num){
return num ? (this._damping = num, this) : this._damping;
}
gravity(num){
return num ? (this._gravity = num, this) : this._gravity;
}
length(num){
return num ? (this._length = num, this) : this._length;
}
origin(arr){
return arr ? (this._origin = arr, this) : this._origin;
}
position(arr){
return arr ? (this._position = arr, this) : this._position;
}
velocity(num){
return num ? (this._velocity = num, this) : this._velocity;
}
tick(){
return this
.acceleration((-1 * this.gravity() / this.length()) * Math.sin(this.angle()))
.velocity((this.velocity() + this.acceleration()) * this.damping())
.angle(this.angle() + this.velocity())
.position(
vecmath.add(
[this.length() * Math.sin(this.angle()), this.length() * Math.cos(this.angle())],
this.origin()
)
);
}
}
// Create the pendula
const pendula = [];
const rows = 8;
const cols = 10;
let i = 1;
for (let row = 0; row < rows; row++){
const evenRow = row % 2 === 0;
for (let col = 0; col < cols; col++){
if (evenRow && col === cols - 1) continue;
const px = innerWidth / cols;
const x = px / 2 + col * px + (evenRow ? px / 2 : 0);
const y = 10 + row * innerHeight / rows;
pendula.push(
new Pendulum()
.gravity(.15)
.length(-10 + px / 2)
.origin([x, y])
.angle(Math.PI / 2 * (i % 2 === 0 ? 1 : -1))
);
i++;
}
}
// Draw to Canvas
const canvas = document.querySelector("canvas");
canvas.width = innerWidth;
canvas.height = innerHeight;
const ctx = canvas.getContext("2d");
ctx.strokeStyle = "#aaa";
function tick(){
requestAnimationFrame(tick);
ctx.clearRect(0, 0, innerWidth, innerHeight);
pendula.forEach((p, i, e) => {
draw(p, `rgb(200, 40, ${(i + 1) / e.length * 255})`);
});
}
tick();
function draw(p, fill){
p.tick();
ctx.fillStyle = fill;
ctx.beginPath();
ctx.moveTo(...p.origin());
ctx.lineTo(...p.position());
ctx.stroke();
ctx.beginPath();
ctx.arc(...p.origin(), 3, 0, Math.PI * 2)
ctx.fill();
ctx.beginPath();
ctx.arc(...p.position(), 10, 0, Math.PI * 2)
ctx.fill();
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment