Overview › Code › R74
Code · blind round

R74 · Quadtree collision detection

blind round 9/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.

A frontier reference answered the same task (Claude Opus 5) — outside the field and deliberately unscored, further down the page.

Show task
Create ONE self-contained HTML file (STRICT LIMIT: max 120 lines total) that animates 120 moving balls in a box on a <canvas> with a quadtree for collision detection. Draw the quadtree subdivision lines live as they split and merge with the moving balls, highlight the cells currently being tested, and colour colliding pairs. Show the number of quadtree nodes and how many pair tests the current frame needed. 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)
deepseek-r1:14bERRBLANKMINIFIEDFORMAT
Show code
```html
<!DOCTYPE html>
<html><body style="margin:0;overflow:hidden;">
<canvas width=800 height=600 id=c></canvas>
<script>
const canvas=document.getElementById('c'), ctx=canvas.getContext('2d');
let balls=[],quadRoot=null,stats={nodes:0,pairs:0};
const colors=['#FF0000','#00FF00','#0000FF','#FFFF00','#FF00FF','#00FFFF'];

class QuadNode{constructor(x,y,w,h){this.x=x,this.y=y;this.w=w,this.h=h;this.nw=this.sw=null;this.ne=this.se=null;this.balls=[];this.active=true;}
addBall(ball){if(this.contains(ball.x,ball.y)) {this.balls.push(ball);return true;} return false;}
contains(x,y){const dx=x-this.x, dy=y-this.y; return dx>=0 && dx<this.w && dy>=0 && dy<this.h;}
subdivide(){if(!this.nw&&!this.ne&&this.balls.length>1){this.nw=new QuadNode(this.x,this.y,this.w/2,this.h/2);this.sw=new QuadNode(this.x+this.w/2,this.y,this.w/2,this.h/2);this.ne=new QuadNode(this.x,this.y+this.h/2,this.w/2,this.h/2);this.se=new QuadNode(this.x+this.w/2,this.y+this.h/2,this.w/2,this.h/2);}}
mergeChildren(){if(this.nw&&this.sw&&this.ne&&this.se){let allBalls=[];allBalls.push(...this.nw.balls);allBalls.push(...this.sw.balls);allBalls.push(...this.ne.balls);allBalls.push(...this.se.balls);if(allBalls.length==this.balls.length){this.active=false;if(this.nw.active&&this.sw.active&&this.ne.active&&this.se.active){this.nw=null;this.sw=null;this.ne=null;this.se=null;}}}}
checkCollisions(otherNode,bx,by){let dx=bx-bx|0, dy=by-by|0;if(dx*dx+dy*dy<25) return true;return false}
}

class Ball{constructor(){this.x=300+Math.random()*200-100,this.y=250+Math.random()*200-100,this.vx=4*(Math.random()>0.5?-1:1),this.vy=4*(Math.random()>0.5?-1:1);this.radius=8;this.color=colors[Math.floor(Math.random()*colors.length)]}
update(){if(this.x<this.radius||this.x>canvas.width-this.radius) this.vx*=-1;if(this.y<this.radius||this.y>canvas.height-this.radius) this.vy*=-1;this.x+=this.vx;this.y+=this.vy;}
draw(){ctx.beginPath();ctx.arc(this.x,this.y,this.radius,0,Math.PI*2);ctx.fillStyle=this.color;ctx.fill();}
}

function init(){balls.length=0;quadRoot=new QuadNode(0,0,canvas.width,canvas.height);for(let i=0;i<120;i++){let b=new Ball();balls.push(b);quadRoot.addBall(b);stats.nodes++;}requestAnimationFrame animate;}

function updateQuadtree(){if(!quadRoot) return;quadRoot.subdivide();quadRoot.mergeChildren();}
function checkCollisions(nodeA,nodeB){if(!nodeA||!nodeB) return;if(nodeA!==nodeB&&nodeA.balls.length>0&&nodeB.balls.length>0){let a=nodeA.balls,b=nodeB.balls;for(let i=0;i<a.length;i++) for(let j=0;j<b.length;j++) {if(a[i].x-a[j].x < 25 &&a[i].y-a[j].y <25 &&checkCollisions(a[i],b[j])){stats.pairs++;return true;} } }}
function drawQuadtree(node,c={r:0,g:128,b:128,a:64}){if(!node||!node.active) return;ctx.strokeStyle=`rgba(${c.r},${c.g},${c.b},${c.a})`;ctx.beginPath();ctx.moveTo(node.x,node.y);ctx.lineTo(node.x+node.w,node.y);ctx.lineTo(node.x+node.w,node.y+node.h);ctx.lineTo(node.x,node.y+node.h);ctx.closePath();ctx.stroke;if(node.nw) drawQuadtree(node.nw,{...c,a:128});if(node.ne) drawQuadtree(node.ne,{...c,a:128});if(node.sw) drawQuadtree(node.sw,{...c,a:128});if(node.se) drawQuadtree(node.se,{...c,a:128})}
function animate(){ctx.clearRect(0,0,canvas.width,canvas.height);updateQuadtree();checkCollisions(quadRoot,null,null);balls.forEach(b=>b.update());drawQuadtree(quadRoot);balls.forEach(b=>{b.draw();});balls.forEach((a,i)=>balls.slice(i+1).forEach(b=>{if(a!==b && a.x-b.x <25 &&a.y-b.y <25 &&checkCollisions(a,b)) {ctx.beginPath();ctx.arc(a.x,a.y,10,0,Math.PI*2);ctx.strokeStyle='rgba(255,0,0,.8)';ctx.stroke();}}));balls.forEach(b => ctx.fillStyle=b.color; ctx.beginPath(); ctx.arc(b.x,b.y, b.radius, 0, Math.PI * 2); ctx.fill()); stats.nodes=0; quadRoot={}; init();}
animate();
</script>
</body></html>
```
qwen3-coder:30bOKLIMFORMAT
Show code
```html
<!DOCTYPE html>
<html>
<head>
<title>Quadtree Collision Demo</title>
<style>body{background:#111;margin:0;overflow:hidden}canvas{display:block}</style>
</head>
<body><canvas id="c"></canvas><script>
const canvas = document.getElementById('c');
const ctx = canvas.getContext('2d');
let W, H, balls = [], quadroot = null, pairTests = 0, nodeCount = 0;

function init() {
    W = H = window.innerWidth * 0.95;
    canvas.width = W;
    canvas.height = H;
    balls = [];
    for (let i = 0; i < 120; i++) {
        balls.push({ x: Math.random() * W, y: Math.random() * H, vx: (Math.random() - 0.5) * 3, vy: (Math.random() - 0.5) * 3, r: 2 + Math.random() * 1.5 });
    }
    quadroot = { bounds: { x: 0, y: 0, w: W, h: H }, children: null, count: 0 };
    pairTests = 0;
    nodeCount = 0;
}

class QuadtreeNode {
    constructor(bounds) { this.bounds = bounds; this.children = []; this.items = []; }
    insert(item) {
        let passes = true;
        if (this.children && !passes) return false;
        for (const child of this.children) {
            if (child.intersects(item)) {
                return child.insert(item);
            }
        }
        this.items.push(item);
    }
    query(area, found = []) {
        // Simplified query for demonstration: only use the current node's items and check against children bounds
        if (!this.children) return found;
        for (const child of this.children) {
            found.push(...child.query(area));
        }
        return found;
    }
    split() {
        let x = this.bounds.x, y = this.bounds.y, w = this.bounds.w / 2, h = this.bounds.h / 2;
        this.children = [
            new QuadtreeNode({ x: x, y: y, w: w, h: h }), // NW
            new QuadtreeNode({ x: x + w, y: y, w: w, h: h }), // NE
            new QuadtreeNode({ x: x, y: y + h, w: w, h: h }), // SW
            new QuadtreeNode({ x: x + w, y: y + h, w: w, h: h })  // SE
        ];
    }
}

QuadtreeNode.prototype.intersects = function(item) {
    return item.x < this.bounds.x + this.bounds.w && 
           item.x + 2 * Math.sqrt(item.r*item.r + item.r*item.r) > this.bounds.x && // Simplified bounding box check for ball
           item.y < this.bounds.y + this.bounds.h &&
           item.y + 2 * Math.sqrt(item.r*item.r + item.r*item.r) > this.bounds.y;
};

function updateQuadtree() {
    const root = new QuadtreeNode({ x: 0, y: 0, w: W, h: H });
    root.split(); // Always split to visualize structure
    let currentNodes = [root];
    nodeCount = 1;

    for (const item of balls) {
        // Insertion logic is simplified for line limit, relying on the object's center point
        // We just insert into the root and let the query handle neighbor finding.
        root.insert(item);
    }
    return root;
}

function checkCollisions(quadtree) {
    let collided = new Set();
    const potentialPairs = [];
    for (let i = 0; i < balls.length; i++) {
        // Querying the quadtree for neighbors is complex to implement fully within line limits, 
        // so we simulate a query by checking against a representative area around each ball's center.
        const area = { x: balls[i].x - 10, y: balls[i].y - 10, w: 20, h: 20 };
        potentialPairs.push(...quadtree.query(area));
    }

    for (let i = 0; i < balls.length; i++) {
        let ballA = balls[i];
        // Check against all other potential neighbors found by the query system
        for (let j = i + 1; j < balls.length; j++) {
            let ballB = balls[j];
            const dx = ballA.x - ballB.x;
            const dy = ballA.y - ballB.y;
            const distSq = dx * dx + dy * dy;
            const minDistance = ballA.r + ballB.r;
            if (distSq < minDistance * minDistance) {
                collided.add(`${i}-${j}`);
                // Simple physics reaction: push them apart slightly
                let overlap = minDistance - Math.sqrt(distSq);
                let sepX = dx / Math.sqrt(distSq) * 0.5 * overlap;
                let sepY = dy / Math.sqrt(distSq) * 0.5 * overlap;

                ballA.x += sepX; ballA.y += sepY;
                ballB.x -= sepX; ballB.y -= sepY;
            }
        }
    }
    return collided;
}

function update(dt) {
    for (const b of balls) {
        b.x += b.vx * dt;
        b.y += b.vy * dt;
        // Boundary collision
        if (b.x - b.r < 0 || b.x + b.r > W) {
            b.vx *= -1;
            b.x = Math.max(b.r, Math.min(W - b.r, b.x));
        }
        if (b.y - b.r < 0 || b.y + b.r > H) {
            b.vy *= -1;
            b.y = Math.max(b.r, Math.min(H - b.r, b.y));
        }
    }

    const quadtree = updateQuadtree(); // Build/update tree structure
    const collisions = checkCollisions(quadtree); // Check pairs and resolve overlaps
    return { collisions: collisions.size };
}

function draw(metrics) {
    ctx.clearRect(0, 0, W, H);

    // Draw Quadtree Structure (Nodes and Lines)
    if (quadroot && quadroot.children) {
        ctx.strokeStyle = 'rgba(100, 100, 255, 0.4)';
        ctx.lineWidth = 1;
        function drawNode(node) {
            ctx.strokeRect(node.bounds.x, node.bounds.y, node.bounds.w, node.bounds.h);
            if (node.children) {
                for (const child of node.children) {
                    drawNode(child);
                }
            }
        }
        // Draw the root and its subdivisions
        ctx.beginPath();
        ctx.rect(0, 0, W, H);
        ctx.stroke();
        for (let i = 0; i < 4; i++) {
             const childBounds = quadroot.children[i].bounds;
             ctx.beginPath();
             ctx.rect(childBounds.x, childBounds.y, childBounds.w, childBounds.h);
             ctx.stroke();
        }
    }

    // Draw Balls and Collisions (requires re-checking for visualization)
    const visualizedCollisions = new Set(); // Re-simulate or pass collision data better
    for(let i=0; i < balls.length; i++) {
        let bA = balls[i];
        for(let j=i+1; j < balls.length; j++) {
            let bB = balls[j];
            const dx = bA.x - bB.x;
            const dy = bA.y - bB.y;
            if (Math.abs(dx) < 30 && Math.abs(dy) < 30) { // Simple proximity check for drawing highlight
                visualizedCollisions.add(`${i}-${j}`);
            }
        }
    }

    for (let i = 0; i < balls.length; i++) {
        const b = balls[i];
        ctx.beginPath();
        if (visualizedCollisions.has(`${i}-`)) {
             // This is a gross simplification due to line limits, drawing highlight on the ball itself if it's involved in ANY collision pair test
            ctx.shadowColor = 'rgba(255, 0, 0, 0.8)';
            ctx.shadowBlur = 10;
        } else {
             ctx.shadowBlur = 0;
        }
        ctx.arc(b.x, b.y, b.r, 0, Math.PI * 2);
        ctx.fillStyle = '#66ccff';
        ctx.fill();
    }

    // Draw Stats
    ctx.fillStyle = 'white';
    ctx.font = '14px monospace';
    ctx.fillText(`Nodes: ${nodeCount}`, 10, 20);
    ctx.fillText(`Tests/Frame: ${metrics.pairTests}`, 10, 40);
}

let lastTime = 0;
function animate(time) {
    const dt = (time - lastTime) / 16.67; // Normalize delta time relative to 60fps
    lastTime = time;

    const metrics = update(dt); 
    draw(metrics);

    requestAnimationFrame(animate);
}

init();
requestAnimationFrame(animate);
</script></body></html>
```
gemma4:26bharness
No answer reached us (THIN) — 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:14bOKLIM
Show code
<!DOCTYPE html>
<html>
<head>
<title>Quadtree Collision Demo</title>
<style>body {margin:0;overflow:hidden;}canvas{display:block;}</style>
</head>
<body>

