Split the gallery into sections with a divider, start the colored set

Generators now live in gen/<section>/<subject>/<action>.js; the section
folder picks where a tile shows in the gallery and isn't part of the URL.
The first 50 are in gen/bw, a dashed divider follows, then gen/color.

First colored ones: fox/sleep, pig/oink, duck/swim, whale/spout,
turtle/walk, owl/blink, crab/dance, butterfly/flutter, dino/stomp,
jellyfish/pulse.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-22 20:45:17 +00:00
co-authored by Claude Opus 5.5
parent 8842194c3c
commit ab7e0dc0df
77 changed files with 275 additions and 8 deletions
+16
View File
@@ -0,0 +1,16 @@
// envelope flap opens, a letter with a beating heart slides out, then tucks back in and the flap closes
const{sin,cos,PI,min,max}=Math,T=2*PI
export const frames=24,delay=70
const ease=u=>(u=min(1,max(0,u)),u<.5?2*u*u:1-2*(1-u)**2)
export default(c,t)=>{
const fy=1-2*ease(t/.18)+2*ease((t-.82)/.18),ly=118-78*ease((t-.18)/.25)+78*ease((t-.62)/.2),hb=1+.15*sin(2*T*t*3)
const flap=()=>{c.beginPath();c.moveTo(48,110);c.lineTo(128,110+64*fy);c.lineTo(208,110);c.closePath();c.fill();c.stroke()}
c.beginPath();c.roundRect(48,108,160,108,10);c.fill();c.stroke()
if(fy<0)flap()
c.beginPath();c.rect(68,ly,120,96);c.fill();c.stroke()
c.lineWidth=4;for(const y of[64,76])c.beginPath(),c.moveTo(92,ly+y),c.lineTo(164,ly+y),c.stroke()
c.save();c.translate(128,ly+32);c.scale(hb,hb);c.fillStyle='#111';c.beginPath();c.moveTo(0,16);c.bezierCurveTo(-26,-2,-14,-22,0,-10);c.bezierCurveTo(14,-22,26,-2,0,16);c.fill();c.restore()
c.lineWidth=7;c.fillStyle='#fff'
c.beginPath();c.moveTo(48,116);c.lineTo(128,172);c.lineTo(208,116);c.lineTo(208,206);c.quadraticCurveTo(208,216,198,216);c.lineTo(58,216);c.quadraticCurveTo(48,216,48,206);c.closePath();c.fill();c.stroke()
if(fy>=0)flap()
}