OverviewCode › R87
Code · blind round

R87 · Gray-Scott reaction-diffusion

blind round 3/8 local models answered

Answers below are anonymized and shuffled. Read, judge, pick a favourite — then scroll down and reveal the models.

The field: deepseek-r1:14b · gemma4:26b · llama3.1:8b · mistral-small:24b · qwen3-coder:30b · qwen3:14b · qwen3:8b · qwen3.5:9b — listed alphabetically, which is not the order of the cards below.

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. Which ones, and why, is spelled out in the reveal.

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 runs a Gray-Scott reaction-diffusion simulation on a grid of at least 120x120 cells and draws it to a <canvas> via ImageData: two chemicals diffuse at different rates and react, so a few seeded blobs grow into branching, coral-like patterns. Use a second buffer so a step never reads values it has already overwritten. Show the number of simulation steps. 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 — blind

Local models (Brain cluster) — identical prompt, anonymized order
MODEL Aqwen3:14bERRBLANKFORMAT
Show code
```html
<!DOCTYPE html>
<html><head><title>Gray-Scott Simulation</title></head><body style="background:#000;margin:0;"><canvas id="c"></canvas><script>
    const c = document.getElementById('c');
    const ctx = c.getContext('2d');

    let S, G, A, B;
    let r, s, d_a, d_b, k;
    let bufferA, bufferB, nextA, nextB;
    let step = 0;

    function init(size) {
        S = size;
        G = size;
        c.width = c.height = S * 2; // Higher resolution for visual quality
        const scaleFactor = Math.floor(c.width / (S * 1.5)); // Scale down slightly to fit step counter
        c.style.width = `${Math.min(800, S*2)}px`;
        c.style.height = `${Math.min(600, S*2)}px`;

        bufferA = new Float32Array(S * S);
        bufferB = new Float32Array(S * S);
        nextA = new Float32Array(S * S);
        nextB = new Float32Array(S * S);

        // Parameters (Standard values for patterns)
        r = 0.055; // Reactant rate A consumption
        s = 1.0;   // Source strength B replacement
        d_a = 1.0; // Diffusion A
        d_b = 0.5; // Diffusion B
        k = 1.8;   // Reaction coefficient

        step = 0;

        // Seeding the initial state (Center area)
        for (let y = 0; y < S; y++) {
            for (let x = 0; x < S; x++) {
                const i = y * S + x;
                bufferA[i] = 1.0;
                bufferB[i] = 1.0;
            }
        }

        // Create a central 'seed' blob (B=0, A slightly higher)
        let startX = Math.floor(S * 0.3);
        let startY = Math.floor(S * 0.5);
        let radius = Math.floor(S * 0.15);

        for (let y = -radius; y <= radius; y++) {
            for (let x = -radius; x <= radius; x++) {
                const nx = startX + x;
                const ny = startY + y;

                if (nx >= 0 && nx < S && ny >= 0 && ny < S) {
                    const i = ny * S + nx;
                    bufferA[i] = 1.0; // High A
                    bufferB[i] = 0.0; // Low B
                }
            }
        }

        // Initialize the rendering background (black/dark)
        ctx.fillStyle = '#000';
        ctx.fillRect(0, 0, c.width, c.height);
    }

    function diffuse(p, buffer, nextBuffer, rate) {
        let sum = 0;
        for (let i = -1; i <= 1; i++) {
            for (let j = -1; j <= 1; j++) {
                if (i === 0 && j === 0) continue;
                const x = p[0] + i;
                const y = p[1] + j;

                if (x >= 0 && x < S && y >= 0 && y < S) {
                    sum += buffer[(y * S + x)];
                }
            }
        }
        return sum / 8.0;
    }

    function stepSimulation() {
        for (let y = 0; y < S; y++) {
            for (let x = 0; x < S; x++) {
                const i = y * S + x;

                // Calculate new A concentration (Diffusion and Reaction)
                let nextAVal = d_a * diffuse(p, bufferA, nextA, d_a);
                nextAVal = (bufferA[i] + 2.0 * d_a / 8.0 * (diffuse(p, bufferA, nextA, 0) - bufferA[i])) - r * bufferA[i] * bufferB[i] * bufferB[i];
                nextAVal = Math.max(0, Math.min(1, nextAVal));

                // Calculate new B concentration (Diffusion and Reaction)
                let nextBVal = d_b * diffuse(p, bufferB, nextB, d_b);
                nextBVal = (bufferB[i] + 2.0 * d_b / 8.0 * (diffuse(p, bufferB, nextB, 0) - bufferB[i])) + r * bufferA[i] * bufferB[i] * bufferB[i] - s * bufferB[i];
                nextBVal = Math.max(0, Math.min(1, nextBVal));

                // Store results in the 'next' buffers
                nextA[i] = nextAVal;
                nextB[i] = nextBVal;
            }
        }

        // Swap buffers (Next becomes Current)
        let temp = bufferA; bufferA = nextA; nextA = temp;
        temp = bufferB; bufferB = nextB; nextB = temp;

        step++;
    }
    
    function draw() {
        const imageData = ctx.getImageData(0, 0, c.width, c.height);
        const data = imageData.data;

        for (let y = 0; y < S; y++) {
            for (let x = 0; x < S; x++) {
                const i = y * S + x;
                
                // Normalize values to [0, 1] and scale them for color mapping.
                // A: Blue/Cyan component (e.g., higher A -> more blue)
                // B: Green component (e.g., higher B -> more green)
                // The combination creates the coral structure visualization.

                let a = bufferA[i]; 
                let b = bufferB[i]; 

                // Simple color mapping based on concentration ratio
                let rVal = Math.pow(Math.max(0, (1 - a)), 2); // Red intensity decreases with A
                let gVal = b * 2;                               // Green intensity proportional to B
                let bVal = a * 2;                               // Blue intensity proportional to A

                // Scale colors and apply to the ImageData array
                const dataIndex = (y * c.width + x) * 4;
                data[dataIndex]     = Math.floor(rVal * 255); // R
                data[dataIndex + 1] = Math.floor(gVal * 255); // G
                data[dataIndex + 2] = Math.floor(bVal * 255); // B
                data[dataIndex + 3] = 255;                        // A (Alpha)
            }
        }

        ctx.putImageData(imageData, 0, 0);
    }

    function loop() {
        stepSimulation();
        draw();
        requestAnimationFrame(loop);
    }

    init(160); // Use 160x160 grid for better detail while staying efficient
    loop();
</script></body></html>
```
No usable answer reached the harness (TIMEOUT). Counted as a did-not-finish.
MODEL Cdeepseek-r1:14bDNF
No usable answer reached the harness (TIMEOUT). Counted as a did-not-finish.
MODEL Dqwen3.5:9bBLANKFORMAT
Show code
```html
<!DOCTYPE html>
<html><head><title>Gray-Scott Simulation</title></head><body style="background:#000;margin:0;"><canvas id="c"></canvas><script>
const N = 128;
let canvas, ctx;
let bufferA = new Float32Array(N * N); // A chemical concentration
let bufferB = new Float32Array(N * N); // B chemical concentration
let nextBufferA = new Float32Array(N * N);
let nextBufferB = new Float32Array(N * N);

// Parameters (Tuning for visible patterns)
const DA = 0.16;
const DB = 0.08;
const FEED_RATE = 0.055; // k
const KILL_RATE = 0.004; // m

let stepCount = 0;

function initialize() {
    canvas = document.getElementById('c');
    ctx = canvas.getContext('2d');
    canvas.width = N * 3;
    canvas.height = N * 3;
    
    // Seed the center with high B concentration (the catalyst)
    const center = Math.floor(N / 2);
    for (let i = 0; i < N * N; i++) {
        bufferA[i] = 1.0; // Start A at max
        bufferB[i] = 0.0; // Start B at zero
    }

    // Create a central "blob" of B (the seed)
    const radiusSq = Math.floor(N * N) / 80;
    for (let y = 0; y < N; y++) {
        for (let x = 0; x < N; x++) {
            const i = y * N + x;
            const distSq = (x - center)**2 + (y - center)**2;
            if (distSq < radiusSq) {
                bufferB[i] = Math.random() * 0.5 + 0.1; // Low initial B concentration
            }
        }
    }

    nextBufferA.fill(0);
    nextBufferB.fill(0);
    stepCount = 0;
    requestAnimationFrame(loop);
}

function diffLaplacian(buf, x, y) {
    let val = buf[(y - 1) * N + x] + buf[(y + 1) * N + x] + buf[y * N + (x - 1)] + buf[y * N + (x + 1)] - 4 * buf[y * N + x];
    return val;
}

function step() {
    // Swap buffers: current -> next, next -> current (Effectively swapping roles)
    [nextBufferA, bufferA] = [bufferA, nextBufferA];
    [nextBufferB, bufferB] = [bufferB, nextBufferB];

    for (let y = 1; y < N - 1; y++) {
        for (let x = 1; x < N - 1; x++) {
            const i = y * N + x;
            
            // Calculate Laplacian for A and B (using current values)
            const laplacianA = diffLaplacian(bufferA, x, y);
            const laplacianB = diffLaplacian(bufferB, x, y);

            // Reaction-Diffusion Equations:
            // d/dt A = DA * Laplacian(A) - A*B^2 + k*(1-A)
            let nextA = bufferA[i] + (DA * laplacianA - bufferA[i] * bufferB[i] * bufferB[i] + FEED_RATE * (1.0 - bufferA[i])) * 0.2;

            // d/dt B = DB * Laplacian(B) + A*B^2 - (k+m)*B
            let nextB = bufferB[i] + (DB * laplacianB + bufferA[i] * bufferB[i] * bufferB[i] - (FEED_RATE + KILL_RATE) * bufferB[i]) * 0.2;

            // Clamping and storing
            nextBufferA[i] = Math.max(0, Math.min(1, nextA));
            nextBufferB[i] = Math.max(0, Math.min(1, nextB));
        }
    }
    
    // Copy the calculated 'next' values back to the primary buffers for the next iteration
    for (let i = 0; i < N * N; i++) {
        bufferA[i] = nextBufferA[i];
        bufferB[i] = nextBufferB[i];
    }
}

function draw() {
    const imageData = ctx.getImageData(0, 0, N, N);
    const data = imageData.data;

    for (let y = 0; y < N; y++) {
        for (let x = 0; x < N; x++) {
            const i = y * N + x;
            // Normalize values for color mapping
            const a = bufferA[i];
            const b = bufferB[i];

            // Color map: A -> Red/Green, B -> Blue. Gradient mix for coral effect.
            let r = Math.floor(a * 255);
            let g = Math.floor(a * 100 + b * 150); // Green component depends on both
            let b_color = Math.floor(b * 255);

            const offset = (y * N + x) * 4;
            data[offset] = r;       // Red
            data[offset + 1] = g;   // Green
            data[offset + 2] = b_color; // Blue
            data[offset + 3] = 255; // Alpha (Opaque)
        }
    }

    ctx.putImageData(imageData, 0, 0);
}

function loop() {
    step();
    draw();
    document.querySelector('body').innerHTML = `<canvas id="c" width="${N*3}" height="${N*3}" style="display:block;">${""}</canvas><div style="color:#fff;font-family:monospace;margin-top:10px;">Steps: ${stepCount++}</div>`;
    requestAnimationFrame(loop);
}

initialize();
</script></body></html>
```
MODEL Egemma4:26bDNF
No usable answer reached the harness (TIMEOUT). Counted as a did-not-finish.
MODEL Fllama3.1:8bDNF
No usable answer reached the harness (TIMEOUT). Counted as a did-not-finish.
MODEL Gqwen3-coder:30bERRBLANKFORMAT
Show code
```html
<!DOCTYPE html>
<html><head><title>Gray-Scott Simulation</title></head><body style="background:#000;margin:0;"><canvas id="c"></canvas><script>
const c = document.getElementById('c');
const ctx = c.getContext('2d');

let W = 160; // Width
let H = 160; // Height (Choosing a power of two or easy number for buffer management)
let GRID_SIZE = W; 

c.width = W * 2; // Drawing resolution double the logical grid size for better display
c.height = H * 2;

const A = new Float32Array(W * H);
const B = new Float32Array(W * H);
let A_next = new Float32Array(W * H);
let B_next = new Float32Array(W * H);

// Simulation Parameters (Gray-Scott standard values)
const DA = 1.0; // Diffusion rate of A
const DB = 0.5; // Diffusion rate of B
const FEED = 0.055; // Feed rate
const KILL = 0.062; // Kill rate

let stepCount = 0;

function init() {
    // Initialize with random or seeded pattern (e.g., a central blob)
    for(let i=0; i < W*H; ++i) {
        A[i] = 1.0; // Initial A concentration
        B[i] = 0.0; // Initial B concentration
    }

    // Seed the pattern (center square blob, easier to control than pure random noise)
    let cx = Math.floor(W/3);
    let cy = Math.floor(H/3);
    for(let y=cy-10; y<cy+10; ++y) {
        for(let x=cx-10; x<cx+10; ++x) {
            if (x >= 0 && x < W && y >= 0 && y < H) {
                const i = y * W + x;
                A[i] = 0.8;
                B[i] = 0.9;
            }
        }
    }
}

function calculateLaplacian(arr, x, y, width) {
    let sum = 0;
    // Simple stencil: center - (neighbors * 1/4)
    sum += arr[(y-1)*width + x]; // Top
    sum += arr[(y+1)*width + x]; // Bottom
    sum += arr[y*width + (x-1)]; // Left
    sum += arr[y*width + (x+1)]; // Right
    return sum - 4 * arr[y * width + x];
}

function step() {
    // Swap buffers: A/B -> A_next/B_next
    [A, A_next] = [A_next, A];
    [B, B_next] = [B_next, B];
    
    // Use temporary storage for the new values before assignment
    for(let i=0; i < W*H; ++i) {
        A_next[i] = 0.0;
        B_next[i] = 0.0;
    }

    for (let y = 1; y < H - 1; y++) {
        for (let x = 1; x < W - 1; x++) {
            const i = y * W + x;
            
            // Calculate Laplacian for A and B at current position (using old values)
            let lapA = calculateLaplacian(A, x, y, W);
            let lapB = calculateLaplacian(B, x, y, W);

            const a = A[i];
            const b = B[i];

            // Gray-Scott Reaction Diffusion Equations:
            // 1. Diffusive terms (using previous step's values)
            // 2. Reaction term (-a*b^2 + k*(1-a)) is simplified/standardized below.
            
            const reaction = a * b * b; // The consumption rate
            
            // A_next = DA * Laplacian(A) - (a*b^2 + FEED*A)
            let nextA = DA * lapA - (reaction + FEED * a);

            // B_next = DB * Laplacian(B) + (a*b^2 - KILL*B)
            let nextB = DB * lapB + (reaction - KILL * b);


            A_next[i] = Math.min(1, Math.max(0, nextA)); // Clamp [0, 1]
            B_next[i] = Math.min(1, Math.max(0, nextB)); // Clamp [0, 1]
        }
    }

    // Copy the calculated next state back into the primary buffers for the next iteration
    for (let i = 0; i < W * H; ++i) {
        A[i] = A_next[i];
        B[i] = B_next[i];
    }

    stepCount++;
}

function draw() {
    const imageData = ctx.getImageData(0, 0, W * 2, H * 2);
    const data = imageData.data;
    let offset = 0;

    for (let y = 0; y < H; y++) {
        for (let x = 0; x < W; x++) {
            // Map normalized [0, 1] values to color components
            const a_val = A[y * W + x];
            const b_val = B[y * W + x];

            // Color mapping: (A, B) -> RGB. Example pattern: Blue/Cyan based on B, Green based on A.
            let r = Math.floor(a_val * 255);
            let g = Math.floor(b_val * 100 + a_val * 155);
            let b = Math.floor((1 - b_val) * 200);

            // Index in ImageData array (x, y, x+1, y, ...)
            data[offset++] = r;     // R
            data[offset++] = g;     // G
            data[offset++] = b;     // B
            data[offset++] = 255;   // A (Opacity)
        }
    }

    ctx.putImageData(imageData, 0, 0);
}

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

init();
animate();
</script></body></html>
```
MODEL Hqwen3:8bDNF
No usable answer reached the harness (ERR). Counted as a did-not-finish.
Reveal the models— compare the answers first, then open. Names, blind scores and latencies are hidden until you do.

Who wrote what

Blind labelModelBlind scoreLatencyStatus
MODEL A qwen3:14b 28.4 sOK
MODEL B mistral-small:24b DNF
MODEL C deepseek-r1:14b DNF
MODEL D qwen3.5:9b 26.5 sOK
MODEL E gemma4:26b harness
MODEL F llama3.1:8b DNF
MODEL G qwen3-coder:30b 25.4 sOK
MODEL H qwen3:8b harness