<script>
const canvas = document.getElementById('pendulum-canvas');
const ctx = canvas.getContext('2d');
function resizeCanvas() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
}
resizeCanvas();
window.addEventListener('resize', resizeCanvas);
// Triple pendulum parameters
const g = 9.81; // gravity
const dt = 0.02; // time step
// Pendulum properties
const m1 = 10, m2 = 10, m3 = 10; // masses
const L1 = 150, L2 = 120, L3 = 100; // lengths (scaled for display)
// Initial angles and angular velocities
let theta1 = Math.PI / 2;
let theta2 = Math.PI / 2;
let theta3 = Math.PI / 2;
let omega1 = 0;
let omega2 = 0;
let omega3 = 0;
// Origin point (top of pendulum)
const originX = canvas.width / 2;
const originY = 100;
// Trail arrays
const trail1 = [];
const trail2 = [];
const trail3 = [];
const maxTrailLength = 800;
function calculatePositions() {
// Position of first pendulum bob
const x1 = originX + L1 * Math.sin(theta1);
const y1 = originY + L1 * Math.cos(theta1);
// Position of second pendulum bob
const x2 = x1 + L2 * Math.sin(theta2);
const y2 = y1 + L2 * Math.cos(theta2);
// Position of third pendulum bob
const x3 = x2 + L3 * Math.sin(theta3);
const y3 = y2 + L3 * Math.cos(theta3);
return { x1, y1, x2, y2, x3, y3 };
}
function updatePhysics() {
// Lagrangian mechanics for triple pendulum
const { x1, y1, x2, y2, x3, y3 } = calculatePositions();
// Simplified physics calculations for triple pendulum
// (This is a numerical approximation of the complex differential equations)
// Angular accelerations (simplified)
const alpha1 = (-g * (2 * m1 + m2 + m3) * Math.sin(theta1) -
m2 * g * Math.sin(theta1 - 2 * theta2) -
2 * Math.sin(theta1 - theta2) * m2 *
(omega2 * omega2 * L2 + omega1 * omega1 * L1 * Math.cos(theta1 - theta2))) /
(L1 * (2 * m1 + m2 + m3 - m2 * Math.cos(2 * theta1 - 2 * theta2)));
const alpha2 = (2 * Math.sin(theta1 - theta2) *
(omega1 * omega1 * L1 * (m1 + m2 + m3) +
g * (m1 + m2 + m3) * Math.cos(theta1) +
omega2 * omega2 * L2 * m2 * Math.cos(theta1 - theta2))) /
(L2 * (2 * m1 + m2 + m3 - m2 * Math.cos(2 * theta1 - 2 * theta2)));
const alpha3 = (-g * m3 * Math.sin(theta3) -
0.5 * m3 * (omega1 * omega1 * L1 + omega2 * omega2 * L2) *
Math.sin(theta3 - theta2)) / (L3 * m3);
// Add damping to prevent infinite energy
const damping = 0.999;
// Update angular velocities
omega1 += alpha1 * dt;
omega2 += alpha2 * dt;
omega3 += alpha3 * dt;
// Apply damping
omega1 *= damping;
omega2 *= damping;
omega3 *= damping;
// Update angles
theta1 += omega1 * dt;
theta2 += omega2 * dt;
theta3 += omega3 * dt;
}
function addToTrail(trail, x, y) {
trail.push({ x, y });
if (trail.length > maxTrailLength) {
trail.shift();
}
}
function drawTrail(trail, color, maxOpacity = 0.8) {
if (trail.length < 2) return;
for (let i = 1; i < trail.length; i++) {
const opacity = (i / trail.length) * maxOpacity;
ctx.strokeStyle = color.replace('1)', `${opacity})`);
ctx.lineWidth = Math.max(0.5, (i / trail.length) * 3);
ctx.beginPath();
ctx.moveTo(trail[i-1].x, trail[i-1].y);
ctx.lineTo(trail[i].x, trail[i].y);
ctx.stroke();
}
}
function draw() {
// Fade the canvas instead of clearing it completely for trail effect
ctx.fillStyle = 'rgba(10, 10, 10, 0.05)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
const { x1, y1, x2, y2, x3, y3 } = calculatePositions();
// Add current positions to trails
addToTrail(trail1, x1, y1);
addToTrail(trail2, x2, y2);
addToTrail(trail3, x3, y3);
// Draw trails
drawTrail(trail1, 'rgba(255, 100, 100, 1)', 0.6);
drawTrail(trail2, 'rgba(100, 255, 100, 1)', 0.7);
drawTrail(trail3, 'rgba(100, 100, 255, 1)', 0.8);
// Draw pendulum rods
ctx.strokeStyle = '#444';
ctx.lineWidth = 3;
// Rod 1
ctx.beginPath();
ctx.moveTo(originX, originY);
ctx.lineTo(x1, y1);
ctx.stroke();
// Rod 2
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.stroke();
// Rod 3
ctx.beginPath();
ctx.moveTo(x2, y2);
ctx.lineTo(x3, y3);
ctx.stroke();
// Draw pendulum bobs
// Bob 1
ctx.fillStyle = '#ff6464';
ctx.beginPath();
ctx.arc(x1, y1, 12, 0, 2 * Math.PI);
ctx.fill();
ctx.strokeStyle = '#fff';
ctx.lineWidth = 2;
ctx.stroke();
// Bob 2
ctx.fillStyle = '#64ff64';
ctx.beginPath();
ctx.arc(x2, y2, 10, 0, 2 * Math.PI);
ctx.fill();
ctx.stroke();
// Bob 3
ctx.fillStyle = '#6464ff';
ctx.beginPath();
ctx.arc(x3, y3, 8, 0, 2 * Math.PI);
ctx.fill();
ctx.stroke();
// Draw pivot point
ctx.fillStyle = '#fff';
ctx.beginPath();
ctx.arc(originX, originY, 6, 0, 2 * Math.PI);
ctx.fill();
}
function animate() {
updatePhysics();
draw();
requestAnimationFrame(animate);
}
// Start the animation
animate();
// Handle window resize
window.addEventListener('resize', () => {
resizeCanvas();
// Clear trails on resize to avoid visual artifacts
trail1.length = 0;
trail2.length = 0;
trail3.length = 0;
});
</script>
<script>
const canvas = document.getElementById('pendulum-canvas');
const ctx = canvas.getContext('2d');
function resizeCanvas() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
}
resizeCanvas();
window.addEventListener('resize', resizeCanvas);
// Triple pendulum parameters
const g = 9.81; // gravity
const dt = 0.02; // time step
// Pendulum properties
const m1 = 10, m2 = 10, m3 = 10; // masses
const L1 = 150, L2 = 120, L3 = 100; // lengths (scaled for display)
// Initial angles and angular velocities
let theta1 = Math.PI / 2;
let theta2 = Math.PI / 2;
let theta3 = Math.PI / 2;
let omega1 = 0;
let omega2 = 0;
let omega3 = 0;
// Origin point (top of pendulum)
const originX = canvas.width / 2;
const originY = 100;
// Trail arrays
const trail1 = [];
const trail2 = [];
const trail3 = [];
const maxTrailLength = 800;
function calculatePositions() {
// Position of first pendulum bob
const x1 = originX + L1 * Math.sin(theta1);
const y1 = originY + L1 * Math.cos(theta1);
// Position of second pendulum bob
const x2 = x1 + L2 * Math.sin(theta2);
const y2 = y1 + L2 * Math.cos(theta2);
// Position of third pendulum bob
const x3 = x2 + L3 * Math.sin(theta3);
const y3 = y2 + L3 * Math.cos(theta3);
return { x1, y1, x2, y2, x3, y3 };
}
function updatePhysics() {
// Lagrangian mechanics for triple pendulum
const { x1, y1, x2, y2, x3, y3 } = calculatePositions();
// Simplified physics calculations for triple pendulum
// (This is a numerical approximation of the complex differential equations)
// Angular accelerations (simplified)
const alpha1 = (-g * (2 * m1 + m2 + m3) * Math.sin(theta1) -
m2 * g * Math.sin(theta1 - 2 * theta2) -
2 * Math.sin(theta1 - theta2) * m2 *
(omega2 * omega2 * L2 + omega1 * omega1 * L1 * Math.cos(theta1 - theta2))) /
(L1 * (2 * m1 + m2 + m3 - m2 * Math.cos(2 * theta1 - 2 * theta2)));
const alpha2 = (2 * Math.sin(theta1 - theta2) *
(omega1 * omega1 * L1 * (m1 + m2 + m3) +
g * (m1 + m2 + m3) * Math.cos(theta1) +
omega2 * omega2 * L2 * m2 * Math.cos(theta1 - theta2))) /
(L2 * (2 * m1 + m2 + m3 - m2 * Math.cos(2 * theta1 - 2 * theta2)));
const alpha3 = (-g * m3 * Math.sin(theta3) -
0.5 * m3 * (omega1 * omega1 * L1 + omega2 * omega2 * L2) *
Math.sin(theta3 - theta2)) / (L3 * m3);
// Add damping to prevent infinite energy
const damping = 0.999;
// Update angular velocities
omega1 += alpha1 * dt;
omega2 += alpha2 * dt;
omega3 += alpha3 * dt;
// Apply damping
omega1 *= damping;
omega2 *= damping;
omega3 *= damping;
// Update angles
theta1 += omega1 * dt;
theta2 += omega2 * dt;
theta3 += omega3 * dt;
}
function addToTrail(trail, x, y) {
trail.push({ x, y });
if (trail.length > maxTrailLength) {
trail.shift();
}
}
function drawTrail(trail, color, maxOpacity = 0.8) {
if (trail.length < 2) return;
for (let i = 1; i < trail.length; i++) {
const opacity = (i / trail.length) * maxOpacity;
ctx.strokeStyle = color.replace('1)', `${opacity})`);
ctx.lineWidth = Math.max(0.5, (i / trail.length) * 3);
ctx.beginPath();
ctx.moveTo(trail[i-1].x, trail[i-1].y);
ctx.lineTo(trail[i].x, trail[i].y);
ctx.stroke();
}
}
function draw() {
// Fade the canvas instead of clearing it completely for trail effect
ctx.fillStyle = 'rgba(10, 10, 10, 0.05)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
const { x1, y1, x2, y2, x3, y3 } = calculatePositions();
// Add current positions to trails
addToTrail(trail1, x1, y1);
addToTrail(trail2, x2, y2);
addToTrail(trail3, x3, y3);
// Draw trails
drawTrail(trail1, 'rgba(255, 100, 100, 1)', 0.6);
drawTrail(trail2, 'rgba(100, 255, 100, 1)', 0.7);
drawTrail(trail3, 'rgba(100, 100, 255, 1)', 0.8);
// Draw pendulum rods
ctx.strokeStyle = '#444';
ctx.lineWidth = 3;
// Rod 1
ctx.beginPath();
ctx.moveTo(originX, originY);
ctx.lineTo(x1, y1);
ctx.stroke();
// Rod 2
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.stroke();
// Rod 3
ctx.beginPath();
ctx.moveTo(x2, y2);
ctx.lineTo(x3, y3);
ctx.stroke();
// Draw pendulum bobs
// Bob 1
ctx.fillStyle = '#ff6464';
ctx.beginPath();
ctx.arc(x1, y1, 12, 0, 2 * Math.PI);
ctx.fill();
ctx.strokeStyle = '#fff';
ctx.lineWidth = 2;
ctx.stroke();
// Bob 2
ctx.fillStyle = '#64ff64';
ctx.beginPath();
ctx.arc(x2, y2, 10, 0, 2 * Math.PI);
ctx.fill();
ctx.stroke();
// Bob 3
ctx.fillStyle = '#6464ff';
ctx.beginPath();
ctx.arc(x3, y3, 8, 0, 2 * Math.PI);
ctx.fill();
ctx.stroke();
// Draw pivot point
ctx.fillStyle = '#fff';
ctx.beginPath();
ctx.arc(originX, originY, 6, 0, 2 * Math.PI);
ctx.fill();
}
function animate() {
updatePhysics();
draw();
requestAnimationFrame(animate);
}
// Start the animation
animate();
// Handle window resize
window.addEventListener('resize', () => {
resizeCanvas();
// Clear trails on resize to avoid visual artifacts
trail1.length = 0;
trail2.length = 0;
trail3.length = 0;
});
</script>