OverviewCode › R96
Code · blind round

R96 · Force-directed graph settling

blind round 3/8 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 8 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 lays out a small graph on a <canvas> with a force simulation: about 25 nodes connected by edges, where every pair of nodes repels each other and every edge pulls its two nodes together like a spring, with damping so the layout comes to rest instead of oscillating. Draw edges and labelled nodes each frame while the tangle visibly untangles. Show the node count and the total energy, and restart with a fresh random graph once it has settled. 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.5:9bOKFORMAT
Show code
```html
<!DOCTYPE html>
<html>
<head>
<title>Force Simulation Graph</title>
<style>
body {margin:0;overflow:hidden;background:#111;}canvas {display:block;border:1px solid #333;}</style>
</head>
<body>
<canvas id="graphCanvas"></canvas>
<script>
(function() {
    const canvas = document.getElementById('graphCanvas');
    const ctx = canvas.getContext('2d');

    let W, H;
    let N = 25;
    let nodes = [];
    let edges = [];
    let isRunning = true;
    let FRAME_COUNT = 0;

    // Physics constants
    const K_SPRING = 0.01; // Spring constant (Attraction)
    const REPULSION_COEFF = 2000; // Repulsion strength
    const DAMPING = 0.98; // Velocity damping factor
    const MAX_VELOCITY = 3;

    function resizeCanvas() {
        W = canvas.width = window.innerWidth * 0.95;
        H = canvas.height = window.innerHeight * 0.85;
    }

    function initGraph() {
        nodes = [];
        edges = [];
        for (let i = 0; i < N; i++) {
            nodes.push({ x: Math.random() * W, y: Math.random() * H, vx: 0, vy: 0, fx: 0, fy: 0 });
        }

        // Generate edges (fully connected graph)
        edges = [];
        for (let i = 0; i < N; i++) {
            for (let j = i + 1; j < N; j++) {
                edges.push({ source: i, target: j });
            }
        }

        isRunning = true;
    }

    function calculateForces() {
        // Reset forces
        nodes.forEach(n => { n.fx = 0; n.fy = 0; });

        let totalEnergy = 0;

        // 1. Calculate Repulsion (All pairs)
        for (let i = 0; i < N; i++) {
            for (let j = i + 1; j < N; j++) {
                const n1 = nodes[i];
                const n2 = nodes[j];

                const dx = n1.x - n2.x;
                const dy = n1.y - n2.y;
                let distSq = dx * dx + dy * dy;
                let dist = Math.sqrt(distSq);
                if (dist === 0) { dist = 0.001; }

                // Force magnitude: Inverse square law repulsion
                const force = REPULSION_COEFF / distSq;

                n1.fx += (dx / dist) * force;
                n1.fy += (dy / dist) * force;
                n2.fx -= (dx / dist) * force;
                n2.fy -= (dy / dist) * force;
            }
        }

        // 2. Calculate Attraction (Springs/Edges)
        for (const edge of edges) {
            const n1 = nodes[edge.source];
            const n2 = nodes[edge.target];

            const dx = n1.x - n2.x;
            const dy = n1.y - n2.y;
            let distSq = dx * dx + dy * dy;
            let dist = Math.sqrt(distSq);
            if (dist === 0) { dist = 0.001; }

            // Spring force: F = k * x (Hooke's Law, using current distance for approximation)
            const idealLength = W / N * 0.5; // Simple estimate for desired length
            let displacement = Math.abs(dist) - idealLength;
            let forceMagnitude = K_SPRING * displacement;

            // Apply force components
            const fX = (dx / dist) * forceMagnitude;
            const fY = (dy / dist) * forceMagnitude;

            n1.fx += fX;
            n1.fy += fY;
            n2.fx -= fX;
            n2.fy -= fY;
        }

        // 3. Update positions and velocities (Integration)
        for (let i = 0; i < N; i++) {
            const n = nodes[i];
            
            // Apply forces to acceleration proxy
            n.vx += n.fx * 0.1;
            n.vy += n.fy * 0.1;

            // Apply damping and limit velocity
            n.vx *= DAMPING;
            n.vy *= DAMPING;

            const speed = Math.sqrt(n.vx * n.vx + n.vy * n.vy);
            if (speed > MAX_VELOCITY) {
                n.vx = (n.vx / speed) * MAX_VELOCITY;
                n.vy = (n.vy / speed) * MAX_VELOCITY;
            }

            // Update position
            n.x += n.vx;
            n.y += n.vy;
        }
    }

    function draw() {
        ctx.fillStyle = 'rgba(17, 17, 17, 0.4)';
        ctx.fillRect(0, 0, W, H); // Fade effect for trailing motion

        // Draw Edges
        ctx.strokeStyle = '#666';
        ctx.lineWidth = 1.5;
        for (const edge of edges) {
            const n1 = nodes[edge.source];
            const n2 = nodes[edge.target];
            ctx.beginPath();
            ctx.moveTo(n1.x, n1.y);
            ctx.lineTo(n2.x, n2.y);
            ctx.stroke();
        }

        // Draw Nodes and Labels
        for (let i = 0; i < N; i++) {
            const n = nodes[i];
            
            // Node circle
            ctx.fillStyle = '#4CAF50';
            ctx.beginPath();
            ctx.arc(n.x, n.y, 3, 0, Math.PI * 2);
            ctx.fill();

            // Label
            ctx.fillStyle = '#FFF';
            ctx.font = '10px Arial';
            ctx.fillText(`N${i}`, n.x + 5, n.y - 5);
        }
    }

    function updateMetrics() {
        const totalEnergy = Math.abs(nodes.reduce((sum, n) => sum + (n.fx * n.vx + n.fy * n.vy), 0));
        const nodeCountText = `Nodes: ${N}`;
        const energyText = `Energy Est.: ${totalEnergy.toFixed(2)}`;

        const infoDiv = document.createElement('div');
        infoDiv.style.position = 'absolute';
        infoDiv.style.top = '10px';
        infoDiv.style.left = '10px';
        infoDiv.style.color = '#CCC';
        infoDiv.innerHTML = `${nodeCountText}<br>${energyText}`;

        let infoContainer = document.getElementById('info');
        if (!infoContainer) {
            infoContainer = document.createElement('div');
            infoContainer.id = 'info';
            document.body.appendChild(infoContainer);
        }
        infoContainer.innerHTML = `${nodeCountText}<br>${energyText}`;
    }

    function loop() {
        if (!isRunning) {
            // Check for settlement: if average speed is very low, restart
            const avgSpeedSq = nodes.reduce((sum, n) => sum + (n.vx * n.vx + n.vy * n.vy), 0) / N;
            if (avgSpeedSq < 0.01 && FRAME_COUNT > 300) { // Stay settled for a moment before restarting
                initGraph();
                FRAME_COUNT = 0;
            } else if (Math.random() < 0.005) { // Small chance to restart even if settling slowly
                 initGraph();
                 FRAME_COUNT = 0;
            }
        }

        calculateForces();
        draw();
        updateMetrics();

        requestAnimationFrame(loop);
        FRAME_COUNT++;
    }

    window.addEventListener('resize', () => {
        resizeCanvas();
        // Re-run init to keep nodes within bounds if resizing drastically changes W/H
        nodes = []; 
        edges = [];
        initGraph();
    });

    resizeCanvas();
    initGraph();
    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.
qwen3:14bERRSTATFORMAT
Show code
```html
<!DOCTYPE html>
<html><head><meta charset="UTF-8"><title>Force Graph Simulation</title></head><body style="background:#111;color:#fff;"><canvas id="graphCanvas" width="800" height="600" style="display:block;border:1px solid #333;"></canvas><div id="info" style="padding:10px;font-family:monospace;">Nodes: 0 | Energy: 0.0</div><script>
const canvas = document.getElementById('graphCanvas');
const ctx = canvas.getContext('2d');
const infoDiv = document.getElementById('info');

