@haru_calm · 6/9/2026, 9:37:40 PM
Initial version — all lines are new.
+<style>+ html,body{height:100%;margin:0;overflow:hidden;background:#0c2128;}+ *{box-sizing:border-box;}+ #wrap{position:relative;height:100%;width:100%;font-family:system-ui,-apple-system,Segoe UI,Roboto,sans-serif;-webkit-tap-highlight-color:transparent;touch-action:none;user-select:none;-webkit-user-select:none;}+ canvas{display:block;width:100%;height:100%;position:absolute;inset:0;}+ #head{position:absolute;top:0;left:0;right:0;z-index:2;padding:1.15rem 1.25rem;pointer-events:none;}+ #head h1{margin:0;font-family:"Instrument Serif",Georgia,"Times New Roman",serif;font-weight:400;font-size:2.35rem;line-height:1;color:#EAF6F2;}+ #head p{margin:.2rem 0 0;font-size:.8rem;color:#8fb8ad;font-weight:500;}+ #hint{position:absolute;left:50%;top:52%;transform:translate(-50%,-50%);z-index:2;pointer-events:none;font-family:"Instrument Serif",Georgia,serif;font-size:1.25rem;color:#8fb8ad;opacity:.85;transition:opacity .6s;text-align:center;}+</style>+<div id="wrap">+ <canvas id="cv"></canvas>+ <div id="head">+ <div><h1>Ripple Pond</h1><p>Tap the water. Watch the koi.</p></div>+ </div>+ <div id="hint">tap anywhere</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; }+ // a water "plink": a sine that falls in pitch, like a drop+ function plink(y){+ if(muted) return; var c=actx(); if(!c) return;+ var t=c.currentTime, o=c.createOscillator(), g=c.createGain();+ var f0=560+(1-y)*420+Math.random()*60;+ o.type='sine'; o.frequency.setValueAtTime(f0,t);+ o.frequency.exponentialRampToValueAtTime(f0*0.45,t+0.22);+ g.gain.setValueAtTime(0.0001,t); g.gain.exponentialRampToValueAtTime(0.12,t+0.012);+ g.gain.exponentialRampToValueAtTime(0.0001,t+0.5);+ o.connect(g); g.connect(c.destination); o.start(t); o.stop(t+0.55);+ }++ var wrap=document.getElementById('wrap');+ var cv=document.getElementById('cv'), ctx=cv.getContext('2d');+ var hint=document.getElementById('hint');+ var W=0,H=0,dpr=1,T=0,paused=false;+ var ripples=[], koi=[], pads=[];++ function size(){+ var r=wrap.getBoundingClientRect();+ W=Math.max(180,r.width); H=Math.max(240,r.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);+ pads=[]; var i;+ for(i=0;i<4;i++){ pads.push({x:(0.12+0.76*Math.random())*W, y:(0.2+0.65*Math.random())*H, r:(0.05+Math.random()*0.035)*Math.min(W,H), a:Math.random()*Math.PI*2}); }+ }+ function mkKoi(){+ koi=[];+ var cols=[['#FF7A33','#FFF4E4'],['#F8F3E6','#FF7A33'],['#FFB05C','#FFF4E4']];+ for(var i=0;i<3;i++){+ koi.push({x:(0.2+0.6*Math.random())*W, y:(0.3+0.5*Math.random())*H,+ a:Math.random()*Math.PI*2, sp:0.018+Math.random()*0.012,+ len:34+Math.random()*16, ph:Math.random()*7, turn:0,+ body:cols[i][0], patch:cols[i][1]});+ }+ }++ function bg(){+ var g=ctx.createLinearGradient(0,0,0,H);+ g.addColorStop(0,'#10303a'); g.addColorStop(0.6,'#0c2630'); g.addColorStop(1,'#081c24');+ ctx.fillStyle=g; ctx.fillRect(0,0,W,H);+ // moon shimmer — a soft warm column of light, top right+ var mg=ctx.createRadialGradient(W*0.74,H*0.16,0,W*0.74,H*0.16,Math.min(W,H)*0.5);+ mg.addColorStop(0,'rgba(255,222,160,0.13)'); mg.addColorStop(1,'rgba(255,222,160,0)');+ ctx.fillStyle=mg; ctx.fillRect(0,0,W,H);+ // lily pads+ for(var i=0;i<pads.length;i++){+ var p=pads[i];+ ctx.save(); ctx.translate(p.x,p.y); ctx.rotate(p.a);+ ctx.fillStyle='rgba(34,84,64,0.9)';+ ctx.beginPath(); ctx.ellipse(0,0,p.r,p.r*0.82,0,0.35,Math.PI*2-0.15); ctx.lineTo(0,0); ctx.fill();+ ctx.fillStyle='rgba(70,130,98,0.35)';+ ctx.beginPath(); ctx.ellipse(-p.r*0.2,-p.r*0.15,p.r*0.5,p.r*0.36,0,0,Math.PI*2); ctx.fill();+ ctx.restore();+ }+ }++ function drawKoi(f){+ ctx.save(); ctx.translate(f.x,f.y); ctx.rotate(f.a);+ var w=Math.sin(f.ph)*0.3;+ ctx.globalAlpha=0.92;+ // tail+ ctx.fillStyle=f.body;+ ctx.beginPath();+ ctx.moveTo(-f.len*0.4,0);+ ctx.quadraticCurveTo(-f.len*0.75,w*f.len*0.5,-f.len*0.98,w*f.len*0.85);+ ctx.quadraticCurveTo(-f.len*0.7,w*f.len*0.35+f.len*0.16,-f.len*0.38,5);+ ctx.closePath(); ctx.fill();+ // body+ ctx.beginPath(); ctx.ellipse(0,0,f.len*0.5,f.len*0.19,0,0,Math.PI*2);+ ctx.fillStyle=f.body; ctx.fill();+ // patch (clipped to the body)+ ctx.save(); ctx.clip();+ ctx.fillStyle=f.patch;+ ctx.beginPath(); ctx.ellipse(f.len*0.14,-f.len*0.05,f.len*0.22,f.len*0.13,0.4,0,Math.PI*2); ctx.fill();+ ctx.beginPath(); ctx.ellipse(-f.len*0.2,f.len*0.06,f.len*0.14,f.len*0.09,-0.3,0,Math.PI*2); ctx.fill();+ ctx.restore();+ ctx.globalAlpha=1;+ ctx.restore();+ }++ function stepKoi(f,dt,now){+ f.ph+=f.sp*dt*9;+ f.turn+= (Math.random()-0.5)*0.012*dt;+ f.turn=Math.max(-0.02,Math.min(0.02,f.turn));+ var steer=f.turn;+ // shy away from fresh ripples+ for(var i=0;i<ripples.length;i++){+ var r=ripples[i], age=now-r.t; if(age>1100) continue;+ var dx=f.x-r.x, dy=f.y-r.y, d=Math.hypot(dx,dy);+ if(d<150&&d>1){+ var away=Math.atan2(dy,dx), diff=away-f.a;+ while(diff>Math.PI) diff-=Math.PI*2; while(diff<-Math.PI) diff+=Math.PI*2;+ steer+=diff*0.02*(1-d/150); f.ph+=0.15*dt;+ }+ }+ // keep inside the pond+ var m=46;+ if(f.x<m||f.x>W-m||f.y<m+60||f.y>H-m){+ var toC=Math.atan2(H*0.55-f.y,W*0.5-f.x), diff2=toC-f.a;+ while(diff2>Math.PI) diff2-=Math.PI*2; while(diff2<-Math.PI) diff2+=Math.PI*2;+ steer+=diff2*0.03;+ }+ f.a+=steer*dt;+ f.x+=Math.cos(f.a)*f.sp*f.len*dt; f.y+=Math.sin(f.a)*f.sp*f.len*dt;+ }++ function drawRipples(now){+ for(var i=ripples.length-1;i>=0;i--){+ var r=ripples[i], age=(now-r.t)/1400;+ if(age>=1){ ripples.splice(i,1); continue; }+ for(var k=0;k<3;k++){+ var a2=age-k*0.09; if(a2<0) continue;+ var rad=18+a2*r.max, al=(1-a2)*0.4;+ ctx.strokeStyle='rgba(208,240,228,'+al.toFixed(3)+')';+ ctx.lineWidth=1.6-k*0.4;+ ctx.beginPath(); ctx.ellipse(r.x,r.y,rad,rad*0.62,0,0,Math.PI*2); ctx.stroke();+ }+ }+ }++ var T0=0;+ function tick(now){+ requestAnimationFrame(tick);+ if(paused) return;+ var dt=Math.min(2.4,(now-T0)/16.67); T0=now;+ bg();+ for(var i=0;i<koi.length;i++){ stepKoi(koi[i],dt,now); drawKoi(koi[i]); }+ drawRipples(now);+ }++ var lastDrip=0;+ function splash(e,big){+ var r=cv.getBoundingClientRect(), t=e.touches?e.touches[0]:e;+ var x=t.clientX-r.left, y=t.clientY-r.top;+ ripples.push({x:x,y:y,t:performance.now(),max:big?Math.min(W,H)*0.38:Math.min(W,H)*0.16});+ plink(y/H);+ if(hint.style.opacity!=='0') hint.style.opacity='0';+ }+ wrap.addEventListener('pointerdown',function(e){ splash(e,true); });+ wrap.addEventListener('pointermove',function(e){+ if(!(e.buttons||e.pressure>0)) return;+ e.preventDefault();+ var now=performance.now();+ if(now-lastDrip>160){ lastDrip=now; splash(e,false); }+ });++ if(LL && LL.onPause){ try{ LL.onPause(function(p){ paused=p; if(!p) T0=performance.now(); }); }catch(e){} }+ document.addEventListener('visibilitychange',function(){ paused=document.hidden; if(!paused) T0=performance.now(); });+ window.addEventListener('resize',function(){ size(); });+ size(); mkKoi();+ T0=performance.now(); requestAnimationFrame(tick);+})();+</script>