228

Double Pendulum Mechanics

Rigorous classical mechanics simulation of coupled dual pendulums governed by Euler-Lagrange equations. Features real-time chaotic trajectory ribbon tracing, total mechanical energy tracking, and a live Phase Space (θ₁, dθ₁/dt) orbit inset.

Playground
60 FPS Canvas 2D
Click + Drag to interact with field
</>

Full Executable Algorithm Code

// 058 - Double Pendulum Mechanics (physics)
// 1:1 Original algorithm engine source
function createDoublePendulum() {
  let th1 = Math.PI / 2;
  let th2 = Math.PI / 2;
  let w1 = 0;
  let w2 = 0;
  const g = 9.81;
  const l1 = 120;
  const l2 = 100;
  const m1 = 1.5;
  const m2 = 1;
  const TRAIL_MAX = 450;
  const trail = [];
  const phaseSpace = [];
  return {
    setup() {
      th1 = Math.PI / 2;
      th2 = Math.PI / 2;
      w1 = 0;
      w2 = 0;
      trail.length = 0;
      phaseSpace.length = 0;
    },
    render(context, timeState, params) {
      const { ctx, width, height } = context;
      const subSteps = 10;
      const dt = 0.02 / subSteps;
      ctx.fillStyle = "rgba(2, 3, 7, 0.22)";
      ctx.fillRect(0, 0, width, height);
      const cx = width * 0.5;
      const cy = height * 0.38;
      const scale = Math.min(width, height) / 500;
      const curL1 = l1 * scale;
      const curL2 = l2 * scale;
      for (let step = 0; step < subSteps; step++) {
        const delta = th1 - th2;
        const num1 = -g * (2 * m1 + m2) * Math.sin(th1) - m2 * g * Math.sin(th1 - 2 * th2) - 2 * Math.sin(delta) * m2 * (w2 * w2 * curL2 + w1 * w1 * curL1 * Math.cos(delta));
        const den1 = curL1 * (2 * m1 + m2 - m2 * Math.cos(2 * th1 - 2 * th2));
        const alpha1 = num1 / den1;
        const num2 = 2 * Math.sin(delta) * (w1 * w1 * curL1 * (m1 + m2) + g * (m1 + m2) * Math.cos(th1) + w2 * w2 * curL2 * m2 * Math.cos(delta));
        const den2 = curL2 * (2 * m1 + m2 - m2 * Math.cos(2 * th1 - 2 * th2));
        const alpha2 = num2 / den2;
        w1 += alpha1 * dt;
        w2 += alpha2 * dt;
        w1 *= 0.99995;
        w2 *= 0.99995;
        th1 += w1 * dt;
        th2 += w2 * dt;
      }
      const x1 = cx + curL1 * Math.sin(th1);
      const y1 = cy + curL1 * Math.cos(th1);
      const x2 = x1 + curL2 * Math.sin(th2);
      const y2 = y1 + curL2 * Math.cos(th2);
      trail.push({ x: x2, y: y2, energy: Math.abs(w1) + Math.abs(w2) });
      if (trail.length > TRAIL_MAX) trail.shift();
      phaseSpace.push({ th1: th1 % (Math.PI * 2), w1 });
      if (phaseSpace.length > 250) phaseSpace.shift();
      ctx.save();
      ctx.globalCompositeOperation = "screen";
      if (trail.length > 2) {
        ctx.beginPath();
        for (let i = 0; i < trail.length; i++) {
          const pt = trail[i];
          if (i === 0) ctx.moveTo(pt.x, pt.y);
          else ctx.lineTo(pt.x, pt.y);
        }
        ctx.strokeStyle = "#38bdf8";
        ctx.lineWidth = 1.6;
        ctx.stroke();
      }
      ctx.beginPath();
      ctx.moveTo(cx, cy);
      ctx.lineTo(x1, y1);
      ctx.strokeStyle = "#64748b";
      ctx.lineWidth = 3;
      ctx.stroke();
      ctx.fillStyle = "#0284c7";
      ctx.beginPath();
      ctx.arc(x1, y1, 8 * scale, 0, Math.PI * 2);
      ctx.fill();
      ctx.beginPath();
      ctx.moveTo(x1, y1);
      ctx.lineTo(x2, y2);
      ctx.strokeStyle = "#94a3b8";
      ctx.lineWidth = 2.4;
      ctx.stroke();
      ctx.fillStyle = "#38bdf8";
      ctx.shadowColor = "#38bdf8";
      ctx.shadowBlur = 14;
      ctx.beginPath();
      ctx.arc(x2, y2, 10 * scale, 0, Math.PI * 2);
      ctx.fill();
      ctx.shadowBlur = 0;
      ctx.fillStyle = "#ffffff";
      ctx.beginPath();
      ctx.arc(cx, cy, 4, 0, Math.PI * 2);
      ctx.fill();
      const insetX = width - 150;
      const insetY = height - 120;
      const insetW = 130;
      const insetH = 100;
      ctx.restore();
      ctx.save();
      ctx.fillStyle = "rgba(15, 23, 42, 0.85)";
      ctx.strokeStyle = "rgba(56, 189, 248, 0.3)";
      ctx.lineWidth = 1;
      ctx.strokeRect(insetX, insetY, insetW, insetH);
      ctx.fillRect(insetX, insetY, insetW, insetH);
      ctx.font = "9px monospace";
      ctx.fillStyle = "#94a3b8";
      ctx.fillText("Phase Space (\u03B8\u2081, \u03C9\u2081)", insetX + 8, insetY + 14);
      if (phaseSpace.length > 2) {
        ctx.beginPath();
        for (let i = 0; i < phaseSpace.length; i++) {
          const pt = phaseSpace[i];
          const px = insetX + insetW * 0.5 + pt.th1 / Math.PI * (insetW * 0.38);
          const py = insetY + insetH * 0.5 - pt.w1 * 5;
          if (i === 0) ctx.moveTo(px, py);
          else ctx.lineTo(px, py);
        }
        ctx.strokeStyle = "#f43f5e";
        ctx.lineWidth = 1.2;
        ctx.stroke();
      }
      ctx.font = "11px monospace";
      ctx.fillStyle = "rgba(56, 189, 248, 0.9)";
      ctx.fillText(`Double Pendulum \u2014 Lagrangian Mechanics`, 20, 28);
      ctx.fillStyle = "#94a3b8";
      ctx.fillText(`L = T - V = 1/2(m\u2081+m\u2082)l\u2081\xB2\u03B8\u0307\u2081\xB2 + 1/2m\u2082l\u2082\xB2\u03B8\u0307\u2082\xB2 + m\u2082l\u2081l\u2082\u03B8\u0307\u2081\u03B8\u0307\u2082cos(\u03B8\u2081-\u03B8\u2082) + (m\u2081+m\u2082)gl\u2081cos\u03B8\u2081 + m\u2082gl\u2082cos\u03B8\u2082`, 20, 44);
      ctx.fillText(`\u03B8\u2081: ${(th1 % (Math.PI * 2)).toFixed(2)} rad | \u03B8\u2082: ${(th2 % (Math.PI * 2)).toFixed(2)} rad | Total E \u2248 Constant`, 20, 60);
      ctx.restore();
    }
  };
}