let nodes = [];
let edges = [];
let running = true;
let simulationRunning = false;

// --- Physics Constants ---
const K_SPRING = 0.015; // Spring constant (attraction)
const C_REPULSION = 500; // Repulsion constant (inverse square law influence)
const DAMPING = 0.95;    // Drag/damping factor
const TARGET_GRAVITY = 0; 

function createNode(x, y) {
    return { x: x, y: y, vx: Math.random() * 2 - 1, vy: Math.random() * 2 - 1, mass: 1 };
}

function initGraph(N) {
    nodes = [];
    edges = [];
    for (let i = 0; i < N; i++) {
        nodes.push(createNode(Math.random() * canvas.width, Math.random() * canvas.height));
    }

    // Create a random sparse graph (ensuring connectedness is not strictly required but adds visual interest)
    const maxEdges = N * 3; // Connect each node to ~3 others on average
    let addedEdges = new Set();

    while (edges.length < Math.min(maxEdges, N * (N - 1) / 2)) {
        let i = Math.floor(Math.random() * N);
        let j = Math.floor(Math.random() * N);
        if (i === j) continue;

        const pairKey = i < j ? `${i}-${j}` : `${j}-${i}`;
        if (!addedEdges.has(pairKey)) {
            edges.push({ u: i, v: j });
            addedEdges.add(pairKey);
        }
    }
}

