Show code
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Bubble Sort</title>
<style>
html,body{margin:0;height:100%;background:#0b0f18;overflow:hidden}
body{display:flex;align-items:center;justify-content:center;
font:14px/1.4 ui-monospace,SFMono-Regular,Menlo,monospace;color:#8fa3c0}
#w{width:min(96vw,1200px);height:min(80vh,660px);position:relative}
canvas{width:100%;height:100%;display:block;border-radius:12px;
background:radial-gradient(120% 120% at 50% 0%,#141c2c 0%,#0b0f18 70%);
box-shadow:0 20px 60px #0006,inset 0 0 0 1px #ffffff12}
</style>
</head>
<body>
<div id="w"><canvas id="c"></canvas></div>
<script>
const cv=document.getElementById('c'),ctx=cv.getContext('2d');
const N=80;let W=0,H=0,arr=[],off=[],i=0,j=0,cmp=0,swp=0,done=0,hold=0;
function fit(){const r=cv.getBoundingClientRect(),d=devicePixelRatio||1;
W=r.width;H=r.height;cv.width=W*d|0;cv.height=H*d|0;
ctx.setTransform(d,0,0,d,0,0);}
addEventListener('resize',fit);
function reset(){arr=[];off=[];for(let k=0;k<N;k++){arr.push(.04+Math.random()*.96);off.push(0);}
i=0;j=0;cmp=0;swp=0;done=0;hold=0;}
function step(){
if(done)return;
if(i>=N-1){done=1;return;}
if(j>=N-1-i){i++;j=0;if(i>=N-1)done=1;return;}
cmp++;
if(arr[j]>arr[j+1]){
const t=arr[j];arr[j]=arr[j+1];arr[j+1]=t;swp++;
const bw=(W-40)/N;off[j]=bw;off[j+1]=-bw;
}
j++;
}
function draw(){
ctx.clearRect(0,0,W,H);
const pad=20,top=52,bw=(W-pad*2)/N,gap=Math.min(2,bw*.18),ih=H-top-pad;
for(let k=0;k<N;k++){
off[k]*=.55;if(Math.abs(off[k])<.3)off[k]=0;
const h=Math.max(2,arr[k]*ih),x=pad+k*bw+off[k],y=top+ih-h;
let a,b;
if(!done&&(k===j||k===j+1)){a='#ffd36e';b='#ff8a3d';}
else if(done||k>=N-i){a='#5ef2b6';b='#12b981';}
else{a='#7db8ff';b='#3358d4';}
const g=ctx.createLinearGradient(0,y,0,top+ih);
g.addColorStop(0,a);g.addColorStop(1,b);
ctx.fillStyle=g;
ctx.fillRect(x+gap/2,y,Math.max(1,bw-gap),h);
if(!done&&(k===j||k===j+1)){
ctx.fillStyle='#ffd36e';
ctx.fillRect(x+gap/2,top+ih+4,Math.max(1,bw-gap),3);
}
}
ctx.fillStyle='#0000';
ctx.font='600 15px ui-monospace,Menlo,monospace';
ctx.fillStyle='#e8eefb';
ctx.fillText('BUBBLE SORT',pad,26);
ctx.font='13px ui-monospace,Menlo,monospace';
ctx.fillStyle='#7f93b1';
ctx.textAlign='right';
ctx.fillText('comparisons '+cmp+' swaps '+swp+' sorted '+(done?N:i)+'/'+N+(done?' ✓ done':''),W-pad,26);
ctx.textAlign='left';
ctx.strokeStyle='#ffffff10';ctx.beginPath();
ctx.moveTo(pad,top+ih+1.5);ctx.lineTo(W-pad,top+ih+1.5);ctx.stroke();
}
function loop(){
if(done){if(++hold>110)reset();}
else{for(let s=0;s<5;s++)step();}
draw();
requestAnimationFrame(loop);
}
fit();reset();loop();
</script>
</body>
</html>