Show code
```html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Circle Packing</title></head>
<body><canvas id="canvas" style="border:1px solid #333;"></canvas><script>const canvas=document.getElementById('canvas');const ctx=canvas.getContext('2d');let W=600,H=500;canvas.width=W;canvas.height=H;ctx.fillStyle='#111';ctx.fillRect(0,0,W,H);let circles=[];let animationFrameId=null;function init() {circles=[];let maxAttempts=200;for (let i=0;i<maxAttempts;++i){const x=Math.random()*W;const y=Math.random()*H;if(x-10>0&&y-10>0&&x+10<W&&y+10<H){const r_start=Math.min(Math.random()*20, Math.max(5, 1));let currentR=r_start;if(!isOverlapping(x, y, currentR)){circles.push({x:x,y:y,r:currentR,finalR:0});}else{break;}}drawStats();animate();/* The main loop */const animate=(t)=>{animationFrameId=requestAnimationFrame(()=>runCycle(t));};function runCycle(t){if (circles.length > 0) {const firstCircle=circles[0];if(firstCircle.finalR===0 && !isOverlapping(firstCircle.x, firstCircle.y, firstCircle.r*1.1)){// Grow check
let newR = Math.min(W/2, H/2);while(!isOverlapping(firstCircle.x, firstCircle.y, newR)){newR+=0.5;}newR-=0.5;if(newR > firstCircle.r){firstCircle.finalR=newR;drawGrowth(firstCircle);else{/* Start new circle or finish */}}
} else if (circles[circles.length-1].finalR>0 && circles.filter(c=>c.finalR===0).every(Boolean)){/* All finished, need restart check */const coverageRatio=(Math.PI*(circles.reduce((sum, c)=>sum+c.r*2*c.y/W,0)/3)+Math.PI*(circles.reduce((sum, c)=>sum+c.x*2*c.finalR/H,0)/3))/ (W*H);if(coverageRatio>0.8){/* Restart */init();drawGrowth(circles[0]);return;}} else { /* Start new circle if current one is small or finished */
const lastCircle=circles[circles.length-1];if(!lastCircle||lastCircle.finalR===0 || Math.random()<0.2) {const x=Math.random()*W;const y=Math.random()*H;let r_start=Math.min(Math.random()*20, 15);if(x-r_start>0&&y-r_start>0&&x+r_start<W&&y+r_start<H){circles.push({x:x,y:y,r:r_start,finalR:0});}else{return;}}
}
drawGrowth(circles[0]);
} else {/* First circle setup */if (Math.random() < 0.5) {const x=W/2;let y=Math.random()*H;let r_start=10;while(!isOverlapping(x,y,r_start*1.1)){y+=1;}circles.push({x:x,y:y,r:r_start,finalR:0});}else {const y=H/2;let x=Math.random()*W;let r_start=10;while(!isOverlapping(x,y,r_start*1.1)){x+=1;}circles.push({x:x,y:y,r:r_start,finalR:0});}}
animationFrameId=requestAnimationFrame(()=>runCycle(t));}function isOverlapping(cx,cy,cr){let overlap=false;for(const c of circles){if(c.finalR>0&&Math.hypot(cx-c.x,cy-c.y)<c.r+cr-(c.finalR/2)){overlap=true;break;}if(c.finalR===0 && Math.hypot(cx-c.x,cy-c.y)<c.r+cr){overlap=true;break;}}return overlap}function drawGrowth(circle){ctx.beginPath();ctx.arc(circle.x,circle.y,circle.r*1.1,0,Math.PI*2);ctx.strokeStyle=`rgba(50, 50, 255, ${circle.finalR/W * 0.8 + 0.2})`;ctx.lineWidth=3;ctx.stroke();ctx.beginPath();ctx.arc(circle.x,circle.y,Math.min(canvas.width/2, canvas.height/2),0,Math.PI*2);ctx.clip();let alpha=Math.max(0,Math.min(1,(circle.finalR-circle.r)/5)));if(alpha>0){ctx.fillStyle=`rgba(255, 100, 0, ${alpha * 0.8})`;ctx.globalAlpha=alpha*0.8;ctx.fill();}}function drawStats(){let totalArea=0,count=circles.length;for(const c of circles){totalArea+=Math.PI*(c.finalR>0?c.finalR:c.r)*c.finalR/2;}let coverageRatio=(totalArea/(W*H)).toFixed(3);ctx.fillStyle='#fff';ctx.font='16px Arial';ctx.fillText(`Circles: ${count} | Coverage: ${coverageRatio}`, 10, 20);if (circles[0] && circles[0].finalR===0) ctx.fillText("Growing...", 10, 40);else if(count>=25){ctx.fillStyle='rgba(0,0,0,0.3)';ctx.fillRect(0,0,W,H);}else{ctx.fillStyle='#aaa';ctx.font='14px Arial';ctx.fillText("Click to start (or wait for auto-start)", 10, H-20);}}document.addEventListener('DOMContentLoaded',()=>init());</script></body></html>
```