OverviewCode › R139
Code · blind round

R139 · Small n-body system orbiting a central star

blind round 8/11 local models answered

Every answer below carries the name of the model that wrote it. Blind refers to the scoring: the review was done without knowing which model produced which answer — the published result names them.

2 of the 11 local models delivered nothing through our test harness in this round — for reasons that lie with our setup, not with the models. Those cards are marked DNF like any other, but they are not evidence about those models — the reason is noted on the card itself.

Show task
Create ONE self-contained HTML file (STRICT LIMIT: max 7000 characters in total — this is a budget on characters, not on lines, so removing line breaks does not help) that animates a small gravitational n-body system on a <canvas>: one heavy central star near the centre plus 6 orbiting bodies of varying mass given stable-ish initial orbital velocities, computing real pairwise Newtonian gravitational forces between every pair of bodies each frame (not just star-to-body) with a small enough time step or substeps to keep the integration stable, so the orbits visibly perturb each other over time. Draw a fading trail behind each body and give each body a colour and size related to its mass. Show the number of bodies still in the visible field (not yet ejected or collided into the star). Smooth requestAnimationFrame animation, NO user input, starts immediately and keeps running (restart with a fresh random instance when finished). No external libraries, no comments needed. Output ONLY the HTML file content, nothing else.

The answers

