Overview › Code › R98
Code · blind round

R98 · Squarified treemap of changing data

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.

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 draws a squarified treemap on a <canvas>: split a set of about 20 weighted items into nested rectangles that fill the whole area, choosing the split direction so the rectangles stay close to square rather than becoming thin slivers. Let the weights drift slowly over time and animate each rectangle towards its new position and size instead of jumping. Label the largest rectangles with their share. 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:14bERRBLANKFORMAT
Show code
```html
<!DOCTYPE html>
<html>
<body style="background:#1a1a1a;margin:0;display:flex;justify-content:center;height:100vh">
<canvas id=c width=800 height=600></canvas>
<style>rect{fill:#fff;border:1px solid #333}.label{position:absolute;background:rgba(255,255,255,0.7);padding:4px}</style>
<script>
const canvas = document.getElementById('c'), ctx = canvas.getContext('2d');
const rects = [];
let labels = [], animating = true;

class Treemap {
  constructor() {this.update();}
  
  update() {
    const data = Array(20).fill().map(() => ({size: Math.random(), id: Date.now()})).sort((a,b) => b.size - a.size);
    this.data = data;
    
    const getNode = (x1, y1, x2, y2, items) => {
      if(items.length === 1) return {x1,y1,x2,y2,size:items[0].size,id:items[0].id};
      
      let dx = x2 - x1;
      let dy = y2 - y1;
      const isSquare = Math.min(dx, dy)/Math.max(dx, dy);
      const splitX = (x1 + (x2 - x1) * 0.5).toFixed(2), splitY = (y1 + (y2 - y1)*0.5).toFixed(2);
      
      if(isSquare > 0.8 || Math.random() < 0.3) {
        const mid = items.reduce((a,b)=>a+b.size)/items.length;
        const split = items.findIndex((_,i) => (data.slice(0,i+1).reduce((a,c)=>
          a + c.size,0)) > mid);
        
        return {x1,y1,x2,y2,
                left: getNode(x1,y1,x2*(split/(split+1)), y2, items.slice(0,split+1)),
                right:getNode(x2*(split/(split+1)),y1,x2,y2, items.slice(split+1))};
      } else {
        return {x1,y1,x2,y2,
                top: getNode(x1,y1,x2,y2*0.5, items.slice(0,items.length/2)),
                bottom:getNode(x1,y2*0.5,x2,y2, items.slice(items.length/2))};
      }
    };
    
    const root = getNode(0, 0, canvas.width, canvas.height, data);
    this.root = root;
    this.prevRoot = null;
  }
}

let treemap = new Treemap();

function animate(timestamp) {
  if(!animating) {treemap.update(); animating=true; return;}
  
  ctx.clearRect(0,0,canvas.width,canvas.height);
  
  const interpolate = (a,b,t) => a + (b - a)*t;
  
  if(treemap.prevRoot === null) treemap.prevRoot = {...treemap.root};
  
  let nodes = [];
  
  function traverse(node, fn) {
    fn(node);
    if('left' in node) {
      traverse(node.left, fn);
      traverse(node.right, fn);
    }
  }
  
  traverse(treemap.root, n => nodes.push(n));
  traverse(treemap.prevRoot, n => {
    const prev = {...n};
    Object.assign(n, nodes.find(nn => nn.id === prev.id));
  });
  
  function renderRect(node) {
    if(!node.x1 || !node.y1) return;
    
    let rect = {x:node.x1, y:node.y1, w:node.x2 - node.x1, h:node.y2 - node.y1};
    const target = node === treemap.root ? 
      {x:0,y:0,w:canvas.width,h:canvas.height} : 
      (node.parent.left || node.parent.right) ?
        {x:node.parent.x1 + (node.parent.left ? 0 : node.parent.w * 
          (node.parent.right ? (node.parent.right.size / (node.parent.left.size + 
            node.parent.right.size)) : 1)), y:node.parent.y1, w:node.parent.w * 
          (node.parent.left ? (node.parent.left.size / (node.parent.left.size + 
            node.parent.right.size)) : 1), h:node.parent.h} : {x:node.x1,y:node.y1,w:node.x2 - node.x1,h:node.y2 - node.y1};
    
    rect = {
      x:interpolate(rect.x, target.x, timestamp / (50 * 1e3)),
      y:interpolate(rect.y, target.y, timestamp / (50 * 1e3)),
      w:interpolate(rect.w, target.w, timestamp / (50 * 1e3)),
      h:interpolate(rect.h, target.h, timestamp / (50 * 1e3))
    };
    
    if(node.left) {
      const midX = rect.x + rect.w * ((node.left.size)/(node.left.size + node.right.size));
      renderRect(node.left);
      renderRect(node.right);
    } else {
      ctx.fillRect(rect.x, rect.y, rect.w, rect.h);
      
      if(!treemap.prevRoot) labels.push({
        id: node.id,
        x:rect.x + 5,
        y:rect.y + 5,
        w:rect.w -10,
        h:rect.h -10,
        share:(node.size * 100 / data.reduce((a,c)=>a+c.size,0)).toFixed(2)
      });
    }
  }
  
  if(treemap.prevRoot) {
    renderRect(treemap.root);
    
    // Update labels
    const newLabels = [];
    for(let label of treemap.root.left ? [treemap.root] : []) 
      if(label.size > 0.15 && !labels.find(l => l.id == label.id)) 
        newLabels.push({...label});
    labels = labels.map(l => {
      const targetLabel = newLabels.find(l2 => l2.id == l.id);
      return {
        x:interpolate(l.x, targetLabel?.x || l.x, timestamp / (50 * 1e3)),
        y:interpolate(l.y, targetLabel?.y || l.y, timestamp / (50 * 1e3)),
        w:l.w,
        h:l.h,
        share:l.share
      };
    });
    
    // Draw labels
    ctx.fillStyle = 'white';
    labels.forEach(label => {
      if(label) {
        ctx.fillRect(
          label.x - label.w/2,
          label.y - label.h/4,
          label.w + 10,
          label.h / 2
        );
        ctx.fillStyle = '#000';
        ctx.font = '12px sans-serif';
        ctx.fillText(label.share+'%', label.x - label.w/2, label.y);
      }
    });
    
    animating = timestamp > 5e4;
    if(animating) treemap.prevRoot = null;
  } else {
    treemap.update();
    animating = false;
  }
  
  requestAnimationFrame(timestamp => animate(timestamp));
}

animate(Date.now());
</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.
llama3.1:8bOKFORMAT
Show code
```html
<!DOCTYPE html>
<html>
<head>
<style>
canvas {
  border: 1px solid black;
}
</style>
</head>
<body>
<script>
let canvas = document.createElement('canvas');
document.body.appendChild(canvas);
let ctx = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;

let items = Array(20).fill().map(() => ({ name: Math.random().toString(36), weight: Math.random() }));
let sizes = items.map(() => [0, 0]);
let positions = items.map(() => [Math.random() * canvas.width, Math.random() * canvas.height]);

function anim(timestamp) {
  requestAnimationFrame(anim);
  ctx.clearRect(0, 0, canvas.width, canvas.height);

  let totalWeight = items.reduce((a, b) => a + b.weight, 0);
  let itemWidth = canvas.width / (totalWeight ** 0.5);
  let itemHeight = canvas.height / (totalWeight ** 0.5);

  for (let i = 0; i < items.length; ++i) {
    let w = sizes[i][0];
    let h = sizes[i][1];
    let x = positions[i][0];
    let y = positions[i][1];

    ctx.fillStyle = `hsl(${Math.floor(Math.random() * 360)}, 50%, 50%)`;
    ctx.fillRect(x, y, w, h);
    ctx.font = '10pt Arial';
    ctx.textAlign = 'left';
    ctx.textBaseline = 'top';
    let share = (items[i].weight / totalWeight) * 100;
    ctx.fillText(`${share.toFixed(2)}%`, x + 5, y + 5);

    if (i > 0 && i < items.length - 1) {
      let prevX = positions[i-1][0] + sizes[i-1][0];
      let nextX = positions[i+1][0];

      if (Math.abs(prevX - x) < Math.abs(nextX - x)) {
        // Split horizontally
        w = itemWidth;
        h = Math.max(h, Math.min(sizes[i+1][1], y + h));
        ctx.fillRect(x, Math.floor(y), 2, h);
      } else {
        // Split vertically
        w = Math.max(w, Math.min(sizes[i+1][0], x + w));
        h = itemHeight;
        ctx.fillRect(Math.floor(x), y, w, 2);
      }
    }

    sizes[i] = [w, h];
    let dx = (Math.random() - 0.5) * 10;
    let dy = (Math.random() - 0.5) * 10;
    positions[i][0] += dx;
    positions[i][1] += dy;

    if (positions[i][0] < 0 || positions[i][0] > canvas.width)
      positions[i][0] = Math.max(0, Math.min(canvas.width, positions[i][0]));
    if (positions[i][1] < 0 || positions[i][1] > canvas.height)
      positions[i][1] = Math.max(0, Math.min(canvas.height, positions[i][1]));

    items[i].weight = Math.random();
    sizes[i] = [0, 0];
  }
}

anim();

// Reset when finished
let intervalId = setInterval(() => {
  let newItems = Array(20).fill().map(() => ({ name: Math.random().toString(36), weight: Math.random() }));
  items.forEach((item, i) => item.weight = newItems[i].weight);
  items = newItems;
}, 30000);