function calculateForces() {
    let totalEnergy = 0;

    // 1. Repulsion (N^2) and Spring Force Calculation
    for (let i = 0; i < nodes.length; i++) {
        const nA = nodes[i];
        nA.fx = 0;
        nA.fy = 0;

        // Repulsion from all other nodes j > i
        for (let j = i + 1; j < nodes.length; j++) {
            const nB = nodes[j];

            // Calculate distance vector and squared distance
            const dx = nA.x - nB.x;
            const dy = nA.y - nB.y;
            const distSq = dx * dx + dy * dy;
            let dist = Math.sqrt(distSq);

            // Repulsion force (inverse square magnitude)
            let forceRepel = C_REPULSION / distSq;
            let fxR = (dx / dist) * forceRepel;
            let fyR = (dy / dist) * forceRepel;

            nA.fx += fxR;
            nA.fy += fyR;
            nB.fx -= fxR; // Action-reaction pair
            nB.fy -= fyR;

            // Spring Force (Hooke's Law: F = -k * x)
            let forceSpring = K_SPRING * dist;
            let fxS = -(dx / dist) * forceSpring;
            let fyS = -(dy / dist) * forceSpring;

            nA.fx += fxS;
            nA.fy += fyS;
            nB.fx -= fxS; 
            nB.fy -= fyS;
        }
    }

    // 2. Apply forces and update positions (Euler Integration with Damping)
    for (let i = 0; i < nodes.length; i++) {
        const n = nodes[i];

        // Update velocity: v(t+1) = v(t) * damping + a * dt
        n.vx = n.vx * DAMPING + (n.fx / n.mass);
        n.vy = n.vy * DAMPING + (n.fy / n.mass);

        // Update position: p(t+1) = p(t) + v(t+1) * dt
        n.x += n.vx;
        n.y += n.vy;
    }

    return totalEnergy;
}

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

    // Draw Edges (Springs)
    ctx.strokeStyle = '#555';
    ctx.lineWidth = 1;
    edges.forEach(edge => {
        const nA = nodes[edge.u];
        const nB = nodes[edge.v];
        ctx.beginPath();
        ctx.moveTo(nA.x, nA.y);
        ctx.lineTo(nB.x, nB.y);
        ctx.stroke();
    });

    // Draw Nodes (Circles and Labels)
    for (let i = 0; i < nodes.length; i++) {
        const n = nodes[i];
        
        // Node circle
        ctx.fillStyle = '#fff';
        ctx.beginPath();
        ctx.arc(n.x, n.y, 6, 0, Math.PI * 2);
        ctx.fill();

        // Label
        ctx.fillStyle = '#333';
        ctx.font = '12px monospace';
        ctx.textAlign = 'center';
        ctx.textBaseline = 'middle';
        ctx.fillText(i, n.x, n.y);
    }
}

function updateSimulation() {
    const energy = calculateForces();
    draw();
    infoDiv.innerHTML = `Nodes: ${nodes.length} | Energy: ${energy.toFixed(2)}`;
}

// --- Animation Loop and State Management ---

let lastEnergy = 0;
const STABILITY_THRESHOLD = 1.5; // If energy change is below this, consider it settled.

function animate() {
    if (!simulationRunning) return;

    updateSimulation();
    
    // Check for stability (energy dampening)
    let energyChange = Math.abs(Math.sqrt(lastEnergy * lastEnergy + 1)); // Simple metric proxy for change
    
    // Since calculating the exact potential energy is complex, we use a simple decay check: 
    // If the speed of movement/force application significantly drops over time, it has settled.
    if (energy < STABILITY_THRESHOLD && nodes.every(n => Math.abs(n.vx) < 0.1 && Math.abs(n.vy) < 0.1)) {
        console.log("Simulation settled.");
        simulationRunning = false;
        setTimeout(resetGraph, 2000); // Wait 2 seconds before restarting
    }

    lastEnergy = energy;
    requestAnimationFrame(animate);
}

