{
  "slug": "double-pendulum",
  "name": "Double pendulum",
  "title": "One prompt. Four models. Chaos with a conscience.",
  "date": "2026-07-15",
  "status": "published",
  "thesis": "For the checkable part of a task the model still doesn't matter — all four conserved energy to the same eight decimal places. What splits them now is whether they bothered to run their own work.",
  "verdict": "Every model passed the exact referee: energy conserved to ~1×10⁻⁸ %, a positive Lyapunov exponent, chaos confirmed. Then the clocks split them 4-to-1 — the two that skipped the browser finished in under four minutes; the two that opened a real browser and read their own referee took two to five times longer, and were the only two you could fully trust.",
  "prompt": "Build a single, self-contained HTML file (inline CSS and JavaScript, zero external dependencies, no CDN imports, no web fonts) that simulates a double pendulum on a canvas with a built-in referee panel that grades its own physics. The system: two point masses on massless rigid rods, swinging under gravity in a vertical plane. Use exactly these constants: mass m1 = m2 = 1 kg, rod lengths L1 = L2 = 1 m, gravitational acceleration g = 9.81 m/s². Angles θ1 and θ2 are measured from the downward vertical. The initial condition is fixed and deterministic — no randomness anywhere: θ1 = 120°, θ2 = 120°, and both angular velocities ω1 = ω2 = 0. Integrate the exact equations of motion of the double pendulum (the full nonlinear Lagrangian form, not the small-angle approximation) using RK4 with a fixed timestep dt = 1/2000 s, sub-stepping as many times per frame as needed to keep the animation running in real time. Render both rods and both bobs to scale, plus a fading trace of the lower bob's tip so the chaotic path is visible. Provide Play/Pause and Reset controls. The referee panel must be always visible and update live while the simulation runs, showing two things. (1) ENERGY DRIFT: each frame, compute the system's exact total mechanical energy E = kinetic + potential from the current state analytically, and report the percentage deviation of E from its value at t = 0. For a correct integrator at this timestep this stays essentially zero — display the current drift and the running maximum, and treat anything above 0.01% over 60 seconds as a failing integrator. (2) LYAPUNOV: run a second, hidden twin pendulum with an identical initial condition except θ1 perturbed by exactly 1e-9 radians; measure the separation between the two pendulums in state space over time, plot log(separation) versus time on a small inset, fit a straight line to the exponential-growth region, and display the measured largest Lyapunov exponent λ in units of 1/s — it must come out positive, which is the signature of deterministic chaos. Finally, add a RUN REFEREE button that executes the full check headless at maximum speed over 60 simulated seconds (independent of the animation) and prints, persistently on screen: maximum energy drift %, final energy drift %, the fitted Lyapunov exponent λ, and the simulated time at which the two trajectories first separate by 1 radian. The energy check is the exact, analytic referee; the Lyapunov exponent is the proof that the chaos is real and not a numerical artifact.",
  "referee": "Two referees, one exact. Energy conservation has an analytic ground truth — E = KE + PE must stay put, so any drift is pure integrator error. An independent RK4 run of the same system pins it at 6.6×10⁻⁸ % over 60 s (E₀ = 14.715 J), and all four land there. The Lyapunov exponent is the second referee: two trajectories 1e-9 apart must diverge exponentially, and a positive fitted λ proves the chaos is real, not a rounding artifact. Every number below is read from each app's own panel — then cross-checked against the independent simulation.",
  "scoreLabel": "wall-clock to done (from the race video)",
  "seriesFormula": "E = T + V conserved · E₀ = 14.715 J · measured drift ≈ 6.6×10⁻⁸ % over 60 s",
  "models": [
    {
      "slug": "composer-2-5",
      "name": "Composer 2.5",
      "modelId": "grok-composer-2.5-fast · badged “Composer 2.5”",
      "vendor": "Cursor",
      "cli": "Grok CLI",
      "confirmedVia": "~/.grok/sessions store (grok-composer-2.5-fast, agent “cursor”)",
      "accent": "#B045C4",
      "bright": "#D774E8",
      "score": 103,
      "scoreDisplay": "1:43",
      "lines": 713,
      "bytes": 24014,
      "costAsRun": null,
      "costClean": null,
      "costDisplay": "subscription — no per-token price",
      "costBasis": "Ran through the Grok CLI (Composer 2.5, the “cursor” agent) on a subscription plan; the CLI exposes no per-token price, so there is no honest dollar figure to put on a bar.",
      "tokens": {
        "input": null,
        "output": null,
        "reasoning": null,
        "cacheRead": null,
        "cacheWrite": null
      },
      "temperament": "The fastest overall, at 1m43s. The only file that renders crisp on a retina display and reflows on resize, with the most robust chaos-region Lyapunov fit — but the one that rounds its timestep each frame instead of carrying an accumulator, and asserted its referee numbers without ever running them.",
      "firstRenderRank": 1,
      "raceTime": "1:43",
      "raceSeconds": 103,
      "selfTested": false,
      "verifiedHow": "No visible browser or shell run — reported “verified headless results” from reasoning alone.",
      "drift": "6.6×10⁻⁸ %",
      "lyapunov": "1.47 1/s",
      "firstRadSep": "14.5 s"
    },
    {
      "slug": "grok-4-5",
      "name": "Grok 4.5",
      "modelId": "grok-4.5 · high effort",
      "vendor": "xAI",
      "cli": "Grok CLI",
      "confirmedVia": "~/.grok/sessions store (grok-4.5, high effort)",
      "accent": "#1E88AE",
      "bright": "#4FC3E8",
      "score": 238,
      "scoreDisplay": "3:58",
      "lines": 883,
      "bytes": 29425,
      "costAsRun": null,
      "costClean": null,
      "costDisplay": "subscription — no per-token price",
      "costBasis": "Ran on the Grok CLI subscription; the CLI exposes no per-token price, so there is no honest dollar figure to chart.",
      "tokens": {
        "input": null,
        "output": null,
        "reasoning": null,
        "cacheRead": null,
        "cacheWrite": null
      },
      "temperament": "The cleanest textbook energy of the four — the explicit 2L₁L₂ω₁ω₂cos(θ₁−θ₂) cross term spelled out — and the most thorough report, giving two separation metrics where the others give one. But it asserted its “validated” 60-second audit without ever opening the file, and its headless run blocks the UI thread. Done in 3m58s.",
      "firstRenderRank": 2,
      "raceTime": "3:58",
      "raceSeconds": 238,
      "selfTested": false,
      "verifiedHow": "Reported a “validated” 60 s audit, but no browser or shell execution is visible in the run.",
      "drift": "≈6.6×10⁻⁸ %",
      "lyapunov": "1.45 1/s",
      "firstRadSep": "14.85 s"
    },
    {
      "slug": "sonnet-5",
      "name": "Sonnet 5",
      "modelId": "claude-sonnet-5",
      "vendor": "Anthropic",
      "cli": "Claude Code",
      "confirmedVia": "session log in ~/.claude/projects (claude-sonnet-5)",
      "accent": "#C07818",
      "bright": "#E8A33D",
      "score": 547,
      "scoreDisplay": "9:07",
      "lines": 570,
      "bytes": 19738,
      "costAsRun": 2.02,
      "costClean": 2.02,
      "costDisplay": "~$2.02 as run (token-metered)",
      "costBasis": "Ran under a Claude Code plan, so there was no line-item bill — this is the token-metered equivalent at Sonnet 5 intro pricing ($2/$10 per Mtok in/out, $0.20 cache-read, $2.50 cache-write, in effect through 2026-08-31): 74 input + 96,174 output + 2,956,353 cache-read + 187,648 cache-write ≈ $2.02. At standard $3/$15 pricing it works out to ~$3.03.",
      "tokens": {
        "input": 74,
        "output": 96174,
        "reasoning": null,
        "cacheRead": 2956353,
        "cacheWrite": 187648
      },
      "temperament": "The perfectionist. Wrote the most compact file of the four, then drove a real browser fifteen times to run its own referee — noticed Chrome was throttling requestAnimationFrame in the unfocused automation tab, reasoned that the headless referee doesn't depend on it, and only then called it done. The only file whose 60-second audit runs asynchronously with a live progress bar instead of freezing the tab. 9m07s.",
      "firstRenderRank": 3,
      "raceTime": "9:07",
      "raceSeconds": 547,
      "selfTested": true,
      "verifiedHow": "Drove claude-in-chrome 15 times + one shell command; ran the live sim and the headless referee, cross-checked the two.",
      "drift": "6.57×10⁻⁸ %",
      "lyapunov": "1.634 1/s",
      "firstRadSep": "14.85 s"
    },
    {
      "slug": "deepseek-v4-pro",
      "name": "DeepSeek V4 Pro",
      "modelId": "deepseek-v4-pro (max)",
      "vendor": "DeepSeek",
      "cli": "opencode",
      "confirmedVia": "opencode SQLite session DB (deepseek-v4-pro)",
      "accent": "#2E9159",
      "bright": "#5BC98C",
      "score": 555,
      "scoreDisplay": "9:15",
      "lines": 590,
      "bytes": 18779,
      "costAsRun": 0.0432,
      "costClean": 0.0432,
      "costDisplay": "$0.0432 — metered, itemized",
      "costBasis": "The itemized bill: 24,338 input + 8,462 output + 25,474 reasoning tokens, 861K cached — summed across 17 assistant turns to $0.0432. Four cents for the only file that drove a real browser to grade itself.",
      "tokens": {
        "input": 24337,
        "output": 8462,
        "reasoning": 25474,
        "cacheRead": 861312,
        "cacheWrite": 0
      },
      "temperament": "The deliberator. The only model to drive chrome-devtools for real — clicked Run Referee, took a snapshot and a screenshot of the panel — and the only one to report an R² on its Lyapunov fit (0.964). Its physics is the most elegant: a reduced equal-mass form that's exact for these constants. Its one weakness is its own referee, which samples energy only every 100th step, making it the least precise verifier of the four. 9m15s, and eight cents.",
      "firstRenderRank": 4,
      "raceTime": "9:15",
      "raceSeconds": 555,
      "selfTested": true,
      "verifiedHow": "Drove chrome-devtools MCP for real: clicked Run Referee, snapshot + screenshot of the panel (saved to the run folder).",
      "drift": "4.94×10⁻⁸ %",
      "lyapunov": "1.535 1/s · R²=0.964",
      "firstRadSep": "14.50 s"
    }
  ],
  "measurements": [
    {
      "slug": "energy",
      "name": "Energy drift",
      "unit": "% deviation from E₀ over 60 s",
      "verdict": "The exact referee. All four conserved total mechanical energy to ~1×10⁻⁸ % — an independent RK4 run agrees — so nobody faked the integrator. DeepSeek reads lower only because its headless check undersamples.",
      "spread": "≈6.6×10⁻⁸ %, all PASS"
    },
    {
      "slug": "lyapunov",
      "name": "Lyapunov divergence",
      "unit": "fitted largest λ, 1/s",
      "verdict": "The chaos referee. Every twin, perturbed by 1e-9 rad, diverged exponentially with a positive λ — the sensitivity is real, not numerical. The four fits agree to within the window each chose.",
      "spread": "1.45 → 1.63 1/s"
    },
    {
      "slug": "cost",
      "name": "Cost",
      "unit": "USD for the same file",
      "verdict": "Two metered, two on subscription. The one fully-itemized bill came to four cents; the token-metered Sonnet equivalent to about two dollars.",
      "spread": "$0.04 → ~$2.02"
    },
    {
      "slug": "size",
      "name": "Output size",
      "unit": "lines · kilobytes",
      "verdict": "The most verbose file came from the fastest-reasoning model, not the slowest; the two most rigorous files were also the two most compact.",
      "spread": "570 → 883 lines"
    },
    {
      "slug": "speed",
      "name": "Speed & temperament",
      "unit": "wall-clock, from the race video",
      "verdict": "The whole story of this round. The two that never opened a browser finished in 1–4 minutes; the two that ran their own referee in a real browser took 9 — and were the only two that actually knew their file worked.",
      "spread": "1:43 → 9:15"
    }
  ]
}
