@liveloop · 5/18/2026, 1:52:26 PM
Initial version — all lines are new.
+<style>+ html,body{height:100%;margin:0;font-family:system-ui,-apple-system,Segoe UI,Roboto,sans-serif;background:#070612;overflow:hidden;color:#e9d5ff;}+ #wrap{position:relative;height:100%;width:100%;display:flex;flex-direction:column;}+ #top{display:flex;align-items:center;justify-content:space-between;padding:.7rem .9rem .2rem;}+ #top h1{margin:0;font-size:1rem;font-weight:900;letter-spacing:.03em;}+ #ctrls{display:flex;gap:.4rem;align-items:center;padding:.4rem .9rem .6rem;}+ #ctrls button{font:inherit;font-size:.78rem;font-weight:800;border:0;border-radius:9999px;padding:.4rem .7rem;background:rgba(255,255,255,.13);color:#e9d5ff;cursor:pointer;-webkit-tap-highlight-color:transparent;}+ #play{background:linear-gradient(90deg,#22d3ee,#a855f7)!important;color:#fff!important;min-width:3.4rem;}+ #tempo{font-size:.78rem;font-weight:800;opacity:.85;min-width:3.4rem;text-align:center;}+ #grid{flex:1;display:block;width:100%;touch-action:none;}+</style>+<div id="wrap">+ <div id="top"><h1>🎚️ Beat Grid</h1></div>+ <div id="ctrls">+ <button id="play" type="button">Play</button>+ <button id="slow" type="button">–</button>+ <span id="tempo">110 BPM</span>+ <button id="fast" type="button">+</button>+ <button id="clear" type="button">Clear</button>+ </div>+ <canvas id="grid"></canvas>+</div>+<script>+(function(){+ var LL=window.liveloop||null;+ var cv=document.getElementById('grid'), ctx=cv.getContext('2d');+ var playBtn=document.getElementById('play'), tempoEl=document.getElementById('tempo');+ var STEPS=16, ROWS=5;+ var NAMES=['Kick','Snare','Hat','Clap','Tom'];+ var COLORS=['#fb7185','#fbbf24','#22d3ee','#a3e635','#c084fc'];+ var pattern=[];+ var W=0,H=0,dpr=1,labelW=0,stepW=0,rowH=0,gridTop=0;+ var tempo=110, playing=false, ac=null;+ var nextStepTime=0, schedStep=0, visStep=-1, schedQ=[], schedTimer=0;+ var raf=0,running=false;++ function defaultPattern(){+ var p=[];+ for(var r=0;r<ROWS;r++){ var row=[]; for(var s=0;s<STEPS;s++) row.push(0); p.push(row); }+ [0,4,8,12].forEach(function(s){ p[0][s]=1; });+ [4,12].forEach(function(s){ p[1][s]=1; });+ [0,2,4,6,8,10,12,14].forEach(function(s){ p[2][s]=1; });+ p[3][12]=1;+ return p;+ }+ function setCanvas(){+ var rc=cv.getBoundingClientRect();+ W=Math.max(220,rc.width); H=Math.max(160,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);+ labelW=Math.round(W*0.17);+ stepW=(W-labelW-W*0.02)/STEPS;+ rowH=H/ROWS;+ gridTop=0;+ }+ function noiseBuf(a,dur){+ var n=Math.floor(a.sampleRate*dur), b=a.createBuffer(1,n,a.sampleRate), d=b.getChannelData(0);+ for(var i=0;i<n;i++) d[i]=Math.random()*2-1;+ return b;+ }+ function kick(a,t){+ var o=a.createOscillator(),g=a.createGain();+ o.frequency.setValueAtTime(155,t); o.frequency.exponentialRampToValueAtTime(48,t+0.12);+ g.gain.setValueAtTime(1,t); g.gain.exponentialRampToValueAtTime(0.001,t+0.3);+ o.connect(g).connect(a.destination); o.start(t); o.stop(t+0.32);+ }+ function snare(a,t){+ var s=a.createBufferSource(); s.buffer=noiseBuf(a,0.2);+ var hp=a.createBiquadFilter(); hp.type='highpass'; hp.frequency.value=1200;+ var g=a.createGain(); g.gain.setValueAtTime(0.8,t); g.gain.exponentialRampToValueAtTime(0.001,t+0.18);+ s.connect(hp).connect(g).connect(a.destination); s.start(t); s.stop(t+0.2);+ var o=a.createOscillator(),og=a.createGain(); o.type='triangle'; o.frequency.value=190;+ og.gain.setValueAtTime(0.45,t); og.gain.exponentialRampToValueAtTime(0.001,t+0.1);+ o.connect(og).connect(a.destination); o.start(t); o.stop(t+0.11);+ }+ function hat(a,t){+ var s=a.createBufferSource(); s.buffer=noiseBuf(a,0.05);+ var hp=a.createBiquadFilter(); hp.type='highpass'; hp.frequency.value=7600;+ var g=a.createGain(); g.gain.setValueAtTime(0.4,t); g.gain.exponentialRampToValueAtTime(0.001,t+0.05);+ s.connect(hp).connect(g).connect(a.destination); s.start(t); s.stop(t+0.06);+ }+ function clap(a,t){+ for(var k=0;k<3;k++){+ var s=a.createBufferSource(); s.buffer=noiseBuf(a,0.09);+ var bp=a.createBiquadFilter(); bp.type='bandpass'; bp.frequency.value=1600;+ var g=a.createGain(), tt=t+k*0.02;+ g.gain.setValueAtTime(0.5,tt); g.gain.exponentialRampToValueAtTime(0.001,tt+0.09);+ s.connect(bp).connect(g).connect(a.destination); s.start(tt); s.stop(tt+0.1);+ }+ }+ function tom(a,t){+ var o=a.createOscillator(),g=a.createGain();+ o.frequency.setValueAtTime(200,t); o.frequency.exponentialRampToValueAtTime(95,t+0.22);+ g.gain.setValueAtTime(0.8,t); g.gain.exponentialRampToValueAtTime(0.001,t+0.28);+ o.connect(g).connect(a.destination); o.start(t); o.stop(t+0.3);+ }+ var VOICES=[kick,snare,hat,clap,tom];+ function trigger(r,t){ try{ VOICES[r](ac,t); }catch(e){} }+ function stepDur(){ return 60/tempo/4; }+ function scheduler(){+ if(!ac||!playing) return;+ while(nextStepTime < ac.currentTime+0.12){+ for(var r=0;r<ROWS;r++) if(pattern[r][schedStep]) trigger(r,nextStepTime);+ schedQ.push({s:schedStep,t:nextStepTime});+ nextStepTime+=stepDur();+ schedStep=(schedStep+1)%STEPS;+ }+ }+ function startPlay(){+ if(!ac){+ var AC=window.AudioContext||window.webkitAudioContext;+ if(!AC) return;+ try{ ac=new AC(); }catch(e){ return; }+ }+ if(ac.state!=='running'&&ac.resume){ try{ ac.resume(); }catch(e){} }+ playing=true; playBtn.textContent='Stop';+ schedStep=0; visStep=-1; schedQ=[];+ nextStepTime=ac.currentTime+0.06;+ if(schedTimer) clearInterval(schedTimer);+ schedTimer=setInterval(scheduler,25);+ scheduler();+ }+ function stopPlay(){+ playing=false; playBtn.textContent='Play'; visStep=-1;+ if(schedTimer){ clearInterval(schedTimer); schedTimer=0; }+ }+ function save(){+ if(LL&&LL.storage&&LL.storage.set){+ try{ LL.storage.set({pattern:pattern,tempo:tempo}); }catch(e){}+ }+ }+ function rr(x,y,w,h,rad){+ var r=Math.min(rad,w/2,h/2);+ ctx.beginPath();+ ctx.moveTo(x+r,y);+ ctx.arcTo(x+w,y,x+w,y+h,r);+ ctx.arcTo(x+w,y+h,x,y+h,r);+ ctx.arcTo(x,y+h,x,y,r);+ ctx.arcTo(x,y,x+w,y,r);+ ctx.closePath();+ }+ function render(){+ ctx.fillStyle='#070612'; ctx.fillRect(0,0,W,H);+ if(playing&&ac){+ while(schedQ.length && schedQ[0].t<=ac.currentTime){ visStep=schedQ[0].s; schedQ.shift(); }+ }+ for(var r=0;r<ROWS;r++){+ var y=r*rowH;+ ctx.fillStyle=COLORS[r];+ ctx.font='800 '+Math.round(rowH*0.2)+'px system-ui';+ ctx.textAlign='left'; ctx.textBaseline='middle';+ ctx.fillText(NAMES[r],W*0.03,y+rowH/2);+ for(var s=0;s<STEPS;s++){+ var x=labelW+s*stepW;+ var beat=Math.floor(s/4)%2===0;+ var on=pattern[r][s];+ var cx=x+stepW*0.5, cy=y+rowH*0.5;+ var w=stepW*0.78, h=rowH*0.62;+ if(s===visStep){+ ctx.fillStyle='rgba(255,255,255,0.1)';+ ctx.fillRect(x,y,stepW,rowH);+ }+ ctx.fillStyle=on?COLORS[r]:(beat?'rgba(255,255,255,0.09)':'rgba(255,255,255,0.045)');+ rr(cx-w/2,cy-h/2,w,h,Math.min(w,h)*0.28); ctx.fill();+ if(on){+ ctx.fillStyle='rgba(255,255,255,0.3)';+ rr(cx-w/2+w*0.16,cy-h/2+h*0.14,w*0.68,h*0.22,h*0.1); ctx.fill();+ }+ }+ }+ }+ function frame(){+ if(!running) return;+ render();+ raf=requestAnimationFrame(frame);+ }+ function startLoop(){ if(running)return; running=true; raf=requestAnimationFrame(frame); }+ function stopLoop(){ running=false; if(raf)cancelAnimationFrame(raf); raf=0; }+ function cellAt(px,py){+ if(px<labelW) return null;+ var s=Math.floor((px-labelW)/stepW), r=Math.floor(py/rowH);+ if(s<0||s>=STEPS||r<0||r>=ROWS) return null;+ return {r:r,s:s};+ }+ cv.addEventListener('pointerdown',function(e){+ var rc=cv.getBoundingClientRect();+ var c=cellAt(e.clientX-rc.left,e.clientY-rc.top);+ if(!c) return;+ pattern[c.r][c.s]=pattern[c.r][c.s]?0:1;+ if(pattern[c.r][c.s] && ac && ac.state==='running') trigger(c.r,ac.currentTime+0.01);+ save();+ });+ playBtn.addEventListener('click',function(){ if(playing) stopPlay(); else startPlay(); });+ document.getElementById('slow').addEventListener('click',function(){+ tempo=Math.max(60,tempo-10); tempoEl.textContent=tempo+' BPM'; save();+ });+ document.getElementById('fast').addEventListener('click',function(){+ tempo=Math.min(190,tempo+10); tempoEl.textContent=tempo+' BPM'; save();+ });+ document.getElementById('clear').addEventListener('click',function(){+ for(var r=0;r<ROWS;r++) for(var s=0;s<STEPS;s++) pattern[r][s]=0;+ save();+ });+ function onResize(){ setCanvas(); }+ window.addEventListener('resize',onResize);+ if(LL && LL.onResize) LL.onResize(onResize);+ if(LL && LL.onVisibility) LL.onVisibility(function(v){+ if(!v && playing) stopPlay();+ if(v) startLoop(); else stopLoop();+ });+ document.addEventListener('visibilitychange',function(){+ if(document.hidden){ if(playing) stopPlay(); stopLoop(); } else startLoop();+ });++ pattern=defaultPattern();+ setCanvas();+ tempoEl.textContent=tempo+' BPM';+ if(LL&&LL.storage&&LL.storage.get){+ LL.storage.get().then(function(s){+ if(s){+ if(s.pattern&&s.pattern.length===ROWS){+ var ok=true;+ for(var r=0;r<ROWS;r++) if(!s.pattern[r]||s.pattern[r].length!==STEPS) ok=false;+ if(ok) pattern=s.pattern;+ }+ if(typeof s.tempo==='number'&&s.tempo>=60&&s.tempo<=190){+ tempo=s.tempo; tempoEl.textContent=tempo+' BPM';+ }+ }+ },function(){});+ }+ startLoop();+})();+</script>