Add an anime section after a second divider with five chibi girls

catgirl/wave, twintails/wink, ponytail/blush, bob/nom, witch/spell.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-22 21:14:34 +00:00
co-authored by Claude Opus 5.5
parent 045754de53
commit 7cebe4a05e
17 changed files with 259 additions and 2 deletions
+49
View File
@@ -0,0 +1,49 @@
// chibi girl with a blue bob munching a rice ball: bite, then happy chewing with puffed cheeks, twice
const{sin,cos,PI,max,min}=Math,T=2*PI
export const frames=16,delay=90
const SKIN='#ffe3d1',HAIR='#4dabf7',HD='#1c7ed6',HL='#d0ebff',EYE='#9775fa',ED='#5f3dc4',HOOD='#ffd43b',HDK='#fab005',NORI='#2b3a2f',BL='#ffa8a8',CLIP='#ff6b6b'
const eye=(c,x,y,d)=>{
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y,14,17,0,0,T);c.fill()
c.save();c.beginPath();c.ellipse(x,y+2,11.5,15,0,0,T);c.clip();c.fillStyle=EYE;c.fill();c.fillStyle=ED;c.beginPath();c.ellipse(x,y-8,13,10,0,0,T);c.fill();c.restore()
c.lineWidth=2.5;c.beginPath();c.ellipse(x,y+2,11.5,15,0,0,T);c.stroke()
c.fillStyle='#111';c.beginPath();c.ellipse(x,y+3,5.5,7.5,0,0,T);c.fill()
c.fillStyle='#fff';c.beginPath();c.arc(x-4,y-5,4.5,0,T);c.fill();c.beginPath();c.arc(x+5,y+8,2,0,T);c.fill()
c.lineWidth=6;c.beginPath();c.moveTo(x-16,y-6);c.quadraticCurveTo(x,y-20,x+16,y-6);c.moveTo(x+d*15,y-7);c.lineTo(x+d*21,y-12);c.stroke()
}
const onigiri=(c,x,y,r)=>{
const shape=()=>{c.beginPath();c.moveTo(x,y-34);c.quadraticCurveTo(x+6,y-38,x+12,y-30);c.lineTo(x+34,y+16);c.quadraticCurveTo(x+38,y+26,x+26,y+26);c.lineTo(x-26,y+26);c.quadraticCurveTo(x-38,y+26,x-34,y+16);c.lineTo(x-12,y-30);c.quadraticCurveTo(x-6,y-38,x,y-34);c.closePath()}
c.save();c.beginPath();c.rect(0,0,256,256);c.arc(x+12,y-30,14,0,T,true);c.clip()
shape();c.fillStyle='#fff';c.fill();c.save();c.clip();c.fillStyle=NORI;c.fillRect(x-14,y+2,28,30);c.restore();shape();c.lineWidth=6;c.stroke()
c.restore();c.save();shape();c.clip();c.beginPath();c.arc(x+12,y-30,14,0,T);c.lineWidth=5;c.stroke();c.restore()
}
export default(c,t)=>{
const u=(t*2)%1,bite=u<.2,chew=!bite,ny=bite?-14:10*min(1,(u-.2)/.15),puff=chew?1+.25*Math.abs(sin(u*PI*4)):0,y=chew?2*sin(u*PI*4):0
c.lineWidth=6
c.fillStyle=HD;c.beginPath();c.moveTo(128,34);c.bezierCurveTo(200,34,214,100,206,176);c.quadraticCurveTo(204,184,194,184);c.lineTo(62,184);c.quadraticCurveTo(52,184,50,176);c.bezierCurveTo(42,100,56,34,128,34);c.fill();c.stroke()
c.fillStyle=HOOD;c.beginPath();c.moveTo(98,186);c.quadraticCurveTo(60,196,52,250);c.lineTo(204,250);c.quadraticCurveTo(196,196,158,186);c.closePath();c.fill();c.stroke()
c.fillStyle=HDK;c.beginPath();c.moveTo(96,188);c.quadraticCurveTo(128,214,160,188);c.quadraticCurveTo(150,204,128,206);c.quadraticCurveTo(106,204,96,188);c.fill();c.lineWidth=5;c.stroke()
for(const d of[-1,1])c.beginPath(),c.moveTo(128+d*14,206),c.lineTo(128+d*16,236),c.stroke(),c.beginPath(),c.arc(128+d*16,239,3.5,0,T),c.fillStyle='#fff',c.fill(),c.stroke()
c.fillStyle=SKIN;c.beginPath();c.rect(116,168,24,22);c.fill()
c.save();c.translate(0,y)
c.lineWidth=6;c.fillStyle=SKIN;c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
if(puff)for(const d of[-1,1])c.beginPath(),c.arc(128+d*40,164,13*puff,d<0?PI*.5:-PI*.5,d<0?PI*1.5:PI*.5),c.fillStyle=SKIN,c.fill(),c.stroke()
if(chew){c.lineWidth=5;for(const x of[103,153])c.beginPath(),c.arc(x,144,12,PI*1.15,PI*1.85),c.stroke()}
else{eye(c,103,140,-1);eye(c,153,140,1)}
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*40,161,12,6,0,0,T),c.fill()
c.lineWidth=2.5;for(const d of[-1,1])for(const k of[-5,0,5])c.beginPath(),c.moveTo(128+d*40+k-2,164),c.lineTo(128+d*40+k+2,158),c.stroke()
c.lineWidth=3.5;c.beginPath()
if(bite)c.ellipse(128,170,8,9,0,0,T),c.fillStyle='#c92a2a',c.fill();else if(u*8%2<1)c.moveTo(120,168),c.quadraticCurveTo(124,173,128,169),c.quadraticCurveTo(132,173,136,168);else c.ellipse(128,169,4,3,0,0,T)
c.stroke()
c.lineWidth=6;c.fillStyle=HAIR;c.beginPath();c.moveTo(66,178)
c.bezierCurveTo(48,74,88,36,128,36);c.bezierCurveTo(168,36,208,74,190,178)
c.quadraticCurveTo(184,140,180,110);c.lineTo(170,112);c.quadraticCurveTo(166,106,158,112);c.quadraticCurveTo(150,106,142,112)
c.quadraticCurveTo(134,106,126,112);c.quadraticCurveTo(118,106,110,112);c.quadraticCurveTo(102,106,94,112);c.quadraticCurveTo(88,106,84,112)
c.lineTo(76,110);c.quadraticCurveTo(72,140,66,178);c.closePath();c.fill();c.stroke()
c.strokeStyle=HL;c.lineWidth=5;for(const[a,e]of[[1.24,1.36],[1.44,1.52],[1.62,1.74]])c.beginPath(),c.arc(128,104,56,PI*a,PI*e),c.stroke()
c.strokeStyle=CLIP;c.lineWidth=5;c.beginPath();c.moveTo(172,82);c.lineTo(186,98);c.moveTo(166,88);c.lineTo(180,104);c.stroke();c.strokeStyle='#111'
c.restore()
if(chew)for(const[x,yy,o]of[[46,96,0],[210,88,.5]]){const s=9*sin(PI*((u*2+o)%1));if(s>1.5){c.fillStyle='#fff';c.lineWidth=3.5;c.beginPath();c.moveTo(x,yy-s);for(const[px,py,qx,qy]of[[s,0,s*.18,-s*.18],[0,s,s*.18,s*.18],[-s,0,-s*.18,s*.18],[0,-s,-s*.18,-s*.18]])c.quadraticCurveTo(x+qx,yy+qy,x+px,yy+py);c.fill();c.stroke()}}
const ox=128,oy=206+ny
onigiri(c,ox,oy)
c.lineWidth=5;c.fillStyle=SKIN;for(const d of[-1,1])c.beginPath(),c.ellipse(ox+d*34,oy+12,11,13,d*.3,0,T),c.fill(),c.stroke()
}
+55
View File
@@ -0,0 +1,55 @@
// chibi catgirl with lavender hair waving hello, head tilting, ear twitching, blinking her golden cat eyes
const{sin,cos,PI}=Math,T=2*PI
export const frames=16,delay=80
const SKIN='#ffe3d1',HAIR='#b197fc',HD='#9775fa',HL='#e5dbff',EAR='#ffc9d6',EYE='#fcc419',ED='#e67700',COL='#364fc7',BOW='#fa5252',BL='#ffa8a8'
const eye=(c,x,y,shut,d)=>{
if(shut){c.lineWidth=5;c.beginPath();c.moveTo(x-15,y);c.quadraticCurveTo(x,y+8,x+15,y);c.stroke();return}
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y,14,17,0,0,T);c.fill()
c.save();c.beginPath();c.ellipse(x,y+2,11.5,15,0,0,T);c.clip();c.fillStyle=EYE;c.fill();c.fillStyle=ED;c.beginPath();c.ellipse(x,y-8,13,10,0,0,T);c.fill();c.restore()
c.lineWidth=2.5;c.beginPath();c.ellipse(x,y+2,11.5,15,0,0,T);c.stroke()
c.fillStyle='#111';c.beginPath();c.ellipse(x,y+2,3.2,9,0,0,T);c.fill()
c.fillStyle='#fff';c.beginPath();c.arc(x-4,y-5,4.5,0,T);c.fill();c.beginPath();c.arc(x+5,y+8,2,0,T);c.fill()
c.lineWidth=6;c.beginPath();c.moveTo(x-16,y-6);c.quadraticCurveTo(x,y-20,x+16,y-6);c.moveTo(x+d*15,y-7);c.lineTo(x+d*21,y-12);c.stroke()
c.lineWidth=2.5;c.beginPath();c.moveTo(x-8,y+18);c.lineTo(x+8,y+18);c.stroke()
}
export default(c,t,i)=>{
const tilt=.05*sin(t*T),blink=i==10||i==11,tw=i>=3&&i<=5?.3*sin((i-3)*PI/2):0
const head=f=>{c.save();c.translate(128,178);c.rotate(tilt);c.translate(-128,-178);f();c.restore()}
head(()=>{c.fillStyle=HD;c.beginPath();c.moveTo(128,34);c.bezierCurveTo(200,34,214,96,206,150);c.quadraticCurveTo(212,200,200,228);c.lineTo(56,228);c.quadraticCurveTo(44,200,50,150);c.bezierCurveTo(42,96,56,34,128,34);c.fill();c.stroke()})
c.fillStyle='#fff';c.beginPath();c.moveTo(100,188);c.quadraticCurveTo(62,196,54,250);c.lineTo(202,250);c.quadraticCurveTo(194,196,156,188);c.closePath();c.fill();c.stroke()
c.fillStyle=COL;c.beginPath();c.moveTo(98,189);c.lineTo(128,228);c.lineTo(158,189);c.lineTo(172,197);c.lineTo(128,246);c.lineTo(84,197);c.closePath();c.fill();c.lineWidth=5;c.stroke()
c.strokeStyle='#fff';c.lineWidth=3;c.beginPath();c.moveTo(90,198);c.lineTo(128,238);c.lineTo(166,198);c.stroke();c.strokeStyle='#111'
c.fillStyle=SKIN;c.beginPath();c.moveTo(116,166);c.lineTo(116,192);c.lineTo(128,206);c.lineTo(140,192);c.lineTo(140,166);c.fill();c.lineWidth=5;c.beginPath();c.moveTo(116,166);c.lineTo(116,190);c.moveTo(140,166);c.lineTo(140,190);c.stroke()
c.fillStyle=BOW;for(const d of[-1,1])c.beginPath(),c.moveTo(128,226),c.lineTo(128+d*20,216),c.lineTo(128+d*18,238),c.closePath(),c.fill(),c.stroke()
c.beginPath();c.arc(128,227,5,0,T);c.fill();c.stroke()
head(()=>{
for(const[d,r]of[[-1,0],[1,tw]]){
c.save();c.translate(128+d*34,58);c.rotate(r)
c.lineWidth=6;c.fillStyle=HAIR;c.beginPath();c.moveTo(d*-26,8);c.lineTo(d*14,-46);c.lineTo(d*22,12);c.closePath();c.fill();c.stroke()
c.fillStyle=EAR;c.beginPath();c.moveTo(d*-12,4);c.lineTo(d*12,-30);c.lineTo(d*14,6);c.closePath();c.fill()
c.restore()
}
c.lineWidth=6;c.fillStyle=SKIN;c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
eye(c,103,140,blink,-1);eye(c,153,140,blink,1)
c.lineWidth=2.5;c.beginPath();c.moveTo(128,156);c.lineTo(127,159);c.stroke()
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*42,161,12,6,0,0,T),c.fill()
c.lineWidth=2.5;for(const d of[-1,1])for(const k of[-5,0,5])c.beginPath(),c.moveTo(128+d*42+k-2,164),c.lineTo(128+d*42+k+2,158),c.stroke()
c.lineWidth=3.5;c.beginPath();c.moveTo(119,167);c.quadraticCurveTo(123.5,173,128,168);c.quadraticCurveTo(132.5,173,137,167);c.stroke()
c.fillStyle='#fff';c.lineWidth=2;c.beginPath();c.moveTo(131,169);c.lineTo(136,169);c.lineTo(133.5,174);c.closePath();c.fill();c.stroke()
c.lineWidth=6;c.fillStyle=HAIR;c.beginPath();c.moveTo(66,150)
c.bezierCurveTo(52,74,88,36,128,36);c.bezierCurveTo(168,36,204,74,190,150)
c.quadraticCurveTo(190,118,180,100);c.quadraticCurveTo(176,100,172,108);c.quadraticCurveTo(166,96,158,90);c.quadraticCurveTo(154,100,148,108)
c.quadraticCurveTo(140,94,130,88);c.quadraticCurveTo(126,100,120,108);c.quadraticCurveTo(112,96,104,90);c.quadraticCurveTo(100,100,94,106)
c.quadraticCurveTo(88,98,80,98);c.quadraticCurveTo(70,118,66,150);c.closePath();c.fill();c.stroke()
c.strokeStyle=HL;c.lineWidth=5;for(const[a,b]of[[1.22,1.34],[1.42,1.5],[1.62,1.74]])c.beginPath(),c.arc(128,104,56,PI*a,PI*b),c.stroke()
c.strokeStyle='#111'
})
const a=-1.45+.35*sin(2*T*t),ex=206,ey=208,hx=ex+48*cos(a),hy=ey+48*sin(a)
for(const[w,s]of[[30,'#111'],[19,'#fff']])c.lineWidth=w,c.strokeStyle=s,c.beginPath(),c.moveTo(168,224),c.lineTo(ex,ey),c.lineTo(ex+34*cos(a),ey+34*sin(a)),c.stroke()
c.strokeStyle='#111';c.lineWidth=5;c.fillStyle=SKIN
c.save();c.translate(hx,hy);c.rotate(a+PI/2)
for(const x of[-9,-3,3,9])c.beginPath(),c.ellipse(x,-14,3.6,8,0,0,T),c.fill(),c.stroke()
c.beginPath();c.ellipse(-14,-2,4,8,-.7,0,T);c.fill();c.stroke()
c.beginPath();c.ellipse(0,-2,13,12,0,0,T);c.fill();c.stroke()
c.restore()
}
+50
View File
@@ -0,0 +1,50 @@
// chibi girl with a brown ponytail getting shy: head tilting, blush pulsing, little hearts floating up
const{sin,cos,PI,max}=Math,T=2*PI
export const frames=16,delay=80
const SKIN='#ffe3d1',HAIR='#a9744f',HD='#8d6346',HL='#e8c39e',EYE='#51cf66',ED='#2b8a3e',CARD='#fff3bf',TIE='#fa5252',BL='#ff8fab',HT='#f06595',SW='#a5d8ff'
const eye=(c,x,y,d)=>{
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y,14,17,0,0,T);c.fill()
c.save();c.beginPath();c.ellipse(x,y,14,17,0,0,T);c.clip();c.beginPath();c.ellipse(x-3,y+4,11.5,15,0,0,T);c.fillStyle=EYE;c.fill();c.lineWidth=2.5;c.stroke()
c.fillStyle=ED;c.beginPath();c.ellipse(x-3,y-4,13,9,0,0,T);c.fill()
c.fillStyle='#111';c.beginPath();c.ellipse(x-3,y+5,5.5,7.5,0,0,T);c.fill()
c.fillStyle='#fff';c.beginPath();c.arc(x-7,y-2,4.5,0,T);c.fill();c.beginPath();c.arc(x+2,y+10,2,0,T);c.fill();c.restore()
c.lineWidth=6;c.beginPath();c.moveTo(x-16,y-5);c.quadraticCurveTo(x,y-18,x+16,y-5);c.moveTo(x+d*15,y-6);c.lineTo(x+d*20,y-11);c.stroke()
c.lineWidth=2.5;c.beginPath();c.moveTo(x-8,y+18);c.lineTo(x+8,y+18);c.stroke()
}
const heart=(c,x,y,s)=>{c.save();c.translate(x,y);c.scale(s,s);c.beginPath();c.moveTo(0,8);c.bezierCurveTo(-13,-1,-9,-12,0,-5);c.bezierCurveTo(9,-12,13,-1,0,8);c.restore();c.fill();c.stroke()}
export default(c,t)=>{
const tilt=.07*sin(t*T),bl=1+.18*sin(2*T*t)
c.fillStyle=HT;c.lineWidth=4
for(const[x0,o]of[[40,0],[214,.33],[30,.66]]){const p=(t+o)%1,s=1.4*sin(PI*p);if(s>.15)heart(c,x0+8*sin(p*T*2),190-p*150,s)}
const head=f=>{c.save();c.translate(128,180);c.rotate(tilt);c.translate(-128,-180);f();c.restore()}
head(()=>{
c.save();c.translate(170,52);c.rotate(.35+.22*sin(t*T-.8))
c.lineWidth=6;c.fillStyle=HAIR;c.beginPath();c.moveTo(-10,0);c.bezierCurveTo(30,-10,70,40,64,120);c.quadraticCurveTo(58,140,44,148);c.quadraticCurveTo(48,90,4,22);c.closePath();c.fill();c.stroke()
c.strokeStyle=HD;c.lineWidth=3;c.beginPath();c.moveTo(12,10);c.bezierCurveTo(40,30,54,70,52,118);c.stroke();c.strokeStyle='#111'
c.restore()
c.lineWidth=6;c.fillStyle=HD;c.beginPath();c.moveTo(128,34);c.bezierCurveTo(196,34,206,96,198,158);c.lineTo(58,158);c.bezierCurveTo(50,96,60,34,128,34);c.fill();c.stroke()
})
c.lineWidth=6;c.fillStyle=CARD;c.beginPath();c.moveTo(100,190);c.quadraticCurveTo(62,198,54,250);c.lineTo(202,250);c.quadraticCurveTo(194,198,156,190);c.closePath();c.fill();c.stroke()
c.fillStyle='#fff';c.beginPath();c.moveTo(108,190);c.lineTo(128,214);c.lineTo(148,190);c.closePath();c.fill();c.lineWidth=5;c.stroke()
c.fillStyle=SKIN;c.beginPath();c.moveTo(116,168);c.lineTo(116,192);c.lineTo(128,206);c.lineTo(140,192);c.lineTo(140,168);c.fill();c.beginPath();c.moveTo(116,168);c.lineTo(116,190);c.moveTo(140,168);c.lineTo(140,190);c.stroke()
c.lineWidth=4;for(const y of[226,240])c.beginPath(),c.arc(128,y,3,0,T),c.stroke()
head(()=>{
c.lineWidth=6;c.fillStyle=SKIN;c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
eye(c,103,140,-1);eye(c,153,140,1)
c.lineWidth=2.5;c.beginPath();c.moveTo(127,156);c.lineTo(126,159);c.stroke()
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*42,161,15*bl,8*bl,0,0,T),c.fill()
c.lineWidth=2.5;for(const d of[-1,1])for(const k of[-8,-3,2,7])c.beginPath(),c.moveTo(128+d*42+k-2,165),c.lineTo(128+d*42+k+2,157),c.stroke()
c.lineWidth=3.5;c.beginPath();c.moveTo(118,168);c.quadraticCurveTo(121,164,124,168);c.quadraticCurveTo(127,172,130,168);c.quadraticCurveTo(133,164,136,168);c.stroke()
c.lineWidth=6;c.fillStyle=HAIR;c.beginPath();c.moveTo(70,150)
c.bezierCurveTo(54,74,88,36,128,36);c.bezierCurveTo(168,36,202,74,186,150)
c.quadraticCurveTo(184,118,176,104);c.quadraticCurveTo(170,98,164,104);c.quadraticCurveTo(156,92,144,90)
c.quadraticCurveTo(136,104,124,108);c.quadraticCurveTo(118,96,102,92);c.quadraticCurveTo(96,104,86,110)
c.quadraticCurveTo(80,104,78,104);c.quadraticCurveTo(70,124,70,150);c.closePath();c.fill();c.stroke()
c.strokeStyle=HL;c.lineWidth=5;for(const[a,e]of[[1.24,1.36],[1.44,1.52],[1.62,1.74]])c.beginPath(),c.arc(128,104,56,PI*a,PI*e),c.stroke()
c.strokeStyle='#111';c.lineWidth=3.5;c.beginPath();c.moveTo(90,118);c.lineTo(112,113);c.moveTo(144,113);c.lineTo(166,118);c.stroke()
c.fillStyle=TIE;c.lineWidth=5;c.beginPath();c.ellipse(170,52,10,14,.4,0,T);c.fill();c.stroke()
c.fillStyle=SW;c.lineWidth=4;const sy=96+3*sin(2*T*t);c.beginPath();c.moveTo(194,sy-12);c.quadraticCurveTo(202,sy,194,sy+6);c.quadraticCurveTo(186,sy,194,sy-12);c.fill();c.stroke()
})
c.lineWidth=5;c.fillStyle=SKIN
for(const d of[-1,1]){const k=d*2*sin(4*T*t);c.beginPath();c.ellipse(128+d*12+k,214,12,10,d*.3,0,T);c.fill();c.stroke()}
}
+56
View File
@@ -0,0 +1,56 @@
// chibi idol with pink twin tails, holding up a peace sign and winking a star out
const{sin,cos,PI,max,min}=Math,T=2*PI
export const frames=16,delay=80
const SKIN='#ffe3d1',HAIR='#f783ac',HD='#e64980',HL='#ffdeeb',EYE='#4dabf7',ED='#1864ab',RB='#f06595',CLIP='#ffd43b',BL='#ffa8a8',MO='#c92a2a',TG='#ff8fab'
const eye=(c,x,y,d)=>{
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y,14,17,0,0,T);c.fill()
c.save();c.beginPath();c.ellipse(x,y+2,11.5,15,0,0,T);c.clip();c.fillStyle=EYE;c.fill();c.fillStyle=ED;c.beginPath();c.ellipse(x,y-8,13,10,0,0,T);c.fill();c.restore()
c.lineWidth=2.5;c.beginPath();c.ellipse(x,y+2,11.5,15,0,0,T);c.stroke()
c.fillStyle='#111';c.beginPath();c.ellipse(x,y+3,5.5,7.5,0,0,T);c.fill()
c.fillStyle='#fff';c.beginPath();c.arc(x-4,y-5,4.5,0,T);c.fill();c.beginPath();c.arc(x+5,y+8,2,0,T);c.fill()
c.lineWidth=6;c.beginPath();c.moveTo(x-16,y-6);c.quadraticCurveTo(x,y-20,x+16,y-6);c.moveTo(x+d*15,y-7);c.lineTo(x+d*21,y-12);c.stroke()
c.lineWidth=2.5;c.beginPath();c.moveTo(x-8,y+18);c.lineTo(x+8,y+18);c.stroke()
}
const star=(c,x,y,r,a)=>{c.save();c.translate(x,y);c.rotate(a);c.beginPath();for(let k=0;k<10;k++){const rr=k%2?r*.45:r,b=k*PI/5-PI/2;c.lineTo(cos(b)*rr,sin(b)*rr)}c.closePath();c.restore();c.fill();c.stroke()}
export default(c,t)=>{
const w=t>=.45&&t<.85,b=-3*max(0,sin(2*T*t)),sw=.07*sin(t*T)
for(const d of[-1,1]){
c.save();c.translate(128+d*64,78+b);c.rotate(d*.12+sw)
c.lineWidth=6;c.fillStyle=HAIR;c.beginPath();c.moveTo(0,0);c.bezierCurveTo(d*46,4,d*62,92,d*36,156);c.quadraticCurveTo(d*28,176,d*10,172);c.bezierCurveTo(d*30,110,d*8,46,0,0);c.fill();c.stroke()
c.strokeStyle=HD;c.lineWidth=3;c.beginPath();c.moveTo(d*14,20);c.bezierCurveTo(d*34,50,d*40,100,d*28,140);c.stroke();c.strokeStyle='#111'
c.restore()
}
c.save();c.translate(0,b)
c.lineWidth=6;c.fillStyle=HD;c.beginPath();c.moveTo(128,34);c.bezierCurveTo(196,34,206,96,200,152);c.lineTo(56,152);c.bezierCurveTo(50,96,60,34,128,34);c.fill();c.stroke()
c.restore()
c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.moveTo(100,190);c.quadraticCurveTo(62,198,54,250);c.lineTo(202,250);c.quadraticCurveTo(194,198,156,190);c.closePath();c.fill();c.stroke()
c.fillStyle=SKIN;c.beginPath();c.moveTo(116,170);c.lineTo(116,194);c.quadraticCurveTo(128,206,140,194);c.lineTo(140,170);c.fill();c.lineWidth=5;c.beginPath();c.moveTo(116,170);c.lineTo(116,192);c.moveTo(140,170);c.lineTo(140,192);c.stroke()
c.lineWidth=5;c.fillStyle='#fff';c.beginPath();c.moveTo(108,192);c.quadraticCurveTo(128,214,148,192);c.stroke()
c.fillStyle=RB;for(const d of[-1,1])c.beginPath(),c.moveTo(128,224),c.quadraticCurveTo(128+d*26,200,128+d*30,222),c.quadraticCurveTo(128+d*24,240,128,224),c.fill(),c.stroke()
for(const d of[-1,1])c.beginPath(),c.moveTo(128+d*2,226),c.lineTo(128+d*12,250),c.lineTo(128+d*20,244),c.closePath(),c.fill(),c.stroke()
c.beginPath();c.arc(128,225,6,0,T);c.fill();c.stroke()
c.save();c.translate(0,b)
c.lineWidth=6;c.fillStyle=SKIN;c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
eye(c,103,140,-1)
if(w){c.lineWidth=5;c.beginPath();c.moveTo(166,130);c.lineTo(146,141);c.lineTo(166,151);c.stroke()}else eye(c,153,140,1)
c.lineWidth=2.5;c.beginPath();c.moveTo(128,156);c.lineTo(127,159);c.stroke()
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*42,161,12,6,0,0,T),c.fill()
c.lineWidth=2.5;for(const d of[-1,1])for(const k of[-5,0,5])c.beginPath(),c.moveTo(128+d*42+k-2,164),c.lineTo(128+d*42+k+2,158),c.stroke()
if(w){c.lineWidth=3.5;c.fillStyle=MO;c.beginPath();c.moveTo(119,166);c.quadraticCurveTo(128,184,137,166);c.closePath();c.fill();c.stroke();c.save();c.clip();c.fillStyle=TG;c.beginPath();c.ellipse(128,178,7,5,0,0,T);c.fill();c.restore()}
else{c.lineWidth=3.5;c.beginPath();c.arc(128,164,7,.15*PI,.85*PI);c.stroke()}
c.lineWidth=6;c.fillStyle=HAIR;c.beginPath();c.moveTo(70,146)
c.bezierCurveTo(54,74,88,36,128,36);c.bezierCurveTo(168,36,202,74,186,146)
c.quadraticCurveTo(186,116,178,102);c.quadraticCurveTo(170,104,164,112);c.quadraticCurveTo(160,98,150,92);c.quadraticCurveTo(144,104,136,110)
c.quadraticCurveTo(132,96,124,90);c.quadraticCurveTo(118,102,110,108);c.quadraticCurveTo(106,96,96,94);c.quadraticCurveTo(90,104,86,112)
c.quadraticCurveTo(80,104,78,102);c.quadraticCurveTo(70,120,70,146);c.closePath();c.fill();c.stroke()
c.strokeStyle=HL;c.lineWidth=5;for(const[a,e]of[[1.24,1.36],[1.44,1.52],[1.62,1.74]])c.beginPath(),c.arc(128,104,56,PI*a,PI*e),c.stroke()
c.strokeStyle='#111';c.fillStyle=CLIP;c.lineWidth=4;for(const d of[-1,1])star(c,128+d*62,76,11,d*.3)
c.restore()
if(w){const p=(t-.45)/.4;c.fillStyle=CLIP;c.lineWidth=4;star(c,172+p*50,128-p*50,6+10*sin(PI*p),p*4)}
const hx=62,hy=168+b*1.5
for(const[l,s]of[[30,'#111'],[19,'#fff']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(92,226),c.lineTo(52,210),c.lineTo(hx,hy+14),c.stroke()
c.strokeStyle='#111'
for(const[fx,fy]of[[-12,-34],[6,-36]])for(const[l,s]of[[14,'#111'],[7,SKIN]])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(hx,hy),c.lineTo(hx+fx,hy+fy),c.stroke()
c.strokeStyle='#111';c.lineWidth=5;c.fillStyle=SKIN;c.beginPath();c.ellipse(hx,hy+4,13,12,0,0,T);c.fill();c.stroke()
c.beginPath();c.ellipse(hx+8,hy+2,5,8,.6,0,T);c.fill();c.stroke()
}
+47
View File
@@ -0,0 +1,47 @@
// chibi witch with long blonde hair twirling her star wand, leaving a trail of colored sparkles, hat tip flopping
const{sin,cos,PI,max}=Math,T=2*PI
export const frames=16,delay=80
const SKIN='#ffe3d1',HAIR='#ffd43b',HD='#fab005',HL='#fff9db',EYE='#fa5252',ED='#c92a2a',HAT='#7048e8',HATD='#5f3dc4',BAND='#ffd43b',ROBE='#845ef7',BL='#ffa8a8',SP=['#ffd43b','#f783ac','#66d9e8','#b2f2bb']
const eye=(c,x,y,d)=>{
c.fillStyle='#fff';c.beginPath();c.ellipse(x,y,14,17,0,0,T);c.fill()
c.save();c.beginPath();c.ellipse(x,y+2,11.5,15,0,0,T);c.clip();c.fillStyle=EYE;c.fill();c.fillStyle=ED;c.beginPath();c.ellipse(x,y-8,13,10,0,0,T);c.fill();c.restore()
c.lineWidth=2.5;c.beginPath();c.ellipse(x,y+2,11.5,15,0,0,T);c.stroke()
c.fillStyle='#111';c.beginPath();c.ellipse(x,y+3,5.5,7.5,0,0,T);c.fill()
c.fillStyle='#fff';c.beginPath();c.arc(x-4,y-5,4.5,0,T);c.fill();c.beginPath();c.arc(x+5,y+8,2,0,T);c.fill()
c.lineWidth=6;c.beginPath();c.moveTo(x-16,y-6);c.quadraticCurveTo(x,y-20,x+16,y-6);c.moveTo(x+d*15,y-7);c.lineTo(x+d*21,y-12);c.stroke()
c.lineWidth=2.5;c.beginPath();c.moveTo(x-8,y+18);c.lineTo(x+8,y+18);c.stroke()
}
const star=(c,x,y,r,a=0)=>{c.save();c.translate(x,y);c.rotate(a);c.beginPath();for(let k=0;k<10;k++){const rr=k%2?r*.45:r,b=k*PI/5-PI/2;c.lineTo(cos(b)*rr,sin(b)*rr)}c.closePath();c.restore();c.fill();c.stroke()}
const tipAt=u=>{const a=u*T,hx=196+12*cos(a),hy=190+12*sin(a);return[hx+cos(a*.5-1.1)*0+36*cos(-1.1+.3*sin(a)),hy+36*sin(-1.1+.3*sin(a)),hx,hy]}
export default(c,t)=>{
const fl=10*sin(t*T)
c.lineWidth=6
c.fillStyle=HD;c.beginPath();c.moveTo(128,40);c.bezierCurveTo(204,40,214,110,208,170);c.quadraticCurveTo(216,206,204,234);c.quadraticCurveTo(190,226,180,236);c.lineTo(76,236);c.quadraticCurveTo(66,226,52,234);c.quadraticCurveTo(40,206,48,170);c.bezierCurveTo(42,110,52,40,128,40);c.fill();c.stroke()
c.fillStyle=ROBE;c.beginPath();c.moveTo(100,190);c.quadraticCurveTo(62,198,54,250);c.lineTo(202,250);c.quadraticCurveTo(194,198,156,190);c.closePath();c.fill();c.stroke()
c.fillStyle='#fff';for(const d of[-1,1])c.beginPath(),c.moveTo(128,212),c.lineTo(128+d*30,190),c.lineTo(128+d*34,206),c.closePath(),c.fill(),c.stroke()
c.fillStyle=BAND;c.beginPath();c.arc(128,214,7,0,T);c.fill();c.stroke()
c.fillStyle=SKIN;c.beginPath();c.rect(116,168,24,26);c.fill();c.lineWidth=5;c.beginPath();c.moveTo(116,168);c.lineTo(116,190);c.moveTo(140,168);c.lineTo(140,190);c.stroke()
c.lineWidth=6;c.fillStyle=SKIN;c.beginPath();c.moveTo(74,96);c.bezierCurveTo(70,152,100,184,128,186);c.bezierCurveTo(156,184,186,152,182,96);c.bezierCurveTo(182,44,74,44,74,96);c.fill();c.stroke()
eye(c,103,140,-1);eye(c,153,140,1)
c.lineWidth=2.5;c.beginPath();c.moveTo(128,156);c.lineTo(127,159);c.stroke()
c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*42,161,12,6,0,0,T),c.fill()
c.lineWidth=2.5;for(const d of[-1,1])for(const k of[-5,0,5])c.beginPath(),c.moveTo(128+d*42+k-2,164),c.lineTo(128+d*42+k+2,158),c.stroke()
c.lineWidth=3.5;c.fillStyle='#c92a2a';c.beginPath();c.moveTo(120,166);c.quadraticCurveTo(128,178,136,166);c.closePath();c.fill();c.stroke()
c.lineWidth=6;c.fillStyle=HAIR;c.beginPath();c.moveTo(66,160)
c.bezierCurveTo(50,80,88,46,128,46);c.bezierCurveTo(168,46,206,80,190,160)
c.quadraticCurveTo(188,124,180,106);c.quadraticCurveTo(172,104,166,112);c.quadraticCurveTo(160,98,150,94);c.quadraticCurveTo(144,104,136,110)
c.quadraticCurveTo(132,98,124,92);c.quadraticCurveTo(118,104,110,108);c.quadraticCurveTo(104,98,96,96);c.quadraticCurveTo(90,104,86,112)
c.quadraticCurveTo(80,104,76,106);c.quadraticCurveTo(68,126,66,160);c.closePath();c.fill();c.stroke()
c.fillStyle=HAT;c.beginPath();c.moveTo(80,66);c.bezierCurveTo(94,44,108,24,126,14);c.quadraticCurveTo(152+fl,2,184+fl,22);c.quadraticCurveTo(160+fl*.6,20,148,34);c.bezierCurveTo(156,46,168,56,176,66);c.closePath();c.fill();c.stroke()
c.save();c.clip();c.fillStyle=BAND;c.fillRect(70,48,120,12);c.restore();c.beginPath();c.moveTo(80,66);c.bezierCurveTo(94,44,108,24,126,14);c.quadraticCurveTo(152+fl,2,184+fl,22);c.quadraticCurveTo(160+fl*.6,20,148,34);c.bezierCurveTo(156,46,168,56,176,66);c.closePath();c.stroke()
c.fillStyle=HATD;c.beginPath();c.ellipse(128,68,94,16,0,0,T);c.fill();c.stroke()
c.fillStyle=BAND;c.lineWidth=4;star(c,108,40,9,-.2)
c.lineWidth=6
for(let k=1;k<=6;k++){const[x,y]=tipAt(t-k*.05);const s=9*(1-k/7);c.fillStyle=SP[k%4];c.lineWidth=3;star(c,x,y,s,k)}
const[sx,sy,hx,hy]=tipAt(t)
for(const[l,s]of[[30,'#111'],[19,ROBE]])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(166,222),c.lineTo(hx-6,hy+10),c.stroke()
c.strokeStyle='#111'
for(const[l,s]of[[11,'#111'],[5,'#8d6346']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(hx,hy),c.lineTo(sx,sy),c.stroke()
c.strokeStyle='#111';c.lineWidth=5;c.fillStyle=SKIN;c.beginPath();c.ellipse(hx,hy,12,11,0,0,T);c.fill();c.stroke()
c.fillStyle='#ffd43b';c.lineWidth=5;star(c,sx,sy,14,t*T)
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 230 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 211 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 182 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 236 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 152 KiB

+1 -1
View File
@@ -1,5 +1,5 @@
// renders gen/<section>/<subject>/<action>.js into public/<subject>/<action>.{webp,gif} // renders gen/<section>/<subject>/<action>.js into public/<subject>/<action>.{webp,gif}
// the section folder (bw, color) only picks where it shows in the gallery, it isn't part of the url // the section folder (bw, color, anime) only picks where it shows in the gallery, it isn't part of the url
// npm run gen renders only what's missing (published files never change by accident) // npm run gen renders only what's missing (published files never change by accident)
// npm run gen -- cat/blink re-renders the named ones // npm run gen -- cat/blink re-renders the named ones
import{createCanvas}from'@napi-rs/canvas' import{createCanvas}from'@napi-rs/canvas'
+1 -1
View File
@@ -5,7 +5,7 @@
// tile images get ?v=<hash> so browsers drop old copies when a file changes, links stay clean for hotlinking // tile images get ?v=<hash> so browsers drop old copies when a file changes, links stay clean for hotlinking
import{readdirSync as rd,readFileSync as rf}from'fs' import{readdirSync as rd,readFileSync as rf}from'fs'
import{createHash}from'crypto' import{createHash}from'crypto'
const sections=['bw','color'] const sections=['bw','color','anime']
const hidden=['cat/tail.gif'] // same drawing as cat/wag.gif, kept so old links still work const hidden=['cat/tail.gif'] // same drawing as cat/wag.gif, kept so old links still work
const walk=d=>rd(d,{withFileTypes:true}).flatMap(e=>e.isDirectory()?walk(`${d}/${e.name}`):`${d}/${e.name}`) const walk=d=>rd(d,{withFileTypes:true}).flatMap(e=>e.isDirectory()?walk(`${d}/${e.name}`):`${d}/${e.name}`)
const dims=(b,x)=>x=='gif'?[b.readUInt16LE(6),b.readUInt16LE(8)]:[b.readUIntLE(24,3)+1,b.readUIntLE(27,3)+1] const dims=(b,x)=>x=='gif'?[b.readUInt16LE(6),b.readUInt16LE(8)]:[b.readUIntLE(24,3)+1,b.readUIntLE(27,3)+1]