function resetGraph() {
    initGraph(25); // Always use 25 nodes as specified
    simulationRunning = true;
    lastEnergy = 1000; // Initialize high energy to start the movement
    // Start the loop if it wasn't running
    if (!animationLoopId) {
        animate();
    }
}

let animationLoopId = requestAnimationFrame(animate);

// Initial setup call
resetGraph();
</script></body></html>
```
gemma4:26bharness
No answer reached us (TIMEOUT) — 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.
No usable answer reached the harness (TIMEOUT). Counted as a did-not-finish.
No usable answer reached the harness (TIMEOUT). Counted as a did-not-finish.
No usable answer reached the harness (TIMEOUT). Counted as a did-not-finish.
qwen3-coder:30bBLANKLIMFORMAT
Show code
```html
<!DOCTYPE html>
<html>
<head><title>Force Graph Simulation</title><style>body{margin:0;overflow:hidden}canvas{display:block}</style></head><body><canvas id="graphCanvas" width="800" height="600"></canvas><script>
const canvas = document.getElementById('graphCanvas');
const ctx = canvas.getContext('2d');

let nodes = [];
let edges = [];
let isSimulating = false;
let lastEnergy = 0;
let simulationInterval = 1; // Lower means faster updates
let animationFrameId = null;

// --- Physics Constants ---
const K_REPEL = 3000; // Repulsion constant (higher = stronger push)
const K_ATTRACT = 0.05; // Attraction constant (spring stiffness)
const DAMPING = 0.98; // Energy decay per step
const MAX_STEPS = 1000; // Max steps before considering the graph settled

function initializeNodes(count, width, height) {
    nodes = [];
    for (let i = 0; i < count; i++) {
        nodes.push({ x: Math.random() * width, y: Math.random() * height, vx: 0, vy: 0, index: i });
    }
}

function initializeEdges(nodeCount) {
    edges = [];
    for (let i = 0; i < nodeCount; i++) {
        for (let j = i + 1; j < nodeCount; j++) {
            // Connect every pair (complete graph)
            edges.push({ u: i, v: j });
        }
    }
}

function calculateForces() {
    let totalEnergy = 0;
    const N = nodes.length;
    
    // Reset velocities
    for (let node of nodes) {
        node.vx *= DAMPING;
        node.vy *= DAMPING;
    }

    // Apply forces for all pairs
    for (let i = 0; i < N; i++) {
        const n1 = nodes[i];
        
        // Repulsion force (all other nodes)
        for (let j = 0; j < N; j++) {
            if (i === j) continue;
            const n2 = nodes[j];

            let dx = n2.x - n1.x;
            let dy = n2.y - n1.y;
            let distSq = dx * dx + dy * dy;
            let dist = Math.sqrt(distSq);
            if (dist < 0.1) continue;

            // Repulsion force: proportional to 1/d^2
            let fx_repel = K_REPEL / distSq / N;
            let fy_repel = K_REPEL / distSq / N;

            n1.vx -= (fx_repel * dx) / dist;
            n1.vy -= (fy_repel * dy) / dist;
        }
    }

    // Apply spring force (edges)
    for (const edge of edges) {
        const n1 = nodes[edge.u];
        const n2 = nodes[edge.v];

        let dx = n2.x - n1.x;
        let dy = n2.y - n1.y;
        let dist = Math.sqrt(dx * dx + dy * dy);
        
        // Ideal length (L=1 for simplicity, giving stiffness)
        let displacement = 1 - dist; 

        // Spring force magnitude: -k_s * x (Hooke's Law simplified)
        let fx_spring = K_ATTRACT * displacement * dx / dist;
        let fy_spring = K_ATTRACT * displacement * dy / dist;

        // Apply forces equally to both nodes (A pulls B, B pulls A)
        n1.vx += fx_spring;
        n1.vy += fy_spring;
        n2.vx -= fx_spring;
        n2.vy -= fy_spring;
    }

    // Update positions using accumulated velocities
    const W = canvas.width;
    const H = canvas.height;
    for (let node of nodes) {
        node.x += node.vx;
        node.y += node.vy;
        // Simple boundary containment
        if (node.x < 0 || node.x > W) node.vx -= Math.sign(node.x - W/2);
        if (node.y < 0 || node.y > H) node.vy -= Math.sign(node.y - H/2);
    }

    // Calculate total energy change (simplified potential energy sum)
    let currentEnergy = 0;
    for (const edge of edges) {
        const n1 = nodes[edge.u];
        const n2 = nodes[edge.v];
        const dx = n2.x - n1.x;
        const dy = n2.y - n1.y;
        let dist = Math.sqrt(dx * dx + dy * dy);
        // Potential Energy: 0.5 * k * x^2 (Spring) + K_REPEL/d (Repulsion approximation)
        currentEnergy += 0.5 * K_ATTRACT * (1 - dist)**2;
    }
    return currentEnergy;

}