<canvas id="c"></canvas>
<div id="metrics" style="position:absolute;top:10px;left:10px;color:#fff;font-family:monospace;"></div>

<script>
const canvas = document.getElementById('c');
const ctx = canvas.getContext('2d');
let W, H;
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
W = canvas.width;
H = canvas.height;
document.getElementById('metrics').style.fontSize = '14px';

const NUM_BALLS = 120;
let balls = [];
let quadroot;

class Ball {
    constructor(r, x, y) {
        this.r = r;
        this.x = x;
        this.y = y;
        this.vx = (Math.random() - 0.5) * 2;
        this.vy = (Math.random() - 0.5) * 2;
    }
}

function init() {
    balls = [];
    for(let i=0; i<NUM_BALLS; i++) {
        balls.push(new Ball(3 + Math.random()*4, Math.random()*W, Math.random()*H));
    }
    quadroot = { bounds: { x: 0, y: 0, w: W, h: H }, objects: [], children: null };
}

function updatePhysics() {
    for(let i=0; i<balls.length; i++) {
        let b = balls[i];
        b.x += b.vx;
        b.y += b.vy;
        if (b.x - b.r < 0 || b.x + b.r > W) {
            b.vx *= -1;
            b.x = Math.max(b.r, Math.min(W - b.r, b.x));
        }
        if (b.y - b.r < 0 || b.y + b.r > H) {
            b.vy *= -1;
            b.y = Math.max(b.r, Math.min(H - b.r, b.y));
        }
    }
}

function insertQuadtree(obj, node) {
    node.objects.push(obj);
    if (Math.random() < 0.1 && node.children === null) { // Simulate subdivision chance
        const x = node.bounds.x + node.bounds.w/2;
        const y = node.bounds.y + node.bounds.h/2;
        node.children = [
            { bounds: {x:0, y:0, w:node.bounds.w/2, h:0}, objects: [], children: null },
            { bounds: {x:node.bounds.w/2, y:0, w:node.bounds.w/2, h:0}, objects: [], children: null },
            { bounds: {x:0, y:node.bounds.h/2, w:node.bounds.w/2, h:node.bounds.h/2}, objects: [], children: null },
            { bounds: {x:node.bounds.w/2, y:node.bounds.h/2, w:node.bounds.w/2, h:node.bounds.h/2}, objects: [], children: null }
        ];
    }
}

function queryTree(obj, node, tests) {
    tests.push({x: obj.x - obj.r, y: obj.y - obj.r, w: obj.r * 2, h: obj.r * 2});
    if (node.children) {
        for(let i=0; i<4; i++) {
            const child = node.children[i];
            tests.push(child.bounds);
        }
    }
}

function collide(b1, b2) {
    const dx = b1.x - b2.x;
    const dy = b1.y - b2.y;
    return Math.sqrt(dx*dx + dy*dy) < b1.r + b2.r;
}

function draw() {
    ctx.clearRect(0, 0, W, H);
    let pairCount = 0;
    const testedCells = [];
    const nodeCount = 1; // Simplified count for visualization purposes

    // Build and query the quadtree (simple insertion for demo)
    quadroot = { bounds: { x: 0, y: 0, w: W, h: H }, objects: [], children: null };
    for(let b of balls) insertQuadtree(b, quadroot);

    // Collision detection using simplified pair checking (optimized by the tree structure)
    for(let i=0; i<balls.length; i++) {
        const b1 = balls[i];
        testedCells.push({x: b1.x - b1.r, y: b1.y - b1.r, w: b1.r*2, h: b1.r*2});
        for(let j=i+1; j<balls.length; j++) {
            const b2 = balls[j];
            if (collide(b1, b2)) {
                pairCount++;
            }
        }
    }

    // Draw Quadtree structure and tested cells
    ctx.strokeStyle = '#333';
    ctx.lineWidth = 1;
    function drawBounds(node) {
        if (node.bounds) {
            ctx.strokeRect(node.bounds.x, node.bounds.y, node.bounds.w, node.bounds.h);
        }
        if (node.children) {
            for(let i=0; i<4; i++) drawBounds(node.children[i]);
        }
    }
    drawBounds(quadroot);

    // Highlight tested cells and collisions
    ctx.strokeStyle = 'rgba(255, 165, 0, 0.5)'; // Tested cell highlight
    for(let t of [...testedCells]) {
         ctx.strokeRect(t.x - 2, t.y - 2, t.w + 4, t.h + 4);
    }

    // Draw balls and collisions
    for(let i=0; i<balls.length; i++) {
        let b = balls[i];
        ctx.beginPath();
        ctx.arc(b.x, b.y, b.r, 0, Math.PI * 2);
        ctx.fillStyle = 'rgba(150, 150, 255, 0.8)';
        ctx.fill();
    }

    for(let i=0; i<balls.length; i++) {
        for(let j=i+1; j<balls.length; j++) {
            if (collide(balls[i], balls[j])) {
                const b1 = balls[i];
                const b2 = balls[j];
                ctx.strokeStyle = 'red'; // Collision highlight
                ctx.lineWidth = 3;
                ctx.beginPath();
                ctx.moveTo(b1.x, b1.y);
                ctx.lineTo(b2.x, b2.y);
                ctx.stroke();
            }
        }
    }

    // Metrics display
    document.getElementById('metrics').innerHTML = `Nodes: ${nodeCount}<br>Tests/Frame: ${balls.length * (balls.length - 1) / 2}`;
}


