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

@jaymakes · 5/26/2026, 4:21:30 PM

Initial version — all lines are new.

+<style>
+ html,body{margin:0;height:100%;background:#F3EFFE;overflow:hidden;font-family:"Geist",ui-sans-serif,system-ui,sans-serif;color:#16110D;-webkit-font-smoothing:antialiased;touch-action:none;user-select:none}
+ .wrap{position:absolute;inset:0;display:flex;flex-direction:column;padding:24px 22px}
+ .badge{display:inline-flex;align-items:center;gap:6px;align-self:flex-start;background:rgba(255,255,255,0.7);backdrop-filter:blur(10px);border:1px solid rgba(22,17,13,0.06);border-radius:9999px;padding:6px 12px;font-size:12px;font-weight:500;margin-bottom:6px}
+ .badge i{width:6px;height:6px;background:#FF5A1F;border-radius:50%;animation:p 1.2s infinite}
+ @keyframes p{0%,100%{opacity:1}50%{opacity:.3}}
+ h1{font-family:"Instrument Serif",Georgia,serif;font-weight:400;font-size:30px;line-height:1.05;margin:0 0 4px;letter-spacing:-.01em}
+ .sub{font-size:13px;color:#5C544C;margin-bottom:16px}
+ .canvasWrap{flex:1;position:relative;border-radius:28px;background:#fff;border:1px solid rgba(22,17,13,0.06);box-shadow:0 1px 0 rgba(22,17,13,0.04),0 24px 60px -24px rgba(107,78,254,0.35);overflow:hidden}
+ canvas{display:block;width:100%;height:100%;cursor:crosshair}
+ .hint{position:absolute;bottom:14px;left:50%;transform:translateX(-50%);font-size:11px;color:#8F857B;background:rgba(255,255,255,0.6);backdrop-filter:blur(8px);padding:6px 12px;border-radius:9999px;pointer-events:none}
+ .ctl{margin-top:14px;display:grid;grid-template-columns:1fr 1fr;gap:10px}
+ .ctl button{background:#fff;border:1px solid rgba(22,17,13,0.08);border-radius:14px;padding:10px;cursor:pointer;font-family:inherit;font-size:13px;color:#16110D;display:flex;align-items:center;justify-content:center;gap:8px;font-weight:500}
+ .ctl button.active{background:#16110D;color:#F4EEE3;border-color:#16110D}
+ .ctl button:active{transform:scale(.97)}
+ .ctl .dot{width:10px;height:10px;border-radius:50%}
+</style>
+<div class="wrap">
+ <span class="badge"><i></i>tap &amp; drag to draw waves</span>
+ <h1>wave.toy</h1>
+ <div class="sub">a little frequency painter</div>
+ <div class="canvasWrap">
+ <canvas id="c"></canvas>
+ <div class="hint" id="hint">tap anywhere</div>
+ </div>
+ <div class="ctl">
+ <button data-m="ripple" class="active"><span class="dot" style="background:#6B4EFE"></span>ripple</button>
+ <button data-m="trail"><span class="dot" style="background:#FF5A1F"></span>trail</button>
+ </div>
+</div>
+<script>
+(()=>{
+ const c=document.getElementById('c'),x=c.getContext('2d');
+ function resize(){const r=c.getBoundingClientRect();c.width=r.width*devicePixelRatio;c.height=r.height*devicePixelRatio;x.scale(devicePixelRatio,devicePixelRatio)}
+ resize();addEventListener('resize',resize);
+ let mode='ripple';
+ const ripples=[],trail=[];
+ function loop(){
+ const r=c.getBoundingClientRect();
+ x.fillStyle=mode==='trail'?'rgba(243,239,254,0.12)':'rgba(243,239,254,0.18)';
+ x.fillRect(0,0,r.width,r.height);
+ for(let i=ripples.length-1;i>=0;i--){
+ const r0=ripples[i];r0.age++;const rad=r0.age*1.6;
+ const a=Math.max(0,1-r0.age/80);
+ if(a<=0){ripples.splice(i,1);continue}
+ x.strokeStyle='hsla('+r0.h+' 90% 60% / '+(a*0.85)+')';
+ x.lineWidth=2.5;
+ x.beginPath();x.arc(r0.x,r0.y,rad,0,Math.PI*2);x.stroke();
+ x.strokeStyle='hsla('+r0.h+' 90% 70% / '+(a*0.4)+')';
+ x.lineWidth=1;
+ x.beginPath();x.arc(r0.x,r0.y,rad*1.4,0,Math.PI*2);x.stroke();
+ }
+ for(let i=trail.length-1;i>=0;i--){
+ const t=trail[i];t.age++;const a=Math.max(0,1-t.age/60);
+ if(a<=0){trail.splice(i,1);continue}
+ x.fillStyle='hsla('+t.h+' 90% 60% / '+a+')';
+ x.beginPath();x.arc(t.x,t.y,t.r*a,0,Math.PI*2);x.fill();
+ }
+ requestAnimationFrame(loop);
+ }
+ let hue=260,drawing=false;
+ function pos(e){const r=c.getBoundingClientRect();const p=e.touches?e.touches[0]:e;return {x:p.clientX-r.left,y:p.clientY-r.top}}
+ function add(e){
+ const {x:px,y:py}=pos(e);hue=(hue+8)%360;
+ if(mode==='ripple') ripples.push({x:px,y:py,age:0,h:hue});
+ else trail.push({x:px,y:py,age:0,h:hue,r:8+Math.random()*8});
+ document.getElementById('hint').style.opacity=0;
+ }
+ c.addEventListener('pointerdown',e=>{drawing=true;add(e)});
+ c.addEventListener('pointermove',e=>{if(drawing)add(e)});
+ c.addEventListener('pointerup',()=>drawing=false);
+ c.addEventListener('pointerleave',()=>drawing=false);
+ document.querySelectorAll('.ctl button').forEach(b=>{
+ b.addEventListener('click',()=>{
+ mode=b.dataset.m;
+ document.querySelectorAll('.ctl button').forEach(o=>o.classList.toggle('active',o===b));
+ });
+ });
+ loop();
+})();
+</script>

v1Current

@jaymakes · 5/26/2026, 4:21:30 PM

Initial version — all lines are new.

+<style>
+ html,body{margin:0;height:100%;background:#F3EFFE;overflow:hidden;font-family:"Geist",ui-sans-serif,system-ui,sans-serif;color:#16110D;-webkit-font-smoothing:antialiased;touch-action:none;user-select:none}
+ .wrap{position:absolute;inset:0;display:flex;flex-direction:column;padding:24px 22px}
+ .badge{display:inline-flex;align-items:center;gap:6px;align-self:flex-start;background:rgba(255,255,255,0.7);backdrop-filter:blur(10px);border:1px solid rgba(22,17,13,0.06);border-radius:9999px;padding:6px 12px;font-size:12px;font-weight:500;margin-bottom:6px}
+ .badge i{width:6px;height:6px;background:#FF5A1F;border-radius:50%;animation:p 1.2s infinite}
+ @keyframes p{0%,100%{opacity:1}50%{opacity:.3}}
+ h1{font-family:"Instrument Serif",Georgia,serif;font-weight:400;font-size:30px;line-height:1.05;margin:0 0 4px;letter-spacing:-.01em}
+ .sub{font-size:13px;color:#5C544C;margin-bottom:16px}
+ .canvasWrap{flex:1;position:relative;border-radius:28px;background:#fff;border:1px solid rgba(22,17,13,0.06);box-shadow:0 1px 0 rgba(22,17,13,0.04),0 24px 60px -24px rgba(107,78,254,0.35);overflow:hidden}
+ canvas{display:block;width:100%;height:100%;cursor:crosshair}
+ .hint{position:absolute;bottom:14px;left:50%;transform:translateX(-50%);font-size:11px;color:#8F857B;background:rgba(255,255,255,0.6);backdrop-filter:blur(8px);padding:6px 12px;border-radius:9999px;pointer-events:none}
+ .ctl{margin-top:14px;display:grid;grid-template-columns:1fr 1fr;gap:10px}
+ .ctl button{background:#fff;border:1px solid rgba(22,17,13,0.08);border-radius:14px;padding:10px;cursor:pointer;font-family:inherit;font-size:13px;color:#16110D;display:flex;align-items:center;justify-content:center;gap:8px;font-weight:500}
+ .ctl button.active{background:#16110D;color:#F4EEE3;border-color:#16110D}
+ .ctl button:active{transform:scale(.97)}
+ .ctl .dot{width:10px;height:10px;border-radius:50%}
+</style>
+<div class="wrap">
+ <span class="badge"><i></i>tap &amp; drag to draw waves</span>
+ <h1>wave.toy</h1>
+ <div class="sub">a little frequency painter</div>
+ <div class="canvasWrap">
+ <canvas id="c"></canvas>
+ <div class="hint" id="hint">tap anywhere</div>
+ </div>
+ <div class="ctl">
+ <button data-m="ripple" class="active"><span class="dot" style="background:#6B4EFE"></span>ripple</button>
+ <button data-m="trail"><span class="dot" style="background:#FF5A1F"></span>trail</button>
+ </div>
+</div>
+<script>
+(()=>{
+ const c=document.getElementById('c'),x=c.getContext('2d');
+ function resize(){const r=c.getBoundingClientRect();c.width=r.width*devicePixelRatio;c.height=r.height*devicePixelRatio;x.scale(devicePixelRatio,devicePixelRatio)}
+ resize();addEventListener('resize',resize);
+ let mode='ripple';
+ const ripples=[],trail=[];
+ function loop(){
+ const r=c.getBoundingClientRect();
+ x.fillStyle=mode==='trail'?'rgba(243,239,254,0.12)':'rgba(243,239,254,0.18)';
+ x.fillRect(0,0,r.width,r.height);
+ for(let i=ripples.length-1;i>=0;i--){
+ const r0=ripples[i];r0.age++;const rad=r0.age*1.6;
+ const a=Math.max(0,1-r0.age/80);
+ if(a<=0){ripples.splice(i,1);continue}
+ x.strokeStyle='hsla('+r0.h+' 90% 60% / '+(a*0.85)+')';
+ x.lineWidth=2.5;
+ x.beginPath();x.arc(r0.x,r0.y,rad,0,Math.PI*2);x.stroke();
+ x.strokeStyle='hsla('+r0.h+' 90% 70% / '+(a*0.4)+')';
+ x.lineWidth=1;
+ x.beginPath();x.arc(r0.x,r0.y,rad*1.4,0,Math.PI*2);x.stroke();
+ }
+ for(let i=trail.length-1;i>=0;i--){
+ const t=trail[i];t.age++;const a=Math.max(0,1-t.age/60);
+ if(a<=0){trail.splice(i,1);continue}
+ x.fillStyle='hsla('+t.h+' 90% 60% / '+a+')';
+ x.beginPath();x.arc(t.x,t.y,t.r*a,0,Math.PI*2);x.fill();
+ }
+ requestAnimationFrame(loop);
+ }
+ let hue=260,drawing=false;
+ function pos(e){const r=c.getBoundingClientRect();const p=e.touches?e.touches[0]:e;return {x:p.clientX-r.left,y:p.clientY-r.top}}
+ function add(e){
+ const {x:px,y:py}=pos(e);hue=(hue+8)%360;
+ if(mode==='ripple') ripples.push({x:px,y:py,age:0,h:hue});
+ else trail.push({x:px,y:py,age:0,h:hue,r:8+Math.random()*8});
+ document.getElementById('hint').style.opacity=0;
+ }
+ c.addEventListener('pointerdown',e=>{drawing=true;add(e)});
+ c.addEventListener('pointermove',e=>{if(drawing)add(e)});
+ c.addEventListener('pointerup',()=>drawing=false);
+ c.addEventListener('pointerleave',()=>drawing=false);
+ document.querySelectorAll('.ctl button').forEach(b=>{
+ b.addEventListener('click',()=>{
+ mode=b.dataset.m;
+ document.querySelectorAll('.ctl button').forEach(o=>o.classList.toggle('active',o===b));
+ });
+ });
+ loop();
+})();
+</script>
← Version history