Liveloop

An interactive timeline for social media. Every post is a tiny app you can play, save, and remix.

Product

  • Feed
  • Create
  • Claude Code plugin
  • Blog

Legal

  • Privacy Policy
  • Terms of Service
  • Cookie Policy
  • DMCA

Project

  • Templates
© 2026 Liveloop. All rights reserved.
LiveloopVersion history

v1Current

@liveloop · 5/18/2026, 11:03:22 AM

Initial version — all lines are new.

+<style>
+ html,body{height:100%;margin:0;background:#070612;overflow:hidden;}
+ *{box-sizing:border-box;}
+ #wrap{position:relative;height:100%;width:100%;font-family:system-ui,-apple-system,Segoe UI,Roboto,sans-serif;}
+ canvas{display:block;width:100%;height:100%;touch-action:none;}
+ #hud{position:absolute;top:0;left:0;right:0;display:flex;justify-content:space-between;align-items:flex-start;padding:.7rem .95rem;color:#dbeafe;font-size:.86rem;font-weight:800;letter-spacing:.04em;pointer-events:none;text-shadow:0 0 10px rgba(0,0,0,.95);}
+ #hud .lab{font-size:.56rem;opacity:.55;letter-spacing:.14em;margin-bottom:.05rem;}
+ #hud .r{text-align:right;}
+ #ov{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.5rem;text-align:center;padding:1.6rem;background:rgba(7,6,18,.86);color:#e9d5ff;cursor:pointer;-webkit-tap-highlight-color:transparent;touch-action:none;user-select:none;-webkit-user-select:none;}
+ #ov h1{margin:0;font-size:2.05rem;font-weight:900;letter-spacing:.02em;background:linear-gradient(90deg,#22d3ee,#a3e635,#f472b6);-webkit-background-clip:text;background-clip:text;color:transparent;}
+ #ov .sub{margin:.1rem 0 0;font-size:.9rem;color:#a5b4fc;max-width:25ch;}
+ #ov .big{margin:.1rem 0 0;font-size:1.05rem;font-weight:800;color:#e9d5ff;}
+ #ov .pulse{margin-top:.5rem;font-size:.84rem;color:#67e8f9;animation:ospulse 1.3s ease-in-out infinite;}
+ @keyframes ospulse{0%,100%{opacity:.35}50%{opacity:1}}
+</style>
+<div id="wrap">
+ <canvas id="cv"></canvas>
+ <div id="hud">
+ <div><div class="lab">DISTANCE</div><div id="score">0</div></div>
+ <div class="r"><div class="lab">BEST</div><div id="best">0</div></div>
+ </div>
+ <div id="ov">
+ <h1>ORB SURGE</h1>
+ <p class="sub" id="ovsub">Drag to steer your swarm into the better gate. Don't let it hit zero.</p>
+ <p class="big" id="ovbig"></p>
+ <p class="pulse">Tap to play</p>
+ </div>
+</div>
+<script>
+(function(){
+ var LL=window.liveloop||null;
+ var cv=document.getElementById('cv'), ctx=cv.getContext('2d');
+ var ov=document.getElementById('ov'), ovsub=document.getElementById('ovsub'),
+ ovbig=document.getElementById('ovbig'), ovtitle=ov.querySelector('h1');
+ var scoreEl=document.getElementById('score'), bestEl=document.getElementById('best');
+ var W=0,H=0,dpr=1, MAXORBS=48;
+ var state='start', count=0, best=0, prog=0, speed=0, elapsed=0;
+ var crowdX=0, gates=[], parts=[], cluster=[], orbR=0;
+ var nextSpawnProg=0, gatesSpawned=0, trackOff=0, flashCol='', flashUntil=0;
+ var lastT=0, raf=0, running=false, keyL=false, keyR=false;
+
+ function setCanvas(){
+ var r=cv.getBoundingClientRect();
+ W=Math.max(160,r.width); H=Math.max(220,r.height);
+ dpr=Math.min(window.devicePixelRatio||1,2.5);
+ cv.width=Math.round(W*dpr); cv.height=Math.round(H*dpr);
+ ctx.setTransform(dpr,0,0,dpr,0,0);
+ orbR=Math.max(4,W*0.021);
+ cluster=buildCluster(orbR);
+ }
+ function buildCluster(r){
+ var off=[], st=r*1.62, k=0;
+ while(off.length<MAXORBS){
+ var cnt=k===0?1:6*k;
+ for(var i=0;i<cnt && off.length<MAXORBS;i++){
+ var a=(i/cnt)*6.2832+k*0.7;
+ off.push({dx:Math.cos(a)*st*k,dy:Math.sin(a)*st*k});
+ }
+ k++;
+ }
+ return off;
+ }
+ function playerY(){ return H*0.74; }
+ function gateH(){ return H*0.115; }
+ function clampCount(c){
+ c=Math.round(c);
+ if(c<0)c=0; if(c>99999)c=99999;
+ return c;
+ }
+ function rint(a,b){ return a+Math.floor(Math.random()*(b-a+1)); }
+ function goodOp(){
+ return Math.random()<0.45 ? {t:'x',v:rint(2,3)} : {t:'+',v:rint(8,40)};
+ }
+ function minusOp(){ return {t:'-',v:rint(8,32)}; }
+ function badOp(){ return Math.random()<0.4 ? {t:'/',v:2} : minusOp(); }
+ function makeGate(){
+ var roll=Math.random(), l, r;
+ if(gatesSpawned<3) roll=Math.random()*0.85;
+ if(roll<0.6){ l=goodOp(); r=badOp(); }
+ else if(roll<0.85){ l=goodOp(); r=goodOp(); }
+ else { l={t:'/',v:2}; r=minusOp(); }
+ if(Math.random()<0.5){ var t=l; l=r; r=t; }
+ gatesSpawned++;
+ return { y:-gateH(), left:l, right:r, applied:false };
+ }
+ function opText(o){
+ if(o.t==='x') return '×'+o.v;
+ if(o.t==='+') return '+'+o.v;
+ if(o.t==='-') return '−'+o.v;
+ return '÷'+o.v;
+ }
+ function opGood(o){ return o.t==='x'||o.t==='+'; }
+ function applyOp(c,o){
+ if(o.t==='x') return c*o.v;
+ if(o.t==='+') return c+o.v;
+ if(o.t==='-') return c-o.v;
+ return Math.floor(c/o.v);
+ }
+ function newGame(){
+ count=5; prog=0; speed=H*0.0072; elapsed=0;
+ crowdX=W/2; gates=[]; parts=[]; nextSpawnProg=0.16; gatesSpawned=0; trackOff=0;
+ state='playing'; syncHud();
+ }
+ function syncHud(){
+ scoreEl.textContent=Math.floor(prog*120);
+ bestEl.textContent=best;
+ }
+ function flash(c){ flashCol=c; flashUntil=performance.now()+360; }
+ function burst(x,y,c){
+ for(var i=0;i<14;i++){
+ var a=Math.random()*6.2832;
+ parts.push({x:x,y:y,vx:Math.cos(a)*(1+Math.random()*3),
+ vy:Math.sin(a)*(1+Math.random()*3)-1.2,life:16+Math.random()*14,color:c});
+ }
+ }
+ function gameOver(){
+ state='over';
+ var sc=Math.floor(prog*120);
+ if(sc>best){ best=sc; saveBest(); }
+ syncHud();
+ ovtitle.textContent='GAME OVER';
+ ovsub.textContent='Your swarm ran out.';
+ ovbig.textContent='Distance '+sc+' · Best '+best;
+ ov.style.display='flex';
+ }
+ function applyGate(g){
+ g.applied=true;
+ var side=crowdX<W/2?g.left:g.right;
+ var before=count;
+ count=clampCount(applyOp(count,side));
+ if(count>before){ flash('#4ade80'); burst(crowdX,playerY(),'#4ade80'); }
+ else if(count<before){ flash('#fb7185'); }
+ if(count<=0) gameOver();
+ }
+ function step(dt){
+ elapsed+=dt/60;
+ speed=H*(0.0072+elapsed*0.00019);
+ if(speed>H*0.019) speed=H*0.019;
+ prog+=speed*dt/H;
+ trackOff=(trackOff+speed*dt)%(H*0.16);
+ if(keyL) crowdX-=W*0.03*dt;
+ if(keyR) crowdX+=W*0.03*dt;
+ var m=W*0.13;
+ if(crowdX<m)crowdX=m; if(crowdX>W-m)crowdX=W-m;
+ var py=playerY(), gh=gateH(), i;
+ for(i=0;i<gates.length;i++){
+ var g=gates[i];
+ g.y+=speed*dt;
+ if(!g.applied && g.y+gh/2>=py){ applyGate(g); if(state!=='playing') return; }
+ }
+ while(gates.length && gates[0].y>H+4) gates.shift();
+ if(prog>=nextSpawnProg){ gates.push(makeGate()); nextSpawnProg+=0.6; }
+ for(i=parts.length-1;i>=0;i--){
+ var p=parts[i];
+ p.x+=p.vx*dt; p.y+=p.vy*dt; p.vy+=0.13*dt; p.life-=dt;
+ if(p.life<=0) parts.splice(i,1);
+ }
+ syncHud();
+ }
+ function rr(x,y,w,h,rad){
+ var r=Math.min(rad,w/2,h/2);
+ ctx.beginPath();
+ ctx.moveTo(x+r,y);
+ ctx.arcTo(x+w,y,x+w,y+h,r);
+ ctx.arcTo(x+w,y+h,x,y+h,r);
+ ctx.arcTo(x,y+h,x,y,r);
+ ctx.arcTo(x,y,x+w,y,r);
+ ctx.closePath();
+ }
+ function drawGate(g){
+ var gh=gateH();
+ ctx.globalAlpha=g.applied?0.2:1;
+ var sides=[{o:g.left,x:0},{o:g.right,x:W/2}];
+ for(var k=0;k<2;k++){
+ var s=sides[k], good=opGood(s.o);
+ var gd=ctx.createLinearGradient(s.x,g.y,s.x,g.y+gh);
+ gd.addColorStop(0,good?'#22d3ee':'#fb7185');
+ gd.addColorStop(1,good?'#34d399':'#f43f5e');
+ ctx.fillStyle=gd;
+ rr(s.x+4,g.y,W/2-8,gh,12); ctx.fill();
+ ctx.fillStyle='rgba(255,255,255,0.96)';
+ ctx.font='900 '+Math.round(gh*0.42)+'px system-ui';
+ ctx.textAlign='center'; ctx.textBaseline='middle';
+ ctx.fillText(opText(s.o),s.x+W/4,g.y+gh/2);
+ }
+ ctx.globalAlpha=1;
+ }
+ function drawCrowd(){
+ var py=playerY(), n=Math.min(Math.max(count,1),MAXORBS), i;
+ ctx.shadowColor='#67e8f9'; ctx.shadowBlur=12;
+ for(i=0;i<n;i++){
+ var o=cluster[i];
+ ctx.fillStyle=i===0?'#f0fdff':'#7dd3fc';
+ ctx.beginPath();
+ ctx.arc(crowdX+o.dx,py+o.dy,orbR,0,6.2832);
+ ctx.fill();
+ }
+ ctx.shadowBlur=0;
+ ctx.fillStyle=(performance.now()<flashUntil)?flashCol:'#e9d5ff';
+ ctx.font='900 '+Math.round(H*0.062)+'px system-ui';
+ ctx.textAlign='center'; ctx.textBaseline='alphabetic';
+ ctx.shadowColor='rgba(0,0,0,0.9)'; ctx.shadowBlur=8;
+ ctx.fillText(String(count),crowdX,py-orbR*7);
+ ctx.shadowBlur=0;
+ }
+ function render(){
+ var bg=ctx.createLinearGradient(0,0,0,H);
+ bg.addColorStop(0,'#0d0a26'); bg.addColorStop(1,'#070612');
+ ctx.fillStyle=bg; ctx.fillRect(0,0,W,H);
+ ctx.strokeStyle='rgba(125,211,252,0.10)'; ctx.lineWidth=2;
+ var gap=H*0.16;
+ for(var y=trackOff-gap;y<H;y+=gap){
+ ctx.beginPath(); ctx.moveTo(0,y); ctx.lineTo(W,y); ctx.stroke();
+ }
+ ctx.strokeStyle='rgba(255,255,255,0.06)';
+ ctx.beginPath(); ctx.moveTo(W/2,0); ctx.lineTo(W/2,H); ctx.stroke();
+ var i;
+ for(i=0;i<gates.length;i++) drawGate(gates[i]);
+ for(i=0;i<parts.length;i++){
+ var p=parts[i];
+ ctx.globalAlpha=Math.max(0,Math.min(1,p.life/18));
+ ctx.fillStyle=p.color;
+ ctx.fillRect(p.x-2,p.y-2,4,4);
+ }
+ ctx.globalAlpha=1;
+ if(state!=='start') drawCrowd();
+ }
+ function frame(now){
+ if(!running) return;
+ var dt=(now-lastT)/16.667; lastT=now;
+ if(dt<0.2)dt=0.2; if(dt>2.6)dt=2.6;
+ if(state==='playing') step(dt);
+ render();
+ raf=requestAnimationFrame(frame);
+ }
+ function startLoop(){
+ if(running) return;
+ running=true; lastT=performance.now(); raf=requestAnimationFrame(frame);
+ }
+ function stopLoop(){ running=false; if(raf)cancelAnimationFrame(raf); raf=0; }
+ function saveBest(){
+ if(LL&&LL.storage&&LL.storage.set){ try{ LL.storage.set({best:best}); }catch(e){} }
+ }
+ function loadBest(){
+ if(LL&&LL.storage&&LL.storage.get){
+ LL.storage.get().then(function(s){
+ if(s&&typeof s.best==='number'&&s.best>best){ best=s.best; syncHud(); }
+ },function(){});
+ }
+ }
+ function moveTo(clientX){
+ var r=cv.getBoundingClientRect(), m=W*0.13;
+ crowdX=clientX-r.left;
+ if(crowdX<m)crowdX=m; if(crowdX>W-m)crowdX=W-m;
+ }
+ cv.addEventListener('pointerdown',function(e){ moveTo(e.clientX); });
+ cv.addEventListener('pointermove',function(e){ moveTo(e.clientX); });
+ window.addEventListener('keydown',function(e){
+ if(e.key==='ArrowLeft')keyL=true; else if(e.key==='ArrowRight')keyR=true;
+ });
+ window.addEventListener('keyup',function(e){
+ if(e.key==='ArrowLeft')keyL=false; else if(e.key==='ArrowRight')keyR=false;
+ });
+ ov.addEventListener('pointerdown',function(e){
+ e.preventDefault();
+ ov.style.display='none';
+ newGame();
+ });
+ function onResize(){
+ var oW=W,oH=H;
+ setCanvas();
+ if(oW>0&&oH>0&&(oW!==W||oH!==H)){
+ var sx=W/oW, sy=H/oH, i;
+ crowdX*=sx;
+ for(i=0;i<gates.length;i++) gates[i].y*=sy;
+ for(i=0;i<parts.length;i++){ parts[i].x*=sx; parts[i].y*=sy; }
+ }
+ }
+ window.addEventListener('resize',onResize);
+ if(LL && LL.onResize) LL.onResize(onResize);
+ if(LL && LL.onVisibility) LL.onVisibility(function(v){ if(v) startLoop(); else stopLoop(); });
+ document.addEventListener('visibilitychange',function(){
+ if(document.hidden) stopLoop(); else startLoop();
+ });
+
+ setCanvas(); syncHud(); loadBest(); startLoop();
+})();
+</script>

v1Current

@liveloop · 5/18/2026, 11:03:22 AM

Initial version — all lines are new.

+<style>
+ html,body{height:100%;margin:0;background:#070612;overflow:hidden;}
+ *{box-sizing:border-box;}
+ #wrap{position:relative;height:100%;width:100%;font-family:system-ui,-apple-system,Segoe UI,Roboto,sans-serif;}
+ canvas{display:block;width:100%;height:100%;touch-action:none;}
+ #hud{position:absolute;top:0;left:0;right:0;display:flex;justify-content:space-between;align-items:flex-start;padding:.7rem .95rem;color:#dbeafe;font-size:.86rem;font-weight:800;letter-spacing:.04em;pointer-events:none;text-shadow:0 0 10px rgba(0,0,0,.95);}
+ #hud .lab{font-size:.56rem;opacity:.55;letter-spacing:.14em;margin-bottom:.05rem;}
+ #hud .r{text-align:right;}
+ #ov{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.5rem;text-align:center;padding:1.6rem;background:rgba(7,6,18,.86);color:#e9d5ff;cursor:pointer;-webkit-tap-highlight-color:transparent;touch-action:none;user-select:none;-webkit-user-select:none;}
+ #ov h1{margin:0;font-size:2.05rem;font-weight:900;letter-spacing:.02em;background:linear-gradient(90deg,#22d3ee,#a3e635,#f472b6);-webkit-background-clip:text;background-clip:text;color:transparent;}
+ #ov .sub{margin:.1rem 0 0;font-size:.9rem;color:#a5b4fc;max-width:25ch;}
+ #ov .big{margin:.1rem 0 0;font-size:1.05rem;font-weight:800;color:#e9d5ff;}
+ #ov .pulse{margin-top:.5rem;font-size:.84rem;color:#67e8f9;animation:ospulse 1.3s ease-in-out infinite;}
+ @keyframes ospulse{0%,100%{opacity:.35}50%{opacity:1}}
+</style>
+<div id="wrap">
+ <canvas id="cv"></canvas>
+ <div id="hud">
+ <div><div class="lab">DISTANCE</div><div id="score">0</div></div>
+ <div class="r"><div class="lab">BEST</div><div id="best">0</div></div>
+ </div>
+ <div id="ov">
+ <h1>ORB SURGE</h1>
+ <p class="sub" id="ovsub">Drag to steer your swarm into the better gate. Don't let it hit zero.</p>
+ <p class="big" id="ovbig"></p>
+ <p class="pulse">Tap to play</p>
+ </div>
+</div>
+<script>
+(function(){
+ var LL=window.liveloop||null;
+ var cv=document.getElementById('cv'), ctx=cv.getContext('2d');
+ var ov=document.getElementById('ov'), ovsub=document.getElementById('ovsub'),
+ ovbig=document.getElementById('ovbig'), ovtitle=ov.querySelector('h1');
+ var scoreEl=document.getElementById('score'), bestEl=document.getElementById('best');
+ var W=0,H=0,dpr=1, MAXORBS=48;
+ var state='start', count=0, best=0, prog=0, speed=0, elapsed=0;
+ var crowdX=0, gates=[], parts=[], cluster=[], orbR=0;
+ var nextSpawnProg=0, gatesSpawned=0, trackOff=0, flashCol='', flashUntil=0;
+ var lastT=0, raf=0, running=false, keyL=false, keyR=false;
+
+ function setCanvas(){
+ var r=cv.getBoundingClientRect();
+ W=Math.max(160,r.width); H=Math.max(220,r.height);
+ dpr=Math.min(window.devicePixelRatio||1,2.5);
+ cv.width=Math.round(W*dpr); cv.height=Math.round(H*dpr);
+ ctx.setTransform(dpr,0,0,dpr,0,0);
+ orbR=Math.max(4,W*0.021);
+ cluster=buildCluster(orbR);
+ }
+ function buildCluster(r){
+ var off=[], st=r*1.62, k=0;
+ while(off.length<MAXORBS){
+ var cnt=k===0?1:6*k;
+ for(var i=0;i<cnt && off.length<MAXORBS;i++){
+ var a=(i/cnt)*6.2832+k*0.7;
+ off.push({dx:Math.cos(a)*st*k,dy:Math.sin(a)*st*k});
+ }
+ k++;
+ }
+ return off;
+ }
+ function playerY(){ return H*0.74; }
+ function gateH(){ return H*0.115; }
+ function clampCount(c){
+ c=Math.round(c);
+ if(c<0)c=0; if(c>99999)c=99999;
+ return c;
+ }
+ function rint(a,b){ return a+Math.floor(Math.random()*(b-a+1)); }
+ function goodOp(){
+ return Math.random()<0.45 ? {t:'x',v:rint(2,3)} : {t:'+',v:rint(8,40)};
+ }
+ function minusOp(){ return {t:'-',v:rint(8,32)}; }
+ function badOp(){ return Math.random()<0.4 ? {t:'/',v:2} : minusOp(); }
+ function makeGate(){
+ var roll=Math.random(), l, r;
+ if(gatesSpawned<3) roll=Math.random()*0.85;
+ if(roll<0.6){ l=goodOp(); r=badOp(); }
+ else if(roll<0.85){ l=goodOp(); r=goodOp(); }
+ else { l={t:'/',v:2}; r=minusOp(); }
+ if(Math.random()<0.5){ var t=l; l=r; r=t; }
+ gatesSpawned++;
+ return { y:-gateH(), left:l, right:r, applied:false };
+ }
+ function opText(o){
+ if(o.t==='x') return '×'+o.v;
+ if(o.t==='+') return '+'+o.v;
+ if(o.t==='-') return '−'+o.v;
+ return '÷'+o.v;
+ }
+ function opGood(o){ return o.t==='x'||o.t==='+'; }
+ function applyOp(c,o){
+ if(o.t==='x') return c*o.v;
+ if(o.t==='+') return c+o.v;
+ if(o.t==='-') return c-o.v;
+ return Math.floor(c/o.v);
+ }
+ function newGame(){
+ count=5; prog=0; speed=H*0.0072; elapsed=0;
+ crowdX=W/2; gates=[]; parts=[]; nextSpawnProg=0.16; gatesSpawned=0; trackOff=0;
+ state='playing'; syncHud();
+ }
+ function syncHud(){
+ scoreEl.textContent=Math.floor(prog*120);
+ bestEl.textContent=best;
+ }
+ function flash(c){ flashCol=c; flashUntil=performance.now()+360; }
+ function burst(x,y,c){
+ for(var i=0;i<14;i++){
+ var a=Math.random()*6.2832;
+ parts.push({x:x,y:y,vx:Math.cos(a)*(1+Math.random()*3),
+ vy:Math.sin(a)*(1+Math.random()*3)-1.2,life:16+Math.random()*14,color:c});
+ }
+ }
+ function gameOver(){
+ state='over';
+ var sc=Math.floor(prog*120);
+ if(sc>best){ best=sc; saveBest(); }
+ syncHud();
+ ovtitle.textContent='GAME OVER';
+ ovsub.textContent='Your swarm ran out.';
+ ovbig.textContent='Distance '+sc+' · Best '+best;
+ ov.style.display='flex';
+ }
+ function applyGate(g){
+ g.applied=true;
+ var side=crowdX<W/2?g.left:g.right;
+ var before=count;
+ count=clampCount(applyOp(count,side));
+ if(count>before){ flash('#4ade80'); burst(crowdX,playerY(),'#4ade80'); }
+ else if(count<before){ flash('#fb7185'); }
+ if(count<=0) gameOver();
+ }
+ function step(dt){
+ elapsed+=dt/60;
+ speed=H*(0.0072+elapsed*0.00019);
+ if(speed>H*0.019) speed=H*0.019;
+ prog+=speed*dt/H;
+ trackOff=(trackOff+speed*dt)%(H*0.16);
+ if(keyL) crowdX-=W*0.03*dt;
+ if(keyR) crowdX+=W*0.03*dt;
+ var m=W*0.13;
+ if(crowdX<m)crowdX=m; if(crowdX>W-m)crowdX=W-m;
+ var py=playerY(), gh=gateH(), i;
+ for(i=0;i<gates.length;i++){
+ var g=gates[i];
+ g.y+=speed*dt;
+ if(!g.applied && g.y+gh/2>=py){ applyGate(g); if(state!=='playing') return; }
+ }
+ while(gates.length && gates[0].y>H+4) gates.shift();
+ if(prog>=nextSpawnProg){ gates.push(makeGate()); nextSpawnProg+=0.6; }
+ for(i=parts.length-1;i>=0;i--){
+ var p=parts[i];
+ p.x+=p.vx*dt; p.y+=p.vy*dt; p.vy+=0.13*dt; p.life-=dt;
+ if(p.life<=0) parts.splice(i,1);
+ }
+ syncHud();
+ }
+ function rr(x,y,w,h,rad){
+ var r=Math.min(rad,w/2,h/2);
+ ctx.beginPath();
+ ctx.moveTo(x+r,y);
+ ctx.arcTo(x+w,y,x+w,y+h,r);
+ ctx.arcTo(x+w,y+h,x,y+h,r);
+ ctx.arcTo(x,y+h,x,y,r);
+ ctx.arcTo(x,y,x+w,y,r);
+ ctx.closePath();
+ }
+ function drawGate(g){
+ var gh=gateH();
+ ctx.globalAlpha=g.applied?0.2:1;
+ var sides=[{o:g.left,x:0},{o:g.right,x:W/2}];
+ for(var k=0;k<2;k++){
+ var s=sides[k], good=opGood(s.o);
+ var gd=ctx.createLinearGradient(s.x,g.y,s.x,g.y+gh);
+ gd.addColorStop(0,good?'#22d3ee':'#fb7185');
+ gd.addColorStop(1,good?'#34d399':'#f43f5e');
+ ctx.fillStyle=gd;
+ rr(s.x+4,g.y,W/2-8,gh,12); ctx.fill();
+ ctx.fillStyle='rgba(255,255,255,0.96)';
+ ctx.font='900 '+Math.round(gh*0.42)+'px system-ui';
+ ctx.textAlign='center'; ctx.textBaseline='middle';
+ ctx.fillText(opText(s.o),s.x+W/4,g.y+gh/2);
+ }
+ ctx.globalAlpha=1;
+ }
+ function drawCrowd(){
+ var py=playerY(), n=Math.min(Math.max(count,1),MAXORBS), i;
+ ctx.shadowColor='#67e8f9'; ctx.shadowBlur=12;
+ for(i=0;i<n;i++){
+ var o=cluster[i];
+ ctx.fillStyle=i===0?'#f0fdff':'#7dd3fc';
+ ctx.beginPath();
+ ctx.arc(crowdX+o.dx,py+o.dy,orbR,0,6.2832);
+ ctx.fill();
+ }
+ ctx.shadowBlur=0;
+ ctx.fillStyle=(performance.now()<flashUntil)?flashCol:'#e9d5ff';
+ ctx.font='900 '+Math.round(H*0.062)+'px system-ui';
+ ctx.textAlign='center'; ctx.textBaseline='alphabetic';
+ ctx.shadowColor='rgba(0,0,0,0.9)'; ctx.shadowBlur=8;
+ ctx.fillText(String(count),crowdX,py-orbR*7);
+ ctx.shadowBlur=0;
+ }
+ function render(){
+ var bg=ctx.createLinearGradient(0,0,0,H);
+ bg.addColorStop(0,'#0d0a26'); bg.addColorStop(1,'#070612');
+ ctx.fillStyle=bg; ctx.fillRect(0,0,W,H);
+ ctx.strokeStyle='rgba(125,211,252,0.10)'; ctx.lineWidth=2;
+ var gap=H*0.16;
+ for(var y=trackOff-gap;y<H;y+=gap){
+ ctx.beginPath(); ctx.moveTo(0,y); ctx.lineTo(W,y); ctx.stroke();
+ }
+ ctx.strokeStyle='rgba(255,255,255,0.06)';
+ ctx.beginPath(); ctx.moveTo(W/2,0); ctx.lineTo(W/2,H); ctx.stroke();
+ var i;
+ for(i=0;i<gates.length;i++) drawGate(gates[i]);
+ for(i=0;i<parts.length;i++){
+ var p=parts[i];
+ ctx.globalAlpha=Math.max(0,Math.min(1,p.life/18));
+ ctx.fillStyle=p.color;
+ ctx.fillRect(p.x-2,p.y-2,4,4);
+ }
+ ctx.globalAlpha=1;
+ if(state!=='start') drawCrowd();
+ }
+ function frame(now){
+ if(!running) return;
+ var dt=(now-lastT)/16.667; lastT=now;
+ if(dt<0.2)dt=0.2; if(dt>2.6)dt=2.6;
+ if(state==='playing') step(dt);
+ render();
+ raf=requestAnimationFrame(frame);
+ }
+ function startLoop(){
+ if(running) return;
+ running=true; lastT=performance.now(); raf=requestAnimationFrame(frame);
+ }
+ function stopLoop(){ running=false; if(raf)cancelAnimationFrame(raf); raf=0; }
+ function saveBest(){
+ if(LL&&LL.storage&&LL.storage.set){ try{ LL.storage.set({best:best}); }catch(e){} }
+ }
+ function loadBest(){
+ if(LL&&LL.storage&&LL.storage.get){
+ LL.storage.get().then(function(s){
+ if(s&&typeof s.best==='number'&&s.best>best){ best=s.best; syncHud(); }
+ },function(){});
+ }
+ }
+ function moveTo(clientX){
+ var r=cv.getBoundingClientRect(), m=W*0.13;
+ crowdX=clientX-r.left;
+ if(crowdX<m)crowdX=m; if(crowdX>W-m)crowdX=W-m;
+ }
+ cv.addEventListener('pointerdown',function(e){ moveTo(e.clientX); });
+ cv.addEventListener('pointermove',function(e){ moveTo(e.clientX); });
+ window.addEventListener('keydown',function(e){
+ if(e.key==='ArrowLeft')keyL=true; else if(e.key==='ArrowRight')keyR=true;
+ });
+ window.addEventListener('keyup',function(e){
+ if(e.key==='ArrowLeft')keyL=false; else if(e.key==='ArrowRight')keyR=false;
+ });
+ ov.addEventListener('pointerdown',function(e){
+ e.preventDefault();
+ ov.style.display='none';
+ newGame();
+ });
+ function onResize(){
+ var oW=W,oH=H;
+ setCanvas();
+ if(oW>0&&oH>0&&(oW!==W||oH!==H)){
+ var sx=W/oW, sy=H/oH, i;
+ crowdX*=sx;
+ for(i=0;i<gates.length;i++) gates[i].y*=sy;
+ for(i=0;i<parts.length;i++){ parts[i].x*=sx; parts[i].y*=sy; }
+ }
+ }
+ window.addEventListener('resize',onResize);
+ if(LL && LL.onResize) LL.onResize(onResize);
+ if(LL && LL.onVisibility) LL.onVisibility(function(v){ if(v) startLoop(); else stopLoop(); });
+ document.addEventListener('visibilitychange',function(){
+ if(document.hidden) stopLoop(); else startLoop();
+ });
+
+ setCanvas(); syncHud(); loadBest(); startLoop();
+})();
+</script>
← Version history