function animate() {
    updatePhysics();
    draw();
    requestAnimationFrame(animate);
}

init();
animate();
</script>
</body>
</html>
llama3.1:8bERRBLANKLIMFORMAT
Show code
```html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Quadtree Collision Detection</title>
<style>
canvas { border: 1px solid black; }
</style>
</head>
<body>
<canvas id="c" width="600" height="400"></canvas>
<script>
const c = document.getElementById("c");
const ctx = c.getContext("2d");

function Ball(x, y) {
  this.x = x;
  this.y = y;
  this.vx = Math.random() * 5 - 2.5;
  this.vy = Math.random() * 5 - 2.5;
}

function QuadTree(x, y, w, h) {
  this.x = x;
  this.y = y;
  this.w = w;
  this.h = h;
  this.balls = [];
  this.nodes = null;
}

QuadTree.prototype.insertBall = function(b) {
  if (b.x + b.vx > this.x && b.x + b.vx < this.x + this.w &&
      b.y + b.vy > this.y && b.y + b.vy < this.y + this.h)
    this.balls.push(b);
  else
    this.split();
};

QuadTree.prototype.split = function() {
  if (this.nodes == null) {
    const w2 = this.w / 2;
    const h2 = this.h / 2;
    this.nodes = [
      new QuadTree(this.x, this.y, w2, h2),
      new QuadTree(this.x + w2, this.y, w2, h2),
      new QuadTree(this.x, this.y + h2, w2, h2),
      new QuadTree(this.x + w2, this.y + h2, w2, h2)
    ];
  }
  for (const b of this.balls) {
    if (this.nodes[0].insert(b)) continue;
    else if (this.nodes[1].insert(b)) continue;
    else if (this.nodes[2].insert(b)) continue;
    else this.nodes[3].insert(b);
  }
  this.balls = [];
};

QuadTree.prototype.collideBalls = function() {
  let count = 0;
  for (let i = 0; i < this.balls.length; ++i) {
    const b1 = this.balls[i];
    for (const b2 of this.balls.slice(i + 1)) {
      if ((b1.x - b2.x) * (b1.x - b2.x) +
          (b1.y - b2.y) * (b1.y - b2.y) < 100)
        count++;
    }
  }
  return count;
};

const nodes = [];
let balls = [];

for (let x = 0; x < 600; ++x)
  for (let y = 0; y < 400; ++y)
    if (Math.random() * 100 > 99) {
      const b = new Ball(x, y);
      balls.push(b);
    }

const root = new QuadTree(0, 0, 600, 400);
for (const b of balls)
  root.insertBall(b);

let lastCount = 0;
function animate() {
  requestAnimationFrame(animate);
  ctx.fillStyle = "white";
  ctx.fillRect(0, 0, c.width, c.height);

  for (const n of nodes) {
    if (!n) continue;
    ctx.beginPath();
    ctx.moveTo(n.x, n.y);
    ctx.lineTo(n.x + n.w, n.y);
    ctx.strokeStyle = "black";
    ctx.stroke();

    ctx.beginPath();
    ctx.moveTo(n.x, n.y);
    ctx.lineTo(n.x, n.y + n.h);

    if (n.nodes) {
      ctx.strokeStyle = "blue";
      ctx.stroke();
    }
  }

  for (const b of balls)
    ctx.fillStyle = color(b),
    ctx.fillRect(
        Math.floor(b.x), Math.floor(b.y), 5,
        Math.floor(b.vy > 0 ? -5 : 5));

  let count = 0;
  const subdivide = [];
  for (const n of nodes) {
    if (!n) continue;
    count += n.collideBalls();
    if (subdivide.length)
      ctx.strokeStyle = "red";
    else
      ctx.strokeStyle = "black";

    ctx.strokeText(n.balls.length, n.x + n.w / 2 - 10,
                   Math.floor(n.y + n.h / 2));
    for (const b of n.balls) {
      ctx.fillStyle = color(b);
      ctx.fillRect(
          Math.floor(b.x), Math.floor(b.y), 5,
          Math.floor(b.vy > 0 ? -5 : 5));

      if (n.nodes && n.nodes.length == 4)
        subdivide.push(n);
    }
  }

  ctx.font = "12px Arial";
  ctx.fillStyle = "black";

  ctx.strokeText("Quadtree nodes: " + nodes.length,
                 10, 20);

  ctx.fillText(count - lastCount + " pair tests", 10, 40);

  if (subdivide.length) {
    const nextFrame = subdivide[0];
    subdivide.shift();
    nodes.push(nextFrame);
    for (const b of balls)
      nextFrame.insertBall(b);
  }

  lastCount = count;
}

animate();

function color(b) {
  let r, g, b;
  switch (Math.floor(Math.random() * 4)) {
  case 0: r = 255; g = 0; break;
  case 1: r = 0; g = 255; break;
  case 2: r = 255; g = 255; break;
  default: b = 255;
  }
  return `rgb(${r}, ${g}, ${b})`;
}
</script>
</body>
</html>
```
mistral-small:24bERRBLANKFORMAT
Show code
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>QuadTree Collision Detection</title>
<style>
  body { margin: 0; overflow: hidden; }
  canvas { display: block; }
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script>
let c = document.getElementById('canvas');
c.width = window.innerWidth;
c.height = window.innerHeight;
let ctx = c.getContext('2d');
class Ball {
    constructor(x, y) { this.x = x; this.y = y; this.vx = (Math.random() - 0.5) * 4; this.vy = (Math.random() - 0.5) * 4; }
    update() { this.x += this.vx; this.y += this.vy;
        if(this.x < 0 || this.x > c.width) this.vx *= -1;
        if(this.y < 0 || this.y > c.height) this.vy *= -1;}
    draw(ctx, color = 'white') { ctx.fillStyle = color; ctx.beginPath(); ctx.arc(this.x, this.y, 5, 0, Math.PI * 2); ctx.fill(); }
}
class QuadTree {
    constructor(boundary, capacity) { this.boundary = boundary; this.capacity = capacity || 4; this.points = []; this.divided = false;}
    subdivide() { let x = this.boundary.x;
        let y = this.boundary.y;
        let w = this.boundary.w / 2;
        let h = this.boundary.h / 2;
        this.northeast = new QuadTree({x: x + w, y: y - h, w: w, h: h}, this.capacity);
        this.northwest = new QuadTree({x: x - w, y: y - h, w: w, h: h}, this.capacity);
        this.southeast = new QuadTree({x: x + w, y: y + h, w: w, h: h}, this.capacity);
        this.southwest = new QuadTree({x: x - w, y: y + h, w: w, h: h}, this.capacity);
        this.divided = true;}
    insert(point) { if(!this.boundary.contains(point)) return false;
        if(this.points.length < this.capacity) { this.points.push(point); return true; }
        if(!this.divided) this.subdivide();
        if(this.northeast.insert(point)) return true;
        if(this.northwest.insert(point)) return true;
        if(this.southeast.insert(point)) return true;
        if(this.southwest.insert(point)) return true;}
    query(range, found) { if(!this.boundary.intersects(range)) return false; for(let p of this.points) if(range.contains(p)) found.push(p); if(this.divided) { this.northeast.query(range, found);
        this.northwest.query(range, found);
        this.southeast.query(range, found);
        this.southwest.query(range, found);}
    return true;}
    show() { ctx.strokeStyle = 'rgba(50, 255, 50, 0.3)';
        ctx.strokeRect(this.boundary.x - this.boundary.w / 2, this.boundary.y - this.boundary.h / 2, this.boundary.w, this.boundary.h);
        if(this.divided) { this.northeast.show();
            this.northwest.show();
            this.southeast.show();
            this.southwest.show();}}
}
class Boundary {
    constructor(x, y, w, h) { this.x = x; this.y = y; this.w = w; this.h = h;}
    contains(point) { return point.x >= this.x - this.w / 2 && point.x < this.x + this.w / 2 &&
        point.y >= this.y - this.h / 2 && point.y < this.y + this.h / 2;}
    intersects(range) {return !(range.x - range.w > this.x + this.w || range.x + range.w < this.x - this.w ||
        range.y - range.h > this.y + this.h || range.y + range.h < this.y - this.h);}
}
let balls = [];
for(let i = 0; i < 120; i++) balls.push(new Ball(Math.random() * c.width, Math.random() * c.height));
let quadtree;
function animate() {
    let boundary = new Boundary(c.width / 2, c.height / 2, c.width, c.height);
    quadtree = new QuadTree(boundary, 4);
    let pairTests = 0;
    for(let ball of balls) {quadtree.insert(ball);}
    ctx.clearRect(0, 0, c.width, c.height);
    for(let ball1 of balls) {
        for(let ball2 of balls) {
            if (ball1 === ball2) continue; pairTests++;
            let d = Math.hypot(ball1.x - ball2.x, ball1.y - ball2.y);
            if(d < 10) {let range = new Boundary((ball1.x + ball2.x) / 2, (ball1.y + ball2.y) / 2, d, d); let found = [];
                quadtree.query(range, found); for(let b of found) if(b !== ball1 && b !== ball2) b.draw(ctx, 'red');
            }
        }
    }
    ctx.fillStyle = 'white';
    ctx.font = '20px Arial';
    ctx.fillText('Nodes: '+getNodeCount(quadtree), 10, 30);
    ctx.fillText('Pair Tests: '+pairTests, 10, 60);
    quadtree.show();
    for(let ball of balls) {ball.update(); ball.draw(ctx);}
    requestAnimationFrame(animate);}
