Double pendulum · ring-2.6-1t
Simulate a chaotic double pendulum on a canvas using the correct equations of motion, drawing the arms and a colorful fading trail of the tip.
transcript · 6 messages
Simulate a chaotic double pendulum on a canvas using the correct equations of motion, drawing the arms and a colorful fading trail of the tip.
thinking
The user wants a single-page web app that simulates a chaotic double pendulum with correct equations of motion, drawing the arms and a colorful fading trail of the tip. I need to create an index.html file with all CSS and JS inline, no external resources.Let me think about the physics: A double pendulum consists of two pendulums attached end to end. The equations of motion are derived from Lagrangian mechanics. State variables: θ1, θ2 (angles), ω1, ω2 (angular velocities) The equations of motion for a double pendulum: Let: - m1, m2 = masses - L1, L2 = rod lengths - g = gravity - θ1, θ2 = angles from vertical - ω1 = dθ1/dt, ω2 = dθ2/dt The angular accelerations are: α1 = d²θ1/dt² α2 = d²θ2/dt² The standard equations: α1 = (-g(2m1+m2)sin(θ1) - m2*g*sin(θ1-2θ2) - 2*sin(θ1-θ2)*m2*(ω2²*L2 + ω1²*L1*cos(θ1-θ2))) / (L1*(2m1+m2-m2*cos(2θ1-2θ2))) α2 = (2*sin(θ1-θ2)*(ω1²*L1*(m1+m2) + g*(m1+m2)*cos(θ1) + ω2²*L2*m2*cos(θ1-θ2))) / (L2*(2m1+m2-m2*cos(2θ1-2θ2))) I'll use RK4 integration for numerical stability. For the trail, I'll store past positions and draw them with decreasing opacity and changing hue. Let me write this all up in a single HTML file.
I'll create a self-contained double pendulum simulation with correct Lagrangian equations of motion, RK4 integration, and a colorful fading trail.
{"content":"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"UTF-8\">\n<meta name=\"viewport\" content=\"width=device-width, iniValidation failed for tool "write": - path: must have required properties path Received arguments: { "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"UTF-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n<title>Chaotic Double Pendulum</title>\n<style>\n*{margin:0;padding:0;box-sizing:border-box}\nbody{background:#000;overflow:hidden;display:flex;justify-content:center;align-items:center;height:100vh}\ncanvas{display:block;cursor:pointer}\n.info{position:absolute;top:12px;left:12px;color:rgba(255,255,255,0.5);font:13px/1.5 monospace;pointer-events:none;user-select:none}\n.info span{color:#aaa}\n</style>\n</head>\n<body>\n<div class=\"info\">\n <span>Double Pendulum</span> — chaotic motion simulation<br>\n Lagrangian mechanics · RK4 integration · fading trail<br>\n Click to reset with new initial conditions\n</div>\n<canvas id=\"c\"></canvas>\n<script>\n// --- Canvas setup ---\nconst canvas = document.getElementById('c');\nconst ctx = canvas.getContext('2d');\nfunction resize() {\n canvas.width = window.innerWidth;\n canvas.height = window.innerHeight;\n}\nresize();\nwindow.addEventListener('resize', resize);\n\n// --- Physics constants ---\nconst G = 9.81; // gravitational acceleration\nconst M1 = 1.0; // mass 1\nconst M2 = 1.0; // mass 2\nconst L1 = 1.0; // length 1 (metres, scaled on screen)\nconst L2 = 1.0; // length 2\nconst DT = 0.01; // base time step\nconst STEPS_PER_FRAME = 8;\nconst TRAIL_MAX = 600; // trail length\nconst DAMPING = 0.9999; // very slight damping for visual settling\n\n// --- State: [theta1, omega1, theta2, omega2] ---\nlet state = [];\nlet trail = []; // array of {x, y, hue}\nlet time = 0;\n\n// Pivot position (center-bottom area of canvas)\nfunction pivot() {\n return { x: canvas.width / 2, y: canvas.height * 0.35 };\n}\n\n// Random initial condition (small perturbation from rest)\nfunction randomInit() {\n const p = pivot();\n const scale = Math.min(p.x, p.y);\n return [\n Math.PI * (0.5 + (Math.random()-0.5)*0.8), // theta1: near upright\n 0, // omega1\n Math.PI * (0.5 + (Math.random()-0.5)*0.8), // theta2\n 0 // omega2\n ];\n}\n\nfunction init() {\n state = randomInit();\n trail = [];\n time = 0;\n}\n\n// --- Equations of motion (Lagrangian derivation) ---\n// Returns [dtheta1, domega1, dtheta2, domega2]\nfunction derivatives(s) {\n const [t1, w1, t2, w2] = s;\n const dt = t1 - t2;\n const st = Math.sin(dt);\n const ct = Math.cos(dt);\n const s1 = Math.sin(t1);\n const s2 = Math.sin(t2);\n\n const den = 2*M1 + M2 - M2*Math.cos(2*dt); // common denominator\n\n const a1 = (\n -G*(2*M1 + M2)*s1\n - M2*G*Math.sin(t1 - 2*t2)\n - 2*st*M2*(w2*w2*L2 + w1*w1*L1*ct)\n ) / (L1 * den);\n\n const a2 = (\n 2*st*(w1*w1*L1*(M1+M2)\n + G*(M1+M2)*Math.cos(t1)\n + w2*w2*L2*M2*ct)\n ) / (L2 * den);\n\n return [w1, a1, w2, a2];\n}\n\n// --- RK4 integrator ---\nfunction rk4Step(s, h) {\n const k1 = derivatives(s);\n\n const s2 = s.map((v,i) => v + 0.5*h*k1[i]);\n const k2 = derivatives(s2);\n\n const s3 = s.map((v,i) => v + 0.5*h*k2[i]);\n const k3 = derivatives(s3);\n\n const s4 = s.map((v,i) => v + h*k3[i]);\n const k4 = derivatives(s4);\n\n return s.map((v,i) =>\n v + (h/6)*(k1[i] + 2*k2[i] + 2*k3[i] + k4[i])\n );\n}\n\n// --- Compute tip position from angles ---\nfunction tipPos(s) {\n const [t1, , t2] = s;\n const p = pivot();\n const scale = Math.min(p.y * 0.45, p.x * 0.35);\n const x1 = p.x + scale * L1 * Math.sin(t1);\n const y1 = p.y + scale * L1 * Math.cos(t1);\n const x2 = x1 + scale * L2 * Math.sin(t2);\n const y2 = y1 + scale * L2 * Math.cos(t2);\n return { x1, y1, x2, y2 };\n}\n\n// --- Drawing ---\nfunction draw() {\n ctx.fillStyle = 'rgba(0,0,0,0.06)'; // fade factor for trail\n ctx.fillRect(0, 0, canvas.width, canvas.height);\n\n const pos = tipPos(state);\n\n // --- Draw trail ---\n if (trail.length > 1) {\n for (let i = 1; i < trail.length; i++) {\n const prev = trail[i-1];\n const cur = trail[i];\n const t = i / trail.length; // 0 … 1\n const alpha = t * 0.9; // fade out\n\n ctx.beginPath();\n ctx.moveTo(prev.x, prev.y);\n ctx.lineTo(cur.x, cur.y);\n ctx.strokeStyle = `hsla(${cur.hue},100%,60%,${alpha})`;\n ctx.lineWidth = Math.max(1, t * 3);\n ctx.lineCap = 'round';\n ctx.stroke();\n }\n }\n\n // --- Draw arms ---\n const p = pivot();\n const armW = 3;\n\n // Arm 1\n ctx.beginPath();\n ctx.moveTo(p.x, p.y);\n ctx.lineTo(pos.x1, pos.y1);\n ctx.strokeStyle = 'rgba(220,220,220,0.85)';\n ctx.lineWidth = armW;\n ctx.lineCap = 'round';\n ctx.stroke();\n\n // Arm 2\n ctx.beginPath();\n ctx.moveTo(pos.x1, pos.y1);\n ctx.lineTo(pos.x2, pos.y2);\n ctx.strokeStyle = 'rgba(180,180,180,0.85)';\n ctx.stroke();\n\n // --- Draw masses ---\n const r1 = 10, r2 = 8;\n\n ctx.beginPath();\n ctx.arc(p.x, p.y, 4, 0, Math.PI*2);\n ctx.fillStyle = '#fff';\n ctx.fill();\n\n ctx.beginPath();\n ctx.arc(pos.x1, pos.y1, r1, 0, Math.PI*2);\n ctx.fillStyle = 'rgba(255,255,255,0.15)';\n ctx.fill();\n ctx.strokeStyle = 'rgba(255,255,255,0.5)';\n ctx.lineWidth = 1.5;\n ctx.stroke();\n\n ctx.beginPath();\n ctx.arc(pos.x2, pos.y2, r2, 0, Math.PI*2);\n ctx.fillStyle = 'rgba(255,255,255,0.2)';\n ctx.fill();\n ctx.strokeStyle = 'rgba(255,255,255,0.6)';\n ctx.lineWidth = 1.5;\n ctx.stroke();\n}\n\n// --- Main loop ---\nfunction step() {\n // Sub-step integration\n for (let i = 0; i < STEPS_PER_FRAME; i++) {\n state = rk4Step(state, DT);\n // apply light damping to angular velocities\n state[1] *= DAMPING;\n state[3] *= DAMPING;\n time += DT;\n }\n\n // Record trail point\n const pos = tipPos(state);\n const hue = (time * 40) % 360; // slowly shifting colour\n trail.push({ x: pos.x2, y: pos.y2, hue });\n\n // Keep trail bounded\n if (trail.length > TRAIL_MAX) {\n trail.shift();\n }\n\n draw();\n requestAnimationFrame(step);\n}\n\n// --- Start ---\ninit();\nstep();\n\n// Click to restart with new initial conditions\ncanvas.addEventListener('click', () => {\n // Fade trail quickly\n trail = [];\n state = randomInit();\n});\n</script>\n</body>\n</html>" }
thinking
The write tool failed because I didn't specify the path parameter. Let me fix that.
/tmp/ws/index.html (5086 bytes)Successfully wrote 5086 bytes to /tmp/ws/index.html
thinking
The file has been written successfully. Let me verify it exists and looks correct.
Done. The file `/tmp/ws/index.html` is a fully self-contained double pendulum simulation. Here's what it includes: **Physics:** - Correct Lagrangian-derived equations of motion for a double pendulum - 4th-order Runge-Kutta (RK4) integration for numerical stability - Tiny velocity damping so motion eventually settles **Visuals:** - Black canvas with semi-transparent overlay each frame to create a **colorful fading trail** of the tip - Trail hue shifts over time (HSL color wheel), with opacity and width fading from old to new - Two white pendulum arms drawn as lines, with mass circles at pivot and both bobs - Responsive canvas that fills the viewport **Interaction:** - Click anywhere to reset with new randomized initial conditions, producing a completely different chaotic trajectory
TAKE IT TO CODE
A single self-contained HTML file — copy it and open in any browser, or reuse the prompt with your own pi-agent run.
RUN
EVALUATION
Chaotic double pendulum with colorful fading tip trail; click/drag/wheel/keys appear to reset/restart the simulation to a fresh swing.
Vision grade · anthropic/claude-sonnet-5
MEASUREMENTS
Raw readings from the capture, not a verdict — the grade above is the only judgement. Percentages are how much of the screen changed; the measure under-reports sparse motion on a dark background.