@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">🧩 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×3</button>+ <button class="diff on" data-n="4">4×4</button>+ <button class="diff" data-n="5">5×5</button>+ </div>+ <div id="board">+ <div id="grid"></div>+ <img id="peekImg" alt="">+ </div>+ </div>+ <div id="foot">+ <button id="peek">👀 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>