Double the black-and-white section with 50 more stickers

Animals (mouse, sheep, cow, elephant, monkey, lion, sloth, bat, spider,
hedgehog, shark, seal, caterpillar, pigeon, koala, squirrel), weather
(snowman, tornado, wind, icicle), food (toaster, banana, lemon, cherry,
pudding, teapot, hot dog, lollipop, carrot, milk), objects (scissors,
padlock, candle, fan, gears, stapler, faucet, trash, tv, cursor) and toys
and symbols (yo-yo, spring, Newton's cradle, drum, trumpet, bowling,
basketball, skull, question mark, puzzle).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-22 23:48:07 +00:00
co-authored by Claude Opus 5.5
parent b937e4d1f7
commit 7108136edd
150 changed files with 1258 additions and 0 deletions
+35
View File
@@ -0,0 +1,35 @@
// banana peel opening flap by flap to show the happy curved fruit inside, then folding shut again in reverse
// each flap is a quadratic center line widened along its normal, so it can bend from hugging the fruit to drooping open
// flap roots and the peel's top edge aren't outlined while shut, so the closed banana reads as one piece
const{sin,PI,min,max,hypot}=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)
const mix=(a,b,o)=>a.map((v,i)=>v+(b[i]-v)*o)
const flap=(c,[P,C,Q],W,o,stem)=>{
const pt=s=>[(1-s)**2*P[0]+2*s*(1-s)*C[0]+s*s*Q[0],(1-s)**2*P[1]+2*s*(1-s)*C[1]+s*s*Q[1]]
const L=[],R=[]
for(let k=0;k<=20;k++){const s=k/20,[x,y]=pt(s),[x2,y2]=pt(min(1,s+.01)),[x0,y0]=pt(max(0,s-.01)),dx=x2-x0,dy=y2-y0,n=hypot(dx,dy)||1,h=W*(1-s)**.6;L.push([x-dy/n*h,y+dx/n*h]);R.push([x+dy/n*h,y-dx/n*h])}
if(stem){const[x,y]=Q,[x0,y0]=pt(.95),n=hypot(x-x0,y-y0);c.lineWidth=12;c.beginPath();c.moveTo(x,y);c.lineTo(x+(x-x0)/n*14,y+(y-y0)/n*14);c.stroke();c.lineWidth=4;c.strokeStyle='#fff';c.stroke();c.strokeStyle='#111';c.lineWidth=7}
c.beginPath();for(const p of L)c.lineTo(...p);for(const p of R.reverse())c.lineTo(...p);c.fill();c.stroke()
if(o>.3)c.lineWidth=4,c.beginPath(),[...Array(17)].forEach((_,k)=>c.lineTo(...pt((k+1)/20))),c.stroke(),c.lineWidth=7
}
const star=(c,x,y,r)=>{const k=r*.18;c.beginPath();c.moveTo(x,y-r);for(const[px,py,qx,qy]of[[r,0,k,-k],[0,r,k,k],[-r,0,-k,k],[0,-r,-k,-k]])c.quadraticCurveTo(x+qx,y+qy,x+px,y+py);c.fill();c.stroke()}
const shut=[[[110,166],[100,100],[132,56]],[[146,166],[156,104],[140,56]],[[127,170],[122,100],[136,58]]]
const wide=[[[110,154],[60,116],[44,198]],[[146,154],[198,120],[214,198]],[[127,158],[124,200],[112,236]]]
export default(c,t)=>{
const o=[0,1,2].map(k=>ease((t-k*.13)/.13)-ease((t-.62-(2-k)*.1)/.1)),open=o[2]>.9
c.beginPath();c.moveTo(104,162);c.bezierCurveTo(100,120,108,84,136,58);c.bezierCurveTo(148,84,154,120,152,162);c.fill();c.stroke()
c.fillStyle='#111';for(const x of[120,141])c.beginPath(),c.ellipse(x,110,4.5,6,0,0,T),c.fill()
c.fillStyle='#fff';for(const x of[119,140])c.beginPath(),c.arc(x,108,1.8,0,T),c.fill()
c.lineWidth=4;c.beginPath();c.arc(130,118,7,.15*PI,.85*PI);c.stroke()
c.lineWidth=2.5;for(const x of[112,148])for(const k of[-3,1])c.beginPath(),c.moveTo(x+k-1.5,127),c.lineTo(x+k+1.5,122),c.stroke()
c.lineWidth=7;c.fillStyle='#fff'
const base=()=>{c.beginPath();c.moveTo(158,146);c.bezierCurveTo(160,186,146,210,124,220);c.quadraticCurveTo(112,224,110,216);c.bezierCurveTo(98,196,94,176,96,146)}
for(const k of[0,1])flap(c,[0,1,2].map(i=>mix(shut[k][i],wide[k][i],o[k])),18,o[k])
base();c.fill();c.stroke()
const m=max(o[0],o[1]);if(m>.05)c.lineWidth=7*min(1,m*3),c.beginPath(),c.moveTo(96,148),c.quadraticCurveTo(127,156,158,148),c.stroke(),c.lineWidth=7
c.save();c.translate(114,222);c.rotate(.5);c.beginPath();c.roundRect(-7,-4,14,10,4);c.lineWidth=16;c.strokeStyle='#fff';c.stroke();c.fillStyle='#111';c.fill();c.lineWidth=7;c.strokeStyle='#111';c.stroke();c.restore()
c.fillStyle='#fff'
flap(c,[0,1,2].map(i=>mix(shut[2][i],wide[2][i],o[2])),16,o[2],1)
if(open){c.lineWidth=4;const p=(t-.39)/.23;for(const[x,y,ph]of[[70,70,0],[190,78,.5]]){const r=11*sin(PI*((p*2+ph)%1));if(r>2)star(c,x,y,r)}}
}
+16
View File
@@ -0,0 +1,16 @@
// basketball bouncing: squashes on the floor, stretches as it flies, seams turning as it spins
const{PI,abs}=Math,T=2*PI
export const frames=20,delay=60
export default(c,t)=>{
const h=116*4*t*(1-t),v=abs(1-2*t),con=t<.03,r=48,k=1-h/170
const sx=con?1.2:1-.08*v,sy=con?.8:1+.1*v
for(const[l,s]of[[11,'#111'],[4,'#fff']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(128-46*k,238),c.lineTo(128+46*k,238),c.stroke()
c.lineWidth=7;c.strokeStyle='#111'
c.translate(128,232-h);c.scale(sx,sy);c.translate(0,-r)
c.beginPath();c.arc(0,0,r,0,T);c.fill()
c.save();c.clip();c.rotate(t*T);c.lineWidth=5;c.beginPath()
c.moveTo(-r,0);c.lineTo(r,0);c.moveTo(0,-r);c.lineTo(0,r)
for(const d of[-1,1])c.moveTo(d*r*.66,-r),c.quadraticCurveTo(d*r*.08,0,d*r*.66,r)
c.stroke();c.restore()
c.beginPath();c.arc(0,0,r,0,T);c.stroke()
}
+19
View File
@@ -0,0 +1,19 @@
// bat flapping its wings and bobbing up and down
// every dark part gets its white rim in one pass first, so the bat reads as one silhouette on black
const{sin,cos,PI}=Math,T=2*PI
export const delay=60
export default(c,t)=>{
const w=sin(2*T*t),y=118-8*w,E=[[100,-22],[78,18],[52,10],[30,26],[2,14]]
const wing=d=>()=>{c.save();c.translate(128+d*14,y);c.scale(d,1);c.rotate(-.45*w);c.moveTo(0,-6);c.quadraticCurveTo(50,-46,...E[0]);for(let i=1;i<5;i++){const[p,q]=[E[i-1],E[i]];c.quadraticCurveTo((p[0]+q[0])/2,(p[1]+q[1])/2-12,...q)}c.closePath();c.restore()}
const parts=[wing(-1),wing(1),()=>c.ellipse(128,y+18,22,28,0,0,T),()=>c.arc(128,y-14,24,0,T)]
for(const d of[-1,1])parts.push(()=>{c.moveTo(128+d*8,y-34);c.lineTo(128+d*22,y-60);c.lineTo(128+d*22,y-24);c.closePath()})
c.lineWidth=16;c.strokeStyle='#fff';for(const f of parts)c.beginPath(),f(),c.stroke()
c.fillStyle='#111';for(const f of parts)c.beginPath(),f(),c.fill()
c.lineWidth=3;c.strokeStyle='#fff'
for(const d of[-1,1]){c.save();c.translate(128+d*14,y);c.scale(d,1);c.rotate(-.45*w);for(const[x,yy]of E.slice(1,4))c.beginPath(),c.moveTo(4,0),c.lineTo(x,yy-10),c.stroke();c.restore()}
c.fillStyle='#fff';for(const d of[-1,1])c.beginPath(),c.arc(128+d*10,y-16,7,0,T),c.fill()
c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.arc(128+d*9,y-15,3.8,0,T),c.fill()
c.fillStyle='#fff';for(const d of[-1,1])c.beginPath(),c.arc(128+d*9-1.5,y-17,1.4,0,T),c.fill()
for(const d of[-1,1])c.beginPath(),c.moveTo(128+d*3,y-3),c.lineTo(128+d*7,y+5),c.lineTo(128+d*9,y-3),c.closePath(),c.fill()
c.lineWidth=3;c.beginPath();c.moveTo(118,y-4);c.quadraticCurveTo(128,y,138,y-4);c.stroke()
}
+32
View File
@@ -0,0 +1,32 @@
// bowling ball rolls into three pins, they tumble up and spin back down into place, the ball rolls off into the distance and a new one pops in
const{sin,cos,PI,min,max}=Math,T=2*PI
export const frames=24,delay=70
const D=(c,f,a=11,b=4)=>{for(const[l,s]of[[a,'#111'],[b,'#fff']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),f(),c.stroke();c.lineWidth=7;c.strokeStyle='#111'}
const shape=c=>{c.beginPath();c.moveTo(-9,36);c.bezierCurveTo(-22,16,-16,-4,-7,-16);c.bezierCurveTo(-3,-22,-14,-28,-11,-36);c.arc(0,-36,11,PI,0);c.bezierCurveTo(14,-28,3,-22,7,-16);c.bezierCurveTo(16,-4,22,16,9,36);c.closePath()}
const pin=(c,x,y,s,a)=>{
c.save();c.translate(x,y);c.rotate(a);c.scale(s,s);c.translate(0,5.5);c.lineWidth=7/s
shape(c);c.fill();c.stroke()
c.save();shape(c);c.clip();c.fillStyle='#111';c.fillRect(-20,-22,40,4);c.fillRect(-20,-15,40,4);c.restore()
c.restore()
}
const ball=(c,x,y,r)=>{
c.beginPath();c.arc(x,y,r,0,T);c.lineWidth=16*r/34;c.strokeStyle='#fff';c.stroke();c.fillStyle='#111';c.fill();c.lineWidth=7*r/34;c.strokeStyle='#111';c.stroke()
const a=x/30;c.fillStyle='#fff'
for(const[hx,hy,s]of[[6,-20,5],[20,-11,5],[9,4,6]])c.beginPath(),c.arc(x+(hx*cos(a)-hy*sin(a))*r/34,y+(hx*sin(a)+hy*cos(a))*r/34,s*r/34,0,T),c.fill()
c.lineWidth=7;c.strokeStyle='#111';c.fillStyle='#fff'
}
// x, base y, scale, then how far it splays sideways, how high it hops and which way it spins
const PINS=[[172,178,1.05,0,56,1],[146,208,1.2,-30,50,-1],[198,208,1.2,-12,42,1]]
export default(c,t)=>{
const roll=t<.22,bx=roll?60+32*t/.22:92+80*(t-.22)/.28,by=roll?178:178-24*(t-.22)/.28,bs=roll?1:max(0,1-(t-.22)/.28)
c.translate(0,-8)
if(roll)for(const o of[0,.5])D(c,()=>c.arc(bx,by,39,PI*(.75+o)+bx/30,PI*(1.05+o)+bx/30))
if(t>=.22&&t<.36){const k=sin(PI*(t-.22)/.14);for(let j=0;j<5;j++){const b=-PI*.85+j*PI*.3;D(c,()=>{c.moveTo(124+cos(b)*22*k,166+sin(b)*22*k);c.lineTo(124+cos(b)*46*k,166+sin(b)*46*k)})}}
if(t>=.82){const u=(t-.82)/.18;ball(c,60,178,30*(u+.35*sin(PI*u)))}
else if(bs>.05)ball(c,bx,by,30*bs)
for(const[x,y,s,dx,h,sp]of PINS){
const e=(t-.24)/.52,hop=e>0&&e<1?sin(PI*e):0,w=t>=.76&&t<.92?.12*sin(T*(t-.76)/.08)*(1-(t-.76)/.16):0
if(hop)pin(c,x+dx*hop,y-41.5*s-h*hop,s,sp*T*e)
else{c.save();c.translate(x,y);c.rotate(w);pin(c,0,-41.5*s,s,0);c.restore()}
}
}
+22
View File
@@ -0,0 +1,22 @@
// sleepy candle in a little holder, its flame flickering and leaning while glow lines pulse around it
const{sin,cos,PI}=Math,T=2*PI
export const frames=16,delay=80
export default(c,t)=>{
const L=9*sin(T*t)+4*sin(3*T*t+1),H=1+.1*sin(2*T*t+.5),g=.5+.5*sin(2*T*t)
for(const[w,s]of[[25,'#111'],[11,'#fff']])c.lineWidth=w,c.strokeStyle=s,c.beginPath(),c.arc(200,206,15,-PI*.7,PI*.6),c.stroke()
c.lineWidth=7;c.strokeStyle='#111'
c.beginPath();c.ellipse(128,218,80,16,0,0,T);c.fill();c.stroke()
c.beginPath();c.moveTo(94,122);c.lineTo(94,214);c.quadraticCurveTo(128,226,162,214);c.lineTo(162,122);c.fill();c.stroke()
c.beginPath();c.ellipse(128,122,34,9,0,0,T);c.fill();c.stroke()
c.beginPath();c.moveTo(140,128);c.lineTo(140,160);c.arc(146,160,6,PI,0,true);c.lineTo(152,130);c.fill();c.lineWidth=6;c.stroke()
c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.arc(128+d*16,168,4,0,T),c.fill()
c.lineWidth=5;c.beginPath();c.arc(128,174,8,.2*PI,.8*PI);c.stroke()
for(const d of[-1,1])c.beginPath(),c.arc(128+d*16,166,8,1.15*PI,1.85*PI),c.stroke()
c.lineWidth=6;c.beginPath();c.moveTo(128,118);c.lineTo(128,104);c.stroke()
const tx=128+L,ty=98-62*H
for(const a of[-2.6,-2,-1.15,-.55])for(const[w,s]of[[11,'#111'],[4,'#fff']])c.lineWidth=w,c.strokeStyle=s,c.beginPath(),c.moveTo(128+cos(a)*(46+4*g),74+sin(a)*(46+4*g)),c.lineTo(128+cos(a)*(58+8*g),74+sin(a)*(58+8*g)),c.stroke()
c.lineWidth=7;c.strokeStyle='#111';c.fillStyle='#fff'
const fl=(s,b)=>{c.beginPath();c.moveTo(128+L*s,98-(98-ty)*s);c.quadraticCurveTo(128+18*s+L*.3,70+28*(1-s),128+16*s,98);c.arc(128,98,16*s,0,PI);c.quadraticCurveTo(128-18*s+L*.3,70+28*(1-s),128+L*s,98-(98-ty)*s)}
fl(1);c.fill();c.stroke()
c.fillStyle='#111';fl(.5);c.fill();c.fillStyle='#fff'
}
+25
View File
@@ -0,0 +1,25 @@
// carrot tugged at in its soil mound, then pops up out of the ground happy with dirt flying, and sinks back down
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)
const mound=c=>{c.beginPath();c.moveTo(22,238);c.bezierCurveTo(28,196,82,184,128,184);c.bezierCurveTo(174,184,228,196,234,238);c.closePath()}
export default(c,t)=>{
const r=t<.3?10*sin(PI*((t/.1)%1))**2:t<.42?118*ease((t-.3)/.12):t<.64?118+4*sin((t-.42)/.22*T):118*(1-ease((t-.64)/.28))
const y=184-r,out=r>60,strain=t<.3,sw=t<.3?.25*sin(6*T*t):.08*sin(2*T*t)
if(r>20){c.fillStyle='#111';c.beginPath();c.ellipse(128,190,30,6,0,0,T);c.fill();c.fillStyle='#fff'}
c.save();c.beginPath();c.rect(0,0,256,232);c.clip();c.translate(128,y)
for(const a of[-.42,0,.42]){
c.save();c.rotate(a+sw*(a?1:-1));for(const[w,s]of[[13,'#111'],[5,'#fff']])c.lineWidth=w,c.strokeStyle=s,c.beginPath(),c.moveTo(0,-8),c.lineTo(0,-34),c.stroke()
c.lineWidth=7;c.strokeStyle='#111';c.beginPath();c.ellipse(0,-46,10,17,0,0,T);c.fill();c.stroke();c.lineWidth=4;c.beginPath();c.moveTo(0,-36);c.lineTo(0,-56);c.stroke();c.restore()
}
c.lineWidth=7;c.beginPath();c.moveTo(-32,2);c.quadraticCurveTo(-32,-10,-18,-10);c.lineTo(18,-10);c.quadraticCurveTo(32,-10,32,2);c.lineTo(5,120);c.quadraticCurveTo(0,128,-5,120);c.closePath();c.fill();c.stroke()
c.lineWidth=4;for(const[x,yy,d]of[[-27,40,1],[-22,66,1],[25,52,-1],[19,80,-1]])c.beginPath(),c.moveTo(x,yy),c.lineTo(x+d*10,yy),c.stroke()
c.lineWidth=5
if(strain)for(const d of[-1,1])c.beginPath(),c.moveTo(d*6,10),c.lineTo(d*16,16),c.lineTo(d*6,22),c.stroke()
else{c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.ellipse(d*12,16,4.5,6,0,0,T),c.fill();c.fillStyle='#fff';for(const d of[-1,1])c.beginPath(),c.arc(d*12-1.5,14,1.8,0,T),c.fill()}
if(out){c.beginPath();c.arc(0,30,7,.1*PI,.9*PI);c.stroke()}else{c.beginPath();c.moveTo(-6,32);c.lineTo(6,32);c.stroke()}
c.restore()
c.lineWidth=7;mound(c);c.fill();c.stroke()
c.lineWidth=3;for(const[x,yy,rr]of[[64,214,4],[90,200,3],[176,206,4],[196,222,3],[112,224,3],[150,218,4]])c.beginPath(),c.arc(x,yy,rr,0,T),c.stroke()
if(t>=.3&&t<.5){const u=(t-.3)/.2;c.lineWidth=4;for(const[vx,vy]of[[-1,1],[-.5,1.3],[.5,1.25],[1,1.05],[.1,1.4]]){const x=128+vx*70*u,yy=186-vy*120*u+150*u*u;c.beginPath();c.arc(x,yy,4.5,0,T);c.fill();c.stroke()}}
}
+25
View File
@@ -0,0 +1,25 @@
// caterpillar crawling along a twig, a hump rolling through its segments from tail to head
const{sin,cos,PI,max}=Math,T=2*PI
export const frames=16,delay=70
export default(c,t)=>{
c.beginPath();c.moveTo(60,222);c.quadraticCurveTo(52,246,82,248);c.quadraticCurveTo(84,228,60,222);c.fill();c.stroke()
c.lineWidth=4;c.beginPath();c.moveTo(62,226);c.quadraticCurveTo(66,240,80,246);c.stroke();c.lineWidth=7
c.beginPath();c.roundRect(14,206,228,20,10);c.fill();c.stroke()
c.lineWidth=4;for(const x of[44,116,196])c.beginPath(),c.moveTo(x,216),c.lineTo(x+18,216),c.stroke()
const s=[...Array(7)].map((_,i)=>{const p=t*T-i*.9,l=max(0,sin(p));return[34+i*25-5*cos(p),186-20*l*l]})
for(const[x,y]of s){
c.lineWidth=5;for(const d of[-9,9])c.beginPath(),c.ellipse(x+d,y+20,5,4,0,0,T),c.fill(),c.stroke()
c.lineWidth=7;c.beginPath();c.arc(x,y,20,0,T);c.fill();c.stroke()
c.fillStyle='#111';c.beginPath();c.arc(x,y-7,4.5,0,T);c.fill();c.fillStyle='#fff'
}
const p=t*T-7*.9,l=max(0,sin(p)),hx=34+7*25-5*cos(p)+2,hy=172-16*l*l
for(const d of[-1,1]){
const ex=hx+d*12+3*sin(t*T*2+d),ey=hy-46
for(const[w,st]of[[11,'#111'],[4,'#fff']])c.lineWidth=w,c.strokeStyle=st,c.beginPath(),c.moveTo(hx+d*8,hy-20),c.quadraticCurveTo(hx+d*6,hy-36,ex,ey),c.stroke()
c.strokeStyle='#111';c.lineWidth=5;c.beginPath();c.arc(ex,ey,6,0,T);c.fill();c.stroke()
}
c.lineWidth=7;c.beginPath();c.arc(hx,hy,27,0,T);c.fill();c.stroke()
c.fillStyle='#111';for(const x of[hx-4,hx+14])c.beginPath(),c.ellipse(x,hy-4,5,7,0,0,T),c.fill()
c.fillStyle='#fff';for(const x of[hx-6,hx+12])c.beginPath(),c.arc(x,hy-7,2,0,T),c.fill()
c.lineWidth=4;c.beginPath();c.arc(hx+5,hy+8,7,.15*PI,.85*PI);c.stroke()
}
+21
View File
@@ -0,0 +1,21 @@
// two cherries on joined stems swinging like a pendulum, each lagging a little, the leaf flapping along
// the black cherries get a white rim so they show on dark backgrounds
const{sin,cos,PI}=Math,T=2*PI
export const frames=20,delay=70
export default(c,t)=>{
const P=[128,30],ch=[[-.3,152,0],[.27,140,.35]]
const ends=ch.map(([b,L,ph])=>{const a=b+.3*sin(T*t-ph);return[P[0]+L*sin(a),P[1]+L*cos(a),a]})
for(const[w,s]of[[13,'#111'],[5,'#fff']]){c.lineWidth=w;c.strokeStyle=s;for(const[x,y,a]of ends)c.beginPath(),c.moveTo(...P),c.quadraticCurveTo((P[0]+x)/2-20*cos(a)*Math.sign(a),(P[1]+y)/2,x,y-30),c.stroke()}
c.lineWidth=7;c.strokeStyle='#111'
c.save();c.translate(...P);c.rotate(-.5+.18*sin(T*t-.6));c.beginPath();c.moveTo(0,0);c.bezierCurveTo(-14,-26,-46,-30,-62,-14);c.bezierCurveTo(-46,4,-14,10,0,0);c.fill();c.stroke();c.lineWidth=4;c.beginPath();c.moveTo(-4,-2);c.quadraticCurveTo(-32,-12,-56,-14);c.stroke();c.restore()
c.lineWidth=7;c.beginPath();c.arc(...P,8,0,T);c.fill();c.stroke()
for(const[k,[x,y]]of ends.entries()){
c.beginPath();c.arc(x,y,36,0,T);c.lineWidth=16;c.strokeStyle='#fff';c.stroke();c.fillStyle='#111';c.fill();c.lineWidth=7;c.strokeStyle='#111';c.stroke()
c.strokeStyle='#fff';c.fillStyle='#fff';c.lineWidth=5
c.beginPath();c.arc(x,y,24,PI*1.12,PI*1.4);c.stroke()
if(k)for(const d of[-1,1])c.beginPath(),c.ellipse(x+d*11,y+2,4,5.5,0,0,T),c.fill()
else{c.beginPath();c.ellipse(x-11,y+2,4,5.5,0,0,T);c.fill();c.lineWidth=4;c.beginPath();c.moveTo(x+6,y+4);c.quadraticCurveTo(x+11,y-2,x+16,y+4);c.stroke()}
c.lineWidth=4;c.beginPath();c.arc(x,y+11,6,.15*PI,.85*PI);c.stroke()
c.strokeStyle='#111';c.fillStyle='#fff'
}
}
+27
View File
@@ -0,0 +1,27 @@
// cow chewing its cud side to side, the bell under its chin swinging and one ear flicking
const{sin,cos,PI}=Math,T=2*PI
export const delay=80
export default(c,t)=>{
const j=5*sin(2*T*t),bs=.3*sin(2*T*t-.8),ef=t>.55&&t<.8?sin(PI*(t-.55)/.25):0
c.beginPath();c.rect(100,150,56,52);c.fill();c.stroke()
c.beginPath();c.roundRect(92,192,72,13,6);c.fill();c.stroke()
c.save();c.translate(128,204);c.rotate(bs)
c.beginPath();c.moveTo(-9,0);c.lineTo(9,0);c.lineTo(16,28);c.quadraticCurveTo(0,34,-16,28);c.closePath();c.fill();c.stroke()
c.fillStyle='#111';c.beginPath();c.arc(0,35,5,0,T);c.fill();c.restore();c.fillStyle='#fff'
for(const d of[-1,1]){
c.beginPath();c.moveTo(128+d*22,58);c.quadraticCurveTo(128+d*46,54,128+d*46,28);c.quadraticCurveTo(128+d*34,42,128+d*14,50);c.closePath();c.fill();c.stroke()
c.save();c.translate(128+d*46,90);c.rotate(d*(.3+(d>0?.45*ef:0)))
c.beginPath();c.ellipse(d*26,0,28,12,0,0,T);c.fill();c.stroke()
c.lineWidth=4;c.beginPath();c.ellipse(d*28,0,15,4,0,0,T);c.stroke();c.lineWidth=7;c.restore()
}
const head=()=>{c.beginPath();c.roundRect(78,44,100,128,[46,46,40,40])}
head();c.fill();c.save();c.clip();c.fillStyle='#111'
c.beginPath();c.ellipse(90,60,28,22,.5,0,T);c.fill();c.beginPath();c.ellipse(180,128,18,28,-.3,0,T);c.fill();c.restore()
head();c.stroke();c.fillStyle='#fff'
c.lineWidth=5;c.beginPath();c.moveTo(116,50);c.quadraticCurveTo(120,36,128,46);c.quadraticCurveTo(134,34,140,50);c.stroke();c.lineWidth=7
c.fillStyle='#111';for(const x of[108,148])c.beginPath(),c.arc(x,100,7,0,T),c.fill()
c.fillStyle='#fff';for(const x of[106,146])c.beginPath(),c.arc(x,97,2.5,0,T),c.fill()
c.beginPath();c.ellipse(128+j,148,50,30,0,0,T);c.fill();c.stroke()
c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.ellipse(128+j+d*18,144,5,8,-d*.25,0,T),c.fill()
c.lineWidth=4;c.beginPath();c.moveTo(114+j,164+j*.5);c.quadraticCurveTo(128+j*1.6,169,142+j,164-j*.5);c.stroke()
}
+18
View File
@@ -0,0 +1,18 @@
// Newton's cradle: the end balls take turns swinging out and clacking back while the middle three stay still
const{sin,cos,PI}=Math,T=2*PI
export const frames=20,delay=60
const D=(c,f,a=11,b=4)=>{for(const[l,s]of[[a,'#111'],[b,'#fff']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),f(),c.stroke();c.lineWidth=7;c.strokeStyle='#111'}
export default(c,t)=>{
const A=.42,L=104,P=44,sw=-A*sin(t*T)
for(const d of[-1,1])D(c,()=>{c.moveTo(128+d*96,40);c.lineTo(128+d*110,222)},14,5)
c.beginPath();c.roundRect(10,218,236,20,9);c.fill();c.stroke()
const k=t<.1?176:t>=.5&&t<.6?80:0
if(k)for(const a of[-.6,0,.6])D(c,()=>{c.moveTo(k+sin(a)*22,P+L-cos(a)*22);c.lineTo(k+sin(a)*38,P+L-cos(a)*38)})
for(let i=0;i<5;i++){
const x0=64+32*i,a=i==0&&t<.5||i==4&&t>=.5?sw:0,x=x0+L*sin(a),y=P+L*cos(a)
D(c,()=>{c.moveTo(x0,P);c.lineTo(x,y)},9,3)
c.beginPath();c.arc(x,y,16,0,T);c.fill();c.stroke()
c.lineWidth=3;c.beginPath();c.arc(x,y,9,PI*1.1,PI*1.5);c.stroke();c.lineWidth=7
}
c.beginPath();c.roundRect(22,28,212,16,8);c.fill();c.stroke()
}
+19
View File
@@ -0,0 +1,19 @@
// mouse pointer glides onto a like button and clicks it: the button presses in, a ring ripples out and the heart fills
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 go=ease(t/.3)-ease((t-.62)/.3),px=174-24*go,py=132-28*go,click=t>=.34&&t<.42,on=t>=.36&&t<.9,rp=(t-.36)/.3
const by=click?5:0
c.lineWidth=10
c.beginPath();c.roundRect(22,40+by,212,96,48);c.fill();c.stroke()
c.save();c.translate(124,90+by);c.scale(2.4,2.4)
c.beginPath();c.moveTo(0,10);c.bezierCurveTo(-18,-2,-12,-18,0,-8);c.bezierCurveTo(12,-18,18,-2,0,10);c.closePath()
c.restore();if(on)c.fillStyle='#111',c.fill(),c.fillStyle='#fff';c.lineWidth=8;c.stroke()
if(rp>0&&rp<1){const r=14+40*rp;for(const[w,s]of[[15,'#111'],[6,'#fff']])c.lineWidth=w,c.strokeStyle=s,c.beginPath(),c.arc(150,104,r,0,T),c.stroke()
for(const a of[-1.9,-1.1,-.3])for(const[w,s]of[[15,'#111'],[6,'#fff']])c.lineWidth=w,c.strokeStyle=s,c.beginPath(),c.moveTo(150+cos(a)*(r+12),104+sin(a)*(r+12)),c.lineTo(150+cos(a)*(r+26),104+sin(a)*(r+26)),c.stroke()}
c.lineWidth=10;c.strokeStyle='#111'
c.save();c.translate(px,py);c.scale(click?.9:1,click?.9:1)
c.beginPath();for(const[x,y]of[[0,0],[0,96],[24,74],[42,112],[61,102],[43,66],[74,64]])c.lineTo(x,y);c.closePath();c.fill();c.stroke()
c.restore()
}
+20
View File
@@ -0,0 +1,20 @@
// snare drum with two sticks taking turns hitting it, the head dipping and impact lines on every hit
const{sin,cos,PI,abs,max}=Math,T=2*PI
export const frames=20,delay=60
const D=(c,f,a=11,b=4)=>{for(const[l,s]of[[a,'#111'],[b,'#fff']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),f(),c.stroke();c.lineWidth=7;c.strokeStyle='#111'}
export default(c,t)=>{
const top=128,bot=192,rx=86,ry=26,h=[abs(sin(PI*t)),abs(cos(PI*t))]
const dip=4*max(0,1-Math.min(...h)/.25)
c.beginPath();c.moveTo(128-rx,top);c.lineTo(128-rx,bot);c.ellipse(128,bot,rx,ry,0,PI,0,true);c.lineTo(128+rx,top);c.closePath();c.fill();c.stroke()
c.lineWidth=5;for(const y of[top+12,bot-12])c.beginPath(),c.ellipse(128,y,rx,ry,0,.08,PI-.08),c.stroke()
c.lineWidth=4;c.beginPath()
for(let k=0;k<=8;k++){const x=52+k*19,e=Math.sqrt(1-((x-128)/rx)**2)*ry;c.lineTo(x,k%2?bot-12+e:top+12+e)}
c.stroke();c.lineWidth=7
c.beginPath();c.ellipse(128,top,rx,ry-dip,0,0,T);c.fill();c.stroke()
for(const d of[-1,1]){
const k=h[d<0?0:1],px=128+d*116,py=92,a=(d<0?.335:PI-.335)-d*.75*k,tx=px+cos(a)*97,ty=py+sin(a)*97
if(k<.2)for(const b of[-.7,0,.7])D(c,()=>{c.moveTo(tx+sin(b)*16,top-6-cos(b)*16);c.lineTo(tx+sin(b)*32,top-6-cos(b)*32)})
D(c,()=>{c.moveTo(px,py);c.lineTo(tx,ty)},17,8)
c.beginPath();c.ellipse(tx,ty,9,7,a,0,T);c.fill();c.stroke()
}
}
+35
View File
@@ -0,0 +1,35 @@
// elephant lifting its trunk and spraying a shower of water back over its head onto its back
const{sin,cos,PI,atan2}=Math,T=2*PI
export const frames=24,delay=70
const sm=x=>x<0?0:x>1?1:x*x*(3-2*x)
export default(c,t)=>{
const k=t<.3?sm(t/.3):t<.65?1:sm(1-(t-.65)/.25),L=(a,b)=>a+(b-a)*k,fl=.07*sin(2*T*t)
const B=[198,148],C=[L(224,248),L(180,116)],P=[L(212,232),L(224,50)]
const q=s=>[0,1].map(i=>(1-s)**2*B[i]+2*s*(1-s)*C[i]+s*s*P[i]),qd=s=>[0,1].map(i=>2*(1-s)*(C[i]-B[i])+2*s*(P[i]-C[i]))
for(const x of[54,86,120,150]){
c.beginPath();c.roundRect(x,172,28,52,[0,0,10,10]);c.fill();c.stroke()
c.lineWidth=4;for(const o of[8,20])c.beginPath(),c.arc(x+o,224,4,PI,0),c.stroke();c.lineWidth=7
}
for(const[l,s]of[[11,'#111'],[4,'#fff']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(42,146),c.quadraticCurveTo(26,160,28,186),c.stroke()
c.strokeStyle='#111';c.lineWidth=5;c.beginPath();c.moveTo(28,184);c.quadraticCurveTo(20,196,28,202);c.quadraticCurveTo(36,196,28,184);c.fill();c.stroke();c.lineWidth=7
c.beginPath();c.ellipse(108,150,72,54,0,0,T);c.fill();c.stroke()
c.beginPath();c.arc(176,114,46,0,T);c.fill();c.stroke()
c.beginPath();c.moveTo(...B);c.quadraticCurveTo(...C,...P)
for(const[l,s]of[[34,'#111'],[22,'#fff']])c.lineWidth=l,c.strokeStyle=s,c.stroke()
c.strokeStyle='#111';c.lineWidth=3
for(const s of[.55,.7,.85]){const[x,y]=q(s),[dx,dy]=qd(s),a=atan2(dy,dx)+PI/2;c.beginPath();c.moveTo(x-7*cos(a),y-7*sin(a));c.lineTo(x+7*cos(a),y+7*sin(a));c.stroke()}
const[dx,dy]=qd(1);c.lineWidth=5;c.beginPath();c.ellipse(...P,15,7,atan2(dy,dx)+PI/2,0,T);c.fill();c.stroke()
c.beginPath();c.arc(176,114,42,0,T);c.fill()
c.lineWidth=7;c.save();c.translate(150,118);c.scale(1+fl,1)
c.beginPath();c.ellipse(0,0,30,42,0,0,T);c.fill();c.stroke()
c.lineWidth=4;c.beginPath();c.ellipse(-3,4,18,28,0,0,T);c.stroke();c.restore();c.lineWidth=7
c.fillStyle='#111';c.beginPath();c.arc(192,100,6,0,T);c.fill()
c.fillStyle='#fff';c.beginPath();c.arc(190,98,2,0,T);c.fill()
c.lineWidth=3;for(const o of[-5,0,5])c.beginPath(),c.moveTo(196+o-2,124),c.lineTo(196+o+2,118),c.stroke()
c.lineWidth=4
for(let i=0;i<8;i++){
const s=(t-.3-i*.034)/.22;if(s<=0||s>=1)continue
const vx=-(110+14*(i%3)),x=232+vx*s,y=50-76*s+150*s*s,a=atan2(-76+300*s,vx),r=8.5*(1-.35*s)
c.save();c.translate(x,y);c.rotate(a);c.beginPath();c.arc(0,0,r,-PI/2,PI/2);c.lineTo(-r*2.4,0);c.closePath();c.restore();c.fill();c.stroke()
}
}
+26
View File
@@ -0,0 +1,26 @@
// desk fan with its blades whirring and its head turning slowly side to side
const{sin,cos,PI}=Math,T=2*PI
export const frames=16,delay=90
export default(c,t)=>{
const y=.55*sin(t*T),k=cos(y),sx=22*sin(y)
for(const[w,s]of[[30,'#111'],[16,'#fff']])c.lineWidth=w,c.strokeStyle=s,c.beginPath(),c.moveTo(128+sx*.4,170),c.lineTo(128,214),c.stroke()
c.lineWidth=7;c.strokeStyle='#111'
c.beginPath();c.ellipse(128,220,62,16,0,0,T);c.fill();c.stroke()
c.fillStyle='#111';c.beginPath();c.ellipse(160,220,8,4,0,0,T);c.fill();c.fillStyle='#fff'
c.save();c.translate(128+sx,98);c.scale(k,1)
c.beginPath();c.arc(0,0,82,0,T);c.fill();c.stroke()
const r=t*T*7/3
for(let b=0;b<3;b++){
const a=r+b*T/3
c.beginPath();c.moveTo(cos(a)*14,sin(a)*14)
c.bezierCurveTo(cos(a-.5)*48,sin(a-.5)*48,cos(a-.32)*72,sin(a-.32)*72,cos(a+.05)*70,sin(a+.05)*70)
c.bezierCurveTo(cos(a+.5)*64,sin(a+.5)*64,cos(a+.55)*36,sin(a+.55)*36,cos(a+.6)*14,sin(a+.6)*14)
c.closePath();c.lineWidth=6;c.fill();c.stroke()
}
c.lineWidth=3;for(let s=0;s<12;s++){const a=s*T/12;c.beginPath();c.moveTo(cos(a)*24,sin(a)*24);c.lineTo(cos(a)*78,sin(a)*78);c.stroke()}
c.beginPath();c.arc(0,0,52,0,T);c.stroke()
c.lineWidth=7;c.beginPath();c.arc(0,0,82,0,T);c.stroke()
c.beginPath();c.arc(0,0,20,0,T);c.fill();c.stroke()
c.fillStyle='#111';c.beginPath();c.arc(0,0,6,0,T);c.fill()
c.restore()
}
+22
View File
@@ -0,0 +1,22 @@
// faucet with a drop swelling at the spout, letting go, and splashing into a puddle with a ripple
const{sin,cos,PI,min,max}=Math,T=2*PI
export const frames=24,delay=70
export default(c,t)=>{
const X=170,ty=150,py=222
c.lineWidth=7
c.beginPath();c.ellipse(X,py,62,14,0,0,T);c.fill();c.stroke()
if(t>=.62){const p=(t-.62)/.38;c.lineWidth=5;c.beginPath();c.ellipse(X,py,10+44*p,2+9*p,0,0,T);c.stroke()
if(p<.7)for(const d of[-1,1]){const q=p/.7;c.lineWidth=6;c.beginPath();c.arc(X+d*(10+26*q),py-8-44*4*q*(1-q),7*(1-q*.4),0,T);c.fill();c.stroke()}}
for(const[w,s]of[[52,'#111'],[38,'#fff']])c.lineWidth=w,c.strokeStyle=s,c.beginPath(),c.moveTo(44,96),c.lineTo(126,96),c.quadraticCurveTo(X,96,X,132),c.stroke()
c.lineWidth=7;c.strokeStyle='#111'
c.beginPath();c.roundRect(X-26,126,52,20,7);c.fill();c.stroke()
c.beginPath();c.roundRect(20,56,28,80,9);c.fill();c.stroke()
c.beginPath();c.moveTo(92,70);c.lineTo(92,46);c.stroke()
for(const[w,s]of[[24,'#111'],[10,'#fff']])c.lineWidth=w,c.strokeStyle=s,c.beginPath(),c.moveTo(60,40),c.lineTo(124,40),c.stroke()
c.lineWidth=7;c.strokeStyle='#111';c.beginPath();c.arc(92,40,11,0,T);c.fill();c.stroke()
const drop=(y,r,s)=>{c.beginPath();c.moveTo(X,y-r*s);c.quadraticCurveTo(X+r*.9,y-r*.4,X+r,y);c.arc(X,y,r,0,PI);c.quadraticCurveTo(X-r*.9,y-r*.4,X,y-r*s);c.lineWidth=6;c.fill();c.stroke()
c.fillStyle='#111';c.beginPath();c.arc(X-r*.38,y-r*.05,r*.2,0,T);c.fill();c.fillStyle='#fff'}
if(t<.46){const g=t/.46,r=6+10*g;drop(ty+r*.5+6*g,r,1.5+.9*g)}
else if(t<.62){const f=(t-.46)/.16;drop(ty+20+(py-12-ty-20)*f*f,15,2.2)}
else drop(ty+3,6,1.4)
}
+20
View File
@@ -0,0 +1,20 @@
// two meshing gears turning against each other, the small one spinning faster
// the small gear's angle comes from rolling contact, offset half a tooth so its gap meets the big gear's tooth
const{sin,cos,PI}=Math,T=2*PI
export const frames=16,delay=70
const gear=(c,x,y,n,R,th,holes)=>{
const p=T/n,ri=R-10,ro=R+8
c.beginPath()
for(let k=0;k<n;k++){const a=th+k*p;for(const[f,r]of[[-.5,ri],[-.3,ri],[-.18,ro],[.18,ro],[.3,ri]])c.lineTo(x+cos(a+f*p)*r,y+sin(a+f*p)*r)}
c.closePath();c.lineWidth=7;c.fill();c.stroke()
c.lineWidth=5;c.beginPath();c.arc(x,y,R-22,0,T);c.stroke()
for(let k=0;k<holes;k++){const a=th+k*T/holes;c.beginPath();c.arc(x+cos(a)*(R-38)*.95,y+sin(a)*(R-38)*.95,R*.13,0,T);c.stroke()}
c.lineWidth=7;c.beginPath();c.arc(x,y,R*.2,0,T);c.stroke()
c.fillStyle='#111';c.beginPath();c.arc(x,y,6,0,T);c.fill();c.fillStyle='#fff'
}
export default(c,t)=>{
const N=12,n=8,m=11,R=N*m/2,r=n*m/2,f=-.6,bx=90,by=158,sx=bx+(R+r)*cos(f),sy=by+(R+r)*sin(f)
const tb=t*T/6,ts=f+PI+PI/n-(N/n)*(tb-f)
gear(c,bx,by,N,R,tb,6)
gear(c,sx,sy,n,r,ts,0)
}
+27
View File
@@ -0,0 +1,27 @@
// hedgehog curls into a spiky ball, rolls with speed lines, then uncurls and sniffs
// the spiky coat is a zigzag band whose sweep grows from its back to all the way round
const{sin,cos,PI}=Math,T=2*PI
export const frames=24,delay=70
const sm=x=>x<0?0:x>1?1:x*x*(3-2*x)
export default(c,t)=>{
const k=t<.15?0:t<.3?sm((t-.15)/.15):t<.7?1:t<.85?sm(1-(t-.7)/.15):0,u=1-k
const ro=t>=.3&&t<.7?2*T*(t-.3)/.4:0,rx=70-18*k,ry=48+4*k,cx=128,cy=158+4*k-(k==1?3*Math.abs(sin(2*ro)):0),sn=k?0:1.5*sin(8*T*t)
const P=(a,s)=>[cx+(rx+s)*cos(a),cy+(ry+s)*sin(a)]
if(k==1){c.lineWidth=7;for(const[o,y]of[[0,-20],[14,4],[4,26]]){const p=(t*5+o/14)%1,x0=cx-rx-24-o-8*p
for(const[l,s]of[[11,'#111'],[4,'#fff']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(x0,cy+y),c.lineTo(x0-30,cy+y),c.stroke()}
c.strokeStyle='#111';c.lineWidth=7}
if(u>.02)for(const x of[-34,30])c.beginPath(),c.ellipse(cx+x,cy+ry+4,15*u,8*u,0,0,T),c.fill(),c.stroke()
c.beginPath();c.ellipse(cx,cy,rx,ry,0,0,T);c.fill();c.stroke()
if(u>.02){
c.beginPath();c.ellipse(cx+rx*.8+8*u,cy+8+sn*.3,28*u,18*u,-.2,0,T);c.fill();c.stroke()
c.fillStyle='#111';c.beginPath();c.arc(cx+rx*.8+34*u,cy+2+sn,7*u,0,T);c.fill()
c.beginPath();c.arc(cx+rx*.55,cy-ry*.1,5.5*u,0,T);c.fill();c.fillStyle='#fff';c.beginPath();c.arc(cx+rx*.55-1.5,cy-ry*.1-2,2*u,0,T);c.fill()
}
const S=PI*(1.05+.95*k),a0=-.42*PI+ro,n=Math.round(S/(T/24)),I=a=>[cx+.9*rx*cos(a),cy+.9*ry*sin(a)],E=I(a0),band=()=>{
c.beginPath();for(let i=0;i<=2*n;i++){const a=a0-i*S/(2*n);c.lineTo(...P(a,i%2?18:2))}
c.lineTo(...I(a0-S));c.quadraticCurveTo(cx+.45*rx+k*(E[0]-cx-.45*rx),cy+.05*ry+k*(E[1]-cy-.05*ry),...E);c.closePath()}
band();c.fill();c.stroke()
c.lineWidth=4;for(let i=3;i<n-2;i+=2){const a=a0-i*S/n;c.beginPath();c.moveTo(cx+rx*.74*cos(a),cy+ry*.74*sin(a));c.lineTo(...P(a,-8));c.stroke()}
c.lineWidth=7
if(u>.02){c.beginPath();c.arc(cx+rx*.3,cy-ry*.66,8*u,0,T);c.fill();c.stroke()}
}
+30
View File
@@ -0,0 +1,30 @@
// hot dog getting a zigzag of mustard squirted along it as the bottle slides across, then the line wipes off for the loop
// the black sausage gets a white rim so it shows on dark backgrounds
const{sin,PI,min,max,abs}=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)
const zy=x=>149+8*sin((x-50)/156*T*5)
export default(c,t)=>{
const sq=t<.6,head=50+156*ease(t/.6),tail=50+156*ease((t-.78)/.22),bx=t<.78?head:206-156*ease((t-.78)/.22),by=118-(t>=.78?34*sin(PI*min(1,(t-.78)/.22)):0)
c.beginPath();c.moveTo(34,160);c.quadraticCurveTo(34,124,80,122);c.lineTo(176,122);c.quadraticCurveTo(222,124,222,160);c.closePath();c.fill();c.stroke()
c.beginPath();c.roundRect(16,130,224,38,19);c.lineWidth=16;c.strokeStyle='#fff';c.stroke();c.fillStyle='#111';c.fill();c.lineWidth=7;c.strokeStyle='#111';c.stroke()
c.strokeStyle='#fff';c.lineWidth=5;for(const[x0,x1]of[[30,44],[212,226]])c.beginPath(),c.moveTo(x0,140),c.lineTo(x1,138),c.stroke()
if(head-tail>1)for(const[w,s]of[[12,'#111'],[6,'#fff']]){c.lineWidth=w;c.strokeStyle=s;c.beginPath();for(let x=tail;x<=head;x+=2)c.lineTo(x,zy(x));c.stroke()}
c.lineWidth=7;c.strokeStyle='#111';c.fillStyle='#fff'
c.beginPath();c.moveTo(30,160);c.quadraticCurveTo(128,172,226,160);c.quadraticCurveTo(226,206,180,206);c.lineTo(76,206);c.quadraticCurveTo(30,206,30,160);c.fill();c.stroke()
const happy=t>=.6&&t<.78
c.fillStyle='#111'
if(happy){c.lineWidth=5;for(const x of[106,150])c.beginPath(),c.arc(x,186,7,PI*1.15,PI*1.85),c.stroke()}
else{for(const x of[106,150])c.beginPath(),c.ellipse(x,184,5,6.5,0,0,T),c.fill();c.fillStyle='#fff';for(const x of[104,148])c.beginPath(),c.arc(x,182,2,0,T),c.fill()}
c.lineWidth=4.5;c.beginPath();c.arc(128,190,8,.15*PI,.85*PI);c.stroke()
c.lineWidth=3;for(const x of[86,170])for(const o of[-5,0,5])c.beginPath(),c.moveTo(x+o-2,198),c.lineTo(x+o+2,191),c.stroke()
c.lineWidth=7;c.fillStyle='#fff'
if(sq&&t>.02)for(const[w,s]of[[11,'#111'],[4,'#fff']])c.lineWidth=w,c.strokeStyle=s,c.beginPath(),c.moveTo(bx,by),c.lineTo(bx,zy(bx)-2),c.stroke()
c.lineWidth=7;c.strokeStyle='#111'
const k=sq?1-.1*abs(sin(10*T*t)):1
c.save();c.translate(bx,by);c.rotate(t>=.78?-.4*sin(PI*min(1,(t-.78)/.22)):0)
c.beginPath();c.moveTo(-5,0);c.lineTo(-12,-24);c.lineTo(12,-24);c.lineTo(5,0);c.closePath();c.fill();c.stroke()
c.scale(k,1);c.beginPath();c.roundRect(-22,-92,44,70,12);c.fill();c.stroke()
c.lineWidth=4;for(const y of[-66,-48])c.beginPath(),c.moveTo(-12,y),c.lineTo(12,y),c.stroke()
c.restore()
}
+23
View File
@@ -0,0 +1,23 @@
// icicles hanging from a snowy ledge, a drop swelling on the longest one, falling and splashing into a puddle
const{sin,cos,PI,max}=Math,T=2*PI
export const frames=24,delay=70
export default(c,t)=>{
c.beginPath();c.ellipse(128,226,82,17,0,0,T);c.fill();c.stroke()
const s=(t-.68)/.32
if(t>=.68){
c.lineWidth=4;for(const k of[0,.4]){const q=s-k;if(q>0&&q<.9)c.beginPath(),c.ellipse(128,226,14+62*q,4+11*q,0,0,T),c.stroke()}
c.lineWidth=4;for(const v of[-1,-.4,.4,1])if(s<.8)c.beginPath(),c.arc(128+v*50*s,220-80*s+100*s*s,7-5*s,0,T),c.fill(),c.stroke()
}
c.lineWidth=7
for(const[x,w,l]of[[54,24,50],[90,28,78],[128,32,104],[164,28,72],[200,22,46]]){
c.beginPath();c.moveTo(x-w/2,50);c.quadraticCurveTo(x-w*.18,50+l*.6,x,50+l);c.quadraticCurveTo(x+w*.18,50+l*.6,x+w/2,50);c.fill();c.stroke()
c.lineWidth=4;c.beginPath();c.moveTo(x-w*.18,58);c.quadraticCurveTo(x-w*.12,50+l*.45,x-2,50+l*.7);c.stroke();c.lineWidth=7
}
c.beginPath();c.roundRect(16,34,224,16,4);c.fill();c.stroke()
c.lineWidth=4;c.beginPath();for(const x of[70,136,196])c.moveTo(x,38),c.lineTo(x,46);c.stroke();c.lineWidth=7
c.beginPath();c.moveTo(12,40);c.quadraticCurveTo(14,18,40,18);c.quadraticCurveTo(70,6,100,16);c.quadraticCurveTo(136,4,170,16);c.quadraticCurveTo(204,6,226,18);c.quadraticCurveTo(246,22,244,40)
c.quadraticCurveTo(236,48,222,40);c.quadraticCurveTo(176,34,150,38);c.quadraticCurveTo(144,52,134,40);c.quadraticCurveTo(80,34,44,38);c.quadraticCurveTo(36,50,28,40);c.quadraticCurveTo(18,46,12,40);c.fill();c.stroke()
const drop=(x,y,r)=>{c.lineWidth=5;c.beginPath();c.moveTo(x,y-r*2.2);c.bezierCurveTo(x+r*.4,y-r*1.2,x+r,y-r*.6,x+r,y);c.arc(x,y,r,0,PI);c.bezierCurveTo(x-r,y-r*.6,x-r*.4,y-r*1.2,x,y-r*2.2);c.fill();c.stroke()}
if(t<.5){const r=3+8*t/.5;drop(128,148+r*1.8,r)}
else if(t<.68){const f=(t-.5)/.18;drop(128,170+f*f*50,10)}
}
+35
View File
@@ -0,0 +1,35 @@
// koala hugging a branch and dozing off, its head slowly nodding while Z's drift up
const{sin,cos,PI}=Math,T=2*PI
export const frames=20,delay=90
export default(c,t)=>{
const a=.07*sin(t*T),dy=3+3*sin(t*T)
c.beginPath();c.ellipse(122,184,50,54,0,0,T);c.fill();c.stroke()
c.save();c.translate(112,152);c.rotate(a);c.translate(-112,-152+dy)
for(const[x,y,r]of[[68,84,26],[150,76,24]]){
c.lineWidth=7;c.beginPath();c.arc(x,y,r,0,T);c.fill();c.stroke()
c.lineWidth=4;for(const k of[-1,0,1])c.beginPath(),c.moveTo(x+k*7,y+8),c.lineTo(x+k*9,y-6),c.stroke()
}
c.lineWidth=7;c.beginPath();c.arc(108,112,44,0,T);c.fill();c.stroke()
c.lineWidth=5;for(const x of[88,128])c.beginPath(),c.arc(x,104,8,.15*PI,.85*PI),c.stroke()
c.fillStyle='#111';c.beginPath();c.ellipse(108,122,13,16,0,0,T);c.fill()
c.fillStyle='#fff';c.beginPath();c.ellipse(103,115,3,5,-.3,0,T);c.fill()
c.lineWidth=4;c.beginPath();c.arc(108,140,6,.2*PI,.8*PI);c.stroke()
c.restore()
c.lineWidth=7;c.beginPath();c.roundRect(148,8,38,242,18);c.fill();c.stroke()
c.lineWidth=4;for(const[x,y]of[[160,30],[174,84],[160,118],[172,236]])c.beginPath(),c.moveTo(x,y),c.quadraticCurveTo(x+4,y+10,x,y+20),c.stroke()
for(const[w,s]of[[11,'#111'],[4,'#fff']])c.lineWidth=w,c.strokeStyle=s,c.beginPath(),c.moveTo(184,52),c.lineTo(226,30),c.stroke()
c.strokeStyle='#111'
for(const[x,y,r]of[[214,22,-.9],[232,46,.6]]){
c.lineWidth=6;c.beginPath();c.ellipse(x,y,16,8,r,0,T);c.fill();c.stroke()
c.lineWidth=3;c.beginPath();c.moveTo(x-12*cos(r),y-12*sin(r));c.lineTo(x+12*cos(r),y+12*sin(r));c.stroke()
}
for(const[x0,y0,x1,y1]of[[126,172,166,154],[138,214,170,214]]){
for(const[w,s]of[[34,'#111'],[20,'#fff']])c.lineWidth=w,c.strokeStyle=s,c.beginPath(),c.moveTo(x0,y0),c.lineTo(x1,y1),c.stroke()
c.strokeStyle='#111';c.lineWidth=6;c.beginPath();c.ellipse(x1+4,y1,14,12,0,0,T);c.fill();c.stroke()
c.lineWidth=3;for(const k of[-1,0,1])c.beginPath(),c.moveTo(x1+14,y1+k*5),c.lineTo(x1+20,y1+k*6),c.stroke()
}
for(const k of[0,.5]){
const p=(t+k)%1,s=9*sin(PI*p),x=110-12*p,y=46-26*p
if(s>2)for(const[w,st]of[[10,'#111'],[4,'#fff']])c.lineWidth=w,c.strokeStyle=st,c.beginPath(),c.moveTo(x-s,y-s),c.lineTo(x+s,y-s),c.lineTo(x-s,y+s),c.lineTo(x+s,y+s),c.stroke()
}
}
+27
View File
@@ -0,0 +1,27 @@
// lemon squeezed from both sides: it squashes, scrunches its face and squirts juice drops up and away
const{sin,PI,min,max}=Math,T=2*PI
export const frames=24,delay=70
export default(c,t)=>{
const s=sin(PI*min(1,max(0,(t-.12)/.46)))**1.5,sq=s>.35
c.save();c.translate(128,150);c.scale(1-.2*s,1+.16*s)
c.save();c.translate(10,-58);c.rotate(-.5);c.beginPath();c.ellipse(16,-6,20,9,-.2,0,T);c.fill();c.stroke();c.lineWidth=4;c.beginPath();c.moveTo(-2,-2);c.lineTo(30,-9);c.stroke();c.restore()
c.lineWidth=14
const nub=d=>{c.beginPath();c.ellipse(d*80,0,14,11,0,0,T)},body=()=>{c.beginPath();c.ellipse(0,0,78,58,0,0,T)}
for(const d of[-1,1])nub(d),c.stroke();body();c.stroke()
for(const d of[-1,1])nub(d),c.fill();body();c.fill()
c.lineWidth=3;for(const[x,y]of[[-50,-28],[46,-34],[-56,22],[54,24],[-30,40],[34,44],[-8,-46],[20,-44]])c.beginPath(),c.arc(x,y,2.5,0,T),c.stroke()
c.lineWidth=5;c.fillStyle='#111'
if(sq)for(const d of[-1,1])c.beginPath(),c.moveTo(d*16,-18),c.lineTo(d*30,-10),c.lineTo(d*16,-2),c.stroke()
else{for(const d of[-1,1])c.beginPath(),c.ellipse(d*24,-10,6,8,0,0,T),c.fill();c.fillStyle='#fff';for(const d of[-1,1])c.beginPath(),c.arc(d*24-2,-13,2.5,0,T),c.fill()}
if(sq){c.beginPath();c.ellipse(0,16,7,9,0,0,T);c.fillStyle='#111';c.fill()}
else{c.beginPath();c.arc(0,6,11,.15*PI,.85*PI);c.stroke()}
c.lineWidth=3;for(const d of[-1,1])for(const o of[-6,0,6])c.beginPath(),c.moveTo(d*46+o-2,14),c.lineTo(d*46+o+3,6),c.stroke()
c.restore()
if(s>.25)for(const d of[-1,1])for(const[w,st]of[[11,'#111'],[4,'#fff']]){c.lineWidth=w;c.strokeStyle=st;for(const y of[-22,0,22]){const x=128+d*(124-16*s);c.beginPath();c.moveTo(x,150+y);c.lineTo(x-d*12,150+y*.9);c.stroke()}}
c.lineWidth=4;c.strokeStyle='#111';c.fillStyle='#fff'
for(const[vx,v,t0]of[[-1,1,.3],[.35,1.15,.34],[1,.95,.38],[-.4,1.05,.43]]){
const u=(t-t0)/.4;if(u<0||u>=1)continue
const x=128+vx*86*u,y=88-v*362*u+546*u*u
c.beginPath();c.moveTo(x,y-11);c.quadraticCurveTo(x+8,y-1,x+7,y+3);c.arc(x,y+3,7,0,PI);c.quadraticCurveTo(x-8,y-1,x,y-11);c.fill();c.stroke()
}
}
+37
View File
@@ -0,0 +1,37 @@
// lion roaring: the mane puffs out, the mouth opens wide on its fangs and roar lines burst out
// the mane is one outline: every tuft is stroked wide first, then all of them are filled
const{sin,cos,PI}=Math,T=2*PI
export const frames=20,delay=70
const sm=x=>x<0?0:x>1?1:x*x*(3-2*x)
export default(c,t)=>{
const r=t<.3?0:t<.42?sm((t-.3)/.12):t<.8?1:sm(1-(t-.8)/.12),sh=r*1.5*sin(10*T*t),R=62+10*r,n=14
if(r>.5)for(const d of[-1,1])for(const k of[0,1]){
const p=((t-.42)*3+k*.5)%1,rr=104+16*p
for(const[l,s]of[[11,'#111'],[4,'#fff']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.arc(128,140,rr,d>0?-.32:PI-.32,d>0?.32:PI+.32),c.stroke()
}
c.translate(sh,0)
const L=[...Array(n)].map((_,i)=>[128+R*cos(i*T/n+.11),126+R*.94*sin(i*T/n+.11)]),tr=24+4*r
c.lineWidth=14;c.strokeStyle='#111';for(const[x,y]of L)c.beginPath(),c.arc(x,y,tr,0,T),c.stroke()
c.fillStyle='#fff';for(const[x,y]of L)c.beginPath(),c.arc(x,y,tr,0,T),c.fill()
c.beginPath();c.arc(128,126,R,0,T);c.fill()
c.lineWidth=7
for(const d of[-1,1]){c.beginPath();c.arc(128+d*38,86,14,0,T);c.fill();c.stroke();c.lineWidth=4;c.beginPath();c.arc(128+d*38,88,6,0,T);c.stroke();c.lineWidth=7}
c.beginPath();c.arc(128,128,50,0,T);c.fill();c.stroke()
if(r>.5){c.lineWidth=6;for(const d of[-1,1])c.beginPath(),c.moveTo(128+d*30,108),c.lineTo(128+d*14,116),c.lineTo(128+d*30,124),c.stroke()}
else{
c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.arc(128+d*20,116,6.5,0,T),c.fill()
c.fillStyle='#fff';for(const d of[-1,1])c.beginPath(),c.arc(128+d*20-2,113,2.3,0,T),c.fill()
}
if(r>.05){
const h=6+24*r,y=154+4*r
c.fillStyle='#111';c.beginPath();c.ellipse(128,y+h/2,14+8*r,h/2+4,0,0,T);c.fill()
c.fillStyle='#fff';c.lineWidth=3
for(const d of[-1,1])c.beginPath(),c.moveTo(128+d*15*(.6+.4*r),y),c.lineTo(128+d*9*(.6+.4*r),y+6+6*r),c.lineTo(128+d*5,y+2),c.closePath(),c.fill()
if(r>.4)for(const d of[-1,1])c.beginPath(),c.moveTo(128+d*14,y+h+4),c.lineTo(128+d*9,y+h-6),c.lineTo(128+d*5,y+h+3),c.closePath(),c.fill()
}
c.fillStyle='#fff';c.lineWidth=5
for(const d of[-1,1]){c.beginPath();c.ellipse(128+d*14,146,17,13,0,0,T);c.fill();c.stroke()}
c.fillStyle='#fff';c.beginPath();c.ellipse(128,148,10,9,0,0,T);c.fill()
c.fillStyle='#111';for(const d of[-1,1])for(const[x,y]of[[10,144],[18,150],[12,152]])c.beginPath(),c.arc(128+d*x,y,1.8,0,T),c.fill()
c.beginPath();c.moveTo(116,132);c.lineTo(140,132);c.quadraticCurveTo(140,140,128,146);c.quadraticCurveTo(116,140,116,132);c.fill()
}
+17
View File
@@ -0,0 +1,17 @@
// swirl lollipop spinning its spiral round, a ribbon on the stick and a sparkle twinkling beside it
const{sin,cos,PI,max}=Math,T=2*PI
export const frames=16,delay=70
const star=(c,x,y,r)=>{const k=r*.18;c.beginPath();c.moveTo(x,y-r);for(const[px,py,qx,qy]of[[r,0,k,-k],[0,r,k,k],[-r,0,-k,k],[0,-r,-k,-k]])c.quadraticCurveTo(x+qx,y+qy,x+px,y+py);c.fill();c.stroke()}
export default(c,t)=>{
c.beginPath();c.roundRect(119,150,18,96,7);c.fill();c.stroke()
c.beginPath();c.arc(128,98,72,0,T);c.fill()
c.save();c.clip();c.lineWidth=13;c.beginPath()
for(let th=0;th<=T*4.6;th+=.08){const r=th/T*16,a=th-T*t;c.lineTo(128+r*cos(a),98+r*sin(a))}
c.stroke();c.restore()
c.lineWidth=7;c.beginPath();c.arc(128,98,72,0,T);c.stroke()
c.strokeStyle='#fff';c.lineWidth=6;c.beginPath();c.arc(128,98,58,PI*1.12,PI*1.38);c.stroke();c.strokeStyle='#111';c.lineWidth=7
for(const d of[-1,1])c.beginPath(),c.moveTo(128,184),c.quadraticCurveTo(128+d*30,160,128+d*34,180),c.quadraticCurveTo(128+d*30,200,128,184),c.fill(),c.stroke()
for(const d of[-1,1])c.beginPath(),c.moveTo(128+d*3,188),c.lineTo(128+d*14,214),c.lineTo(128+d*22,206),c.closePath(),c.fill(),c.stroke()
c.beginPath();c.arc(128,185,7,0,T);c.fill();c.stroke()
c.lineWidth=4;for(const[x,y,ph,R]of[[214,36,0,15],[44,170,.5,10]]){const r=R*max(0,sin(PI*((t*2+ph)%1)));if(r>2)star(c,x,y,r)}
}
+26
View File
@@ -0,0 +1,26 @@
// milk carton tipping to pour into a glass that fills up, then a straw sips it back down for the loop
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)
const glass=c=>{c.beginPath();c.moveTo(160,140);c.lineTo(168,226);c.quadraticCurveTo(169,232,175,232);c.lineTo(213,232);c.quadraticCurveTo(219,232,220,226);c.lineTo(228,140)}
export default(c,t)=>{
const a=1.05*(ease(t/.18)-ease((t-.55)/.15)),pour=a>.85,L=.78*(ease((t-.17)/.4)-ease((t-.68)/.22)),ly=230-88*L
const C=[88,112],sp=[C[0]+34*cos(a)+42*sin(a),C[1]+34*sin(a)-42*cos(a)]
c.lineWidth=4;for(const x of[172,180])c.beginPath(),c.moveTo(x,166),c.lineTo(x+4,196),c.stroke()
if(L>.01){c.save();glass(c);c.clip();c.beginPath();c.moveTo(150,240);c.lineTo(150,ly);for(let x=150;x<=240;x+=3)c.lineTo(x,ly+(pour?2.5*sin(x/7-t*T*6):0));c.lineTo(240,240);c.fill();c.lineWidth=5;c.stroke();c.restore()}
const st=t>=.62&&t<.96?min(1,(t-.62)/.06,(.96-t)/.06):0
if(st>0){const o=(1-st)*70;for(const[w,s]of[[15,'#111'],[7,'#fff']])c.lineWidth=w,c.strokeStyle=s,c.beginPath(),c.moveTo(246+o*.4,70-o),c.lineTo(226+o*.4,100-o),c.lineTo(196+o*.4,214-o),c.stroke()}
for(const[w,s]of[[11,'#111'],[4,'#fff']])c.lineWidth=w,c.strokeStyle=s,glass(c),c.stroke()
if(pour)for(const[w,s]of[[14,'#111'],[6,'#fff']])c.lineWidth=w,c.strokeStyle=s,c.beginPath(),c.moveTo(...sp),c.quadraticCurveTo(sp[0]+40,sp[1]-4,194,ly),c.stroke()
c.lineWidth=7;c.strokeStyle='#111'
c.save();c.translate(...C);c.rotate(a)
c.beginPath();c.roundRect(-12,-72,24,12,3);c.fill();c.stroke()
c.beginPath();c.moveTo(-36,64);c.lineTo(-36,-38);c.lineTo(0,-64);c.lineTo(36,-38);c.lineTo(36,64);c.closePath();c.fill();c.stroke()
c.lineWidth=5;c.beginPath();c.moveTo(-36,-38);c.lineTo(36,-38);c.stroke()
if(pour)for(const x of[-14,14])c.beginPath(),c.arc(x,8,7,PI*1.15,PI*1.85),c.stroke()
else{c.fillStyle='#111';for(const x of[-14,14])c.beginPath(),c.ellipse(x,6,5,7,0,0,T),c.fill();c.fillStyle='#fff';for(const x of[-16,12])c.beginPath(),c.arc(x,3,2,0,T),c.fill()}
c.beginPath();c.arc(0,16,9,.15*PI,.85*PI);c.stroke()
c.lineWidth=3;for(const d of[-1,1])for(const o of[-5,0,5])c.beginPath(),c.moveTo(d*24+o-2,30),c.lineTo(d*24+o+2,23),c.stroke()
c.lineWidth=4;c.beginPath();c.moveTo(0,40);c.quadraticCurveTo(9,52,0,56);c.quadraticCurveTo(-9,52,0,40);c.stroke()
c.restore()
}
+35
View File
@@ -0,0 +1,35 @@
// monkey swinging from a vine by one arm and waving with the other, its legs and tail lagging behind
// drawn at Z times the base size, so line widths are divided by Z to stay at the usual ink weight
const{sin,cos,PI}=Math,T=2*PI
export const frames=16,delay=85
const Z=1.6,W=x=>x/Z
export default(c,t)=>{
const a=.12*sin(T*t),lg=-.22*cos(T*t),wv=sin(2*T*t)
const dbl=(f,core,o=12)=>{c.beginPath();f();for(const[l,s]of[[core+W(o),'#111'],[core,'#fff']])c.lineWidth=l,c.strokeStyle=s,c.stroke();c.strokeStyle='#111';c.lineWidth=W(7)}
c.translate(130,-64);c.rotate(a)
for(const[l,s]of[[13,'#111'],[5,'#fff']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(0,0),c.quadraticCurveTo(5,40,0,84),c.stroke()
c.translate(0,86);c.scale(Z,Z);c.translate(0,-86);c.strokeStyle='#111';c.lineWidth=W(7)
c.save();c.translate(14,198);c.rotate(lg)
dbl(()=>{c.moveTo(0,0);c.bezierCurveTo(30,14,40,-20,28,-28);c.quadraticCurveTo(18,-32,20,-20)},5,8)
for(const x of[-14,6])dbl(()=>{c.moveTo(x,0);c.lineTo(x-3,14)},12)
for(const x of[-17,3])c.beginPath(),c.ellipse(x,18,11,6.5,0,0,T),c.fill(),c.stroke()
c.restore()
dbl(()=>{c.moveTo(12,150);c.lineTo(2,90)},12)
c.beginPath();c.arc(0,88,9,0,T);c.fill();c.stroke()
c.beginPath();c.ellipse(6,178,26,28,0,0,T);c.fill();c.stroke()
c.lineWidth=W(5);c.beginPath();c.ellipse(4,184,14,16,0,0,T);c.stroke();c.lineWidth=W(7)
const hx=-38+4*wv,hy=190-8*wv
dbl(()=>{c.moveTo(-6,166);c.lineTo(hx,hy)},12)
c.beginPath();c.arc(hx-3,hy+2,9,0,T);c.fill();c.stroke()
for(const[x,y]of[[-52,136],[10,128]]){
c.beginPath();c.arc(x,y,10,0,T);c.fill();c.stroke()
c.lineWidth=W(4);c.beginPath();c.arc(x,y,4,0,T);c.stroke();c.lineWidth=W(7)
}
c.beginPath();c.arc(-21,134,31,0,T);c.fill();c.stroke()
const face=[()=>c.arc(-30,130,11.5,0,T),()=>c.arc(-12,130,11.5,0,T),()=>c.ellipse(-21,147,16,11,0,0,T)]
c.lineWidth=W(5);for(const f of face)c.beginPath(),f(),c.stroke();for(const f of face)c.beginPath(),f(),c.fill()
c.fillStyle='#111';for(const x of[-29,-13])c.beginPath(),c.arc(x,131,4.5,0,T),c.fill()
c.fillStyle='#fff';for(const x of[-30,-14])c.beginPath(),c.arc(x,129.5,1.6,0,T),c.fill()
c.fillStyle='#111';for(const x of[-24,-18])c.beginPath(),c.arc(x,143,1.6,0,T),c.fill()
c.lineWidth=W(4);c.beginPath();c.arc(-21,148,7.5,.15*PI,.85*PI);c.stroke()
}
+28
View File
@@ -0,0 +1,28 @@
// mouse sitting up and sniffing: nose twitching, whiskers wiggling, an ear flicking and its tail curling
const{sin,cos,PI}=Math,T=2*PI
export const delay=70
export default(c,t)=>{
const sn=t<.5?sin(8*T*t):0,tw=2*sn,ef=t>.6&&t<.8?sin(PI*(t-.6)/.2):0,tc=sin(T*t)
for(const[l,s]of[[15,'#111'],[6,'#fff']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(160,214),c.bezierCurveTo(206,232,238,204,226,170+6*tc),c.quadraticCurveTo(216,142+8*tc,198,152+6*tc),c.stroke()
c.lineWidth=7;c.strokeStyle='#111'
c.beginPath();c.ellipse(128,182,50,46,0,0,T);c.fill();c.stroke()
c.lineWidth=5;c.beginPath();c.ellipse(128,194,28,24,0,0,T);c.stroke();c.lineWidth=7
for(const x of[104,152])c.beginPath(),c.ellipse(x,226,19,9,0,0,T),c.fill(),c.stroke()
for(const d of[-1,1]){
c.save();c.translate(128+d*26,96);c.rotate(d>0?.35*ef:0)
c.beginPath();c.arc(d*20,-26,30,0,T);c.fill();c.stroke()
c.lineWidth=5;c.beginPath();c.arc(d*20,-26,16,0,T);c.stroke();c.lineWidth=7;c.restore()
}
c.beginPath();c.arc(128,112,46,0,T);c.fill();c.stroke()
for(const x of[112,144])c.beginPath(),c.arc(x,166,10,0,T),c.fill(),c.stroke()
for(const d of[-1,1])for(const a of[-.24,0,.24]){
const x=128+d*16,y=132+tw*.5,e=(d>0?0:PI)+d*(a+.1*sn)
for(const[l,s]of[[7,'#111'],[2.5,'#fff']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(x,y),c.lineTo(x+46*cos(e),y+46*sin(e)),c.stroke()
}
c.strokeStyle='#111';c.fillStyle='#111'
for(const x of[110,146])c.beginPath(),c.arc(x,106,7,0,T),c.fill()
c.fillStyle='#fff';for(const x of[108,144])c.beginPath(),c.arc(x,103,2.5,0,T),c.fill()
c.lineWidth=3;c.beginPath();c.rect(123,145,10,9);c.fill();c.stroke();c.beginPath();c.moveTo(128,145);c.lineTo(128,154);c.stroke()
c.lineWidth=4;c.beginPath();c.moveTo(128,128+tw);c.lineTo(128,139);c.arc(122,139,6,0,PI);c.moveTo(140,139);c.arc(134,139,6,0,PI);c.stroke()
c.fillStyle='#111';c.beginPath();c.ellipse(128,127+tw,8,6,0,0,T);c.fill()
}
+24
View File
@@ -0,0 +1,24 @@
// key slides into a padlock and turns, the shackle pops up and swings round on its right leg, then it all closes and the key slides out
// the swing is a horizontal squash about the right leg, so the shackle seems to turn in depth
const{sin,cos,PI,min,max,abs}=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 dx=26*(1-ease(t/.16))+26*ease((t-.88)/.12),turn=ease((t-.16)/.12)-ease((t-.76)/.12)
const up=ease((t-.28)/.1)-ease((t-.68)/.1),sw=2.4*(ease((t-.36)/.14)-ease((t-.54)/.14))
c.save();c.translate(148,-22*up);c.scale(cos(sw),1);c.translate(-148,0)
for(const[w,k]of[[34,'#111'],[20,'#fff']])c.lineWidth=w,c.strokeStyle=k,c.beginPath(),c.moveTo(72,150),c.lineTo(72,84),c.arc(110,84,38,PI,0),c.lineTo(148,150),c.stroke()
c.restore()
c.lineWidth=7;c.strokeStyle='#111'
c.beginPath();c.roundRect(40,122,140,110,18);c.fill();c.stroke()
c.lineWidth=5;c.beginPath();c.moveTo(54,138);c.lineTo(166,138);c.stroke()
c.fillStyle='#111';c.beginPath();c.arc(110,170,11,0,T);c.moveTo(104,174);c.lineTo(101,204);c.lineTo(119,204);c.lineTo(116,174);c.fill()
if(up>.6&&sw<.3)for(const a of[-2.4,-1.9,-1.4])for(const[w,k]of[[11,'#111'],[4,'#fff']])c.lineWidth=w,c.strokeStyle=k,c.beginPath(),c.moveTo(64+cos(a)*30,108+sin(a)*30),c.lineTo(64+cos(a)*44,108+sin(a)*44),c.stroke()
const k=max(.3,abs(cos(turn*PI/2)))
c.save();c.translate(dx,172);c.scale(1,k);c.lineWidth=7;c.strokeStyle='#111';c.fillStyle='#fff'
c.beginPath();c.roundRect(106,-9,84,18,6);c.fill();c.stroke()
c.lineWidth=5;for(const x of[136,152])c.beginPath(),c.moveTo(x,-9),c.lineTo(x,9),c.stroke()
c.lineWidth=7;c.beginPath();c.arc(204,0,24,0,T);c.fill();c.stroke()
c.beginPath();c.arc(208,0,8,0,T);c.stroke()
c.restore()
}
+28
View File
@@ -0,0 +1,28 @@
// pigeon walking in place: its head jerks forward and holds while the body catches up, feet stepping in turn
const{sin,cos,PI,max}=Math,T=2*PI
export const frames=20,delay=60
export default(c,t)=>{
const q=(2*t)%1,s=q/.25,e=q<.25?s*s*(3-2*s):1-(q-.25)/.75,hx=-6+18*e,hy=-3*sin(PI*e),by=12+1.5*cos(4*PI*t)
for(const j of[0,1]){
const l=max(0,sin(T*t+j*PI)),fx=100+j*24+8*l,fy=234-10*l
for(const[w,st]of[[11,'#111'],[4,'#fff']]){
c.lineWidth=w;c.strokeStyle=st;c.beginPath()
c.moveTo(104+j*20,180+by);c.lineTo(fx,fy);c.moveTo(fx-8,fy);c.lineTo(fx+14,fy);c.moveTo(fx,fy);c.lineTo(fx+12,fy+5)
c.stroke()
}
}
c.strokeStyle='#111';c.lineWidth=7
c.beginPath();c.moveTo(74,136+by);c.lineTo(20,160+by);c.lineTo(26,180+by);c.lineTo(78,164+by);c.closePath();c.fill();c.stroke()
c.lineWidth=4;c.beginPath();c.moveTo(36,156+by);c.lineTo(42,174+by);c.moveTo(48,151+by);c.lineTo(54,170+by);c.stroke()
const hd=()=>{c.beginPath();c.arc(160+hx,84+hy+by,21,0,T)},bd=()=>{c.beginPath();c.ellipse(110,146+by,58,42,-.12,0,T)},nk=()=>{c.beginPath();c.moveTo(134,128+by);c.lineTo(150+hx*.7,98+hy+by)}
c.lineWidth=14;bd();c.stroke();hd();c.stroke();c.lineWidth=44;nk();c.stroke()
bd();c.fill();hd();c.fill();c.lineWidth=30;c.strokeStyle='#fff';nk();c.stroke();c.strokeStyle='#111'
c.lineWidth=5;c.beginPath();c.moveTo(80,128+by);c.quadraticCurveTo(140,118+by,150,156+by);c.quadraticCurveTo(116,182+by,62,150+by);c.stroke()
c.fillStyle='#111';for(const x of[100,120])c.beginPath(),c.moveTo(x,134+by),c.lineTo(x+8,132+by),c.lineTo(x+20,160+by),c.lineTo(x+12,163+by),c.closePath(),c.fill()
c.lineWidth=4;for(const r of[24,32])c.beginPath(),c.arc(130+hx*.4,104+by+hy*.5,r,-.2*PI,.25*PI),c.stroke()
const X=160+hx,Y=84+hy+by
c.fillStyle='#fff';c.lineWidth=5;c.beginPath();c.moveTo(176+hx,80+hy+by);c.lineTo(197+hx,88+hy+by);c.lineTo(177+hx,93+hy+by);c.closePath();c.fill();c.stroke()
c.lineWidth=3;c.beginPath();c.ellipse(X+18,Y-5,5,4,0,0,T);c.fill();c.stroke()
c.beginPath();c.arc(X+6,Y-4,6,0,T);c.fill();c.stroke()
c.fillStyle='#111';c.beginPath();c.arc(X+7,Y-4,3,0,T);c.fill()
}
+22
View File
@@ -0,0 +1,22 @@
// custard pudding on a plate wobbling and jiggling, the caramel on top shining
// the whole pudding gets a white rim so the black caramel shows on dark backgrounds
const{sin,exp,PI}=Math,T=2*PI
export const frames=16,delay=70
const body=c=>{c.beginPath();c.moveTo(-66,0);c.lineTo(-46,-94);c.quadraticCurveTo(0,-108,46,-94);c.lineTo(66,0);c.quadraticCurveTo(0,8,-66,0);c.closePath()}
export default(c,t)=>{
c.beginPath();c.ellipse(128,214,100,20,0,0,T);c.fill();c.stroke()
c.lineWidth=4;c.beginPath();c.ellipse(128,212,74,12,0,0,T);c.stroke();c.lineWidth=7
const k=.16*sin(2*T*t),sy=1+.05*sin(4*T*t+.6)
c.save();c.translate(128,212);c.transform(1,0,k,sy,0,0)
body(c);c.lineWidth=16;c.strokeStyle='#fff';c.stroke();c.fill()
c.save();body(c);c.clip();c.fillStyle='#111';c.beginPath();c.moveTo(-90,-130);c.lineTo(90,-130)
for(let x=90;x>=-90;x-=2)c.lineTo(x,-82+[[-48,12],[-22,22],[4,10],[28,18],[52,9]].reduce((y,[d,L])=>y+L*exp(-(((x-d)/6)**2)),0))
c.closePath();c.fill();c.restore()
c.lineWidth=7;c.strokeStyle='#111';body(c);c.stroke()
c.strokeStyle='#fff';c.lineWidth=5;c.beginPath();c.moveTo(-32,-94);c.quadraticCurveTo(-20,-100,-6,-100);c.stroke();c.strokeStyle='#111'
c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.ellipse(d*18,-42,5.5,7.5,0,0,T),c.fill()
c.fillStyle='#fff';for(const d of[-1,1])c.beginPath(),c.arc(d*18-2,-45,2.2,0,T),c.fill()
c.lineWidth=4.5;c.beginPath();c.arc(0,-32,9,.15*PI,.85*PI);c.stroke()
c.lineWidth=3;for(const d of[-1,1])for(const o of[-5,0,5])c.beginPath(),c.moveTo(d*38+o-2,-24),c.lineTo(d*38+o+2,-32),c.stroke()
c.restore()
}
+32
View File
@@ -0,0 +1,32 @@
// two jigsaw pieces slide together and snap into place with a click spark, grin about it, then drift apart
const{sin,cos,PI,atan2,max}=Math,T=2*PI
export const frames=24,delay=70
const D=(c,f,a=11,b=4)=>{for(const[l,s]of[[a,'#111'],[b,'#fff']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),f(),c.stroke();c.lineWidth=7;c.strokeStyle='#111'}
// one side from (x1,y1) to (x2,y2) with a round tab (k=1) or slot (k=-1) in the middle; outward is to the left of travel
const side=(c,x1,y1,x2,y2,k)=>{
const l=Math.hypot(x2-x1,y2-y1),dx=(x2-x1)/l,dy=(y2-y1)/l,nx=dy*k,ny=-dx*k,mx=(x1+x2)/2,my=(y1+y2)/2
const cx=mx+nx*13,cy=my+ny*13,a1=atan2(my-dy*9-cy,mx-dx*9-cx),a2=atan2(my+dy*9-cy,mx+dx*9-cx),ao=atan2(ny,nx)
const cw=((ao-a1)%T+T)%T<((a2-a1)%T+T)%T
c.lineTo(mx-dx*9,my-dy*9);c.arc(cx,cy,15.8,a1,a2,!cw);c.lineTo(x2,y2)
}
const face=(c,x,happy,look)=>{
c.fillStyle='#111';c.lineWidth=5
for(const d of[-1,1]){
if(happy)c.beginPath(),c.arc(x+d*12,2,7,PI*1.1,PI*1.9),c.stroke()
else c.beginPath(),c.ellipse(x+d*12+look,0,5,7,0,0,T),c.fill()
}
if(happy){c.lineWidth=3;for(const d of[-1,1])for(const o of[-4,0,4])c.beginPath(),c.moveTo(x+d*20+o-2,16),c.lineTo(x+d*20+o+2,10),c.stroke()}
c.lineWidth=4;c.beginPath();c.arc(x,10,happy?8:5,.15*PI,.85*PI);c.stroke()
c.lineWidth=7;c.fillStyle='#fff'
}
export default(c,t)=>{
const g=t<.35?22*(1-(t/.35)**2):t<.7?0:22*(1-cos(PI*(t-.7)/.3))/2,on=t>=.35&&t<.7,j=on&&t<.45?3*sin((t-.35)*80):0
c.translate(128,128);c.scale(1.12,1.12);c.translate(-128,-128)
if(t>=.35&&t<.52){const k=sin(PI*(t-.35)/.17);for(const a of[-.9,-.45,.45,.9])D(c,()=>{c.moveTo(128+sin(a)*20*k,90-cos(a)*20*k);c.lineTo(128+sin(a)*44*k,90-cos(a)*44*k)})}
c.save();c.translate(128-g,128+j)
c.beginPath();c.moveTo(-76,-38);side(c,-76,-38,0,-38,1);side(c,0,-38,0,38,1);c.lineTo(-76,38);c.closePath();c.fill();c.stroke()
face(c,-38,on,g>4?4:0);c.restore()
c.save();c.translate(128+g,128+j)
c.beginPath();c.moveTo(0,-38);c.lineTo(76,-38);c.lineTo(76,38);side(c,76,38,0,38,1);side(c,0,38,0,-38,-1);c.closePath();c.fill();c.stroke()
face(c,42,on,g>4?-4:0);c.restore()
}
+11
View File
@@ -0,0 +1,11 @@
// big question mark bouncing and tilting curiously while its dot hops along on its own beat
const{sin,PI,abs}=Math,T=2*PI
export const frames=20,delay=70
export default(c,t)=>{
const j=30*abs(sin(PI*t)),sq=j<5,dj=24*abs(sin(2*PI*t))
c.save();c.translate(128,184-j);c.rotate(.16*sin(t*T));if(sq)c.scale(1.1,.9);c.translate(-128,-184)
for(const[w,s]of[[48,'#111'],[34,'#fff']]){c.lineWidth=w;c.strokeStyle=s;c.beginPath();c.arc(128,82,40,PI,PI*2.3);c.quadraticCurveTo(128,126,128,148);c.lineTo(128,158);c.stroke()}
c.restore()
c.lineWidth=7;c.strokeStyle='#111'
c.beginPath();c.ellipse(128,212-dj,dj<4?22:19,dj<4?16:19,0,0,T);c.fill();c.stroke()
}
+18
View File
@@ -0,0 +1,18 @@
// scissors snipping open and shut twice, with a little spark at the tips each time they close
const{sin,cos,PI,max}=Math,T=2*PI
export const frames=16,delay=70
export default(c,t)=>{
const o=.5+.5*cos(2*T*t),th=.03+.3*o
c.translate(140,122);c.rotate(-.75)
for(const s of[1,-1]){
c.save();c.rotate(s*th)
for(const[w,k]of[[30,'#111'],[16,'#fff']])c.lineWidth=w,c.strokeStyle=k,c.beginPath(),c.moveTo(0,0),c.quadraticCurveTo(-28,-s*4,-50,-s*22),c.stroke(),c.beginPath(),c.arc(-72,-s*36,24,0,T),c.stroke()
c.lineWidth=7;c.strokeStyle='#111'
c.beginPath();c.moveTo(-10,-s*14);c.quadraticCurveTo(50,-s*18,112,0);c.quadraticCurveTo(50,s*4,-10,s*8);c.closePath();c.fill();c.stroke()
c.restore()
}
c.beginPath();c.arc(0,0,10,0,T);c.fill();c.stroke()
c.fillStyle='#111';c.beginPath();c.arc(0,0,3.5,0,T);c.fill();c.fillStyle='#fff'
const sp=max(0,1-o*4)
if(sp>0)for(const a of[-1,0,1])for(const[w,k]of[[11,'#111'],[4,'#fff']])c.lineWidth=w,c.strokeStyle=k,c.beginPath(),c.moveTo(116+cos(a)*12,sin(a)*12),c.lineTo(116+cos(a)*(12+16*sp),sin(a)*(12+16*sp)),c.stroke()
}
+26
View File
@@ -0,0 +1,26 @@
// seal balancing a ball on its nose, the ball rolling side to side while its flippers clap
const{sin,cos,PI,atan2}=Math,T=2*PI
export const frames=16,delay=70
export default(c,t)=>{
const bx=11*sin(t*T),cl=(1-cos(2*T*t))/2,ph=.15+1.75*cl
const flip=(px,py,L)=>{const dx=sin(ph),dy=cos(ph);c.beginPath();c.ellipse(px+dx*L/2,py+dy*L/2,10,L/2+4,atan2(dy,dx)-PI/2,0,T);c.fill();c.stroke()}
for(const[x,y,r]of[[50,212,-.9],[44,234,-.3]])c.beginPath(),c.ellipse(x,y,22,10,r,0,T),c.fill(),c.stroke()
flip(150,166,40)
const parts=[[112,196,66,40,-.35],[148,150,26,38,-.35],[158,112,30,26,-.95],[180,88,17,12,-.95]]
c.lineWidth=14;for(const[x,y,rx,ry,r]of parts)c.beginPath(),c.ellipse(x,y,rx,ry,r,0,T),c.stroke()
for(const[x,y,rx,ry,r]of parts)c.beginPath(),c.ellipse(x,y,rx,ry,r,0,T),c.fill()
c.lineWidth=4;c.beginPath();c.moveTo(150,186);c.quadraticCurveTo(130,226,74,222);c.stroke()
c.fillStyle='#111';for(const[x,y]of[[96,176],[84,190],[104,194]])c.beginPath(),c.arc(x,y,3.5,0,T),c.fill()
c.beginPath();c.ellipse(164,104,6,8,-.3,0,T);c.fill();c.beginPath();c.ellipse(189,76,6,5,-.95,0,T);c.fill()
c.fillStyle='#fff';c.beginPath();c.arc(162,101,2.2,0,T);c.fill()
c.fillStyle='#111';for(const[x,y]of[[176,92],[182,96],[172,98]])c.beginPath(),c.arc(x,y,1.8,0,T),c.fill()
c.lineWidth=4;c.beginPath();c.moveTo(178,104);c.quadraticCurveTo(186,102,192,94);c.stroke()
c.lineWidth=7;c.fillStyle='#fff';flip(136,174,46)
if(cl>.8)for(const a of[-.6,0,.6])for(const[l,s]of[[11,'#111'],[4,'#fff']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(196+cos(a)*14,156+sin(a)*14),c.lineTo(196+cos(a)*26,156+sin(a)*26),c.stroke()
c.strokeStyle='#111';c.lineWidth=7
const x=190+bx,y=50+bx*bx/60
c.beginPath();c.arc(x,y,26,0,T);c.fill();c.stroke()
c.save();c.translate(x,y);c.rotate(bx/26);c.fillStyle='#111'
c.beginPath();c.arc(0,0,23,0,T);c.clip();c.beginPath();c.ellipse(0,0,9,30,0,0,T);c.fill()
c.lineWidth=4;c.beginPath();c.ellipse(0,0,19,30,0,0,T);c.stroke();c.restore()
}
+17
View File
@@ -0,0 +1,17 @@
// shark fin circling round a pool of water, bigger as it passes in front, a little wake trailing behind
const{sin,cos,PI,abs}=Math,T=2*PI
export const frames=20,delay=70
export default(c,t)=>{
c.beginPath();for(let k=0;k<=120;k++){const a=k/120*T,r=1+.025*sin(a*16);c.lineTo(128+114*r*cos(a),150+74*r*sin(a))}
c.closePath();c.fill();c.stroke()
c.lineWidth=4;c.beginPath();c.ellipse(128,150,96,60,0,0,T);c.stroke()
const a=t*T+PI/2,x=128+62*cos(a),y=158+36*sin(a),dir=sin(a)>0?-1:1,d=.75+.25*sin(a),sx=dir*(.4+.6*abs(sin(a)))*d*1.3,sy=d*1.3
const P=(px,py)=>[x+px*sx,y+py*sy]
c.lineWidth=4;c.beginPath()
for(const s of[-1,1])c.moveTo(...P(-26,0)),c.quadraticCurveTo(...P(-40,5*s),...P(-58,4+8*s))
c.stroke()
c.beginPath();c.moveTo(...P(-22,0));c.quadraticCurveTo(...P(-6,-28),...P(-20,-64));c.quadraticCurveTo(...P(16,-46),...P(30,0));c.closePath()
c.lineWidth=7;c.fillStyle='#111';c.fill();c.stroke()
c.strokeStyle='#fff';c.lineWidth=4;c.beginPath();c.moveTo(...P(14,-8));c.quadraticCurveTo(...P(8,-30),...P(-8,-46));c.stroke()
c.strokeStyle='#111';c.fillStyle='#fff'
}
+23
View File
@@ -0,0 +1,23 @@
// fluffy sheep hopping in place, its wool squashing on landing and stretching in the air
// the wool is one outline: every puff is stroked wide first, then all of them are filled
const{sin,cos,PI}=Math,T=2*PI
export const delay=60
export default(c,t)=>{
const air=t>=.25,a=(t-.25)/.75,h=air?70*4*a*(1-a):0,g=air?0:sin(PI*t/.25),st=air?.08*(1-4*a*(1-a)):0
const sx=1+.16*g-.4*st,sy=1-.16*g+st
const wool=(L,r)=>{c.lineWidth=14;c.strokeStyle='#111';for(const[x,y]of L)c.beginPath(),c.arc(x,y,r,0,T),c.stroke();c.fillStyle='#fff';for(const[x,y]of L)c.beginPath(),c.arc(x,y,r,0,T),c.fill()}
const dark=f=>{c.beginPath();f();c.lineWidth=14;c.strokeStyle='#fff';c.stroke();c.fillStyle='#111';c.fill()}
c.save();c.translate(128,232-h);c.scale(sx,sy)
for(const x of[-38,-14,14,38])for(const[l,s]of[[22,'#fff'],[12,'#111']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(x,-50),c.lineTo(x,-6),c.stroke()
wool([...Array(14)].map((_,i)=>[66*cos(i*T/14),-90+40*sin(i*T/14)]),22)
c.beginPath();c.ellipse(0,-90,66,40,0,0,T);c.fill()
for(const d of[-1,1])dark(()=>c.ellipse(d*34,-92,20,9,d*.45,0,T))
dark(()=>c.ellipse(0,-76,27,33,0,0,T))
wool([[-13,-104],[0,-111],[13,-104]],12)
c.fillStyle='#fff';for(const d of[-1,1])c.beginPath(),c.arc(d*11,-80,8,0,T),c.fill()
c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.arc(d*10,-79,4.5,0,T),c.fill()
c.fillStyle='#fff';for(const d of[-1,1])c.beginPath(),c.arc(d*10-1.5,-81,1.6,0,T),c.fill()
c.strokeStyle='#fff';c.lineWidth=3;c.beginPath();c.moveTo(-8,-60);c.quadraticCurveTo(-4,-54,0,-59);c.quadraticCurveTo(4,-54,8,-60);c.stroke()
c.restore();c.strokeStyle='#111';c.lineWidth=7;c.fillStyle='#fff'
if(g>.2)for(const s of[-1,1])c.beginPath(),c.arc(128+s*(100+12*g),226,9*g,0,T),c.fill(),c.stroke()
}
+20
View File
@@ -0,0 +1,20 @@
// cute skull bobbing along while its jaw chatters open and shut
const{sin,PI,abs}=Math,T=2*PI
export const frames=16,delay=70
const D=(c,f,a=11,b=4)=>{for(const[l,s]of[[a,'#111'],[b,'#fff']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),f(),c.stroke();c.lineWidth=7;c.strokeStyle='#111'}
const teeth=(c,y,h)=>{c.lineWidth=4;for(const x of[104,116,128,140,152])c.beginPath(),c.roundRect(x-6,y,12,h,3),c.fill(),c.stroke();c.lineWidth=7}
export default(c,t)=>{
const o=12*abs(sin(4*PI*t)),b=4*sin(t*T)
if(o<4)for(const d of[-1,1])for(const y of[-12,0,12])D(c,()=>{c.moveTo(128+d*(74+(y?0:6)),186+y);c.lineTo(128+d*94,186+y)})
c.translate(128,140+b);c.rotate(.05*sin(t*T));c.translate(-128,-140)
c.beginPath();c.moveTo(92,182+o);c.lineTo(164,182+o);c.quadraticCurveTo(166,214+o,128,216+o);c.quadraticCurveTo(90,214+o,92,182+o);c.fill();c.stroke()
teeth(c,172+o,14)
const sk=()=>{c.beginPath();c.ellipse(128,94,76,68,0,0,T)},up=()=>{c.beginPath();c.roundRect(86,110,84,52,16)}
c.lineWidth=14;sk();c.stroke();up();c.stroke();sk();c.fill();up();c.fill();c.lineWidth=7
teeth(c,158,14)
c.fillStyle='#111'
for(const x of[100,156])c.beginPath(),c.ellipse(x,106,20,22,0,0,T),c.fill()
c.beginPath();c.moveTo(128,128);c.lineTo(119,143);c.quadraticCurveTo(128,147,137,143);c.closePath();c.fill()
c.fillStyle='#fff';for(const x of[100,156])c.beginPath(),c.arc(x-6,99,6,0,T),c.fill(),c.beginPath(),c.arc(x+6,114,2.5,0,T),c.fill()
c.lineWidth=4;c.beginPath();c.moveTo(150,30);c.lineTo(156,44);c.lineTo(148,52);c.lineTo(154,62);c.stroke()
}
+26
View File
@@ -0,0 +1,26 @@
// sloth hanging from a branch by its arms, slowly swaying and taking a long sleepy blink
const{sin,cos,PI}=Math,T=2*PI
export const frames=16,delay=100
export default(c,t)=>{
const a=.07*sin(T*t),bl=t>.5&&t<.8
const dbl=(f,b,w)=>{c.beginPath();f();for(const[l,s]of[[b,'#111'],[w,'#fff']])c.lineWidth=l,c.strokeStyle=s,c.stroke();c.strokeStyle='#111';c.lineWidth=7}
for(const[x,r]of[[34,-.5],[222,.5]])c.beginPath(),c.ellipse(x,30,16,8,r,0,T),c.fill(),c.stroke()
c.beginPath();c.roundRect(12,38,232,24,12);c.fill();c.stroke()
c.lineWidth=4;for(const x of[40,196,220])c.beginPath(),c.moveTo(x,46),c.quadraticCurveTo(x+8,50,x+14,46),c.stroke();c.lineWidth=7
c.translate(128,50);c.rotate(a);c.translate(-128,-50)
for(const d of[-1,1])dbl(()=>{c.moveTo(128+d*24,212);c.quadraticCurveTo(128+d*36,232,128+d*30,240)},26,14)
for(const d of[-1,1])dbl(()=>{c.moveTo(128+d*34,152);c.lineTo(128+d*36,56)},30,18)
c.lineWidth=5
for(const d of[-1,1])for(const o of[-8,0,8])c.beginPath(),c.moveTo(128+d*36+o,58),c.quadraticCurveTo(128+d*36+o+9,40,128+d*36+o-1,34),c.stroke()
c.lineWidth=7
c.beginPath();c.ellipse(128,182,46,50,0,0,T);c.fill();c.stroke()
c.lineWidth=4;for(const y of[180,196,212])c.beginPath(),c.arc(128,y-30,26,.35*PI,.65*PI),c.stroke();c.lineWidth=7
c.beginPath();c.arc(128,130,40,0,T);c.fill();c.stroke()
c.lineWidth=4;c.beginPath();c.ellipse(128,138,30,24,0,0,T);c.stroke()
c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*15,136,12,6.5,d*.45,0,T),c.fill()
c.fillStyle='#fff';c.strokeStyle='#fff'
for(const d of[-1,1])if(bl)c.lineWidth=2.5,c.beginPath(),c.arc(128+d*14,134,4,.15*PI,.85*PI),c.stroke()
else{c.beginPath();c.arc(128+d*14,135,4.5,0,T);c.fill();c.fillStyle='#111';c.beginPath();c.arc(128+d*14,135.5,2.8,0,T);c.fill();c.fillStyle='#fff';c.beginPath();c.arc(128+d*14-1,134,1,0,T);c.fill()}
c.strokeStyle='#111';c.fillStyle='#111';c.beginPath();c.ellipse(128,148,7,5,0,0,T);c.fill()
c.lineWidth=3.5;c.beginPath();c.arc(128,149,9,.2*PI,.8*PI);c.stroke()
}
+34
View File
@@ -0,0 +1,34 @@
// snowman waving a twig arm and tipping its top hat while snowflakes drift past
const{sin,cos,PI,max}=Math,T=2*PI
export const frames=20,delay=80
const flake=(c,x,y,r)=>{for(const[w,s]of[[9,'#111'],[3,'#fff']]){c.lineWidth=w;c.strokeStyle=s;c.beginPath();for(let k=0;k<3;k++){const a=k*PI/3;c.moveTo(x-cos(a)*r,y-sin(a)*r);c.lineTo(x+cos(a)*r,y+sin(a)*r)}c.stroke()}}
const twig=(c,x0,y0,a,L)=>{
const x1=x0+cos(a)*L,y1=y0+sin(a)*L
for(const[w,s]of[[11,'#111'],[4,'#fff']]){
c.lineWidth=w;c.strokeStyle=s;c.beginPath();c.moveTo(x0,y0);c.lineTo(x1,y1)
for(const d of[-.6,.6])c.moveTo(x0+cos(a)*L*.7,y0+sin(a)*L*.7),c.lineTo(x0+cos(a)*L*.7+cos(a+d)*16,y0+sin(a)*L*.7+sin(a+d)*16)
c.moveTo(x1,y1);c.lineTo(x1+cos(a-.5)*12,y1+sin(a-.5)*12);c.moveTo(x1,y1);c.lineTo(x1+cos(a+.5)*12,y1+sin(a+.5)*12);c.stroke()
}
c.strokeStyle='#111'
}
export default(c,t)=>{
for(const[x,o]of[[30,0],[226,.4],[206,.75]]){const p=(t+o)%1;if(p>.08&&p<.92)flake(c,x+6*sin(p*T),p*130+18,8*sin(PI*p))}
twig(c,100,140,PI+.45,58);twig(c,156,140,-.75+.4*sin(2*T*t),58)
const B=[[128,204,46],[128,140,34],[128,86,26]]
c.lineWidth=14;for(const[x,y,r]of B)c.beginPath(),c.arc(x,y,r,0,T),c.stroke()
for(const[x,y,r]of B)c.beginPath(),c.arc(x,y,r,0,T),c.fill()
c.fillStyle='#111';for(const y of[126,144,162])c.beginPath(),c.arc(128,y,4.5,0,T),c.fill()
for(const x of[118,138])c.beginPath(),c.arc(x,78,4.5,0,T),c.fill()
for(const k of[-2,-1,0,1,2])c.beginPath(),c.arc(128+k*7,100-Math.abs(k)*2,2.5,0,T),c.fill()
c.fillStyle='#fff';c.lineWidth=5;c.beginPath();c.moveTo(128,84);c.lineTo(156,90);c.lineTo(128,93);c.closePath();c.fill();c.stroke()
c.lineWidth=3;c.beginPath();c.moveTo(138,86);c.lineTo(136,91);c.moveTo(146,88);c.lineTo(144,91);c.stroke()
const sw=3*sin(T*t)
c.lineWidth=6;c.beginPath();c.moveTo(146,110);c.quadraticCurveTo(152+sw,130,150+sw,146);c.lineTo(138+sw,146);c.quadraticCurveTo(142,128,136,110);c.fill();c.stroke()
c.beginPath();c.roundRect(98,106,60,14,7);c.fill();c.stroke()
c.lineWidth=4;c.beginPath();for(const x of[112,128,144])c.moveTo(x,108),c.lineTo(x,118);c.moveTo(140+sw,134);c.lineTo(150+sw,134);c.stroke()
const l=max(0,sin(T*t))**2
c.save();c.translate(150,64);c.rotate(-.35*l);c.translate(-150,-64-10*l)
const dark=f=>{c.beginPath();f();c.lineWidth=16;c.strokeStyle='#fff';c.stroke();c.fillStyle='#111';c.fill();c.lineWidth=7;c.strokeStyle='#111';c.stroke()}
dark(()=>c.roundRect(110,26,36,36,4));dark(()=>c.ellipse(128,62,32,7,0,0,T))
c.fillStyle='#fff';c.fillRect(110,48,36,7);c.restore()
}
+22
View File
@@ -0,0 +1,22 @@
// spider bouncing on its thread like a bungee cord, legs wiggling
// legs and body get their white rims in one pass first, so they read on black
const{sin,cos,PI}=Math,T=2*PI
export const delay=70
export default(c,t)=>{
const y=132+32*sin(T*t)
for(const[l,s]of[[6,'#111'],[2,'#fff']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),c.moveTo(128,0),c.lineTo(128,y-20),c.stroke()
const legs=[]
for(const d of[-1,1])for(let i=0;i<4;i++){
const wg=6*sin(2*T*t+i*1.3+(d>0?1:0)),by=y-9+i*7
legs.push([[128+d*18,by],[128+d*(50+i*3),y-30+i*14+wg*.5],[128+d*(70-i*2),y-10+i*20+wg]])
}
const pl=p=>{c.beginPath();c.moveTo(...p[0]);c.lineTo(...p[1]);c.lineTo(...p[2])}
c.strokeStyle='#fff';c.lineWidth=15;for(const p of legs)pl(p),c.stroke()
c.lineWidth=16;c.beginPath();c.ellipse(128,y,30,27,0,0,T);c.stroke()
c.strokeStyle='#111';c.lineWidth=7;for(const p of legs)pl(p),c.stroke()
c.fillStyle='#111';c.beginPath();c.ellipse(128,y,30,27,0,0,T);c.fill()
c.fillStyle='#fff';for(const d of[-1,1])c.beginPath(),c.arc(128+d*11,y-4,8,0,T),c.fill()
c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.arc(128+d*10,y-2,4.5,0,T),c.fill()
c.fillStyle='#fff';for(const d of[-1,1])c.beginPath(),c.arc(128+d*10-1.5,y-4,1.5,0,T),c.fill()
c.strokeStyle='#fff';c.lineWidth=3;c.beginPath();c.arc(128,y+8,6,.15*PI,.85*PI);c.stroke()
}
+28
View File
@@ -0,0 +1,28 @@
// spring toy squashes down, boings up into the air stretched and wobbling, then lands and settles
const{sin,cos,PI,max,min}=Math,T=2*PI
export const frames=24,delay=60
const N=4,R=40,E=11
const D=(c,f,a=11,b=4)=>{for(const[l,s]of[[a,'#111'],[b,'#fff']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),f(),c.stroke();c.lineWidth=7;c.strokeStyle='#111'}
export default(c,t)=>{
const sq=t<.3,air=!sq&&t<.8,a=(t-.3)/.5
const L=sq?100-50*(t/.3)**2:air?100+30*(1-a)*cos(3*PI*a):100-16*sin(PI*(t-.8)/.2),J=air?44*4*a*(1-a):0
const yb=212-J,yt=yb-L,hy=yt-28,k=1-J/110
D(c,()=>{c.moveTo(128-46*k,232);c.lineTo(128+46*k,232)})
c.beginPath();c.ellipse(128,yb+8,50,13,0,0,PI);c.lineTo(78,yb);c.ellipse(128,yb,50,13,0,PI,0,true);c.closePath();c.fill();c.stroke()
c.beginPath();c.ellipse(128,yb,50,13,0,0,T);c.fill();c.stroke()
// the coil: back half-turns first, then the front ones over them
const P=th=>[128+R*sin(th),yb-L*th/(N*T)+E*cos(th)]
for(const f of[1,0])for(let k=f;k<=2*N;k+=2){
const a0=max(0,(k-.5)*PI),a1=min(N*T,(k+.5)*PI)
D(c,()=>{for(let j=0;j<=12;j++){const[x,y]=P(a0+(a1-a0)*j/12);c.lineTo(x,y)}},14,5)
}
c.beginPath();c.ellipse(128,yt+4,30,9,0,0,T);c.fill();c.stroke()
c.beginPath();c.arc(128,hy,30,0,T);c.fill();c.stroke()
if(air&&a<.5)for(const d of[-1,1])D(c,()=>{for(let j=0;j<=6;j++)c.lineTo(128+d*(48+(j%2)*10),hy-24+j*8)})
c.fillStyle='#111';c.lineWidth=5
if(sq&&t>.08)for(const d of[-1,1])c.beginPath(),c.moveTo(128+d*18,hy-10),c.lineTo(128+d*8,hy-4),c.lineTo(128+d*18,hy+2),c.stroke()
else{for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*12,hy-4,5,7,0,0,T),c.fill();c.fillStyle='#fff';for(const d of[-1,1])c.beginPath(),c.arc(128+d*12-2,hy-7,2,0,T),c.fill()}
c.lineWidth=4;c.beginPath()
if(air)c.ellipse(128,hy+12,5,6,0,0,T);else c.arc(128,hy+8,7,.15*PI,.85*PI)
c.stroke()
}
+32
View File
@@ -0,0 +1,32 @@
// squirrel sitting up nibbling an acorn in its paws, jaw working, crumbs falling, fluffy tail flicking
const{sin,cos,PI}=Math,T=2*PI
export const frames=16,delay=70
export default(c,t)=>{
const ny=3*sin(4*T*t),open=sin(4*T*t)>0,tr=.06*sin(T*t)+.03*sin(2*T*t)
c.save();c.translate(108,206);c.rotate(tr);c.translate(-108,-206)
c.beginPath();c.moveTo(112,214);c.bezierCurveTo(40,220,20,150,38,104);c.bezierCurveTo(52,60,104,30,136,50)
c.bezierCurveTo(112,56,88,74,84,104);c.bezierCurveTo(80,144,98,176,124,192);c.closePath();c.fill();c.stroke()
c.lineWidth=4;for(const[x0,y0,x1,y1,x2,y2]of[[52,118,60,96,76,80],[48,160,56,140,62,128],[94,52,108,46,118,46]])c.beginPath(),c.moveTo(x0,y0),c.quadraticCurveTo(x1,y1,x2,y2),c.stroke()
c.restore();c.lineWidth=7
c.beginPath();c.moveTo(146,84);c.quadraticCurveTo(136,50,156,34);c.quadraticCurveTo(172,54,168,80);c.fill();c.stroke()
const parts=[[134,168,38,48,.2],[118,208,34,28,0],[162,102,32,28,.25],[190,114,17,13,.3]]
c.lineWidth=14;for(const[x,y,rx,ry,r]of parts)c.beginPath(),c.ellipse(x,y,rx,ry,r,0,T),c.stroke()
for(const[x,y,rx,ry,r]of parts)c.beginPath(),c.ellipse(x,y,rx,ry,r,0,T),c.fill()
c.lineWidth=4;c.beginPath();c.moveTo(154,150);c.quadraticCurveTo(160,180,146,206);c.moveTo(96,196);c.quadraticCurveTo(112,184,132,190);c.stroke()
c.lineWidth=7;c.beginPath();c.ellipse(150,236,22,8,0,0,T);c.fill();c.stroke()
c.fillStyle='#111';c.beginPath();c.ellipse(168,94,6,8,.2,0,T);c.fill();c.beginPath();c.ellipse(205,110,5,4,0,0,T);c.fill()
c.fillStyle='#fff';c.beginPath();c.arc(166,91,2.2,0,T);c.fill()
c.lineWidth=4;c.beginPath();c.arc(176,116,8+(open?2:0),.6*PI,1.6*PI);c.stroke()
if(open){c.fillStyle='#111';c.beginPath();c.ellipse(197,124,5,4,0,0,T);c.fill();c.fillStyle='#fff';c.fillRect(194,120,6,4)}
else c.beginPath(),c.moveTo(191,123),c.lineTo(202,121),c.stroke()
for(const[w,s]of[[9,'#111'],[3,'#fff']]){c.lineWidth=w;c.strokeStyle=s;c.beginPath();c.moveTo(204,114);c.lineTo(226,108);c.moveTo(204,118);c.lineTo(224,124);c.stroke()}
c.strokeStyle='#111';c.fillStyle='#fff'
const ax=187,ay=150+ny
c.lineWidth=6;c.beginPath();c.ellipse(ax,ay+6,12,14,0,0,T);c.fill();c.stroke()
c.beginPath();c.ellipse(ax,ay-6,15,9,0,PI,T);c.closePath();c.fill();c.stroke()
c.lineWidth=3;c.save();c.beginPath();c.ellipse(ax,ay-6,15,9,0,PI,T);c.clip();for(let k=-3;k<=3;k++)c.beginPath(),c.moveTo(ax+k*7-8,ay-16),c.lineTo(ax+k*7+8,ay),c.moveTo(ax+k*7+8,ay-16),c.lineTo(ax+k*7-8,ay),c.stroke();c.restore()
c.lineWidth=5;c.beginPath();c.moveTo(ax,ay-15);c.lineTo(ax+3,ay-22);c.stroke()
for(const[w,s]of[[24,'#111'],[12,'#fff']])c.lineWidth=w,c.strokeStyle=s,c.beginPath(),c.moveTo(152,152),c.lineTo(174,158+ny),c.stroke()
c.strokeStyle='#111';c.lineWidth=5;for(const[x,y]of[[174,158],[200,156]])c.beginPath(),c.ellipse(x,y+ny,8,7,0,0,T),c.fill(),c.stroke()
c.lineWidth=3;for(const k of[0,.5]){const p=(2*t+k)%1;c.beginPath();c.arc(182+k*16+4*sin(p*7),170+p*58,3.5,0,T);c.fill();c.stroke()}
}
+26
View File
@@ -0,0 +1,26 @@
// stapler chomping like a hungry mouth with a big eye, and on each bite a staple hops out the front and lands
const{sin,cos,PI,min,max}=Math,T=2*PI
export const frames=20,delay=70
export default(c,t)=>{
const u=(t*2)%1,bite=u<.14?u/.14:u<.3?1-(u-.14)/.16:0,a=.5*(1-bite),p=(u-.16)/.5
c.lineWidth=7
c.beginPath();c.roundRect(20,198,212,32,16);c.fill();c.stroke()
c.beginPath();c.roundRect(170,186,40,16,5);c.fill();c.stroke()
c.save();c.translate(50,186);c.rotate(-a)
for(const[w,s]of[[30,'#fff'],[16,'#111']])c.lineWidth=w,c.strokeStyle=s,c.beginPath(),c.moveTo(24,14),c.lineTo(160,14),c.stroke()
c.lineWidth=7;c.strokeStyle='#111'
c.beginPath();c.moveTo(-18,-4);c.quadraticCurveTo(-18,-58,36,-60);c.lineTo(160,-50);c.quadraticCurveTo(184,-48,184,-22);c.lineTo(184,2);c.quadraticCurveTo(184,14,170,14);c.lineTo(4,14);c.quadraticCurveTo(-18,14,-18,-4);c.fill();c.stroke()
c.lineWidth=5;c.beginPath();c.moveTo(14,-34);c.lineTo(110,-30);c.stroke()
if(bite>.5){c.lineWidth=6;c.beginPath();c.moveTo(140,-32);c.lineTo(152,-24);c.lineTo(140,-16);c.stroke()}
else{c.lineWidth=6;c.beginPath();c.arc(150,-24,15,0,T);c.stroke();c.fillStyle='#111';c.beginPath();c.arc(153,-22,8,0,T);c.fill();c.fillStyle='#fff';c.beginPath();c.arc(150,-25,3,0,T);c.fill()}
c.restore()
c.lineWidth=7;c.beginPath();c.arc(46,188,15,0,T);c.fill();c.stroke()
c.fillStyle='#111';c.beginPath();c.arc(46,188,5,0,T);c.fill();c.fillStyle='#fff'
if(p>0&&p<1){
const x=220+14*p,y=176-80*4*p*(1-p)+20*p
c.save();c.translate(x,y);c.rotate(p*T)
for(const[w,k]of[[13,'#111'],[5,'#fff']])c.lineWidth=w,c.strokeStyle=k,c.beginPath(),c.moveTo(-12,8),c.lineTo(-12,-5),c.lineTo(12,-5),c.lineTo(12,8),c.stroke()
c.restore()
}
if(bite>.6)for(const[x,y]of[[196,108],[220,112],[240,126]])for(const[w,k]of[[11,'#111'],[4,'#fff']])c.lineWidth=w,c.strokeStyle=k,c.beginPath(),c.moveTo(x-6,y+6),c.lineTo(x+4,y-4),c.stroke()
}
+33
View File
@@ -0,0 +1,33 @@
// teapot tipping forward to pour a stream of tea into a cup, ripples on the cup, steam curling up
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)
const P=[148,188],rot=(x,y,a)=>[P[0]+(x-P[0])*cos(a)-(y-P[1])*sin(a),P[1]+(x-P[0])*sin(a)+(y-P[1])*cos(a)]
export default(c,t)=>{
const a=.55*(ease(t/.2)-ease((t-.7)/.2)),pour=a>.45,[sx,sy]=rot(190,108,a)
for(const[x0,ph]of[[184,0],[222,.5]])for(const[w,s]of[[13,'#111'],[6,'#fff']]){c.lineWidth=w;c.strokeStyle=s;c.beginPath();for(let y=176;y>=128;y-=2)c.lineTo(x0+(2+4*(176-y)/48)*sin((176-y)/36*T-t*T*2+ph*T),y);c.stroke()}
c.lineWidth=7;c.strokeStyle='#111'
c.beginPath();c.ellipse(204,234,46,9,0,0,T);c.fill();c.stroke()
for(const[w,s]of[[25,'#111'],[11,'#fff']])c.lineWidth=w,c.strokeStyle=s,c.beginPath(),c.arc(236,204,12,-PI/2,PI/2),c.stroke()
c.lineWidth=7;c.strokeStyle='#111'
c.beginPath();c.moveTo(170,186);c.lineTo(176,222);c.quadraticCurveTo(178,230,188,230);c.lineTo(220,230);c.quadraticCurveTo(230,230,232,222);c.lineTo(238,186);c.fill();c.stroke()
c.beginPath();c.ellipse(204,186,34,8,0,0,T);c.fill();c.stroke()
c.fillStyle='#111';c.beginPath();c.ellipse(204,187,27,5,0,0,T);c.fill();c.fillStyle='#fff'
if(pour){c.strokeStyle='#fff';c.lineWidth=2.5;const r=(t*6)%1;c.beginPath();c.ellipse(204,187,6+18*r,1.5+3*r,0,0,T);c.stroke();c.strokeStyle='#111'}
if(pour)for(const[w,s]of[[14,'#111'],[6,'#fff']])c.lineWidth=w,c.strokeStyle=s,c.beginPath(),c.moveTo(sx,sy),c.quadraticCurveTo(sx+8,sy+18+2*sin(8*T*t),204,186),c.stroke()
c.lineWidth=7;c.strokeStyle='#111'
c.save();c.translate(...P);c.rotate(a);c.translate(-P[0],-P[1])
for(const[w,s]of[[27,'#111'],[13,'#fff']])c.lineWidth=w,c.strokeStyle=s,c.beginPath(),c.arc(50,140,22,PI*.5,PI*1.5),c.stroke()
c.lineWidth=7;c.strokeStyle='#111'
c.beginPath();c.moveTo(136,140);c.quadraticCurveTo(164,138,176,112);c.lineTo(192,104);c.lineTo(186,120);c.quadraticCurveTo(170,164,136,172);c.closePath();c.fill();c.stroke()
c.beginPath();c.roundRect(72,180,56,12,5);c.fill();c.stroke()
c.beginPath();c.moveTo(50,150);c.bezierCurveTo(50,104,76,92,100,92);c.bezierCurveTo(124,92,148,104,148,150);c.quadraticCurveTo(148,186,112,186);c.lineTo(88,186);c.quadraticCurveTo(50,186,50,150);c.fill();c.stroke()
c.beginPath();c.moveTo(70,98);c.quadraticCurveTo(100,64,130,98);c.closePath();c.fill();c.stroke()
c.beginPath();c.arc(100,72,9,0,T);c.fill();c.stroke()
c.lineWidth=5
if(pour)for(const x of[84,116])c.beginPath(),c.arc(x,140,8,PI*1.15,PI*1.85),c.stroke()
else{c.fillStyle='#111';for(const x of[84,116])c.beginPath(),c.ellipse(x,138,5.5,7.5,0,0,T),c.fill();c.fillStyle='#fff';for(const x of[82,114])c.beginPath(),c.arc(x,135,2.2,0,T),c.fill()}
c.beginPath();c.arc(100,150,10,.15*PI,.85*PI);c.stroke()
c.lineWidth=3;for(const x of[66,134])for(const o of[-5,0,5])c.beginPath(),c.moveTo(x+o-2,164),c.lineTo(x+o+2,156),c.stroke()
c.restore()
}
+33
View File
@@ -0,0 +1,33 @@
// toaster pops two slices of toast that hop up and drop back in, the lever snapping up then pushed down again
const{sin,PI,min,max}=Math,T=2*PI
export const frames=20,delay=70
const ease=u=>(u=min(1,max(0,u)),u<.5?2*u*u:1-2*(1-u)**2)
const toast=(c,x,y,a)=>{
c.save();c.translate(x,y);c.rotate(a)
c.beginPath();c.moveTo(-22,60);c.lineTo(-22,6);c.bezierCurveTo(-36,-2,-28,-26,0,-24);c.bezierCurveTo(28,-26,36,-2,22,6);c.lineTo(22,60);c.closePath();c.fill();c.stroke()
c.lineWidth=4;c.beginPath();c.moveTo(-13,60);c.lineTo(-13,8);c.bezierCurveTo(-24,0,-18,-14,0,-14);c.bezierCurveTo(18,-14,24,0,13,8);c.lineTo(13,60);c.stroke()
c.restore();c.lineWidth=7
}
export default(c,t)=>{
const hop=(t0,H,s)=>{const u=(t-t0)/.42;return u>0&&u<1?[H*4*u*(1-u),s*sin(PI*u)]:[0,0]}
const[ha,ra]=hop(.3,76,-.3),[hb,rb]=hop(.35,62,.25)
const ly=196-56*ease((t-.3)/.04)+56*ease((t-.8)/.15),j=t<.3?1.5*sin(12*T*t)*sin(PI*t/.3):0,up=t>=.3&&t<.55
c.translate(128,236);c.scale(1.14,1.14);c.translate(-128,-236)
toast(c,104,122-ha,ra);toast(c,152,122-hb,rb)
if(t>=.3&&t<.42)for(const d of[-1,1])for(const[w,s]of[[11,'#111'],[4,'#fff']]){c.lineWidth=w;c.strokeStyle=s;for(const[a,r0]of[[-.5,0],[0,6],[.5,0]]){const x=128+d*(96+r0),y=82;c.beginPath();c.moveTo(x+d*12*sin(a+.9),y-12*sin(a+1.2));c.lineTo(x+d*30*sin(a+.9),y-30*sin(a+1.2));c.stroke()}}
c.lineWidth=7;c.strokeStyle='#111'
c.save();c.translate(j,0)
for(const x of[66,162])c.beginPath(),c.roundRect(x,214,28,14,5),c.fill(),c.stroke()
c.beginPath();c.roundRect(200,ly-7,26,14,6);c.fill();c.stroke()
c.beginPath();c.roundRect(48,116,160,104,24);c.fill();c.stroke()
c.lineWidth=5;c.beginPath();c.moveTo(70,134);c.lineTo(186,134);c.stroke();c.lineWidth=7
c.fillStyle='#111'
if(up)for(const x of[104,152])c.beginPath(),c.arc(x,162,10,0,T),c.fill()
else for(const x of[104,152])c.beginPath(),c.ellipse(x,164,6,8,0,0,T),c.fill()
c.fillStyle='#fff';for(const x of[101,149])c.beginPath(),c.arc(x+(up?-1:0),up?158:160,up?3.5:2.5,0,T),c.fill()
c.lineWidth=5
if(up){c.beginPath();c.ellipse(128,188,7,9,0,0,T);c.fillStyle='#111';c.fill()}
else{c.beginPath();c.arc(128,176,12,.15*PI,.85*PI);c.stroke()}
c.lineWidth=3;for(const x of[80,176])for(const o of[-6,0,6])c.beginPath(),c.moveTo(x+o-2,186),c.lineTo(x+o+3,178),c.stroke()
c.restore()
}
+15
View File
@@ -0,0 +1,15 @@
// tornado of stacked swirl bands twisting and swaying, marks sliding round each band, debris whirling and dust puffing at the base
const{sin,cos,PI}=Math,T=2*PI
export const frames=16,delay=70
export default(c,t)=>{
const N=8,B=[...Array(N)].map((_,k)=>{const f=k/(N-1);return[128+(4+30*f*f)*sin(t*T-2.6*f),40+k*24,10+92*(1-f)**1.3,16-6*f]})
const deb=[[2.5,0,0],[4.2,2.1,1],[5.6,4.2,0]].map(([k,o,ty])=>{const b=B[Math.round(k)],a=2*T*t+o,r=b[2]+20;return[b[0]+r*cos(a),40+k*24+r*.22*sin(a),sin(a),ty,a]})
const piece=([x,y,_,ty,a])=>{c.lineWidth=4;c.save();c.translate(x,y);c.rotate(a*1.5);c.beginPath();ty?c.rect(-9,-4,18,8):c.arc(0,0,5,0,T);c.fill();c.stroke();c.restore()}
for(const d of deb)if(d[2]<0)piece(d)
for(const[dx,r,o]of[[-22,10,0],[20,9,2],[0,9,4]])c.lineWidth=7,c.beginPath(),c.arc(B[N-1][0]+dx,226,r+2*sin(2*T*t+o),0,T),c.fill(),c.stroke()
B.forEach(([x,y,r,h],k)=>{
c.lineWidth=7;c.beginPath();c.ellipse(x,y,r,h,0,0,T);c.fill();c.stroke()
c.lineWidth=4;for(const j of[0,1]){const a=((2*T*t+j*PI+k*1.3)%T+T)%T;if(a>.4&&a<PI-.4)c.beginPath(),c.ellipse(x,y,r-7,h-5,0,a-.4,a+.4),c.stroke()}
})
c.lineWidth=7;for(const d of deb)if(d[2]>=0)piece(d)
}
+29
View File
@@ -0,0 +1,29 @@
// paper ball tossed in an arc into a happy trash bin, the lid flips open to catch it and the bin bounces
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 lid=.65*(ease((t-.18)/.14)-ease((t-.56)/.12)),land=t>.56&&t<.8?(t-.56)/.24:0,sq=land?.08*sin(land*PI*2)*(1-land):0
const bp=(t-.06)/.48
c.save();c.translate(158,232);c.scale(1+sq,1-sq);c.translate(-158,-232)
c.save();c.translate(228,110);c.rotate(lid)
c.beginPath();c.roundRect(-144,-12,148,18,8);c.fill();c.stroke()
c.beginPath();c.roundRect(-88,-26,32,16,7);c.fill();c.stroke()
c.restore()
c.restore()
if(bp<1){
const s=min(1,t/.06),q=min(1,max(0,bp)),x=(1-q)**2*50+2*(1-q)*q*88+q*q*158,y=(1-q)**2*184+2*(1-q)*q*-30+q*q*130
c.save();c.translate(x,y);c.rotate(q*5);c.scale(s,s)
c.beginPath();c.arc(0,0,24,0,T);c.fill();c.stroke()
c.lineWidth=5;c.beginPath();c.moveTo(-15,-7);c.lineTo(-3,-2);c.lineTo(5,-15);c.moveTo(-10,12);c.lineTo(3,5);c.lineTo(15,10);c.moveTo(12,-10);c.lineTo(7,3);c.stroke()
c.restore();c.lineWidth=7
}
c.save();c.translate(158,232);c.scale(1+sq,1-sq);c.translate(-158,-232)
c.beginPath();c.moveTo(90,118);c.lineTo(226,118);c.lineTo(212,232);c.lineTo(104,232);c.closePath();c.fill();c.stroke()
c.lineWidth=5;for(const x of[124,158,192])c.beginPath(),c.moveTo(x,136),c.lineTo(x+(x-158)*.08,168),c.stroke()
const hap=t>.56&&t<.92
c.fillStyle='#111';c.lineWidth=6
for(const d of[-1,1]){c.beginPath();if(hap)c.arc(158+d*26,198,9,1.15*PI,1.85*PI),c.stroke();else c.arc(158+d*26,196,7,0,T),c.fill()}
c.beginPath();c.arc(158,206,10,.15*PI,.85*PI);c.stroke()
c.restore();c.fillStyle='#fff'
}
+30
View File
@@ -0,0 +1,30 @@
// trumpet tooting three times, a valve pressing on each note, the bell flaring and sound waves rolling out
const{sin,PI,floor}=Math,T=2*PI
export const frames=24,delay=70
const D=(c,f,a=11,b=4)=>{for(const[l,s]of[[a,'#111'],[b,'#fff']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),f(),c.stroke();c.lineWidth=7;c.strokeStyle='#111'}
export default(c,t)=>{
const p=(3*t)%1,n=floor(3*t),toot=sin(PI*p)
for(const o of[0,.5]){
const q=(t+o)%1,s=.4*sin(PI*q),x=206+20*q+6*sin(q*T*2),y=88-66*q
if(s<.08)continue
c.save();c.translate(x,y);c.scale(s,s)
D(c,()=>{c.moveTo(18,-6);c.lineTo(18,-80);c.quadraticCurveTo(26,-56,44,-50)},16,6)
c.lineWidth=7/s;c.beginPath();c.ellipse(0,0,22,15,-.35,0,T);c.fill();c.stroke();c.restore()
}
for(const r of[14+30*p,2+30*p])if(r>12)D(c,()=>c.arc(214,128,r,-.6,.6))
c.translate(6,4)
D(c,()=>{c.moveTo(140,124);c.bezierCurveTo(160,124,160,164,140,164);c.lineTo(66,164);c.bezierCurveTo(50,164,50,140,66,140)},22,10)
D(c,()=>{c.moveTo(26,124);c.lineTo(150,124)},22,10)
c.beginPath();c.moveTo(12,112);c.quadraticCurveTo(20,124,12,136);c.lineTo(28,128);c.lineTo(28,120);c.closePath();c.fill();c.stroke()
for(let k=0;k<3;k++){
const x=80+24*k,d=k==n?8*toot:0
D(c,()=>{c.moveTo(x,100);c.lineTo(x,86+d)},10,4)
c.beginPath();c.ellipse(x,84+d,11,5,0,0,T);c.fill();c.stroke()
c.beginPath();c.roundRect(x-7,104,14,66,5);c.fill();c.stroke()
c.beginPath();c.roundRect(x-10,98,20,10,4);c.fill();c.stroke()
}
c.save();c.translate(146,124);c.scale(1+.06*toot,1+.1*toot)
c.beginPath();c.moveTo(-2,-8);c.bezierCurveTo(24,-9,38,-22,50,-34);c.lineTo(50,34);c.bezierCurveTo(38,22,24,9,-2,8);c.closePath();c.fill();c.stroke()
c.beginPath();c.ellipse(50,0,8,34,0,0,T);c.fill();c.stroke()
c.restore()
}
+24
View File
@@ -0,0 +1,24 @@
// retro tv with wobbling rabbit-ear antennas and snowy static flickering on the screen
// the static cycles through three fixed noise patterns so the file stays small
const{sin,cos,PI}=Math,T=2*PI
export const frames=12,delay=80
let s=7;const rnd=()=>(s=s*16807%2147483647)/2147483647
const noise=[0,1,2].map(()=>Array.from({length:22*17},()=>rnd()<.5))
export default(c,t,i)=>{
for(const[d,a0,L]of[[-1,-2.1,70],[1,-1.05,80]]){
const a=a0+.1*sin(t*T*2+d)
for(const[w,s2]of[[13,'#111'],[5,'#fff']])c.lineWidth=w,c.strokeStyle=s2,c.beginPath(),c.moveTo(128,86),c.lineTo(128+cos(a)*L,86+sin(a)*L),c.stroke()
c.lineWidth=6;c.strokeStyle='#111';c.beginPath();c.arc(128+cos(a)*L,86+sin(a)*L,8,0,T);c.fill();c.stroke()
}
c.lineWidth=7;c.beginPath();c.ellipse(128,90,26,12,0,PI,0);c.fill();c.stroke()
for(const x of[72,184])for(const[w,s2]of[[22,'#111'],[8,'#fff']])c.lineWidth=w,c.strokeStyle=s2,c.beginPath(),c.moveTo(x,210),c.lineTo(x+(x<128?-8:8),232),c.stroke()
c.lineWidth=7;c.strokeStyle='#111'
c.beginPath();c.roundRect(32,88,192,124,20);c.fill();c.stroke()
const scr=()=>{c.beginPath();c.roundRect(48,104,132,92,16)}
c.save();scr();c.clip();const n=noise[i%3]
c.fillStyle='#111';for(let y=0;y<17;y++)for(let x=0;x<22;x++)if(n[y*22+x])c.fillRect(48+x*6,104+y*6,6,6)
c.restore();c.fillStyle='#fff';scr();c.stroke()
for(const y of[124,156])c.beginPath(),c.arc(202,y,10,0,T),c.fill(),c.stroke()
c.lineWidth=4;for(const y of[180,190])c.beginPath(),c.moveTo(194,y),c.lineTo(210,y),c.stroke()
c.lineWidth=5;for(const[y,a]of[[124,.6+t*T],[156,-.4]])c.beginPath(),c.moveTo(202,y),c.lineTo(202+cos(a)*8,y+sin(a)*8),c.stroke()
}
+20
View File
@@ -0,0 +1,20 @@
// puffy-cheeked wind cloud blowing, its cheeks swelling as dashed gusts stream out to the right
const{sin,cos,PI}=Math,T=2*PI
export const frames=16,delay=70
export default(c,t)=>{
const pf=1+.08*(1-cos(2*T*t))/2,G=[
()=>{c.moveTo(156,112);c.bezierCurveTo(184,92,204,110,224,94);c.arc(224,108,14,-PI/2,PI*.9)},
()=>{c.moveTo(162,128);c.bezierCurveTo(190,120,210,142,240,130)},
()=>{c.moveTo(156,144);c.bezierCurveTo(180,166,196,152,214,176);c.arc(202,184,14,-.6,PI*1.3)}]
c.setLineDash([34,22]);c.lineDashOffset=-2*t*56
for(const g of G)for(const[w,s]of[[11,'#111'],[4,'#fff']])c.lineWidth=w,c.strokeStyle=s,c.beginPath(),g(),c.stroke()
c.setLineDash([]);c.strokeStyle='#111'
const P=[[62,108,36],[102,82,40],[92,148,34],[50,154,26],[132,108,30],[140,138,22*pf]]
c.lineWidth=14;for(const[x,y,r]of P)c.beginPath(),c.arc(x,y,r,0,T),c.stroke()
for(const[x,y,r]of P)c.beginPath(),c.arc(x,y,r,0,T),c.fill()
c.lineWidth=5;for(const[x,d]of[[84,1],[116,-1]])c.beginPath(),c.moveTo(x-8*d,100),c.lineTo(x+4*d,107),c.lineTo(x-8*d,114),c.stroke()
c.lineWidth=4;for(const x of[78,110])c.beginPath(),c.arc(x,96,12,1.2*PI,1.7*PI),c.stroke()
c.beginPath();c.arc(132,134,14*pf,-.2*PI,.6*PI);c.stroke()
c.lineWidth=5;c.beginPath();c.ellipse(158,126,9,11,0,0,T);c.fill();c.stroke()
c.fillStyle='#111';c.beginPath();c.ellipse(159,126,4,6,0,0,T);c.fill()
}
+18
View File
@@ -0,0 +1,18 @@
// yo-yo drops down its string spinning, hangs there a beat, then climbs back up to the finger
const{sin,cos,PI,abs}=Math,T=2*PI
export const frames=20,delay=70
const D=(c,f,a=11,b=4)=>{for(const[l,s]of[[a,'#111'],[b,'#fff']])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),f(),c.stroke();c.lineWidth=7;c.strokeStyle='#111'}
export default(c,t)=>{
const y=114+82*(1-cos(t*T))/2,a=3*T*t
D(c,()=>{c.moveTo(128,62);c.lineTo(128,y)},9,3)
if(abs(sin(t*T))>.3)for(const s of[0,PI])D(c,()=>c.arc(128,y,58,a+s,a+s+.8))
c.beginPath();c.arc(128,y,44,0,T);c.fill();c.stroke()
c.lineWidth=5;c.beginPath();c.arc(128,y,32,0,T);c.stroke()
for(let k=0;k<3;k++){const b=a+k*T/3;c.beginPath();c.moveTo(128+cos(b)*9,y+sin(b)*9);c.quadraticCurveTo(128+cos(b+.8)*24,y+sin(b+.8)*24,128+cos(b+.4)*31,y+sin(b+.4)*31);c.stroke()}
c.fillStyle='#111';c.beginPath();c.arc(128,y,8,0,T);c.fill()
c.lineWidth=7;c.fillStyle='#fff'
c.beginPath();c.roundRect(112,-30,32,98,16);c.fill();c.stroke()
c.lineWidth=4;c.beginPath();c.roundRect(119,40,18,20,7);c.stroke()
c.beginPath();for(const y of[12,19])c.moveTo(119,y),c.quadraticCurveTo(128,y+4,137,y);c.stroke()
c.lineWidth=5;c.beginPath();c.ellipse(128,58,19,6,0,0,T);c.stroke()
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

Some files were not shown because too many files have changed in this diff Show More