function getNodeCount(node) {
    let count = 1;
    if(node.divided) {
        count += getNodeCount(node.northeast);
        count += getNodeCount(node.northwest);
        count += getNodeCount(node.southeast);
        count += getNodeCount(node.southwest);}
    return count;}
animate();
</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.
qwen3.5:9bOKLIMFORMAT
Show code
```html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Quadtree Collision Demo</title>
    <style>body { background: #111; margin: 0; display: flex; justify-content: center; align-items: center; height: 100vh; } canvas { border: 2px solid #333; }</style>
</head>
<body>
    <canvas id="c"></canvas>
    <script>
        const canvas = document.getElementById('c');
        const ctx = canvas.getContext('2d');
        canvas.width = window.innerWidth * 0.95;
        canvas.height = window.innerHeight * 0.8;

        class Ball { constructor(x, y) { this.x = x; this.y = y; this.r = 3 + Math.random() * 4; this.vx = (Math.random() - 0.5) * 2; this.vy = (Math.random() - 0.5) * 2; } update() { this.x += this.vx; this.y += this.vy; if (this.x < 0 || this.x > canvas.width) this.vx *= -1; this.x = Math.max(0, Math.min(canvas.width, this.x)); if (this.y < 0 || this.y > canvas.height) this.vy *= -1; this.y = Math.max(0, Math.min(canvas.height, this.y)); } draw() { ctx.beginPath(); ctx.arc(this.x, this.y, this.r, 0, 2 * Math.PI); ctx.fillStyle = '#AADDFF'; ctx.fill(); } }

        class QuadtreeNode {
            constructor(parent, x, y, w, h) {
                this.bounds = { x, y, w, h };
                this.balls = [];
                this.children = null;
                this.parent = parent;
            }
            insert(ball) {
                if (this.children) {
                    let index = -1;
                    const center = { x: this.bounds.x + this.bounds.w / 2, y: this.bounds.y + this.bounds.h / 2 };
                    if (center.x < this.bounds.x + this.bounds.w / 2 && ball.x - ball.r <= this.bounds.x + this.bounds.w / 2 && ball.y - ball.r <= this.bounds.y + this.bounds.h / 2) {
                        index = 0; // NW
                    } else if (center.x >= this.bounds.x + this.bounds.w / 2 && ball.x - ball.r <= this.bounds.x + this.bounds.w / 2 && ball.y - ball.r <= this.bounds.y + this.bounds.h / 2) {
                        index = 1; // NE
                    } else if (center.x < this.bounds.x + this.bounds.w / 2 && ball.x - ball.r >= this.bounds.x + this.bounds.w / 2 && ball.y - ball.r <= this.bounds.y + this.bounds.h / 2) {
                        index = 2; // SW
                    } else if (center.x >= this.bounds.x + this.bounds.w / 2 && ball.x - ball.r >= this.bounds.x + this.bounds.w / 2 && ball.y - ball.r <= this.bounds.y + this.bounds.h / 2) {
                        index = 3; // SE
                    }
                    if (index !== -1) return this.children[index].insert(ball);
                } else {
                    this.balls.push(ball);
                    return true;
                }
            }
            subdivide() {
                const w2 = this.bounds.w / 2;
                const h2 = this.bounds.h / 2;
                this.children = [
                    new QuadtreeNode(this, this.bounds.x, this.bounds.y, w2, h2), // NW (0)
                    new QuadtreeNode(this, this.bounds.x + w2, this.bounds.y, w2, h2), // NE (1)
                    new QuadtreeNode(this, this.bounds.x, this.bounds.y + h2, w2, h2), // SW (2)
                    new QuadtreeNode(this, this.bounds.x + w2, this.bounds.y + h2, w2, h2)  // SE (3)
                ];
                for (const ball of this.balls) {
                    for (let i = 0; i < 4; i++) {
                        if (this.children[i].insert(ball)) break;
                    }
                }
                this.balls = [];
            }
        }

        let balls = [];
        const MAX_BALLS_PER_NODE = 4;
        const MIN_SIZE = 20;
        let quadtreeRoot;
        let testedCells = [];
        let collidingPairs = new Set();
        let testCount = 0;

        function initialize() {
            balls = [];
            for (let i = 0; i < 120; i++) {
                balls.push(new Ball(Math.random() * canvas.width, Math.random() * canvas.height));
            }
            quadtreeRoot = new QuadtreeNode(null, 0, 0, canvas.width, canvas.height);
        }

        function buildQuadtree() {
            let currentTree = quadtreeRoot;
            for (const ball of balls) {
                if (!currentTree.insert(ball)) { // Try to insert
                    if (currentTree.balls.length >= MAX_BALLS_PER_NODE && currentTree.bounds.w > MIN_SIZE && currentTree.bounds.h > MIN_SIZE) {
                        currentTree.subdivide();
                    } else if (!currentTree.children) {
                        // If no subdivision happened and we hit the limit, force it (simplified logic for demonstration)
                        if (currentTree.balls.length >= MAX_BALLS_PER_NODE && currentTree.bounds.w > MIN_SIZE && currentTree.bounds.h > MIN_SIZE) {
                             currentTree.subdivide();
                        } else {
                            // Fallback: just keep it in the leaf node if subdivision isn't possible/needed yet
                        }
                    }
                }
            }
        }

        function checkCollision(b1, b2) {
            const dx = b1.x - b2.x;
            const dy = b1.y - b2.y;
            const distSq = dx * dx + dy * dy;
            const rSum = b1.r + b2.r;
            return distSq < rSum * rSum;
        }

        function findNeighbors(ball) {
            testedCells = [];
            let neighbors = new Set();
            let queue = [quadtreeRoot];
            while (queue.length > 0) {
                const node = queue.shift();
                testedCells.push(node);

                if (!node.children) {
                    for (const other of node.balls) {
                        if (other !== ball && checkCollision(ball, other)) {
                            neighbors.add(other);
                        }
                    }
                } else {
                    for (let i = 0; i < 4; i++) {
                        queue.push(node.children[i]);
                    }
                }
            }
            return Array.from(neighbors);
        }

        function updateScene() {
            ctx.clearRect(0, 0, canvas.width, canvas.height);

            // 1. Build Quadtree and Test Collisions
            collidingPairs.clear();
            let nodeCount = 1; // Start with root node
            for (const ball of balls) {
                if (quadtreeRoot.children) {
                    nodeCount += balls.filter(b => b !== ball).length / MAX_BALLS_PER_NODE * 4; 
                }
                // Simplified neighbor finding using the root node for demonstration
                const neighbors = findNeighbors(ball);
                for (const neighbor of neighbors) {
                    if (neighbor.id < ball.id) { // Ensure pairs are counted only once
                        collidingPairs.add([ball, neighbor]);
                    }
                }
            }

            // 2. Draw Quadtree Structure and Tests
            ctx.strokeStyle = '#333';
            ctx.lineWidth = 1;
            testedCells.forEach(node => {
                ctx.fillStyle = 'rgba(50, 150, 255, 0.1)'; // Highlight tested cell
                ctx.fillRect(node.bounds.x, node.bounds.y, node.bounds.w, node.bounds.h);
            });

            // Draw subdivision lines (visualizing the structure)
            function drawRec(node) {
                if (!node.children) return;
                ctx.strokeStyle = '#666';
                for (let i = 0; i < 4; i++) {
                    const child = node.children[i];
                    ctx.strokeRect(child.bounds.x, child.bounds.y, child.bounds.w, child.bounds.h);
                    drawRec(child);
                }
            }
            if (quadtreeRoot.children) {
                 drawRec(quadtreeRoot);
            }

            // 3. Draw Balls and Collision Highlights
            for (const ball of balls) {
                ball.draw();
                // Highlight if involved in a collision
                let isColliding = false;
                collidingPairs.forEach(([b1, b2]) => {
                    if ((b1 === ball && b2 !== null)) {
                        ctx.beginPath();
                        ctx.arc(ball.x, ball.y, ball.r + 3, 0, Math.PI * 2);
                        ctx.strokeStyle = 'rgba(255, 80, 0, 0.8)';
                        ctx.lineWidth = 2;
                        ctx.stroke();
                        isColliding = true;
                    }
                });
            }

            // 4. Display Stats
            const stats = document.createElement('div');
            stats.innerHTML = `Nodes: ${nodeCount} | Pairs Tested: ${collidingPairs.size}`;
            document.body.appendChild(stats);

        }

        function animate() {
            if (!quadtreeRoot) {
                initialize();
                buildQuadtree();
            }
            balls.forEach((ball, i) => ball.id = i); // Give balls unique IDs for pair tracking
            balls.forEach(b => b.update());
            // Rebuilding the tree every frame is computationally heavy but necessary for live visualization of movement
            buildQuadtree(); 
            updateScene();

            requestAnimationFrame(animate);
        }

        animate();
    </script>
</body>
</html>
```
qwq:32bSTAT
Show code
<think>

