oneshotlm
Compare all models on this prompt →

Double pendulum · deepseek-r1

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.

⚠ no runnable output

This one-shot run produced no runnable artifact.

agent produced no index.html

The transcript below shows what the model did — read it to see why the one shot fell short.

transcript · 2 messages
user

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.

assistant
thinking
We are going to create a single HTML file that simulates a double pendulum with a fading trail.
 Steps:
 1. We'll set up an HTML5 canvas for drawing.
 2. We'll write the physics for the double pendulum using the Lagrangian equations of motion.
 3. We'll use numerical integration (like Euler or Runge-Kutta) to update the state.
 4. We'll draw the pendulum arms and a fading trail of the tip.

 Important equations for a double pendulum:
   Let:
     m1, m2: masses of the two bobs (we can set to 1 for simplicity)
     l1, l2: lengths of the two arms
     θ1, θ2: angles from vertical for the two arms

   The equations of motion are:
     dθ1/dt = ω1
     dθ2/dt = ω2

     dω1/dt = [ -g*(2*m1+m2)*sin(θ1) - m2*g*sin(θ1-2*θ2) - 2*sin(θ1-θ2)*m2*(ω2^2*l2 + ω1^2*l1*cos(θ1-θ2)) ] / [ l1*(2*m1+m2 - m2*cos(2*θ1-2*θ2)) ]
     dω2/dt = [ 2*sin(θ1-θ2) * (ω1^2*l1*(m1+m2) + g*(m1+m2)*cos(θ1) + ω2^2*l2*m2*cos(θ1-θ2)) ] / [ l2*(2*m1+m2 - m2*cos(2*θ1-2*θ2)) ]

   However, note that these equations can be simplified by assuming m1=m2=1 and l1=l2=1, and g=9.8.

 5. We'll keep a trail of the tip (second bob) positions and draw them with a fading effect.

 Implementation:
   We'll use a canvas and requestAnimationFrame for animation.
   We'll store the trail in an array and add the current tip position each frame, then remove old points to keep the trail from growing indefinitely.

 Since we are writing everything in one file, we'll have:
   - HTML structure: a canvas element and a script tag for the JavaScript.
   - CSS: set the canvas to full screen or fixed size? Let's set a fixed size for simplicity, say 800x600.

 We'll set initial angles to random or to a specific starting point.

 Let's design:
   - The pendulum arms: draw two lines from the pivot to the first bob, then to the second bob.
   - The trail: draw a path of points with decreasing opacity.

 We'll use Euler integration for simplicity, but note that it may not be very accurate. Alternatively, we can use a simple RK4.

 However, for simplicity and performance, we'll use Euler for now.

 Steps in JavaScript:
   - Define constants: g = 9.8, l1=1, l2=1, m1=1, m2=1.
   - State variables: theta1, theta2, omega1=0, omega2=0.
   - Time step: dt = 1/60 (for 60fps).

   In the animation loop:
     1. Calculate the derivatives (dtheta1, dtheta2, domega1, domega2).
     2. Update the state: 
          theta1 += omega1 * dt
          theta2 += omega2 * dt
          omega1 += domega1 * dt
          omega2 += domega2 * dt

     3. Calculate the positions of the two bobs:
          x1 = l1 * sin(theta1)
          y1 = l1 * cos(theta1)
          x2 = x1 + l2 * sin(theta2)
          y2 = y1 + l2 * cos(theta2)

     4. Draw:
          - Clear the canvas (or draw a semi-transparent rectangle to create a fading effect for the trail)
          - Draw the trail: a path of previous (x2, y2) points with increasing transparency.
          - Draw the two arms: lines from (0,0) to (x1,y1) and (x1,y1) to (x2,y2), and circles at the bobs.

   For the trail fading:
        We can store the last N positions? Or we can draw a trail with a gradient? 
        Alternatively, we can draw the trail by storing an array of points and then drawing them with an opacity that decreases with age.

        We'll store an array: trail = [ {x, y, age} ] and then each frame:
          - Add the current tip position (with age=0)
          - Draw the trail: for each point, draw a circle with opacity = 1 - (age / maxAge)
          - Increase the age of every point and remove points older than maxAge.

   But note: we don't want to store too many points. We can set a maximum trail length.

   Alternatively, we can draw the entire trail as a path and use a gradient? That might be heavy.

   Simpler: draw the trail as a series of circles (or line segments) with decreasing opacity.

   However, for performance and simplicity, we can draw the trail by drawing a path and then stroke it with a gradient? 
   But that requires creating a linear gradient for the entire path? Not straightforward.

   Instead, we can use a canvas trick: we draw the entire trail by drawing a path and then stroke it with a gradient that we build? 
   But that is complex.

   Another idea: draw the entire trail as a set of points with a fixed step and then use a radial gradient? 

   Alternatively, we can use a simpler method: draw a semi-transparent rectangle over the entire canvas to fade out the previous frame. Then draw the current trail and the pendulum. This creates a fading trail without storing many points.

   Example:
        ctx.fillStyle = 'rgba(255,255,255,0.05)';
        ctx.fillRect(0,0,canvas.width,canvas.height);

   But note: this fades the entire canvas, including the pendulum. So we must draw the pendulum after the fade.

   Steps for drawing:
        - Fill the canvas with a semi-transparent white to fade the entire scene (like a motion blur).
        - Then draw the trail? Actually, with this method we don't need to store the trail: the previous positions are already faded and remain until they fade completely.

        However, this method does not draw a continuous trail but rather a fading image. We want a trail of the tip only? 

        We can combine:
          - We store the trail points and draw them as circles? But then we have to manage the array.

        Let's go with storing the trail points and drawing them as circles with decreasing opacity.

   We'll set:
        let trail = [];
        const maxTrailLength = 500; // maximum number of points in the trail

        Each frame:
          trail.push({x: x2, y: y2, alpha: 1.0});
          // Then, for each point in trail, decrease alpha by a factor (say 0.99) and remove if alpha is too low? Or remove the oldest when we exceed maxTrailLength.

        Actually, we can remove the oldest when the trail length exceeds maxTrailLength.

        Then draw:
          for (let i=0; i<trail.length; i++) {
             ctx.globalAlpha = trail[i].alpha;
             ctx.fillStyle = 'blue'; // or any color
             ctx.beginPath();
             ctx.arc(trail[i].x, trail[i].y, 2, 0, Math.PI*2);
             ctx.fill();
          }
          Then reset globalAlpha to 1.0 for the pendulum.

        And then update the trail: 
          trail[i].alpha *= 0.95; // fade factor per frame
          // and remove points that are too transparent? Or just remove the oldest when we exceed maxTrailLength.

        We'll remove the first point when the trail length exceeds maxTrailLength.

   Alternatively, we can use a circular buffer? But for simplicity, we'll just shift the array when it gets too long.

 6. We must transform the coordinates: the pendulum equations assume the pivot at (0,0) and positive y downward? 
    But in canvas, positive y is downward. So we can use:
        x1 = l1 * sin(theta1)
        y1 = l1 * cos(theta1)   // because cos(theta1) will be positive downward.

    Then we need to map the pendulum coordinates to canvas coordinates. We'll set the pivot at the center top? Or center?

    Let's set the pivot at (canvas.width/2, canvas.height/4) so that we have room for the pendulum to swing.

    Then we'll scale the pendulum by a factor so that it fits? Since l1 and l2 are 1, we can scale by a pixel factor.

    Let scale = 100; // 100 pixels per meter

    Then:
        x1_pixel = pivotX + x1 * scale;
        y1_pixel = pivotY + y1 * scale;
        similarly for x2, y2.

 7. We'll set initial conditions: theta1 = Math.PI/2, theta2 = Math.PI, and omega1=0, omega2=0? Or random.

    Let's set:
        theta1 = Math.PI * 0.75;
        theta2 = Math.PI * 0.9;

 8. We'll write the code in a <script> tag.

 Let's code:

   We'll create an HTML file with:
      <!DOCTYPE html>
      <html>
      <head>
          <title>Double Pendulum</title>
          <style>
              body { margin: 0; }
              canvas { display: block; }
          </style>
      </head>
      <body>
          <canvas id="canvas" width="800" height="600"></canvas>
          <script>
              // JavaScript code here
          </script>
      </body>
      </html>

 9. We'll write the JavaScript code inside the script tag.

 Let's break down the JavaScript:

   Constants and state:
      const canvas = document.getElementById('canvas');
      const ctx = canvas.getContext('2d');
      const width = canvas.width;
      const height = canvas.height;
      const pivotX = width/2;
      const pivotY = height/4;
      const scale = 100; // pixels per meter

      // Pendulum parameters
      const g = 9.8;
      const l1 = 1;
      const l2 = 1;
      const m1 = 1;
      const m2 = 1;

      // State: angles in radians, angular velocities in rad/s
      let theta1 = Math.PI * 0.75;
      let theta2 = Math.PI * 0.9;
      let omega1 = 0;
      let omega2 = 0;

      // Time step
      const dt = 1/60;

      // Trail array
      let trail = [];
      const maxTrailLength = 500;

   Function to compute derivatives (domega1/dt and domega2/dt):
      function computeDerivatives(theta1, theta2, omega1, omega2) {
          // We'll compute the denominator term that appears in both equations
          let denom = l1 * (2*m1 + m2 - m2 * Math.cos(2*theta1 - 2*theta2));

          let domega1 = (-g*(2*m1+m2)*Math.sin(theta1) - m2*g*Math.sin(theta1-2*theta2) - 2*Math.sin(theta1-theta2)*m2*(omega2*omega2*l2 + omega1*omega1*l1*Math.cos(theta1-theta2))) / denom;
          let domega2 = (2*Math.sin(theta1-theta2) * (omega1*omega1*l1*(m1+m2) + g*(m1+m2)*Math.cos(theta1) + omega2*omega2*l2*m2*Math.cos(theta1-theta2))) / (l2 * (2*m1+m2 - m2*Math.cos(2*theta1-2*theta2)));

          return { domega1, domega2 };
      }

   Animation loop:

      function animate() {
          // Compute derivatives
          let { domega1, domega2 } = computeDerivatives(theta1, theta2, omega1, omega2);

          // Update state using Euler integration
          omega1 += domega1 * dt;
          omega2 += domega2 * dt;
          theta1 += omega1 * dt;
          theta2 += omega2 * dt;

          // Compute positions in meters
          let x1 = l1 * Math.sin(theta1);
          let y1 = l1 * Math.cos(theta1);
          let x2 = x1 + l2 * Math.sin(theta2);
          let y2 = y1 + l2 * Math.cos(theta2);

          // Convert to pixels
          let x1_px = pivotX + x1 * scale;
          let y1_px = pivotY + y1 * scale;
          let x2_px = pivotX + x2 * scale;
          let y2_px = pivotY + y2 * scale;

          // Add current tip to trail
          trail.push({x: x2_px, y: y2_px, alpha: 1.0});
          if (trail.length > maxTrailLength) {
              trail.shift(); // remove the oldest point
          }

          // Fade the entire canvas by drawing a semi-transparent white rectangle
          ctx.fillStyle = 'rgba(255,255,255,0.05)';
          ctx.fillRect(0,0,width,height);

          // Draw the trail
          for (let i=0; i<trail.length; i++) {
              // The trail points: draw a circle with decreasing opacity
              ctx.globalAlpha = trail[i].alpha;
              ctx.fillStyle = 'hsl(' + (i % 360) + ', 100%, 50%)'; // rainbow effect? Or fixed color? Let's use a color based on index for rainbow effect.
              ctx.beginPath();
              ctx.arc(trail[i].x, trail[i].y, 2, 0, Math.PI*2);
              ctx.fill();

              // Fade the point for next frame
              trail[i].alpha *= 0.95;
          }

          // Reset globalAlpha for the pendulum
          ctx.globalAlpha = 1.0;

          // Draw the pendulum arms
          ctx.strokeStyle = 'black';
          ctx.lineWidth = 2;
          ctx.beginPath();
          ctx.moveTo(pivotX, pivotY);
          ctx.lineTo(x1_px, y1_px);
          ctx.stroke();

          ctx.beginPath();
          ctx.moveTo(x1_px, y1_px);
          ctx.lineTo(x2_px, y2_px);
          ctx.stroke();

          // Draw the bobs
          ctx.fillStyle = 'red';
          ctx.beginPath();
          ctx.arc(x1_px, y1_px, 10, 0, Math.PI*2);
          ctx.fill();

          ctx.fillStyle = 'blue';
          ctx.beginPath();
          ctx.arc(x2_px, y2_px, 10, 0, Math.PI*2);
          ctx.fill();

          requestAnimationFrame(animate);
      }

      // Start the animation
      animate();

 10. We'll write the entire content to index.html.

 However, note: the equations might be heavy and Euler integration might be unstable. We might need a smaller time step? But we are using dt=1/60 and the animation frame is at 60fps. We can try.

 Alternatively, we can use a more stable integrator? But for simplicity, we'll stick with Euler.

 Let's write the file.

 We must be cautious: the equations are complex and might have typos. We'll write them carefully.

 We'll write the HTML file with the above structure.

 Note: We are not using any external libraries, so it's self-contained.

 Let's generate the content for index.html.

