@theo_synth · 6/9/2026, 8:17:45 PM
Initial version — all lines are new.
+<style>+ html,body{height:100%;margin:0;overflow:hidden;background:#F4EEE3;}+ *{box-sizing:border-box;}+ #wrap{position:relative;height:100%;width:100%;overflow:hidden;font-family:system-ui,-apple-system,Segoe UI,Roboto,sans-serif;-webkit-tap-highlight-color:transparent;user-select:none;-webkit-user-select:none;background:linear-gradient(180deg,#F8F3EA,#F1E7D7);display:flex;flex-direction:column;}+ #wrap::before,#wrap::after{content:"";position:absolute;border-radius:50%;filter:blur(60px);pointer-events:none;z-index:0;}+ #wrap::before{width:62%;height:30%;right:-8%;top:-6%;background:radial-gradient(circle,rgba(232,226,255,.9),transparent 70%);}+ #wrap::after{width:62%;height:30%;left:-10%;bottom:6%;background:radial-gradient(circle,rgba(255,227,210,.85),transparent 70%);}+ #head{position:relative;z-index:2;padding:1.1rem 1.25rem .2rem;}+ #head h1{margin:0;font-family:"Instrument Serif",Georgia,"Times New Roman",serif;font-weight:400;font-size:2.35rem;line-height:1;color:#16110D;}+ #head p{margin:.25rem 0 0;font-size:.82rem;color:#8F857B;font-weight:500;}+ #keys{position:relative;z-index:2;flex:1;display:flex;flex-direction:column;gap:.5rem;padding:.7rem .9rem;min-height:0;}+ .key{flex:1;border:0;border-radius:18px;cursor:pointer;position:relative;overflow:hidden;box-shadow:0 6px 18px rgba(60,32,217,.10);transition:transform .06s ease, filter .06s ease;display:flex;align-items:center;padding:0 1.2rem;color:rgba(255,255,255,.92);font-weight:800;font-size:.8rem;letter-spacing:.08em;}+ .key span{position:relative;z-index:2;text-shadow:0 1px 2px rgba(0,0,0,.18);}+ .key::after{content:"";position:absolute;left:0;top:0;bottom:0;width:42%;background:linear-gradient(90deg,rgba(255,255,255,.28),transparent);}+ .key.down{transform:scale(.985);filter:brightness(1.18) saturate(1.1);}+ #bar{position:relative;z-index:2;display:flex;align-items:center;gap:.6rem;padding:.4rem .95rem calc(.9rem + env(safe-area-inset-bottom));}+ #rec{border:0;border-radius:999px;height:2.7rem;padding:0 1.1rem;display:flex;align-items:center;gap:.5rem;font-weight:800;font-size:.85rem;cursor:pointer;background:#fff;color:#16110D;box-shadow:0 4px 14px rgba(0,0,0,.08);}+ #rec .dot{width:.7rem;height:.7rem;border-radius:50%;background:#E14C8F;}+ #rec.on{background:#E14C8F;color:#fff;}+ #rec.on .dot{background:#fff;animation:blink 1s steps(2,start) infinite;}+ @keyframes blink{50%{opacity:.25}}+ #status{font-size:.78rem;color:#8F857B;font-weight:600;}+ #soundhint{position:absolute;z-index:3;left:50%;top:.9rem;transform:translateX(-50%);background:#16110D;color:#fff;font-size:.72rem;font-weight:700;padding:.3rem .8rem;border-radius:999px;opacity:.92;pointer-events:none;}+ #soundhint.hide{display:none;}+</style>+<div id="wrap">+ <div id="head"><h1>Pocket Piano</h1><p>Tap the bars. Hit record to loop a little tune.</p></div>+ <div id="soundhint">turn on sound ◐</div>+ <div id="keys"></div>+ <div id="bar">+ <button id="rec"><span class="dot"></span><span id="reclabel">Record</span></button>+ <div id="status"></div>+ </div>+</div>+<script>+(function(){+ var LL=window.liveloop||null;+ // sound: this loop IS sound, so declare it (chrome draws the control) and+ // gate on the feed-wide mute. SDK auto-unlocks the AudioContext on tap.+ var muted = LL ? LL.muted : true;+ var hint=document.getElementById('soundhint');+ function refreshHint(){ if(hint) hint.className = muted ? '' : 'hide'; }+ if(LL && LL.onMute){ try{ LL.onMute(function(m){ muted=m; refreshHint(); }); }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 note(freq){+ if(muted) return; var c=actx(); if(!c) return;+ var t=c.currentTime, o1=c.createOscillator(), o2=c.createOscillator(), g=c.createGain();+ o1.type='triangle'; o2.type='sine';+ o1.frequency.setValueAtTime(freq,t); o2.frequency.setValueAtTime(freq*2,t);+ g.gain.setValueAtTime(0.0001,t); g.gain.exponentialRampToValueAtTime(0.22,t+0.008);+ g.gain.exponentialRampToValueAtTime(0.0001,t+0.9);+ var g2=c.createGain(); g2.gain.value=0.32; o2.connect(g2); g2.connect(g);+ o1.connect(g); g.connect(c.destination);+ o1.start(t); o2.start(t); o1.stop(t+0.95); o2.stop(t+0.95);+ }++ // Pentatonic C major across ~1.5 octaves — any combination sounds pleasant.+ var NOTES=[+ {n:'D5',f:587.33}, {n:'C5',f:523.25}, {n:'A4',f:440.00},+ {n:'G4',f:392.00}, {n:'E4',f:329.63}, {n:'D4',f:293.66}, {n:'C4',f:261.63}+ ];+ var GRAD=[['#7C5CFF','#9B7BFF'],['#6B4EFE','#8466FF'],['#E14C8F','#F37AAE'],+ ['#3C9A6A','#5FC68C'],['#F6A93B','#FBC56E'],['#FF7A3C','#FF9A63'],['#FF5A1F','#FF7A48']];+ var keysEl=document.getElementById('keys'), keyEls=[];+ NOTES.forEach(function(nt,i){+ var b=document.createElement('button'); b.className='key';+ b.style.background='linear-gradient(135deg,'+GRAD[i][1]+','+GRAD[i][0]+')';+ b.innerHTML='<span>'+nt.n+'</span>';+ b.addEventListener('pointerdown',function(e){ e.preventDefault(); hit(i); });+ keysEl.appendChild(b); keyEls.push(b);+ });++ function press(i){ var b=keyEls[i]; b.classList.add('down'); setTimeout(function(){ b.classList.remove('down'); },110); }+ function hit(i, fromLoop){+ press(i); note(NOTES[i].f);+ if(recording && !fromLoop){ seq.push({i:i, t:performance.now()-recStart}); }+ }++ // --- record + loop ---+ var rec=document.getElementById('rec'), reclabel=document.getElementById('reclabel'), statusEl=document.getElementById('status');+ var recording=false, seq=[], recStart=0, loop=null, loopLen=0, raf=0, paused=false, playIdx=0, loopStart=0;+ rec.addEventListener('click',function(){+ if(!recording && !loop){ // start recording+ recording=true; seq=[]; recStart=performance.now(); rec.classList.add('on'); reclabel.textContent='Stop'; statusEl.textContent='recording…';+ } else if(recording){ // stop -> loop if we captured anything+ recording=false; rec.classList.remove('on'); reclabel.textContent='Record';+ if(seq.length>0){ loopLen=Math.max(1200,(performance.now()-recStart)+400); startLoop(); reclabel.textContent='Clear'; statusEl.textContent='looping · '+seq.length+' notes'; }+ else statusEl.textContent='';+ } else { // clear the loop+ loop=null; reclabel.textContent='Record'; statusEl.textContent='';+ }+ });+ function startLoop(){ loop=seq.slice(); playIdx=0; loopStart=performance.now(); }+ function tick(now){+ raf=requestAnimationFrame(tick);+ if(paused || !loop) return;+ var el=now-loopStart;+ while(playIdx<loop.length && loop[playIdx].t<=el){ hit(loop[playIdx].i, true); playIdx++; }+ if(el>=loopLen){ loopStart=now; playIdx=0; }+ }++ if(LL && LL.onPause){ try{ LL.onPause(function(p){ paused=p; if(!p) loopStart=performance.now()-0; }); }catch(e){} }+ document.addEventListener('visibilitychange',function(){ paused=document.hidden; });+ refreshHint();+ raf=requestAnimationFrame(tick);+})();+</script>