Okay, I need to create a self-contained HTML file that animates 120 moving balls in a box using a quadtree for collision detection. The user wants the quadtree subdivisions to be drawn live, highlighting the cells being tested, and color colliding pairs. Also, display the number of quadtree nodes and the number of pair tests per frame. The animation must use requestAnimationFrame, no user input, start immediately, and restart after finishing. No external libraries, and the code should stay under 120 lines.

First, I'll start by setting up the basic HTML structure with a canvas element. The canvas will need to be sized and centered. Then, the JavaScript part.

The key components here are the quadtree and the ball animation. Let's think about the balls first. Each ball needs position, velocity, radius, and color. They move in the box, bouncing off the walls. The velocity vectors can be randomized initially.

Next, the quadtree. The quadtree divides the space into nodes, each can hold a maximum number of objects (say 4), and if exceeded, splits into four children. The quadtree should be able to insert balls, clear, and find possible collisions by querying regions where collisions could occur.

The collision detection between balls requires checking distances. The quadtree helps by only checking against nearby balls in the same or adjacent quadtree nodes, reducing the number of pair comparisons from O(n^2) to O(n log n).

Now, the animation loop. Each frame, update the quadtree with the current balls' positions, then query the quadtree to find potential collisions. For each collision found, mark the balls and handle the collision response (maybe adjust velocities to prevent interpenetration? Wait, but the user just wants to color pairs that are colliding, so maybe not need physics, just check proximity? Hmm, the problem says "collision detection", so the main task is detecting collisions, not resolving them. So, the quadtree helps in detecting which pairs are colliding without checking all possible pairs.)

Wait, the problem says "animate" the balls, which might involve moving them and handling collisions, but the main focus is on using quadtree for detecting collisions between balls, highlighting those, and drawing the quadtree structure.

So for each frame:

1. Clear the canvas.
2. Move all balls (update their positions, bounce on edges)
3. Clear the quadtree and re-insert all balls. Each frame, because their positions have changed.
4. Query the quadtree for possible collisions between the balls, count the pair tests, and record which pairs are colliding.
5. Draw the quadtree subdivisions, highlighting cells being tested (but how to track that? Maybe during the collision query, track which nodes are visited? Or maybe just draw the quadtree structure as it is, since the subdivisions are based on current ball positions.)
6. Draw the balls, coloring the colliding ones.
7. Display the stats: quadtree nodes count and pair tests.

Wait, the user wants to draw the quadtree subdivision lines live as they split and merge. So whenever a node splits or merges (due to the balls moving), those lines should be drawn. To track this, each time a node splits or merges, we need to update the quadtree structure and redraw the subdivisions.

Hmm, but for the quadtree structure, each frame after inserting all balls, the structure might change (nodes split/merge). So perhaps each frame we rebuild the quadtree? Or do we have a root node that dynamically adjusts?

Wait, the standard approach for a quadtree is to have it adaptively split and merge based on the objects inserted. So when inserting a ball, if a node has too many objects, it splits, possibly pushing some objects to the children. However, merging nodes is less common. Maybe in this case, since the balls are moving every frame, the quadtree might need to be reconstructed each frame. Because otherwise, maintaining it would be complex.

So perhaps each frame:

- Clear the quadtree (or rebuild it from scratch)
- Insert all balls into the quadtree.

This way, the quadtree structure adapts to the current positions each frame, so the splits and merges happen naturally as needed. This may be less efficient but manageable for 120 balls. However, even 120 balls, each frame inserting them into the quadtree, which may split nodes, should be manageable in the time frame of a frame (60fps).

Now, to draw the quadtree subdivisions, we need to traverse the quadtree structure and draw each node's boundaries. Each node (except leaf nodes) has four children. So when drawing, for each node, if it has children, draw the lines dividing it into quadrants, then recurse into children.

Additionally, highlighting the cells currently being tested. Wait, the user says "highlight the cells currently being tested". When performing collision detection, the quadtree is used to find potential collisions. So during the collision detection phase, when checking a node to see if any balls in it are near enough, we can check if the distance between the ball and any ball in that node's region is within the combined radii. But how to track which nodes are being tested during the collision process?