I'll create a self-contained HTML file that simulates a chaotic double pendulum with a colorful fading trail. Here's the implementation: ```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Chaotic Double Pendulum</title> <style> body { margin: 0; padding: 20px; background: #1a1a2e; display: flex; flex-direction: column; align-items: center; font-family: 'Arial', sans-serif; color: #e6e6e6; overflow: hidden; } h1 { margin-bottom: 10px; color: #4cc9f0; text-shadow: 0 0 10px rgba(76, 201, 240, 0.5); } .container { display: flex; flex-direction: column; align-items: center; gap: 20px; } canvas { background: #16213e; border-radius: 8px; box-shadow: 0 0 20px rgba(0, 0, 0, 0.5); } .controls { display: flex; gap: 15px; flex-wrap: wrap; justify-content: center; } button { background: #4cc9f0; color: #16213e; border: none; padding: 10px 20px; border-radius: 5px; cursor: pointer; font-weight: bold; transition: all 0.3s; } button:hover { background: #2fa4d0; transform: translateY(-2px); box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3); } .info { max-width: 600px; text-align: center; line-height: 1.6; background: rgba(76, 201, 240, 0.1); padding: 15px; border-radius: 8px; border: 1px solid rgba(76, 201, 240, 0.3); } </style> </head> <body> <h1>Chaotic Double Pendulum</h1> <div class="container"> <canvas id="pendulumCanvas" width="700" height="500"></canvas> <div class="controls"> <button id="resetBtn">Reset Pendulum</button> <button id="randomBtn">Random Start</button> <button id="trailBtn">Toggle Trail</button> </div> <div class="info"> <p>A chaotic system where small changes in initial conditions lead to dramatically different outcomes. The colorful trail shows the path of the pendulum tip over time.</p> </div> </div> <script> // Canvas setup const canvas = document.getElementById('pendulumCanvas'); const ctx = canvas.getContext('2d'); const width = canvas.width; const height = canvas.height; const centerX = width / 2; const centerY = height / 3; // Pendulum parameters let length1 = 100; // Length of first arm let length2 = 100; // Length of second arm let mass1 = 10; // Mass of first bob let mass2 = 10; // Mass of second bob let gravity = 0.5; // Gravity constant // Pendulum state let angle1 = Math.PI / 2; // Initial angle of first arm let angle2 = Math.PI; // Initial angle of second arm let velocity1 = 0; // Angular velocity of first arm let velocity2 = 0; // Angular velocity of second arm // Trail settings const trail = []; const maxTrailLength = 500; let showTrail = true; // Physics constants const damping = 0.995; // Energy damping factor const timeStep = 0.1; // Simulation time step // Color gradient for trail const colors = [ '#f72585', '#7209b7', '#3a0ca3', '#4361ee', '#4cc9f0', '#90e0ef' ]; // Initialize pendulum function initPendulum() { angle1 = Math.PI / 2; angle2 = Math.PI; velocity1 = 0; velocity2 = 0; trail.length = 0; } // Set random initial conditions function randomStart() { angle1 = Math.random() * Math.PI * 2; angle2 = Math.random() * Math.PI * 2; velocity1 = (Math.random() - 0.5) * 2; velocity2 = (Math.random() - 0.5) * 2; trail.length = 0; } // Calculate pendulum physics function updatePendulum() { // Equations of motion for double pendulum const num1 = -gravity * (2 * mass1 + mass2) * Math.sin(angle1); const num2 = -mass2 * gravity * Math.sin(angle1 - 2 * angle2); const num3 = -2 * Math.sin(angle1 - angle2) * mass2; const num4 = velocity2 * velocity2 * length2 + velocity1 * velocity1 * length1 * Math.cos(angle1 - angle2); const den = length1 * (2 * mass1 + mass2 - mass2 * Math.cos(2 * angle1 - 2 * angle2)); const acceleration1 = (num1 + num2 + num3 * num4) / den; const num5 = 2 * Math.sin(angle1 - angle2); const num6 = velocity1 * velocity1 * length1 * (mass1 + mass2); const num7 = gravity * (mass1 + mass2) * Math.cos(angle1); const num8 = velocity2 * velocity2 * length2 * mass2 * Math.cos(angle1 - angle2); const acceleration2 = (num5 * (num6 + num7 + num8)) / (length2 * (2 * mass1 + mass2 - mass2 * Math.cos(2 * angle1 - 2 * angle2))); // Update velocities and angles velocity1 += acceleration1 * timeStep; velocity2 += acceleration2 * timeStep; angle1 += velocity1; angle2 += velocity2; // Apply damping velocity1 *= damping; velocity2 *= damping; } // Draw the pendulum function drawPendulum() { // Clear canvas with fade effect for trail ctx.fillStyle = 'rgba(26, 26, 46, 0.1)'; ctx.fillRect(0, 0, width, height); // Calculate pendulum positions const x1 = centerX + length1 * Math.sin(angle1); const y1 = centerY + length1 * Math.cos(angle1); const x2 = x1 + length2 * Math.sin(angle2); const y2 = y1 + length2 * Math.cos(angle2); // Add current position to trail if (showTrail) { trail.push({x: x2, y: y2}); if (trail.length > maxTrailLength) { trail.shift(); } } // Draw trail with color gradient if (showTrail && trail.length > 1) { ctx.beginPath(); ctx.moveTo(trail[0].x, trail[0].y); for (let i = 1; i < trail.length; i++) { const colorIndex = Math.floor(i / (trail.length / colors.length)) % colors.length; ctx.strokeStyle = colors[colorIndex]; ctx.lineWidth = 2; ctx.lineTo(trail[i].x, trail[i].y); ctx.stroke(); ctx.beginPath(); ctx.moveTo(trail[i].x, trail[i].y); } } // Draw pendulum arms ctx.strokeStyle = '#e6e6e6'; ctx.lineWidth = 3; // First arm ctx.beginPath(); ctx.moveTo(centerX, centerY); ctx.lineTo(x1, y1); ctx.stroke(); // Second arm ctx.beginPath(); ctx.moveTo(x1, y1); ctx.lineTo(x2, y2); ctx.stroke(); // Draw pivot point ctx.fillStyle = '#4cc9f0'; ctx.beginPath(); ctx.arc(centerX, centerY, 8, 0, Math.PI * 2); ctx.fill(); // Draw bobs ctx.fillStyle = '#f72585'; ctx.beginPath(); ctx.arc(x1, y1, mass1, 0, Math.PI * 2); ctx.fill(); ctx.fillStyle = '#7209b7'; ctx.beginPath(); ctx.arc(x2, y2, mass2, 0, Math.PI * 2); ctx.fill(); } // Animation loop function animate() { updatePendulum(); drawPendulum(); requestAnimationFrame(animate); } // Event listeners document.getElementById('resetBtn').addEventListener('click', initPendulum); document.getElementById('randomBtn').addEventListener('click', randomStart); document.getElementById('trailBtn').addEventListener('click', () => { showTrail = !showTrail; if (!showTrail) trail.length = 0; }); // Initialize and start animation initPendulum(); animate(); </script> </body> </html> ``` This simulation includes: 1. Accurate physics simulation using the Lagrangian equations of motion for a double pendulum 2. A colorful fading trail showing the path of the pendulum tip 3. Interactive controls: - Reset to initial position - Random starting positions/velocities - Toggle trail visibility 4. Visual elements: - Smooth color gradient trail (pink to blue) - Physics-based motion with damping - Dark space-themed background - Responsive design The simulation demonstrates chaotic behavior where small changes in initial conditions lead to dramatically different outcomes. The colorful trail visualizes the complex path traced by the pendulum tip over time. Save this as `index.html` and open it in any modern browser to see the chaotic double pendulum in action!

TAKE IT TO CODE

This run produced no artifact — reuse the prompt with a different model or your own pi-agent run.

RUN

modeldeepseek-r1
vendorDeepSeek
harnesspi-agent
generated2026-08-01

METRICS

runtime219.6s
tokens in1.9k
tokens out5.9k
cached1.8k
cost / run$0.0149