Local models (Brain cluster) — identical prompt, shuffled order (the sequence is not a ranking)
qwen3-coder:30bOKFORMAT
Show code
```html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>N-Body Gravitational Simulation</title>
    <style>
        body { margin: 0; overflow: hidden; background: #000; }
        canvas { display: block; }
    </style>
</head>
<body>
<canvas id="nbodyCanvas"></canvas>

<script>
    const canvas = document.getElementById('nbodyCanvas');
    const ctx = canvas.getContext('2d');
    let W, H;

    // Simulation Constants
    const G = 0.1; // Gravitational Constant (scaled)
    const DT = 0.05; // Time step
    const TRAIL_LENGTH = 80; // Trail history points
    const MIN_DIST_SQUARED = 10; // Softening factor squared

    let bodies = [];

    function initCanvas() {
        W = window.innerWidth;
        H = window.innerHeight;
        canvas.width = W;
        canvas.height = H;
    }

    class Body {
        constructor(x, y, m, vx, vy, color) {
            this.x = x;
            this.y = y;
            this.m = m;
            this.vx = vx;
            this.vy = vy;
            this.color = color;
            this.trail = [];
        }

        updateTrail() {
            this.trail.push({ x: this.x, y: this.y });
            if (this.trail.length > TRAIL_LENGTH) {
                this.trail.shift();
            }
        }

        draw(ctx) {
            // Draw trail
            for (let i = 0; i < this.trail.length; i++) {
                const p = this.trail[i];
                const alpha = i / this.trail.length;
                ctx.fillStyle = `rgba(${this.color.r}, ${this.color.g}, ${this.color.b}, ${alpha * 0.5})`;
                ctx.beginPath();
                ctx.arc(p.x, p.y, 1 + (i / this.trail.length) * 2, 0, Math.PI * 2);
                ctx.fill();
            }

            // Draw body
            const size = Math.max(3, Math.pow(this.m, 1/3));
            ctx.fillStyle = `rgb(${this.color.r}, ${this.color.g}, ${this.color.b})`;
            ctx.beginPath();
            ctx.arc(this.x, this.y, size, 0, Math.PI * 2);
            ctx.fill();
        }
    }

    function setupBodies() {
        bodies = [];
        const centerX = W / 2;
        const centerY = H / 2;
        
        // Body 0: Central Star (Heavy, stationary anchor)
        bodies.push(new Body(centerX, centerY, 15, 0, 0, {r: 255, g: 255, b: 200}));

        // Six orbiting bodies with varied initial conditions for perturbation
        const setupBody = (angle, radius, mass, speed) => {
            const x = centerX + Math.cos(angle) * radius;
            const y = centerY + Math.sin(angle) * radius;
            const vx = -Math.sin(angle) * speed;
            const vy = Math.cos(angle) * speed;
            // Simple color assignment based on index/mass
            const color = {r: 100 + Math.random() * 155, g: 50 + Math.random() * 155, b: 255 - (Math.random() * 155)};
            return new Body(x, y, mass, vx, vy, color);
        };

        bodies.push(setupBody(0, 150, 3, 1));
        bodies.push(setupBody(2 * Math.PI / 5, 200, 6, 0.8));
        bodies.push(setupBody(4 * Math.PI / 7, 120, 2, 1.3));
        bodies.push(setupBody(6 * Math.PI / 9, 250, 8, 0.5));
        bodies.push(setupBody(Math.PI/4, 180, 4, 1.1));
        bodies.push(setupBody(3 * Math.PI/2, 100, 1, 1.5));
    }

    function calculateForces() {
        const forces = bodies.map(() => ({ fx: 0, fy: 0 }));

        for (let i = 0; i < bodies.length; i++) {
            for (let j = i + 1; j < bodies.length; j++) {
                const b1 = bodies[i];
                const b2 = bodies[j];

                let dx = b2.x - b1.x;
                let dy = b2.y - b1.y;
                let distSq = dx * dx + dy * dy;
                
                // Softening factor prevents division by zero/infinity near collision
                distSq += MIN_DIST_SQUARED; 

                const r = Math.sqrt(distSq);
                let F_magnitude = G * b1.m * b2.m / (r * r); // Using r*r for force calculation simplicity, since the softening factor is already included in distSq
                
                // Recalculate actual magnitude using softened distance (for stability)
                F_magnitude = G * b1.m * b2.m / Math.sqrt(distSq);
                let F_actual = F_magnitude / r;

                let fx = F_actual * dx;
                let fy = F_actual * dy;

                // Apply force to both bodies
                forces[i].fx += fx;
                forces[i].fy += fy;
                forces[j].fx -= fx;
                forces[j].fy -= fy;
            }
        }
        return forces;
    }
    
    function integrate(forces) {
        for (let i = 0; i < bodies.length; i++) {
            const b = bodies[i];
            const force = forces[i];

            // Acceleration calculation: a = F / m
            const ax = force.fx / b.m;
            const ay = force.fy / b.m;

            // Update velocity (Euler step)
            b.vx += ax * DT;
            b.vy += ay * DT;
            
            // Update position
            b.x += b.vx * DT;
            b.y += b.vy * DT;

            // Update trail history
            b.updateTrail();
        }
    }

    function draw() {
        // Clear canvas with slight transparency to create a fading effect (the trail)
        ctx.fillStyle = 'rgba(0, 0, 0, 0.1)';
        ctx.fillRect(0, 0, W, H);

        for (let i = 0; i < bodies.length; i++) {
            bodies[i].draw(ctx);
        }

        // Display body count
        const visibleBodies = bodies.filter(b => b.x > -10 && b.x < W + 10 && b.y > -10 && b.y < H + 10).length;
        ctx.fillStyle = '#FFF';
        ctx.font = '20px monospace';
        ctx.fillText(`Bodies Visible: ${visibleBodies}`, 10, 30);
    }

    function loop() {
        // Check for system collapse or ejection (simple restart condition)
        let allOk = true;
        for(const body of bodies) {
            if (body.m > 0 && Math.hypot(body.x - W/2, body.y - H/2) > W*1.5 || Math.abs(body.vx) > 100 || Math.abs(body.vy) > 100) {
                // Simplified check: if any body gets far away or unstable, restart
                allOk = false;
                break;
            }
        }

        if (!allOk) {
            setupBodies(); // Restart simulation with fresh configuration
        } else {
            const forces = calculateForces();
            integrate(forces);
            draw();
        }

        requestAnimationFrame(loop);
    }

    window.addEventListener('resize', () => {
        initCanvas();
        setupBodies();
    });

    // Initialization sequence
    initCanvas();
    setupBodies();
    loop();

</script>
</body>
</html>
```
deepseek-r1:14bERRBLANKFORMAT
Show code
```html
<!DOCTYPE html>
<html><body style="margin:0;overflow:hidden;background:black;">
<canvas id=c width=800 height=600></canvas>
<script>
const c=document.getElementById('c'),ctx=c.getContext('2d');
const b=64,w=800,h=600,s=(w+b)/2,t=s-h/2;
let stars=Array(7).fill().map(i=>({
  m:1, x:i<1? w/2 : Math.random()*(w-b*0.9)+b*0.05,
  y:i<1? h/2 : Math.random()*(h-b*0.9)+b*0.05,
  vx:Math.random()*0.3,vy:i<1?0:-(i>1?0.008:i>4?0.01:i<6?-0.01:0),
  ax:0,ay:0,tail:[]
}));

stars.forEach(i=>{
  if(i.x<s±b/2 && i.y<t±b/2)i.m=9
});

function force(p,q){
  const dx=p.x-q.x, dy=p.y-q.y,d=Math.sqrt(dx*dx+dy*dy);
  const f=G*p.m*q.m/d²;
  q.ax +=f*dx/(d*(q.m)), q.ay +=f*dy/(d*(q.m));
  p.ax -=f*dx/(d*(p.m)), p.ay -=f*dy/(d*(p.m));
}

const G=0.2;

function update(){
  for(let i=0;i<stars.length;i++){
    const s1=stars[i],s2=stars[j];
    if(i<j)force(s1,s2);
  }
  stars.forEach(p=>{
    p.x +=p.vx, p.y +=p.vy;
    p.vx *=0.99,p.vy *=0.99;
    p.tail.push({x:p.x,y:p.y}),p.tail.shift();
  });
}

function draw(){
  ctx.fillStyle='black';ctx.fillRect(0,0,w,h);
  stars.forEach(p=>{
    const len=p.tail.length;
    for(let i=1;i<len;i++){
      const a=i-1,b=i;
      ctx.beginPath();
      ctx.moveTo(p.tail[a].x,p.tail[a].y);
      ctx.lineTo(p.tail[b].x,p.tail[b].y);
      ctx.strokeStyle='rgba(255,255,255,'+(len-i)/len*0.3)';
      ctx.lineWidth=1;
      ctx.stroke();
    }
    const mx=p.m/6+1,r=Math.sqrt(mx);
    const hue=stars.indexOf(p)*30;
    ctx.beginPath();
    ctx.arc(p.x,p.y,mx,0,2*Math.PI);
    ctx.fillStyle=`hsl(${hue},${p.m>4?'75':'50'}%,${mx*4})`;
    ctx.fill();
  });
}

function loop(){
  update(),draw();
  requestAnimationFrame(loop);
}

loop();
</script>
</body></html>
```
qwen3:8bharness
No answer reached us (ERR) — and the reason lies with our setup, not with the model: reasoning consumed the entire token budget and the answer stayed empty; the gateway rejects budgets above 3000 tokens Counted as a did-not-finish, but it is not evidence about this model.
llama3.1:8bBLANKFORMAT
Show code
```html
<!DOCTYPE html>
<html>
<head>
<style>
body {
  margin: 0;
}
</style>
</head>
<body>
<canvas id="c" width="640" height="480"></canvas>
<script>
var c = document.getElementById('c');
var ctx = c.getContext('2d');
var bodies = [];
for(var i=0;i<6;++i) {
  var m = Math.random() * 100;
  var v = [Math.random()*5-2.5, Math.random()*5-2.5];
  var r = Math.sqrt(v[0]*v[0]+v[1]*v[1]);
  if(r > 4) {
    r /= 10;
    v[0] *= 10; v[1] *= 10;
  }
  var x = Math.random()*c.width + c.width/2 - (r+5);
  var y = Math.random()*c.height + c.height/2 - (r+5);
  var col = '#'+Math.floor(Math.random()*16777215).toString(16);
  bodies.push({m:m, v:v, r:r, x:x, y:y, col:col});
}
var G = 0.1;
var t = 0;
function draw() {
  ctx.clearRect(0,0,c.width,c.height);
  var mx = c.width/2; var my = c.height/2;
  for(var i=0;i<bodies.length;++i) {
    ctx.beginPath();
    ctx.arc(bodies[i].x,bodies[i].y,bodies[i].r,0,2*Math.PI);
    ctx.fillStyle=bodies[i].col;
    ctx.fill();
    var d = Math.sqrt((bodies[i].x-mx)*(bodies[i].x-mx)+(bodies[i].y-my)*(bodies[i].y-my));
    if(d < bodies[i].r) {
      for(var j=0;j<bodies.length;++j) {
        if(j != i && d2(bodies[j],{x:mx,y:my}) < (bodies[j].r+bodies[i].r)) {
          bodies.splice(i,1); --i;
          ctx.clearRect(0,0,c.width,c.height);
          draw();
          return draw();
        }
      }
    } else if(d > c.width || d > c.height) {
      bodies.splice(i,1); --i;
      ctx.clearRect(0,0,c.width,c.height);
      draw();
      return draw();
    }
    var dt = 0.001;
    for(var j=0;j<bodies.length;++j) {
      if(j != i)
        bodies[i].v[0] += G * bodies[j].m / (d2(bodies[i],bodies[j]))*(bodies[j].x-bodies[i].x)/d;
      bodies[i].v[1] += G * bodies[j].m / (d2(bodies[i],bodies[j]))*(bodies[j].y-bodies[i].y)/d;
    }
    var dx = -dt * 0.5 * (bodies[i].v[0]);
    var dy = -dt * 0.5 * (bodies[i].v[1]);
    bodies[i].x += dx; bodies[i].y += dy;
    ctx.beginPath();
    ctx.arc(bodies[i].x,bodies[i].y,3,0,2*Math.PI);
    ctx.fillStyle='rgba(255,255,255,'+0.05*20/(bodies[i].m+1)+')';
    ctx.fill();
  }
  var trails = [];
  for(var i=0;i<bodies.length;++i) {
    var x = bodies[i].x; var y = bodies[i].y;
    for(var j=-6;j<7;++j)
      if(0 <= j && j < Math.floor(bodies[i].r*10)) {
        trails.push({x:x,y:y-5*j});
      }
  }
  ctx.beginPath();
  ctx.moveTo(trails[0].x,trails[0].y);
  for(var i=1;i<trails.length;++i)
    ctx.lineTo(trails[i].x,trails[i].y);
  ctx.strokeStyle='rgba(255,255,255,0.05)';
  ctx.stroke();
}
function d2(b,a) {
  return (a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y);
}
setInterval(draw,16);
</script>
</body>
</html>
```
mistral-small:24bOKFORMAT
Show code
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>N-Body Simulation</title>
<style>
canvas { display: block; margin: auto; background: #000; }
</style>
</head>
<body onload="start()">
<canvas id="nbody" width="800" height="600"></canvas>
<script>
function start() {
let w = 800, h = 600;
let ctx = document.getElementById('nbody').getContext('2d');
let bodies = [];
let G = 1e-3; let dt = .5; let trailLen = 36;
let stars = 6;

function random(min, max) { return Math.random() * (max - min) + min; }
function dist(x1, y1, x2, y2) { return Math.sqrt((x2-x1)**2 + (y2-y1)**2); }

class Body {
constructor(mass, x, y, vx, vy) {
this.mass = mass;
this.x = x; this.y = y; this.vx = vx; this.vy = vy;
this.trail = [];
}}
function randomBody() { return new Body(random(50, 200), random(w/4, w*3/4), random(h/4, h*3/4), random(-1, 1), random(-1, 1)); }

for (let i=0; i<stars; ++i) { bodies.push(randomBody()); }
bodies.push(new Body(5000, w / 2, h / 2, 0, 0));

function update() {
for (let b of bodies) {
b.trail.unshift({ x: b.x, y: b.y });
if (b.trail.length > trailLen) { b.trail.pop(); }}
for (let i=0; i<bodies.length; ++i) {
for (let j=i+1; j<bodies.length; ++j) {
let dx = bodies[j].x - bodies[i].x;
let dy = bodies[j].y - bodies[i].y;
let d2 = dx**2 + dy**2;

if(d2 === 0) continue;
let f = G * bodies[i].mass * bodies[j].mass / d2;
bodies[i].vx += (f * dx / d2) * dt / bodies[i].mass;
bodies[i].vy += (f * dy / d2) * dt / bodies[i].mass;
bodies[j].vx -= (f * dx / d2) * dt / bodies[j].mass;
bodies[j].vy -= (f * dy / d2) * dt / bodies[j].mass;}}
for (let b of bodies) { b.x += b.vx*dt; b.y += b.vy*dt; }}
function draw() {
ctx.clearRect(0, 0, w, h);
for (let i=0; i<bodies.length; ++i) {
let b = bodies[i];
let size = Math.sqrt(b.mass)/10;
let alpha = .8 - (Math.random())*0.3;
ctx.beginPath();
ctx.arc(b.x, b.y, size, 0, 2 * Math.PI);
ctx.fillStyle=`rgba(${size*15}, ${size*7}, 255, ${alpha})`;
ctx.fill();
for(let p of b.trail) {
let alpha = .4 - (b.trail.indexOf(p)/trailLen)*0.3;
ctx.beginPath();
ctx.arc(p.x, p.y, size*.6, 0, 2 * Math.PI);
ctx.fillStyle=`rgba(${size*15}, ${size*7}, 255, ${alpha})`;
ctx.fill(); }}}

let visibleBodies = bodies.length;

function animate() {
update();
draw();
visibleBodies = bodies.filter(b => dist(w/2, h/2, b.x, b.y) < w && Math.sqrt(dist(0,0,b.x,b.y)**2)<1e6).length;
ctx.fillStyle = 'white';
ctx.font = '24px Arial';
ctx.fillText(`Bodies: ${visibleBodies}`, 10, h - 10);
if (visibleBodies === 0) start();
requestAnimationFrame(animate); }
animate();}
</script>
</body>
</html>
```
qwen3.5:9bSTATFORMAT
Show code
```html
<!DOCTYPE html>
<html>
<head>
<title>N-Body Gravity Simulation</title>
<style>body { margin: 0; overflow: hidden; background: #000; } canvas { display: block; } </style>
</head>
<body>
<canvas id="c"></canvas>
<script>
const c = document.getElementById('c');
const ctx = c.getContext('2d');
let width, height;

function init() {
    width = window.innerWidth;
    height = window.innerHeight;
    c.width = width;
    c.height = height;
}

window.addEventListener("resize", init);
init();

class Body {
    constructor(x, y, m, vx, vy) {
        this.pos = { x: x, y: y };
        this.vel = { x: vx, y: vy };
        this.acc = { x: 0, y: 0 };
        this.mass = m;
        this.radius = Math.max(2, Math.pow(m, 1/3));
        this.color = `hsl(${Math.random() * 360}, 80%, ${50 + m*5}%)`;
        this.trail = [];
    }

    applyForce(ax, ay) {
        this.acc.x += ax;
        this.acc.y += ay;
    }

    resetAcc() {
        this.acc.x = 0;
        this.acc.y = 0;
    }
}

let bodies = [];
const N_BODIES = 7; // Star + 6 orbiting bodies
const G = 1000; // Gravitational constant scale factor
let timeStep = 0.8; // Time step for integration (slower steps are more stable)
let trailLength = 30;

function setupBodies() {
    bodies = [];
    
    // Central Star (Heavy, near center)
    const starMass = 20000;
    bodies.push(new Body(width / 2, height / 2, starMass, 0, 0));

    // Orbiting bodies
    for (let i = 1; i < N_BODIES; i++) {
        const m = Math.random() * 400 + 50; // Varying mass (50 to 450)
        const r = Math.random() * Math.min(width, height) / 2 * 0.8 + 50; // Orbital radius
        const angle = Math.random() * Math.PI * 2;

        // Initial velocity calculation for semi-stable orbit (v^2 = G*M/r)
        // We use the star's mass as the primary gravitational source for initial kick
        let v_mag = Math.sqrt(G * starMass / r);
        let vx = Math.cos(angle) * v_mag * (0.9 + Math.random() * 0.2); // Slight perturbation
        let vy = Math.sin(angle) * v_mag * (0.9 + Math.random() * 0.2);

        bodies.push(new Body(width / 2 + r * Math.cos(angle), height / 2 + r * Math.sin(angle), m, vx, vy));
    }
}

function calculateForces() {
    for (let i = 0; i < bodies.length; i++) {
        bodies[i].resetAcc();
    }

    for (let i = 0; i < bodies.length; i++) {
        const bodyA = bodies[i];
        for (let j = i + 1; j < bodies.length; j++) {
            const bodyB = bodies[j];

            const dx = bodyB.pos.x - bodyA.pos.x;
            const dy = bodyB.pos.y - bodyA.pos.y;
            let r2 = dx * dx + dy * dy;
            
            // Softening parameter (epsilon) to prevent division by zero/infinity near collision
            let r3 = Math.pow(r2 + 100, 1.5); 

            // F = G * m1 * m2 / r^2
            const forceMagnitude = G * bodyA.mass * bodyB.mass / r3;

            const ax = (forceMagnitude * dx) / Math.sqrt(r2 + 100);
            const ay = (forceMagnitude * dy) / Math.sqrt(r2 + 100);

            bodyA.applyForce(ax, ay);
            bodyB.applyForce(-ax, -ay); // Action-Reaction pair
        }
    }
}

function updatePositions() {
    for (const body of bodies) {
        // Update velocity and position using Euler integration
        body.vel.x += body.acc.x * timeStep;
        body.vel.y += body.acc.y * timeStep;
        
        body.pos.x += body.vel.x * timeStep;
        body.pos.y += body.vel.y * timeStep;

        // Update trail
        body.trail.push({ x: body.pos.x, y: body.pos.y });
        if (body.trail.length > trailLength) {
            body.trail.shift();
        }
    }
}

function draw() {
    ctx.fillStyle = 'rgba(0, 0, 0, 0.1)'; // Fading effect for trails/background
    ctx.fillRect(0, 0, width, height);

    for (const body of bodies) {
        // Draw trail
        if (body.trail.length > 1) {
            ctx.beginPath();
            ctx.moveTo(body.trail[0].x, body.trail[0].y);
            for (let i = 1; i < body.trail.length; i++) {
                ctx.lineTo(body.trail[i].x, body.trail[i].y);
            }
            ctx.strokeStyle = body.color + '50'; // Semi-transparent color for trail
            ctx.lineWidth = 1;
            ctx.stroke();
        }

        // Draw body
        ctx.beginPath();
        ctx.arc(body.pos.x, body.pos.y, body.radius, 0, Math.PI * 2);
        ctx.fillStyle = body.color;
        ctx.fill();
    }
}

function loop() {
    calculateForces();
    updatePositions();
    draw();
    requestAnimationFrame(loop);
}

// Initialization sequence
setupBodies();
loop();
</script>
</body>
</html>
```
gemma4:26bharness
No answer reached us (ERR) — and the reason lies with our setup, not with the model: reasoning consumed the entire token budget (3 min 28 s, zero characters of answer); larger budgets are refused by the gateway Counted as a did-not-finish, but it is not evidence about this model.
qwen3:14bERRBLANKFORMAT
Show code
```html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>N-Body Gravity Simulator</title>
    <style>
        body { margin: 0; overflow: hidden; background-color: #000; }
        canvas { display: block; }
        #info { position: absolute; top: 10px; left: 10px; color: #fff; font-family: monospace; font-size: 14px; opacity: 0.7; z-index: 10; }
    </style>
</head>
<body>
<canvas id="c"></canvas>
<div id="info">Bodies: </div>

<script>
const canvas = document.getElementById('c');
const ctx = canvas.getContext('2d');
let width, height;
let bodies = [];
let animationFrameId;

// --- Configuration Constants ---
const G = 0.1; // Gravitational Constant (scaled)
const SOFTENING = 5; // Softening factor for r^2 to prevent division by zero
const DT = 0.03; // Time step
const TRAIL_LENGTH = 80;

class Body {
    constructor(x, y, m, vx, vy, color) {
        this.pos = { x: x, y: y };
        this.vel = { x: vx, y: vy };
        this.acc = { x: 0, y: 0 };
        this.mass = m;
        this.color = color;
        this.radius = Math.pow(m, 1/3) * 2; // Size related to cube root of mass
        this.trail = [];
    }

    updateTrail() {
        this.trail.push({ x: this.pos.x, y: this.pos.y });
        if (this.trail.length > TRAIL_LENGTH) {
            this.trail.shift();
        }
    }

    draw(ctx) {
        // Draw trail
        for (let i = 0; i < this.trail.length; i++) {
            const p = this.trail[i];
            const alpha = i / this.trail.length * 0.5 + 0.1;
            ctx.fillStyle = `rgba(${this.color.r}, ${this.color.g}, ${this.color.b}, ${alpha})`;
            ctx.beginPath();
            ctx.arc(p.x, p.y, this.radius * (i / this.trail.length) * 0.5, 0, Math.PI * 2);
            ctx.fill();
        }

        // Draw body
        ctx.fillStyle = `rgb(${this.color.r}, ${this.color.g}, ${this.color.b})`;
        ctx.beginPath();
        ctx.arc(this.pos.x, this.pos.y, this.radius, 0, Math.PI * 2);
        ctx.fill();
    }
}

// --- Core Physics Loop ---

function computeForces() {
    for (let i = 0; i < bodies.length; i++) {
        bodies[i].acc.x = 0;
        bodies[i].acc.y = 0;
    }

    for (let i = 0; i < bodies.length; i++) {
        for (let j = i + 1; j < bodies.length; j++) {
            const b1 = bodies[i];
            const b2 = bodies[j];

            const dx = b2.pos.x - b1.pos.x;
            const dy = b2.pos.y - b1.pos.y;
            let r2 = dx * dx + dy * dy;
            r2 += SOFTENING * SOFTENING; 
            let r = Math.sqrt(r2);

            // Force calculation: F = G * m1 * m2 / r^2
            const force = (G * b1.mass * b2.mass) / r2;
            
            // Acceleration calculation: a = F / m
            const ax1 = force * dx / (b1.mass * r);
            const ay1 = force * dy / (b1.mass * r);

            const ax2 = -force * dx / (b2.mass * r);
            const ay2 = -force * dy / (b2.mass * r);


            // Apply acceleration updates
            bodies[i].acc.x += ax1;
            bodies[i].acc.y += ay1;

            bodies[j].acc.x += ax2;
            bodies[j].acc.y += ay2;
        }
    }
}

function integrate() {
    for (let b of bodies) {
        // Update velocity: v(t+dt) = v(t) + a * dt
        b.vel.x += b.acc.x * DT;
        b.vel.y += b.acc.y * DT;

        // Update position: p(t+dt) = p(t) + v(t+dt) * dt
        b.pos.x += b.vel.x * DT;
        b.pos.y += b.vel.y * DT;

        // Reset acceleration for next step
        b.acc.x = 0;
        b.acc.y = 0;

        b.updateTrail();
    }
}

function draw() {
    // Clear background with subtle fade effect (instead of full clear)
    ctx.fillStyle = 'rgba(0, 0, 0, 0.1)';
    ctx.fillRect(0, 0, width, height);

    for (let b of bodies) {
        b.draw(ctx);
    }

    // Update info display
    const livingBodies = bodies.filter(b => 
        Math.abs(b.pos.x) < width * 1.5 && Math.abs(b.pos.y) < height * 1.5
    ).length;
    document.getElementById('info').textContent = `Bodies: ${livingBodies}`;
}

function mainLoop() {
    computeForces();
    integrate();
    draw();
    animationFrameId = requestAnimationFrame(mainLoop);
}

// --- Initialization and Setup ---

function resize() {
    width = window.innerWidth;
    height = window.innerHeight;
    canvas.width = width;
    canvas.height = height;
}

function createSystem() {
    bodies = [];

    const c = (r, g) => ({ r: Math.floor(Math.random() * 255), g: Math.floor(Math.random() * 255), b: Math.floor(Math.random() * 255) });
    
    // Central Star (Heavy Anchor)
    bodies.push(new Body(width / 2, height / 2, 100, 0, 0, {r: 255, g: 255, b: 150}));

    const initialBodies = [
        // Pos X, Pos Y, Mass, Vx, Vy, Color
        [width * 0.4, height / 2 - 50, 1.5, Math.random() * 3 + 1, Math.sqrt(G * 10) * 1.2, c(100, 100)],
        [width * 0.6, height / 2 - 50, 2.0, Math.random() * 3 + 1, -Math.sqrt(G * 10) * 1.4, c(100, 150, 100)],
        [width / 2 - 150, height * 0.7, 1.0, Math.random() * 3 + 1, Math.random() * 3 + 1, c(150, 100, 100)],
        [width / 2 + 150, height * 0.3, 1.2, -Math.random() * 3 - 1, Math.random() * 3 + 1, c(100, 100, 200)],
        [width * 0.1, height * 0.9, 0.8, 0, 0, c(50, 50, 255)], // Test body far out
        [width * 0.9, height * 0.9, 1.8, -Math.random() * 3 - 1, -Math.random() * 3 - 1, c(255, 100, 50)]
    ];

    // Adjust initial velocities for a more stable start (approximate circular velocity)
    const starMass = bodies[0].mass;
    for (let i = 0; i < 6; i++) {
        const [x, y, m, _, _, color] = initialBodies[i];
        const dx = x - width / 2;
        const dy = y - height / 2;
        // Target velocity for circular orbit: v = sqrt(G * M / r)
        const r = Math.sqrt(dx*dx + dy*dy);
        const targetV = Math.sqrt(G * starMass / r) * 0.9; // Use 90% of stable speed

        // Set initial velocities perpendicular to the position vector (for stability)
        let vx_init, vy_init;
        if (Math.abs(dx) < 1 && Math.abs(dy) < 1){ // If near center
             vx_init = 0; vy_init = targetV;
        } else {
            // Calculate perpendicular components for initial velocity
            vx_init = -targetV * (y - height / 2) / r;
            vy_init = targetV * (x - width / 2) / r;
        }


        bodies.push(new Body(x, y, m, vx_init, vy_init, color));
    }
}

window.addEventListener('resize', () => {
    resize();
    createSystem(); // Reinitialize system on resize
});

// Start everything
resize(); 
createSystem();
mainLoop();
</script>
</body>
</html>
```
deepseek-r1:32bERRBLANKMINIFIEDFORMAT
Show code
```html
<!DOCTYPE html><html><body style="margin:0;padding:0;display:flex;"><canvas id=c></canvas>
<script>
"use strict";const canvas=document.getElementById("c"),ctx=canvas.getContext("2d");let W=window.innerWidth,H=window.innerHeight,collisionCount=0,ejectionCount=0,bodies=[];const NBODIES=7,G=1e-3,DT=.1,MAX_MASS=1e4,COLLISION_DIST=50,EJECTION_DIST=1000;
function init(){collisionCount=0,ejectionCount=0;bodies=[{x:0,y:0,vx:0,vy:0,mass:MAX_MASS*2,radius:20,color:"#ffff00",trail:[]},...Array(NBODIES-1).fill().map(()=>({x:Math.random()*8e3-4e3,y:Math.random()*8e3-4e3,vx:0,vy:0,mass:MAX_MASS*Math.random(),radius:10*Math.random()+5,color:`hsl(${Math.random()*360},100%,50%)`,trail:[]}));for(let i=1;i<NBODIES;++i){const body=bodies[i],r=Math.hypot(body.x,body.y);body.vx=-G*bodies[0].mass/(r*r)**.5*body.y/r;body.vy=G*bodies[0].mass/(r*r)**.5*body.x/r;if(Math.random()>.9)body.vx+=Math.random()*2-1,body.vy+=Math.random()*2-1;}
}
function computeForces(){for(let i=0;i<NBODIES;++i)for(let j=i+1;j<NBODIES;++j){const b1=bodies[i],b2=bodies[j],dx=b2.x-b1.x,dy=b2.y-b1.y,r=Math.hypot(dx,dy),force=G*b1.mass*b2.mass/(r*r);if(r>0){const fx=dx/r*force,fy=dy/r*force;b1.vx-=fx/b1.mass,b1.vy-=fy/b1.mass;b2.vx+=fx/b2.mass,b2.vy+=fy/b2.mass;}}
function updateBodies(){for(let body of bodies){if(body.trail.length<60)body.trail.unshift({x:body.x,y:body.y,alpha:1});else body.trail.shift();}computeForces();for(let body of bodies){const vx=body.vx,vy=body.vy;body.x+=vx*DT,body.y+=vy*DT;if(body.trail.length<60)body.trail.unshift({x:body.x,y:body.y,alpha:1});else body.trail.shift();if(Math.hypot(body.x,body.y)<COLLISION_DIST&&body!==bodies[0])collisionCount++;if(Math.hypot(body.x,body.y)>EJECTION_DIST)bodies.splice(bodies.indexOf(body),1);}}
function draw(){ctx.fillStyle="#000",ctx.fillRect(0,0,W,H);for(let body of bodies){const x=body.x+canvas.width/2,y=body.y+canvas.height/2;ctx.beginPath();ctx.arc(x,y,body.radius,0,Math.PI*2);ctx.fillStyle=body.color;ctx.fill();}for(let i=0;i<NBODIES;++i)for(let j=0;j<body.trail.length-1;++j){const t1=body.trail[j],t2=body.trail[j+1];ctx.beginPath();ctx.strokeStyle=body.color;ctx.lineWidth=2;ctx.globalAlpha=t1.alpha*(.9)**j;ctx.moveTo(t1.x+canvas.width/2,t1.y+canvas.height/2);ctx.lineTo(t2.x+canvas.width/2,t2.y+canvas.height/2);ctx.stroke();}
}
function animate(){if(bodies.length<=1)init();updateBodies();draw();requestAnimationFrame(animate);}
init();animate();
</script>
<div style="position:absolute;top:10px;left:10px;color:white;">Active bodies: <span id=bcount>6</span></div>
</body></html>
```
qwq:32bSTATLIM
Show code
<think>

Okay, I need to create a self-contained HTML file that animates a gravitational n-body system. The user specified one central star and six orbiting bodies. The key requirements are using real Newtonian forces between every pair each frame, with a small time step to keep integration stable. The animation should use requestAnimationFrame, have fading trails, and display the number of bodies remaining. Also, no external libraries, and the final code must be under 7000 characters.

First, I'll outline the structure. The HTML needs a canvas and some JavaScript inside a script tag. The canvas will handle rendering, and the JavaScript will manage the physics and animation.

Starting with the setup: the canvas element. I'll set its size, maybe 600x600 pixels. Then the JavaScript part. 

For the bodies, I need an array. The central star will be the first element with a high mass. The other six have varying masses. Each body should have position, velocity, mass, and a color based on mass. Maybe more massive bodies are redder, smaller ones bluer? Or some other color variation. Size could be proportional to mass.

The gravitational force between bodies: For each pair, compute the vector between them, distance squared, and apply the force. The acceleration is F/m. I'll loop through all pairs, calculate the force, add to each body's acceleration.

Integration: Use a small time step. The user mentioned substeps might be needed. But for simplicity, maybe just take a small dt like 0.01 or 0.005. Euler method? But that might not be stable. Maybe the Verlet method? Hmm, Euler can work if the time step is small enough. Alternatively, Runge-Kutta? But that might complicate. Let's start with Euler.

Wait, the problem states "small enough time step or substeps". So perhaps integrate in substeps per frame? Like, each frame's physics is done in multiple small steps. But that might be computationally intensive. Alternatively, use a small base dt. Let me think.

The animation loop will use requestAnimationFrame. Each frame, calculate forces, update velocities and positions. The trails can be done by storing the previous positions and drawing lines between them, with a decay factor so older points fade out.

For the trails: each body has an array of previous positions/points. Each frame, add the current position, then when drawing, loop through the points and draw lines with decreasing opacity. Need to manage the array length so it doesn't grow indefinitely. Maybe cap it at 50 points or something, and shift out the oldest as needed.

The counting of remaining bodies: need to check if each body is still within the canvas or collided with the star. Collisions: if the distance between two bodies is less than the sum of their radii (since masses can vary, size is proportional to mass). The star is at the center. The orbiting bodies might be ejected if they go beyond the canvas bounds.

Starting positions: the star is at (300, 300). The other bodies need to be placed with initial velocities that put them in orbit. Since it's n-body, they have to consider the star's gravity and each other's. The initial velocities should be tangential to approximate circular orbits. But since it's n-body, their orbits will perturb each other.

Masses: Star has a large mass, say 1e5 or something. The orbiting bodies have varying masses, maybe between 1 and 100. That way, their gravitational effects on each other are noticeable but not too disruptive to the system.

Color and size: size could be Math.sqrt(mass) multiplied by a factor. Color could be based on mass, like red for higher masses and blue for lower, using HSL where hue depends on mass.

Now, putting this into code.

First, variables: canvas, ctx. The bodies array. Trails array for each body.

Constants: G (gravitational constant), which in simulation units should be adjusted for the scale. Let's see. Let's set G to 1e-4, but need to test.

Time step: dt. Let's start with 0.01.

Function to compute forces:

function computeForces() {
  for (let i = 0; i < bodies.length; i++) {
    const a = bodies[i];
    a.ax = 0;
    a.ay = 0;
    for (let j = 0; j < bodies.length; j++) {
      if (i === j) continue;
      const b = bodies[j];
      const dx = b.x - a.x;
      const dy = b.y - a.y;
      const distSqr = dx*dx + dy*dy;
      if (distSqr < 1e-6) continue; // prevent division by zero
      const force = G * a.mass * b.mass / distSqr;
      const invDist = 1 / Math.sqrt(distSqr);
      const ax = dx * invDist;
      const ay = dy * invDist;
      a.ax += force * ax / a.mass;
      a.ay += force * ay / a.mass;
    }
  }
}

Wait, no. The force on a from b is (dx, dy) direction. The magnitude is G * m_a * m_b / dist^2. The acceleration of a is force / mass of a. So the components are:

a.ax += ( (G * b.mass / dist^3) * dx );
Similarly for ay.

Wait, let me re-derive:

F = G * (m_a * m_b) / r^2 * direction (dx, dy).

Acceleration of a due to b: F/m_a = (G m_b / r^3) * (dx, dy)

So:

a.ax += ( (G * b.mass * dx ) / (distSqr * Math.sqrt(distSqr)) )) ?

Wait, r^3 is (distSqr)^(3/2). So yes, dx * dx + dy * dy is distSqr. So invDistCube = 1/(distSqr * Math.sqrt(distSqr))? Or better, invDistCube = 1/(dist * dist * dist) where dist is sqrt(dx^2 + dy^2). So perhaps better to compute dist = Math.sqrt(dx*dx + dy*dy), then invDistCube = G * b.mass / (dist * dist * dist).

Wait let me re-calculate:

The acceleration vector for a from b is ( (G * b.mass / r^3) * dx, (G * b.mass / r^3 )* dy )

where r is the distance between a and b.

So:

dist = Math.sqrt(dx*dx + dy*dy)

if dist is too small (to prevent singularity), skip.

Then a.ax += G * b.mass * dx / (dist**3)

Similarly for ay.

This is more accurate.

So in code:

let dist = Math.hypot(dx, dy);

if (dist < 1e-3) continue; // avoid singularity

const forceFactor = G * b.mass / (dist * dist * dist);

a.ax += dx * forceFactor;

a.ay += dy * forceFactor;

Wait, no, because G is already part of the constant. Let me see:

Wait the standard formula is:

F = G * m1 * m2 / r^2 * direction (dx, dy)

So acceleration of a due to b is F/m1 = (G * m2 / r^3) * (dx, dy)

Hence:

a.ax += (G * b.mass / (dist**3)) * dx;

a.ay += (G * b.mass / (dist**3)) * dy;

Yes, that's right.

So in code:

dist = Math.sqrt(dx*dx + dy*dy);

if (dist < 1e-3) continue;

const invDist3 = G / (dist ** 3);

a.ax += invDist3 * dx * b.mass;

a.ay += invDist3 * dy * b.mass;

Wait, that way, the acceleration is (G * b.mass / (dist^3)) * dx and dy.

Yes.

So this is how the force is calculated.

Now, for the integration step.

After computing forces, we need to update velocity and position.

Using Euler method:

for each body:

a.vx += a.ax * dt;

a.vy += a.ay * dt;

a.x += a.vx * dt;

a.y += a.vy * dt;

But Euler is first-order, which can be unstable for large dt. So perhaps using a smaller dt? Let's see.

Alternatively, use a higher-order method. But for simplicity, let's proceed with Euler and choose a small enough dt. Let's try dt = 0.01. Maybe adjust later.

Now, the initial conditions.

The central star should be at center (300, 300), with a large mass. Let's say 1e5. The orbiting bodies need to start at some positions with appropriate velocities to orbit the star.

Let me think: for a circular orbit, v = sqrt(G * M / r). But in our units, since G is 1e-4, we need to set parameters so that this holds.

Alternatively, set the star's mass as 1e5, G as 1e-4. Suppose a body at 100 pixels from the center (distance) has velocity v such that v^2 = (G * M)/r.

Let me see:

G = 1e-4, M = 1e5, r = 100.

v^2 = (1e-4 * 1e5)/100 = (10)/100 = 0.1 => v = ~0.316.

So initial velocity for a circular orbit would be around 0.3.

But since our time step is 0.01, over each frame, the position would be updated by 0.01 * velocity.

Hmm.

Alternatively, maybe set G to 1 for simplicity? Let me recalculate.

Wait, the choice of constants can be adjusted. Maybe better to set G=1, and scale masses and initial positions accordingly.

Alternatively, to make the trails visible over time, perhaps set dt to be small, say 0.005, and then have the animation loop call computeForces multiple times each frame? That might be needed for stability.

Alternatively, use a Runge-Kutta 4 method for better accuracy. But that's more complex.

But since the user allows substeps, maybe do multiple steps per frame. Let's think: each requestAnimationFrame (which is about 60 times per second, so roughly 0.016 seconds between frames). Suppose we do 50 substeps each frame with a very small dt, say dt = 0.001. So total time per frame: 0.05 seconds, but this might be excessive and slow down the animation.

Alternatively, perhaps using a fixed dt, and just making sure it's small enough.

Alternatively, given time constraints, proceed with Euler and adjust parameters to keep it stable enough.

Now, the initial positions and velocities.

Star is at (300,300). The other bodies should be placed in a circle around it with some random angles and distances. Let's say initial positions are between 100 and 200 pixels from the center. Random angles between 0 and 2pi.

Velocities: initial velocity vectors need to be tangential to the orbit. So direction is perpendicular to the position vector.

So for each body:

distance from center is r = random between 100-200.

angle theta = random(0, 2pi).

x = 300 + r * cos(theta);

y = 300 + r * sin(theta);

Then velocity needs to be perpendicular (for circular orbit) with magnitude v = sqrt(G*M/r) where M is the star's mass.

But in our case, the star's mass is part of the bodies, so the total mass at the center is bodies[0].mass.

Wait, but the initial velocity should be based on the star's mass only, since the other bodies are initially far apart. But since they are orbiting, maybe starting with their velocities as per the star's gravity.

Alternatively, given that the system is n-body, their initial velocities can be based on the star's contribution, and then they can perturb each other.

So for each orbiting body's initial velocity:

vx = -v * sin(theta); // to get tangential direction

vy = v * cos(theta);

where v = sqrt( (G * M_star) / r );

Since G is 1e-4, M_star is 1e5, so:

v = sqrt( (1e-4 * 1e5 ) / r ) = sqrt(10 / r )

So for r=100, v= sqrt(0.1) ~0.316, for r=200, sqrt(0.05) ~0.224.

Hence, initial velocities are in that range.

So, code-wise:

const G = 1e-4;

const starMass = 1e5;

bodies array will have the star first, then six others.

Initializing the bodies:

function init() {

bodies = [];

// Star

bodies.push({

    x: 300, y: 300,

    vx:0, vy:0,

    mass: starMass,

    radius: 10,

    color: 'yellow'

});

for (let i=0; i<6; i++) {

    const theta = Math.random() * Math.PI*2;

    let r = 100 + Math.random()*100;

    const M = Math.random() * 90 + 10; // mass between 10 and 100

    const v = Math.sqrt( (G * starMass) / r );

    const vx = -v * Math.sin(theta);

    const vy = v * Math.cos(theta);

    bodies.push( {

        x: 300 + r * Math.cos(theta),

        y: 300 + r * Math.sin(theta),

        vx, vy,

        mass: M,

        radius: Math.sqrt(M)*1.5, // or something to make it visible

        color: 'hsl('+ (360 - 240*M/100) + ',50%,50%)'

    });

}

}

Wait, color calculation: maybe using HSL where higher mass is more red (0 degrees), lower mass more blue (240 degrees). So if M ranges 10-100, then:

hue = 240 - (240 * (M -10)/90 )

Wait, let's see:

For M=10: 240 - (240*(0)) = 240 (blue)

For M=100: 240 - (240*(90/90)) = 0 (red)

Yes, so the formula would be:

hue = 240 - (240 * (M-10)/90 )

Which can be rewritten as:

hue = 240 * (100 - M)/90 ?

Wait, perhaps better to compute:

var hue = 240 * (1 - (M -10)/90);

Wait, let me see:

At M=10: (M-10)/90 =0 → 240*(1-0)=240.

At M=100: (90)/90 → 1 → 240*(0) =0.

Yes, so:

hue = 240 * (1 - (M -10)/90 )

Yes. So in code, (240 * (1 - (M -10)/90 )) → 240*( (100 - M)/90 )

= 240*( (100 - M)/90 ) → 240*(1 - (M-10)/90 )

So in code:

color: 'hsl('+ ( 240 * ( (100 - M)/90 )) + ',50%,50%)'

Alternatively, to simplify:

hue = 240 * (100 - M) / 90

Wait, 240*( (100 - M)/90 ) → which is the same as above.

So, in code:

const hue = 240 * (100 - M)/90;

But in JS:

const hue = 240 * (100 - M)/(90);

Yes.

So that's the color.

Radius: proportional to sqrt(mass) to make it visually reasonable. Let's say radius = Math.sqrt(M)*2 or so. For M=10 → ~4.47, M=100 → 20 (since sqrt(100)=10, times 2 is 20).

But the star is radius 10. So maybe the star should have a larger radius. Let's set the star's radius as 15.

Wait, in code, the star's radius is 10. Maybe make it 20?

Yes.

Also, the trails: Each body has a trail array, which holds past positions. Each frame adds the current position (x,y) to the trail array, and then when drawing, loops through the trail and draws lines from previous points to current, with decreasing opacity. The trail should gradually fade out, so each point's opacity can be adjusted based on its age.

Alternatively, for each point in the trail, the opacity decreases over time. The trails could be stored as an array for each body, with each element being a point (x,y, time). But to keep it simple, maybe the trail array stores points and each frame, when drawing, the opacity is based on the length of the trail. For example, the first point is 100% opacity, the second 90%, etc., but this requires passing an opacity value with each point. Alternatively, just limit the trail length and let older points fade as the array grows.

Alternatively, each point in the trail can be drawn with a line from the previous point to current, with decreasing alpha.

Alternatively, use a gradient or decay factor each step. This might be complicated.

Perhaps the simplest way is to have each body's trail array store the past positions, and when drawing the trail, for each point in the trail, draw a small circle or a line segment with decreasing opacity as it gets older.

But to manage this, perhaps each trail entry could store the x,y and a timestamp, and then calculate the opacity based on how old the point is. However, in JS, time is measured in ms, so each frame, we can track the current time and compute the age.

Alternatively, for simplicity, just maintain a trail array of, say, 50 points, and each point's opacity decreases as it gets older. For example, the oldest point is 25% opacity, the newest 100%.

Alternatively, since the trails are just for visualization, maybe when drawing the trail, for each body:

ctx.beginPath();

for (let j=0; j<trail.length-1; j++) {

    const pt1 = trail[j];

    const pt2 = trail[j+1];

    ctx.moveTo(pt1.x, pt1.y);

    ctx.lineTo(pt2.x, pt2.y);

}

ctx.strokeStyle = this.color;

ctx.globalAlpha = 0.5; // trail opacity

ctx.stroke();

ctx.globalAlpha = 1; // reset

But to have the trail fade over time, perhaps each point's contribution to the trail decays over steps.

Alternatively, just let the trails be a fixed length, say 50 points, and each frame adds the current position and shifts out old ones.

Wait, maybe each body has a trail array that gets pushed the current (x,y), and then when drawing, we iterate through the trail array and draw lines between consecutive points with decreasing opacity. For example, each segment's opacity is based on its age.

Alternatively, just draw lines with low alpha so that the trail gradually fades.

Alternatively, here's a possible approach:

Each body has a trail array, which holds objects {x,y}.

Each frame:

- After computing new positions, add current (x,y) to the trail array.

- If the trail array exceeds some maximum length (e.g., 100), remove the oldest elements.

- When drawing the trail, draw a line from each point in the trail array with a low opacity, so that older points are overwritten and gradually fade.

Alternatively, use a trail length of 100 points and draw lines between each point with a small stroke and alpha.

Perhaps:

When drawing each body's trail:

ctx.strokeStyle = body.color;

ctx.globalAlpha = 0.2;

ctx.lineWidth = 1;

ctx.beginPath();

for (let i=0; i<body.trail.length; i++) {

   const pt = body.trail[i];

   ctx.lineTo(pt.x, pt.y);

}

ctx.stroke();

ctx.globalAlpha = 1;

But this would draw a continuous line from the first point to the last, which might not show the fading. Alternatively, use a loop through the trail array and draw segments with decreasing alpha.

Alternatively, use a for loop that draws lines from each point to the next with a color that gets more transparent.

Alternatively, this might be getting too complex. Let's proceed with a simple approach where the trails are stored as arrays and drawn with low alpha.

Now, putting this together.

The main loop: each frame, compute forces, update velocities and positions (but since it's Euler, we need to do every frame).

Wait, no: in the code, to compute the positions, we first compute the net force on each body, then update their velocities and positions.

Wait, in the Euler method:

For each body (except the star, perhaps?), compute acceleration (F/m):

But to compute forces between all bodies.

Wait, the force between two bodies is G * m1 m2 / r^2 directed along the line connecting them.

But since force on body A from B is F = G * m_A m_B / r^2 * direction vector, then acceleration of A is F/m_A = G*m_B / r^2 * direction.

Hence, for each body, their acceleration is sum over all other bodies of (G * other.mass / r^2 * direction).

Hence, in code:

function computeForces() {

    for (let i=0; i < bodies.length; i++) {

        const body = bodies[i];

        let ax = 0, ay = 0;

        for (let j=0; j < bodies.length; j++) {

            if (i === j) continue;

            const other = bodies[j];

            const dx = other.x - body.x;

            const dy = other.y - body.y;

            const dist = Math.hypot(dx, dy);

            if (dist < 1) continue; // prevent division by zero

            const force = G * other.mass / (dist * dist);

            ax += force * dx / dist; // acceleration component x

            ay += force * dy / dist; // acceleration component y

        }

        body.vx += ax * dt;

        body.vy += ay * dt;

        // Wait no, Euler method: the acceleration is ax, so velocity += ax * dt

        // Then position += velocity * dt

    }

    // Now update positions

    for (let i=0; i < bodies.length; i++) {

        const body = bodies[i];

        body.x += body.vx * dt;

        body.y += body.vy * dt;

    }

}

Wait, but the order is important: first compute all the forces and compute new velocities, then compute new positions based on the new velocities? Or based on previous velocities?

Actually, Euler method is:

v_new = v_old + a * dt

x_new = x_old + v_old * dt

Wait, so first compute acceleration (a) for each body from the current positions.

Then compute the new velocity as v_old + a * dt.

Then compute the new position using the old velocity?

No, actually, the standard Euler method is:

v_{n+1} = v_n + a_n * dt

x_{n+1} = x_n + v_n * dt

Where a_n is acceleration computed from x_n.

So in code:

for each body:

compute a based on current positions.

Then update velocities with v += a * dt.

Then update positions with x += v_old * dt.

Wait, but the new velocity is not used for the position update. So the code should be:

body.vx = body.vx + ax * dt;

body.vy = body.vy + ay * dt;

then,

body.x += body.vx_prev * dt;

body.y += body.vy_prev * dt;

But in code, since we can't save the previous velocity, perhaps it's better to first compute all the acceleration and store them temporarily, then update velocities and positions.

Alternatively, separate loops for acceleration and velocity/position:

First loop through all bodies to compute acceleration (store in arrays or temporary variables).

Second loop to update velocities using the computed accelerations.

Third loop to update positions using the previous velocities (before updating).

Alternatively, perhaps code:

const dt = 0.01; // or whatever.

function updatePhysics() {

    let ax, ay;

    // First compute all accelerations and store in temporary arrays

    const a = [];

    for (let i=0; i < bodies.length; i++) {

        const body = bodies[i];

        ax = 0; ay = 0;

        for (let j=0; j < bodies.length; j++) {

            if (i === j) continue;

            const other = bodies[j];

            const dx = other.x - body.x;

            const dy = other.y - body.y;

            const dist = Math.hypot(dx, dy);

            if (dist < 1e-3) continue; // prevent nan

            const force = G * other.mass / (dist*dist);

            ax += force * dx / dist;

            ay += force * dy / dist;

        }

        a[i] = { ax, ay };

    }

    // Now update velocities and positions

    for (let i=0; i < bodies.length; i++) {

        const body = bodies[i];

        const ai = a[i];

        body.vx += ai.ax * dt;

        body.vy += ai.ay * dt;

        // Update positions using previous vx, vy ?

        // Wait no, the old velocity is used for displacement.

        body.x += body.vx * dt; // Wait, no! Because the acceleration is from the current positions.

        // Wait, according to Euler:

        // v_{n+1} = v_n + a_n * dt

        // x_{n+1} = x_n + v_n * dt

        // So the velocity is updated now, but the position uses the previous velocity.

        // So need to store the previous velocity?

        Hmm, this is a problem.

        To correctly compute x_{n+1} = x_n + v_n * dt, but v_n is before the acceleration step.

        So to compute the velocity first, then the displacement.

        Wait, no: the order is that the acceleration is computed from the current positions, then the velocity is updated with the acceleration. Then the positions are updated with the previous velocity.

        So to do this correctly, you need to first compute the acceleration based on current positions, then compute velocities based on acceleration, then compute positions based on previous velocities.

        But since we are using the same variables (body.vx is being updated), the position update will use the new velocity unless we store the old velocity.

        So an alternative approach is:

        // First, compute the new velocity by adding acceleration * dt to current velocity.

        // But to compute the position, we need the previous velocity before the velocity is updated.

        // So perhaps we should compute the acceleration, store the new velocities, then update the velocities and compute the positions based on the previous velocities.

        Alternatively, here's the code:

        Save the current velocity before updating.

        let oldVx = body.vx;

        let oldVy = body.vy;

        body.vx += ai.ax * dt;

        body.vy += ai.ay * dt;

        body.x += oldVx * dt;

        body.y += oldVy * dt;

        That way, the position is based on the previous velocity.

    }

}

Hmm, that seems better.

So in code, in the second loop:

body.x += body.vx * dt; // no, because vx is updated in this step.

Wait, so to prevent using the new velocity for the position update, we need to store the old velocity first.

Thus:

In the second loop:

for each body:

    const oldVx = body.vx;

    const oldVy = body.vy;

    body.vx += a[i].ax * dt;

    body.vy += a[i].ay * dt;

    body.x += oldVx * dt;

    body.y += oldVy * dt;

Yes, that works.

So the code would be structured as follows.

Now, considering the star (the central body), should it be affected by the other bodies? In the case of a simulation of planets around the sun, the sun's mass is so large that its movement is negligible, so perhaps we can fix its position and velocity. Alternatively, if we want an accurate simulation, we should let all bodies interact. So in the code above, all bodies are treated equally, so the central body will also move, but if it's much more massive, its motion will be minimal.

So, in the initialization, set the central body as having a large mass, and the planets with small masses.

Now, putting all this together.

The complete code would involve:

- Canvas setup.

- Initialization of bodies (central star, planets).

- Animation loop: each frame, compute physics, update positions, draw.

- Trail handling for each body.

Now, the user's request is to "create" this, so they probably want the code.

Now, I need to write this step by step.

First, setting up the canvas and context.

Then, defining the bodies, each with x, y, vx, vy, mass, and trail array.

Then, the update function that computes forces and updates positions/velocities.

Then, the draw function that draws the bodies and trails.

Handling the trails: each body has a trail array, which is pushed new position each frame, and limited in length.

The main loop uses requestAnimationFrame.

Now, let's proceed to code.

First, the HTML:

<canvas id="simCanvas"></canvas>

Then, the JavaScript:

const canvas = document.getElementById('simCanvas');

const ctx = canvas.getContext('2d');

// Canvas size

canvas.width = window.innerWidth;

canvas.height = window.innerHeight;

// Constants

const G = 1; // Gravitational constant. Adjust as needed.

const dt = 0.01; // Time step.

// Body class

class Body {

    constructor(x, y, mass, color) {

        this.x = x;

        this.y = y;

        this.vx = 0;

        this.vy = 0;

        this.mass = mass;

        this.color = color;

        this.trail = []; // Array to store trail points

    }

    updateTrail() {

        // Add current position to trail

        this.trail.push({x: this.x, y: this.y});

        // Limit trail length to 200 points

        if (this.trail.length > 200) {

            this.trail.shift(); // Remove oldest point

        }

    }

}

// Initialize bodies

const bodies = [];

// Central star

bodies.push(new Body(canvas.width/2, canvas.height/2, 1000, 'yellow'));

// Add planets

// For example, add 3 planets with different orbits

const numPlanets = 3;

for (let i=0; i < numPlanets; i++) {

    const angle = (Math.PI * 2) * i / numPlanets;

    const radius = 150 + 50*i; // Increasing radius for each planet

    const mass = 1; // Small mass compared to star

    const x = canvas.width/2 + radius * Math.cos(angle);

    const y = canvas.height/2 + radius * Math.sin(angle);

    // Initial velocity to orbit (perpendicular to position vector)

    const speed = Math.sqrt(G * bodies[0].mass / radius); // Circular orbit velocity

    const vx = -speed * Math.sin(angle); // Tangential direction

    const vy = speed * Math.cos(angle);

    bodies.push(new Body(x, y, mass, `hsl(${360/numPlanets *i}, 100%, 50%)`));

}

// Update function

function update() {

    // Compute acceleration for all bodies

    const accelerations = [];

    for (let i=0; i < bodies.length; i++) {

        const body = bodies[i];

        let ax = 0, ay = 0;

        for (let j=0; j < bodies.length; j++) {

            if (i === j) continue;

            const other = bodies[j];

            const dx = other.x - body.x;

            const dy = other.y - body.y;

            const dist = Math.hypot(dx, dy);

            if (dist < 1e-3) continue; // Prevent division by zero

            const force = (G * other.mass) / (dist * dist);

            ax += (dx / dist) * force;

            ay += (dy / dist) * force;

        }

        accelerations[i] = { ax, ay };

    }

    // Update velocities and positions

    for (let i=0; i < bodies.length; i++) {

        const body = bodies[i];

        const acc = accelerations[i];

        const oldVx = body.vx;

        const oldVy = body.vy;

        body.vx += acc.ax * dt;

        body.vy += acc.ay * dt;

        body.x += oldVx * dt;

        body.y += oldVy * dt;

        // Update trail

        body.updateTrail();

    }

}

// Draw function

function draw() {

    ctx.clearRect(0, 0, canvas.width, canvas.height);

    // Draw each body and their trails

    ctx.fillStyle = 'white';

    ctx.strokeStyle = 'white';

    bodies.forEach(body => {

        // Draw trail

        ctx.beginPath();

        ctx.lineWidth = 1;

        ctx.strokeStyle = body.color;

        ctx.globalAlpha = 0.5; // Trail transparency

        for (let i=0; i < body.trail.length; i++) {

            const pt = body.trail[i];

            ctx.lineTo(pt.x, pt.y);

        }

        ctx.stroke();

        ctx.globalAlpha = 1;

        // Draw the body as a circle

        ctx.beginPath();

        ctx.arc(body.x, body.y, Math.sqrt(body.mass), 0, Math.PI*2);

        ctx.fillStyle = body.color;

        ctx.fill();

    });

}

// Animation loop

function animate() {

    update();

    draw();

    requestAnimationFrame(animate);

}

animate();

// Adjust canvas on window resize

window.addEventListener('resize', () => {

    canvas.width = window.innerWidth;

    canvas.height = window.innerHeight;

    // Reset body positions? Maybe not, but could.

    // Alternatively, recalculate initial positions based on new canvas size.

    // But for simplicity, not handled here.

});

Now, some notes:

- The gravitational constant G is set to 1, but might need tuning for the simulation to look nice. The initial velocity calculation uses the circular orbit formula: v = sqrt(G*M/r), where M is the central mass.

- The planets are initialized with positions on a circle, and velocities tangential to their orbits, so they should start in circular orbits. The central star has a large mass (1000) so its movement is negligible.

- The trail is stored as an array of objects, each with x,y. Each frame, the current position is added to the trail, and the array is limited to 200 elements to prevent it from getting too long.

- The draw function first clears the canvas, then draws each body's trail with a lower alpha, then draws the body as a filled circle whose radius is proportional to the square root of the mass (so that area scales with mass, but visually it's sqrt(mass) radius).

- The animation loop calls update() and draw() each frame.

Potential issues:

- The trails might become too long over time, but with the shift() method, they stay limited to 200 points.

- The canvas resizing could cause the bodies to be in the wrong position if the canvas size changes, but the code currently doesn't handle that.

- The gravitational constant and time step (dt) may need adjustment for proper scaling. For instance, with G=1 and dt=0.01, the orbits might be too fast or too slow.

- The bodies' masses: the central star has mass 1000, planets have mass 1. This should mean the star's acceleration is small, but if the dt is too large, numerical errors might accumulate.

- The initial velocity uses the circular orbit formula; if the mass of the star is changed, that might need adjustment.

Possible improvements:

- Add a scaling factor to adjust the gravitational constant and time step for better visualization.

- Implement a better way to handle canvas resizing, perhaps reinitializing the bodies' positions based on new canvas dimensions.

- Add controls to toggle trails or adjust parameters.

- Handle edge cases where two bodies get too close (distance near zero), though in this setup with the massive central body and small planets, that's less likely.

- Optimize the force calculations by not rechecking every pair twice (but for a few bodies, it's negligible).

Overall, this code should create a simulation of a star with several orbiting planets, with trails showing their paths. The user can adjust parameters like G, dt, mass, initial positions, and colors to customize the simulation.
No usable answer reached the harness (ERR). Counted as a did-not-finish.
Outside the Brain cluster — same task, same character budget, collected through their own tooling (response times not comparable)
Claude Opus 5frontier referenceOK
Show code
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>N-Body Gravity</title><style>
html,body{margin:0;height:100%;background:#05060c;overflow:hidden}
canvas{display:block}
#h{position:fixed;left:14px;top:12px;font:12px/1.6 ui-monospace,SFMono-Regular,Menlo,monospace;color:#93aacd;letter-spacing:.14em;text-shadow:0 0 10px #000;pointer-events:none}
#h b{color:#ffe6a3;font-weight:600}
</style></head><body>
<canvas id="c"></canvas><div id="h"></div>
<script>
var c=document.getElementById('c'),x=c.getContext('2d'),hud=document.getElementById('h');
var W,H,B,t,G=1,SOFT=7,SM=1600;
function rs(){W=c.width=innerWidth;H=c.height=innerHeight}
function col(m){var k=Math.min(1,m/14);return'hsl('+(206-152*k)+',88%,'+(74-20*k)+'%)'}
function init(){
 rs();t=0;
 B=[{x:W/2,y:H/2,vx:0,vy:0,m:SM,r:10,c:'#fff3c4'}];
 var R=Math.min(W,H)*0.42,i,a,d,m,v,s;
 for(i=0;i<6;i++){
  a=Math.random()*6.2832;
  d=68+i*(R-68)/5.4+Math.random()*20;
  m=1+Math.random()*13;
  v=Math.sqrt(G*SM/d)*(0.93+Math.random()*0.12);
  s=i%3?1:-1;
  B.push({x:W/2+Math.cos(a)*d,y:H/2+Math.sin(a)*d,vx:-Math.sin(a)*v*s,vy:Math.cos(a)*v*s,m:m,r:2+Math.pow(m,0.44)*1.8,c:col(m)});
 }
 var px=0,py=0;for(i=0;i<B.length;i++){px+=B[i].m*B[i].vx;py+=B[i].m*B[i].vy}
 B[0].vx-=px/B[0].m;B[0].vy-=py/B[0].m;
 x.fillStyle='#05060c';x.fillRect(0,0,W,H);
}
function step(dt){
 var i,j,a,b,dx,dy,d2,d,f,n=B.length;
 for(i=0;i<n;i++){B[i].ax=0;B[i].ay=0}
 for(i=0;i<n;i++)for(j=i+1;j<n;j++){
  a=B[i];b=B[j];dx=b.x-a.x;dy=b.y-a.y;
  d2=dx*dx+dy*dy+SOFT*SOFT;d=Math.sqrt(d2);f=G/(d2*d);
  a.ax+=f*b.m*dx;a.ay+=f*b.m*dy;
  b.ax-=f*a.m*dx;b.ay-=f*a.m*dy;
 }
 for(i=0;i<n;i++){b=B[i];b.vx+=b.ax*dt;b.vy+=b.ay*dt;b.x+=b.vx*dt;b.y+=b.vy*dt}
}
function frame(){
 requestAnimationFrame(frame);
 for(var k=0;k<8;k++)step(0.06);
 t++;
 var S=B[0],i,b,dx,dy,d,vis=0;
 for(i=B.length-1;i>0;i--){
  b=B[i];dx=b.x-S.x;dy=b.y-S.y;d=Math.sqrt(dx*dx+dy*dy);
  if(d<S.r+b.r+3){S.m+=b.m;S.vx+=b.m*b.vx/S.m;S.vy+=b.m*b.vy/S.m;B.splice(i,1);}
  else if(b.x<-W||b.x>2*W||b.y<-H||b.y>2*H)B.splice(i,1);
 }
 x.fillStyle='rgba(5,6,12,0.085)';x.fillRect(0,0,W,H);
 x.globalCompositeOperation='lighter';
 for(i=1;i<B.length;i++){
  b=B[i];
  if(b.x>0&&b.x<W&&b.y>0&&b.y<H)vis++;
  var g=x.createRadialGradient(b.x,b.y,0,b.x,b.y,b.r*3.4);
  g.addColorStop(0,b.c);g.addColorStop(0.35,b.c);g.addColorStop(1,'rgba(0,0,0,0)');
  x.globalAlpha=0.5;x.fillStyle=g;x.beginPath();x.arc(b.x,b.y,b.r*3.4,0,6.2832);x.fill();
  x.globalAlpha=1;x.fillStyle=b.c;x.beginPath();x.arc(b.x,b.y,b.r,0,6.2832);x.fill();
 }
 var q=x.createRadialGradient(S.x,S.y,0,S.x,S.y,38);
 q.addColorStop(0,'rgba(255,250,226,0.98)');q.addColorStop(0.2,'rgba(255,214,118,0.55)');
 q.addColorStop(0.5,'rgba(255,150,50,0.16)');q.addColorStop(1,'rgba(0,0,0,0)');
 x.fillStyle=q;x.beginPath();x.arc(S.x,S.y,38,0,6.2832);x.fill();
 x.globalCompositeOperation='source-over';
 hud.innerHTML='N-BODY GRAVITY &nbsp; BODIES IN FIELD <b>'+vis+' / 6</b> &nbsp; ELAPSED <b>'+(t/60).toFixed(1)+'s</b>';
 if(B.length<3||t>5400)init();
}
addEventListener('resize',init);
init();frame();
</script></body></html>
GPT-5.5 (Codex)frontier referenceERRBLANKMINIFIED
Show code
<!doctype html><html><head><meta charset=utf-8><title>nbody</title><style>html,body{margin:0;height:100%;overflow:hidden;background:#05070b;color:#dfe7ff;font:14px system-ui}canvas{display:block}#n{position:fixed;left:14px;top:12px;text-shadow:0 1px 6px #000}</style></head><body><canvas id=c></canvas><div id=n></div><script>
let C=c.getContext("2d"),W,H,B=[],G=.42,done=0,last=0,cols=["#ffcf6b","#7dd3fc","#a7f3d0","#fda4af","#c4b5fd","#fde68a","#93c5fd"];
onresize=()=>{W=c.width=innerWidth;H=c.height=innerHeight};onresize();
function R(a,b){return a+Math.random()*(b-a)}
function init(){B=[];done=0;let S={x:W/2+R(-20,20),y:H/2+R(-20,20),vx:0,vy:0,m:26000,r:16,c:"#fff1a8",live:1,star:1};B.push(S);for(let i=0;i<6;i++){let a=i*Math.PI*2/6+R(-.18,.18),d=Math.min(W,H)*R(.14,.38),m:R(35,170),v=Math.sqrt(G*S.m/d)*R(.9,1.08),x=S.x+Math.cos(a)*d,y=S.y+Math.sin(a)*d,tx=-Math.sin(a),ty=Math.cos(a);B.push({x,y,vx:tx*v+R(-.08,.08),vy:ty*v+R(-.08,.08),m,r:3+Math.sqrt(m)*.45,c:cols[i+1],live:1})}C.fillStyle="#05070b";C.fillRect(0,0,W,H)}
function step(){let ax=B.map(_=>0),ay=B.map(_=>0);for(let i=0;i<B.length;i++)for(let j=i+1;j<B.length;j++){let A=B[i],D=B[j];if(!A.live||!D.live)continue;let dx=D.x-A.x,dy=D.y-A.y,q=dx*dx+dy*dy+36,s=Math.sqrt(q),f=G/(q*s);ax[i]+=dx*D.m*f;ay[i]+=dy*D.m*f;ax[j]-=dx*A.m*f;ay[j]-=dy*A.m*f}for(let i=0;i<B.length;i++){let A=B[i];if(!A.live||A.star)continue;A.vx+=ax[i]*.42;A.vy+=ay[i]*.42;A.x+=A.vx*.42;A.y+=A.vy*.42;let S=B[0],dx=A.x-S.x,dy=A.y-S.y;if(dx*dx+dy*dy<(S.r+A.r)*(S.r+A.r)||A.x<-80||A.x>W+80||A.y<-80||A.y>H+80)A.live=0}}
function draw(){C.globalCompositeOperation="source-over";C.fillStyle="rgba(5,7,11,.12)";C.fillRect(0,0,W,H);for(let A of B){if(!A.live)continue;let g=C.createRadialGradient(A.x,A.y,0,A.x,A.y,A.r*(A.star?3:2));g.addColorStop(0,A.c);g.addColorStop(1,"#0000");C.fillStyle=g;C.beginPath();C.arc(A.x,A.y,A.r*(A.star?3:2),0,7);C.fill();C.fillStyle=A.c;C.beginPath();C.arc(A.x,A.y,A.r,0,7);C.fill()}let k=B.slice(1).filter(a=>a.live).length;n.textContent=k+" bodies";if(!k&&!done){done=1;setTimeout(init,900)}}
function loop(t){requestAnimationFrame(loop);let s=Math.min(5,Math.max(1,((t-last)||16)/16|0));last=t;for(let i=0;i<s*5;i++)step();draw()}
init();requestAnimationFrame(loop);
</script></body></html>
Claude Sonnet 5frontier referenceOK
Show code
<!DOCTYPE html><html><head><meta charset="utf8"><title>N-Body Gravity</title><style>html,body{margin:0;overflow:hidden;background:#000}#hud{position:fixed;top:8px;left:10px;color:#eee;font:14px monospace;text-shadow:0 0 4px #000}</style></head><body><canvas id="cv"></canvas><div id="hud"></div><script>
const cv=document.getElementById('cv'),ctx=cv.getContext('2d'),hud=document.getElementById('hud');
let W=cv.width=innerWidth,H=cv.height=innerHeight;
addEventListener('resize',()=>{W=cv.width=innerWidth;H=cv.height=innerHeight;});
const G=1;
let bodies=[],restarting=false;
function init(){
bodies=[];
const cx=W/2,cy=H/2,starM=9000+Math.random()*2000;
bodies.push({x:cx,y:cy,vx:0,vy:0,m:starM,r:15,c:'#ffdd55',alive:1});
for(let i=0;i<6;i++){
const m=8+Math.random()*35;
const ang=Math.random()*Math.PI*2;
const dist=55+Math.random()*Math.min(W,H)*0.35;
const x=cx+Math.cos(ang)*dist,y=cy+Math.sin(ang)*dist;
const spd=Math.sqrt(G*starM/dist)*(0.82+Math.random()*0.36);
const vx=-Math.sin(ang)*spd,vy=Math.cos(ang)*spd;
const hue=Math.floor(260-m*5);
bodies.push({x,y,vx,vy,m,r:2.5+Math.sqrt(m),c:`hsl(${hue},85%,62%)`,alive:1});
}
restarting=false;
}
function step(dt){
for(let i=0;i<bodies.length;i++){
const b=bodies[i];
if(!b.alive)continue;
let ax=0,ay=0;
for(let j=0;j<bodies.length;j++){
if(i===j)continue;
const o=bodies[j];
if(!o.alive)continue;
const dx=o.x-b.x,dy=o.y-b.y;
const d2=dx*dx+dy*dy+30;
const d=Math.sqrt(d2);
const f=G*o.m/d2;
ax+=f*dx/d;ay+=f*dy/d;
}
b.ax=ax;b.ay=ay;
}
for(let i=0;i<bodies.length;i++){
const b=bodies[i];
if(!b.alive)continue;
b.vx+=b.ax*dt;b.vy+=b.ay*dt;
b.x+=b.vx*dt;b.y+=b.vy*dt;
}
const star=bodies[0];
for(let i=1;i<bodies.length;i++){
const b=bodies[i];
if(!b.alive)continue;
const dx=b.x-star.x,dy=b.y-star.y;
const d=Math.sqrt(dx*dx+dy*dy);
if(d<star.r+b.r)b.alive=0;
if(d>Math.max(W,H)*1.6)b.alive=0;
}
}
function frame(){
requestAnimationFrame(frame);
ctx.fillStyle='rgba(0,0,0,0.14)';
ctx.fillRect(0,0,W,H);
const sub=8,dt=0.5/sub;
for(let s=0;s<sub;s++)step(dt);
for(const b of bodies){
if(!b.alive)continue;
ctx.beginPath();
ctx.fillStyle=b.c;
ctx.arc(b.x,b.y,b.r,0,Math.PI*2);
ctx.fill();
}
let n=0;
for(let i=1;i<bodies.length;i++)if(bodies[i].alive)n++;
hud.textContent='Bodies in field: '+n+' / 6';
if(n===0&&!restarting){
restarting=true;
setTimeout(init,1200);
}
}
init();
frame();
</script></body></html>
Claude Fable 5frontier referenceOK
Show code
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>N-Body</title>
<style>
html,body{margin:0;height:100%;overflow:hidden;background:#000}
canvas{display:block}
</style>
</head>
<body>
<canvas id="c"></canvas>
<script>
const cv=document.getElementById("c"),g=cv.getContext("2d");
let W,H;
function rs(){W=cv.width=innerWidth;H=cv.height=innerHeight;g.fillStyle="#000";g.fillRect(0,0,W,H)}
rs();
addEventListener("resize",rs);
const G=1200;
let B=[];
function init(){
  B=[];
  const s={m:2600+Math.random()*900,x:W/2+(Math.random()*40-20),y:H/2+(Math.random()*40-20),vx:0,vy:0,star:1};
  B.push(s);
  const rmax=Math.min(W,H)/2-60;
  for(let i=0;i<6;i++){
    const m=6+Math.random()*54;
    const r=90+Math.random()*Math.max(60,rmax-90);
    const a=Math.random()*Math.PI*2;
    const v=Math.sqrt(G*s.m/r)*(0.92+Math.random()*0.16);
    B.push({m:m,x:s.x+Math.cos(a)*r,y:s.y+Math.sin(a)*r,vx:-Math.sin(a)*v,vy:Math.cos(a)*v});
  }
  let px=0,py=0;
  for(let i=1;i<B.length;i++){px+=B[i].vx*B[i].m;py+=B[i].vy*B[i].m}
  s.vx=-px/s.m;s.vy=-py/s.m;
  g.fillStyle="#000";g.fillRect(0,0,W,H);
}
function rad(b){return b.star?4+Math.sqrt(b.m)*0.26:2+Math.sqrt(b.m)*0.55}
function col(b){return b.star?"#ffd35e":"hsl("+(200-b.m*3|0)+",85%,65%)"}
function step(dt){
  const n=B.length;
  for(let i=0;i<n;i++){
    const a=B[i];
    for(let j=i+1;j<n;j++){
      const b=B[j];
      const dx=b.x-a.x,dy=b.y-a.y;
      const d2=dx*dx+dy*dy+30;
      const f=G/(d2*Math.sqrt(d2));
      a.vx+=f*b.m*dx*dt;a.vy+=f*b.m*dy*dt;
      b.vx-=f*a.m*dx*dt;b.vy-=f*a.m*dy*dt;
    }
  }
  for(let i=0;i<n;i++){const b=B[i];b.x+=b.vx*dt;b.y+=b.vy*dt}
  const s=B[0],lim=Math.max(W,H)*0.9;
  for(let i=B.length-1;i>0;i--){
    const b=B[i];
    const dx=b.x-s.x,dy=b.y-s.y;
    const d=Math.hypot(dx,dy);
    if(d<rad(s)+rad(b)){
      const M=s.m+b.m;
      s.vx=(s.vx*s.m+b.vx*b.m)/M;
      s.vy=(s.vy*s.m+b.vy*b.m)/M;
      s.m=M;
      B.splice(i,1);
    }else if(d>lim){
      B.splice(i,1);
    }
  }
}
function draw(){
  g.fillStyle="rgba(0,0,0,0.075)";
  g.fillRect(0,0,W,H);
  for(const b of B){
    const r=rad(b);
    g.save();
    g.shadowBlur=b.star?26:10;
    g.shadowColor=col(b);
    g.fillStyle=col(b);
    g.beginPath();
    g.arc(b.x,b.y,r,0,Math.PI*2);
    g.fill();
    g.restore();
  }
  const alive=B.length-1;
  g.save();
  g.shadowBlur=0;
  g.fillStyle="rgba(0,0,0,0.55)";
  g.fillRect(10,10,190,30);
  g.fillStyle="#9fe8ff";
  g.font="14px monospace";
  g.fillText("bodies in field: "+alive+" / 6",20,30);
  g.restore();
}
init();
function frame(){
  for(let k=0;k<8;k++)step(0.002);
  draw();
  if(B.length<=1)init();
  requestAnimationFrame(frame);
}
requestAnimationFrame(frame);
</script>
</body>
</html>
Claude Haiku 4.5frontier referenceBLANK
Show code
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>N-Body System</title>
<style>
body{margin:0;background:#000;display:flex;align-items:center;justify-content:center;height:100vh;font-family:monospace}
canvas{display:block;border:1px solid #333}
#info{position:absolute;top:10px;color:#0f0}
</style>
</head>
<body>
<canvas id="c" width="800" height="600"></canvas>
<div id="info">Bodies: 0</div>
<script>
const canvas=document.getElementById('c');
const ctx=canvas.getContext('2d');
const G=45,dt=0.01,substeps=3;

class Body{
constructor(x,y,vx,vy,m,color){
this.x=x;this.y=y;this.vx=vx;this.vy=vy;this.m=m;this.color=color;
this.ax=0;this.ay=0;this.trail=[];
}
update(){
this.vx+=this.ax*dt;this.vy+=this.ay*dt;
this.x+=this.vx*dt;this.y+=this.vy*dt;
this.trail.push([this.x,this.y]);
if(this.trail.length>25)this.trail.shift();
this.ax=0;this.ay=0;
}
draw(){
const r=Math.sqrt(this.m)*0.6;
ctx.fillStyle=this.color;ctx.beginPath();
ctx.arc(this.x,this.y,r,0,6.28);ctx.fill();
ctx.strokeStyle=this.color;ctx.globalAlpha=0.3;
ctx.lineWidth=0.5;ctx.beginPath();
for(let p of this.trail)ctx.lineTo(p[0],p[1]);
ctx.stroke();ctx.globalAlpha=1;
}
inBounds(){
return this.x>-60&&this.x<860&&this.y>-60&&this.y<660;
}
}

let bodies=[];

function init(){
bodies=[];
const cx=400,cy=300;
bodies.push(new Body(cx,cy,0,0,75,'#fff'));
const colors=['#e85','#5e8','#58e','#ee5','#5ee','#e5e'];
for(let i=0;i<6;i++){
const angle=Math.random()*6.28;
const r=80+Math.random()*60;
const m=2+Math.random()*5;
const v=Math.sqrt(G*75/r)*0.96;
const x=cx+Math.cos(angle)*r;
const y=cy+Math.sin(angle)*r;
const vx=-Math.sin(angle)*v;
const vy=Math.cos(angle)*v;
bodies.push(new Body(x,y,vx,vy,m,colors[i]));
}
}

function physics(){
for(let i=0;i<bodies.length;i++){
for(let j=i+1;j<bodies.length;j++){
const dx=bodies[j].x-bodies[i].x;
const dy=bodies[j].y-bodies[i].y;
const d2=dx*dx+dy*dy;
if(d2<64){bodies.splice(j,1);j--;continue;}
const d=Math.sqrt(d2);
const f=G/(d*d2);
const fx=f*dx;
const fy=f*dy;
bodies[i].ax+=fx*bodies[j].m;bodies[i].ay+=fy*bodies[j].m;
bodies[j].ax-=fx*bodies[i].m;bodies[j].ay-=fy*bodies[i].m;
}
}
for(let b of bodies)b.update();
}

function render(){
ctx.fillStyle='#000';ctx.fillRect(0,0,800,600);
let alive=0;
for(let i=bodies.length-1;i>=0;i--){
if(!bodies[i].inBounds()){bodies.splice(i,1);}else{
bodies[i].draw();
if(i>0)alive++;
}
}
document.getElementById('info').textContent='Bodies: '+alive;
if(bodies.length<2)init();
}

function animate(){
for(let i=0;i<substeps;i++)physics();
render();
requestAnimationFrame(animate);
}

init();
animate();
</script>
</body>
</html>
Results at a glance— every model in this round with score, response time and status.

Who wrote what

ModelBlind scoreLatencyStatus
qwen3-coder:30b 31.5 sOK
deepseek-r1:14b 33.0 sOK
qwen3:8b harness
llama3.1:8b 72.7 sOK
mistral-small:24b 99.1 sOK
qwen3.5:9b 56.8 sOK
gemma4:26b harness
qwen3:14b 33.8 sOK
deepseek-r1:32b 62.1 sOK
qwq:32b 274.0 sOK
command-r:35b DNF