// Start animation on page load
</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.
mistral-small:24bERRBLANKMINIFIEDFORMAT
Show code
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"><meta name=viewport content="width=device-width,user-scalable=no"><title>Squarified Treemap</title><style>*{margin:0;padding:0}body{display:flex;justify-content:center;align-items:center;height:100vh;background:#f0f0f0;font-family:Arial,sans-serif}#canvas{border:2px solid black}</style>
</head>
<body>
<canvas id=canvas width=800 height=600></canvas><script>const canvas=document.getElementById('canvas'); const ctx=canvas.getContext('2d'); let items=[...Array(20)].map(()=>({w:Math.random()*10+5,label:''})); function draw(){ctx.clearRect(0,0,canvas.width,canvas.height);items.forEach(drawItem);}function drawItem(item,x=0,y=0,w,h){if(w>h){const a=[],b[]; for(let i=0;i<w;++i)a[i]=item.w*Math.random();for(let i=0;i<h;++i)b[i]=item.w*(1-Math.random()); const k=a.reduce((p,c)=>c+p,p=>Math.min(p,...b));const idx=k>=h?a.indexOf(Math.max(...a)):b.indexOf(Math.min(...b));w-k>h?(item.label=Math.round(item.w/items.reduce((s,i)=>s+i.w,0)*100)+'%',drawItem({...item,w:k},x,y,k,h),drawItem({w:h,w},x+y+h,y,w-k)):(item.label='',drawItem({...item,h:w},x,y,w,h)},item.label&&ctx.fillText(item.label,x+5,y+15)); function treemap(){let rects=items.map((item,i)=>({x:0,y:0,width:canvas.width,height:canvas.height,label:0})),nextRect=(rect,items)=>{const w=rect.width,h=rect.height,wg=w/h>h/w?w:h,wh=w/h<h/w?h:w,xs=y=>{const yp=[],xp=[],n=Math.random()*100|0;for(let i=n;i<n+10;++i)yp.push(w*i/n),xp.push(h*(items[i%n].w/items[n].w));return Math.max(...yp.map((y,x)=>w*x*h-y));}; return wg>wh?(nextRect({...rect,width:xs(rect.x)},items.slice(0,n)),[nextRect({...rect,width:w-xs(rect.x),x:x},items.slice(n))]):(nextRect({...rect,height:xs(rect.y)},items.slice(0,n)),[nextRect({...rect,height:h-xs(rect.y),y:s},items.slice(n))]);}; let newRects=[];for(let i of rects){newRects.push(...nextRect(i,items));}return newRects;} function animate(){requestAnimationFrame(animate);ctx.save();ctx.translate(canvas.width/2,canvas.height/2);draw();ctx.restore();const speed=0.1;let newItems=[...items];for(let i of items){newItems.push({w:i.w+(Math.random()-0.5)*speed});}items=newItems;if(items.some(i=>i.w<0||i.w>30)){items=[...Array(20)].map(()=>({w:Math.random()*10+5,label:''}));treemap();}}animate();

</script>
</body></html>
```
qwen3:14bSTATLIM
Show code
<!DOCTYPE html>
<html>
<head><title>Animated Treemap</title></head>
<style>body{margin:0;background:#222;display:flex;justify-content:center;align-items:center;height:100vh}canvas{border:1px solid #444}</style>
<script>
const canvas = document.createElement('canvas');
document.body.appendChild(canvas);
const ctx = canvas.getContext('2d');

let items = Array(20).fill(0).map(() => Math.random() * 10 + 5);
let targetLayout = [];
let currentRects = [];
let animationProgress = {};

// Initialize canvas size
canvas.width = window.innerWidth * 0.9;
canvas.height = window.innerHeight * 0.8;

function initializeState() {
    items = Array(20).fill(0).map(() => Math.random() * 10 + 5);
    targetLayout = [];
    currentRects = [];
    animationProgress = {};
}

// Core Treemap Algorithm (Simplified Best-Fit Tiling)
function calculateTreemap(weights, parentX, parentY, parentW, parentH, index = null) {
    if (!weights || weights.length === 0) return;

    let currentArea = [];
    for (let i = 0; i < weights.length; i++) {
        currentArea.push({ w: weights[i], id: i });
    }

    function pack(rects, x, y, w, h) {
        if (rects.length === 0) return [];
        if (rects.length === 1) return [{ w: rects[0].w, h: rects[0].h, x: x, y: y }];

        let bestSplit = null;
        let minAspectRatioPenalty = Infinity;
        const totalWeight = rects.reduce((sum, r) => sum + r.w, 0);
        
        // Try horizontal splits (iterate through all possible cuts)
        for (let i = 1; i < rects.length; i++) {
            const leftWeights = rects.slice(0, i).map(r => r.w);
            const rightWeights = rects.slice(i).map(r => r.w);

            // Calculate ideal widths based on weight proportion
            const totalW_L = leftWeights.reduce((a, b) => a + b, 0);
            const totalW_R = rightWeights.reduce((a, b) => a + b, 0);
            
            let splitPenalty;
            if (totalW_L === 0 || totalW_R === 0) {
                splitPenalty = Infinity; continue;
            }

            // Simple proxy for squarification: check aspect ratios of the two resulting containers.
            const targetH1 = h * (totalW_L / (totalW_L + totalW_R));
            const targetH2 = h * (totalW_R / (totalW_L + totalW_R));

            // Penalty based on how far the sub-rectangles are from square aspect ratio 1.0
            splitPenalty = Math.abs(Math.sqrt(targetH1 * w) - 1) + Math.abs(Math.sqrt(targetH2 * w) - 1);

            if (splitPenalty < minAspectRatioPenalty) {
                minAspectRatioPenalty = splitPenalty;
                bestSplit = { type: 'vertical', index: i, leftWeights: [...leftWeights], rightWeights: [...rightWeights] };
            }
        }


        // Try vertical splits (iterate through all possible cuts) - omitted for brevity/complexity constraint.
        // For simplicity and character limit, we rely on a greedy recursive split based on weight order.

        if (!bestSplit) { 
             // Fallback: just stack them vertically if no good horizontal cut found? No, stick to the best-fit logic structure.
             // Since full optimal squarification is too complex for tight limits, we use the single best vertical split found above.
        }


        let finalLayout = [];
        if (bestSplit) {
            const nextRects = [...bestSplit.leftWeights, ...bestSplit.rightWeights];

            // Calculate dimensions based on weights ratio
            const totalW = rects.reduce((a, b) => a + b, 0);
            
            // Recursive call: The width is determined by the sum of weights relative to the total weight * W.
            function recurse(weights, parentX, parentY, parentW, parentH) {
                if (weights.length === 1) {
                    const w = weights[0];
                    return [{ w: w, h: parentH, x: parentX, y: parentY }];
                }

                // Find best split again for the sub-problem? Too slow/complex. 
                // Instead, just use the proportional division of the current container (W, H).
                let bestSplitIndex = -1;
                let minPenalty = Infinity;
                const N = weights.length;

                for(let i = 1; i < N; i++) {
                    const totalWeightL = weights.slice(0, i).reduce((a, b) => a + b, 0);
                    const totalWeightR = weights.slice(i).reduce((a, b) => a + b, 0);

                    if (totalWeightL === 0 || totalWeightR === 0) continue;

                    // Calculate resulting dimensions if split here:
                    const targetW1 = parentW * (totalWeightL / (totalWeightL + totalWeightR));
                    const targetW2 = parentW * (totalWeightR / (totalWeightL + totalWeightR));

                    // Penalty calculation based on square aspect ratio deviation
                    let penalty = Math.abs(Math.sqrt(targetW1 * parentH) - 1) + Math.abs(Math.sqrt(targetW2 * parentH) - 1);

                    if (penalty < minPenalty) {
                        minPenalty = penalty;
                        bestSplitIndex = i;
                    }
                }


                if (bestSplitIndex === -1) return []; // Should not happen if weights > 1
                
                const nextWeights = [...weights.slice(0, bestSplitIndex), ...weights.slice(bestSplitIndex)];

                // Recurse on the two halves
                const leftRects = recurse(nextWeights.slice(0, bestSplitIndex), parentX, parentY, 
                    parentW * (nextWeights.slice(0, bestSplitIndex).reduce((a, b) => a + b, 0) / totalWeight), 
                    parentH);

                const rightRects = recurse(nextWeights.slice(bestSplitIndex), parentX + leftRects[leftRects.length - 1].w, parentY, 
                    parentW * (nextWeights.slice(bestSplitIndex).reduce((a, b) => a + b, 0) / totalWeight), 
                    parentH);
                
                return [...leftRects, ...rightRects];
            }

            // Start the recursion from placing all weights in one container (W=canvas.width, H=canvas.height)
            finalLayout = recurse(weights, parentX, parentY, parentW, parentH);
        }

        return finalLayout;
    }

    // Since the iterative/recursive logic is too long for character limit while being robust, 
    // we use a simplified proportional tiling that approximates squarification by splitting based on weight ratios.
    function simpleTiling(weights, parentX, parentY, parentW, parentH) {
        let currentWeights = [...weights];
        let rects = [];

        while (currentWeights.length > 0) {
            const totalWeight = currentWeights.reduce((a, b) => a + b, 0);
            if (totalWeight === 0) break;

            // For simplicity, we always try to split the remaining area proportional to weights
            let remainingW = parentW;
            let remainingH = parentH;
            let currentX = parentX;
            let currentY = parentY;

            if (currentWeights.length === 1) {
                const w = currentWeights[0];
                rects.push({ w: w, h: remainingH, x: parentX, y: parentY });
                break;
            }

            // Use a simple iterative packing approach (stacking horizontally until next split is needed)
            let tempRects = [];
            let currentHeight = 0;

            for(let i = 0; i < currentWeights.length; i++) {
                const w_i = currentWeights[i];
                if (i === 0) {
                    tempRects.push({w: w_i, h: remainingH});
                    currentHeight = remainingH;
                } else {
                    // Attempt to stack side by side until aspect ratio degrades too much? Too complex.
                    // Fallback to sequential horizontal filling based on weight proportion.
                    tempRects.push({w: w_i, h: remainingH}); 
                }
            }

            let cumulativeW = currentWeights.reduce((a, b) => a + b, 0);
            if (cumulativeW > 0 && Math.abs(1 - (canvas.width / canvas.height * totalWeight)) < 0.2) {
                 // If the overall bounding box is somewhat square, just proportional width split works best.
                let x_offset = parentX;
                for(let i=0; i < currentWeights.length; i++) {
                    const w = currentWeights[i];
                    rects.push({w: w * (parentW / totalWeight), h: parentH, x: x_offset, y: parentY});
                    x_offset += w * (parentW / totalWeight);
                }
            } else {
                // Fallback to simple proportional tiling
                 let x_offset = parentX;
                 for(let i=0; i < currentWeights.length; i++) {
                     const w = currentWeights[i];
                     rects.push({w: w * (parentW / totalWeight), h: parentH, x: x_offset, y: parentY});
                     x_offset += w * (parentW / totalWeight);
                 }
            }

        }
        return rects;
    }

    // Use the simplified tiling method that guarantees filling and is simple enough for constraints.
    targetLayout = simpleTiling(weights, 0, 0, canvas.width, canvas.height);
}


function updateWeights() {
    items = items.map(w => Math.max(1, w + (Math.random() - 0.5) * 0.2));
}

// Find the index of the rectangle corresponding to item 'i' in the targetLayout
function findIndexForWeight(weightArray, i) {
    for (let j = 0; j < weightArray.length; j++) {
        const r = targetLayout[j];
        if (Math.abs(r.w - weightArray[i] * (canvas.width / items.reduce((a, b) => a + b, 0))) < 1) {
            return j;
        }
    }
    //
qwen3-coder:30bERRBLANKLIMFORMAT
Show code
```html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Animated Squarified Treemap</title>
    <style>
        body { margin: 0; background: #111; display: flex; justify-content: center; align-items: center; height: 100vh; }
        canvas { border: 1px solid #333; }
    </style>
</head>
<body>
    <canvas id="treemapCanvas"></canvas>

    <script>
        const canvas = document.getElementById('treemapCanvas');
        const ctx = canvas.getContext('2d');
        let W, H;
        canvas.width = 800;
        canvas.height = 600;
        W = canvas.width;
        H = canvas.height;

        // --- Data Structure and Initialization ---
        let items = [];
        let treemapNodes = [];
        let animationState = {};
        const NUM_ITEMS = 20;
        const ANIMATION_SPEED = 0.05;
        const DAMPING = 0.95;

        function initializeItems() {
            items = Array(NUM_ITEMS).fill(0).map(() => ({ name: 'Item', weight: Math.random() * 0.1 + 0.5 }));
            // Ensure total weight is manageable and non-zero
            let totalWeight = items.reduce((sum, item) => sum + item.weight, 0);
            items.forEach(item => item.weight *= (totalWeight / parseFloat(totalWeight.toFixed(2)) || 1));
        }

        function calculateTreemap(parentWidth, parentHeight, nodes) {
            if (!nodes || nodes.length === 0) return [];

            // Sort by weight descending for better visualization
            const sortedNodes = [...nodes].sort((a, b) => b.weight - a.weight);
            let remainingW = parentWidth;
            let remainingH = parentHeight;
            let currentX = 0;
            let currentY = 0;

            // Simple horizontal packing approximation for complexity reduction while maintaining squarification goal
            const results = [];

            for (const node of sortedNodes) {
                if (node.weight === 0) continue;
                
                // Calculate ideal size based on weight proportion
                let targetW = parentWidth * Math.sqrt(node.weight / totalWeight);
                let targetH = parentHeight * Math.sqrt(node.weight / totalWeight);

                // Adjusting for practical layout (simple side-by-side packing)
                let actualW = Math.max(50, node.weight * 10 + 20); 
                let actualH = Math.min(parentHeight - 40, parentHeight * node.weight / 10);

                // Simple heuristic to keep aspect ratio close (prioritize filling the space)
                if ((remainingW/actualW < remainingH/actualH) && (remainingW > 50)) {
                    actualH = Math.min(parentHeight - 40, actualW * (parentHeight / parentWidth));
                    actualW = remainingW;
                } else {
                     actualW = Math.min(parentWidth - 40, remainingW);
                     actualH = node.weight * 15 + 20;
                }

                let x = currentX;
                let y = currentY;
                let w = actualW;
                let h = actualH;

                if (x + w > parentWidth || y + h > parentHeight) continue;
                
                results.push({ x: x, y: y, w: w, h: h, weight: node.weight, name: node.name });

                currentX += w;
                remainingW -= w;
            }
            return results;
        }


        function initializeTreemap() {
            initializeItems();
            const totalWeight = items.reduce((sum, item) => sum + item.weight, 0);
            
            // Calculate target positions (The treemaps are drawn relative to the whole canvas area)
            treemapNodes = calculateTreemap(W, H, items);

            // Initialize animation state: Target state matches calculated treemap, current state starts at zero
            animationState.targets = treemapNodes;
            animationState.current = treemapNodes.map(node => ({ 
                x: W * (Math.random() - 0.5) * 0.2, 
                y: H * (Math.random() - 0.5) * 0.2, 
                w: Math.max(10, node.w / 4), 
                h: Math.max(10, node.h / 4) 
            }));

            animationState.startTime = performance.now();
        }


        // --- Animation and Drawing ---

        function updateAnimation(dt) {
            const current = animationState.current;
            const targets = animationState.targets;

            for (let i = 0; i < treemapNodes.length; i++) {
                if (!current[i]) continue;
                const target = targets[i];
                const node = current[i];

                // Linear interpolation for smooth movement towards the target state
                node.x += (target.x - node.x) * ANIMATION_SPEED * dt;
                node.y += (target.y - node.y) * ANIMATION_SPEED * dt;
                node.w += (target.w - node.w) * ANIMATION_SPEED * dt;
                node.h += (target.h - node.h) * ANIMATION_SPEED * dt;

                // Apply damping to stabilize overshoot
                node.x = node.x * DAMPING + target.x * (1 - DAMPING);
                node.y = node.y * DAMPING + target.y * (1 - DAMPING);
                node.w *= DAMPING + ANIMATION_SPEED;
                node.h *= DAMPING + ANIMATION_SPEED;
            }

            // Check if animation is stable enough to re-initialize weights/treemap
            if (current.every((c, i) => Math.abs(c.x - targets[i].x) < 1 && Math.abs(c.w - targets[i].w) < 1)) {
                // Animation complete: Drift weights and restart the process
                driftWeights();
                initializeTreemap(); // Re-calculates new target coordinates
            }

        }

        function driftWeights() {
             items = items.map(item => ({ name: item.name, weight: Math.max(0.1, item.weight + (Math.random() - 0.5) * 0.02) }));
        }


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

            // Draw rectangles and labels
            for (let i = 0; i < treemapNodes.length; i++) {
                const node = animationState.current[i];
                const targetNode = animationState.targets[i];
                
                // Color based on weight/index
                ctx.fillStyle = `hsl(${i * (360 / treemapNodes.length)}, 70%, ${Math.min(50, node.w/(W*1.2) * 80 + 40)}%)`;
                ctx.fillRect(node.x, node.y, node.w, node.h);

                // Labeling the largest rectangles by share percentage
                const totalWeight = items.reduce((sum, item) => sum + item.weight, 0);
                let labelText = '';
                if (targetNode && targetNode.weight / totalWeight > 0.15) {
                    const share = Math.min(10, (targetNode.weight / totalWeight) * 100).toFixed(1);
                    labelText = `${share}%`;
                }

                ctx.fillStyle = '#FFF';
                ctx.font = 'bold 14px sans-serif';
                ctx.fillText(labelText, node.x + 5, node.y + 20);
            }
        }

        let lastTime = 0;
        function loop(currentTime) {
            const dt = (currentTime - lastTime) / 16.66; // Normalize delta time relative to target 60fps frame
            lastTime = currentTime;

            updateAnimation(dt);
            drawTreemap();

            requestAnimationFrame(loop);
        }

        // Start the process
        initializeTreemap();
        requestAnimationFrame(loop);
    </script>
</body>
</html>
```
qwen3.5:9bERRBLANKLIMFORMAT
Show code
```html
<!DOCTYPE html>
<html>
<head>
<title>Animated Treemap</title>
<style>
    body { margin: 0; overflow: hidden; background-color: #f0f0f0; }
    canvas { display: block; }
</style>
</head>
<body>

<canvas id="treemapCanvas"></canvas>

<script>
(function() {
    const canvas = document.getElementById('treemapCanvas');
    const ctx = canvas.getContext('2d');

    let W, H;
    let items = [];
    let animationFrameId = null;
    let lastWeights = [];

    // --- Core Treemap Logic ---

    /** Calculates the optimal target dimensions for all weights recursively (Squarified layout) */
    function calculateLayout(weights, x0, y0, w0, h0) {
        if (weights.length === 0) return null;

        let totalWeight = weights.reduce((sum, w) => sum + w, 0);
        const rootItem = { targetW: w0, targetH: h0, targetX: x0, targetY: y0, weight: totalWeight, children: [], color: '#ccc', name: 'Root' };

        // Initial pass to assign weights/children (simulating the hierarchy)
        for(let i = 0; i < weights.length; i++) {
            const w = weights[i];
            rootItem.children.push({ weight: w, name: `Item ${i+1}` });
        }

        // Recursive function to perform squarification and calculate target dimensions
        function subdivide(parent, currentW, currentH) {
            if (parent.children.length === 0 || !currentW || !currentH) return;

            let remainingArea = [{ w: currentW, h: currentH, weight: parent.weight }];
            
            // Simple greedy squarification approximation for demonstration purposes
            // For N items, we iterate until the area is filled.
            
            const childrenWeights = parent.children.map(c => c.weight);

            let remainingRects = [{ x: 0, y: 0, w: currentW, h: currentH, totalWeight: parent.weight }];
            let placedItems = [];
            let cursor = 0;

            while (cursor < childrenWeights.length) {
                const nextChild = parent.children[cursor];
                if (!nextChild || nextChild.weight === 0) {
                    cursor++; continue;
                }

                // Find the best fit remaining rectangle to place the current child
                let targetRectIndex = -1;
                let maxAreaFit = -Infinity;

                for (let i = 0; i < remainingRects.length; i++) {
                    const rect = remainingRects[i];
                    // Try placing it either horizontally or vertically in this spot
                    if (rect.w * nextChild.weight / parent.weight > maxAreaFit) {
                         maxAreaFit = rect.w * nextChild.weight / parent.weight;
                         targetRectIndex = i;
                    }
                }

                const targetRect = remainingRects[targetRectIndex];
                if (!targetRect) break;

                // Calculate the ideal split dimensions based on weights and current area ratio
                let newW, newH;

                // Assuming we are splitting along the longest axis of the available space
                if (Math.abs(targetRect.w / targetRect.h) > 1.2) { // Wider than tall -> Split vertically
                    newW = Math.max(0.1, nextChild.weight * targetRect.w / parent.weight);
                    newH = targetRect.h;
                } else { // Taller or square -> Split horizontally
                    newW = targetRect.w;
                    newH = Math.max(0.1, nextChild.weight * targetRect.h / parent.weight);
                }

                // The actual placement coordinates and dimensions
                const itemTarget = { 
                    targetX: targetRect.x + (Math.abs(targetRect.w / targetRect.h) > 1.2 ? remainingRects[0].w * (nextChild.weight / parent.weight) : 0),
                    targetY: targetRect.y + (Math.abs(targetRect.w / targetRect.h) > 1.2 ? 0 : remainingRects[0].h * (nextChild.weight / parent.weight)),
                    targetW: newW, 
                    targetH: newH, 
                    weight: nextChild.weight
                };

                // Update the available space list (This is highly simplified and assumes a perfect fill)
                remainingRects[targetRectIndex] = { x: itemTarget.targetX, y: itemTarget.targetY, w: itemTarget.targetW, h: itemTarget.targetH, totalWeight: 0 };

                // Move to the next child (this simplified structure only handles one level of nesting)
                placedItems.push(itemTarget);
            }

            return placedItems;
        }

        const targets = subdivide(rootItem, w0, h0);
        
        items = [];
        targets.forEach((t, i) => {
            items.push({ 
                targetW: t.targetW, targetH: t.targetH, 
                targetX: t.targetX, targetY: t.targetY, 
                weight: t.weight, name: `Item ${i+1}`, color: `hsl(${i * 360 / targets.length}, 70%, 55%)` 
            });
        });

        // Initialize current state to match target state for the first run
        items.forEach(item => {
             item.currentW = item.targetW; 
             item.currentH = item.targetH; 
             item.currentX = item.targetX; 
             item.currentY = item.targetY;
        });

    }


    /** Generates initial random weights and triggers the layout calculation */
    function initializeWeights() {
        const numItems = 20;
        let newWeights = [];
        // Ensure total sum is manageable (e.g., around 100)
        let sum = Math.random(5) + 7; 

        for (let i = 0; i < numItems; i++) {
            newWeights.push(Math.max(0.1, Math.random() * 0.3));
            sum += newWeights[i];
        }
        lastWeights = [...newWeights];
        calculateLayout(newWeights);
    }

    /** Updates weights with a smooth random drift */
    function updateWeights() {
        const factor = 0.95; // Dampening factor
        let newWeights = lastWeights.map((w, i) => 
            Math.max(0.1, w * factor + (Math.random() - 0.5) * 0.02)
        );

        // Normalize weights slightly to prevent total collapse/explosion
        const totalNew = newWeights.reduce((a, b) => a + b, 0);
        newWeights = newWeights.map(w => w / totalNew * (1 + Math.random() * 0.05)); // Keep scale stable

        lastWeights = newWeights;
        calculateLayout(newWeights);
    }


    // --- Animation and Drawing Loop ---

    const DAMPING_FACTOR = 0.12; // Controls speed of transition (lower is slower)
    let startTime = Date.now();

    function animate() {
        const elapsed = Math.min(1, (Date.now() - startTime) / 300); // Cap animation duration at 300ms
        startTime = Date.now();

        // 1. Update current positions towards target
        items.forEach(item => {
            item.currentX += (item.targetX - item.currentX) * DAMPING_FACTOR;
            item.currentY += (item.targetY - item.currentY) * DAMPING_FACTOR;
            item.currentW += (item.targetW - item.currentW) * DAMPING_FACTOR;
            item.currentH += (item.targetH - item.currentH) * DAMPING_FACTOR;
        });

        // 2. Clear and Draw Canvas
        ctx.clearRect(0, 0, W, H);
        items.forEach(item => {
            ctx.fillStyle = item.color;
            ctx.fillRect(item.currentX, item.currentY, item.currentW, item.currentH);

            // Draw border and label
            ctx.strokeStyle = '#333';
            ctx.lineWidth = 0.5;
            ctx.strokeRect(item.currentX, item.currentY, item.currentW, item.currentH);

            const labelShare = ((item.weight / lastWeights.reduce((a, b) => a + b, 0)) * 100).toFixed(1);
            ctx.fillStyle = '#ffffff';
            ctx.font = 'bold 12px Arial';
            ctx.fillText(`${labelShare}%`, item.currentX + 5, item.currentY + 18);
        });

        // Check if animation is finished (within tolerance)
        let stable = true;
        for (const item of items) {
             if (Math.abs(item.targetW - item.currentW) > 0.5 || Math.abs(item.targetH - item.currentH) > 0.5) {
                stable = false;
                break;
            }
        }

        // Schedule next frame
        requestAnimationFrame(function() {
            if (stable && elapsed >= 1) {
                updateWeights(); // Trigger new layout and drift
            } else {
                animate();
            }
        });
    }

    // --- Initialization ---
    window.addEventListener('resize', () => {
        W = canvas.width = window.innerWidth;
        H = canvas.height = window.innerHeight;
    });
    
    window.onload = function() {
        W = canvas.width = window.innerWidth;
        H = canvas.height = window.innerHeight;
        initializeWeights();
        animate();
    };

})();
</script>

</body>
</html>
```
command-r:35bERRBLANKFORMAT
Show code
```html
<!DOCTYPE html>
<html>
  <head>
    <style>
      canvas {
        border: 1px solid black;
      }
    </style>
  </head>
  <body>
    <canvas id="treemap"></canvas>
    <script>
      const ctx = document.getElementById("treemap").getContext("2d");

      class Node {
        constructor(val, x, y, w, h) {
          this.val = val;
          this.x = x;
          this.y = y;
          this.w = w;
          this.h = h;
          this.labelVisible = false;
        }

        draw() {
          ctx.fillStyle = "rgb(255, 100, 10)";
          ctx.fillRect(this.x - 0.5, this.y - 0.5, this.w + 1, this.h + 1);
        }

        animate(targetNode) {
          const distX = targetNode.x - this.x;
          const distY = targetNode.y - this.y;
          const distW = targetNode.w - this.w;
          const distH = targetNode.h - this.h;

          if (Math.abs(distX) < 1 && Math.abs(distY) < 1 && Math.abs(distW) < 1 && Math.abs(distH) < 1) {
            return false;
          }

          this.x += distX * 0.25;
          this.y += distY * 0.25;
          this.w += distW * 0.25;
          this.h += distH * 0.25;
          ctx.strokeStyle = "black";
          ctx.lineWidth = 1;
          ctx.strokeRect(this.x, this.y, this.w + 1, this.h + 1);

          if (Math.abs(distX) < 1 && Math.abs(distY) < 1) {
            this.labelVisible = true;

            const labelWidth = ctx.measureText(`${this.val}`);
            ctx.fillStyle = "white";
            ctx.strokeStyle = "black";
            if (this.w - 2 > labelWidth.width) {
              ctx.strokeRect(this.x + 1, this.y + 1, this.w - 2, this.h - 2);
              ctx.fillText(`${this.val}`, this.x + 1, this.y + this.h / 2);
            } else {
              this.labelVisible = false;
            }
          }

          return true;
        }
      }

      function squarify(valList, x, y, w, h) {
        const children = [];

        valList.sort((a, b) => b.weight - a.weight);
        let i = 0;
        while (i < valList.length) {
          const rowHeight = h * valList[i].weight / w;

          for (let j = 0; j < valList.length && children.length <= 30; ++j) {
            if (rowHeight === 0 || isNaN(rowHeight)) return children;

            children.push(new Node(valList[i].name, x, y + rowHeight * i++, w, rowHeight));
          }

          valList = valList.slice(j);
          h -= rowHeight * j;
        }

        for (const child of children) {
          if (child.h <= 30 || child.w > 65) continue;
          const subvals = [];
          let size = 0;

          for (let i = 0, val; val = valList[i]; ++i) {
            subvals.push(val);
            size += val.weight;
          }

          const targetNode = squarify(subvals, child.x + 20, child.y, child.w - 40, child.h / size * (size + child.h));
          for (const node of targetNode) {
            children.push(node);
          }
        }

        return children;
      }

      function randomize() {
        const valList = [];
        for (let i = 0; i < Math.random() * 25 + 13; ++i) {
          valList.push({name: `Item ${i}`, weight: (Math.random() - 0.4) * 8});
        }

        const width = ctx.canvas.width / window.devicePixelRatio, height = ctx.canvas.height / window.devicePixelRatio;
        return squarify(valList, 1, 1, width, height);
      }

      let nodes = randomize();

      function draw() {
        for (const node of nodes) {
          if (node.animate(node)) continue;

          node.draw();

          if (node.labelVisible) {
            ctx.fillStyle = "white";
            const labelWidth = ctx.measureText(`${node.val}`);
            ctx.strokeRect(node.x + 1, node.y + 1, node.w - 2, node.h - 2);
            ctx.fillText(`${node.val}`, node.x + 1, node.y + node.h / 2);
          }
        }
      }

      function animate() {
        nodes = randomize();

        for (const node of nodes) {
          node.labelVisible = false;
        }

        let animating = true;

        while (animating) {
          ctx.clearRect(0, 0, width / window.devicePixelRatio + 2, height / window.devicePixelRatio + 2);

          for (const node of nodes) {
            if (!node.animate(nodes.find((n) => n === node))) animating = false;

            node.draw();

            if (node.labelVisible && !isNaN(node.w)) {
              ctx.fillStyle = "white";
              const labelWidth = ctx.measureText(`${node.val}`);

              if (node.w - 2 > labelWidth.width) {
                ctx.strokeRect(node.x + 1, node.y + 1, node.w - 2, node.h - 2);
                ctx.fillText(`${node.val}`, node.x + 1, node.y + node.h / 2);
              }
            }
          }

          requestAnimationFrame(animate);
        }
      }

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

Okay, I need to create a self-contained HTML file that draws a squarified treemap on a canvas. The user specified that the rectangles should stay close to square, so I'll have to implement the squarified algorithm. The animation should smoothly transition the rectangles to their new positions and sizes as the weights drift over time. Also, the animation must use requestAnimationFrame, start immediately, and restart after finishing. Let me break down the steps.

First, I need to set up the HTML structure. The canvas element should be in the body with some styling to make it responsive or at least properly sized. Since there's no external library, everything has to be vanilla JavaScript.

The main challenge is the squarified treemap algorithm. I remember that squarified treemapping involves dividing the current rectangle into subrectangles in a way that minimizes aspect ratios. The algorithm works by selecting the split direction (vertical or horizontal) that keeps the rectangles as square as possible. The process is recursive: split the parent rectangle into a row or column of children, then each child is again split if needed.

I'll need a function to generate the rectangles based on the current weights. The weights should drift over time, so I can have a function that slowly adjusts the values each frame. Since the animation is smooth, the rectangles should interpolate between their current state and the target state.

For the animation loop, I'll use requestAnimationFrame. Each frame, I'll update the weights slightly, re-calculate the treemap layout based on the new weights, then interpolate between the old and new positions/sizes. The interpolation should be done using a damping factor to create smooth transitions.

Labeling the largest rectangles with their share: I need to determine which rectangles are the largest. Perhaps after calculating the tree, I can track the largest ones (maybe the top N by area) and display their percentage. The label text should be centered in each rectangle.

Let me outline the steps in code:

1. Initialize canvas and context.
2. Set up initial data: items with weights.
3. Create a Treemap class or function that handles the layout.
4. Implement the squarified algorithm for the Treemap.
5. Animate loop:
   a. Update weights (drift)
   b. Recalculate the treemap layout
   c. Interpolate current positions towards new positions
   d. Draw rectangles and labels
   e. Use requestAnimationFrame recursively.

Wait, but interpolation might be tricky. Maybe each frame, we can compute the new target positions based on current weights, then move towards that target over frames. Alternatively, we can have a current state and a target state, and each frame, blend between them.

Alternatively, each time the weights change, we compute the new layout and then animate each rectangle's properties from old to new over a certain duration. But since the weights are drifting continuously, maybe it's better to have the layout recomputed each frame with slightly updated weights, and then animate smoothly between frames' states.

Hmm, perhaps the simplest way is to compute the entire layout each frame with the current weights, then interpolate between the previous layout and the new one. But that might be computationally heavy. Alternatively, since the weights drift slowly, the changes between frames can be small, so interpolation can be done with a small step each frame.

Alternatively, the animation can be done by moving each rectangle's position and size towards the target calculated from the current weights. The target is updated each frame, and the current position is a weighted average between previous position and the new target.

Let me think of the data structure. Each rectangle (node) can have properties like x, y, width, height, and also target values (targetX, targetY, etc.). Each frame, we compute the new target based on the current weights, and then adjust the current values by moving 10% (or some damping factor) towards the target. That way, it smoothly transitions.

So the steps:

- Initialize rectangles with initial positions and sizes based on initial weights.
- In each frame:
   - Update weights slightly (e.g., add some random noise and normalize)
   - Recompute the layout to get new target positions and sizes for each rectangle.
   - For each rectangle, interpolate current position and size towards the target.
   - Draw the rectangles with their current interpolated attributes.
   - Check if the animation should restart (e.g., after some time), then reset weights and layout.

Wait, but how do I restart? Maybe after a certain number of frames? Or after the weights have drifted enough? The user says "restart with a fresh random instance when finished". So perhaps after a certain duration (like 5 seconds), reset everything.

Alternatively, the animation could run indefinitely, but the weights are drifting over time, so the treemap is always in motion. Maybe the user just wants it to loop indefinitely with smooth transitions.

Now, the squarified algorithm. Let me recall the steps for squarified treemapping:

The algorithm works by dividing the current rectangle into a row or column of subrectangles. For each subrectangle, the aspect ratio is considered. The goal is to choose the split direction (vertical or horizontal) so that the rectangles are as square as possible. The steps are something like:

Given a rectangle and a list of weights:

1. The rectangle is split into a row or column of children. The split direction is chosen such that the aspect ratio is minimized.
2. The children are arranged in the direction of the split, each with a width/height proportional to their weight.
3. The process is done recursively for each child until all weights are placed.

Alternatively, the squarified algorithm works by adding items to a row (or column) until adding another would make the aspect ratio worse than a new row. Here's a simplified version:

For a given rectangle and list of weights:

- Sort the weights in descending order (maybe? Or not sure, but order might matter).
- Decide the split direction (vertical or horizontal). The direction is chosen to minimize the aspect ratio.
- The split direction is determined by comparing the aspect ratios of the two possible splits (row vs column).
- Once the direction is chosen, the items are split into rows or columns.

Wait, maybe the algorithm works by starting with a list of weights and a rectangle. For each step:

- Find the best way to split the current rectangle into a row or column of rectangles, such that the aspect ratios are as close to 1 as possible.
- This is done by considering all possible splits (dividing the current rectangle into a certain number of children in one direction) and choosing the split that gives the least worst aspect ratio.

Alternatively, the standard squarified approach is to add items to a row until adding another item would exceed a threshold, then create a new row.

Hmm, perhaps looking up the algorithm steps would help. Since I can't look it up, I'll try to remember. The key idea is that for each step, you have a current row (or column) being built, and you keep adding items to that row. After each addition, you calculate the aspect ratio of the current row and decide whether to split here or keep adding. The split is made when the aspect ratio becomes worse than if you had split at this point.

Alternatively, here's a rough outline:

To create a row of rectangles with total area equal to the sum of their weights, but arranged to minimize aspect ratios:

1. The rectangle has a given width and height. The target is to split it into subrectangles with areas proportional to the weights.
2. The algorithm chooses between vertical or horizontal orientation (split direction) by computing which gives a better aspect ratio.
3. For each possible split direction, the algorithm divides the current rectangle into rows or columns such that each subrectangle's area is proportional to the weight.

Wait, perhaps the standard squarified approach works as follows (from Wikipedia or similar):

The goal is to partition a rectangle into smaller rectangles with areas proportional to given weights, such that the aspect ratio of all rectangles is as close to 1 as possible.

The algorithm for a single level (a single rectangle being split into a row or column):

1. The algorithm starts by sorting the weights in ascending or descending order (not sure).
2. The current rectangle is considered, and we decide to split it either vertically or horizontally. The direction is chosen such that the resulting rectangles have the least aspect ratio.
3. The split is into a row of rectangles along the chosen direction. The widths (or heights) of the rectangles are proportional to the weights, but arranged such that their aspect ratios are minimized.

Alternatively, here's a possible way to implement the split for a given rectangle and list of weights:

Function squarify(rect, weights):

   if sum(weights) == 0:
      return []

   total_weight = sum(weights)
   rect_area = rect.width * rect.height
   scale = rect_area / total_weight  # scaling factor to convert weights to areas

   # decide split direction (vertical or horizontal)
   # try both possibilities and choose the better one.

   # option 1: vertical split (rows)
   # each rectangle has height proportional to weight, width equal to rect.width
   # aspect ratio for each is (rect.width / (height_i)) where height_i = (weight_i * scale)^0.5 ?

   Wait, perhaps the algorithm proceeds by creating a row of rectangles in the current rectangle. The idea is to add items to the row until adding another would make the aspect ratio worse.

   Here's another approach from memory: 

   1. Sort the weights in descending order.
   2. The algorithm builds a row (or column) of rectangles. For each step, it considers adding the next weight to the current row, and computes the aspect ratio of the current row plus the new item. If adding it would make the aspect ratio worse, we split here and start a new row.

Wait, perhaps the algorithm works as follows:

To compute the rectangles in a row/column:

Initialize current_height = 0, current_width = 0 (depending on direction).

For each weight in the list:

   tentative_height = sqrt((current_height^2 * current_width) + weight) ?

Hmm, this is getting a bit vague. Maybe it's easier to look for a simple algorithm that can be coded quickly.

Alternatively, here's a possible approach for a single split (just the first level, then recurse):

Given a target rectangle and a list of items with weights, we need to split the rectangle into subrectangles with areas proportional to the weights. To choose the split direction (vertical or horizontal), we can compute which direction would give a better aspect ratio.

Let's denote the rectangle as rect (x,y,w,h). The total area is w*h. The sum of weights is S. Each item's area is (weight_i / S) * w*h.

So, if we split vertically (rows):

Each subrectangle's width is rect.w and height is (weight_i * rect.h) / S. The aspect ratio is width / height = w/( (weight_i * h)/S ). Since each rectangle's aspect ratio depends on its own area proportion.

Wait, but for squarified, we might need to arrange the items so that all of them have aspect ratios as close to 1 as possible. Alternatively, the algorithm first chooses between vertical or horizontal split, then arranges the items in rows or columns.

Perhaps the key is to compute for both options (vertical and horizontal) and pick the one with the minimal maximum aspect ratio.

Alternatively, perhaps the algorithm proceeds as follows for a single split:

Compute the total area needed for all items: T = sum(weights). The rectangle has area W*H. So scaling factor is scale = (W*H)/T.

For each item, area_i = weight_i * scale.

The items are arranged either in rows (vertical split) or columns (horizontal split). 

For vertical split:

Each item's width = W, height_i = sqrt(area_i) (if arranged in rows, then each has width W, height proportional to sqrt(weight_i)), but wait that might not add up.

Alternatively, arrange vertically so each item has the same width as the parent, and height proportional to their weight. The total height needed would be sum(height_i) = sum( sqrt(area_i) )? No, perhaps not.

Wait, perhaps arranging in rows (vertical direction):

Each rectangle's width is the same as the parent's width. The heights are proportional to their weight. The total height must equal the parent's height.

Thus, each height_i = (weight_i / T) * H.

The aspect ratio for each rectangle would be (width_i / height_i) = (W / ( (weight_i / T)*H )).

The aspect ratios are all W/H * T / weight_i. But this might vary a lot between rectangles.

Alternatively, the algorithm chooses the split direction (vertical or horizontal) that minimizes the maximum aspect ratio of the resulting rectangles.

To compute that:

Compute the possible aspect ratios for each direction:

For vertical split:

Each rectangle's aspect ratio is (rect.width) / (height_i) = rect.width / ( (weight_i * rect.height) / T )

The maximum aspect ratio would be the maximum over all items of (rect.width * T) / (weight_i * rect.height)

Alternatively, the aspect ratio can be calculated as width/height or height/width, whichever is larger. So the aspect ratio is max(w/h, h/w).

To find which split direction gives a better (smaller) maximum aspect ratio.

So to decide between horizontal and vertical splits:

Compute the aspect ratios for both options and choose the one with the minimum maximum aspect ratio.

Once the direction is chosen, then the rectangles are arranged in rows or columns accordingly.

Then, recursively apply the same process to each subrectangle.

This seems manageable.

So putting this into code:

First, structure the data as a tree. Each node has children, and each leaf node has a weight.

The root is the entire canvas.

The function to split a rectangle into children:

function split(rect, weights, minWeight) {
   // base case: if no children, return a single rectangle
   if (weights.length == 0) return [];
   const sum = weights.reduce((a, b) => a + b, 0);
   let bestDirection = 'vertical'; // start with some default
   let bestMaxAspect = Infinity;
   let aspectVertical = calculateAspectRatioIfSplitVertically(rect, weights);
   let aspectHorizontal = calculateAspectRatioIfSplitHorizontally(rect, weights);

   if (aspectVertical < aspectHorizontal) {
      bestDirection = 'vertical';
      bestMaxAspect = aspectVertical;
   } else {
      bestDirection = 'horizontal';
      bestMaxAspect = aspectHorizontal;
   }

   // proceed with bestDirection, split into children rectangles
   let children = [];
   if (bestDirection === 'vertical') {
      // split vertically into rows
      const totalH = rect.h;
      for (let i = 0; i < weights.length; i++) {
          const wgt = weights[i];
          const h = (wgt / sum) * rect.h;
          children.push({ 
              x: rect.x, 
              y: rect.y + prevY, 
              w: rect.w, 
              h: h, 
              // ... other properties
          });
          prevY += h;
      }
   } else {
      // split horizontally into columns
      const totalW = rect.w;
      for (let i =0; i< weights.length; i++) {
          const wgt = weights[i];
          const w = (wgt / sum)*rect.w;
          children.push({
              x: rect.x + prevX,
              y: rect.y,
              w: w,
              h: rect.h,
              //...
          });
          prevX += w;
      }
   }
   return children;
}

Wait, but this is a very simplified approach. The aspect ratio calculation needs to be more precise.

Alternatively, maybe to calculate the maximum aspect ratio for vertical split:

For vertical split (rows):

Each child's height is (weight_i / sum) * rect.h

Thus, the width of each rectangle is rect.w, height is as above.

Aspect ratio for each rectangle is (rect.w / ( (weight_i / sum)*rect.h )) 

The maximum of these would be the maximum aspect ratio.

Similarly for horizontal split: aspect ratio is ( (weight_i / sum)*rect.w ) / rect.h 

So the maximum for each direction can be computed.

Then choose the direction which has lower maximum aspect ratio.

Once the direction is chosen, the children are arranged accordingly.

But this is only for splitting into a single level. To get a full treemap, we need to recursively apply this process to each child until the weight is below a threshold.

Wait, but how do we know when to stop splitting? The user said "split a set of about 20 weighted items into nested rectangles". So the initial list has 20 items, which may get split into smaller groups, but ultimately, each item must have a rectangle. Wait no, the items are already the leaf nodes? Or each item is a weight that needs to be represented as a rectangle?

Actually, in a treemap, each item is a leaf node. So the algorithm needs to arrange all items into rectangles, possibly grouped into nested rectangles. So the initial split is for all items, but maybe some splits will create subgroups.

Wait, perhaps the algorithm is applied recursively. The initial rectangle contains all items. The split is done to divide them into groups, each group is a subrectangle, and then each group is split again into subgroups until each item is in its own rectangle.

Alternatively, perhaps each split divides the current group into a row or column of subgroups, each subgroup being a list of items. Each subgroup's weight is the sum of the weights in the subgroup. Then recursively split each subgroup into their own rectangles.

Hmm, maybe that's the way. Let me think:

We have a list of items with weights. The root rectangle is the entire canvas. The root's weight is the sum of all items. The first split divides the items into two or more subgroups, each of which becomes a child rectangle. Each subgroup is then split recursively until each subgroup has a single item.

Wait, but that might not be squarified. Alternatively, the algorithm for squarified treemapping builds rectangles in rows, combining items until adding another would make the aspect ratio worse.

Alternatively, the standard squarified algorithm is a row-based algorithm where items are arranged in rows, and each row is a group of items. The algorithm chooses where to split the items into rows to minimize the aspect ratio.

Perhaps implementing the row-based approach:

The algorithm starts with the list of items sorted by weight in descending order. Then it iterates through the items, adding them to the current row until adding the next item would make the aspect ratio worse than splitting here. Then a new row is started.

This is a greedy algorithm, but it can give a decent approximation.

To implement this:

Suppose the parent rectangle is rect (x,y,w,h). The items are sorted in descending order of weight.

Compute the total weight T = sum of all items.

The goal is to arrange the items in rows (vertical split) or columns (horizontal split), whichever is better, then arrange the items in that direction.

Alternatively, proceed with the following steps:

1. Sort the items in descending order of weight.

2. Compute the maximum possible aspect ratio for both vertical and horizontal splits.

3. Choose the direction with smaller maximum aspect ratio.

4. Based on the chosen direction, arrange the items into rows or columns.

Wait, but this is too vague.

Alternatively, let's look for a simple implementation of a row-based squarified algorithm.

Looking up pseudocode for a squarified treemap:

The algorithm for a single split (row):

1. Sort items in descending order of weight.

2. Initialize current row with first item.

3. For each subsequent item, compute the aspect ratio if added to current row, and compare to splitting here.

Wait, the exact steps are a bit involved. Since time is limited, perhaps a simplified approach can be used, where each split divides the items into two groups (or more?), but this might not be optimal.

Alternatively, for simplicity, the code can split the items into two groups each time, choosing the split direction that minimizes the aspect ratio.

Let me try to code this in JavaScript (since that's what you mentioned):

First, the root rectangle is the whole canvas.

function splitRecursive(rect, items) {
  if (items.length === 0) return []; // should not happen
  const sum = items.reduce((a, b) => a + b.weight, 0);
  // decide split direction
  let verticalAspect = Infinity;
  let horizontalAspect = Infinity;

  // compute if split vertically: each item's height is proportional to weight
  let maxHeight = 0;
  for (const item of items) {
    const h = (item.weight / sum) * rect.h;
    const aspect = rect.w / h;
    if (aspect > maxHeight) maxHeight = aspect;
  }
  verticalAspect = maxHeight;

  // compute if split horizontally: each item's width proportional to weight
  let maxWidth = 0;
  for (const item of items) {
    const w = (item.weight / sum) * rect.w;
    const aspect = w / rect.h;
    if (aspect > maxWidth) maxWidth = aspect;
  }
  horizontalAspect = maxWidth;

  let direction;
  if (verticalAspect < horizontalAspect) {
    direction = 'vertical';
  } else {
    direction = 'horizontal';
  }

  // now split into children
  const children = [];
  if (direction === 'vertical') {
    // each item gets a row
    let y = rect.y;
    for (const item of items) {
      const h = (item.weight / sum) * rect.h;
      const newRect = { x: rect.x, y: y, w: rect.w, h: h };
      children.push(newRect);
      y += h;
    }
  } else {
    let x = rect.x;
    for (const item of items) {
      const w = (item.weight / sum) * rect.w;
      const newRect = { x: x, y: rect.y, w: w, h: rect.h };
      children.push(newRect);
      x += w;
    }
  }

  // recursively split each child, but only if there are multiple items?
  // Wait, each child is assigned to a single item? No, need to clarify:

  Wait, in this approach, each item is given its own rectangle in the split direction. But that would just create a bar chart, which isn't a treemap. The treemap requires grouping items into subrectangles and nesting further.

Ah, right. The mistake here is that the algorithm should group items into subgroups (subrectangles) and each subgroup is further split. The pseudocode above assigns each item to a rectangle directly, which is incorrect.

Therefore, the correct approach is that in each split, the items are divided into subgroups, and each subgroup is represented as a rectangle which is then recursively split.

To do that properly, we need to decide how to partition the items into subgroups when splitting.

Perhaps, the simplest way is to split the items into two groups each time, choosing the split point that minimizes the aspect ratio.

But that might be computationally heavy.

Alternatively, use a greedy approach that creates rows of items, similar to the squarified algorithm.

Let me look for a simple pseudocode for a row-based squarified treemap:

1. Sort the items in descending order of weight.

2. Initialize a list of rows.

3. For each item, add it to the current row, and compute the aspect ratio. If the aspect ratio becomes too large, split the row here and start a new row.

Wait, perhaps:

Initialize an empty list of rows.

While there are items left:

   Start a new row.

   Add items to the row until adding another would cause the aspect ratio to exceed a threshold.

But the exact algorithm requires more precise steps.

Alternatively, here's an outline of the squarified algorithm (adapted from http://www.cs.umd.edu/hcil/treemaphistory/ )

The algorithm proceeds as follows to build a row:

1. Sort the items in descending order of area.

2. Initialize the current row with the first item.

3. For each subsequent item:

   - Compute the aspect ratio of the row if the item is added to it.

   - If this aspect ratio is smaller than the aspect ratio of the row without the item, add the item to the row.

   - Else, split the current row and start a new row with the current item.

Hmm, not sure.

Alternatively, here's a different approach:

Suppose we have a list of items to be placed into rows (vertical split). The rows are placed within the current rectangle.

The optimal row is the one that minimizes the maximum aspect ratio of all rows.

To compute this, for each possible split point, compute the aspect ratios and choose the best.

This is time-consuming but manageable for small numbers.

Alternatively, for simplicity, here's a basic implementation:

function splitIntoRows(items, rect) {
    const rows = [];
    let currentRow = [];
    let currentHeight = 0;
    let remainingItems = [...items];
    while (remainingItems.length > 0) {
        const item = remainingItems.shift();
        currentRow.push(item);
        currentHeight += item.weight; // assuming the total is the rect's height
        const height = (currentHeight / total) * rect.h;
        const aspect = rect.w / height;
        if (aspect > threshold) {
            // need to split here
            rows.push(currentRow);
            currentRow = [item];
            currentHeight = item.weight;
        }
    }
    rows.push(currentRow);
    return rows;
}

Wait, but this is very rough.

Given the time constraints, perhaps it's better to proceed with a simplified version where each split divides the items into two groups, based on the direction chosen, then each group is represented recursively.

Here's a possible code outline:

Each node has a rectangle and a list of items (or a single item if it's a leaf).

The root node has the entire canvas and all items.

function buildTreemap(rect, items) {
    if (items.length === 0) return;
    // base case: single item
    if (items.length === 1) {
        const item = items[0];
        // assign the rectangle to this item
        return { rect: rect, children: [], item: item };
    }
    // decide split direction
    const sum = items.reduce((a, b) => a + b.weight, 0);
    // calculate aspect for vertical and horizontal splits
    let verticalMaxAspect = 0;
    let horizontalMaxAspect = 0;
    
    for (const item of items) {
        const h = (item.weight / sum) * rect.h;
        const aspect = rect.w / h;
        verticalMaxAspect = Math.max(verticalMaxAspect, aspect);
        
        const w = (item.weight / sum) * rect.w;
        const hAspect = w / rect.h;
        horizontalMaxAspect = Math.max(horizontalMaxAspect, hAspect);
    }
    
    let direction;
    if (verticalMaxAspect < horizontalMaxAspect) {
        direction = 'vertical';
    } else {
        direction = 'horizontal';
    }
    
    // split the items into two groups (for simplicity, split evenly)
    const half = Math.floor(items.length / 2);
    const group1 = items.slice(0, half);
    const group2 = items.slice(half);
    
    // create two children rectangles
    let childRects;
    if (direction === 'vertical') {
        const splitY = rect.y + (rect.h * (group1.reduce((a,b) => a + b.weight,0)/sum));
        childRects = [
            { x: rect.x, y: rect.y, w: rect.w, h: splitY - rect.y },
            { x: rect.x, y: splitY, w: rect.w, h: rect.h - (splitY - rect.y) }
        ];
    } else {
        const splitX = rect.x + (rect.w * (group1.reduce((a,b)=>a+b.weight,0)/sum));
        childRects = [
            { x: rect.x, y: rect.y, w: splitX - rect.x, h: rect.h },
            { x: splitX, y: rect.y, w: rect.w - (splitX - rect.x), h: rect.h }
        ];
    }
    
    const children = [
        buildTreemap(childRects[0], group1),
        buildTreemap(childRects[1], group2)
    ];
    
    return { rect: rect, children: children };
}

This is a rough implementation where each split divides the items into two groups, splits the rectangle accordingly, and proceeds recursively. While not optimal, it gives a treemap-like structure.

Now, to create the rectangles visually, perhaps we can represent each item's rectangle as an object.

However, since the user asked for creating the rectangles, perhaps the answer should be in the form of a step-by-step explanation followed by the implementation.

But given the initial problem, maybe the user wants a simple algorithm explanation and pseudocode for a treemap.

Alternatively, since the user's question is "Create the rectangles for a treemap", perhaps the answer should outline the steps:

Steps to create rectangles for a treemap:

1. Sort the items by their weights (sizes) in descending order.

2. Choose a split direction (horizontal or vertical) based on which would result in a more "balanced" aspect ratio for the rectangles.

3. Divide the items into groups that will be placed in each sub-rectangle.

4. Recursively apply the same process to each sub-rectangle until all items are placed in leaf rectangles.

5. The size of each rectangle corresponds to the total weight of the items it contains.

For example, using a binary split at each step:

- Start with the root rectangle (entire area) and all items.

- Compute the total weight.

- Split the items into two groups (e.g., by dividing the list in half).

- Compute the split point along the chosen axis (e.g., horizontal or vertical) based on the proportion of the total weight of each group.

- Assign each group to a sub-rectangle, then recurse.

This is a simplified version, but it's a common approach for treemaps.

Alternatively, the squarified algorithm is more efficient but complex.

In summary, the process involves recursively partitioning the space based on item sizes and choosing split directions to keep aspect ratios as square as possible.
deepseek-r1:32bBLANKFORMAT
Show code
```html
<!DOCTYPE html><meta charset="utf-8"><canvas id=c></canvas>
<style>body{margin:0;overflow:hidden}canvas{display:block}</style>
<script>
class Item{
constructor(area,x,y,width,height,parent=null,children=[],labeled=false){
this.area=area,this.x=x,this.y=y,this.width=width,this.height=height,this.parent=parent,this.children=children,this.labeled=labeled
}
}
let canvas=document.getElementById('c'),ctx=canvas.getContext('2d'),items=[],
frame=0,size=850,empty=40,tick=1/60;
function generate(items,n=20){
for(let i=0;i<n;i++)items.push({area:Math.random(),children:[]})
scale(items[0],size-empty,size)
}
function scale(item,width,height){
item.width=width,item.height=height
if(!item.children.length&&Math.random()>.9)split(item)
else if(item.children.length)for(let child of item.children)
child.area=Math.random()*item.area *.5+.5*item.area
item.children.forEach((c,i)=>{let s=i+1;split(item)})
}
function split(parent){
if(!parent.children.length||Math.random()>.5){
let total=parent.children.reduce((a,c)=>a+c.area,0)
let width=parent.width,height=parent.height,
childrenPerRow=Math.floor(Math.sqrt(parent.children.length)),
rows=Math.ceil(parent.children.length/childrenPerRow),
rowHeight=height/rows;
parent.children.forEach(c=>{
c.x=c.parent.x
c.y=c.parent.y+rows>1?Math.min(rows-1,c.index)/rows*height:0
c.width=width/(childrenPerRow||1)
c.height=rowHeight
})
}else{
// similar for vertical split
}
}
function update(){
items=Array(20).fill().map(()=>({area:Math.random(),x:0,y:0,width:0,height:0,children:[]}));
scale(items[0],size-empty,size);
requestAnimationFrame(update);
draw();
}
function draw(){
ctx.fillStyle='white';ctx.fillRect(0,0,canvas.width,canvas.height);
items.forEach(item=>{
if(!item.children.length||Math.random()>.5){
let c=ctx.createPattern(
createPattern(item.area), 'repeat');
ctx.fillStyle=c;
}else{
let color=(1-Math.min(2*frame/size,.9))*255|0;
ctx.fillStyle=`hsl(${color},70%,60%)`;
}
if(item.labeled&&item.width>80&&item.height>30){
ctx.fillRect(item.x+empty,item.y+empty, item.width-empty*2, item.height-empty*2);
let share=(item.area/totalArea)*100;
ctx.fillStyle='white';ctx.font='24px sans-serif';
ctx.fillText(share.toFixed(1)+'%',item.x+35,item.y+35)
}
})
}
function createPattern(area){
let c=document.createElement('canvas'),w=64,h=64,context=c.getContext('2d');
c.width=w;c.height=h;
let gradient=context.createRadialGradient(
w/2,h/2,1,w/2,h/2,Math.min(w,h)/3);
gradient.addColorStop(0,'hsl('+frame+ ',70%,50%)');
gradient.addColorStop(1,'hsl('+(frame+area*360)+',70%,40%)');
context.fillStyle=gradient;context.fillRect(0,0,w,h);
return c;
}
generate(items);totalArea=items[0].area;
requestAnimationFrame(update);
</script>
```
Outside the Brain cluster — same task, same character budget, collected through their own tooling (response times not comparable)
Claude Opus 5frontier referenceOK
Show code
<!doctype html>
<html><head><meta charset="utf-8"><title>Squarified treemap</title>
<style>
html,body{margin:0;height:100%;background:#0b0e16;overflow:hidden}
canvas{display:block;width:100vw;height:100vh}
#hud{position:fixed;left:12px;top:11px;font:13px ui-monospace,monospace;color:#dbe7ff;
background:rgba(11,14,22,.62);padding:5px 9px;border-radius:5px}
</style></head><body>
<canvas id="c"></canvas><div id="hud">items</div>
<script>
const cv=document.getElementById('c'),g=cv.getContext('2d'),hud=document.getElementById('hud');
let W,H;
function fit(){W=cv.width=innerWidth;H=cv.height=innerHeight}
fit();addEventListener('resize',fit);

const N=20,items=[];
for(let i=0;i<N;i++)items.push({
  v:8+Math.random()*90, phase:Math.random()*6.28, rate:.12+Math.random()*.3,
  hue:(i*37)%360, cur:null,
});

// Squarified: Zeile solange fuellen, wie sich das schlechteste Seitenverhaeltnis
// dadurch verbessert; sonst Zeile abschliessen und im Rest weitermachen.
function worst(row,len,sum){
  const s2=sum*sum, l2=len*len;
  let mx=0,mn=Infinity;
  for(const r of row){mx=Math.max(mx,r);mn=Math.min(mn,r)}
  return Math.max(l2*mx/s2, s2/(l2*mn));
}
function squarify(vals,x,y,w,h,out){
  if(!vals.length)return;
  const total=vals.reduce((a,b)=>a+b.n,0);
  let i=0;
  while(i<vals.length){
    const horiz=w>=h, len=horiz?h:w;
    const row=[]; let sum=0;
    let j=i;
    while(j<vals.length){
      const nx=[...row.map(r=>r.n),vals[j].n];
      const ns=sum+vals[j].n;
      if(row.length && worst(nx,len,ns)>worst(row.map(r=>r.n),len,sum))break;
      row.push(vals[j]);sum=ns;j++;
    }
    const thick=sum/total*(horiz?w:h);
    let off=0;
    for(const r of row){
      const frac=r.n/sum;
      if(horiz){
        out.push({it:r.it,x,y:y+off,w:thick,h:len*frac});
        off+=len*frac;
      } else {
        out.push({it:r.it,x:x+off,y,w:len*frac,h:thick});
        off+=len*frac;
      }
    }
    const rest=vals.slice(j);
    if(!rest.length)return;
    if(horiz)return squarify(rest,x+thick,y,w-thick,h,out);
    return squarify(rest,x,y+thick,w,h-thick,out);
  }
}

let t0=0;
function frame(tm){
  if(!t0)t0=tm;
  const t=(tm-t0)/1000;
  for(const it of items) it.n=Math.max(4,it.v*(1+.55*Math.sin(t*it.rate+it.phase)));

  const sorted=items.map(it=>({it,n:it.n})).sort((a,b)=>b.n-a.n);
  const out=[];
  squarify(sorted,0,0,W,H,out);

  g.fillStyle='#0b0e16';g.fillRect(0,0,W,H);
  const total=sorted.reduce((a,b)=>a+b.n,0);
  for(const r of out){
    const it=r.it;
    // Weich nachfuehren statt springen.
    if(!it.cur)it.cur={x:r.x,y:r.y,w:r.w,h:r.h};
    const c=it.cur, k=.12;
    c.x+=(r.x-c.x)*k;c.y+=(r.y-c.y)*k;c.w+=(r.w-c.w)*k;c.h+=(r.h-c.h)*k;

    g.fillStyle='hsl('+it.hue+',52%,'+(26+it.n/total*140)+'%)';
    g.fillRect(c.x+1,c.y+1,Math.max(0,c.w-2),Math.max(0,c.h-2));
    g.strokeStyle='rgba(6,9,16,.85)';g.lineWidth=2;
    g.strokeRect(c.x+1,c.y+1,Math.max(0,c.w-2),Math.max(0,c.h-2));

    if(c.w>66&&c.h>30){
      g.fillStyle='rgba(255,255,255,.93)';
      g.font='600 13px ui-monospace,monospace';
      g.textAlign='left';g.textBaseline='top';
      g.fillText((it.n/total*100).toFixed(1)+'%',c.x+9,c.y+8);
    }
  }
  hud.textContent='items '+N+'  ·  squarified  ·  drifting weights';
  requestAnimationFrame(frame);
}
requestAnimationFrame(frame);
</script></body></html>
GPT-5.5 (Codex)frontier referenceSTATMINIFIED
Show code
<!doctype html><html><head><meta charset=utf-8><title>Treemap</title><style>html,body{margin:0;height:100%;overflow:hidden;background:#111}canvas{display:block;width:100vw;height:100vh}</style></head><body><canvas id=c></canvas><script>
let C=c.getContext('2d'),W,H,DPR,items=[],boxes=[],next=[],t=0,phase=0,dur=5200,hu=Math.random()*360;
function rnd(){let a=[];for(let i=0;i<22;i++)a.push({n:'Item '+(i+1),w:3+Math.random()*30,v:0,x:0,y:0,w0:0,h:0,tx:0,ty:0,tw:0,th:0});return a}
function size(){DPR=devicePixelRatio||1;W=c.width=innerWidth*DPR;H=c.height=innerHeight*DPR;c.style.width=innerWidth+'px';c.style.height=innerHeight+'px'}
onresize=size;size();
function worst(r,s,m,M){let q=s*s;return Math.max(q*M/(r*r),r*r/(q*m))}
function layout(a,x,y,w,h,out){let sum=a.reduce((p,o)=>p+o.w,0);a=a.slice().sort((p,q)=>q.w-p.w).map(o=>({o,a:o.w*w*h/sum}));sq(a,x,y,w,h,out)}
function sq(a,x,y,w,h,out){let row=[],s=0,m=1e9,M=0,r=Math.min(w,h);while(a.length){let it=a[0],ns=s+it.a,nm=Math.min(m,it.a),nM=Math.max(M,it.a);if(!row.length||worst(r,ns,nm,nM)<=worst(r,s,m,M)){row.push(it);s=ns;m=nm;M=nM;a.shift()}else break}if(w>=h){let hh=s/w,xx=x;for(let e of row){let ww=e.a/hh;out.push([e.o,xx,y,ww,hh]);xx+=ww}y+=hh;h-=hh}else{let ww=s/h,yy=y;for(let e of row){let hh=e.a/ww;out.push([e.o,x,yy,ww,hh]);yy+=hh}x+=ww;w-=ww}if(a.length&&w>1&&h>1)sq(a,x,y,w,h,out)}
function target(){let o=[];layout(items,0,0,W,H,o);for(let b of o){let q=b[0];q.tx=b[1];q.ty=b[2];q.tw=b[3];q.th=b[4]}}
function reset(){items=rnd();target();for(let q of items){q.x=q.tx;q.y=q.ty;q.w0=q.tw;q.h=q.th}phase=performance.now();dur=4600+Math.random()*2400;hu=Math.random()*360}
function drift(){for(let q of items){q.w*=.86+Math.random()*.32;q.w=Math.max(1,Math.min(55,q.w))}target();phase=performance.now();dur=4600+Math.random()*2400}
function mix(a,b,k){return a+(b-a)*k}
function frame(now){if(now-phase>dur){drift();if(Math.random()<.08)reset()}let k=Math.min(1,(now-phase)/dur);k=k*k*(3-2*k);C.clearRect(0,0,W,H);let total=items.reduce((p,o)=>p+o.w,0);items.sort((a,b)=>b.w-a.w);for(let i=0;i<items.length;i++){let q=items[i];q.x=mix(q.x,q.tx,.035);q.y=mix(q.y,q.ty,.035);q.w0=mix(q.w0,q.tw,.035);q.h=mix(q.h,q.th,.035);let pad=2*DPR,x=q.x+pad,y=q.y+pad,w=q.w0-pad*2,h=q.h-pad*2;C.fillStyle=`hsl(${(hu+i*31)%360} 65% ${37+i%5*6}%)`;C.fillRect(x,y,w,h);C.strokeStyle='rgba(255,255,255,.18)';C.strokeRect(x+.5,y+.5,w-1,h-1);if(w>110*DPR&&h>48*DPR){C.fillStyle='rgba(255,255,255,.92)';C.font=700*Math.min(22*DPR,Math.max(13*DPR,h*.18))+'px system-ui';C.fillText(q.n,x+10*DPR,y+22*DPR);C.font=500*Math.min(15*DPR,Math.max(11*DPR,h*.12))+'px system-ui';C.fillText((q.w/total*100).toFixed(1)+'%',x+10*DPR,y+42*DPR)}}requestAnimationFrame(frame)}
reset();requestAnimationFrame(frame);
</script></body></html>
Claude Sonnet 5frontier referenceOK
Show code
<!DOCTYPE html><html><head><meta charset="utf8"><title>Squarified Treemap</title><style>html,body{margin:0;background:#12141a;overflow:hidden}canvas{display:block}</style></head><body><canvas id="c"></canvas><script>
const cv=document.getElementById('c'),ctx=cv.getContext('2d');
let W,H;function resize(){W=cv.width=innerWidth;H=cv.height=innerHeight;}resize();addEventListener('resize',resize);
let items,cur,t0,TOPM=36;
function init(){
  items=[];
  let n=20;
  for(let i=0;i<n;i++){
    items.push({id:i,base:20+Math.random()*80,freq:0.1+Math.random()*0.3,ph:Math.random()*6.28,hue:(i/n*360)|0});
  }
  cur={};
  for(const it of items)cur[it.id]={x:W/2,y:H/2,w:1,h:1};
  t0=performance.now();
}
init();
function squarify(list,x,y,w,h,out){
  if(list.length===0)return;
  if(list.length===1){out.push({id:list[0].id,x,y,w,h,value:list[0].value});return;}
  const total=list.reduce((s,d)=>s+d.value,0);
  const area=w*h;
  const short=Math.min(w,h);
  let best=1,bestScore=Infinity,acc=0;
  for(let k=1;k<=list.length;k++){
    acc+=list[k-1].value;
    const rowArea=acc/total*area;
    const rowSide=rowArea/short;
    let worst=0;
    for(let j=0;j<k;j++){
      const a2=list[j].value/total*area;
      const side=a2/rowSide;
      const ratio=Math.max(rowSide/side,side/rowSide);
      if(ratio>worst)worst=ratio;
    }
    if(worst<bestScore){bestScore=worst;best=k;}else break;
  }
  const row=list.slice(0,best),rest=list.slice(best);
  const rowValue=row.reduce((s,d)=>s+d.value,0);
  const rowArea=rowValue/total*area;
  if(w>=h){
    const bandW=rowArea/h;
    let yy=y;
    for(const d of row){
      const a2=d.value/total*area;
      const bh=a2/bandW;
      out.push({id:d.id,x,y:yy,w:bandW,h:bh,value:d.value});
      yy+=bh;
    }
    squarify(rest,x+bandW,y,w-bandW,h,out);
  }else{
    const bandH=rowArea/w;
    let xx=x;
    for(const d of row){
      const a2=d.value/total*area;
      const bw=a2/bandH;
      out.push({id:d.id,x:xx,y,w:bw,h:bandH,value:d.value});
      xx+=bw;
    }
    squarify(rest,x,y+bandH,w,h-bandH,out);
  }
}
function frame(now){
  const t=(now-t0)/1000;
  for(const it of items){
    it.value=Math.max(4,it.base*(1+0.5*Math.sin(t*it.freq+it.ph)));
  }
  const list=items.slice().sort((a,b)=>b.value-a.value);
  const total=list.reduce((s,d)=>s+d.value,0);
  let target=[];
  squarify(list,0,TOPM,W,H-TOPM,target);
  const tmap={};
  for(const r of target)tmap[r.id]=r;
  for(const it of items){
    const c=cur[it.id],tg=tmap[it.id];
    if(!tg)continue;
    c.x+=(tg.x-c.x)*0.08;c.y+=(tg.y-c.y)*0.08;
    c.w+=(tg.w-c.w)*0.08;c.h+=(tg.h-c.h)*0.08;
  }
  ctx.fillStyle='#12141a';ctx.fillRect(0,0,W,H);
  const byVal=items.slice().sort((a,b)=>b.value-a.value);
  for(const it of items){
    const c=cur[it.id];
    if(c.w<=0||c.h<=0)continue;
    ctx.fillStyle='hsl('+it.hue+',60%,45%)';
    ctx.fillRect(c.x,c.y,c.w,c.h);
    ctx.strokeStyle='#12141a';ctx.lineWidth=2;
    ctx.strokeRect(c.x,c.y,c.w,c.h);
  }
  for(let i=0;i<5;i++){
    const it=byVal[i];const c=cur[it.id];
    if(c.w>44&&c.h>22){
      const pct=(it.value/total*100).toFixed(1)+'%';
      ctx.fillStyle='#fff';ctx.font='12px monospace';
      ctx.fillText(pct,c.x+5,c.y+16);
    }
  }
  ctx.fillStyle='#eee';ctx.font='16px monospace';
  ctx.fillText('items: '+items.length,10,24);
  requestAnimationFrame(frame);
}
requestAnimationFrame(frame);
</script></body></html>
Claude Haiku 4.5frontier referenceOK
Show code
<!DOCTYPE html>
<html>
<head><meta charset="utf-8"><meta name="viewport" content="width=device-width"><style>body{margin:0;background:#000;display:flex;justify-content:center;align-items:center;height:100vh}canvas{border:1px solid #666}#i{position:absolute;top:10px;left:10px;color:#fff;font-family:monospace;font-size:12px}</style></head>
<body>
<canvas id="c"></canvas>
<div id="i">Items: 20</div>
<script>
const c=document.getElementById('c'),ctx=c.getContext('2d');
c.width=600;c.height=400;
let data=[],rects=[],t=0;
function init(){data=[];for(let i=0;i<20;i++)data.push({val:Math.random()*100,id:i,x:0,y:0,w:0,h:0,tx:0,ty:0,tw:0,th:0});}
function treemap(items,x,y,w,h,depth=0){if(items.length===0)return;if(items.length===1){items[0].tx=x;items[0].ty=y;items[0].tw=w;items[0].th=h;return;}
const total=items.reduce((a,b)=>a+b.val,0);const vert=w>=h;const s=(vert?h:w);let row=[],rowSum=0,rowAspect=Infinity;
for(let i=0;i<items.length;i++){row.push(items[i]);rowSum+=items[i].val;const aspect=Math.max(s*s*rowSum/rowSum/rowSum,rowSum*rowSum/s/s/rowSum);if(aspect>rowAspect||i===items.length-1){if(i<items.length-1)row.pop();treemapRow(row,x,y,w,h,vert);const rowW=rowSum/total*(vert?w:h);if(vert){treemap(items.slice(i+1-(row.length>0?0:1)),x+rowW,y,w-rowW,h,depth+1);}else{treemap(items.slice(i+1-(row.length>0?0:1)),x,y+rowW,w,h-rowW,depth+1);}return;}}
}
function treemapRow(items,x,y,w,h,vert){const total=items.reduce((a,b)=>a+b.val,0);const s=vert?h:w;let pos=0;items.forEach(it=>{const sz=s*it.val/total;it.tx=vert?x+pos:x;it.ty=vert?y:y+pos;it.tw=vert?w:sz;it.th=vert?sz:h;pos+=sz;});}
function f(){t+=0.01;data.forEach(d=>{d.val=Math.max(5,d.val+Math.sin(t*0.5+d.id)*5);d.x+=(d.tx-d.x)*0.1;d.y+=(d.ty-d.y)*0.1;d.w+=(d.tw-d.w)*0.1;d.h+=(d.th-d.h)*0.1;});
treemap(data,0,0,c.width,c.height);
ctx.fillStyle='#000';ctx.fillRect(0,0,c.width,c.height);
data.forEach((d,i)=>{const hue=i*18%360;ctx.fillStyle=`hsl(${hue},70%,50%)`;ctx.fillRect(d.x,d.y,d.w,d.h);ctx.strokeStyle='#000';ctx.lineWidth=2;ctx.strokeRect(d.x,d.y,d.w,d.h);if(d.w>50&&d.h>30){ctx.fillStyle='#fff';ctx.font='12px mono';ctx.fillText(((d.val/d.val)*100).toFixed(0)+'%',d.x+5,d.y+20);}});
requestAnimationFrame(f);}
init();f();
</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 —131.9 sOK
gemma4:26b ——harness
llama3.1:8b —111.0 sOK
qwen3:8b ——harness
mistral-small:24b —174.9 sOK
qwen3:14b —48.1 sOK
qwen3-coder:30b —30.6 sOK
qwen3.5:9b —71.1 sOK
command-r:35b —275.4 sOK
qwq:32b —241.1 sOK
deepseek-r1:32b —242.6 sOK