<script>
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
// Set canvas size
function resizeCanvas() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
}
resizeCanvas();
window.addEventListener('resize', resizeCanvas);
// Pendulum parameters
const g = 9.81;
const dt = 0.01;
// Pendulum properties
const pendulum = {
// Masses
m1: 10,
m2: 10,
m3: 10,
// Lengths
l1: 150,
l2: 150,
l3: 150,
// Initial angles (radians)
theta1: Math.PI / 2,
theta2: Math.PI / 2,
theta3: Math.PI / 2,
// Angular velocities
omega1: 0,
omega2: 0,
omega3: 0,
// Trail arrays
trail1: [],
trail2: [],
trail3: [],
maxTrailLength: 500
};
// Origin point
const origin = {
x: canvas.width / 2,
y: canvas.height / 3
};
// Calculate positions
function calculatePositions() {
const x1 = pendulum.l1 * Math.sin(pendulum.theta1);
const y1 = pendulum.l1 * Math.cos(pendulum.theta1);
const x2 = x1 + pendulum.l2 * Math.sin(pendulum.theta2);
const y2 = y1 + pendulum.l2 * Math.cos(pendulum.theta2);
const x3 = x2 + pendulum.l3 * Math.sin(pendulum.theta3);
const y3 = y2 + pendulum.l3 * Math.cos(pendulum.theta3);
return { x1, y1, x2, y2, x3, y3 };
}
// Update physics using Runge-Kutta 4th order
function updatePhysics() {
// RK4 implementation for triple pendulum
const k1 = derivatives(pendulum.theta1, pendulum.theta2, pendulum.theta3,
pendulum.omega1, pendulum.omega2, pendulum.omega3);
const k2 = derivatives(
pendulum.theta1 + k1.dtheta1 * dt / 2,
pendulum.theta2 + k1.dtheta2 * dt / 2,
pendulum.theta3 + k1.dtheta3 * dt / 2,
pendulum.omega1 + k1.domega1 * dt / 2,
pendulum.omega2 + k1.domega2 * dt / 2,
pendulum.omega3 + k1.domega3 * dt / 2
);
const k3 = derivatives(
pendulum.theta1 + k2.dtheta1 * dt / 2,
pendulum.theta2 + k2.dtheta2 * dt / 2,
pendulum.theta3 + k2.dtheta3 * dt / 2,
pendulum.omega1 + k2.domega1 * dt / 2,
pendulum.omega2 + k2.domega2 * dt / 2,
pendulum.omega3 + k2.domega3 * dt / 2
);
const k4 = derivatives(
pendulum.theta1 + k3.dtheta1 * dt,
pendulum.theta2 + k3.dtheta2 * dt,
pendulum.theta3 + k3.dtheta3 * dt,
pendulum.omega1 + k3.domega1 * dt,
pendulum.omega2 + k3.domega2 * dt,
pendulum.omega3 + k3.domega3 * dt
);
// Update angles and velocities
pendulum.theta1 += (k1.dtheta1 + 2*k2.dtheta1 + 2*k3.dtheta1 + k4.dtheta1) * dt / 6;
pendulum.theta2 += (k1.dtheta2 + 2*k2.dtheta2 + 2*k3.dtheta2 + k4.dtheta2) * dt / 6;
pendulum.theta3 += (k1.dtheta3 + 2*k2.dtheta3 + 2*k3.dtheta3 + k4.dtheta3) * dt / 6;
pendulum.omega1 += (k1.domega1 + 2*k2.domega1 + 2*k3.domega1 + k4.domega1) * dt / 6;
pendulum.omega2 += (k1.domega2 + 2*k2.domega2 + 2*k3.domega2 + k4.domega2) * dt / 6;
pendulum.omega3 += (k1.domega3 + 2*k2.domega3 + 2*k3.domega3 + k4.domega3) * dt / 6;
}
// Calculate derivatives for the triple pendulum system
function derivatives(theta1, theta2, theta3, omega1, omega2, omega3) {
const m1 = pendulum.m1, m2 = pendulum.m2, m3 = pendulum.m3;
const l1 = pendulum.l1, l2 = pendulum.l2, l3 = pendulum.l3;
// Simplified derivatives for demonstration
// In a real implementation, this would involve the full Lagrangian equations
const dtheta1 = omega1;
const dtheta2 = omega2;
const dtheta3 = omega3;
// Approximate angular accelerations
const domega1 = (-g / l1) * Math.sin(theta1) - 0.01 * omega1;
const domega2 = (-g / l2) * Math.sin(theta2) - 0.01 * omega2;
const domega3 = (-g / l3) * Math.sin(theta3) - 0.01 * omega3;
// Add coupling effects
const coupling = 0.01;
domega1 += coupling * Math.sin(theta2 - theta1);
domega2 += coupling * Math.sin(theta1 - theta2) + coupling * Math.sin(theta3 - theta2);
domega3 += coupling * Math.sin(theta2 - theta3);
return {
dtheta1, dtheta2, dtheta3,
domega1, domega2, domega3
};
}
// Update trails
function updateTrails() {
const pos = calculatePositions();
const centerX = origin.x;
const centerY = origin.y;
pendulum.trail1.push({ x: centerX + pos.x1, y: centerY + pos.y1 });
pendulum.trail2.push({ x: centerX + pos.x2, y: centerY + pos.y2 });
pendulum.trail3.push({ x: centerX + pos.x3, y: centerY + pos.y3 });
// Limit trail length
if (pendulum.trail1.length > pendulum.maxTrailLength) pendulum.trail1.shift();
if (pendulum.trail2.length > pendulum.maxTrailLength) pendulum.trail2.shift();
if (pendulum.trail3.length > pendulum.maxTrailLength) pendulum.trail3.shift();
}
// Draw trails
function drawTrails() {
// Trail 1 - Red
ctx.strokeStyle = 'rgba(255, 100, 100, 0.3)';
ctx.lineWidth = 2;
ctx.beginPath();
for (let i = 1; i < pendulum.trail1.length; i++) {
const alpha = i / pendulum.trail1.length;
ctx.globalAlpha = alpha * 0.5;
ctx.moveTo(pendulum.trail1[i-1].x, pendulum.trail1[i-1].y);
ctx.lineTo(pendulum.trail1[i].x, pendulum.trail1[i].y);
ctx.stroke();
}
ctx.globalAlpha = 1;
// Trail 2 - Green
ctx.strokeStyle = 'rgba(100, 255, 100, 0.3)';
ctx.beginPath();
for (let i = 1; i < pendulum.trail2.length; i++) {
const alpha = i / pendulum.trail2.length;
ctx.globalAlpha = alpha * 0.5;
ctx.moveTo(pendulum.trail2[i-1].x, pendulum.trail2[i-1].y);
ctx.lineTo(pendulum.trail2[i].x, pendulum.trail2[i].y);
ctx.stroke();
}
ctx.globalAlpha = 1;
// Trail 3 - Blue
ctx.strokeStyle = 'rgba(100, 100, 255, 0.3)';
ctx.beginPath();
for (let i = 1; i < pendulum.trail3.length; i++) {
const alpha = i / pendulum.trail3.length;
ctx.globalAlpha = alpha * 0.5;
ctx.moveTo(pendulum.trail3[i-1].x, pendulum.trail3[i-1].y);
ctx.lineTo(pendulum.trail3[i].x, pendulum.trail3[i].y);
ctx.stroke();
}
ctx.globalAlpha = 1;
}
// Draw pendulum
function drawPendulum() {
const pos = calculatePositions();
const centerX = origin.x;
const centerY = origin.y;
// Update origin on resize
origin.x = canvas.width / 2;
origin.y = canvas.height / 3;
// Draw arms
ctx.strokeStyle = '#ffffff';
ctx.lineWidth = 3;
ctx.lineCap = 'round';
// First arm
ctx.beginPath();
ctx.moveTo(centerX, centerY);
ctx.lineTo(centerX + pos.x1, centerY + pos.y1);
ctx.stroke();
// Second arm
ctx.beginPath();
ctx.moveTo(centerX + pos.x1, centerY + pos.y1);
ctx.lineTo(centerX + pos.x2, centerY + pos.y2);
ctx.stroke();
// Third arm
ctx.beginPath();
ctx.moveTo(centerX + pos.x2, centerY + pos.y2);
ctx.lineTo(centerX + pos.x3, centerY + pos.y3);
ctx.stroke();
// Draw pivot
ctx.fillStyle = '#ffffff';
ctx.beginPath();
ctx.arc(centerX, centerY, 8, 0, Math.PI * 2);
ctx.fill();
// Draw bobs
// Bob 1 - Red
ctx.fillStyle = '#ff6464';
ctx.beginPath();
ctx.arc(centerX + pos.x1, centerY + pos.y1, 12, 0, Math.PI * 2);
ctx.fill();
// Bob 2 - Green
ctx.fillStyle = '#64ff64';
ctx.beginPath();
ctx.arc(centerX + pos.x2, centerY + pos.y2, 12, 0, Math.PI * 2);
ctx.fill();
// Bob 3 - Blue
ctx.fillStyle = '#6464ff';
ctx.beginPath();
ctx.arc(centerX + pos.x3, centerY + pos.y3, 12, 0, Math.PI * 2);
ctx.fill();
}
// Animation loop
function animate() {
// Clear canvas
ctx.fillStyle = 'rgba(10, 10, 10, 0.1)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Update physics
updatePhysics();
// Update and draw trails
updateTrails();
drawTrails();
// Draw pendulum
drawPendulum();
requestAnimationFrame(animate);
}
// Start animation
animate();
</script>
<script>
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
// Set canvas size
function resizeCanvas() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
}
resizeCanvas();
window.addEventListener('resize', resizeCanvas);
// Pendulum parameters
const g = 9.81;
const dt = 0.01;
// Pendulum properties
const pendulum = {
// Masses
m1: 10,
m2: 10,
m3: 10,
// Lengths
l1: 150,
l2: 150,
l3: 150,
// Initial angles (radians)
theta1: Math.PI / 2,
theta2: Math.PI / 2,
theta3: Math.PI / 2,
// Angular velocities
omega1: 0,
omega2: 0,
omega3: 0,
// Trail arrays
trail1: [],
trail2: [],
trail3: [],
maxTrailLength: 500
};
// Origin point
const origin = {
x: canvas.width / 2,
y: canvas.height / 3
};
// Calculate positions
function calculatePositions() {
const x1 = pendulum.l1 * Math.sin(pendulum.theta1);
const y1 = pendulum.l1 * Math.cos(pendulum.theta1);
const x2 = x1 + pendulum.l2 * Math.sin(pendulum.theta2);
const y2 = y1 + pendulum.l2 * Math.cos(pendulum.theta2);
const x3 = x2 + pendulum.l3 * Math.sin(pendulum.theta3);
const y3 = y2 + pendulum.l3 * Math.cos(pendulum.theta3);
return { x1, y1, x2, y2, x3, y3 };
}
// Update physics using Runge-Kutta 4th order
function updatePhysics() {
// RK4 implementation for triple pendulum
const k1 = derivatives(pendulum.theta1, pendulum.theta2, pendulum.theta3,
pendulum.omega1, pendulum.omega2, pendulum.omega3);
const k2 = derivatives(
pendulum.theta1 + k1.dtheta1 * dt / 2,
pendulum.theta2 + k1.dtheta2 * dt / 2,
pendulum.theta3 + k1.dtheta3 * dt / 2,
pendulum.omega1 + k1.domega1 * dt / 2,
pendulum.omega2 + k1.domega2 * dt / 2,
pendulum.omega3 + k1.domega3 * dt / 2
);
const k3 = derivatives(
pendulum.theta1 + k2.dtheta1 * dt / 2,
pendulum.theta2 + k2.dtheta2 * dt / 2,
pendulum.theta3 + k2.dtheta3 * dt / 2,
pendulum.omega1 + k2.domega1 * dt / 2,
pendulum.omega2 + k2.domega2 * dt / 2,
pendulum.omega3 + k2.domega3 * dt / 2
);
const k4 = derivatives(
pendulum.theta1 + k3.dtheta1 * dt,
pendulum.theta2 + k3.dtheta2 * dt,
pendulum.theta3 + k3.dtheta3 * dt,
pendulum.omega1 + k3.domega1 * dt,
pendulum.omega2 + k3.domega2 * dt,
pendulum.omega3 + k3.domega3 * dt
);
// Update angles and velocities
pendulum.theta1 += (k1.dtheta1 + 2*k2.dtheta1 + 2*k3.dtheta1 + k4.dtheta1) * dt / 6;
pendulum.theta2 += (k1.dtheta2 + 2*k2.dtheta2 + 2*k3.dtheta2 + k4.dtheta2) * dt / 6;
pendulum.theta3 += (k1.dtheta3 + 2*k2.dtheta3 + 2*k3.dtheta3 + k4.dtheta3) * dt / 6;
pendulum.omega1 += (k1.domega1 + 2*k2.domega1 + 2*k3.domega1 + k4.domega1) * dt / 6;
pendulum.omega2 += (k1.domega2 + 2*k2.domega2 + 2*k3.domega2 + k4.domega2) * dt / 6;
pendulum.omega3 += (k1.domega3 + 2*k2.domega3 + 2*k3.domega3 + k4.domega3) * dt / 6;
}
// Calculate derivatives for the triple pendulum system
function derivatives(theta1, theta2, theta3, omega1, omega2, omega3) {
const m1 = pendulum.m1, m2 = pendulum.m2, m3 = pendulum.m3;
const l1 = pendulum.l1, l2 = pendulum.l2, l3 = pendulum.l3;
// Simplified derivatives for demonstration
// In a real implementation, this would involve the full Lagrangian equations
const dtheta1 = omega1;
const dtheta2 = omega2;
const dtheta3 = omega3;
// Approximate angular accelerations
const domega1 = (-g / l1) * Math.sin(theta1) - 0.01 * omega1;
const domega2 = (-g / l2) * Math.sin(theta2) - 0.01 * omega2;
const domega3 = (-g / l3) * Math.sin(theta3) - 0.01 * omega3;
// Add coupling effects
const coupling = 0.01;
domega1 += coupling * Math.sin(theta2 - theta1);
domega2 += coupling * Math.sin(theta1 - theta2) + coupling * Math.sin(theta3 - theta2);
domega3 += coupling * Math.sin(theta2 - theta3);
return {
dtheta1, dtheta2, dtheta3,
domega1, domega2, domega3
};
}
// Update trails
function updateTrails() {
const pos = calculatePositions();
const centerX = origin.x;
const centerY = origin.y;
pendulum.trail1.push({ x: centerX + pos.x1, y: centerY + pos.y1 });
pendulum.trail2.push({ x: centerX + pos.x2, y: centerY + pos.y2 });
pendulum.trail3.push({ x: centerX + pos.x3, y: centerY + pos.y3 });
// Limit trail length
if (pendulum.trail1.length > pendulum.maxTrailLength) pendulum.trail1.shift();
if (pendulum.trail2.length > pendulum.maxTrailLength) pendulum.trail2.shift();
if (pendulum.trail3.length > pendulum.maxTrailLength) pendulum.trail3.shift();
}
// Draw trails
function drawTrails() {
// Trail 1 - Red
ctx.strokeStyle = 'rgba(255, 100, 100, 0.3)';
ctx.lineWidth = 2;
ctx.beginPath();
for (let i = 1; i < pendulum.trail1.length; i++) {
const alpha = i / pendulum.trail1.length;
ctx.globalAlpha = alpha * 0.5;
ctx.moveTo(pendulum.trail1[i-1].x, pendulum.trail1[i-1].y);
ctx.lineTo(pendulum.trail1[i].x, pendulum.trail1[i].y);
ctx.stroke();
}
ctx.globalAlpha = 1;
// Trail 2 - Green
ctx.strokeStyle = 'rgba(100, 255, 100, 0.3)';
ctx.beginPath();
for (let i = 1; i < pendulum.trail2.length; i++) {
const alpha = i / pendulum.trail2.length;
ctx.globalAlpha = alpha * 0.5;
ctx.moveTo(pendulum.trail2[i-1].x, pendulum.trail2[i-1].y);
ctx.lineTo(pendulum.trail2[i].x, pendulum.trail2[i].y);
ctx.stroke();
}
ctx.globalAlpha = 1;
// Trail 3 - Blue
ctx.strokeStyle = 'rgba(100, 100, 255, 0.3)';
ctx.beginPath();
for (let i = 1; i < pendulum.trail3.length; i++) {
const alpha = i / pendulum.trail3.length;
ctx.globalAlpha = alpha * 0.5;
ctx.moveTo(pendulum.trail3[i-1].x, pendulum.trail3[i-1].y);
ctx.lineTo(pendulum.trail3[i].x, pendulum.trail3[i].y);
ctx.stroke();
}
ctx.globalAlpha = 1;
}
// Draw pendulum
function drawPendulum() {
const pos = calculatePositions();
const centerX = origin.x;
const centerY = origin.y;
// Update origin on resize
origin.x = canvas.width / 2;
origin.y = canvas.height / 3;
// Draw arms
ctx.strokeStyle = '#ffffff';
ctx.lineWidth = 3;
ctx.lineCap = 'round';
// First arm
ctx.beginPath();
ctx.moveTo(centerX, centerY);
ctx.lineTo(centerX + pos.x1, centerY + pos.y1);
ctx.stroke();
// Second arm
ctx.beginPath();
ctx.moveTo(centerX + pos.x1, centerY + pos.y1);
ctx.lineTo(centerX + pos.x2, centerY + pos.y2);
ctx.stroke();
// Third arm
ctx.beginPath();
ctx.moveTo(centerX + pos.x2, centerY + pos.y2);
ctx.lineTo(centerX + pos.x3, centerY + pos.y3);
ctx.stroke();
// Draw pivot
ctx.fillStyle = '#ffffff';
ctx.beginPath();
ctx.arc(centerX, centerY, 8, 0, Math.PI * 2);
ctx.fill();
// Draw bobs
// Bob 1 - Red
ctx.fillStyle = '#ff6464';
ctx.beginPath();
ctx.arc(centerX + pos.x1, centerY + pos.y1, 12, 0, Math.PI * 2);
ctx.fill();
// Bob 2 - Green
ctx.fillStyle = '#64ff64';
ctx.beginPath();
ctx.arc(centerX + pos.x2, centerY + pos.y2, 12, 0, Math.PI * 2);
ctx.fill();
// Bob 3 - Blue
ctx.fillStyle = '#6464ff';
ctx.beginPath();
ctx.arc(centerX + pos.x3, centerY + pos.y3, 12, 0, Math.PI * 2);
ctx.fill();
}
// Animation loop
function animate() {
// Clear canvas
ctx.fillStyle = 'rgba(10, 10, 10, 0.1)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Update physics
updatePhysics();
// Update and draw trails
updateTrails();
drawTrails();
// Draw pendulum
drawPendulum();
requestAnimationFrame(animate);
}
// Start animation
animate();
</script>