Double pendulum · deepseek-v4-flash-0731
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 chaotic double pendulum simulation on canvas with correct equations of motion, arms, and a colorful fading trail.
Let me write a self-contained index.html file.
Let me think about the correct equations of motion for a double pendulum.
Using the standard formulation with angles θ1, θ2, masses m1, m2, lengths L1, L2, gravity g.
Let me use angular acceleration equations.
Standard equations (from common derivations):
Let:
- Δθ = θ1 - θ2
- m1, m2 masses
- L1, L2 lengths
- g gravity
θ1'' = [ -g(2m1 + m2) sin θ1 - m2 g sin(θ1 - 2θ2) - 2 sin(θ1 - θ2) m2 (θ2'^2 L2 + θ1'^2 L1 cos(θ1 - θ2)) ] / [ L1 (2m1 + m2 - m2 cos(2θ1 - 2θ2)) ]
θ2'' = [ 2 sin(θ1 - θ2) (θ1'^2 L1 (m1 + m2) + g (m1 + m2) cos θ1 + θ2'^2 L2 m2 cos(θ1 - θ2)) ] / [ L2 (2m1 + m2 - m2 cos(2θ1 - 2θ2)) ]
Let me double check these equations. This is a well-known form.
Actually a cleaner form with mass ratio μ = m2/(m1+m2):
θ1'' = [ -g(2m1+m2) sinθ1 - m2 g sin(θ1-2θ2) - 2 sin(θ1-θ2) m2 (θ2'^2 L2 + θ1'^2 L1 cos(θ1-θ2)) ] / [L1(2m1+m2 - m2 cos(2θ1-2θ2))]
θ2'' = [ 2 sin(θ1-θ2)(θ1'^2 L1(m1+m2) + g(m1+m2)cosθ1 + θ2'^2 L2 m2 cos(θ1-θ2)) ] / [L2(2m1+m2 - m2 cos(2θ1-2θ2))]
Yes these are correct and standard.
I'll simulate with RK4 integration for accuracy. Actually for simplicity and correct physics, RK4 is good. But do it carefully.
Let me define state = [θ1, θ2, ω1, ω2] where ω = dθ/dt.
Derivatives:
dθ1/dt = ω1
dθ2/dt = ω2
dω1/dt = θ1''
dω2/dt = θ2''
I'll implement RK4.
For the fading trail: store points of the tip (position of second bob), draw with fading. A common approach: store trail points with age, draw each with low alpha to create fade, or use a semi-transparent background fill each frame (which naturally fades old drawn content).
The semi-transparent rectangle overlay technique: each frame fill with rgba(background, alpha) which leaves fading traces. But combined with canvas 2d this creates nice trails. However if we draw both arms and trail each frame, and overlay a translucent bg each frame, the arms get smeared too.
Alternative: draw arm with full opacity each frame, and maintain trail separately. Better approach: use an offscreen trail canvas, draw tip points on it with gradient colors each frame, then composite trail canvas with fading using destination-out or fill with transparent.
Simplest robust approach for a fading colorful trail:
- Use a main canvas.
- Each frame: draw trail points from previous iterations with decreasing alpha/color (age), then draw the arm with full opacity.
Store trail as array of points with a hue that cycles. Each frame, increment age of each point and only draw those within max age, with alpha and hue based on age.
Actually to get smooth fading, reduce alpha of older points. Let me store the trail points each frame and keep last N. Color by index with hue cycling.
Let me implement: trail array of positions. Max trail length e.g. 2000 points. Each frame push current tip position. Then loop over trail, compute age = currentIndex - pointIndex (or store creation frame), draw a small circle with lineWidth, color hue = (base + t*frequency) % 360, alpha decreasing with age.
That's clean. Use line to connect consecutive points for a ribbon effect, or draw dots. Drawing small dots is fine.
Let me draw with ctx.beginPath, lineWidth decreasing with age, strokeStyle hue based on index.
Performance: with ~1000s of points, drawing each individually each frame could be slow. But canvas 2d can handle it if we don't reset path each point. We can batch by color/alpha. Simpler: draw dots. Might be fine for reasonable trail length.
Let me keep trail length moderate, say 800 points, and draw line segments. Actually let me draw a stroke for each segment - that's many strokes. Alternatively use globalAlpha per segment.
I'll implement a reasonably performant approach: for the trail, iterate points, for each segment draw a line with color based on recentness. Let's use dots with radius ~1.5 to 0.5.
Let me just do it.
Canvas sizing: full window. Center pivot at middle.
Let me write the physics and rendering.
Configuration: L1 = L2 = some fraction of min(width,height). Let mint = min(w,h); L = mint * 0.18 maybe. Total reach ~0.72. Add some margin.
masses equal m1=m2=1. g in units of px/s^2? Use g scaled. Let me define g = 9.81 but scaled to pixels: use g in terms of length per s^2. Let's set g = 1500 (pixels/s^2) something. Actually arbitrary; the dynamics scale. Use g = 900. motion. Let me define physical values: L in pixels chosen, g_px = 1500.
Let me set dt for integration, substeps to keep stable. With RK4, dt = 1/60 should be fine but chaotic system might need smaller. Use substeps ~ 4 per frame with dt = 1/240 each. I'll do RK4 with fixed small dt stepped multiple times per animation frame based on real elapsed time, capped.
Let me implement: accumulate real dt (cap at 0.05s), then step in fixed sub-dt of e.g. 1/120.
Let me write it plainly.
Let me create the file.
I'll add controls: reset button, maybe pause. Nice UI. Sliders for speed? Keep it simple: a reset button and a speed maybe. Let me add a small control panel: Reset, and a button to randomize, plus readout of angles maybe. Keep minimal but polished.
Let me write.
For RK4 function f(state):
[ω1, ω2, a1, a2].
Compute using equations.
I'll implement:
function derivatives(state) {
let [th1, th2, w1, w2] = state;
const dth = th1 - th2;
const denom = 2*m1 + m2 - m2*Math.cos(2*th1 - 2*th2);
const a1 = (-g*(2*m1+m2)*Math.sin(th1) - m2*g*Math.sin(th1-2*th2) - 2*Math.sin(dth)*m2*(w2*w2*L2 + w1*w1*L1*Math.cos(dth))) / (L1*denom);
const a2 = (2*Math.sin(dth)*(w1*w1*L1*(m1+m2) + g*(m1+m2)*Math.cos(th1) + w2*w2*L2*m2*Math.cos(dth))) / (L2*denom);
return [w1, w2, a1, a2];
}
Good.
RK4 step.
Coordinates:
pivot = (cx, cy).
x1 = cx + L1*sin(th1), y1 = cy + L1*cos(th1) (with y down, angle measured from downward vertical, positive clockwise). That's standard: θ measured from vertical downward.
Actually use standard: x = L sinθ, y = L cosθ measured from downward vertical. Works fine visually.
x2 = x1 + L2*sin(th2), y2 = y1 + L2*cos(th2).
Draw.
Trail: hue cycling. baseHue = (frame*0.8)%360. alpha from 1 down to 0.
Let me draw trail as line segments for smoothness: for i from 1 to n, segment between trail[i-1] and trail[i]. Color by i with age.
But calling strokeStyle change per segment = n strokes. For n=800 that's 800 beginPath/stroke per frame ~ acceptable but maybe heavy. Let me reduce trail to ~500 and it's fine. Alternatively draw dots which is also many. It's OK.
Let me instead draw dashes of circles. I'll go with segments using stroke with lineWidth 2 and decreasing alpha via globalAlpha. Fine.
Actually to reduce cost, I can draw the trail on an offscreen with the fading trick. But let's keep direct.
Let me implement color as HSL by recentness: hue = 260 + t*240 cycling through purple->red->orange->green->blue. Let hue = (t / maxAge * 360) maybe scaled. Let me do hue cycling by index: hue = (frameCount - age)*0.6 mod 360.
I'll just compute hue from point's creation frame.
Let me code.
Let me also handle window resize.
Let me write the HTML now.
I'll make it clean and well-formatted.
The trail alpha: young points bright alpha ~0.9, old fade to 0. Use a=smoothstep. Good.
Let me write file.I'll build the chaotic double pendulum simulation. Let me create the self-contained `index.html`.
/workspace/index.html (12489 bytes)Successfully wrote 12489 bytes to /workspace/index.html
thinking
Let me verify the file exists and is valid. Let me do a quick sanity check with node for the HTML/JS syntax, and maybe verify the file.
Let me verify the file was created correctly.
{"command":"cd /workspace && ls -la index.html && node -e \"\nconst fs=require('fs');\nconst html=fs.readFileSync('index.html','utf8');\ncon-rw-r--r-- 1 node node 12517 Aug 1 05:18 index.html JS syntax OK
The file is created and the JavaScript parses cleanly. `index.html` is a single self-contained, sandboxed web app (no network, no external resources) simulating a **chaotic double pendulum**: **Physics** — Uses the exact, correct equations of motion for the double pendulum (two coupled second-order ODEs for θ₁, θ₂), integrated with **RK4** at a fixed small timestep (h = 1/240, sub-stepped per frame) so it stays accurate and stable through chaotic trajectories. Angles are measured from the downward vertical. **Rendering** - Two arms drawn with round line caps, plus pivot and bob joints (the tip bob is highlighted). - A **colorful fading trail** of the tip: stored points are drawn as a rainbow ribbon whose hue continuously cycles over time and whose alpha/width fall off with age, creating a smooth glowing fade behind the swinging arm. **Interactivity** - **Drag anywhere on the pendulum** to grab and fling the tip, middle bob, or the arm midpoint (with continuous-angle tracking so it never jumps). - **Reset** (classic start) and **Randomize** buttons. - Sliders for **Speed**, **Trail length**, and **Gravity**, plus a live readout of the angles and angular velocities. - Fully responsive — rescales to the window with devicePixelRatio support. Everything (CSS in `<style>`, JS in `<script>`) is inline in the one file, so it runs fully offline. Just open `index.html` in a browser.
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
Double pendulum simulation with colorful fading trails, animates on its own, and drag flings the tip as instructed.
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.