OverviewCode › R71
Code · blind round

R71 · Flood fill on a pixel grid

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.

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.

The reveal also holds a frontier reference on the same task (Claude Opus 5) — kept out of sight here so it cannot steer your judgement.

Show task
Create ONE self-contained HTML file (STRICT LIMIT: max 120 lines total) that animates a flood fill on a 60x40 grid of coloured cells drawn on a <canvas>. Generate a few random blobs of colour, pick a start cell, and expand the fill one frontier step at a time so the growing region is clearly visible. Show the number of cells filled and the current frontier size. 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
No usable answer reached the harness (TIMEOUT). Counted as a did-not-finish.
MODEL Bqwen3:8bDNF
No usable answer reached the harness (ERR). Counted as a did-not-finish.
MODEL Cqwen3:14bSTATLIMFORMAT
Show code
```html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Flood Fill Animation</title>
    <style>body { background: #111; display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; flex-direction: column;} canvas { border: 2px solid #444; }</style>
</head>
<body>
    <canvas id="grid"></canvas>
    <div id="stats">Filled Cells: 0 | Frontier Size: 0</div>

    <script>
        const canvas = document.getElementById('grid');
        const ctx = canvas.getContext('2d');
        const GRID_W = 60;
        const GRID_H = 40;
        canvas.width = GRID_W * 8;
        canvas.height = GRID_H * 8;

        let grid = [];
        let startX, startY;
        let queue = [];
        let isAnimating = false;

        function initializeGrid() {
            grid = Array(GRID_H).fill(0).map(() => Array(GRID_W).fill(null));
            for (let y = 0; y < GRID_H; y++) {
                for (let x = 0; x < GRID_W; x++) {
                    grid[y][x] = '#000'; // Default background color
                }
            }

            const blobsToDraw = Math.floor(Math.random() * 3) + 2;
            for (let i = 0; i < blobsToDraw; i++) {
                let startX_b = Math.floor(Math.random() * GRID_W);
                let startY_b = Math.floor(Math.random() * GRID_H);
                let color = `hsl(${Math.random() * 360}, 70%, ${Math.random() * 40 + 50}%)`;
                const blobSize = Math.max(1, Math.floor(Math.random() * 8) + 2);

                for (let dy = -blobSize; dy <= blobSize; dy++) {
                    for (let dx = -blobSize; dx <= blobSize; dx++) {
                        const x = startX_b + dx;
                        const y = startY_b + dy;

                        if (x >= 0 && x < GRID_W && y >= 0 && y < GRID_H) {
                            grid[y][x] = color;
                        }
                    }
                }
            }

            // Pick a random starting cell that is not the background color
            do {
                startX = Math.floor(Math.random() * GRID_W);
                startY = Math.floor(Math.random() * GRID_H);
            } while (grid[startY][startX] === null || grid[startY][startX].startsWith('#000'));

            queue = [ { x: startX, y: startY } ];
        }

        function drawGrid() {
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            for (let y = 0; y < GRID_H; y++) {
                for (let x = 0; x < GRID_W; x++) {
                    ctx.fillStyle = grid[y][x];
                    ctx.fillRect(x * 8, y * 8, 8, 8);
                }
            }
        }

        function animateStep() {
            if (queue.length === 0) {
                isAnimating = false;
                document.getElementById('stats').innerHTML = 'Flood Fill Complete!';
                setTimeout(() => { initializeGrid(); requestAnimationFrame(gameLoop); }, 1500);
                return;
            }

            const nextQueue = [];
            let frontierCoords = [];
            let filledCount = 0;

            // Process the current queue (the cells to fill in this step)
            for (let i = 0; i < queue.length; i++) {
                const { x, y } = queue[i];
                if (!grid[y][x].startsWith('#FILL')) {
                    grid[y][x] = '#FILL'; // Mark as filled
                    filledCount++;
                }

                // Check neighbors for the new frontier
                const neighbors = [[0, 1], [0, -1], [1, 0], [-1, 0]];
                for (let [dx, dy] of neighbors) {
                    const nx = x + dx;
                    const ny = y + dy;

                    if (nx >= 0 && nx < GRID_W && ny >= 0 && ny < GRID_H && grid[ny][nx].startsWith('#BLOB') && !grid[ny][nx].startsWith('#FILL')) {
                        frontierCoords.push({ x: nx, y: ny });
                    }
                }
            }

            // Update queue and draw
            queue = frontierCoords;
            drawGrid();

            const frontierSize = queue.length;
            document.getElementById('stats').innerHTML = `Filled Cells: ${filledCount} | Frontier Size: ${frontierSize}`;

            if (isAnimating) {
                requestAnimationFrame(gameLoop);
            }
        }

        function gameLoop() {
            animateStep();
        }

        // Start the animation immediately
        initializeGrid();
        requestAnimationFrame(gameLoop);
    </script>
</body>
</html>
```
MODEL Dqwen3.5:9bSTATLIMFORMAT
Show code
```html
<!DOCTYPE html>
<html>
<head>
<title>Flood Fill Animation</title>
<style>body {background:#111;display:flex;justify-content:center;align-items:center;min-height:100vh;margin:0;color:white;}canvas {border:2px solid #333;}</style>
</head>
<body>
<canvas id="canvas" width="60"*50" height="40"*50"></canvas>

<script>
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
let GRID_W = 60;
let GRID_H = 40;
let CELL_SIZE = 50 / 60; // Scale down slightly for fit

// Grid state: 0=Empty, 1=Target Color, 2=Filled
let grid = Array(GRID_H).fill(0).map(() => Array(GRID_W).fill(0));
let colorGrid = Array(GRID_H).fill(0).map(() => Array(GRID_W).fill(null));

function randomColor() {
    const letters = '0123456789ABCDEF';
    let color = '#';
    for (let i = 0; i < 6; i++) {
        color += letters[Math.floor(Math.random() * 16)];
    }
    return color;
}

function initializeGrid() {
    grid = Array(GRID_H).fill(0).map(() => Array(GRID_W).fill(0));
    colorGrid = Array(GRID_H).fill(0).map(() => Array(GRID_W).fill(null));
    const numBlobs = 5;
    let filledCount = 0;

    for (let i = 0; i < numBlobs; i++) {
        let startX = Math.floor(Math.random() * GRID_W);
        let startY = Math.floor(Math.random() * GRID_H);
        let color = randomColor();

        for (let y = 0; y < GRID_H; y++) {
            for (let x = 0; x < GRID_W; x++) {
                if (!colorGrid[y][x] && Math.random() > 0.8) {
                    colorGrid[y][x] = color;
                    grid[y][x] = 1; // Mark as colored cell
                }
            }
        }
    }
    // Select a target starting point (a random colored cell)
    let startY, startX;
    do {
        startY = Math.floor(Math.random() * GRID_H);
        startX = Math.floor(Math.random() * GRID_W);
    } while (!colorGrid[startY][startX]);

    return { startX, startY, targetColor: colorGrid[startY][startX] };
}

let state = initializeGrid();
let isRunning = true;

function drawGrid(filledCells) {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    for (let y = 0; y < GRID_H; y++) {
        for (let x = 0; x < GRID_W; x++) {
            const color = colorGrid[y][x];
            ctx.fillStyle = color || '#111';
            ctx.fillRect(x * CELL_SIZE, y * CELL_SIZE, CELL_SIZE - 0.5, CELL_SIZE - 0.5);

            if (grid[y][x] === 2) { // Filled state indicator
                ctx.strokeStyle = 'rgba(0, 150, 255, 0.8)';
                ctx.lineWidth = 3;
                ctx.strokeRect(x * CELL_SIZE + 1, y * CELL_SIZE + 1, CELL_SIZE - 2, CELL_SIZE - 2);
            }
        }
    }

    // Draw the current frontier (cells about to be filled)
    if (filledCells && filledCells.length > 0) {
        ctx.fillStyle = 'rgba(0, 255, 0, 0.6)';
        for (const [y, x] of filledCells) {
             ctx.fillRect(x * CELL_SIZE + 1, y * CELL_SIZE + 1, CELL_SIZE - 2, CELL_SIZE - 2);
        }
    }
}

function animateFill() {
    if (!isRunning) return;

    let startY = state.startY;
    let startX = state.startX;
    const targetColor = state.targetColor;
    const frontier = new Set(); // Stores [y, x] coordinates for the next step
    const visited = new Set(); // Tracks cells already in queue or processed

    // Initial setup: Queue for BFS/Frontier search
    let currentQueue = [[startY, startX]];
    frontier.add([startY, startX]);
    visited.add(`${startY},${startX}`);
    
    // Optimization: Instead of full BFS every step, we maintain the frontier boundary explicitly
    // To keep complexity down for line count, we use a simplified queue-based expansion.

    let currentFrontier = [[startY, startX]];
    let filledCount = 1;
    let maxSteps = GRID_W * GRID_H * 0.2; // Limit steps to prevent infinite loop/slowness

    // We need a loop structure that processes step-by-step and updates the state object
    if (state._currentStep === undefined) {
        state._currentQueue = [[startY, startX]];
        state._visited = new Set([`${startY},${startX}`]);
        state._fillCount = 1;
    }

    let queue = state._currentQueue || [];
    let newlyFilledFrontier = [];
    
    if (queue.length > 0) {
        const nextQueue = [];
        
        for (const [y, x] of queue) {
            // Check neighbors (N, S, E, W)
            [[0, 1], [0, -1], [1, 0], [-1, 0]].forEach(([dy, dx]) => {
                let ny = y + dy;
                let nx = x + dx;

                if (ny >= 0 && ny < GRID_H && nx >= 0 && nx < GRID_W) {
                    const key = `${ny},${nx}`;
                    // Check if it matches the target color, is not filled, and hasn't been processed
                    if (!state._visited.has(key) && colorGrid[ny][nx] === targetColor) {
                        newlyFilledFrontier.push([ny, nx]);
                        nextQueue.push([ny, nx]);
                        state._visited.add(key);
                    }
                }
            });
        }
        
        if (newlyFilledFrontier.length > 0) {
            // Process the step: Change state and update metrics
            const currentNeighbors = newlyFilledFrontier;
            
            currentNeighbors.forEach(([y, x]) => {
                grid[y][x] = 2; // Mark as filled
            });

            state._fillCount += newlyFilledFrontier.length;
            state._currentQueue = nextQueue;

            // Redraw with the new frontier
            drawGrid(newlyFilledFrontier);

            document.getElementById('stats').innerHTML = `Cells: ${state._fillCount} | Frontier: ${newlyFilledFrontier.length}`;
        } else {
             // Fill finished
             isRunning = false;
             document.getElementById('stats').innerHTML = `Finished! Total Cells Filled: ${state._fillCount}`;
        }
    }

    if (isRunning) {
        requestAnimationFrame(animateFill);
    }
}


// Initialization and setup for the first run/restart
function startAnimation() {
    const statsDiv = document.createElement('div');
    statsDiv.id = 'stats';
    statsDiv.style.position = 'absolute';
    statsDiv.style.top = '20px';
    statsDiv.style.left = '50%';
    statsDiv.style.transform = 'translate(-50%, 0)';
    statsDiv.style.fontSize = '24px';
    document.body.appendChild(statsDiv);

    // Reset state for fresh run
    state = initializeGrid();
    state._currentQueue = [];
    state._visited = new Set();
    state._fillCount = 0;

    drawGrid(null); // Clear and draw initial colors
    document.getElementById('stats').innerText = "Starting Animation...";
    animateFill();
}

startAnimation();
</script>

<div id="stats" style="position: absolute; top: 20px; left: 50%; transform: translate(-50%, 0); font-size: 24px;"></div>

</body>
</html>
```
MODEL Ellama3.1:8bDNF
No usable answer reached the harness (TIMEOUT). Counted as a did-not-finish.
MODEL Fqwen3-coder:30bSTATLIMFORMAT
Show code
```html
<!DOCTYPE html>
<html>
<head>
<title>Flood Fill Animation</title>
<style>
    body { background-color: #111; display: flex; justify-content: center; align-items: center; min-height: 100vh; margin: 0; }
    canvas { border: 1px solid #333; image-rendering: pixelated; }
    #info { color: white; font-family: monospace; position: absolute; top: 20px; left: 50%; transform: translateX(-50%); background: rgba(0,0,0,0.6); padding: 10px; border-radius: 5px;}
</style>
</head>
<body>

<canvas id="gameCanvas" width="600" height="400"></canvas>
<div id="info">Cells Filled: 0 | Frontier Size: 0</div>

<script>
    const canvas = document.getElementById('gameCanvas');
    const ctx = canvas.getContext('2d');
    const infoDiv = document.getElementById('info');

    let GRID_WIDTH = 60;
    let GRID_HEIGHT = 40;
    let CELL_SIZE = 10;
    let grid = [];
    
    // Color constants
    const BACKGROUND_COLOR = [20, 20, 30]; // Dark background
    const BLOB_COLORS = ["#FF6B6B", "#4ECDC4", "#5F9EA0", "#8D6E63", "#A1C4FD"];

    let currentGridState;
    let startX, startY, targetColor;
    let queue = [];
    let running = false;
    let filledCount = 0;

    function initializeGrid() {
        grid = new Array(GRID_WIDTH).fill(0).map(() => new Array(GRID_HEIGHT).fill(null));

        // Initialize background (simulate an empty canvas)
        for (let x = 0; x < GRID_WIDTH; x++) {
            for (let y = 0; y < GRID_HEIGHT; y++) {
                grid[x][y] = BACKGROUND_COLOR.join(',');
            }
        }

        // Generate random blobs and set target color
        const blobCount = Math.floor(Math.random() * 3) + 2;
        let potentialTargetColor = null;

        for (let i = 0; i < blobCount; i++) {
            let startX_blob = Math.floor(Math.random() * GRID_WIDTH);
            let startY_blob = Math.floor(Math.random() * GRID_HEIGHT);
            let color = BLOB_COLORS[Math.floor(Math.random() * 5)];

            if (!potentialTargetColor) potentialTargetColor = color;

            // Draw a blob (filling connected area with the same random color)
            const tempBlobGrid = JSON.parse(JSON.stringify(grid));
            let queueBlob = [[startX_blob, startY_blob]];
            tempBlobGrid[startX_blob][startY_blob] = color;
            
            while (queueBlob.length > 0) {
                const [x, y] = queueBlob.shift();

                [[x + 1, y], [x - 1, y], [x, y + 1], [x, y - 1]].forEach(([nx, ny]) => {
                    if (nx >= 0 && nx < GRID_WIDTH && ny >= 0 && ny < GRID_HEIGHT && tempBlobGrid[nx][ny] === null) {
                        tempBlobGrid[nx][ny] = color;
                        queueBlob.push([nx, ny]);
                    }
                });
            }

            // Merge the blob into the main grid
            for (let x = 0; x < GRID_WIDTH; x++) {
                for (let y = 0; y < GRID_HEIGHT; y++) {
                    if (tempBlobGrid[x][y] !== null) {
                        grid[x][y] = tempBlobGrid[x][y];
                    }
                }
            }
        }

        // Select starting point and target color
        startX = Math.floor(Math.random() * GRID_WIDTH);
        startY = Math.floor(Math.random() * GRID_HEIGHT);
        targetColor = grid[startX][startY];

        // Reset state
        currentGridState = JSON.parse(JSON.stringify(grid));
        queue = [];
        filledCount = 0;
        running = true;
    }

    function drawGrid() {
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        for (let x = 0; x < GRID_WIDTH; x++) {
            for (let y = 0; y < GRID_HEIGHT; y++) {
                const color = currentGridState[x][y];
                ctx.fillStyle = color;
                ctx.fillRect(x * CELL_SIZE, y * CELL_SIZE, CELL_SIZE, CELL_SIZE);
            }
        }
    }

    function updateCanvas() {
        // Draw the underlying grid state first
        drawGrid(); 
        
        // Highlight the current frontier cells (the queue)
        if (queue.length > 0) {
             ctx.strokeStyle = 'rgba(255, 255, 0, 0.8)';
             ctx.lineWidth = 3;
             for (const [x, y] of queue) {
                 ctx.strokeRect(x * CELL_SIZE + 1, y * CELL_SIZE + 1, CELL_SIZE - 2, CELL_SIZE - 2);
             }
        }

        // Update stats display
        infoDiv.innerHTML = `Cells Filled: ${filledCount} | Frontier Size: ${queue.length}`;
    }

    function findFrontier() {
        if (queue.length === 0) return [];
        let frontier = [];
        for(const [x, y] of queue) {
            [[x + 1, y], [x - 1, y], [x, y + 1], [x, y - 1]].forEach(([nx, ny]) => {
                if (nx >= 0 && nx < GRID_WIDTH && ny >= 0 && ny < GRID_HEIGHT) {
                    // Check if it's the target color and hasn't been processed/queued already
                    const cell = grid[nx][ny];
                    if (cell === targetColor && !queue.some(q => q[0] === nx && q[1] === ny)) {
                        frontier.push([nx, ny]);
                    }
                }
            });
        }
        return frontier;
    }

    function stepFloodFill() {
        if (!running) return;

        // 1. Update the queue with all potential next cells (the true frontier)
        let potentialFrontier = [];
        for(const [x, y] of queue) {
            [[x + 1, y], [x - 1, y], [x, y + 1], [x, y - 1]].forEach(([nx, ny]) => {
                if (nx >= 0 && nx < GRID_WIDTH && ny >= 0 && ny < GRID_HEIGHT) {
                    const cell = grid[nx][ny];
                    // Only add valid target cells that are not already in the queue or filled
                    if (cell === targetColor && !queue.some(q => q[0] === nx && q[1] === ny)) {
                        potentialFrontier.push([nx, ny]);
                    }
                }
            });
        }

        // 2. Select a random cell from the frontier to fill (simulating guided growth)
        if (potentialFrontier.length === 0) {
             running = false; // Finished filling
             return;
        }
        const randomIndex = Math.floor(Math.random() * potentialFrontier.length);
        const [nextX, nextY] = potentialFrontier[randomIndex];

        // 3. Process the step: Fill and update queue/count
        if (currentGridState[nextX][nextY] === targetColor) {
            // Update state
            currentGridState[nextX][nextY] = '#00FFFF'; // Filled color
            filledCount++;

            // Add neighbors of the newly filled cell to the queue for the next step's frontier calculation
            [[nextX + 1, nextY], [nextX - 1, nextY], [nextX, nextY + 1], [nextX, nextY - 1]].forEach(([nx, ny]) => {
                if (nx >= 0 && nx < GRID_WIDTH && ny >= 0 && ny < GRID_HEIGHT) {
                    const neighborColor = grid[nx][ny];
                    // Only add viable candidates to the queue
                    if (neighborColor === targetColor && !queue.some(q => q[0] === nx && q[1] === ny)) {
                        queue.push([nx, ny]);
                    }
                }
            });

        } else {
             // Should not happen if logic is correct, but handles safety break
             running = false; 
             return;
        }
    }

    function animate() {
        if (running) {
            stepFloodFill();
            updateCanvas();
        } else {
            // Restart sequence when finished
            setTimeout(() => {
                initializeGrid();
                animate();
            }, 500); 
        }

        if (running) {
             requestAnimationFrame(animate);
        }
    }

    window.onload = () => {
        initializeGrid();
        // Initial draw before the animation starts
        drawGrid();
        updateCanvas();
        animate();
    };

</script>
</body>
</html>
```
MODEL Gdeepseek-r1:14bDNF
No usable answer reached the harness (TIMEOUT). Counted as a did-not-finish.
MODEL Hgemma4:26bDNF
No usable answer reached the harness (TIMEOUT). 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 mistral-small:24b DNF
MODEL B qwen3:8b harness
MODEL C qwen3:14b 19.4 sOK
MODEL D qwen3.5:9b 33.3 sOK
MODEL E llama3.1:8b DNF
MODEL F qwen3-coder:30b 34.6 sOK
MODEL G deepseek-r1:14b DNF
MODEL H gemma4:26b harness

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>Flood Fill — 60×40</title>
<style>
html,body{height:100%;margin:0;background:#080b12;color:#dbe4f0;font:14px/1.4 ui-monospace,SFMono-Regular,Menlo,monospace;display:flex;align-items:center;justify-content:center}
.wrap{display:flex;flex-direction:column;gap:10px;padding:16px;max-width:100%}
canvas{background:#05070c;border-radius:8px;box-shadow:0 0 0 1px #1d2635,0 18px 50px -20px #000;width:100%;height:auto;image-rendering:pixelated}
.hud{display:flex;flex-wrap:wrap;gap:8px}
.chip{background:#111827;border:1px solid #202b3d;border-radius:6px;padding:6px 10px;letter-spacing:.04em}
.chip b{color:#7dd3fc;font-weight:600}
.chip.f b{color:#fff}
</style>
</head>
<body>
<div class="wrap">
<canvas id="c" width="960" height="640"></canvas>
<div class="hud">
<div class="chip">filled <b id="a">0</b></div>
<div class="chip f">frontier <b id="b">0</b></div>
<div class="chip">step <b id="d">0</b></div>
<div class="chip">region <b id="e">0</b></div>
</div>
</div>
<script>
const W=60,H=40,S=16,c=document.getElementById('c'),x=c.getContext('2d');
const base=document.createElement('canvas');base.width=c.width;base.height=c.height;
const bx=base.getContext('2d');
const PAL=['#16233a','#22405c','#2e6f7a','#3f8f6b','#7fae4b','#c9a227','#c86b3c','#a03e5a','#5b3a7e','#3d5a8c'];
const A=document.getElementById('a'),B=document.getElementById('b'),D=document.getElementById('d'),E=document.getElementById('e');
let grid,seen,depth,frontier,filled,step,total,done,hold,acc,last=0;
function region(s,mark){
  const q=[s],v=new Uint8Array(W*H);v[s]=1;let n=0,col=grid[s];
  while(q.length){const p=q.pop();n++;if(mark)mark[p]=1;
    const i=p%W,j=(p/W)|0;
    if(i>0&&!v[p-1]&&grid[p-1]===col){v[p-1]=1;q.push(p-1)}
    if(i<W-1&&!v[p+1]&&grid[p+1]===col){v[p+1]=1;q.push(p+1)}
    if(j>0&&!v[p-W]&&grid[p-W]===col){v[p-W]=1;q.push(p-W)}
    if(j<H-1&&!v[p+W]&&grid[p+W]===col){v[p+W]=1;q.push(p+W)}}
  return n;
}
function gen(){
  const n=7+((Math.random()*5)|0),seeds=[];
  for(let i=0;i<n;i++)seeds.push({x:Math.random()*W,y:Math.random()*H,c:PAL[(Math.random()*PAL.length)|0],
    w:.65+Math.random()*.9,f:2+((Math.random()*4)|0),p:Math.random()*6.283,a:.14+Math.random()*.3});
  grid=new Array(W*H);
  for(let j=0;j<H;j++)for(let i=0;i<W;i++){
    let bd=1e9,bc=PAL[0];
    for(const s of seeds){
      const dx=i+.5-s.x,dy=j+.5-s.y;
      const d=Math.hypot(dx,dy)*(1+s.a*Math.sin(Math.atan2(dy,dx)*s.f+s.p))/s.w;
      if(d<bd){bd=d;bc=s.c}}
    grid[j*W+i]=bc;
  }
  bx.fillStyle='#05070c';bx.fillRect(0,0,base.width,base.height);
  for(let j=0;j<H;j++)for(let i=0;i<W;i++){bx.fillStyle=grid[j*W+i];bx.fillRect(i*S,j*S,S-1,S-1)}
  let start=0,bestN=-1;
  for(let t=0;t<28;t++){
    const s=(Math.random()*W*H)|0,sz=region(s);
    if(sz>bestN){bestN=sz;start=s}
    if(bestN>=260)break;
  }
  seen=new Uint8Array(W*H);depth=new Int16Array(W*H).fill(-1);
  seen[start]=1;depth[start]=0;frontier=[start];filled=1;step=0;total=bestN;done=false;hold=0;acc=0;
}
function advance(){
  const col=grid[frontier[0]],next=[];
  for(const p of frontier){
    const i=p%W,j=(p/W)|0,nb=[];
    if(i>0)nb.push(p-1);if(i<W-1)nb.push(p+1);if(j>0)nb.push(p-W);if(j<H-1)nb.push(p+W);
    for(const q of nb)if(!seen[q]&&grid[q]===col){seen[q]=1;depth[q]=step+1;next.push(q);filled++}
  }
  frontier=next;step++;
  if(!frontier.length)done=true;
}
function frame(t){
  const dt=Math.min(80,t-last);last=t;
  if(done){hold+=dt;if(hold>1000)gen()}
  else{acc+=dt;while(acc>=42&&!done){acc-=42;advance()}}
  x.drawImage(base,0,0);
  for(let p=0;p<W*H;p++){
    if(!seen[p])continue;
    const i=p%W,j=(p/W)|0,d=depth[p];
    x.fillStyle='hsl('+((196+d*4+t*.02)%360)+' 92% '+(48+8*Math.sin(d*.4+t*.003))+'%)';
    x.fillRect(i*S,j*S,S-1,S-1);
  }
  x.fillStyle='#fff';
  for(const p of frontier){const i=p%W,j=(p/W)|0;x.fillRect(i*S,j*S,S-1,S-1)}
  x.fillStyle='rgba(255,255,255,.28)';
  for(const p of frontier){const i=p%W,j=(p/W)|0;x.fillRect(i*S-2,j*S-2,S+3,S+3)}
  A.textContent=filled;B.textContent=frontier.length;D.textContent=step;E.textContent=total;
  requestAnimationFrame(frame);
}
gen();requestAnimationFrame(frame);
</script>
</body>
</html>