JIT
Grid - Untitled
<html> <head> <title>Triple Pendulum Animation</title> <style> body { margin: 0; overflow: hidden; } canvas { width: 100vw; height: 100vh; display: block; } </style> </head> <body> <canvas id="canvas"></canvas> <script> const canvas = document.getElementById('canvas'); const ctx = canvas.getContext('2d');

let width, height, scale, originX, originY; let g = 9.81; let l1 = 0.2, l2 = 0.2, l3 = 0.2; let m1 = 1, m2 = 1, m3 = 1; let theta1 = Math.PI / 2, theta2 = 0, theta3 = 0; let omega1 = 0, omega2 = 0, omega3 = 0; const initialTheta1 = theta1, initialTheta2 = theta2, initialTheta3 = theta3; const dt = 0.005; let accumulator = 0; let previousTime = performance.now();

const trail1 = [], trail2 = [], trail3 = []; const maxTrailLength = 1000; const trailColors = ['rgba(255, 0, 0, 0.5)', 'rgba(0, 255, 0, 0.5)', 'rgba(0, 0, 255, 0.5)'];

function resize() { width = window.innerWidth; height = window.innerHeight; canvas.width = width; canvas.height = height; scale = Math.min(width, height) / 1.5; originX = width / 2; originY = height / 4; // Reset simulation on resize for clean scaling theta1 = initialTheta1; theta2 = initialTheta2; theta3 = initialTheta3; omega1 = 0; omega2 = 0; omega3 = 0; trail1.length = 0; trail2.length = 0; trail3.length = 0; }

window.addEventListener('resize', resize); resize();

function det3(mat) { return mat[0][0] * (mat[1][1] * mat[2][2] - mat[1][2] * mat[2][1]) - mat[0][1] * (mat[1][0] * mat[2][2] - mat[1][2] * mat[2][0]) + mat[0][2] * (mat[1][0] * mat[2][1] - mat[1][1] * mat[2][0]); }

function computeAlphas(t1, t2, t3, w1, w2, w3) { const thetas = [t1, t2, t3]; const omegas = [w1, w2, w3]; const ls = [l1, l2, l3]; const ms = [m1, m2, m3];

let M = Array.from({length: 3}, () => Array(3).fill(0)); for (let i = 0; i < 3; i++) { for (let j = 0; j < 3; j++) { let sum = 0; for (let k = Math.max(i, j); k < 3; k++) { sum += ms[k]; } M[i][j] = ls[i] * ls[j] * sum * Math.cos(thetas[i] - thetas[j]); } }

let b = Array(3).fill(0); for (let i = 0; i < 3; i++) { let sumM = 0; for (let k = i; k < 3; k++) { sumM += ms[k]; } const g_term = -g * ls[i] * Math.sin(thetas[i]) * sumM;

let c_term = 0;
for (let j = 0; j < 3; j++) {
  let sumM2 = 0;
  for (let k = Math.max(i, j); k < 3; k++) {
    sumM2 += ms[k];
  }
  c_term -= ls[i] * ls[j] * Math.sin(thetas[i] - thetas[j]) * (omegas[j] ** 2) * sumM2;
}
b[i] = g_term + c_term;

}

const detM = det3(M); if (Math.abs(detM) < 1e-10) { return [0, 0, 0]; }

let alphas = []; for (let k = 0; k < 3; k++) { const copy = M.map(row => row.slice()); for (let r = 0; r < 3; r++) { copy[r][k] = b[r]; } alphas[k] = det3(copy) / detM; } return alphas; }

function derivs(state) { const [t1, w1, t2, w2, t3, w3] = state; const [a1, a2, a3] = computeAlphas(t1, t2, t3, w1, w2, w3); return [w1, a1, w2, a2, w3, a3]; }

function rk4_step(state, dt) { const d1 = derivs(state); const temp1 = state.map((s, i) => s + d1[i] * dt / 2); const d2 = derivs(temp1); const temp2 = state.map((s, i) => s + d2[i] * dt / 2); const d3 = derivs(temp2); const temp3 = state.map((s, i) => s + d3[i] * dt); const d4 = derivs(temp3);

return state.map((s, i) => s + (d1[i] + 2 * d2[i] + 2 * d3[i] + d4[i]) * dt / 6); }

function step(dt) { const state = [theta1, omega1, theta2, omega2, theta3, omega3]; const newState = rk4_step(state, dt); theta1 = newState[0]; omega1 = newState[1]; theta2 = newState[2]; omega2 = newState[3]; theta3 = newState[4]; omega3 = newState[5]; }

function getPositions() { const x1 = l1 * Math.sin(theta1); const y1 = l1 * Math.cos(theta1); const x2 = x1 + l2 * Math.sin(theta2); const y2 = y1 + l2 * Math.cos(theta2); const x3 = x2 + l3 * Math.sin(theta3); const y3 = y2 + l3 * Math.cos(theta3); return [{x: x1, y: y1}, {x: x2, y: y2}, {x: x3, y: y3}]; }

function drawTrail(trail, color) { if (trail.length < 2) return; ctx.beginPath(); ctx.moveTo(originX + trail[0].x * scale, originY + trail[0].y * scale); for (let i = 1; i < trail.length; i++) { ctx.lineTo(originX + trail[i].x * scale, originY + trail[i].y * scale); } ctx.strokeStyle = color; ctx.lineWidth = 1; ctx.stroke(); }

function draw() { ctx.fillStyle = 'black'; ctx.fillRect(0, 0, width, height);

const positions = getPositions();

// Add to trails trail1.push(positions[0]); trail2.push(positions[1]); trail3.push(positions[2]); if (trail1.length > maxTrailLength) trail1.shift(); if (trail2.length > maxTrailLength) trail2.shift(); if (trail3.length > maxTrailLength) trail3.shift();

// Draw trails drawTrail(trail1, trailColors[0]); drawTrail(trail2, trailColors[1]); drawTrail(trail3, trailColors[2]);

// Draw arms ctx.beginPath(); ctx.moveTo(originX, originY); ctx.lineTo(originX + positions[0].x * scale, originY + positions[0].y * scale); ctx.lineTo(originX + positions[1].x * scale, originY + positions[1].y * scale); ctx.lineTo(originX + positions[2].x * scale, originY + positions[2].y * scale); ctx.strokeStyle = 'white'; ctx.lineWidth = 2; ctx.stroke();

// Draw bobs const bobRadius = 5; ctx.fillStyle = 'red'; ctx.beginPath(); ctx.arc(originX + positions[0].x * scale, originY + positions[0].y * scale, bobRadius, 0, 2 * Math.PI); ctx.fill(); ctx.fillStyle = 'green'; ctx.beginPath(); ctx.arc(originX + positions[1].x * scale, originY + positions[1].y * scale, bobRadius, 0, 2 * Math.PI); ctx.fill(); ctx.fillStyle = 'blue'; ctx.beginPath(); ctx.arc(originX + positions[2].x * scale, originY + positions[2].y * scale, bobRadius, 0, 2 * Math.PI); ctx.fill(); }

function animate(currentTime) { requestAnimationFrame(animate); const delta = (currentTime - previousTime) / 1000; previousTime = currentTime; accumulator += delta; while (accumulator >= dt) { step(dt); accumulator -= dt; } draw(); }

requestAnimationFrame(animate); </script>

</body> </html>_
Updated files
v1
In one html file create a triple pendulum animation that follows the laws of gravity. No controls, no interactions, simply the animation. Make sure each intercept from one pendulum to another leaves a trail.