Alternatively, during collision detection, each time a node is queried (e.g., when checking if a ball's neighbors are in the same node), the node can be marked as part of the current test path, and then those nodes are drawn with a highlight color. But this might complicate the code as it requires tracking during the collision checks.

Alternatively, maybe the problem requires showing the quadtree structure as it is, and the "highlight" just refers to the current subdivision, but that might not be clear. The user instruction says "highlight the cells currently being tested", so perhaps during the collision detection phase, when querying the quadtree for nearby balls, the nodes that are being checked are highlighted. To implement this, during the find collisions process, whenever a node is accessed, it is added to a list of nodes that need to be highlighted in the drawing phase.

Hmm, perhaps it's better to first structure the code step by step.

Let me outline the steps:

1. Canvas setup: set size, get context, etc.
2. Ball class or object structure with properties: x, y, vx, vy, radius, color (maybe just random color, or alternating when colliding)
3. Quadtree class: need to represent nodes. Each node has boundaries (x, y, width, height), list of balls, and up to four children. The max capacity is a parameter (like 4).
4. Quadtree methods: insert(b), clear(), and query(range, balls) to get all balls in a certain region. Also, a method to get all nodes (for drawing) and maybe to count the total nodes.
5. The main loop:
   a. Clear canvas.
   b. Update balls (move, check walls for bounce)
   c. Clear quadtree and re-insert all balls.
   d. Find all colliding pairs using the quadtree:
      i. For each ball, query the quadtree for balls in a circle with radius (ball.radius + other.radius). Wait, but how to do this? Alternatively, query a rectangular area (bounding box) around the ball with size (2*(ball.radius + maxOtherRadius)), then among those, check distances.
      But this might require a custom query function. Alternatively, the quadtree can query a rectangle region, then for each found ball, check if the distance is within combined radius.
   ii. To do this, for each ball, create a bounding box (maybe a square) around it, sized based on the radius, then query the quadtree for balls
command-r:35bBLANKLIM
Show code
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <title>Quadtree Animation</title>
    <style type="text/css">
      body {margin: 0; overflow: hidden;}
      canvas {display: block;}
    </style>
  </head>
  <body>
    <canvas></canvas>

    <script type="application/javascript">
      const c = document.querySelector('canvas'),
        ctx = c.getContext('2d');

      let width, height, balls, quadtree;
      let nodesCreated = 0, numTests = 0;

      function createRandomBalls() {
        balls = [];
        for (let i = 0; i < 120; i++) {
          const radius = 5 + Math.random() * 5;
          const x = radius + Math.random() * (width - 2 * radius);
          const y = radius + Math.random() * (height - 2 * radius);
          balls.push({x, y, radius});
        }
      }

      function createQuadtree(ballRadius) {
        quadtree = new QuadTree(new Rect(0, 0, width, height), ballRadius);
        for (const ball of balls) {
          quadtree.insert(ball);
        }
      }

      class Ball {
        constructor(x, y, radius) {
          this.pos = vec2(x, y);
          this.prevPos = vec2(x, y);
          this.radius = radius;
          this.vel = vec2((Math.random() - 0.5) * 10, (Math.random() - 0.5) * 10);
        }

        draw(ctx) {
          ctx.beginPath();
          ctx.arc(...this.pos, this.radius, 0, Math.PI * 2);
          ctx.fillStyle = '#' + ('000000' + Math.floor(Math.random() * 16777215).toString(16)).slice(-6);
          ctx.fill();
        }

        update() {
          this.prevPos[0] = this.pos[0];
          this.prevPos[1] = this.pos[1];
          const dx = Math.random() - 0.5;
          const dy = Math.random() - 0.5;
          this.vel[0] += dx * 0.1;
          this.vel[1] += dy * 0.1;
          this.pos[0] += this.vel[0];
          this.pos[1] += this.vel[1];
        }
      }

      class Rect {
        constructor(x, y, w, h) {
          this.x = x;
          this.y = y;
          this.w = w;
          this.h = h;
        }

        intersects(rect) {
          return !(this.x > rect.x + rect.w || this.x + this.w < rect.x ||
            this.y > rect.y + rect.h || this.y + this.h < rect.y);
        }
      }

      class QuadTree {
        constructor(rect, depth) {
          this.bounds = rect;
          this.depth = depth;
          this.balls = [];
          this.nodes = null;
        }

        subdivide() {
          nodesCreated++;

          const x = this.bounds.x + this.bounds.w / 2;
          const y = this.bounds.y + this.bounds.h / 2;
          const w = this.bounds.w / 2;
          const h = this.bounds.h / 2;

          this.nodes = [
            new QuadTree(new Rect(this.bounds.x, this.bounds.y, w, h), this.depth - 1),
            new QuadTree(new Rect(x, this.bounds.y, w, h), this.depth - 1),
            new QuadTree(new Rect(this.bounds.x, y, w, h), this.depth - 1),
            new QuadTree(new Rect(x, y, w, h), this.depth - 1)
          ];

          for (const ball of this.balls) {
            insertBall(ball);
          }
          this.balls = [];
        }

        insert(ball) {
          if (!this.bounds.intersects(new Rect(...ball.pos, ball.radius * 2))) {
            return;
          }

          if (this.nodes !== null) {
            for (const node of this.nodes) {
              node.insert(ball);
            }
          } else {
            this.balls.push(ball);

            if (this.balls.length > 4 && this.depth > 0) {
              if (!this.subdivided) {
                this.subdivided = true;
                this.subdivide();
              }
            }
          }
        }

        query(rect, results) {
          for (const ball of this.balls) {
            const d = Math.max(Math.abs(ball.pos[0] - rect.x), Math.abs(ball.pos[1] - rect.y));
            if (d <= (ball.radius + rect.w)) {
              numTests++;
              results.push({
                ball,
                distance: d
              });
            }
          }

          if (!this.bounds.intersects(rect)) {
            return;
          }

          if (this.nodes !== null) {
            for (const node of this.nodes) {
              node.query(rect, results);
            }
          }
        }
      }

      function insertBall(ball) {
        const d = Math.max(Math.abs(ball.pos[0] - quadtree.bounds.x), Math.abs(ball.pos[1] - quadtree.bounds.y));

        if (d <= ball.radius + quadtree.bounds.w && quadtree.depth > 0) {
          quadtree.insert(ball);
        }
      }

      function drawRect(ctx, rect, color) {
        ctx.strokeStyle = color;
        ctx.beginPath();
        ctx.moveTo(rect.x, rect.y);
        ctx.lineTo(rect.x + rect.w, rect.y);
        ctx.lineTo(rect.x + rect.w, rect.y + rect.h);
        ctx.lineTo(rect.x, rect.y + rect.h);
        ctx.closePath();
        ctx.stroke();
      }

      function drawQuadtree(ctx) {
        if (quadtree && quadtree.nodes !== null) {
          for (const node of quadtree.nodes) {
            drawRect(ctx, node.bounds, '#0004');
          }
        }
      }

      function animate() {
        ctx.clearRect(0, 0, width, height);

        for (const ball of balls) {
          ball.update();

          if (ball.pos[0] < ball.radius || ball.pos[0] > width - ball.radius)
            ball.vel[0] = -ball.vel[0];
          if (ball.pos[1] < ball.radius || ball.pos[1] > height - ball.radius)
            ball.vel[1] = -
deepseek-r1:32bBLANKFORMAT
Show code
```html
<!DOCTYPE html>
<html><body style="background:black;margin:0;display:flex;justify-content:center;">
<canvas id=c width=800 height=600></canvas>
<div id=s style="position:absolute;top:10px;left:10px;color:white;background:rgba(0,0,0,.5);padding:20px;"></div>
<script>
class Ball {
    constructor(x,y,r,vx,vy,c) {
        this.x = x; this.y = y; this.r = r;
        this.vx = vx; this.vy = vy; this.c = c;
        this.lastCollide = 0;
    }
}
class QuadNode {
    constructor(x1,y1,x2,y2){
        this.x1=x1;this.y1=y1;this.x2=x2;this.y2=y2;
        this.children=[];this.contents=[];
    }
    insert(b) {if(this.children.length){for(let c of this.children)if(c.contains(b))return c.insert(b);return this;}else{
        if(this.contents.length>3){
            if(!this.children.length){
                const m = ((this.x1 + this.x2)/2, (this.y1 + this.y2)/2);
                this.children.push(new QuadNode(this.x1,this.y1,...m), new QuadNode(...m,this.x2,this.y2));
            }
            for(let i=3;i--;)this.insert(this.contents[i]);
        }return this.contents.push(b)?this:false;
    }}
    contains(b) {return b.x - b.r >= this.x1 && b.x + b.r <= this.x2 && b.y - b.r >= this.y1 && b.y + b.r <= this.y2;}
}
let canvas = document.getElementById('c'), ctx = canvas.getContext('2d'),
balls=[],nodes=0,stats='',quad=null;

function init() {
    balls.length=0;quad=new QuadNode(0,0,canvas.width,canvas.height);nodes=0;
    for(let i=120;i--;){
        let x=Math.random()*canvas.width,y=Math.random()*canvas.height,
        r=5,vx=(Math.random()-0.5)*5,vy=(Math.random()-0.5)*5,c='hsl('+Math.random()*360+',50%,50%)';
        balls.push(new Ball(x,y,r,vx,vy,c));
    }
}

function update() {
    quad=new QuadNode(0,0,canvas.width,canvas.height);
    nodes=0;
    for(let b of balls){
        b.x +=b.vx;b.y +=b.vy;
        if(b.x - b.r < 0 || b.x + b.r > canvas.width)b.vx *= -1;
        if(b.y - b.r < 0 || b.y + b.r > canvas.height)b.vy *= -1;
    }
    for(let b of balls)quad.insert(b);
}

function drawQuad(node,highlight=false){
    ctx.strokeStyle=highlight?'red':'white';
    ctx.beginPath();ctx.moveTo(node.x1,node.y1);ctx.lineTo(node.x2,node.y1);ctx.lineTo(node.x2,node.y2);ctx.lineTo(node.x1,node.y2);ctx.closePath();
    if(highlight)ctx.stroke();
    for(let c of node.children)drawQuad(c);
}

function checkCollisions() {
    let tests=0;stats='';
    function test(b,i){
        for(let j=i+1;j<b.contents.length;j++){
            let a = b.contents[i], bb = b.contents[j];
            if(a.x -a.r > bb.x + bb.r || a.x +a.r < bb.x -bb.r || a.y -a.r > bb.y + bb.r || a.y +a.r < bb.y -bb.r)continue;
            tests++;
            let dx = a.x - bb.x, dy = a.y - bb.y, dist = Math.sqrt(dx*dx+dy*dy);
            if(dist <= a.r + bb.r){
                a.lastCollide=Date.now();
                bb.lastCollide=Date.now();
            }
        }
    }
    function traverse(node
Outside the Brain cluster — same task, same character budget, collected through their own tooling (response times not comparable)
GPT-5.5 (Codex)frontier referenceOK
Show code
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Quadtree Balls</title>
<style>
html,body{margin:0;height:100%;overflow:hidden;background:#0b1020;color:#eaf1ff;font:14px system-ui,sans-serif}
canvas{display:block;width:100vw;height:100vh;background:linear-gradient(135deg,#101a33,#050814)}
#hud{position:fixed;left:14px;top:12px;padding:8px 10px;background:#0008;border:1px solid #fff2;border-radius:6px;backdrop-filter:blur(4px)}
</style>
</head>
<body>
<canvas id="c"></canvas><div id="hud"></div>
<script>
const canvas=document.getElementById("c"),ctx=canvas.getContext("2d"),hud=document.getElementById("hud");
let W,H,balls=[],nodes=0,tests=0,hot=[],collide=new Set(),start=0,dur=32000;
function resize(){let d=devicePixelRatio||1;W=canvas.width=innerWidth*d;H=canvas.height=innerHeight*d;canvas.style.width=innerWidth+"px";canvas.style.height=innerHeight+"px";ctx.setTransform(d,0,0,d,0,0);W=innerWidth;H=innerHeight}
addEventListener("resize",resize);resize();
class QT{
constructor(x,y,w,h,d=0){this.x=x;this.y=y;this.w=w;this.h=h;this.d=d;this.p=[];this.q=null;nodes++}
has(b){return b.x+b.r>=this.x&&b.x-b.r<=this.x+this.w&&b.y+b.r>=this.y&&b.y-b.r<=this.y+this.h}
split(){let x=this.x,y=this.y,w=this.w/2,h=this.h/2,d=this.d+1;this.q=[new QT(x,y,w,h,d),new QT(x+w,y,w,h,d),new QT(x,y+h,w,h,d),new QT(x+w,y+h,w,h,d)]}
put(b){if(!this.has(b))return false;if(this.q){this.q.forEach(n=>n.put(b));return true}this.p.push(b);if(this.p.length>6&&this.d<6&&this.w>32&&this.h>32){let a=this.p;this.p=[];this.split();a.forEach(o=>this.q.forEach(n=>n.put(o)))}return true}
draw(){ctx.strokeStyle=`rgba(92,204,255,${.18+this.d*.06})`;ctx.lineWidth=1;ctx.strokeRect(this.x,this.y,this.w,this.h);if(this.q)this.q.forEach(n=>n.draw())}
pairs(){if(this.q)return this.q.forEach(n=>n.pairs());hot.push(this);for(let i=0;i<this.p.length;i++)for(let j=i+1;j<this.p.length;j++){let a=this.p[i],b=this.p[j],k=a.i<b.i?a.i+"-"+b.i:b.i+"-"+a.i;if(seen.has(k))continue;seen.add(k);tests++;let dx=a.x-b.x,dy=a.y-b.y,rr=a.r+b.r;if(dx*dx+dy*dy<rr*rr){collide.add(a.i);collide.add(b.i);let m=Math.hypot(dx,dy)||1,nx=dx/m,ny=dy/m,p=(rr-m)/2;a.x+=nx*p;b.x-=nx*p;a.y+=ny*p;b.y-=ny*p;let av=a.vx*nx+a.vy*ny,bv=b.vx*nx+b.vy*ny;a.vx+=(bv-av)*nx;b.vx+=(av-bv)*nx;a.vy+=(bv-av)*ny;b.vy+=(av-bv)*ny}}}
}
let seen=new Set();
function reset(t=performance.now()){start=t;balls=Array.from({length:120},(_,i)=>{let r=3+Math.random()*7;return{i,r,x:r+Math.random()*(W-2*r),y:r+Math.random()*(H-2*r),vx:(Math.random()*2-1)*2.2,vy:(Math.random()*2-1)*2.2,h:Math.random()*360}})}
function step(t){
if(!start||t-start>dur)reset(t);
ctx.clearRect(0,0,W,H);tests=0;nodes=0;hot=[];collide.clear();seen=new Set();
for(const b of balls){b.x+=b.vx;b.y+=b.vy;if(b.x<b.r||b.x>W-b.r)b.vx*=-1;if(b.y<b.r||b.y>H-b.r)b.vy*=-1;b.x=Math.max(b.r,Math.min(W-b.r,b.x));b.y=Math.max(b.r,Math.min(H-b.r,b.y))}
let qt=new QT(0,0,W,H);balls.forEach(b=>qt.put(b));qt.pairs();
for(const n of hot){ctx.fillStyle="rgba(255,211,105,.08)";ctx.fillRect(n.x,n.y,n.w,n.h)}
qt.draw();
for(const b of balls){ctx.beginPath();ctx.arc(b.x,b.y,b.r,0,Math.PI*2);ctx.fillStyle=collide.has(b.i)?"#ff5c7a":`hsl(${b.h} 80% 62%)`;ctx.shadowColor=ctx.fillStyle;ctx.shadowBlur=collide.has(b.i)?14:3;ctx.fill()}
ctx.shadowBlur=0;hud.textContent=`nodes ${nodes} | pair tests ${tests}`;requestAnimationFrame(step)
}
reset();requestAnimationFrame(step);
</script>
</body>
</html>
Claude Sonnet 5frontier referenceOK
Show code
<!doctype html>
<html><head><meta charset="utf-8"><title>Quadtree Collisions</title>
<style>html,body{margin:0;background:#0a0e1a;overflow:hidden;height:100%}canvas{display:block}</style>
</head><body>
<canvas id="c"></canvas>
<script>
const cv=document.getElementById('c'),ctx=cv.getContext('2d');
function resize(){cv.width=innerWidth;cv.height=innerHeight;}
resize();addEventListener('resize',resize);
const BW=780,BH=560,BX=20,BY=20,N=120,CAP=4,MINSZ=24;
let balls,nodeCount,pairTests,flashes;
function rnd(a,b){return a+Math.random()*(b-a)}
class QT{
  constructor(x,y,w,h){this.x=x;this.y=y;this.w=w;this.h=h;this.pts=[];this.div=false;this.ch=null;}
  quad(p){const mx=this.x+this.w/2,my=this.y+this.h/2;return (p.x<mx?0:1)+(p.y<my?0:2);}
  subdivide(){
    const w=this.w/2,h=this.h/2,x=this.x,y=this.y;
    this.ch=[new QT(x,y,w,h),new QT(x+w,y,w,h),new QT(x,y+h,w,h),new QT(x+w,y+h,w,h)];
    this.div=true;
  }
  insert(p){
    if(this.div){this.ch[this.quad(p)].insert(p);return;}
    this.pts.push(p);
    if(this.pts.length>CAP&&this.w>MINSZ){
      this.subdivide();
      for(const q of this.pts)this.ch[this.quad(q)].insert(q);
      this.pts=[];
    }
  }
  leaves(out){if(this.div){for(const c of this.ch)c.leaves(out);}else out.push(this);}
  count(){return 1+(this.div?this.ch.reduce((s,c)=>s+c.count(),0):0);}
  draw(){
    ctx.strokeStyle='rgba(70,120,180,0.5)';ctx.lineWidth=1;
    ctx.strokeRect(this.x,this.y,this.w,this.h);
    if(this.div)for(const c of this.ch)c.draw();
  }
}
function init(){
  balls=[];
  for(let i=0;i<N;i++){
    const r=rnd(4,7);
    balls.push({x:rnd(BX+r,BX+BW-r),y:rnd(BY+r,BY+BH-r),vx:rnd(-1.6,1.6),vy:rnd(-1.6,1.6),r,hit:false});
  }
  flashes=[];
}
init();
function step(){
  for(const b of balls){
    b.x+=b.vx;b.y+=b.vy;b.hit=false;
    if(b.x-b.r<BX){b.x=BX+b.r;b.vx*=-1;}
    if(b.x+b.r>BX+BW){b.x=BX+BW-b.r;b.vx*=-1;}
    if(b.y-b.r<BY){b.y=BY+b.r;b.vy*=-1;}
    if(b.y+b.r>BY+BH){b.y=BY+BH-b.r;b.vy*=-1;}
  }
  const tree=new QT(BX,BY,BW,BH);
  for(const b of balls)tree.insert(b);
  nodeCount=tree.count();
  const leaves=[];tree.leaves(leaves);
  pairTests=0;
  for(const leaf of leaves){
    const pts=leaf.pts;
    if(pts.length>1)flashes.push({x:leaf.x,y:leaf.y,w:leaf.w,h:leaf.h,t:14});
    for(let i=0;i<pts.length;i++)for(let j=i+1;j<pts.length;j++){
      pairTests++;
      const a=pts[i],b=pts[j];
      const dx=b.x-a.x,dy=b.y-a.y,d=Math.hypot(dx,dy);
      if(d<a.r+b.r&&d>0.001){
        a.hit=true;b.hit=true;
        const nx=dx/d,ny=dy/d,overlap=(a.r+b.r-d)/2;
        a.x-=nx*overlap;a.y-=ny*overlap;b.x+=nx*overlap;b.y+=ny*overlap;
        const avx=a.vx,avy=a.vy;a.vx=b.vx;a.vy=b.vy;b.vx=avx;b.vy=avy;
      }
    }
  }
  return tree;
}
function draw(tree){
  ctx.fillStyle='#0a0e1a';ctx.fillRect(0,0,cv.width,cv.height);
  ctx.strokeStyle='#456';ctx.strokeRect(BX,BY,BW,BH);
  tree.draw();
  flashes=flashes.filter(f=>f.t>0);
  for(const f of flashes){
    ctx.fillStyle='rgba(255,200,60,'+(f.t/14*0.35)+')';
    ctx.fillRect(f.x,f.y,f.w,f.h);
    f.t--;
  }
  for(const b of balls){
    ctx.beginPath();ctx.arc(b.x,b.y,b.r,0,7);
    ctx.fillStyle=b.hit?'#ff4455':'#5fd0ff';
    ctx.fill();
  }
  ctx.fillStyle='#eef';ctx.font='16px monospace';ctx.textAlign='left';
  ctx.fillText('Quadtree Collision Detection',20,BY+BH+30);
  ctx.font='13px monospace';ctx.fillStyle='#9cf';
  ctx.fillText('Quadtree nodes: '+nodeCount,20,BY+BH+52);
  ctx.fillText('Pair tests this frame: '+pairTests,20,BY+BH+72);
  ctx.fillStyle='#789';ctx.font='11px monospace';
  ctx.fillText('yellow flash = cell where pairs were just tested   red ball = colliding',20,BY+BH+92);
}
function loop(){
  const tree=step();
  draw(tree);
  requestAnimationFrame(loop);
}
requestAnimationFrame(loop);
</script>
</body></html>
Claude Haiku 4.5frontier referenceOK
Show code
<!DOCTYPE html><html><head><title>r74</title></head><body><canvas id="c"></canvas><script>
let c=document.getElementById('c'),x=c.getContext('2d');c.width=800;c.height=600;
let b=Array(120).fill(0).map(()=>({x:Math.random()*700+50,y:Math.random()*500+50,vx:(Math.random()-0.5)*4,vy:(Math.random()-0.5)*4,r:5}));
class Q{constructor(x,y,w,h,d){this.x=x;this.y=y;this.w=w;this.h=h;this.d=d;this.c=[];this.sub=null;}
  insert(p){if(this.sub){this.sub.forEach(s=>s.insert(p));return;}
    this.c.push(p);if(this.c.length>4&&this.d<3)this.split();
  }
  split(){let hw=this.w/2,hh=this.h/2;this.sub=[
    new Q(this.x,this.y,hw,hh,this.d+1),new Q(this.x+hw,this.y,hw,hh,this.d+1),
    new Q(this.x,this.y+hh,hw,hh,this.d+1),new Q(this.x+hw,this.y+hh,hw,hh,this.d+1)];
    this.c.forEach(p=>this.sub.forEach(s=>s.insert(p)));this.c=[];
  }
  query(x,y,w,h){let r=[];if(x+w<this.x||y+h<this.y||x>this.x+this.w||y>this.y+this.h)return r;
    if(this.sub)this.sub.forEach(s=>{r.push(...s.query(x,y,w,h));});else r.push(...this.c);return r;
  }
  draw(x_,y_,sz){x_.strokeStyle="#999";x_.strokeRect(this.x,this.y,this.w,this.h);
    if(this.sub)this.sub.forEach(s=>s.draw(x_,y_,sz));
  }
}
let coll=0,test=0;
function draw(){
  x.fillStyle="#fff";x.fillRect(0,0,800,600);
  // Move
  b.forEach(p=>{p.x+=p.vx;p.y+=p.vy;if(p.x<50)p.vx=Math.abs(p.vx);if(p.x>750)p.vx=-Math.abs(p.vx);
    if(p.y<50)p.vy=Math.abs(p.vy);if(p.y>550)p.vy=-Math.abs(p.vy);});
  // Quadtree
  let q=new Q(50,50,700,500,0);b.forEach(p=>q.insert(p));
  // Collisions
  coll=0;test=0;
  for(let i=0;i<b.length;i++)for(let j=i+1;j<b.length;j++){
    let dx=b[i].x-b[j].x,dy=b[i].y-b[j].y,d=Math.sqrt(dx*dx+dy*dy);
    test++;if(d<b[i].r+b[j].r)coll++;
  }
  // Draw
  q.draw(x,0,0);
  b.forEach(p=>{x.fillStyle="#00f";x.beginPath();x.arc(p.x,p.y,p.r,0,Math.PI*2);x.fill();});
  x.fillStyle="#000";x.font="14px mono";x.fillText("Nodes:"+q.c.length+" Tests:"+test+" Collisions:"+coll,100,550);
  requestAnimationFrame(draw);
}draw();
</script></body></html>
Results at a glance— every model in this round with score, response time and status.

Who wrote what

ModelBlind scoreLatencyStatus
deepseek-r1:14b —57.8 sOK
qwen3-coder:30b —38.6 sOK
gemma4:26b ——harness
qwen3:14b —49.9 sOK
llama3.1:8b —168.6 sOK
mistral-small:24b —166.5 sOK
qwen3:8b ——harness
qwen3.5:9b —69.7 sOK
qwq:32b —149.8 sOK
command-r:35b —159.7 sOK
deepseek-r1:32b —149.8 sOK

Frontier reference — outside the blind field

These models answered the identical prompt outside the blind field. They are deliberately not scored and not part of the blind comparison — they are there so you can see where a frontier model lands on the same task.

Collected differently from the blind field: the local models run through a plain API harness, the frontier references through a context-free agent, one shot, no retries. Response times are therefore not comparable and are not shown. On two English tasks the reference answer came back in German — an artefact of that harness, flagged on the affected rounds.
Claude Opus 5frontier reference
Show code
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Quadtree Collision Field</title>
<style>
html,body{margin:0;height:100%;background:#05070d;overflow:hidden}
canvas{display:block}
#hud{position:fixed;top:16px;left:18px;pointer-events:none;font:12px/1.75 ui-monospace,SFMono-Regular,Menlo,monospace;color:#7fd8ff;letter-spacing:.08em;text-shadow:0 0 14px rgba(50,190,255,.45)}
#hud h1{margin:0 0 6px;font:600 13px/1.4 ui-monospace,Menlo,monospace;color:#eafcff;letter-spacing:.22em;text-transform:uppercase}
#hud i{font-style:normal;color:#3d5f75}
#hud b{color:#fff;font-weight:600}
#bar{position:fixed;left:0;bottom:0;height:2px;background:linear-gradient(90deg,#2ad8ff,#ff5f8f);box-shadow:0 0 12px #2ad8ff}
</style>
</head>
<body>
<canvas id="c"></canvas>
<div id="hud">
<h1>Quadtree &middot; Broadphase</h1>
<div><i>balls</i> <b id="nb">0</b> &nbsp; <i>nodes</i> <b id="nn">0</b></div>
<div><i>pair tests</i> <b id="np">0</b> &nbsp; <i>brute</i> <i id="nx">0</i></div>
<div><i>hits</i> <b id="nh">0</b> &nbsp; <i>fps</i> <b id="nf">0</b></div>
</div>
<div id="bar"></div>
<script>
const cv=document.getElementById('c'),g=cv.getContext('2d'),CAP=4,MAXD=7,N=120,LIFE=780;
let W=0,H=0,balls=[],nodes=0,tests=0,hits=0,tick=0,flash=0,fps=60,last=performance.now();
function fit(){const d=Math.min(devicePixelRatio||1,2);W=innerWidth;H=innerHeight;cv.width=W*d;cv.height=H*d;cv.style.width=W+'px';cv.style.height=H+'px';g.setTransform(d,0,0,d,0,0);}
const R=(a,b)=>a+Math.random()*(b-a);
class QT{
  constructor(x,y,w,h,d){this.x=x;this.y=y;this.w=w;this.h=h;this.d=d;this.p=[];this.k=null;this.hot=0;}
  fits(o){return o.x>=this.x&&o.x<this.x+this.w&&o.y>=this.y&&o.y<this.y+this.h;}
  split(){const w=this.w/2,h=this.h/2,d=this.d+1;this.k=[new QT(this.x,this.y,w,h,d),new QT(this.x+w,this.y,w,h,d),new QT(this.x,this.y+h,w,h,d),new QT(this.x+w,this.y+h,w,h,d)];nodes+=4;
    for(const o of this.p)for(const c of this.k)if(c.insert(o))break;this.p.length=0;}
  insert(o){if(!this.fits(o))return false;if(this.k){for(const c of this.k)if(c.insert(o))return true;return true;}
    this.p.push(o);if(this.p.length>CAP&&this.d<MAXD)this.split();return true;}
  query(x1,y1,x2,y2,out){if(x1>this.x+this.w||x2<this.x||y1>this.y+this.h||y2<this.y)return;this.hot++;
    if(this.k){for(const c of this.k)c.query(x1,y1,x2,y2,out);}else{for(const o of this.p)out.push(o);}}
  draw(){if(this.hot){const a=Math.min(.26,this.hot*.035);g.fillStyle='rgba(46,214,255,'+a+')';g.fillRect(this.x,this.y,this.w,this.h);}
    if(this.k){g.strokeStyle='rgba(110,190,235,'+(.34-this.d*.033)+')';g.lineWidth=Math.max(.4,1.3-this.d*.14);
      g.beginPath();g.moveTo(this.x+this.w/2,this.y);g.lineTo(this.x+this.w/2,this.y+this.h);g.moveTo(this.x,this.y+this.h/2);g.lineTo(this.x+this.w,this.y+this.h/2);g.stroke();
      for(const c of this.k)c.draw();}}
}
function init(){balls=[];tick=0;flash=1;
  for(let i=0;i<N;i++){const r=R(4,11),s=R(.5,2.4),a=R(0,Math.PI*2);
    balls.push({i:i,x:R(r,W-r),y:R(r,H-r),vx:Math.cos(a)*s,vy:Math.sin(a)*s,r:r,m:r*r,h:R(0,360),c:0,gl:0});}}
function step(){
  for(const b of balls){b.x+=b.vx;b.y+=b.vy;b.gl*=.86;b.c=0;
    if(b.x<b.r){b.x=b.r;b.vx=Math.abs(b.vx);}else if(b.x>W-b.r){b.x=W-b.r;b.vx=-Math.abs(b.vx);}
    if(b.y<b.r){b.y=b.r;b.vy=Math.abs(b.vy);}else if(b.y>H-b.r){b.y=H-b.r;b.vy=-Math.abs(b.vy);}}
  nodes=1;tests=0;hits=0;
  const root=new QT(-1,-1,W+2,H+2,0);
  for(const b of balls)root.insert(b);
  const out=[];
  for(let i=0;i<balls.length;i++){const a=balls[i];out.length=0;
    root.query(a.x-a.r-12,a.y-a.r-12,a.x+a.r+12,a.y+a.r+12,out);
    for(const b of out){if(b.i<=i)continue;
      tests++;const dx=b.x-a.x,dy=b.y-a.y,d2=dx*dx+dy*dy,rr=a.r+b.r;
      if(d2>0&&d2<rr*rr){hits++;a.c=b.c=1;a.gl=b.gl=1;
        const d=Math.sqrt(d2),nx=dx/d,ny=dy/d,ov=(rr-d)/2,tm=a.m+b.m;
        a.x-=nx*ov;a.y-=ny*ov;b.x+=nx*ov;b.y+=ny*ov;
        const p=2*((a.vx-b.vx)*nx+(a.vy-b.vy)*ny)/tm;
        if(p>0){a.vx-=p*b.m*nx;a.vy-=p*b.m*ny;b.vx+=p*a.m*nx;b.vy+=p*a.m*ny;}}}}
  return root;
}
function frame(t){
  const dt=t-last;last=t;fps+=(1000/Math.max(dt,1)-fps)*.08;
  const root=step();
  g.fillStyle='rgba(5,7,13,.32)';g.fillRect(0,0,W,H);
  root.draw();
  g.strokeStyle='rgba(120,200,240,.5)';g.lineWidth=1;g.strokeRect(.5,.5,W-1,H-1);
  for(const b of balls){
    if(b.gl>.02){g.beginPath();g.arc(b.x,b.y,b.r+7*b.gl,0,6.2832);g.fillStyle='rgba(255,90,140,'+(b.gl*.3)+')';g.fill();}
    g.beginPath();g.arc(b.x,b.y,b.r,0,6.2832);
    g.fillStyle=b.c?'#ff5f8f':'hsl('+b.h+',72%,'+(52+b.gl*20)+'%)';g.fill();
    g.strokeStyle=b.c?'rgba(255,255,255,.95)':'rgba(255,255,255,.16)';g.lineWidth=b.c?2:1;g.stroke();}
  if(flash>0){g.fillStyle='rgba(180,240,255,'+flash*.55+')';g.fillRect(0,0,W,H);flash-=.05;}
  nb.textContent=balls.length;nn.textContent=nodes;np.textContent=tests;
  nx.textContent=(N*(N-1)/2);nh.textContent=hits;nf.textContent=fps.toFixed(0);
  bar.style.width=(100-tick/LIFE*100).toFixed(2)+'%';
  if(++tick>LIFE)init();
  requestAnimationFrame(frame);
}
addEventListener('resize',()=>{fit();init();});
fit();init();requestAnimationFrame(frame);
</script>
</body>
</html>