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

@theo_synth · 6/9/2026, 9:37:44 PM

Initial version — all lines are new.

+<style>
+ html,body{height:100%;margin:0;overflow:hidden;background:#1a1130;}
+ *{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;-webkit-tap-highlight-color:transparent;touch-action:manipulation;user-select:none;-webkit-user-select:none;background:radial-gradient(120% 90% at 50% 0%,#241740 0%,#170f2b 60%,#120b22 100%);}
+ #head{padding:1.15rem 1.25rem .4rem;}
+ #head h1{margin:0;font-family:"Instrument Serif",Georgia,"Times New Roman",serif;font-weight:400;font-size:2.35rem;line-height:1;color:#F3EEFF;}
+ #head p{margin:.2rem 0 0;font-size:.8rem;color:#a99ac9;font-weight:500;}
+ #mid{flex:1;display:flex;align-items:center;justify-content:center;padding:.4rem 1rem;min-height:0;}
+ #grid{display:grid;grid-template-columns:repeat(4,1fr);grid-template-rows:repeat(4,1fr);gap:10px;width:100%;max-width:min(88vw,52vh);aspect-ratio:1;}
+ .pad{position:relative;border:0;border-radius:18px;cursor:pointer;transition:transform .07s,filter .12s;box-shadow:0 4px 14px rgba(0,0,0,.35),inset 0 1px 0 rgba(255,255,255,.18);}
+ .pad span{position:absolute;left:8px;bottom:6px;font-size:.56rem;font-weight:700;letter-spacing:.04em;color:rgba(255,255,255,.65);text-transform:uppercase;pointer-events:none;}
+ .pad.hit{transform:scale(.93);filter:brightness(1.6);}
+ #bar{height:4px;border-radius:99px;background:rgba(255,255,255,.1);margin:0 auto .35rem;width:100%;max-width:min(88vw,52vh);overflow:hidden;}
+ #bar i{display:block;height:100%;width:0;background:linear-gradient(90deg,#7C5CFF,#FF5A1F);border-radius:99px;}
+ #ctl{display:flex;justify-content:center;gap:14px;padding:0 0 1.15rem;}
+ #ctl button{width:52px;height:52px;border-radius:50%;border:1px solid rgba(255,255,255,.16);background:rgba(255,255,255,.08);color:#EBE4FF;font-size:1.05rem;cursor:pointer;backdrop-filter:blur(6px);transition:transform .07s,background .15s;}
+ #ctl button:active{transform:scale(.92);}
+ #rec.on{background:#E14C8F;border-color:#E14C8F;box-shadow:0 0 16px rgba(225,76,143,.55);}
+ #play.on{background:#7C5CFF;border-color:#7C5CFF;}
+ #hintT{text-align:center;font-size:.72rem;color:#8d7fae;font-weight:600;padding-bottom:.5rem;min-height:1.1em;}
+</style>
+<div id="wrap">
+ <div id="head"><h1>Beat Pad</h1><p>Tap pads. Hit &#9679; to loop a groove.</p></div>
+ <div id="mid"><div id="grid"></div></div>
+ <div id="bar"><i id="pos"></i></div>
+ <div id="hintT"></div>
+ <div id="ctl">
+ <button id="rec" aria-label="record">&#9679;</button>
+ <button id="play" aria-label="play">&#9654;</button>
+ <button id="clr" aria-label="clear">&#10005;</button>
+ </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 noiseBuf(c,dur){
+ var n=Math.floor(c.sampleRate*dur), b=c.createBuffer(1,n,c.sampleRate), d=b.getChannelData(0);
+ for(var i=0;i<n;i++){ d[i]=Math.random()*2-1; }
+ return b;
+ }
+ function env(c,peak,dur){ var g=c.createGain(), t=c.currentTime;
+ g.gain.setValueAtTime(0.0001,t); g.gain.exponentialRampToValueAtTime(peak,t+0.008);
+ g.gain.exponentialRampToValueAtTime(0.0001,t+dur); g.connect(c.destination); return g; }
+ function kick(){ var c=actx(); if(!c) return; var t=c.currentTime,o=c.createOscillator();
+ o.type='sine'; o.frequency.setValueAtTime(150,t); o.frequency.exponentialRampToValueAtTime(40,t+0.16);
+ o.connect(env(c,0.5,0.26)); o.start(t); o.stop(t+0.3); }
+ function snare(){ var c=actx(); if(!c) return; var t=c.currentTime;
+ var s=c.createBufferSource(); s.buffer=noiseBuf(c,0.2);
+ var f=c.createBiquadFilter(); f.type='bandpass'; f.frequency.value=1800; f.Q.value=0.8;
+ s.connect(f); f.connect(env(c,0.3,0.18)); s.start(t);
+ var o=c.createOscillator(); o.type='triangle'; o.frequency.setValueAtTime(200,t);
+ o.connect(env(c,0.22,0.1)); o.start(t); o.stop(t+0.12); }
+ function hat(){ var c=actx(); if(!c) return; var t=c.currentTime;
+ var s=c.createBufferSource(); s.buffer=noiseBuf(c,0.07);
+ var f=c.createBiquadFilter(); f.type='highpass'; f.frequency.value=6500;
+ s.connect(f); f.connect(env(c,0.18,0.06)); s.start(t); }
+ function clap(){ var c=actx(); if(!c) return; var t=c.currentTime;
+ for(var k=0;k<2;k++){ (function(k){
+ var s=c.createBufferSource(); s.buffer=noiseBuf(c,0.12);
+ var f=c.createBiquadFilter(); f.type='bandpass'; f.frequency.value=1200; f.Q.value=1.2;
+ var g=c.createGain(); g.gain.setValueAtTime(0.0001,t+k*0.025);
+ g.gain.exponentialRampToValueAtTime(0.26,t+k*0.025+0.006);
+ g.gain.exponentialRampToValueAtTime(0.0001,t+k*0.025+0.12);
+ s.connect(f); f.connect(g); g.connect(c.destination); s.start(t+k*0.025);
+ })(k); } }
+ function tom(f0){ return function(){ var c=actx(); if(!c) return; var t=c.currentTime,o=c.createOscillator();
+ o.type='sine'; o.frequency.setValueAtTime(f0,t); o.frequency.exponentialRampToValueAtTime(f0*0.55,t+0.18);
+ o.connect(env(c,0.35,0.3)); o.start(t); o.stop(t+0.34); }; }
+ function rim(){ var c=actx(); if(!c) return; var t=c.currentTime,o=c.createOscillator();
+ o.type='square'; o.frequency.setValueAtTime(820,t);
+ o.connect(env(c,0.12,0.045)); o.start(t); o.stop(t+0.06); }
+ function shaker(){ var c=actx(); if(!c) return; var t=c.currentTime;
+ var s=c.createBufferSource(); s.buffer=noiseBuf(c,0.1);
+ var f=c.createBiquadFilter(); f.type='highpass'; f.frequency.value=4200;
+ s.connect(f); f.connect(env(c,0.12,0.1)); s.start(t); }
+ function pluck(fq){ return function(){ var c=actx(); if(!c) return; var t=c.currentTime,o=c.createOscillator();
+ o.type='triangle'; o.frequency.setValueAtTime(fq,t);
+ var f=c.createBiquadFilter(); f.type='lowpass'; f.frequency.setValueAtTime(2600,t);
+ f.frequency.exponentialRampToValueAtTime(600,t+0.4);
+ var g=c.createGain(); g.gain.setValueAtTime(0.0001,t); g.gain.exponentialRampToValueAtTime(0.22,t+0.008);
+ g.gain.exponentialRampToValueAtTime(0.0001,t+0.55);
+ o.connect(f); f.connect(g); g.connect(c.destination); o.start(t); o.stop(t+0.6); }; }
+
+ var SOUNDS=[
+ {n:'kick',fn:kick,c:'#FF5A1F'},{n:'snare',fn:snare,c:'#E14C8F'},{n:'hat',fn:hat,c:'#F6A93B'},{n:'clap',fn:clap,c:'#FF7A55'},
+ {n:'tom',fn:tom(220),c:'#C2491F'},{n:'tom hi',fn:tom(330),c:'#D6679C'},{n:'rim',fn:rim,c:'#C98A2E'},{n:'shake',fn:shaker,c:'#E0663F'},
+ {n:'',fn:pluck(261.63),c:'#7C5CFF'},{n:'',fn:pluck(293.66),c:'#8A66FF'},{n:'',fn:pluck(329.63),c:'#9B7BFF'},{n:'',fn:pluck(392.0),c:'#AB8DFF'},
+ {n:'',fn:pluck(440.0),c:'#6B4EFE'},{n:'',fn:pluck(523.25),c:'#7C5CFF'},{n:'',fn:pluck(587.33),c:'#8F6DFF'},{n:'',fn:pluck(659.25),c:'#A481FF'}
+ ];
+
+ var grid=document.getElementById('grid');
+ var posEl=document.getElementById('pos');
+ var hintT=document.getElementById('hintT');
+ var recBtn=document.getElementById('rec'), playBtn=document.getElementById('play'), clrBtn=document.getElementById('clr');
+ var padEls=[];
+ var LOOP=3200;
+ var events=[]; // {at, idx}
+ var recording=false, playing=false, paused=false;
+ var loopStart=0, lastPos=0;
+
+ function flash(i){
+ var el=padEls[i]; el.classList.add('hit');
+ setTimeout(function(){ el.classList.remove('hit'); },110);
+ }
+ function trigger(i,fromLoop){
+ if(!muted) SOUNDS[i].fn();
+ flash(i);
+ if(recording && !fromLoop){
+ events.push({at:(performance.now()-loopStart)%LOOP, idx:i});
+ }
+ }
+
+ for(var i=0;i<16;i++){
+ (function(i){
+ var b=document.createElement('button'); b.className='pad'; b.type='button';
+ b.style.background='linear-gradient(145deg,'+SOUNDS[i].c+'cc,'+SOUNDS[i].c+'88)';
+ if(SOUNDS[i].n){ var s=document.createElement('span'); s.textContent=SOUNDS[i].n; b.appendChild(s); }
+ b.addEventListener('pointerdown',function(e){ e.preventDefault(); trigger(i,false); });
+ grid.appendChild(b); padEls.push(b);
+ })(i);
+ }
+
+ function setHint(t){ hintT.textContent=t; }
+ function startTransport(){ loopStart=performance.now(); lastPos=0; }
+ function stopAll(){ playing=false; recording=false;
+ recBtn.classList.remove('on'); playBtn.classList.remove('on'); playBtn.innerHTML='&#9654;';
+ posEl.style.width='0'; }
+
+ recBtn.addEventListener('pointerdown',function(e){
+ e.preventDefault();
+ if(recording){ // finish recording -> start playing
+ recording=false; recBtn.classList.remove('on');
+ if(events.length){ playing=true; playBtn.classList.add('on'); playBtn.innerHTML='&#10074;&#10074;'; startTransport(); setHint('looping — jam on top'); }
+ else setHint('');
+ return;
+ }
+ recording=true; playing=false;
+ recBtn.classList.add('on'); playBtn.classList.remove('on'); playBtn.innerHTML='&#9654;';
+ events=[]; startTransport(); setHint('recording… tap pads, then ● again');
+ });
+ playBtn.addEventListener('pointerdown',function(e){
+ e.preventDefault();
+ if(recording) return;
+ if(playing){ stopAll(); setHint(''); return; }
+ if(!events.length){ setHint('record a groove first'); return; }
+ playing=true; playBtn.classList.add('on'); playBtn.innerHTML='&#10074;&#10074;'; startTransport(); setHint('looping — jam on top');
+ });
+ clrBtn.addEventListener('pointerdown',function(e){
+ e.preventDefault(); events=[]; stopAll(); setHint('cleared');
+ setTimeout(function(){ if(hintT.textContent==='cleared') setHint(''); },900);
+ });
+
+ function tick(){
+ requestAnimationFrame(tick);
+ if(paused) return;
+ if(!(playing||recording)){ return; }
+ var pos=(performance.now()-loopStart)%LOOP;
+ posEl.style.width=(pos/LOOP*100)+'%';
+ if(playing){
+ for(var i=0;i<events.length;i++){
+ var at=events[i].at;
+ var crossed = lastPos<=pos ? (at>lastPos && at<=pos) : (at>lastPos || at<=pos);
+ if(crossed) trigger(events[i].idx,true);
+ }
+ }
+ lastPos=pos;
+ }
+ requestAnimationFrame(tick);
+
+ if(LL && LL.onPause){ try{ LL.onPause(function(p){ paused=p;
+ if(p){ if(playing||recording) stopAll(); setHint(''); }
+ }); }catch(e){} }
+ document.addEventListener('visibilitychange',function(){ if(document.hidden && (playing||recording)){ stopAll(); setHint(''); } });
+})();
+</script>

v1Current

@theo_synth · 6/9/2026, 9:37:44 PM

Initial version — all lines are new.

+<style>
+ html,body{height:100%;margin:0;overflow:hidden;background:#1a1130;}
+ *{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;-webkit-tap-highlight-color:transparent;touch-action:manipulation;user-select:none;-webkit-user-select:none;background:radial-gradient(120% 90% at 50% 0%,#241740 0%,#170f2b 60%,#120b22 100%);}
+ #head{padding:1.15rem 1.25rem .4rem;}
+ #head h1{margin:0;font-family:"Instrument Serif",Georgia,"Times New Roman",serif;font-weight:400;font-size:2.35rem;line-height:1;color:#F3EEFF;}
+ #head p{margin:.2rem 0 0;font-size:.8rem;color:#a99ac9;font-weight:500;}
+ #mid{flex:1;display:flex;align-items:center;justify-content:center;padding:.4rem 1rem;min-height:0;}
+ #grid{display:grid;grid-template-columns:repeat(4,1fr);grid-template-rows:repeat(4,1fr);gap:10px;width:100%;max-width:min(88vw,52vh);aspect-ratio:1;}
+ .pad{position:relative;border:0;border-radius:18px;cursor:pointer;transition:transform .07s,filter .12s;box-shadow:0 4px 14px rgba(0,0,0,.35),inset 0 1px 0 rgba(255,255,255,.18);}
+ .pad span{position:absolute;left:8px;bottom:6px;font-size:.56rem;font-weight:700;letter-spacing:.04em;color:rgba(255,255,255,.65);text-transform:uppercase;pointer-events:none;}
+ .pad.hit{transform:scale(.93);filter:brightness(1.6);}
+ #bar{height:4px;border-radius:99px;background:rgba(255,255,255,.1);margin:0 auto .35rem;width:100%;max-width:min(88vw,52vh);overflow:hidden;}
+ #bar i{display:block;height:100%;width:0;background:linear-gradient(90deg,#7C5CFF,#FF5A1F);border-radius:99px;}
+ #ctl{display:flex;justify-content:center;gap:14px;padding:0 0 1.15rem;}
+ #ctl button{width:52px;height:52px;border-radius:50%;border:1px solid rgba(255,255,255,.16);background:rgba(255,255,255,.08);color:#EBE4FF;font-size:1.05rem;cursor:pointer;backdrop-filter:blur(6px);transition:transform .07s,background .15s;}
+ #ctl button:active{transform:scale(.92);}
+ #rec.on{background:#E14C8F;border-color:#E14C8F;box-shadow:0 0 16px rgba(225,76,143,.55);}
+ #play.on{background:#7C5CFF;border-color:#7C5CFF;}
+ #hintT{text-align:center;font-size:.72rem;color:#8d7fae;font-weight:600;padding-bottom:.5rem;min-height:1.1em;}
+</style>
+<div id="wrap">
+ <div id="head"><h1>Beat Pad</h1><p>Tap pads. Hit &#9679; to loop a groove.</p></div>
+ <div id="mid"><div id="grid"></div></div>
+ <div id="bar"><i id="pos"></i></div>
+ <div id="hintT"></div>
+ <div id="ctl">
+ <button id="rec" aria-label="record">&#9679;</button>
+ <button id="play" aria-label="play">&#9654;</button>
+ <button id="clr" aria-label="clear">&#10005;</button>
+ </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 noiseBuf(c,dur){
+ var n=Math.floor(c.sampleRate*dur), b=c.createBuffer(1,n,c.sampleRate), d=b.getChannelData(0);
+ for(var i=0;i<n;i++){ d[i]=Math.random()*2-1; }
+ return b;
+ }
+ function env(c,peak,dur){ var g=c.createGain(), t=c.currentTime;
+ g.gain.setValueAtTime(0.0001,t); g.gain.exponentialRampToValueAtTime(peak,t+0.008);
+ g.gain.exponentialRampToValueAtTime(0.0001,t+dur); g.connect(c.destination); return g; }
+ function kick(){ var c=actx(); if(!c) return; var t=c.currentTime,o=c.createOscillator();
+ o.type='sine'; o.frequency.setValueAtTime(150,t); o.frequency.exponentialRampToValueAtTime(40,t+0.16);
+ o.connect(env(c,0.5,0.26)); o.start(t); o.stop(t+0.3); }
+ function snare(){ var c=actx(); if(!c) return; var t=c.currentTime;
+ var s=c.createBufferSource(); s.buffer=noiseBuf(c,0.2);
+ var f=c.createBiquadFilter(); f.type='bandpass'; f.frequency.value=1800; f.Q.value=0.8;
+ s.connect(f); f.connect(env(c,0.3,0.18)); s.start(t);
+ var o=c.createOscillator(); o.type='triangle'; o.frequency.setValueAtTime(200,t);
+ o.connect(env(c,0.22,0.1)); o.start(t); o.stop(t+0.12); }
+ function hat(){ var c=actx(); if(!c) return; var t=c.currentTime;
+ var s=c.createBufferSource(); s.buffer=noiseBuf(c,0.07);
+ var f=c.createBiquadFilter(); f.type='highpass'; f.frequency.value=6500;
+ s.connect(f); f.connect(env(c,0.18,0.06)); s.start(t); }
+ function clap(){ var c=actx(); if(!c) return; var t=c.currentTime;
+ for(var k=0;k<2;k++){ (function(k){
+ var s=c.createBufferSource(); s.buffer=noiseBuf(c,0.12);
+ var f=c.createBiquadFilter(); f.type='bandpass'; f.frequency.value=1200; f.Q.value=1.2;
+ var g=c.createGain(); g.gain.setValueAtTime(0.0001,t+k*0.025);
+ g.gain.exponentialRampToValueAtTime(0.26,t+k*0.025+0.006);
+ g.gain.exponentialRampToValueAtTime(0.0001,t+k*0.025+0.12);
+ s.connect(f); f.connect(g); g.connect(c.destination); s.start(t+k*0.025);
+ })(k); } }
+ function tom(f0){ return function(){ var c=actx(); if(!c) return; var t=c.currentTime,o=c.createOscillator();
+ o.type='sine'; o.frequency.setValueAtTime(f0,t); o.frequency.exponentialRampToValueAtTime(f0*0.55,t+0.18);
+ o.connect(env(c,0.35,0.3)); o.start(t); o.stop(t+0.34); }; }
+ function rim(){ var c=actx(); if(!c) return; var t=c.currentTime,o=c.createOscillator();
+ o.type='square'; o.frequency.setValueAtTime(820,t);
+ o.connect(env(c,0.12,0.045)); o.start(t); o.stop(t+0.06); }
+ function shaker(){ var c=actx(); if(!c) return; var t=c.currentTime;
+ var s=c.createBufferSource(); s.buffer=noiseBuf(c,0.1);
+ var f=c.createBiquadFilter(); f.type='highpass'; f.frequency.value=4200;
+ s.connect(f); f.connect(env(c,0.12,0.1)); s.start(t); }
+ function pluck(fq){ return function(){ var c=actx(); if(!c) return; var t=c.currentTime,o=c.createOscillator();
+ o.type='triangle'; o.frequency.setValueAtTime(fq,t);
+ var f=c.createBiquadFilter(); f.type='lowpass'; f.frequency.setValueAtTime(2600,t);
+ f.frequency.exponentialRampToValueAtTime(600,t+0.4);
+ var g=c.createGain(); g.gain.setValueAtTime(0.0001,t); g.gain.exponentialRampToValueAtTime(0.22,t+0.008);
+ g.gain.exponentialRampToValueAtTime(0.0001,t+0.55);
+ o.connect(f); f.connect(g); g.connect(c.destination); o.start(t); o.stop(t+0.6); }; }
+
+ var SOUNDS=[
+ {n:'kick',fn:kick,c:'#FF5A1F'},{n:'snare',fn:snare,c:'#E14C8F'},{n:'hat',fn:hat,c:'#F6A93B'},{n:'clap',fn:clap,c:'#FF7A55'},
+ {n:'tom',fn:tom(220),c:'#C2491F'},{n:'tom hi',fn:tom(330),c:'#D6679C'},{n:'rim',fn:rim,c:'#C98A2E'},{n:'shake',fn:shaker,c:'#E0663F'},
+ {n:'',fn:pluck(261.63),c:'#7C5CFF'},{n:'',fn:pluck(293.66),c:'#8A66FF'},{n:'',fn:pluck(329.63),c:'#9B7BFF'},{n:'',fn:pluck(392.0),c:'#AB8DFF'},
+ {n:'',fn:pluck(440.0),c:'#6B4EFE'},{n:'',fn:pluck(523.25),c:'#7C5CFF'},{n:'',fn:pluck(587.33),c:'#8F6DFF'},{n:'',fn:pluck(659.25),c:'#A481FF'}
+ ];
+
+ var grid=document.getElementById('grid');
+ var posEl=document.getElementById('pos');
+ var hintT=document.getElementById('hintT');
+ var recBtn=document.getElementById('rec'), playBtn=document.getElementById('play'), clrBtn=document.getElementById('clr');
+ var padEls=[];
+ var LOOP=3200;
+ var events=[]; // {at, idx}
+ var recording=false, playing=false, paused=false;
+ var loopStart=0, lastPos=0;
+
+ function flash(i){
+ var el=padEls[i]; el.classList.add('hit');
+ setTimeout(function(){ el.classList.remove('hit'); },110);
+ }
+ function trigger(i,fromLoop){
+ if(!muted) SOUNDS[i].fn();
+ flash(i);
+ if(recording && !fromLoop){
+ events.push({at:(performance.now()-loopStart)%LOOP, idx:i});
+ }
+ }
+
+ for(var i=0;i<16;i++){
+ (function(i){
+ var b=document.createElement('button'); b.className='pad'; b.type='button';
+ b.style.background='linear-gradient(145deg,'+SOUNDS[i].c+'cc,'+SOUNDS[i].c+'88)';
+ if(SOUNDS[i].n){ var s=document.createElement('span'); s.textContent=SOUNDS[i].n; b.appendChild(s); }
+ b.addEventListener('pointerdown',function(e){ e.preventDefault(); trigger(i,false); });
+ grid.appendChild(b); padEls.push(b);
+ })(i);
+ }
+
+ function setHint(t){ hintT.textContent=t; }
+ function startTransport(){ loopStart=performance.now(); lastPos=0; }
+ function stopAll(){ playing=false; recording=false;
+ recBtn.classList.remove('on'); playBtn.classList.remove('on'); playBtn.innerHTML='&#9654;';
+ posEl.style.width='0'; }
+
+ recBtn.addEventListener('pointerdown',function(e){
+ e.preventDefault();
+ if(recording){ // finish recording -> start playing
+ recording=false; recBtn.classList.remove('on');
+ if(events.length){ playing=true; playBtn.classList.add('on'); playBtn.innerHTML='&#10074;&#10074;'; startTransport(); setHint('looping — jam on top'); }
+ else setHint('');
+ return;
+ }
+ recording=true; playing=false;
+ recBtn.classList.add('on'); playBtn.classList.remove('on'); playBtn.innerHTML='&#9654;';
+ events=[]; startTransport(); setHint('recording… tap pads, then ● again');
+ });
+ playBtn.addEventListener('pointerdown',function(e){
+ e.preventDefault();
+ if(recording) return;
+ if(playing){ stopAll(); setHint(''); return; }
+ if(!events.length){ setHint('record a groove first'); return; }
+ playing=true; playBtn.classList.add('on'); playBtn.innerHTML='&#10074;&#10074;'; startTransport(); setHint('looping — jam on top');
+ });
+ clrBtn.addEventListener('pointerdown',function(e){
+ e.preventDefault(); events=[]; stopAll(); setHint('cleared');
+ setTimeout(function(){ if(hintT.textContent==='cleared') setHint(''); },900);
+ });
+
+ function tick(){
+ requestAnimationFrame(tick);
+ if(paused) return;
+ if(!(playing||recording)){ return; }
+ var pos=(performance.now()-loopStart)%LOOP;
+ posEl.style.width=(pos/LOOP*100)+'%';
+ if(playing){
+ for(var i=0;i<events.length;i++){
+ var at=events[i].at;
+ var crossed = lastPos<=pos ? (at>lastPos && at<=pos) : (at>lastPos || at<=pos);
+ if(crossed) trigger(events[i].idx,true);
+ }
+ }
+ lastPos=pos;
+ }
+ requestAnimationFrame(tick);
+
+ if(LL && LL.onPause){ try{ LL.onPause(function(p){ paused=p;
+ if(p){ if(playing||recording) stopAll(); setHint(''); }
+ }); }catch(e){} }
+ document.addEventListener('visibilitychange',function(){ if(document.hidden && (playing||recording)){ stopAll(); setHint(''); } });
+})();
+</script>
← Version history