@liveloop · 5/18/2026, 12:51:47 PM
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 1.4rem;color:#e9d5ff;font-size:.95rem;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 .col{text-align:center;}+ #hud .won{color:#22d3ee;}+ #hud .lost{color:#fb7185;}+ #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,#a855f7,#fb7185);-webkit-background-clip:text;background-clip:text;color:transparent;}+ #ov .sub{margin:.1rem 0 0;font-size:.9rem;color:#a5b4fc;max-width:26ch;}+ #ov .pulse{margin-top:.5rem;font-size:.84rem;color:#67e8f9;animation:tlpulse 1.3s ease-in-out infinite;}+ @keyframes tlpulse{0%,100%{opacity:.35}50%{opacity:1}}+</style>+<div id="wrap">+ <canvas id="cv"></canvas>+ <div id="hud">+ <div class="col"><div class="lab">WON</div><div id="won" class="won">0</div></div>+ <div class="col"><div class="lab">DREW</div><div id="drew">0</div></div>+ <div class="col"><div class="lab">LOST</div><div id="lost" class="lost">0</div></div>+ </div>+ <div id="ov">+ <h1>TRIPLE LINE</h1>+ <p class="sub">You are X and move first. Get three in a row before the computer.</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');+ var wonEl=document.getElementById('won'), drewEl=document.getElementById('drew'),+ lostEl=document.getElementById('lost');+ var LINES=[[0,1,2],[3,4,5],[6,7,8],[0,3,6],[1,4,7],[2,5,8],[0,4,8],[2,4,6]];+ var W=0,H=0,dpr=1,cell=0,bx=0,by=0;+ var state='start', board=[], turn='X', result=null, winLine=null;+ var winAnim=0, aiTimer=0, placeT=[];+ var stats={w:0,d:0,l:0};+ var lastT=0,raf=0,running=false;++ function setCanvas(){+ var rc=cv.getBoundingClientRect();+ W=Math.max(200,rc.width); H=Math.max(280,rc.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);+ cell=Math.floor(Math.min(W*0.82,H*0.46)/3);+ bx=Math.round((W-cell*3)/2);+ by=Math.round(H*0.2);+ }+ function newRound(){+ board=['','','','','','','','',''];+ placeT=[0,0,0,0,0,0,0,0,0];+ turn='X'; result=null; winLine=null; winAnim=0; aiTimer=0;+ state='playing';+ }+ function syncHud(){+ wonEl.textContent=stats.w; drewEl.textContent=stats.d; lostEl.textContent=stats.l;+ }+ function winnerOf(b){+ for(var i=0;i<LINES.length;i++){+ var L=LINES[i];+ if(b[L[0]] && b[L[0]]===b[L[1]] && b[L[0]]===b[L[2]]) return {who:b[L[0]],line:L};+ }+ var full=true;+ for(i=0;i<9;i++) if(!b[i]) full=false;+ if(full) return {who:'draw',line:null};+ return null;+ }+ function endRound(w){+ result=w.who; winLine=w.line; state='over'; winAnim=0;+ if(w.who==='X') stats.w++;+ else if(w.who==='O') stats.l++;+ else stats.d++;+ syncHud(); saveStats();+ }+ function evaluate(){+ var w=winnerOf(board);+ if(w){ endRound(w); return; }+ turn=(turn==='X')?'O':'X';+ if(turn==='O') aiTimer=26;+ }+ function aiPick(){+ var i,b;+ // win if possible+ for(i=0;i<9;i++) if(!board[i]){+ b=board.slice(); b[i]='O';+ if(winnerOf(b)&&winnerOf(b).who==='O') return i;+ }+ // block the player+ for(i=0;i<9;i++) if(!board[i]){+ b=board.slice(); b[i]='X';+ if(winnerOf(b)&&winnerOf(b).who==='X') return i;+ }+ if(!board[4]) return 4;+ var corners=[0,2,6,8], sides=[1,3,5,7], pool=[];+ for(i=0;i<4;i++) if(!board[corners[i]]) pool.push(corners[i]);+ if(pool.length) return pool[Math.floor(Math.random()*pool.length)];+ for(i=0;i<4;i++) if(!board[sides[i]]) pool.push(sides[i]);+ return pool[Math.floor(Math.random()*pool.length)];+ }+ function place(i,who){+ board[i]=who; placeT[i]=performance.now();+ }+ function step(dt){+ if(state==='playing' && turn==='O' && aiTimer>0){+ aiTimer-=dt;+ if(aiTimer<=0){+ var mv=aiPick();+ if(mv!=null){ place(mv,'O'); evaluate(); }+ }+ }+ if(state==='over' && winAnim<1){+ winAnim+=dt*0.07;+ if(winAnim>1) winAnim=1;+ }+ }+ function ccx(i){ return bx+(i%3)*cell+cell/2; }+ function ccy(i){ return by+Math.floor(i/3)*cell+cell/2; }+ function markScale(i){+ if(!placeT[i]) return 1;+ var t=(performance.now()-placeT[i])/150;+ if(t>1) t=1;+ return 1-(1-t)*(1-t);+ }+ function drawX(i,sc){+ var x=ccx(i),y=ccy(i),r=cell*0.27*sc;+ ctx.strokeStyle='#22d3ee'; ctx.lineWidth=cell*0.1; ctx.lineCap='round';+ ctx.shadowColor='#22d3ee'; ctx.shadowBlur=12;+ ctx.beginPath();+ ctx.moveTo(x-r,y-r); ctx.lineTo(x+r,y+r);+ ctx.moveTo(x+r,y-r); ctx.lineTo(x-r,y+r);+ ctx.stroke();+ ctx.shadowBlur=0;+ }+ function drawO(i,sc){+ var x=ccx(i),y=ccy(i),r=cell*0.27*sc;+ ctx.strokeStyle='#fb7185'; ctx.lineWidth=cell*0.1;+ ctx.shadowColor='#fb7185'; ctx.shadowBlur=12;+ ctx.beginPath(); ctx.arc(x,y,r,0,6.2832); ctx.stroke();+ ctx.shadowBlur=0;+ }+ function render(){+ var bg=ctx.createLinearGradient(0,0,0,H);+ bg.addColorStop(0,'#140d30'); bg.addColorStop(1,'#070612');+ ctx.fillStyle=bg; ctx.fillRect(0,0,W,H);+ var i;+ for(i=0;i<9;i++){+ var x=bx+(i%3)*cell, y=by+Math.floor(i/3)*cell;+ ctx.fillStyle='rgba(255,255,255,0.05)';+ ctx.beginPath();+ var rad=cell*0.16,pad=cell*0.05;+ ctx.moveTo(x+pad+rad,y+pad);+ ctx.arcTo(x+cell-pad,y+pad,x+cell-pad,y+cell-pad,rad);+ ctx.arcTo(x+cell-pad,y+cell-pad,x+pad,y+cell-pad,rad);+ ctx.arcTo(x+pad,y+cell-pad,x+pad,y+pad,rad);+ ctx.arcTo(x+pad,y+pad,x+cell-pad,y+pad,rad);+ ctx.closePath(); ctx.fill();+ }+ for(i=0;i<9;i++){+ if(board[i]==='X') drawX(i,markScale(i));+ else if(board[i]==='O') drawO(i,markScale(i));+ }+ if(winLine){+ var a=winLine[0], b=winLine[2];+ var x1=ccx(a),y1=ccy(a),x2=ccx(b),y2=ccy(b);+ var ex=x1+(x2-x1)*winAnim, ey=y1+(y2-y1)*winAnim;+ ctx.strokeStyle='#fde68a'; ctx.lineWidth=cell*0.07; ctx.lineCap='round';+ ctx.shadowColor='#fde68a'; ctx.shadowBlur=16;+ ctx.beginPath(); ctx.moveTo(x1,y1); ctx.lineTo(ex,ey); ctx.stroke();+ ctx.shadowBlur=0;+ }+ // status text+ ctx.textAlign='center';+ var ty=by+cell*3+H*0.07;+ if(state==='playing'){+ ctx.fillStyle=turn==='X'?'#67e8f9':'#fb7185';+ ctx.font='800 '+Math.round(H*0.03)+'px system-ui';+ ctx.fillText(turn==='X'?'Your turn':'Computer thinking...',W/2,ty);+ } else if(state==='over'){+ ctx.fillStyle=result==='X'?'#22d3ee':(result==='O'?'#fb7185':'#a5b4fc');+ ctx.font='900 '+Math.round(H*0.05)+'px system-ui';+ ctx.fillText(result==='X'?'YOU WIN':(result==='O'?'COMPUTER WINS':'DRAW'),W/2,ty);+ ctx.fillStyle='#a5b4fc';+ ctx.font='700 '+Math.round(H*0.026)+'px system-ui';+ ctx.fillText('Tap for the next round',W/2,ty+H*0.05);+ }+ }+ 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;+ 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 saveStats(){+ if(LL&&LL.storage&&LL.storage.set){ try{ LL.storage.set({stats:stats}); }catch(e){} }+ }+ function loadStats(){+ if(LL&&LL.storage&&LL.storage.get){+ LL.storage.get().then(function(s){+ if(s&&s.stats&&typeof s.stats.w==='number'){ stats=s.stats; syncHud(); }+ },function(){});+ }+ }+ cv.addEventListener('pointerdown',function(e){+ if(state==='over'){ newRound(); return; }+ if(state!=='playing'||turn!=='X') return;+ var rc=cv.getBoundingClientRect();+ var px=e.clientX-rc.left, py=e.clientY-rc.top;+ var c=Math.floor((px-bx)/cell), r=Math.floor((py-by)/cell);+ if(c<0||c>2||r<0||r>2) return;+ var idx=r*3+c;+ if(board[idx]) return;+ place(idx,'X'); evaluate();+ });+ ov.addEventListener('pointerdown',function(e){+ e.preventDefault();+ ov.style.display='none';+ newRound();+ });+ function onResize(){ setCanvas(); }+ 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(); loadStats(); startLoop();+})();+</script>