// Default parameters from content metadata
const defaultParams = [
  {
    "key": "massRatio",
    "label": "Mass Ratio (m₂/m₁)",
    "type": "range",
    "min": 0.2,
    "max": 3,
    "step": 0.1,
    "defaultValue": 1,
    "description": "Ratio of bob 2 mass to bob 1 mass"
  }
];

if (!window.__art_instances) window.__art_instances = {};
if (!window.__art_instances['double-pendulum-chaos']) {
  const inst = typeof createDoublePendulum === 'function' ? createDoublePendulum() : null;
  if (inst && inst.setup) {
    inst.setup({ ctx, width, height, dpr: 1, aspectRatio: width / height }, defaultParams);
  }
  window.__art_instances['double-pendulum-chaos'] = inst;
}

const instance = window.__art_instances['double-pendulum-chaos'];
if (instance && instance.render) {
  instance.render(
    { ctx, width, height, dpr: 1, aspectRatio: width / height },
    { time, deltaTime: dt, frameCount: Math.floor(time * 60), fps: 60 },
    defaultParams
  );
}
Edit in Interactive Playground Zero Dependencies • Standalone Canvas 2D
ƒ

Mathematical Formulation

high
Analytical Equation
L=TV,ddt(Lθ˙i)Lθi=0\mathcal{L} = T - V, \quad \frac{d}{dt}\left(\frac{\partial \mathcal{L}}{\partial \dot{\theta}_i}\right) - \frac{\partial \mathcal{L}}{\partial \theta_i} = 0
Click to expand
Compact Formula
alpha1 = num1(th1,th2,w1,w2)/den1; alpha2 = num2(th1,th2,w1,w2)/den2;

Mathematical Tags

#classical-mechanics #lagrangian #double-pendulum #phase-space #physics-study #chaos
Author: Classical Mechanics Core Target: 60 FPS

Export & Embed: Double Pendulum Mechanics

4K PNG Snapshot

High-resolution single frame render

WebM Video (5s Loop)

60 FPS browser-captured stream

Standalone JS Script

Complete executable Canvas 2D algorithm

HTML Iframe Embed

<iframe src="https://art.fazleyrabbi.xyz/embed/double-pendulum-chaos" width="500" height="500" frameborder="0" loading="lazy"></iframe>
ESC
↑↓ Navigate Select
110 Mathematical Artworks
Made by Fazley Rabbi

Support the Project

Keep mathematical creative coding alive & open source

Or via Direct Payoneer ($0 Fee)
Payoneer Customer ID: $0 Fee Direct
24076084

💡 Payoneer app: Go to Pay → Pay to recipient → Enter ID 24076084.

Thank you for supporting generative mathematical animations! ✨