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

@remy_play · 6/10/2026, 4:03:52 PM

Initial version — all lines are new.

+<style>
+ html,body{height:100%;margin:0;overflow:hidden;background:#FFF2E2;}
+ *{box-sizing:border-box;}
+ #wrap{position:relative;height:100%;width:100%;display:flex;flex-direction:column;font-family:system-ui,-apple-system,Segoe UI,Roboto,sans-serif;background:radial-gradient(110% 80% at 50% 0%,#FFF9EF 0%,#FFECD3 70%,#FBDFBC 100%);-webkit-tap-highlight-color:transparent;touch-action:manipulation;user-select:none;-webkit-user-select:none;}
+ #head{padding:1.05rem 1.2rem .25rem;}
+ #head h1{margin:0;font-family:"Instrument Serif",Georgia,"Times New Roman",serif;font-weight:400;font-size:2.2rem;line-height:1;color:#4a3a55;}
+ #head p{margin:.2rem 0 0;font-size:.78rem;color:#a08d77;font-weight:500;}
+ #hud{display:none;gap:8px;padding:.5rem 1.2rem 0;}
+ .pill{background:rgba(255,255,255,.85);border:1px solid rgba(120,90,50,.2);border-radius:999px;font-size:.72rem;font-weight:800;color:#4a3a55;padding:.32rem .7rem;}
+ #stage{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:0;padding:.5rem 1.1rem;gap:14px;}
+ #pickWrap{text-align:center;}
+ #pick{border:0;border-radius:999px;background:linear-gradient(135deg,#7C5CFF,#FF5A1F);color:#fff;font-weight:800;font-size:1rem;padding:.95rem 1.7rem;cursor:pointer;box-shadow:0 10px 26px rgba(124,92,255,.35);}
+ #pick:active{transform:scale(.96);}
+ #pickNote{margin-top:.7rem;font-size:.72rem;color:#a08d77;font-weight:600;}
+ #diffs{display:none;gap:8px;}
+ .diff{border:1px solid rgba(120,90,50,.25);background:rgba(255,255,255,.7);color:#6a5440;border-radius:999px;font-size:.8rem;font-weight:800;padding:.55rem 1.1rem;cursor:pointer;}
+ .diff.on{background:#4a3a55;color:#FFF3E4;border-color:#4a3a55;}
+ #board{display:none;position:relative;background:#fff;padding:6px;border-radius:16px;box-shadow:0 14px 34px rgba(90,60,30,.25);}
+ #grid{display:grid;gap:2px;border-radius:10px;overflow:hidden;}
+ .tile{position:relative;background-repeat:no-repeat;cursor:pointer;transition:transform .12s,opacity .12s,filter .12s;}
+ .tile.sel{transform:scale(.92);filter:brightness(1.25);z-index:2;outline:3px solid #7C5CFF;outline-offset:-3px;border-radius:8px;}
+ .tile.ok{cursor:default;}
+ #peekImg{position:absolute;inset:6px;border-radius:10px;object-fit:cover;width:calc(100% - 12px);height:calc(100% - 12px);display:none;z-index:5;pointer-events:none;}
+ #foot{display:none;gap:10px;justify-content:center;padding:.2rem 1.1rem 1.15rem;}
+ #foot button{border:0;border-radius:999px;font-weight:800;font-size:.85rem;padding:.7rem 1.25rem;cursor:pointer;}
+ #peek{background:rgba(255,255,255,.75);color:#6a5440;border:1px solid rgba(120,90,50,.25)!important;}
+ #newPhoto{background:rgba(255,255,255,.75);color:#6a5440;border:1px solid rgba(120,90,50,.25)!important;}
+ #file{display:none;}
+ #win{position:absolute;inset:0;z-index:10;display:none;flex-direction:column;align-items:center;justify-content:center;background:rgba(60,45,75,.6);backdrop-filter:blur(6px);padding:1.3rem;text-align:center;}
+ #winImg{width:62%;max-width:230px;aspect-ratio:1;object-fit:cover;border-radius:14px;border:5px solid #fff;box-shadow:0 16px 40px rgba(0,0,0,.4);transform:rotate(-2deg);}
+ #winTime{margin-top:1rem;font-family:"Instrument Serif",Georgia,serif;font-style:italic;font-size:2.2rem;color:#fff;line-height:1;}
+ #winSub{margin-top:.4rem;font-size:.78rem;font-weight:700;color:rgba(255,255,255,.9);}
+ #win .row{display:flex;gap:10px;margin-top:1.1rem;}
+ #win button{border:0;border-radius:999px;font-weight:800;font-size:.85rem;padding:.75rem 1.3rem;cursor:pointer;background:#fff;color:#4a3a55;}
+ .cf{position:absolute;width:8px;height:8px;border-radius:2px;pointer-events:none;z-index:12;}
+</style>
+<div id="wrap">
+ <div id="head"><h1>Pieces</h1><p>Your photo, scrambled. Tap two pieces to swap.</p></div>
+ <div id="hud"><span class="pill" id="movesPill">0 moves</span><span class="pill" id="timePill">0:00</span></div>
+ <div id="stage">
+ <div id="pickWrap">
+ <button id="pick">&#129513; puzzle my photo</button>
+ <div id="pickNote">your photo never leaves this phone</div>
+ </div>
+ <div id="diffs">
+ <button class="diff" data-n="3">3&#215;3</button>
+ <button class="diff on" data-n="4">4&#215;4</button>
+ <button class="diff" data-n="5">5&#215;5</button>
+ </div>
+ <div id="board">
+ <div id="grid"></div>
+ <img id="peekImg" alt="">
+ </div>
+ </div>
+ <div id="foot">
+ <button id="peek">&#128064; hold to peek</button>
+ <button id="newPhoto">new photo</button>
+ </div>
+ <input id="file" type="file" accept="image/*">
+ <div id="win">
+ <img id="winImg" alt="solved photo">
+ <div id="winTime"></div>
+ <div id="winSub"></div>
+ <div class="row">
+ <button id="again">shuffle again</button>
+ </div>
+ </div>
+</div>
+<script>
+(function(){
+ var LL=window.liveloop||null;
+ var muted = LL ? LL.muted : true;
+ if(LL && LL.onMute){ try{ LL.onMute(function(m){ muted=m; }); }catch(e){} }
+ if(LL && LL.declareMedia){ try{ LL.declareMedia({sound:true}); }catch(e){} }
+ var _ac=null;
+ function actx(){ if(!_ac){ try{ _ac=new (window.AudioContext||window.webkitAudioContext)(); }catch(e){ _ac=null; } } return _ac; }
+ function tone(f,dur,vol,type){
+ if(muted) return; var c=actx(); if(!c) return;
+ var t=c.currentTime, o=c.createOscillator(), g=c.createGain();
+ o.type=type||'sine'; o.frequency.setValueAtTime(f,t);
+ g.gain.setValueAtTime(0.0001,t); g.gain.exponentialRampToValueAtTime(vol||0.1,t+0.01);
+ g.gain.exponentialRampToValueAtTime(0.0001,t+(dur||0.12));
+ o.connect(g); g.connect(c.destination); o.start(t); o.stop(t+(dur||0.12)+0.04);
+ }
+ function snap(){ tone(660,0.1,0.1,'triangle'); }
+ function pickSnd(){ tone(440,0.08,0.08); }
+ function fanfare(){
+ if(muted) return; var c=actx(); if(!c) return;
+ var t=c.currentTime, fr=[523.25,659.25,783.99,1046.5,1318.5];
+ for(var i=0;i<fr.length;i++){ (function(i){
+ var o=c.createOscillator(), g=c.createGain();
+ o.type='triangle'; o.frequency.setValueAtTime(fr[i],t+i*0.09);
+ g.gain.setValueAtTime(0.0001,t+i*0.09); g.gain.exponentialRampToValueAtTime(0.12,t+i*0.09+0.012);
+ g.gain.exponentialRampToValueAtTime(0.0001,t+i*0.09+0.55);
+ o.connect(g); g.connect(c.destination); o.start(t+i*0.09); o.stop(t+i*0.09+0.6);
+ })(i); }
+ }
+
+ var pickWrap=document.getElementById('pickWrap');
+ var pickBtn=document.getElementById('pick'), fileIn=document.getElementById('file');
+ var diffsEl=document.getElementById('diffs');
+ var boardEl=document.getElementById('board'), grid=document.getElementById('grid');
+ var peekImg=document.getElementById('peekImg');
+ var hud=document.getElementById('hud'), movesPill=document.getElementById('movesPill'), timePill=document.getElementById('timePill');
+ var foot=document.getElementById('foot'), peekBtn=document.getElementById('peek'), newBtn=document.getElementById('newPhoto');
+ var winEl=document.getElementById('win'), winImg=document.getElementById('winImg'), winTime=document.getElementById('winTime'), winSub=document.getElementById('winSub');
+ var againBtn=document.getElementById('again');
+ var wrap=document.getElementById('wrap');
+
+ var photoURL='', N=4, perm=[], sel=-1, moves=0, solvedFlag=false;
+ var elapsed=0, ticking=false, lastT=0, paused=false;
+ var best={}; // {"3":ms,"4":ms,"5":ms}
+ try{
+ if(LL && LL.storage && LL.storage.get){
+ LL.storage.get().then(function(s){ if(s && s.best) best=s.best||{}; }).catch(function(){});
+ }
+ }catch(e){}
+ function saveBest(){ try{ if(LL && LL.storage && LL.storage.set) LL.storage.set({best:best}); }catch(e){} }
+
+ function fmt(ms){
+ var s=Math.floor(ms/1000);
+ return Math.floor(s/60)+':'+String(s%60).padStart(2,'0');
+ }
+ function clock(now){
+ requestAnimationFrame(clock);
+ if(!ticking||paused) return;
+ elapsed+=Math.min(100,now-lastT); lastT=now;
+ timePill.textContent=fmt(elapsed);
+ }
+ requestAnimationFrame(clock);
+
+ function boardSize(){
+ var r=document.getElementById('stage').getBoundingClientRect();
+ return Math.max(180,Math.min(r.width-12,r.height-70,360));
+ }
+
+ function shuffle(){
+ perm=[]; for(var i=0;i<N*N;i++) perm.push(i);
+ do{
+ for(var j=perm.length-1;j>0;j--){
+ var k=Math.floor(Math.random()*(j+1)), tmp=perm[j]; perm[j]=perm[k]; perm[k]=tmp;
+ }
+ } while(perm.every(function(p,i){ return p===i; }));
+ }
+
+ function buildGrid(){
+ var S=boardSize(), cell=Math.floor((S-2*(N-1))/N), full=cell*N+2*(N-1);
+ grid.style.gridTemplateColumns='repeat('+N+','+cell+'px)';
+ grid.style.gridTemplateRows='repeat('+N+','+cell+'px)';
+ grid.innerHTML='';
+ for(var i=0;i<N*N;i++){ (function(i){
+ var d=document.createElement('div'); d.className='tile';
+ d.style.width=cell+'px'; d.style.height=cell+'px';
+ var p=perm[i], px=p%N, py=Math.floor(p/N);
+ d.style.backgroundImage='url('+photoURL+')';
+ d.style.backgroundSize=full+'px '+full+'px';
+ d.style.backgroundPosition=(-(px*(cell+2)))+'px '+(-(py*(cell+2)))+'px';
+ if(perm[i]===i) d.classList.add('ok');
+ d.addEventListener('pointerdown',function(e){ e.preventDefault(); tap(i); });
+ grid.appendChild(d);
+ })(i); }
+ }
+
+ function tap(i){
+ if(solvedFlag) return;
+ if(!ticking){ ticking=true; lastT=performance.now(); }
+ if(sel===-1){
+ if(perm[i]===i) return;
+ sel=i; grid.children[i].classList.add('sel'); pickSnd(); return;
+ }
+ if(sel===i){ grid.children[i].classList.remove('sel'); sel=-1; return; }
+ var a=sel; sel=-1;
+ grid.children[a].classList.remove('sel');
+ var tmp=perm[a]; perm[a]=perm[i]; perm[i]=tmp;
+ moves++; movesPill.textContent=moves+' move'+(moves===1?'':'s');
+ snap(); buildGrid();
+ if(perm.every(function(p,idx){ return p===idx; })) win();
+ }
+
+ function win(){
+ solvedFlag=true; ticking=false;
+ fanfare(); confetti();
+ winImg.src=photoURL;
+ winTime.textContent=fmt(elapsed);
+ var key=String(N), prev=best[key];
+ var isBest=!prev||elapsed<prev;
+ if(isBest){ best[key]=Math.round(elapsed); saveBest(); }
+ winSub.textContent=(isBest?'new best for ':'best for ')+N+'×'+N+': '+fmt(best[key])+' · '+moves+' moves';
+ winEl.style.display='flex';
+ }
+
+ function confetti(){
+ var COLS=['#7C5CFF','#FF5A1F','#E14C8F','#F6A93B','#fff'];
+ var r=wrap.getBoundingClientRect();
+ for(var i=0;i<26;i++){ (function(i){
+ var p=document.createElement('span'); p.className='cf';
+ p.style.background=COLS[i%COLS.length];
+ p.style.left=(r.width/2)+'px'; p.style.top=(r.height*0.4)+'px';
+ wrap.appendChild(p);
+ var a=Math.random()*Math.PI*2, v=3+Math.random()*5;
+ var vx=Math.cos(a)*v, vy=Math.sin(a)*v-4, x=0,y=0,rot=0,life=0;
+ function step(){
+ life+=16; x+=vx; y+=vy; vy+=0.25; rot+=9;
+ p.style.transform='translate('+x+'px,'+y+'px) rotate('+rot+'deg)';
+ p.style.opacity=String(Math.max(0,1-life/1000));
+ if(life<1000) requestAnimationFrame(step); else p.remove();
+ }
+ requestAnimationFrame(step);
+ })(i); }
+ }
+
+ function start(){
+ solvedFlag=false; sel=-1; moves=0; elapsed=0; ticking=false;
+ movesPill.textContent='0 moves'; timePill.textContent='0:00';
+ shuffle(); buildGrid();
+ winEl.style.display='none';
+ boardEl.style.display='block'; hud.style.display='flex';
+ diffsEl.style.display='flex'; foot.style.display='flex';
+ }
+
+ pickBtn.addEventListener('click',function(){ fileIn.click(); });
+ fileIn.addEventListener('change',function(){
+ if(!(fileIn.files && fileIn.files[0])) return;
+ var rd=new FileReader();
+ rd.onload=function(){
+ var im=new Image();
+ im.onload=function(){
+ // center-square crop, downscaled — crisp tiles, light memory
+ var S=Math.min(im.width,im.height), out=Math.min(900,S);
+ var c2=document.createElement('canvas'); c2.width=out; c2.height=out;
+ c2.getContext('2d').drawImage(im,(im.width-S)/2,(im.height-S)/2,S,S,0,0,out,out);
+ photoURL=c2.toDataURL('image/jpeg',0.88);
+ peekImg.src=photoURL;
+ pickWrap.style.display='none';
+ start(); pickSnd();
+ };
+ im.src=rd.result;
+ };
+ rd.readAsDataURL(fileIn.files[0]);
+ fileIn.value='';
+ });
+
+ [].forEach.call(diffsEl.children,function(b){
+ b.addEventListener('click',function(){
+ [].forEach.call(diffsEl.children,function(x){ x.classList.remove('on'); });
+ b.classList.add('on');
+ N=+b.getAttribute('data-n');
+ start(); pickSnd();
+ });
+ });
+
+ function peekOn(e){ e.preventDefault(); if(photoURL) peekImg.style.display='block'; }
+ function peekOff(){ peekImg.style.display='none'; }
+ peekBtn.addEventListener('pointerdown',peekOn);
+ peekBtn.addEventListener('pointerup',peekOff);
+ peekBtn.addEventListener('pointercancel',peekOff);
+ peekBtn.addEventListener('pointerleave',peekOff);
+
+ newBtn.addEventListener('click',function(){
+ photoURL=''; ticking=false;
+ boardEl.style.display='none'; hud.style.display='none';
+ diffsEl.style.display='none'; foot.style.display='none';
+ winEl.style.display='none';
+ pickWrap.style.display='block';
+ });
+ againBtn.addEventListener('click',function(){ start(); });
+
+ if(LL && LL.onPause){ try{ LL.onPause(function(p){ paused=p; if(!p) lastT=performance.now(); }); }catch(e){} }
+ document.addEventListener('visibilitychange',function(){ paused=document.hidden; if(!paused) lastT=performance.now(); });
+ window.addEventListener('resize',function(){ if(photoURL && !solvedFlag) buildGrid(); });
+})();
+</script>

v1Current

@remy_play · 6/10/2026, 4:03:52 PM

Initial version — all lines are new.

+<style>
+ html,body{height:100%;margin:0;overflow:hidden;background:#FFF2E2;}
+ *{box-sizing:border-box;}
+ #wrap{position:relative;height:100%;width:100%;display:flex;flex-direction:column;font-family:system-ui,-apple-system,Segoe UI,Roboto,sans-serif;background:radial-gradient(110% 80% at 50% 0%,#FFF9EF 0%,#FFECD3 70%,#FBDFBC 100%);-webkit-tap-highlight-color:transparent;touch-action:manipulation;user-select:none;-webkit-user-select:none;}
+ #head{padding:1.05rem 1.2rem .25rem;}
+ #head h1{margin:0;font-family:"Instrument Serif",Georgia,"Times New Roman",serif;font-weight:400;font-size:2.2rem;line-height:1;color:#4a3a55;}
+ #head p{margin:.2rem 0 0;font-size:.78rem;color:#a08d77;font-weight:500;}
+ #hud{display:none;gap:8px;padding:.5rem 1.2rem 0;}
+ .pill{background:rgba(255,255,255,.85);border:1px solid rgba(120,90,50,.2);border-radius:999px;font-size:.72rem;font-weight:800;color:#4a3a55;padding:.32rem .7rem;}
+ #stage{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:0;padding:.5rem 1.1rem;gap:14px;}
+ #pickWrap{text-align:center;}
+ #pick{border:0;border-radius:999px;background:linear-gradient(135deg,#7C5CFF,#FF5A1F);color:#fff;font-weight:800;font-size:1rem;padding:.95rem 1.7rem;cursor:pointer;box-shadow:0 10px 26px rgba(124,92,255,.35);}
+ #pick:active{transform:scale(.96);}
+ #pickNote{margin-top:.7rem;font-size:.72rem;color:#a08d77;font-weight:600;}
+ #diffs{display:none;gap:8px;}
+ .diff{border:1px solid rgba(120,90,50,.25);background:rgba(255,255,255,.7);color:#6a5440;border-radius:999px;font-size:.8rem;font-weight:800;padding:.55rem 1.1rem;cursor:pointer;}
+ .diff.on{background:#4a3a55;color:#FFF3E4;border-color:#4a3a55;}
+ #board{display:none;position:relative;background:#fff;padding:6px;border-radius:16px;box-shadow:0 14px 34px rgba(90,60,30,.25);}
+ #grid{display:grid;gap:2px;border-radius:10px;overflow:hidden;}
+ .tile{position:relative;background-repeat:no-repeat;cursor:pointer;transition:transform .12s,opacity .12s,filter .12s;}
+ .tile.sel{transform:scale(.92);filter:brightness(1.25);z-index:2;outline:3px solid #7C5CFF;outline-offset:-3px;border-radius:8px;}
+ .tile.ok{cursor:default;}
+ #peekImg{position:absolute;inset:6px;border-radius:10px;object-fit:cover;width:calc(100% - 12px);height:calc(100% - 12px);display:none;z-index:5;pointer-events:none;}
+ #foot{display:none;gap:10px;justify-content:center;padding:.2rem 1.1rem 1.15rem;}
+ #foot button{border:0;border-radius:999px;font-weight:800;font-size:.85rem;padding:.7rem 1.25rem;cursor:pointer;}
+ #peek{background:rgba(255,255,255,.75);color:#6a5440;border:1px solid rgba(120,90,50,.25)!important;}
+ #newPhoto{background:rgba(255,255,255,.75);color:#6a5440;border:1px solid rgba(120,90,50,.25)!important;}
+ #file{display:none;}
+ #win{position:absolute;inset:0;z-index:10;display:none;flex-direction:column;align-items:center;justify-content:center;background:rgba(60,45,75,.6);backdrop-filter:blur(6px);padding:1.3rem;text-align:center;}
+ #winImg{width:62%;max-width:230px;aspect-ratio:1;object-fit:cover;border-radius:14px;border:5px solid #fff;box-shadow:0 16px 40px rgba(0,0,0,.4);transform:rotate(-2deg);}
+ #winTime{margin-top:1rem;font-family:"Instrument Serif",Georgia,serif;font-style:italic;font-size:2.2rem;color:#fff;line-height:1;}
+ #winSub{margin-top:.4rem;font-size:.78rem;font-weight:700;color:rgba(255,255,255,.9);}
+ #win .row{display:flex;gap:10px;margin-top:1.1rem;}
+ #win button{border:0;border-radius:999px;font-weight:800;font-size:.85rem;padding:.75rem 1.3rem;cursor:pointer;background:#fff;color:#4a3a55;}
+ .cf{position:absolute;width:8px;height:8px;border-radius:2px;pointer-events:none;z-index:12;}
+</style>
+<div id="wrap">
+ <div id="head"><h1>Pieces</h1><p>Your photo, scrambled. Tap two pieces to swap.</p></div>
+ <div id="hud"><span class="pill" id="movesPill">0 moves</span><span class="pill" id="timePill">0:00</span></div>
+ <div id="stage">
+ <div id="pickWrap">
+ <button id="pick">&#129513; puzzle my photo</button>
+ <div id="pickNote">your photo never leaves this phone</div>
+ </div>
+ <div id="diffs">
+ <button class="diff" data-n="3">3&#215;3</button>
+ <button class="diff on" data-n="4">4&#215;4</button>
+ <button class="diff" data-n="5">5&#215;5</button>
+ </div>
+ <div id="board">
+ <div id="grid"></div>
+ <img id="peekImg" alt="">
+ </div>
+ </div>
+ <div id="foot">
+ <button id="peek">&#128064; hold to peek</button>
+ <button id="newPhoto">new photo</button>
+ </div>
+ <input id="file" type="file" accept="image/*">
+ <div id="win">
+ <img id="winImg" alt="solved photo">
+ <div id="winTime"></div>
+ <div id="winSub"></div>
+ <div class="row">
+ <button id="again">shuffle again</button>
+ </div>
+ </div>
+</div>
+<script>
+(function(){
+ var LL=window.liveloop||null;
+ var muted = LL ? LL.muted : true;
+ if(LL && LL.onMute){ try{ LL.onMute(function(m){ muted=m; }); }catch(e){} }
+ if(LL && LL.declareMedia){ try{ LL.declareMedia({sound:true}); }catch(e){} }
+ var _ac=null;
+ function actx(){ if(!_ac){ try{ _ac=new (window.AudioContext||window.webkitAudioContext)(); }catch(e){ _ac=null; } } return _ac; }
+ function tone(f,dur,vol,type){
+ if(muted) return; var c=actx(); if(!c) return;
+ var t=c.currentTime, o=c.createOscillator(), g=c.createGain();
+ o.type=type||'sine'; o.frequency.setValueAtTime(f,t);
+ g.gain.setValueAtTime(0.0001,t); g.gain.exponentialRampToValueAtTime(vol||0.1,t+0.01);
+ g.gain.exponentialRampToValueAtTime(0.0001,t+(dur||0.12));
+ o.connect(g); g.connect(c.destination); o.start(t); o.stop(t+(dur||0.12)+0.04);
+ }
+ function snap(){ tone(660,0.1,0.1,'triangle'); }
+ function pickSnd(){ tone(440,0.08,0.08); }
+ function fanfare(){
+ if(muted) return; var c=actx(); if(!c) return;
+ var t=c.currentTime, fr=[523.25,659.25,783.99,1046.5,1318.5];
+ for(var i=0;i<fr.length;i++){ (function(i){
+ var o=c.createOscillator(), g=c.createGain();
+ o.type='triangle'; o.frequency.setValueAtTime(fr[i],t+i*0.09);
+ g.gain.setValueAtTime(0.0001,t+i*0.09); g.gain.exponentialRampToValueAtTime(0.12,t+i*0.09+0.012);
+ g.gain.exponentialRampToValueAtTime(0.0001,t+i*0.09+0.55);
+ o.connect(g); g.connect(c.destination); o.start(t+i*0.09); o.stop(t+i*0.09+0.6);
+ })(i); }
+ }
+
+ var pickWrap=document.getElementById('pickWrap');
+ var pickBtn=document.getElementById('pick'), fileIn=document.getElementById('file');
+ var diffsEl=document.getElementById('diffs');
+ var boardEl=document.getElementById('board'), grid=document.getElementById('grid');
+ var peekImg=document.getElementById('peekImg');
+ var hud=document.getElementById('hud'), movesPill=document.getElementById('movesPill'), timePill=document.getElementById('timePill');
+ var foot=document.getElementById('foot'), peekBtn=document.getElementById('peek'), newBtn=document.getElementById('newPhoto');
+ var winEl=document.getElementById('win'), winImg=document.getElementById('winImg'), winTime=document.getElementById('winTime'), winSub=document.getElementById('winSub');
+ var againBtn=document.getElementById('again');
+ var wrap=document.getElementById('wrap');
+
+ var photoURL='', N=4, perm=[], sel=-1, moves=0, solvedFlag=false;
+ var elapsed=0, ticking=false, lastT=0, paused=false;
+ var best={}; // {"3":ms,"4":ms,"5":ms}
+ try{
+ if(LL && LL.storage && LL.storage.get){
+ LL.storage.get().then(function(s){ if(s && s.best) best=s.best||{}; }).catch(function(){});
+ }
+ }catch(e){}
+ function saveBest(){ try{ if(LL && LL.storage && LL.storage.set) LL.storage.set({best:best}); }catch(e){} }
+
+ function fmt(ms){
+ var s=Math.floor(ms/1000);
+ return Math.floor(s/60)+':'+String(s%60).padStart(2,'0');
+ }
+ function clock(now){
+ requestAnimationFrame(clock);
+ if(!ticking||paused) return;
+ elapsed+=Math.min(100,now-lastT); lastT=now;
+ timePill.textContent=fmt(elapsed);
+ }
+ requestAnimationFrame(clock);
+
+ function boardSize(){
+ var r=document.getElementById('stage').getBoundingClientRect();
+ return Math.max(180,Math.min(r.width-12,r.height-70,360));
+ }
+
+ function shuffle(){
+ perm=[]; for(var i=0;i<N*N;i++) perm.push(i);
+ do{
+ for(var j=perm.length-1;j>0;j--){
+ var k=Math.floor(Math.random()*(j+1)), tmp=perm[j]; perm[j]=perm[k]; perm[k]=tmp;
+ }
+ } while(perm.every(function(p,i){ return p===i; }));
+ }
+
+ function buildGrid(){
+ var S=boardSize(), cell=Math.floor((S-2*(N-1))/N), full=cell*N+2*(N-1);
+ grid.style.gridTemplateColumns='repeat('+N+','+cell+'px)';
+ grid.style.gridTemplateRows='repeat('+N+','+cell+'px)';
+ grid.innerHTML='';
+ for(var i=0;i<N*N;i++){ (function(i){
+ var d=document.createElement('div'); d.className='tile';
+ d.style.width=cell+'px'; d.style.height=cell+'px';
+ var p=perm[i], px=p%N, py=Math.floor(p/N);
+ d.style.backgroundImage='url('+photoURL+')';
+ d.style.backgroundSize=full+'px '+full+'px';
+ d.style.backgroundPosition=(-(px*(cell+2)))+'px '+(-(py*(cell+2)))+'px';
+ if(perm[i]===i) d.classList.add('ok');
+ d.addEventListener('pointerdown',function(e){ e.preventDefault(); tap(i); });
+ grid.appendChild(d);
+ })(i); }
+ }
+
+ function tap(i){
+ if(solvedFlag) return;
+ if(!ticking){ ticking=true; lastT=performance.now(); }
+ if(sel===-1){
+ if(perm[i]===i) return;
+ sel=i; grid.children[i].classList.add('sel'); pickSnd(); return;
+ }
+ if(sel===i){ grid.children[i].classList.remove('sel'); sel=-1; return; }
+ var a=sel; sel=-1;
+ grid.children[a].classList.remove('sel');
+ var tmp=perm[a]; perm[a]=perm[i]; perm[i]=tmp;
+ moves++; movesPill.textContent=moves+' move'+(moves===1?'':'s');
+ snap(); buildGrid();
+ if(perm.every(function(p,idx){ return p===idx; })) win();
+ }
+
+ function win(){
+ solvedFlag=true; ticking=false;
+ fanfare(); confetti();
+ winImg.src=photoURL;
+ winTime.textContent=fmt(elapsed);
+ var key=String(N), prev=best[key];
+ var isBest=!prev||elapsed<prev;
+ if(isBest){ best[key]=Math.round(elapsed); saveBest(); }
+ winSub.textContent=(isBest?'new best for ':'best for ')+N+'×'+N+': '+fmt(best[key])+' · '+moves+' moves';
+ winEl.style.display='flex';
+ }
+
+ function confetti(){
+ var COLS=['#7C5CFF','#FF5A1F','#E14C8F','#F6A93B','#fff'];
+ var r=wrap.getBoundingClientRect();
+ for(var i=0;i<26;i++){ (function(i){
+ var p=document.createElement('span'); p.className='cf';
+ p.style.background=COLS[i%COLS.length];
+ p.style.left=(r.width/2)+'px'; p.style.top=(r.height*0.4)+'px';
+ wrap.appendChild(p);
+ var a=Math.random()*Math.PI*2, v=3+Math.random()*5;
+ var vx=Math.cos(a)*v, vy=Math.sin(a)*v-4, x=0,y=0,rot=0,life=0;
+ function step(){
+ life+=16; x+=vx; y+=vy; vy+=0.25; rot+=9;
+ p.style.transform='translate('+x+'px,'+y+'px) rotate('+rot+'deg)';
+ p.style.opacity=String(Math.max(0,1-life/1000));
+ if(life<1000) requestAnimationFrame(step); else p.remove();
+ }
+ requestAnimationFrame(step);
+ })(i); }
+ }
+
+ function start(){
+ solvedFlag=false; sel=-1; moves=0; elapsed=0; ticking=false;
+ movesPill.textContent='0 moves'; timePill.textContent='0:00';
+ shuffle(); buildGrid();
+ winEl.style.display='none';
+ boardEl.style.display='block'; hud.style.display='flex';
+ diffsEl.style.display='flex'; foot.style.display='flex';
+ }
+
+ pickBtn.addEventListener('click',function(){ fileIn.click(); });
+ fileIn.addEventListener('change',function(){
+ if(!(fileIn.files && fileIn.files[0])) return;
+ var rd=new FileReader();
+ rd.onload=function(){
+ var im=new Image();
+ im.onload=function(){
+ // center-square crop, downscaled — crisp tiles, light memory
+ var S=Math.min(im.width,im.height), out=Math.min(900,S);
+ var c2=document.createElement('canvas'); c2.width=out; c2.height=out;
+ c2.getContext('2d').drawImage(im,(im.width-S)/2,(im.height-S)/2,S,S,0,0,out,out);
+ photoURL=c2.toDataURL('image/jpeg',0.88);
+ peekImg.src=photoURL;
+ pickWrap.style.display='none';
+ start(); pickSnd();
+ };
+ im.src=rd.result;
+ };
+ rd.readAsDataURL(fileIn.files[0]);
+ fileIn.value='';
+ });
+
+ [].forEach.call(diffsEl.children,function(b){
+ b.addEventListener('click',function(){
+ [].forEach.call(diffsEl.children,function(x){ x.classList.remove('on'); });
+ b.classList.add('on');
+ N=+b.getAttribute('data-n');
+ start(); pickSnd();
+ });
+ });
+
+ function peekOn(e){ e.preventDefault(); if(photoURL) peekImg.style.display='block'; }
+ function peekOff(){ peekImg.style.display='none'; }
+ peekBtn.addEventListener('pointerdown',peekOn);
+ peekBtn.addEventListener('pointerup',peekOff);
+ peekBtn.addEventListener('pointercancel',peekOff);
+ peekBtn.addEventListener('pointerleave',peekOff);
+
+ newBtn.addEventListener('click',function(){
+ photoURL=''; ticking=false;
+ boardEl.style.display='none'; hud.style.display='none';
+ diffsEl.style.display='none'; foot.style.display='none';
+ winEl.style.display='none';
+ pickWrap.style.display='block';
+ });
+ againBtn.addEventListener('click',function(){ start(); });
+
+ if(LL && LL.onPause){ try{ LL.onPause(function(p){ paused=p; if(!p) lastT=performance.now(); }); }catch(e){} }
+ document.addEventListener('visibilitychange',function(){ paused=document.hidden; if(!paused) lastT=performance.now(); });
+ window.addEventListener('resize',function(){ if(photoURL && !solvedFlag) buildGrid(); });
+})();
+</script>
← Version history