function drawGraph() {
    ctx.fillStyle = 'black';
    ctx.fillRect(0, 0, canvas.width, canvas.height);

    // Draw Edges
    ctx.strokeStyle = '#333';
    ctx.lineWidth = 1;
    for (const edge of edges) {
        const n1 = nodes[edge.u];
        const n2 = nodes[edge.v];
        ctx.beginPath();
        ctx.moveTo(n1.x, n1.y);
        ctx.lineTo(n2.x, n2.y);
        ctx.stroke();
    }

    // Draw Nodes and Labels
    for (let i = 0; i < nodes.length; i++) {
        const node = nodes[i];
        
        // Node circle
        ctx.beginPath();
        ctx.arc(node.x, node.y, 5, 0, Math.PI * 2);
        ctx.fillStyle = '#1E90FF';
        ctx.fill();

        // Label
        ctx.font = '10px Arial';
        ctx.fillText(i, node.x + 8, node.y - 5);
    }
}

function updateSimulation() {
    if (!isSimulating) return;

    calculateForces();
    drawGraph();

    // Convergence Check: If energy change is minimal, we consider it settled.
    const currentEnergy = calculatePotentialEnergy();
    if (Math.abs(currentEnergy - lastEnergy) < 0.1 && simulationInterval > 1) {
        console.log("Converged.");
        resetGraph();
    }

    lastEnergy = currentEnergy;
    animationFrameId = requestAnimationFrame(updateSimulation);
}

function calculatePotentialEnergy() {
    let energy = 0;
    for (const edge of edges) {
        const n1 = nodes[edge.u];
        const n2 = nodes[edge.v];
        let dx = n2.x - n1.x;
        let dy = n2.y - n1.y;
        let dist = Math.sqrt(dx * dx + dy * dy);
        // Spring Potential Energy: 0.5 * k * x^2
        energy += 0.5 * K_ATTRACT * (1 - dist)**2;
    }
    return energy;
}

function displayStats(energy) {
    const count = nodes.length;
    let statsHtml = `Nodes: ${count} | Energy: ${energy.toFixed(2)}`;
    document.getElementById('graphCanvas').insertAdjacentHTML('afterend', `<div style="position:absolute; top:10px; left:10px; color:#333; font-family: monospace;">${statsHtml}</div>`);
}

function drawStats() {
    const energy = calculatePotentialEnergy();
    // Update or create the stats display element
    let statDiv = document.querySelector('div[style*="top:10px"]');
    if (!statDiv) {
        statDiv = document.createElement('div');
        statDiv.setAttribute('style', 'position:absolute; top:10px; left:10px; color:#333; font-family: monospace;');
        document.getElementById('graphCanvas').insertAdjacentElement('afterend', statDiv);
    }
    statDiv.innerHTML = `Nodes: ${nodes.length} | Energy: ${energy.toFixed(2)}`;
}

function resetGraph() {
    // Reset simulation state
    cancelAnimationFrame(animationFrameId);
    isSimulating = false;

    // Generate new random graph structure
    const newNodeCount = Math.min(30, 10 + Math.floor(Math.random() * 25));
    initializeNodes(newNodeCount, canvas.width, canvas.height);
    initializeEdges(newNodeCount);
    nodes.forEach(n => { n.vx = 0; n.vy = 0; });

    // Restart simulation
    isSimulating = true;
    lastEnergy = 0;
    animationFrameId = requestAnimationFrame(updateSimulation);
}

// --- Initialization ---
const WIDTH = 800;
const HEIGHT = 600;
canvas.width = WIDTH;
canvas.height = HEIGHT;

function start() {
    resetGraph();
}

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

Who wrote what

ModelBlind scoreLatencyStatus
qwen3.5:9b 65.4 sOK
qwen3:8b harness
qwen3:14b 37.0 sOK
gemma4:26b harness
mistral-small:24b DNF
deepseek-r1:14b DNF
llama3.1:8b DNF
qwen3-coder:30b 97.3 sOK