Add minimalist creature sections, 50 black-and-white and 50 colored

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-23 12:57:10 +00:00
co-authored by Claude Opus 5.5
parent c4dd37f62c
commit 95c6ae7fb4
302 changed files with 2655 additions and 2 deletions
+32
View File
@@ -0,0 +1,32 @@
// anglerfish drifting in the dark: its glowing lure bobs and pulses on the stalk, the jaw opens a little to show its teeth
const{sin,cos,PI,max}=Math,T=2*PI
export const frames=24,delay=80,rim=3
export default(c,t)=>{
const s=sin(T*t),k=sin(T*t-1.2),o=t>.35&&t<.85?sin(PI*(t-.35)/.5):0,pu=.5+.5*sin(T*t*2)
const lx=212+8*sin(T*t-1),ly=62+5*cos(T*t-1)
c.fillStyle=c.strokeStyle='#111';c.lineWidth=6
c.translate(128,128+6*s);c.rotate(.03*cos(T*t));c.scale(.92,.92);c.translate(-137,-124)
const shape=(x,y,r,f)=>{c.save();c.translate(x,y);c.rotate(r);c.beginPath();f();c.fill();c.stroke();c.restore()}
shape(66,140,.18*k,()=>{
c.moveTo(4,-16);c.quadraticCurveTo(-18,-30,-36,-32);c.quadraticCurveTo(-28,0,-36,32);c.quadraticCurveTo(-18,30,4,16)})
shape(0,0,0,()=>{
c.moveTo(66,124);c.bezierCurveTo(80,82,140,70,178,96);c.bezierCurveTo(196,108,204,124,204,136)
c.bezierCurveTo(206,160,190,186,150,192);c.bezierCurveTo(110,198,76,176,66,156)})
shape(106,82,-.2+.1*k,()=>{c.moveTo(-14,8);c.lineTo(-2,-16);c.lineTo(14,6)})
shape(122,188,-.5-.3*sin(T*t*2),()=>c.ellipse(-16,8,18,8,0,0,T))
c.lineWidth=4;c.beginPath();c.moveTo(156,86);c.quadraticCurveTo(192,22,lx,ly);c.stroke()
c.beginPath();c.arc(lx,ly,10,0,T);c.fill()
c.lineWidth=3;for(let i=0;i<8;i++){
const a=i*PI/4+T*t/8;c.beginPath();c.moveTo(lx+15*cos(a),ly+15*sin(a));c.lineTo(lx+(18+5*pu)*cos(a),ly+(18+5*pu)*sin(a));c.stroke()
}
c.fillStyle=c.strokeStyle='#fff'
c.beginPath();c.arc(lx,ly,3.5+1.5*pu,0,T);c.fill()
c.beginPath();c.arc(170,118,6,0,T);c.fill()
// mouth: white rim with teeth, the lower lip drops as the jaw opens
const q=(a,b,e,u)=>[(1-u)**2*a[0]+2*u*(1-u)*b[0]+u*u*e[0],(1-u)**2*a[1]+2*u*(1-u)*b[1]+u*u*e[1]]
const M=[150,150],U=[[178,148],[198,136]],L=[[176,156+12*o],[197,140+10*o]]
c.lineWidth=3.5;c.beginPath();c.moveTo(...M);c.quadraticCurveTo(...U[0],...U[1]);c.lineTo(...L[1]);c.quadraticCurveTo(...L[0],...M);c.closePath();c.stroke()
for(const[E,u,d]of[[U,.35,1],[U,.6,1],[U,.84,1],[L,.48,-1],[L,.74,-1]]){
const[x,y]=q(M,...E,u);c.beginPath();c.moveTo(x-3.5,y);c.lineTo(x+3.5,y);c.lineTo(x,y+7*d);c.fill()
}
}
+27
View File
@@ -0,0 +1,27 @@
// ant carrying a leaf twice its size: six legs walk in a tripod gait, the leaf sways overhead, antennae bob
const{sin,cos,PI,max,abs}=Math,T=2*PI
export const frames=24,delay=70,rim=3
export default(c,t)=>{
const p=T*t*2,b=2*abs(sin(p)),sw=.05*sin(p-1)
c.fillStyle=c.strokeStyle='#111';c.lineWidth=5
const shape=f=>{c.beginPath();f();c.fill();c.stroke()}
for(const[k,hx,fo]of[[0,142,26],[1,130,2],[0,118,-24]]){
const q=p+k*PI,fx=hx+fo+9*sin(q),fy=212-6*max(0,cos(q))
c.beginPath();c.moveTo(hx,174);c.lineTo((hx+fx)/2+fo*.35,188-b);c.lineTo(fx,fy);c.stroke()
}
c.translate(0,-b);c.lineWidth=6
c.save();c.translate(74,170);c.rotate(-.2);shape(()=>c.ellipse(0,0,32,23,0,0,T));c.restore()
shape(()=>c.arc(106,172,7,0,T))
c.save();c.translate(130,168);c.rotate(-.15);shape(()=>c.ellipse(0,0,21,11,0,0,T));c.restore()
shape(()=>c.arc(162,156,16,0,T))
c.lineWidth=4;for(const o of[0,6]){c.beginPath();c.moveTo(168+o,146);c.lineTo(184+o,130+2*sin(p+o));c.lineTo(202+o,136+3*sin(p+o));c.stroke()}
c.lineWidth=5;c.beginPath();c.moveTo(174,166);c.quadraticCurveTo(184,170,178,178);c.stroke()
c.save();c.translate(174,164);c.rotate(sw)
c.lineWidth=6;c.beginPath();c.moveTo(0,0);c.lineTo(-24,-52);c.stroke()
c.translate(-24,-52);c.rotate(-2.6)
shape(()=>{c.moveTo(0,0);c.quadraticCurveTo(64,-30,130,0);c.quadraticCurveTo(64,30,0,0)})
c.strokeStyle='#fff';c.lineWidth=3;c.beginPath();c.moveTo(8,0);c.lineTo(118,0)
for(const x of[34,62,90])c.moveTo(x,0),c.lineTo(x+14,-11),c.moveTo(x,0),c.lineTo(x+14,11)
c.stroke();c.restore()
c.fillStyle='#fff';c.beginPath();c.arc(168,152,3.5,0,T);c.fill()
}
+25
View File
@@ -0,0 +1,25 @@
// giant anteater: the thin tongue flicks out of its long snout three times, the huge bushy tail sways, it blinks
const{sin,PI,max}=Math,T=2*PI
export const frames=24,delay=75,rim=3
export default(c,t)=>{
const L=36*max(0,sin(3*T*t)),nod=.04*sin(3*T*t),sw=.08*sin(T*t),bl=t>.9&&t<.96
c.fillStyle=c.strokeStyle='#111';c.lineWidth=6
const shape=(x,y,r,f)=>{c.save();c.translate(x,y);c.rotate(r);c.beginPath();f();c.fill();c.stroke();c.restore()}
c.translate(128,170);c.scale(.88,.88);c.translate(-122,-170)
shape(78,122,sw,()=>{c.moveTo(6,-16);c.bezierCurveTo(-16,-42,-54,-48,-72,-26);c.quadraticCurveTo(-76,-6,-60,4);c.quadraticCurveTo(-54,16,-40,14);c.quadraticCurveTo(-30,24,-18,16);c.quadraticCurveTo(-6,20,6,14)})
c.lineWidth=14
for(const[x0,x1]of[[82,80],[98,100],[136,132],[152,156]])c.beginPath(),c.moveTo(x0,140),c.lineTo(x1,204),c.stroke()
c.lineWidth=6
for(const x of[80,100,132,156])shape(x+4,206,0,()=>c.ellipse(0,0,10,4,0,0,T))
shape(112,128,0,()=>c.ellipse(0,0,52,30,0,0,T))
shape(138,118,0,()=>c.ellipse(0,0,34,28,0,0,T))
c.save();c.translate(160,114);c.rotate(nod);c.translate(-160,-114)
if(L>1)c.lineWidth=2.5,c.beginPath(),c.moveTo(224,144),c.quadraticCurveTo(226+L*.4,148+L*.5+3*sin(T*t*9),226+L*.6,146+L*.9),c.stroke(),c.lineWidth=6
shape(162,104,0,()=>c.ellipse(0,0,6,5,0,0,T))
shape(170,118,0,()=>{c.ellipse(0,0,16,14,0,0,T);c.moveTo(4,-12);c.quadraticCurveTo(30,-4,58,22);c.quadraticCurveTo(52,28,44,24);c.quadraticCurveTo(26,16,0,12)})
c.fillStyle=c.strokeStyle='#fff'
if(bl)c.lineWidth=2.5,c.beginPath(),c.arc(176,112,3,.15*PI,.85*PI),c.stroke()
else c.beginPath(),c.arc(176,114,2.8,0,T),c.fill()
c.restore();c.strokeStyle='#fff';c.lineWidth=4
c.beginPath();c.moveTo(152,104);c.quadraticCurveTo(140,124,118,138);c.stroke()
}
+31
View File
@@ -0,0 +1,31 @@
// armadillo trots in place, curls up into a banded ball, rocks a little, then unrolls and trots on
const{sin,cos,PI}=Math,T=2*PI
export const frames=32,delay=70,rim=3
const ease=x=>x<0?0:x>1?1:x*x*(3-2*x)
export default(c,t)=>{
const k=t<.5?ease((t-.3)/.15):1-ease((t-.75)/.15),rock=t>.45&&t<.75?.5*sin(PI*(t-.45)/.3):0
const span=PI*(1.15+.85*k),R=58-14*k,gy=210,leg=18*(1-k),cx=128+R*rock,cy=gy-leg+R*cos(span/2)
const a0=-PI/2-span/2+rock,a1=-PI/2+span/2+rock,w=sin(T*t*4)*(1-k),bl=t>.12&&t<.17
c.fillStyle=c.strokeStyle='#111';c.lineWidth=6
const shape=(x,y,r,s,f)=>{c.save();c.translate(x,y);c.rotate(r);c.scale(s,s);c.beginPath();f();c.fill();c.stroke();c.restore()}
if(leg>1){c.lineWidth=13;for(const[x,ph]of[[-.55,0],[-.35,PI],[.35,PI],[.55,0]]){
const lx=cx+R*x,a=.35*sin(T*t*4+ph)*(1-k)
c.beginPath();c.moveTo(lx,gy-leg-6);c.lineTo(lx-(leg+6)*sin(a),gy-leg-6+(leg+6)*cos(a)-6);c.stroke()}c.lineWidth=6}
const s=1-k
if(s>.02){
shape(cx+R*cos(a0),cy+R*sin(a0),a0-.25,s,()=>{c.moveTo(-2,-9);c.quadraticCurveTo(22,-4,44,2);c.quadraticCurveTo(22,5,-2,9)})
c.save();c.translate(cx+R*cos(a1)-2,cy+R*sin(a1)-4+1.5*w);c.rotate(a1+.15);c.scale(s,s)
c.beginPath();c.ellipse(8,0,17,13,0,0,T);c.moveTo(14,-9);c.lineTo(40,2);c.lineTo(14,9);c.fill();c.stroke()
c.beginPath();c.ellipse(2,-13,5,8,-.5,0,T);c.fill();c.stroke()
c.fillStyle=c.strokeStyle='#fff'
if(bl)c.lineWidth=2.5,c.beginPath(),c.arc(12,-4,3,.15*PI,.85*PI),c.stroke()
else c.beginPath(),c.arc(12,-3,2.8,0,T),c.fill()
c.restore();c.fillStyle=c.strokeStyle='#111';c.lineWidth=6
}
c.beginPath();c.arc(cx,cy,R,a0,a1);c.closePath();c.fill();c.stroke()
// bands: parallel white stripes kept inside a shrunken copy of the shell, they turn with the ball when it rocks
c.save();c.beginPath();c.arc(cx,cy,R-9,a0,a1);c.closePath();c.clip()
c.translate(cx,cy);c.rotate(rock);c.strokeStyle='#fff';c.lineWidth=3.5
for(let j=-2;j<=2;j++)c.beginPath(),c.moveTo(j*R*.28,-R),c.lineTo(j*R*.28,R),c.stroke()
c.restore()
}
+27
View File
@@ -0,0 +1,27 @@
// camel walking in place: two humps, long legs stepping in diagonal pairs, the head bobs on its curved neck, tail swishes
const{sin,cos,PI,max}=Math,T=2*PI
export const frames=24,delay=80,rim=3
export default(c,t)=>{
const b=2.5*sin(2*T*t),hb=3*sin(2*T*t-1),bl=t>.55&&t<.61
c.fillStyle=c.strokeStyle='#111';c.lineWidth=6
const shape=(x,y,r,f)=>{c.save();c.translate(x,y);c.rotate(r);c.beginPath();f();c.fill();c.stroke();c.restore()}
c.translate(-12,-4)
for(const[x,ph]of[[84,0],[100,PI],[150,PI],[166,0]]){
const a=.3*sin(T*t+ph),lift=max(0,-cos(T*t+ph)),y=136+b
const kx=x-36*sin(a),ky=y+36*cos(a),a2=a+.5*lift,fx=kx-38*sin(a2),fy=ky+38*cos(a2)
c.lineWidth=9;c.beginPath();c.moveTo(x,y-10);c.lineTo(kx,ky);c.lineTo(fx,fy);c.stroke()
shape(fx+3,fy,0,()=>c.ellipse(0,0,6,3,0,0,T))
}
c.lineWidth=5;c.beginPath();c.moveTo(68,112+b);c.quadraticCurveTo(58,126+b,60+4*sin(T*t),142+b);c.stroke()
shape(60+4*sin(T*t),146+b,0,()=>c.ellipse(0,0,4,7,0,0,T))
shape(124,118+b,0,()=>c.ellipse(0,0,58,28,0,0,T))
shape(100,96+b,0,()=>c.ellipse(0,0,24,24,0,0,T))
shape(148,96+b,0,()=>c.ellipse(0,0,24,24,0,0,T))
c.lineWidth=18;c.beginPath();c.moveTo(166,108+b);c.bezierCurveTo(196,118+b,206,104+hb,206,80+hb);c.stroke();c.lineWidth=6
shape(198,58+hb,-.6,()=>c.ellipse(0,0,4,7,0,0,T))
shape(206,68+hb,0,()=>c.ellipse(0,0,13,11,0,0,T))
shape(218,74+hb,.3,()=>c.ellipse(0,0,18,10,0,0,T))
c.fillStyle=c.strokeStyle='#fff'
if(bl)c.lineWidth=3,c.beginPath(),c.arc(208,65+hb,3.5,.15*PI,.85*PI),c.stroke()
else c.beginPath(),c.arc(208,67+hb,3.2,0,T),c.fill()
}
+24
View File
@@ -0,0 +1,24 @@
// cat with bat wings hovering in place: two slow flaps per loop, the body bobs with each beat, tail swishes, slit eyes blink, one tiny fang
const{sin,PI}=Math,T=2*PI
export const frames=24,delay=70,rim=3
export default(c,t)=>{
const a=.5*sin(2*T*t),bl=t>.76&&t<.84,s=sin(T*t)
c.fillStyle=c.strokeStyle='#111';c.lineWidth=6
c.translate(0,6*sin(2*T*t)-4)
const shape=g=>{c.beginPath();g();c.fill();c.stroke()}
for(const d of[-1,1]){
c.save();c.translate(128+d*16,134);c.scale(d*.92,.92);c.rotate(-a)
shape(()=>{c.moveTo(0,-10);c.quadraticCurveTo(40,-48,88,-40);c.quadraticCurveTo(72,-16,78,6);c.quadraticCurveTo(60,-4,52,18);c.quadraticCurveTo(36,4,26,22);c.quadraticCurveTo(14,8,0,14);c.closePath()})
c.restore()
}
c.lineWidth=7;c.beginPath();c.moveTo(142,162);c.bezierCurveTo(160,198,184+10*s,204,186+12*s,186);c.stroke();c.lineWidth=6
shape(()=>c.ellipse(128,150,22,26,0,0,T))
for(const d of[-1,1])shape(()=>c.arc(128+d*10,176,6,0,T))
shape(()=>c.arc(128,104,36,0,T))
for(const d of[-1,1])shape(()=>{c.moveTo(128+d*32,92);c.lineTo(128+d*32,56);c.lineTo(128+d*6,72);c.closePath()})
c.fillStyle=c.strokeStyle='#fff'
for(const d of[-1,1])if(bl)c.lineWidth=3.5,c.beginPath(),c.arc(128+d*14,100,7,.15*PI,.85*PI),c.stroke()
else c.beginPath(),c.ellipse(128+d*14,102,8,9,0,0,T),c.fill(),c.fillStyle='#111',c.beginPath(),c.ellipse(128+d*14,102,2.2,6.5,0,0,T),c.fill(),c.fillStyle='#fff'
c.lineWidth=2.5;for(const d of[-1,1])c.beginPath(),c.arc(128+d*4,114,4,0,PI),c.stroke()
c.beginPath();c.moveTo(131,117);c.lineTo(137,117);c.lineTo(134,124);c.fill()
}
+31
View File
@@ -0,0 +1,31 @@
// cat-whale drifting along: one solid ink shape, it bobs, flicks its tail, twitches an ear, puffs a spout and blinks
const{sin,cos,PI,min,max}=Math,T=2*PI
export const frames=24,delay=85,rim=3
export default(c,t)=>{
const s=sin(T*t),k=sin(T*t-1.4),p=t/.45,tw=t>.6&&t<.68?.3:0,bl=t>.82&&t<.9,tx=46+4*k,ty=92-5*k
c.fillStyle=c.strokeStyle='#111';c.lineWidth=6
c.translate(128,132+7*s);c.rotate(.04*cos(T*t));c.scale(.9,.9);c.translate(-126,-128)
const shape=(x,y,r,f)=>{c.save();c.translate(x,y);c.rotate(r);c.beginPath();f();c.fill();c.stroke();c.restore()}
shape(0,0,0,()=>{
c.moveTo(150,86);c.bezierCurveTo(196,84,226,112,224,146);c.bezierCurveTo(222,182,190,198,148,198)
c.bezierCurveTo(98,198,52,178,44,134);c.quadraticCurveTo(tx-10,114,tx-7,ty);c.lineTo(tx+7,ty)
c.quadraticCurveTo(tx+8,118,72,122);c.bezierCurveTo(84,98,112,86,150,86)})
shape(tx,ty,.2*k-.12,()=>{
c.moveTo(0,6);c.quadraticCurveTo(-16,0,-36,-22);c.quadraticCurveTo(-16,-22,0,-8)
c.quadraticCurveTo(12,-20,26,-20);c.quadraticCurveTo(12,0,0,6)})
shape(152,86,0,()=>{c.moveTo(-14,6);c.lineTo(-6,-30);c.lineTo(14,0)})
shape(190,90,tw,()=>{c.moveTo(-14,-4);c.lineTo(8,-34);c.lineTo(14,8)})
shape(150,190,-.6-.35*sin(T*t-2),()=>c.ellipse(-20,0,24,9,0,0,T))
c.lineWidth=3;for(const[y,d]of[[146,-8],[156,4]])c.beginPath(),c.moveTo(214,y),c.quadraticCurveTo(230,y+d/2,244,y+d),c.stroke()
if(p<1){
// palm-tree spout: grows out of the blowhole, opens, then sinks back
const g=sin(PI*p),x=110,y=94,h=46*g,w=15*min(1,g*1.5)
c.lineWidth=8*g+2;c.beginPath();c.moveTo(x,y);c.lineTo(x,y-h);c.stroke()
for(const d of[-1,1])c.beginPath(),c.moveTo(x,y-h),c.quadraticCurveTo(x+d*w*.8,y-h-w*.5,x+d*w,y-h+w*.5),c.stroke(),
c.beginPath(),c.arc(x+d*w,y-h+w*.5+5,5*g,0,T),c.fill()
}
c.fillStyle=c.strokeStyle='#fff'
if(bl)c.lineWidth=3.5,c.beginPath(),c.arc(190,128,6,.15*PI,.85*PI),c.stroke()
else c.beginPath(),c.arc(190,130,6.5,0,T),c.fill()
c.lineWidth=3;c.beginPath();c.moveTo(214,160);c.quadraticCurveTo(206,167,196,165);c.stroke()
}
+24
View File
@@ -0,0 +1,24 @@
// sheep whose body is a little rain cloud: it floats and bobs, the puffs breathe, tiny legs dangle, raindrops fall from underneath
const{sin,PI,min}=Math,T=2*PI
export const frames=24,delay=80,rim=3
const P=[[78,112,28],[106,90,32],[142,88,32],[170,108,26],[96,130,28],[132,132,30],[160,128,24],[62,130,20]]
export default(c,t)=>{
const y=5*sin(T*t),bl=t>.5&&t<.56
c.fillStyle=c.strokeStyle='#111';c.lineWidth=6
for(const[x,y0,o]of[[70,146,0],[70,146,.5],[128,158,.25],[128,158,.75],[184,128,.4],[184,128,.9]]){
const u=(t+o)%1,z=min(1,(1-u)*4),yy=y0+(232-y0)*u
c.save();c.translate(x,yy);c.scale(z,z);c.beginPath();c.moveTo(0,-9);c.bezierCurveTo(6,-2,6,6,0,6);c.bezierCurveTo(-6,6,-6,-2,0,-9);c.fill();c.restore()
}
c.translate(0,y)
c.lineWidth=8;for(const[i,x]of[92,108,148,164].entries())c.beginPath(),c.moveTo(x,150),c.lineTo(x+3*sin(T*t+i*1.7),176),c.stroke()
c.lineWidth=6
P.forEach(([x,y,r],i)=>{c.beginPath();c.arc(x,y,r+1.5*sin(T*t+i*1.3),0,T);c.fill()})
c.beginPath();c.ellipse(216,100,13,5.5,-.35,0,T);c.fill();c.stroke()
c.save();c.translate(204,124);c.rotate(-.6+.06*sin(T*t))
c.beginPath();c.ellipse(0,0,15,23,0,0,T);c.fill();c.stroke()
c.beginPath();c.arc(-2,-18,12,0,T);c.fill()
c.fillStyle=c.strokeStyle='#fff'
if(bl)c.lineWidth=3,c.beginPath(),c.arc(5,-3,4.5,.15*PI,.85*PI),c.stroke()
else c.beginPath(),c.arc(5,-1,4,0,T),c.fill()
c.restore()
}
+31
View File
@@ -0,0 +1,31 @@
// lion whose mane is dandelion fluff: three seeds blow off and drift away, new ones grow back in their place
const{sin,cos,PI,min,max}=Math,T=2*PI
export const frames=24,delay=85,rim=3
const N=18,F=[1,2,3]
const seed=c=>{c.beginPath();c.moveTo(0,0);c.lineTo(24,0);for(let j=-2;j<=2;j++)c.moveTo(24,0),c.lineTo(24+10*cos(j*.42),10*sin(j*.42));c.stroke()}
export default(c,t)=>{
const bl=t>.86&&t<.92
c.fillStyle=c.strokeStyle='#111';c.lineWidth=6
c.translate(128,128);c.scale(1.1,1.1);c.translate(-128,-128)
const shape=g=>{c.beginPath();g();c.fill();c.stroke()}
c.lineWidth=7;c.beginPath();c.moveTo(150,204);c.quadraticCurveTo(186,210,188+6*sin(T*t),178);c.stroke();c.lineWidth=6
shape(()=>c.ellipse(188+6*sin(T*t),172,7,11,.2,0,T))
shape(()=>c.ellipse(128,182,34,36,0,0,T))
for(const x of[112,144])shape(()=>c.ellipse(x,218,13,8,0,0,T))
c.lineWidth=2.5
for(let i=0;i<N;i++){
const a=-PI/2+i*T/N,j=F.indexOf(i)
if(j<0){c.save();c.translate(128+42*cos(a),100+42*sin(a));c.rotate(a);seed(c);c.restore();continue}
const t0=.15+j*.1,q=min(1,max(0,(t-t0)/.45)),g=min(1,max(0,(t-.7-j*.07)/.15))
if(q<1){const z=min(1,(1-q)*2.5);c.save();c.translate(128+42*cos(a)+40*q+4*sin(T*q*1.5),100+42*sin(a)-26*q);c.rotate(a+q*.8);c.scale(z,z);seed(c);c.restore()}
if(t>=t0+.45&&g>0){c.save();c.translate(128+42*cos(a),100+42*sin(a));c.rotate(a);c.scale(g,g);seed(c);c.restore()}
}
c.lineWidth=6
for(const d of[-1,1])shape(()=>c.arc(128+d*30,70,11,0,T))
shape(()=>c.arc(128,100,40,0,T))
c.fillStyle=c.strokeStyle='#fff'
for(const d of[-1,1])if(bl)c.lineWidth=3.5,c.beginPath(),c.arc(128+d*15,94,5,.15*PI,.85*PI),c.stroke()
else c.beginPath(),c.arc(128+d*15,96,5,0,T),c.fill()
c.beginPath();c.moveTo(121,108);c.lineTo(135,108);c.lineTo(128,116);c.closePath();c.fill()
c.lineWidth=3;c.beginPath();c.moveTo(128,116);c.lineTo(128,120);c.quadraticCurveTo(124,125,119,123);c.moveTo(128,120);c.quadraticCurveTo(132,125,137,123);c.stroke()
}
+20
View File
@@ -0,0 +1,20 @@
// dragonfly hovering, seen from above: four wings buzz between two poses every frame, a long banded tail, a slow figure-eight drift
const{sin,PI}=Math,T=2*PI
export const frames=24,delay=70,rim=3
export default(c,t,i)=>{
const z=i%2
c.fillStyle=c.strokeStyle='#111';c.lineWidth=6
c.translate(128+5*sin(T*t),128+5*sin(T*t*2));c.rotate(.05*sin(T*t));c.translate(-128,-128)
for(const[x,a]of[[166,.36],[144,-.42]])for(const d of[-1,1]){
const l=z?66:80
c.save();c.translate(x,128);c.scale(1,d);c.rotate(a+(z?.06:-.06))
c.beginPath();c.ellipse(0,-l/2-4,11,l/2,0,0,T);c.fill();c.stroke()
c.strokeStyle='#fff';c.lineWidth=2.5;c.beginPath();c.moveTo(0,-12);c.lineTo(0,-l+6);c.stroke()
c.restore()
}
c.beginPath();c.moveTo(148,121);c.lineTo(40,125);c.lineTo(40,131);c.lineTo(148,135);c.closePath();c.fill();c.stroke()
c.beginPath();c.ellipse(158,128,20,13,0,0,T);c.fill();c.stroke()
for(const d of[-1,1])c.beginPath(),c.arc(184,128+d*10,11,0,T),c.fill(),c.stroke()
c.strokeStyle='#fff';c.lineWidth=2.5;for(const x of[66,88,110,130])c.beginPath(),c.moveTo(x,124),c.lineTo(x,132),c.stroke()
c.fillStyle='#fff';for(const d of[-1,1])c.beginPath(),c.arc(188,128+d*12,3.5,0,T),c.fill()
}
+20
View File
@@ -0,0 +1,20 @@
// dust bunny hopping: a fuzzy ball on tiny stick legs, big eyes that look up as it jumps, little dust puffs when it lands
const{sin,cos,PI}=Math,T=2*PI
export const frames=24,delay=70,rim=3
export default(c,t)=>{
const air=t<.5,e=air?sin(PI*t/.5):0,h=36*e,v=air?cos(PI*t/.5):0,sq=t>.5&&t<.68?.14*sin(PI*(t-.5)/.18):0
const sx=1+sq,sy=1-sq+.04*e*e,R=52,LL=14*(1-sq),cx=128,cy=220-5-LL-R*sy-h,bl=t>.8&&t<.86
c.fillStyle=c.strokeStyle='#111';c.lineWidth=5
for(const d of[-1,1]){
const fx=cx+d*(18+4*e),fy=cy+R*sy+LL
c.beginPath();c.moveTo(cx+d*18,cy+R*sy-6);c.lineTo(fx,fy);c.stroke()
c.beginPath();c.ellipse(fx+d*4,fy+1,8,4.5,0,0,T);c.fill()
}
if(t>.5&&t<.8){const q=(t-.5)/.3;for(const d of[-1,1])for(const[a,b,r]of[[70,34,9],[56,26,6]])c.beginPath(),c.arc(cx+d*(a+b*q),214-8*q-(r<9?6:0),r*(1-q),0,T),c.fill()}
c.lineWidth=4;c.beginPath()
for(let k=0;k<72;k++){const a=k*PI/36,rr=k%2?R+13+5*sin(k*2.39):R;c.lineTo(cx+rr*cos(a)*sx,cy+rr*sin(a)*sy)}
c.closePath();c.fill();c.stroke()
c.fillStyle=c.strokeStyle='#fff'
for(const d of[-1,1])if(bl)c.lineWidth=3.5,c.beginPath(),c.arc(cx+d*21,cy-10*sy,9,.15*PI,.85*PI),c.stroke()
else{c.beginPath();c.arc(cx+d*21,cy-8*sy,12,0,T);c.fill();c.fillStyle='#111';c.beginPath();c.arc(cx+d*19,cy-8*sy-3*v*e+1,5.5,0,T);c.fill();c.fillStyle='#fff'}
}
+25
View File
@@ -0,0 +1,25 @@
// fawn standing still: big ears with one flicking, the short tail flicks twice, white spots on its back, it blinks
const{sin,PI}=Math,T=2*PI
export const frames=24,delay=80,rim=3
export default(c,t)=>{
const br=sin(T*t),ef=t>.2&&t<.32?.6*sin(PI*(t-.2)/.12):0,tf=t>.55&&t<.8?.5*sin(T*(t-.55)/.125):0,hd=.06*sin(T*t),bl=t>.86&&t<.92
c.fillStyle=c.strokeStyle='#111';c.lineWidth=6
const shape=(x,y,r,f)=>{c.save();c.translate(x,y);c.rotate(r);c.beginPath();f();c.fill();c.stroke();c.restore()}
c.translate(-6,0);c.lineWidth=7
for(const l of[[86,136,78,176,84,212],[100,138,100,176,102,212],[146,138,146,176,144,212],[160,136,162,176,166,212]])
c.beginPath(),c.moveTo(l[0],l[1]),c.lineTo(l[2],l[3]),c.lineTo(l[4],l[5]),c.stroke()
c.lineWidth=6
shape(76,114,-.3-tf,()=>c.ellipse(-4,-8,6,11,-.4,0,T))
shape(122,126,0,()=>c.ellipse(0,br,50,24+br*.5,0,0,T))
c.lineWidth=20;c.beginPath();c.moveTo(156,118);c.lineTo(172,82);c.stroke();c.lineWidth=6
c.save();c.translate(172,84);c.rotate(hd);c.translate(-172,-84)
shape(166,64,-1,()=>c.ellipse(0,-13,6.5,15,0,0,T))
shape(179,60,.15-ef,()=>c.ellipse(0,-13,6.5,15,0,0,T))
shape(178,72,0,()=>c.ellipse(0,0,16,14,0,0,T))
shape(194,80,.3,()=>c.ellipse(0,0,12,8,0,0,T))
c.fillStyle=c.strokeStyle='#fff'
if(bl)c.lineWidth=3,c.beginPath(),c.arc(182,68,4,.15*PI,.85*PI),c.stroke()
else c.beginPath(),c.arc(182,70,3.6,0,T),c.fill()
c.restore();c.fillStyle='#fff'
for(const[x,y,r]of[[96,116,3.4],[112,112,3.6],[128,111,3.4],[144,114,3.2],[104,127,3],[122,124,3.2],[138,126,3]])c.beginPath(),c.arc(x,y+br,r,0,T),c.fill()
}
+25
View File
@@ -0,0 +1,25 @@
// firefly drifting: wings flutter every frame, the bulb in its tail lights up white with pulsing rays, then goes dark again
const{sin,cos,PI,max}=Math,T=2*PI
export const frames=24,delay=80,rim=3
const W=[[-.45,-.95],[-.2,-1.25]]
export default(c,t,i)=>{
const g=t<.6?sin(PI*t/.6):0
c.fillStyle=c.strokeStyle='#111';c.lineWidth=6
const shape=(x,y,r,f)=>{c.save();c.translate(x,y);c.rotate(r);c.beginPath();f();c.fill();c.stroke();c.restore()}
c.translate(128+4*sin(T*t*2),128+8*sin(T*t));c.rotate(.05*cos(T*t));c.translate(-128,-128)
for(const a of W[i%2])shape(148,112,a,()=>c.ellipse(0,-34,12,34,0,0,T))
shape(106,142,.35,()=>c.ellipse(0,0,46,23,0,0,T))
shape(148,124,0,()=>c.ellipse(0,0,18,15,0,0,T))
shape(170,114,0,()=>c.arc(0,0,13,0,T))
c.lineWidth=3;for(const[x,y]of[[200,84],[190,78]])c.beginPath(),c.moveTo(174,104),c.quadraticCurveTo(x-6,y+14,x,y),c.stroke()
if(g>.15){
c.lineWidth=5;for(const a of[.6,.8,1,1.2]){
const r=36,R=r+6+14*g,x=cos(a*PI),y=sin(a*PI)
c.beginPath();c.moveTo(86+x*r,150+y*r);c.lineTo(86+x*R,150+y*R);c.stroke()
}
}
c.fillStyle=c.strokeStyle='#fff';c.lineWidth=2.5;c.save();c.translate(86,150);c.rotate(.35);c.beginPath()
if(g>.15)c.ellipse(0,0,10+4*g,8+3*g,0,0,T),c.fill();else c.ellipse(0,0,10,8,0,0,T),c.stroke()
c.restore()
c.fillStyle='#fff';c.beginPath();c.arc(174,110,3.5,0,T);c.fill()
}
+23
View File
@@ -0,0 +1,23 @@
// fish with bird wings flying in a gentle arc: beats its wings twice per loop, tilts into the climb and dive, tail swishes
const{sin,cos,PI}=Math,T=2*PI
export const frames=24,delay=70,rim=3
export default(c,t)=>{
const w=cos(2*T*t),bl=t>.6&&t<.66
c.fillStyle=c.strokeStyle='#111';c.lineWidth=6
c.translate(128,136+14*sin(T*t));c.rotate(.2*cos(T*t));c.scale(1.1,1.1);c.translate(-122,-130)
const shape=g=>{c.beginPath();g();c.fill();c.stroke()}
const wing=(x,y,k,s)=>{c.save();c.translate(x,y);c.scale(k,s*k)
shape(()=>{c.moveTo(10,0);c.quadraticCurveTo(22,-50,-36,-80);c.quadraticCurveTo(-46,-60,-40,-52);c.quadraticCurveTo(-50,-40,-40,-32);c.quadraticCurveTo(-46,-20,-34,-14);c.quadraticCurveTo(-34,-4,-16,2);c.closePath()})
c.restore()}
wing(140,104,.75,cos(2*T*t-.5))
c.save();c.translate(76,130);c.rotate(.25*sin(2*T*t))
shape(()=>{c.moveTo(4,0);c.lineTo(-34,-26);c.quadraticCurveTo(-24,0,-34,26);c.closePath()});c.restore()
shape(()=>{c.moveTo(200,128);c.bezierCurveTo(186,98,110,98,72,126);c.lineTo(72,134);c.bezierCurveTo(110,160,186,158,200,128)})
shape(()=>{c.moveTo(146,148);c.lineTo(132,166);c.lineTo(158,150);c.closePath()})
wing(126,106,1,w)
c.fillStyle=c.strokeStyle='#fff';c.lineWidth=3
c.beginPath();c.arc(150,128,20,-.9,.9);c.stroke()
c.beginPath();c.moveTo(198,132);c.lineTo(188,134);c.stroke()
if(bl)c.beginPath(),c.arc(178,118,5,.15*PI,.85*PI),c.stroke()
else c.beginPath(),c.arc(178,120,5.5,0,T),c.fill()
}
+33
View File
@@ -0,0 +1,33 @@
// fox-moth hovering: fox face with a white cheek mask, big moth wings with eyespots beating slowly, feathery antennae
const{sin,cos,PI}=Math,T=2*PI
export const frames=24,delay=80,rim=3
export default(c,t)=>{
const f=.5+.5*cos(T*t),bl=t>.7&&t<.78
c.fillStyle=c.strokeStyle='#111';c.lineWidth=6
c.translate(0,7*sin(T*t)+4)
const shape=g=>{c.beginPath();g();c.fill();c.stroke()}
for(const d of[-1,1]){
c.save();c.translate(128+d*8,136);c.scale(d*(.45+.55*f),1)
shape(()=>{c.moveTo(0,-8);c.bezierCurveTo(28,-76,98,-86,94,-32);c.bezierCurveTo(92,0,52,14,0,8)})
shape(()=>{c.moveTo(0,6);c.bezierCurveTo(42,8,76,42,60,72);c.bezierCurveTo(46,94,10,64,0,22)})
c.fillStyle=c.strokeStyle='#fff';c.lineWidth=4
c.beginPath();c.arc(60,-36,11,0,T);c.stroke()
c.beginPath();c.arc(40,48,5,0,T);c.fill()
c.restore()
}
c.save();c.translate(128,160);c.rotate(.14*sin(T*t-1))
shape(()=>{c.moveTo(-9,0);c.bezierCurveTo(-30,26,-18,64,4,68);c.bezierCurveTo(24,66,22,30,9,0);c.closePath()});c.restore()
shape(()=>c.ellipse(128,148,17,28,0,0,T))
shape(()=>{c.moveTo(88,102);c.lineTo(94,58);c.lineTo(116,84);c.lineTo(140,84);c.lineTo(162,58);c.lineTo(168,102);c.quadraticCurveTo(160,122,128,142);c.quadraticCurveTo(96,122,88,102);c.closePath()})
c.lineWidth=3
for(const d of[-1,1]){
c.save();c.translate(128+d*6,88);c.rotate(d*.06*sin(2*T*t))
c.beginPath();c.moveTo(0,0);c.quadraticCurveTo(-d*2,-34,d*12,-58);c.stroke()
c.beginPath();c.ellipse(d*13,-56,4.5,12,d*.3,0,T);c.fill();c.restore()
}
c.fillStyle=c.strokeStyle='#fff'
c.beginPath();c.moveTo(96,110);c.quadraticCurveTo(114,108,128,120);c.quadraticCurveTo(142,108,160,110);c.quadraticCurveTo(152,126,128,136);c.quadraticCurveTo(104,126,96,110);c.fill()
c.fillStyle='#111';c.beginPath();c.ellipse(128,130,6,4.5,0,0,T);c.fill();c.fillStyle='#fff'
for(const d of[-1,1])if(bl)c.beginPath(),c.moveTo(128+d*9,100),c.lineTo(128+d*23,95),c.stroke()
else c.beginPath(),c.ellipse(128+d*16,98,7.5,4.5,-d*.35,0,T),c.fill()
}
+18
View File
@@ -0,0 +1,18 @@
// rock golem stomping in place: stacked boulders, glowing slit eyes, each stomp gives a little bounce
const{sin,cos,PI,min}=Math,T=2*PI
export const frames=24,delay=80,rim=3
export default(c,t)=>{
const sd=t<.5?-1:1,u=(t*2)%1,lift=u<.55?22*sin(PI*u/.55):0,bo=u>.55?5*sin(PI*min(1,(u-.55)/.3)):0
const bx=128-sd*4*sin(PI*min(1,u/.55)),by=134+bo
c.fillStyle=c.strokeStyle='#111';c.lineWidth=8
const rock=(x,y,rx,ry,s)=>{c.beginPath()
for(let k=0;k<8;k++){const a=k*T/8+.2*sin(s*3+k),q=1+.1*sin(s*5+k*2.1);c.lineTo(x+rx*q*cos(a),y+ry*q*sin(a))}
c.closePath();c.fill();c.stroke()}
rock(106,200-(sd<0?lift:0),18,22,1);rock(150,200-(sd>0?lift:0),18,22,2)
rock(bx,by,50,44,3);rock(bx,by-58,26,22,4)
for(const d of[-1,1]){const sw=d*sd*3*sin(PI*u)
rock(bx+d*58,by-28,20,16,5+d);rock(bx+d*78,by+2+sw,15,20,8+d);rock(bx+d*80,by+36+sw,20,18,11+d)}
c.strokeStyle='#fff';c.lineWidth=5
for(const d of[-1,1])c.beginPath(),c.moveTo(bx+d*5,by-58),c.lineTo(bx+d*15,by-61),c.stroke()
c.lineWidth=3.5;c.beginPath();c.moveTo(bx-6,by-14);c.lineTo(bx+4,by-4);c.lineTo(bx-4,by+6);c.lineTo(bx+6,by+16);c.stroke()
}
+30
View File
@@ -0,0 +1,30 @@
// griffin sitting: eagle front, lion back, both wings beating up and down while the tufted tail swishes and it blinks
const{sin,PI}=Math,T=2*PI
export const frames=24,delay=75,rim=3
export default(c,t)=>{
const f=sin(T*t),bl=t>.78&&t<.84,sw=sin(T*t-1)
c.fillStyle=c.strokeStyle='#111';c.lineWidth=6
const shape=(x,y,r,g)=>{c.save();c.translate(x,y);c.rotate(r);c.beginPath();g();c.fill();c.stroke();c.restore()}
const wing=(x,y,r,s)=>{c.save();c.translate(x,y);c.rotate(r);c.scale(s,-s);c.beginPath()
c.moveTo(0,-10);c.quadraticCurveTo(46,-52,100,-46);c.lineTo(80,-26);c.lineTo(88,-12);c.lineTo(66,-8);c.lineTo(70,8)
c.lineTo(48,4);c.lineTo(48,18);c.lineTo(28,10);c.lineTo(4,14);c.closePath();c.fill();c.stroke();c.restore()}
c.lineWidth=9;c.beginPath();c.moveTo(70,204);c.bezierCurveTo(40,210,26,190,30+6*sw,160);c.stroke();c.lineWidth=6
shape(30+6*sw,160,.3*sw,()=>c.ellipse(0,-8,9,14,0,0,T))
wing(142,132,-2.45+.5*f,.8)
shape(0,0,0,()=>c.ellipse(90,186,40,36,0,0,T))
shape(0,0,0,()=>c.ellipse(96,218,26,8,0,0,T))
shape(128,168,-.35,()=>c.ellipse(0,0,50,32,0,0,T))
shape(158,150,0,()=>c.ellipse(0,0,26,36,0,0,T))
for(const x of[152,170]){
c.lineWidth=11;c.beginPath();c.moveTo(x,170);c.lineTo(x+2,216);c.stroke()
c.lineWidth=5;c.beginPath();for(const[dx,dy]of[[14,4],[8,7],[-9,4]])c.moveTo(x+2,218),c.lineTo(x+2+dx,218+dy);c.stroke()
}
c.lineWidth=34;c.beginPath();c.moveTo(160,140);c.lineTo(172,102);c.stroke();c.lineWidth=6
shape(0,0,0,()=>c.arc(178,92,21,0,T))
shape(0,0,0,()=>{c.moveTo(162,80);c.lineTo(142,72);c.lineTo(158,90);c.lineTo(144,98);c.lineTo(164,102)})
shape(0,0,0,()=>{c.moveTo(194,82);c.quadraticCurveTo(216,82,214,106);c.quadraticCurveTo(206,96,194,100)})
wing(130,138,-2.15+.5*f,.95)
c.fillStyle=c.strokeStyle='#fff'
if(bl)c.lineWidth=3,c.beginPath(),c.moveTo(179,89),c.lineTo(189,89),c.stroke()
else c.beginPath(),c.arc(184,88,4.2,0,T),c.fill()
}
+25
View File
@@ -0,0 +1,25 @@
// heron standing on one leg: S-curved neck and long beak, it tilts its head, the plume feathers sway, it blinks
const{sin,PI}=Math,T=2*PI
export const frames=24,delay=85,rim=3
export default(c,t)=>{
const hd=t>.25&&t<.65?.18*sin(PI*(t-.25)/.4):0,br=sin(T*t),pl=.08*sin(T*t-1),bl=t>.8&&t<.86
c.fillStyle=c.strokeStyle='#111';c.lineWidth=6
const shape=(x,y,r,f)=>{c.save();c.translate(x,y);c.rotate(r);c.beginPath();f();c.fill();c.stroke();c.restore()}
const path=(w,f)=>{c.lineWidth=w;c.beginPath();f();c.stroke();c.lineWidth=6}
c.translate(-10,0)
path(5,()=>{c.moveTo(118,136);c.lineTo(120,214)})
path(4,()=>{c.moveTo(120,214);c.lineTo(140,215);c.moveTo(120,214);c.lineTo(102,215);c.moveTo(120,214);c.lineTo(128,210)})
path(5,()=>{c.moveTo(110,136);c.lineTo(102,154);c.lineTo(112,164)})
shape(0,br*.6,0,()=>{c.moveTo(78,122);c.lineTo(54,146);c.lineTo(84,134)})
shape(112,116+br*.6,-.3,()=>c.ellipse(0,0,46,22,0,0,T))
path(12,()=>{c.moveTo(142,102+br*.6);c.bezierCurveTo(170,92,136,68,160,52)})
c.save();c.translate(160,52);c.rotate(-hd);c.translate(-160,-52)
c.lineWidth=3;c.save();c.translate(158,44);c.rotate(pl)
c.beginPath();c.moveTo(0,-2);c.quadraticCurveTo(-14,-10,-30,-8);c.moveTo(0,2);c.quadraticCurveTo(-12,0,-26,4);c.stroke();c.restore()
c.lineWidth=4;c.beginPath();c.moveTo(174,40);c.lineTo(226,50);c.lineTo(174,52);c.closePath();c.fill();c.stroke();c.lineWidth=6
shape(166,46,0,()=>c.ellipse(0,0,13,10,0,0,T))
c.fillStyle=c.strokeStyle='#fff'
if(bl)c.lineWidth=2.5,c.beginPath(),c.arc(169,42,3,.15*PI,.85*PI),c.stroke()
else c.beginPath(),c.arc(169,44,2.8,0,T),c.fill()
c.restore()
}
+35
View File
@@ -0,0 +1,35 @@
// hippo with a huge slow yawn: the head tips back and the jaws open wide to show white tusks, eyes squeeze shut, then the ears wiggle
const{sin,cos,PI}=Math,T=2*PI
export const frames=24,delay=85,rim=3
export default(c,t)=>{
const y=t>.1&&t<.7?sin(PI*(t-.1)/.6):0,th=Math.min(1,y*3),a=.6*y,w=t>.74&&t<.96?.35*sin(T*3*(t-.74)/.22):0,H=[172,142]
c.fillStyle=c.strokeStyle='#111';c.lineWidth=6
const shape=(x,y,r,f)=>{c.save();c.translate(x,y);c.rotate(r);c.beginPath();f();c.fill();c.stroke();c.restore()}
const at=(r,f)=>{c.save();c.translate(...H);c.rotate(r);c.translate(-H[0],-H[1]);f();c.restore()}
const rot=(r,x,y)=>[H[0]+(x-H[0])*cos(r)-(y-H[1])*sin(r),H[1]+(x-H[0])*sin(r)+(y-H[1])*cos(r)]
c.translate(-10,0)
for(const x of[70,92,136,158])shape(x,190,0,()=>c.roundRect(-11,-20,22,42,9))
shape(110,150,0,()=>c.ellipse(0,0,68,46*(1+.02*y),0,0,T))
at(.25*a,()=>shape(204,152,0,()=>c.ellipse(0,0,32,15,0,0,T)))
shape(0,0,0,()=>{c.moveTo(...rot(-a,180,141));c.lineTo(...rot(-a,232,140));c.lineTo(...rot(.25*a,232,141));c.closePath()})
at(-a,()=>{
shape(154,96,-.4+w,()=>c.ellipse(0,-4,6,9,0,0,T))
shape(170,92,.2-w,()=>c.ellipse(0,-4,6,9,0,0,T))
shape(170,118,0,()=>c.ellipse(0,0,30,26,0,0,T))
shape(206,128,0,()=>c.ellipse(0,0,32,18,0,0,T))
shape(182,100,0,()=>c.arc(0,0,9,0,T))
shape(226,116,0,()=>c.arc(0,0,7,0,T))
})
c.fillStyle=c.strokeStyle='#fff';c.lineWidth=3
at(.25*a,()=>{
c.beginPath();c.moveTo(184,141);c.lineTo(230,141);c.stroke()
if(th>0)for(const x of[210,222])c.beginPath(),c.moveTo(x-3,141),c.lineTo(x+3,141),c.lineTo(x,141-11*th),c.closePath(),c.fill()
})
at(-a,()=>{
c.beginPath();c.moveTo(184,141);c.lineTo(230,140);c.stroke()
if(th>0)c.beginPath(),c.moveTo(214,140),c.lineTo(219,140),c.lineTo(216.5,140+7*th),c.closePath(),c.fill()
c.beginPath();c.ellipse(228,113,2.5,1.8,0,0,T);c.fill()
if(y>.35)c.beginPath(),c.arc(183,97,4,.15*PI,.85*PI),c.stroke()
else c.beginPath(),c.arc(183,99,3.6,0,T),c.fill()
})
}
+18
View File
@@ -0,0 +1,18 @@
// hummingbird hovering: the wing flicks through four blur poses every frame, long beak, a gentle bob and tilt, a blink
const{sin,cos,PI}=Math,T=2*PI
export const frames=24,delay=70,rim=3
const W=[-.1,-.75,-1.4,-.75]
export default(c,t,i)=>{
const s=sin(T*t),bl=t>.7&&t<.78
c.fillStyle=c.strokeStyle='#111';c.lineWidth=6
const shape=(x,y,r,f)=>{c.save();c.translate(x,y);c.rotate(r);c.beginPath();f();c.fill();c.stroke();c.restore()}
c.translate(122,130+8*s);c.rotate(.06*cos(T*t));c.scale(1.25,1.25);c.translate(-128,-124)
shape(94,154,.15*sin(T*t*2),()=>{c.moveTo(4,-8);c.lineTo(-26,14);c.lineTo(-14,16);c.lineTo(-20,30);c.lineTo(8,8)})
shape(122,132,-.6,()=>c.ellipse(0,0,36,19,0,0,T))
shape(150,104,0,()=>c.arc(0,0,18,0,T))
c.lineWidth=4;c.beginPath();c.moveTo(164,101);c.lineTo(210,92);c.stroke()
c.lineWidth=6;shape(124,116,W[i%4],()=>c.ellipse(0,-32,10,32,0,0,T))
c.fillStyle=c.strokeStyle='#fff'
if(bl)c.lineWidth=2.5,c.beginPath(),c.arc(155,99,4,.15*PI,.85*PI),c.stroke()
else c.beginPath(),c.arc(155,101,4.5,0,T),c.fill()
}
+19
View File
@@ -0,0 +1,19 @@
// three-headed hydra: the heads bob out of step on long necks, each flicks its forked tongue in turn, the tail tip sways
const{sin,PI}=Math,T=2*PI
export const frames=24,delay=80,rim=3
export default(c,t)=>{
const ts=6*sin(T*t)
c.fillStyle=c.strokeStyle='#111';c.lineWidth=6
c.beginPath();c.moveTo(48,222);c.bezierCurveTo(52,168,196,164,204,222);c.closePath();c.fill();c.stroke()
c.beginPath();c.moveTo(192,196);c.quadraticCurveTo(222,200,234,178+ts);c.quadraticCurveTo(240,220,196,218);c.closePath();c.fill();c.stroke()
for(const[i,[bx,by,X,Y,d]]of[[100,190,78,100,-1],[128,186,130,64,1],[156,190,180,100,1]].entries()){
const ph=i*2.1,hx=X+5*sin(T*t+ph),hy=Y+9*sin(T*t+ph),q=((t-i/3)%1+1)%1,s=q<.2?sin(PI*q/.2):0
c.lineWidth=20;c.beginPath();c.moveTo(bx,by);c.bezierCurveTo(bx+(hx-bx)*.1,by-60,hx-d*24,hy+40,hx-d*6,hy+4);c.stroke()
c.save();c.translate(hx,hy);c.scale(d,1);c.rotate(-.15+.1*sin(T*t+ph))
c.lineWidth=6;c.beginPath();c.moveTo(-16,-10);c.quadraticCurveTo(10,-20,30,-6);c.quadraticCurveTo(36,0,30,6);c.quadraticCurveTo(10,16,-16,10);c.closePath();c.fill();c.stroke()
c.beginPath();c.moveTo(-12,-10);c.lineTo(-26,-22);c.lineTo(-4,-14);c.closePath();c.fill();c.stroke()
if(s)c.lineWidth=3.5,c.beginPath(),c.moveTo(32,2),c.lineTo(32+12*s,2),c.lineTo(38+12*s,-2*s),c.moveTo(32+12*s,2),c.lineTo(38+12*s,6*s),c.stroke()
c.fillStyle='#fff';c.beginPath();c.arc(8,-6,3.8,0,T);c.fill();c.fillStyle='#111'
c.restore()
}
}
+19
View File
@@ -0,0 +1,19 @@
// inchworm inching along: the back end catches up into a tall loop, then the front stretches out flat again, ground dashes slide back
const{sin,cos,PI,sqrt,max,min}=Math,T=2*PI
export const frames=24,delay=80,rim=3
const L=170,G=100,Y=164
export default(c,t)=>{
const A=t<.5,u=A?t/.5:(t-.5)/.5,e=(1-cos(PI*u))/2,o=43-G*t
const tx=o+(A?G*e:G),hx=o+(A?L:L+G*e),ty=Y-(A?14*sin(PI*u):0),hy=Y-(A?0:16*sin(PI*u)),d=hx-tx
const H=1.35*sqrt(max(0,(L/2)**2-(d/2)**2)),k=(L-d)*.12
c.fillStyle=c.strokeStyle='#111';c.lineWidth=4
for(let n=0;n<4;n++){
const x=20+n*G-G*t+G*.5,l=8*min(1,max(0,(x-20)/24),max(0,(236-x)/24))
if(l>.5)c.beginPath(),c.moveTo(x-l,Y+30),c.lineTo(x+l,Y+30),c.stroke()
}
c.lineWidth=5
for(const[x,y]of[[tx-4,ty],[tx+6,ty],[hx-14,hy],[hx-4,hy]])c.beginPath(),c.moveTo(x,y),c.lineTo(x,y+15),c.stroke()
c.lineWidth=24;c.beginPath();c.moveTo(tx,ty);c.bezierCurveTo(tx-k,ty-H,hx+k,hy-H,hx,hy);c.stroke()
c.lineWidth=6;c.beginPath();c.arc(hx+6,hy-6,15,0,T);c.fill();c.stroke()
c.fillStyle='#fff';c.beginPath();c.arc(hx+11,hy-10,3.5,0,T);c.fill()
}
+28
View File
@@ -0,0 +1,28 @@
// kangaroo hopping in place: big hind feet push off, the tail swings up for balance, a joey peeks out of the pouch
const{sin,PI}=Math,T=2*PI
export const frames=24,delay=75,rim=3
export default(c,t)=>{
const air=t<.6,p=t/.6,h=air?30*sin(PI*p):0,sq=air?0:sin(PI*(t-.6)/.4),jb=-4*sin(T*t),bl=t>.82&&t<.88,tw=t>.66&&t<.74?.3:0
c.fillStyle=c.strokeStyle='#111';c.lineWidth=6
const shape=(x,y,r,f)=>{c.save();c.translate(x,y);c.rotate(r);c.beginPath();f();c.fill();c.stroke();c.restore()}
const line=(w,x0,y0,x1,y1)=>{c.lineWidth=w;c.beginPath();c.moveTo(x0,y0);c.lineTo(x1,y1);c.stroke();c.lineWidth=6}
c.translate(128,214);c.scale(.92,.92);c.translate(-112,-214);c.translate(0,-h)
shape(96,206,air?.5*sin(PI*p):0,()=>c.ellipse(26,0,30,7,0,0,T))
line(16,110,186+8*sq,98,204)
c.translate(0,8*sq);c.translate(116,176);c.rotate(air?.12*sin(PI*p):-.05*sq);c.translate(-116,-176)
shape(102,184,air?.3*sin(PI*p):.08*sq,()=>{c.moveTo(4,-12);c.quadraticCurveTo(-40,0,-78,22);c.quadraticCurveTo(-40,12,4,12)})
shape(116,168,-.3,()=>c.ellipse(0,0,32,36,0,0,T))
shape(142,128,.4,()=>c.ellipse(0,0,32,48,0,0,T))
line(18,154,96,168,76)
shape(164,60,-.35,()=>c.ellipse(0,-10,6,15,0,0,T))
shape(173,58,.05+tw,()=>c.ellipse(0,-10,6,15,0,0,T))
shape(172,72,.15,()=>{c.ellipse(0,0,17,14,0,0,T);c.ellipse(16,6,13,8,.35,0,T)})
line(9,162,118,178,134)
c.strokeStyle='#fff';c.lineWidth=3;c.beginPath();c.moveTo(138,162);c.quadraticCurveTo(150,164,160,156);c.stroke();c.strokeStyle='#111';c.lineWidth=6
shape(160,136+jb,-.4,()=>c.ellipse(0,-6,4,9,0,0,T))
shape(166,147+jb,0,()=>{c.arc(0,0,10,0,T);c.ellipse(9,3,7,5,.3,0,T)})
c.fillStyle=c.strokeStyle='#fff'
c.beginPath();c.arc(169,144+jb,2.4,0,T);c.fill()
if(bl)c.lineWidth=3,c.beginPath(),c.arc(178,66,4,.15*PI,.85*PI),c.stroke()
else c.beginPath(),c.arc(178,68,3.4,0,T),c.fill()
}
+36
View File
@@ -0,0 +1,36 @@
// fox spirit sitting side-on, three white-tipped tails fanned out behind it swaying one after another,
// a fox-fire flame flickering beside its nose, an ear twitches and it blinks
const{sin,cos,PI}=Math,T=2*PI
export const frames=24,delay=80,rim=3
export default(c,t)=>{
const bl=t>.7&&t<.77,tw=t>.36&&t<.44?.3:0,hr=.04*sin(T*t),f=k=>3*sin(T*t*2+k)
c.fillStyle=c.strokeStyle='#111';c.lineWidth=6;c.translate(122,218);c.scale(.88,.88);c.translate(-138,-218)
const shape=(x,y,r,g)=>{c.save();c.translate(x,y);c.rotate(r);c.beginPath();g();c.fill();c.stroke();c.restore()}
// tails: plump, curled tip, white tip cut out by clipping a circle to the tail's own outline
// tails: narrow at the root, bushy in the middle, pointed curling tip; the white tip is a circle clipped to the tail
for(const[i,a]of[-.05,.8,1.65].entries()){
const r=a+.1*sin(T*t-i*.9),L=[],R=[];let x=160,y=194
for(let j=0;j<=20;j++){const s=j/20,th=r+.5*s*s,w=2.5+15*sin(PI*s**1.4);L.push([x-cos(th)*w,y-sin(th)*w]);R.push([x+cos(th)*w,y+sin(th)*w]);x+=sin(th)*5;y-=cos(th)*5}
c.save();c.beginPath();for(const p of[...L,...R.reverse()])c.lineTo(...p);c.closePath();c.fill();c.stroke();c.clip()
c.fillStyle='#fff';c.beginPath();c.arc(x,y,24,0,T);c.fill();c.restore()
}
shape(0,0,0,()=>c.ellipse(136,186,36,32,0,0,T))
shape(0,0,0,()=>c.ellipse(136,218,20,6,0,0,T))
shape(0,0,0,()=>{c.moveTo(86,178);c.bezierCurveTo(70,160,68,132,80,112);c.lineTo(108,104);c.bezierCurveTo(116,130,140,148,158,180);c.lineTo(108,190);c.closePath()})
c.lineWidth=13;c.beginPath();c.moveTo(92,172);c.lineTo(90,213);c.stroke()
c.lineWidth=6;shape(0,0,0,()=>c.ellipse(85,217,11,5.5,0,0,T))
// fox-fire: round base with three flickering tongues
c.save();c.translate(38,64+6*sin(T*t));c.scale(1.15,1.15)
shape(0,0,.08*sin(T*t*2),()=>{c.moveTo(-11,6);c.arc(0,6,11,PI,0,true);c.quadraticCurveTo(14,0,16+f(1),-12);c.quadraticCurveTo(10,-7,7,-7)
c.quadraticCurveTo(10,-22,2+f(2),-38);c.quadraticCurveTo(-9,-22,-6,-9);c.quadraticCurveTo(-11,-9,-15+f(3),-16);c.quadraticCurveTo(-15,-2,-11,6)})
c.restore()
c.translate(96,108);c.rotate(hr);c.translate(-96,-108)
shape(0,0,0,()=>c.ellipse(94,92,20,18,0,0,T))
shape(0,0,0,()=>{c.moveTo(100,82);c.lineTo(56,99);c.quadraticCurveTo(48,103,56,107);c.lineTo(98,110)})
shape(0,0,0,()=>{c.moveTo(102,100);c.lineTo(116,116);c.lineTo(92,110)})
shape(0,0,0,()=>{c.moveTo(82,82);c.lineTo(86,44);c.lineTo(100,76)})
shape(106,80,tw,()=>{c.moveTo(-8,-2);c.lineTo(4,-36);c.lineTo(8,6)})
c.fillStyle=c.strokeStyle='#fff'
if(bl)c.lineWidth=2.5,c.beginPath(),c.moveTo(77,90),c.lineTo(90,87),c.stroke()
else c.beginPath(),c.ellipse(84,89,6.5,3.2,-.3,0,T),c.fill()
}
+21
View File
@@ -0,0 +1,21 @@
// kiwi waddling in place: round body rocking side to side, a long curved beak, little legs stepping, a blink
const{sin,cos,PI,max,abs}=Math,T=2*PI
export const frames=24,delay=80,rim=3
export default(c,t)=>{
const s=sin(T*t),b=abs(s),bl=t>.4&&t<.47
c.fillStyle=c.strokeStyle='#111';c.lineWidth=7
const shape=f=>{c.beginPath();f();c.fill();c.stroke()}
for(const k of[0,1]){
const p=T*t+k*PI,hx=100+k*36,fx=hx+12*sin(p),fy=210-9*max(0,cos(p))
c.beginPath();c.moveTo(hx,162);c.lineTo(fx,fy);c.stroke()
c.lineWidth=5;c.beginPath();c.moveTo(fx-8,fy);c.lineTo(fx+12,fy);c.moveTo(fx,fy);c.lineTo(fx+10,fy-6);c.stroke();c.lineWidth=7
}
c.translate(122,206);c.rotate(.05*s);c.translate(-122,-206-4*b)
c.lineWidth=6
shape(()=>c.ellipse(112,124,66,50,-.08,0,T))
shape(()=>c.arc(172,110,24,0,T))
c.beginPath();c.moveTo(188,112);c.quadraticCurveTo(222,122,234,168);c.stroke()
c.fillStyle=c.strokeStyle='#fff'
if(bl)c.lineWidth=2.5,c.beginPath(),c.arc(178,102,3.5,.15*PI,.85*PI),c.stroke()
else c.beginPath(),c.arc(178,104,3.5,0,T),c.fill()
}
+23
View File
@@ -0,0 +1,23 @@
// kraken surfacing: four tentacles rise out of a rolling wave line and curl, one big eye peeks over the water and looks around
const{sin,cos,PI,max}=Math,T=2*PI
export const frames=24,delay=80,rim=3
export default(c,t)=>{
const W=174,wy=x=>W+4*sin(x/17-T*t),hy=W+3*sin(T*t),bl=t>.55&&t<.61
c.fillStyle=c.strokeStyle='#111';c.lineWidth=6
c.beginPath();c.ellipse(128,hy+8,48,68,0,PI,2*PI);c.closePath();c.fill();c.stroke()
for(const[i,[x,d,L,b0,b1]]of[[72,-1,72,2,.55],[104,-1,128,1.2,.6],[152,1,128,1.2,.6],[184,1,72,2,.55]].entries()){
const B=d*(b0+b1*sin(T*t+i*1.7)),N=26,P=[];let px=x,py=wy(x)+6
for(let j=0;j<=N;j++){const s=j/N,th=-PI/2+d*(L<99?.45:.08)+B*s*s;P.push([px,py,th,13*(1-s)+2.5]);px+=cos(th)*L/N;py+=sin(th)*L/N}
c.lineWidth=4;c.beginPath()
for(const[x,y,th,w]of P)c.lineTo(x-sin(th)*w,y+cos(th)*w)
for(const[x,y,th,w]of P.reverse())c.lineTo(x+sin(th)*w,y-cos(th)*w)
c.closePath();c.fill();c.stroke();P.reverse()
c.fillStyle='#fff'
for(const j of[6,11,16]){const[x,y,th,w]=P[j];c.beginPath();c.arc(x-d*sin(th)*w*.45,y+d*cos(th)*w*.45,max(1.6,w*.2),0,T);c.fill()}
c.fillStyle='#111'
}
c.lineWidth=7;c.beginPath();for(let x=56;x<=200;x+=4)c.lineTo(x,wy(x));c.stroke()
c.fillStyle=c.strokeStyle='#fff'
if(bl)c.lineWidth=3.5,c.beginPath(),c.moveTo(115,hy-26),c.quadraticCurveTo(128,hy-20,141,hy-26),c.stroke()
else{c.beginPath();c.ellipse(128,hy-28,16,10,0,0,T);c.fill();c.fillStyle='#111';c.beginPath();c.arc(128+7*sin(T*t),hy-28,5.5,0,T);c.fill()}
}
+18
View File
@@ -0,0 +1,18 @@
// manta ray seen from above: its wide wings ripple in a slow wave, the thin tail trails and sways, it rises and sinks gently
const{sin,cos,abs,PI}=Math,T=2*PI
export const frames=24,delay=85,rim=3
export default(c,t)=>{
const w=sin(T*t),w2=sin(T*t-1.1),tx=128+(100-6*abs(w)),ty=116+20*w
c.fillStyle=c.strokeStyle='#111';c.lineWidth=6
c.translate(0,-6*sin(T*t+1.2))
c.lineWidth=4;c.beginPath();c.moveTo(128,170);c.quadraticCurveTo(128-10*sin(T*t-1),204,128+12*sin(T*t-2),232);c.stroke();c.lineWidth=6
c.beginPath();c.moveTo(128,94)
for(const d of[-1,1]){
const x=v=>128+d*(v-128)
if(d<0)c.lineTo(108,86),c.quadraticCurveTo(68,88+8*w,x(tx),ty),c.quadraticCurveTo(62,140+12*w2,102,162),c.quadraticCurveTo(116,176,128,178)
else c.quadraticCurveTo(140,176,154,162),c.quadraticCurveTo(194,140+12*w2,tx,ty),c.quadraticCurveTo(188,88+8*w,148,86),c.lineTo(128,94)
}
c.fill();c.stroke()
for(const d of[-1,1]){c.save();c.translate(128+d*16,82);c.rotate(d*(-.12+.08*sin(T*t*2)));c.beginPath();c.ellipse(0,-9,4.5,12,0,0,T);c.fill();c.stroke();c.restore()}
c.fillStyle='#fff';for(const d of[-1,1])c.beginPath(),c.arc(128+d*22,100,4.5,0,T),c.fill()
}
+22
View File
@@ -0,0 +1,22 @@
// praying mantis: it sways, tilts its triangle head side to side, and gives its folded forearms a little pump
const{sin,cos,PI,max}=Math,T=2*PI
export const frames=24,delay=85,rim=3
export default(c,t)=>{
const s=sin(T*t),pm=t>.3&&t<.6?sin(PI*(t-.3)/.3):0,ht=.3*sin(T*t+1)
c.fillStyle=c.strokeStyle='#111';c.lineWidth=4
const shape=f=>{c.beginPath();f();c.fill();c.stroke()}
for(const[hx,kx,ky,fx]of[[130,152,186,166],[118,98,190,82],[112,62,184,46]])c.beginPath(),c.moveTo(hx,160),c.lineTo(kx,ky),c.lineTo(fx,212),c.stroke()
c.translate(122,212);c.rotate(.03*s);c.translate(-122,-212)
c.lineWidth=6
c.save();c.translate(84,158);c.rotate(-.16);shape(()=>{c.moveTo(-50,4);c.quadraticCurveTo(-10,-22,46,-6);c.quadraticCurveTo(10,18,-50,4)});c.restore()
c.lineWidth=12;c.beginPath();c.moveTo(124,158);c.quadraticCurveTo(140,124,150,86);c.stroke()
c.save();c.translate(146,102);c.rotate(-.15*pm)
c.lineWidth=10;c.beginPath();c.moveTo(0,0);c.lineTo(32,8);c.stroke()
c.save();c.translate(32,8);c.rotate(2.5-.5*pm);c.lineWidth=8;c.beginPath();c.moveTo(0,0);c.lineTo(28,0);c.stroke();c.restore()
c.restore()
c.save();c.translate(152,80);c.rotate(ht)
c.lineWidth=3;for(const d of[-1,1])c.beginPath(),c.moveTo(d*6,-16),c.quadraticCurveTo(d*12,-48,d*30,-64),c.stroke()
c.lineWidth=6;shape(()=>{c.moveTo(-20,-16);c.lineTo(20,-16);c.lineTo(0,10);c.closePath()})
c.fillStyle='#fff';for(const d of[-1,1])c.beginPath(),c.arc(d*15,-15,3.5,0,T),c.fill()
c.restore()
}
+25
View File
@@ -0,0 +1,25 @@
// mimic: a treasure chest that chomps twice, white teeth snapping, tongue lolling out the side, eyes on the lid
const{sin,max}=Math,T=2*Math.PI
export const frames=24,delay=70,rim=3
export default(c,t)=>{
const o=max(0,sin(T*t*2)),O=34*o,w=6*sin(T*t*2+1),bl=t>.86&&t<.92
c.fillStyle=c.strokeStyle='#111';c.lineWidth=6
c.translate(0,4-4*o)
const tx=222+w*.4,ty=188+w
c.beginPath();c.moveTo(184,132-O*.5);c.bezierCurveTo(214,128-O*.5,236,150,tx+10,ty-6);c.arc(tx,ty-4,10,.2,Math.PI*.9)
c.bezierCurveTo(212,174,206,156,186,150-O*.3);c.closePath();c.fill();c.stroke()
c.beginPath();c.roundRect(52,138,152,76,8);c.fill();c.stroke()
c.fillRect(58,136-O,140,O+4)
c.save();c.translate(128,140-O);c.rotate(-.05*o);c.translate(-128,-140)
c.beginPath();c.moveTo(52,140);c.lineTo(52,108);c.quadraticCurveTo(128,72,204,108);c.lineTo(204,140);c.closePath();c.fill();c.stroke()
c.fillStyle=c.strokeStyle='#fff'
for(let x=64;x<192;x+=16)c.beginPath(),c.moveTo(x,140),c.lineTo(x+16,140),c.lineTo(x+8,152),c.fill()
c.lineWidth=3.5;for(const x of[82,174])c.beginPath(),c.moveTo(x,101),c.lineTo(x,124),c.stroke()
for(const d of[-1,1])if(bl)c.beginPath(),c.moveTo(128+d*14,114),c.lineTo(128+d*32,110),c.stroke()
else{c.beginPath();c.ellipse(128+d*23,112,10,7,d*.3,0,T);c.fill();c.fillStyle='#111';c.beginPath();c.arc(128+d*19,113,4,0,T);c.fill();c.fillStyle='#fff'}
c.restore();c.fillStyle=c.strokeStyle='#fff'
c.lineWidth=3;c.beginPath();c.moveTo(204,142-O*.4);c.quadraticCurveTo(222,154,tx-1,ty-10);c.stroke()
for(let x=72;x<184;x+=16)c.beginPath(),c.moveTo(x,140),c.lineTo(x+16,140),c.lineTo(x+8,128),c.fill()
c.lineWidth=3.5;for(const x of[82,174])c.beginPath(),c.moveTo(x,152),c.lineTo(x,206),c.stroke()
c.beginPath();c.arc(128,166,7,0,T);c.fill();c.beginPath();c.moveTo(123,168);c.lineTo(133,168);c.lineTo(128,186);c.fill()
}
+33
View File
@@ -0,0 +1,33 @@
// mole popping out of a dirt mound with a puff of dirt: it sniffs with its nose and whiskers, then ducks back down
const{sin,PI,max,min}=Math,T=2*PI
export const frames=24,delay=80,rim=3
export default(c,t)=>{
const up=x=>sin(x*PI*.6)/sin(PI*.6),u=t<.2?up(t/.2):t<.66?1:t<.8?1-sin(PI/2*(t-.66)/.14):0
const cy=240-90*u,sn=t>.24&&t<.62?sin(T*(t-.24)/.38):0,nx=1.5*sin(T*t*6)*(t>.24&&t<.62),tl=.07*sn,pw=min(1,max(0,(u-.5)/.5))
c.fillStyle=c.strokeStyle='#111';c.lineWidth=6
const shape=(x,y,r,f)=>{c.save();c.translate(x,y);c.rotate(r);c.beginPath();f();c.fill();c.stroke();c.restore()}
c.save();c.beginPath();c.rect(0,0,256,196);c.clip()
c.translate(128,196);c.rotate(tl);c.translate(-128,-196)
shape(128,cy,0,()=>c.ellipse(0,0,37,45,0,0,T))
if(cy<176){
c.lineWidth=2.5
for(const d of[-1,1])for(const e of[-5,6])c.beginPath(),c.moveTo(128+d*20,cy+6),c.lineTo(128+d*46,cy+6+e+2*sn*d),c.stroke()
c.lineWidth=6
}
c.restore()
if(pw>0)for(const d of[-1,1])shape(128+d*37,190-8*pw,d*.25,()=>c.ellipse(0,0,15,8,0,0,T))
shape(128,216,0,()=>{c.ellipse(0,0,92,30,0,PI,T);c.closePath()})
if(t>.02&&t<.27){
const p=(t-.02)/.25
for(const[d,s]of[[-1,1],[1,1],[-1,.6],[1,.6]])c.beginPath(),c.arc(128+d*(26+34*p*s),180-34*p*s+44*p*p,4.5*(1-.5*p),0,T),c.fill()
}
c.fillStyle=c.strokeStyle='#fff';c.lineWidth=3
c.beginPath();c.ellipse(128,190,30,7,0,.1*PI,.9*PI);c.stroke()
if(pw>0)for(const d of[-1,1])for(const k of[-1,0,1])c.beginPath(),c.moveTo(128+d*(41+k*5),186-8*pw),c.lineTo(128+d*(45+k*6),182-8*pw),c.stroke()
if(cy<176){
c.save();c.translate(128,196);c.rotate(tl);c.translate(-128,-196)
for(const d of[-1,1])c.beginPath(),c.arc(128+d*12,cy-12,2.8,0,T),c.fill()
c.beginPath();c.ellipse(128+nx,cy+3-Math.abs(nx),6.5,4.8,0,0,T);c.fill()
c.restore()
}
}
+38
View File
@@ -0,0 +1,38 @@
// owl nestled in a crescent moon: the moon's curve is its back and left wing, the lower tip is the perch it grips; big ringed eyes blink, the head tilts, a star twinkles
const{sin,cos,PI,atan2,sqrt,hypot}=Math,T=2*PI
export const frames=24,delay=85,rim=3
// crescent edges are circles through tips B and A, bulging s px toward the lower left
const B=[84,60],A=[204,214],M=[(B[0]+A[0])/2,(B[1]+A[1])/2],L=hypot(A[0]-B[0],A[1]-B[1]),n=[(B[1]-A[1])/L,(A[0]-B[0])/L]
const circ=s=>{const r=(L*L/4+s*s)/(2*s);return[M[0]-(r-s)*n[0],M[1]-(r-s)*n[1],r]}
const[ox,oy,R]=circ(72),[ix,iy,r]=circ(34),an=(x,y,P)=>atan2(P[1]-y,P[0]-x),top=x=>iy+sqrt(r*r-(x-ix)**2)
export default(c,t)=>{
const bl=(t>.3&&t<.36)||(t>.44&&t<.5),k=.5+.5*sin(2*T*t)
c.fillStyle=c.strokeStyle='#111';c.lineWidth=6
c.translate(8,0)
const shape=g=>{c.beginPath();g();c.fill();c.stroke()}
c.save();c.translate(42,40);c.scale(.55+.45*k,.55+.45*k);c.beginPath()
for(let i=0;i<8;i++){const a=i*PI/4-PI/2,q=i%2?4:15;c.lineTo(q*cos(a),q*sin(a))}
c.closePath();c.fill();c.restore()
shape(()=>{c.arc(ox,oy,R,an(ox,oy,B),an(ox,oy,A),true);c.arc(ix,iy,r,an(ix,iy,A),an(ix,iy,B));c.closePath()})
for(const x of[143,163]){
const y=top(x);c.lineWidth=9;c.beginPath();c.moveTo(x,160);c.lineTo(x,y);c.stroke();c.lineWidth=6
for(const d of[-7,0,7])c.beginPath(),c.arc(x+d,y-1-(d?0:1),4.5,0,T),c.fill()
}
c.translate(0,-10)
shape(()=>c.ellipse(182,142,12,32,-.2,0,T))
shape(()=>c.ellipse(144,138,42,48,0,0,T))
c.strokeStyle='#fff';c.lineWidth=2.5
for(const[x,y]of[[134,140],[154,140],[144,156]])c.beginPath(),c.moveTo(x-5,y-3),c.lineTo(x,y+2),c.lineTo(x+5,y-3),c.stroke()
c.strokeStyle='#111';c.lineWidth=6
c.translate(144,112);c.rotate(.12*sin(T*t));c.translate(-144,-112)
shape(()=>{c.ellipse(144,82,42,36,0,0,T)})
for(const d of[-1,1])shape(()=>{c.moveTo(144+d*38,70);c.lineTo(144+d*44,38);c.lineTo(144+d*16,58);c.closePath()})
c.fillStyle=c.strokeStyle='#fff'
for(const d of[-1,1]){
const x=144+d*18,y=84
c.lineWidth=3;c.beginPath();c.arc(x,y,15,0,T);c.stroke()
if(bl)c.lineWidth=3.5,c.beginPath(),c.arc(x,y-3,8,.15*PI,.85*PI),c.stroke()
else c.beginPath(),c.arc(x,y,10,0,T),c.fill(),c.fillStyle='#111',c.beginPath(),c.arc(x,y,5,0,T),c.fill(),c.fillStyle='#fff'
}
c.beginPath();c.moveTo(139,98);c.lineTo(149,98);c.lineTo(144,110);c.closePath();c.fill()
}
+24
View File
@@ -0,0 +1,24 @@
// moray eel peeking out of a hole in a rock: sways, slowly opens and closes its mouth
const{sin,cos,PI}=Math,T=2*PI
export const frames=24,delay=85,rim=3
export default(c,t)=>{
const sw=8*sin(T*t),sw2=12*sin(T*t-.6),m=t>.2&&t<.7?.45*sin(PI*(t-.2)/.5):0,bl=t>.84&&t<.9
c.fillStyle=c.strokeStyle='#111';c.lineWidth=6
c.beginPath();c.moveTo(36,226);c.bezierCurveTo(28,192,56,160,98,158);c.bezierCurveTo(120,146,164,148,182,160)
c.bezierCurveTo(216,166,234,198,222,226);c.quadraticCurveTo(128,236,36,226);c.fill();c.stroke()
c.globalCompositeOperation='destination-out';c.beginPath();c.ellipse(114,186,32,15,0,0,T);c.fill();c.globalCompositeOperation='source-over'
const nx=132+sw2,ny=94
// body tapers from the hole up to the head
const B=[[114,194],[108,160],[118+sw,124],[nx,ny]],P=u=>[0,1].map(j=>(1-u)**3*B[0][j]+3*u*(1-u)**2*B[1][j]+3*u*u*(1-u)*B[2][j]+u**3*B[3][j])
for(let i=1;i<=24;i++)c.lineWidth=31-9*i/24,c.beginPath(),c.moveTo(...P((i-1)/24)),c.lineTo(...P(i/24)),c.stroke()
// front lip of the hole passing in front of the body
c.strokeStyle='#fff';c.lineWidth=3.5;c.beginPath();c.ellipse(114,186,32,15,0,.08*PI,.92*PI);c.stroke();c.strokeStyle='#111'
c.save();c.translate(nx,ny);c.rotate(-.15+.05*sin(T*t-1))
c.lineWidth=6;c.beginPath();c.ellipse(16,-8,32,13,0,0,T);c.fill();c.stroke()
c.save();c.translate(-6,2);c.rotate(m);c.beginPath();c.ellipse(24,5,26,7,0,0,T);c.fill();c.stroke();c.restore()
c.fillStyle=c.strokeStyle='#fff'
if(m<.1)c.lineWidth=3,c.beginPath(),c.moveTo(4,3),c.quadraticCurveTo(24,4,40,1),c.stroke()
if(bl)c.lineWidth=3,c.beginPath(),c.arc(24,-13,4.5,.15*PI,.85*PI),c.stroke()
else c.beginPath(),c.arc(24,-12,4.5,0,T),c.fill()
c.restore()
}
+27
View File
@@ -0,0 +1,27 @@
// narwhal swimming along: bobs, flicks its tail, and the white spiral on its long tusk keeps twirling toward the tip
const{sin,cos,PI}=Math,T=2*PI
export const frames=24,delay=80,rim=3
export default(c,t)=>{
const s=sin(T*t),k=sin(T*t-1.4),bl=t>.8&&t<.86
c.fillStyle=c.strokeStyle='#111';c.lineWidth=6
c.translate(128,128+7*s);c.rotate(.04*cos(T*t));c.scale(.88,.88);c.translate(-134,-128)
const shape=(x,y,r,f)=>{c.save();c.translate(x,y);c.rotate(r);c.beginPath();f();c.fill();c.stroke();c.restore()}
shape(38,127,.3*k,()=>{
c.moveTo(6,-5);c.quadraticCurveTo(-8,-10,-22,-28);c.quadraticCurveTo(-16,-10,-12,0)
c.quadraticCurveTo(-16,10,-22,28);c.quadraticCurveTo(-8,10,6,5)})
shape(0,0,0,()=>{
c.moveTo(40,122);c.bezierCurveTo(64,116,84,96,124,96);c.bezierCurveTo(164,96,194,110,196,132)
c.bezierCurveTo(198,154,172,166,132,166);c.bezierCurveTo(92,166,66,142,40,132)})
shape(142,162,-.6-.35*sin(T*t-2),()=>c.ellipse(-18,0,20,8,0,0,T))
// tusk, with slanted white marks sliding from base to tip so the spiral looks like it turns
c.save();c.translate(190,116);c.rotate(-.3)
const tusk=()=>{c.beginPath();c.moveTo(0,-5);c.lineTo(70,-1.5);c.lineTo(70,1.5);c.lineTo(0,5);c.closePath()}
c.lineWidth=2;tusk();c.fill();c.stroke();c.clip()
c.strokeStyle='#fff';for(let i=-1;i<7;i++){const x=6+(i+t)*10,h=5-3.5*x/70;c.beginPath();c.moveTo(x-h*.8,-h);c.lineTo(x+h*.8,h);c.stroke()}
c.restore()
c.fillStyle=c.strokeStyle='#fff'
for(const[x,y,r]of[[86,112,2.2],[102,104,2.6],[120,101,2.2],[138,103,2.6],[110,114,2]])c.beginPath(),c.arc(x,y,r,0,T),c.fill()
if(bl)c.lineWidth=3.5,c.beginPath(),c.arc(170,122,5.5,.15*PI,.85*PI),c.stroke()
else c.beginPath(),c.arc(170,124,5.5,0,T),c.fill()
c.lineWidth=3;c.beginPath();c.moveTo(189,144);c.quadraticCurveTo(182,150,174,148);c.stroke()
}
+20
View File
@@ -0,0 +1,20 @@
// loch ness monster gliding through the water: head on a long neck and two humps bob above a wavy water line
const{sin,cos,PI}=Math,T=2*PI
export const frames=24,delay=85,rim=3
export default(c,t)=>{
const wy=x=>180+3.5*sin(x*.075+T*t),bl=t>.6&&t<.66,hb=4*sin(T*t-.5)
c.fillStyle=c.strokeStyle='#111';c.lineWidth=6
c.save();c.beginPath();c.moveTo(0,0);c.lineTo(256,0);for(let x=256;x>=0;x-=4)c.lineTo(x,wy(x)+1);c.closePath();c.clip()
c.lineWidth=22;c.beginPath();c.moveTo(176,196);c.bezierCurveTo(168,140,184,104,198,88+hb);c.stroke()
c.lineWidth=6
c.save();c.translate(204,84+hb);c.rotate(.12+.05*sin(T*t-1.5));c.beginPath();c.ellipse(6,0,24,14,0,0,T);c.fill();c.stroke()
c.fillStyle=c.strokeStyle='#fff'
if(bl)c.lineWidth=3,c.beginPath(),c.arc(10,-5,4.5,.15*PI,.85*PI),c.stroke()
else c.beginPath(),c.arc(10,-4,4.5,0,T),c.fill()
c.lineWidth=3;c.beginPath();c.moveTo(27,4);c.quadraticCurveTo(22,8,16,7);c.stroke()
c.restore()
for(const[x,rx,ry,p]of[[122,32,28,1],[68,24,20,2.2]])c.beginPath(),c.ellipse(x,184,rx,ry+4*sin(T*t-p),0,0,T),c.fill(),c.stroke()
c.beginPath();c.moveTo(42,186);c.quadraticCurveTo(28,176,22,158+3*sin(T*t-3));c.quadraticCurveTo(34,166,52,186);c.fill();c.stroke()
c.restore()
c.lineWidth=4;c.beginPath();for(let x=16;x<=240;x+=4)c.lineTo(x,wy(x));c.stroke()
}
+28
View File
@@ -0,0 +1,28 @@
// phoenix rising: big wingbeats lift it, the flame crest and tail feathers flicker
const{sin,PI}=Math,T=2*PI
export const frames=24,delay=70,rim=3
export default(c,t)=>{
const f=sin(T*t)
c.fillStyle=c.strokeStyle='#111';c.lineWidth=6
const shape=(x,y,r,g)=>{c.save();c.translate(x,y);c.rotate(r);c.beginPath();g();c.fill();c.stroke();c.restore()}
c.translate(0,6*f)
// tail: three flame tongues, the side ones curling outward, all flickering
for(const[i,a]of[-.5,0,.5].entries()){
const L=[70,90,70][i]+6*sin(T*t*2+i*1.3),s=5*sin(T*t*2+i*2),o=[1,0,-1][i]*10
shape(128,132,a,()=>{c.moveTo(-12,0);c.bezierCurveTo(-24,L*.4,-6+o,L*.62,o+s,L);c.quadraticCurveTo(2+o/2,L*.78,4,L*.62)
c.quadraticCurveTo(10,L*.75,8+s/2,L*.86);c.bezierCurveTo(20,L*.6,16,L*.3,12,0)})
}
for(const d of[-1,1]){
c.save();c.translate(128+d*12,96);c.scale(d*.88,.88);c.rotate(-.55*f);c.beginPath()
c.moveTo(0,-10);c.quadraticCurveTo(46,-52,100,-46);c.lineTo(80,-26);c.lineTo(88,-12);c.lineTo(66,-8);c.lineTo(70,8)
c.lineTo(48,4);c.lineTo(48,18);c.lineTo(28,10);c.lineTo(4,14);c.closePath();c.fill();c.stroke();c.restore()
}
shape(0,0,0,()=>c.ellipse(128,104,22,36,0,0,T))
shape(0,0,0,()=>c.arc(128,62,17,0,T))
shape(0,0,0,()=>{c.moveTo(140,56);c.quadraticCurveTo(156,56,158,72);c.quadraticCurveTo(150,66,140,70)})
for(const[j,a]of[-.6,-.25,.1].entries()){
const L=[22,28,20][j]+3*sin(T*t*2+j*2)
shape(118+j*6,50,a+.08*sin(T*t*2+j),()=>{c.moveTo(-5,0);c.quadraticCurveTo(-6,-L*.55,0,-L);c.quadraticCurveTo(6,-L*.55,5,0)})
}
c.fillStyle='#fff';c.beginPath();c.arc(134,60,3.8,0,T);c.fill()
}
+29
View File
@@ -0,0 +1,29 @@
// pufferfish: rests, puffs up into a round spiky ball with a surprised look, wobbles, then lets the air out
const{sin,cos,PI,abs,hypot}=Math,T=2*PI
export const frames=24,delay=80,rim=3
const sm=x=>x<0?0:x>1?1:x*x*(3-2*x)
export default(c,t)=>{
const g=sm((t-.1)/.25)-sm((t-.6)/.25),cx=134,cy=128+4*sin(T*t),rx=56+24*g+2*g*sin(T*t*4),ry=42+38*g,bl=t>.9&&t<.96
c.fillStyle=c.strokeStyle='#111';c.lineWidth=6
const shape=(x,y,r,f)=>{c.save();c.translate(x,y);c.rotate(r);c.beginPath();f();c.fill();c.stroke();c.restore()}
const at=a=>[cx+rx*cos(a),cy+ry*sin(a)]
shape(cx-rx+4,cy,.25*sin(T*t*3),()=>{
c.moveTo(4,-12);c.quadraticCurveTo(-14,-24,-26,-22);c.quadraticCurveTo(-20,0,-26,22);c.quadraticCurveTo(-14,24,4,12)})
for(const d of[-1,1])shape(...at(d*2.15),d*(-2.15+PI/2)+.3*sin(T*t*3+d),()=>{c.moveTo(-9,4);c.quadraticCurveTo(-6,-16,6,-18);c.quadraticCurveTo(10,-6,9,4)})
if(g>.04){
c.lineWidth=3
for(let i=0;i<18;i++){
const a=-PI+(i+.5)*T/18
if(abs(a)<.4||abs(a)>PI-.45||abs(abs(a)-2.15)<.2)continue
const[x,y]=at(a),nx=cos(a)/rx,ny=sin(a)/ry,n=hypot(nx,ny),ux=nx/n,uy=ny/n,l=3+13*g
c.beginPath();c.moveTo(x-uy*5-ux*3,y+ux*5-uy*3);c.lineTo(x+ux*l,y+uy*l);c.lineTo(x+uy*5-ux*3,y-ux*5-uy*3);c.fill();c.stroke()
}
c.lineWidth=6
}
shape(0,0,0,()=>c.ellipse(cx,cy,rx,ry,0,0,T))
const ex=cx+rx*.42,ey=cy-ry*.3
c.fillStyle=c.strokeStyle='#fff'
if(bl)c.lineWidth=3.5,c.beginPath(),c.arc(ex,ey-2,6,.15*PI,.85*PI),c.stroke()
else{c.beginPath();c.arc(ex,ey,6+3*g,0,T);c.fill();if(g>.3)c.fillStyle='#111',c.beginPath(),c.arc(ex+1,ey,3.5*g,0,T),c.fill()}
c.strokeStyle='#fff';c.lineWidth=3;c.beginPath();c.arc(cx+rx-10,cy+ry*.15,3.5+1.5*g,0,T);c.stroke()
}
+27
View File
@@ -0,0 +1,27 @@
// puffin with a fish in its beak: the head bobs, the fish dangles and flaps its tail, striped beak, white face mask and belly, a blink
const{sin,PI}=Math,T=2*PI
export const frames=24,delay=85,rim=3
export default(c,t)=>{
const a=.07*sin(T*t*2),sw=.15*sin(T*t*2-1.2),fl=.4*sin(T*t*4),bl=t>.6&&t<.67
c.fillStyle=c.strokeStyle='#111';c.lineWidth=6
const shape=f=>{c.beginPath();f();c.fill();c.stroke()}
for(const d of[-1,1])shape(()=>{c.moveTo(104+d*16,200);c.lineTo(92+d*16,216);c.lineTo(120+d*16,216);c.closePath()})
shape(()=>c.ellipse(106,150,46,60,-.08,0,T))
c.save();c.translate(116,112);c.rotate(a);c.translate(-116,-112)
shape(()=>c.arc(120,84,32,0,T))
c.save();c.translate(184,100);c.rotate(-.2+sw)
shape(()=>c.ellipse(0,24,11,24,0,0,T))
c.translate(0,46);c.rotate(fl);shape(()=>{c.moveTo(0,-2);c.lineTo(-12,16);c.lineTo(0,10);c.lineTo(12,16);c.closePath()})
c.restore()
shape(()=>{c.moveTo(146,58);c.quadraticCurveTo(186,66,200,98);c.quadraticCurveTo(178,110,148,110);c.closePath()})
c.fillStyle=c.strokeStyle='#fff'
c.save();c.translate(184,100);c.rotate(-.2+sw);c.beginPath();c.arc(-3,16,3,0,T);c.fill();c.restore()
c.lineWidth=4;for(const[x,y0,y1]of[[162,66,106],[176,72,106]])c.beginPath(),c.moveTo(x,y0),c.quadraticCurveTo(x+6,88,x,y1),c.stroke()
c.beginPath();c.moveTo(144,70);c.quadraticCurveTo(146,96,130,100);c.quadraticCurveTo(108,100,96,90);c.quadraticCurveTo(116,70,144,70);c.fill()
c.fillStyle=c.strokeStyle='#111'
if(bl)c.lineWidth=2.5,c.beginPath(),c.arc(132,80,4.5,.15*PI,.85*PI),c.stroke()
else c.beginPath(),c.arc(132,82,4.5,0,T),c.fill()
c.lineWidth=2.5;c.beginPath();c.moveTo(126,84);c.lineTo(112,88);c.stroke()
c.restore()
c.fillStyle='#fff';c.beginPath();c.ellipse(120,164,24,36,-.08,0,T);c.fill()
}
+39
View File
@@ -0,0 +1,39 @@
// raccoon sitting up washing its paws: the two paws circle and rub, a white bandit mask holds its eyes, the ringed tail sways
const{sin,cos,PI}=Math,T=2*PI
export const frames=24,delay=80,rim=3
export default(c,t)=>{
const f=2*T*t,hb=1.5*sin(f),sw=.08*sin(T*t),tw=t>.4&&t<.48?.3:0,bl=t>.75&&t<.81
c.fillStyle=c.strokeStyle='#111';c.lineWidth=6
const shape=(x,y,r,fn)=>{c.save();c.translate(x,y);c.rotate(r);c.beginPath();fn();c.fill();c.stroke();c.restore()}
c.translate(-20,0)
c.save();c.translate(150,200);c.rotate(sw);c.translate(-150,-200)
const P=[];for(let i=0;i<=10;i++){const u=i/10;P.push([(1-u)**2*146+2*(1-u)*u*214+u*u*212,(1-u)**2*204+2*(1-u)*u*206+u*u*128,17-7*u])}
for(const[x,y,r]of P)c.beginPath(),c.arc(x,y,r,0,T),c.fill()
c.strokeStyle='#fff';c.lineWidth=5
for(const i of[4,6,8]){const[x,y,r]=P[i],[x2,y2]=P[i+1],dx=x2-x,dy=y2-y,l=Math.hypot(dx,dy),nx=-dy/l*r*.8,ny=dx/l*r*.8
c.beginPath();c.moveTo(x-nx,y-ny);c.lineTo(x+nx,y+ny);c.stroke()}
c.restore();c.strokeStyle='#111';c.lineWidth=6
for(const d of[-1,1])shape(128+d*22,212,0,()=>c.ellipse(0,0,15,7,0,0,T))
shape(128,184,0,()=>c.ellipse(0,0,50,30,0,0,T))
shape(128,160,0,()=>c.ellipse(0,0,42,40,0,0,T))
c.translate(0,hb)
for(const d of[-1,1])shape(128+d*30,66,d*(.25+tw*(d>0)),()=>{c.moveTo(-12,10);c.lineTo(0,-14);c.lineTo(12,10);c.closePath()})
shape(128,98,0,()=>{c.ellipse(0,0,44,32,0,0,T);c.moveTo(-36,4);c.lineTo(-52,18);c.lineTo(-28,24);c.moveTo(36,4);c.lineTo(52,18);c.lineTo(28,24)})
c.fillStyle=c.strokeStyle='#fff'
for(const d of[-1,1])c.beginPath(),c.moveTo(128+d*24,69),c.lineTo(128+d*30,58),c.lineTo(128+d*36,69),c.closePath(),c.fill()
c.lineWidth=18;c.beginPath();c.moveTo(94,92);c.quadraticCurveTo(128,108,162,92);c.stroke()
c.lineWidth=3;c.beginPath();c.moveTo(120,116);c.quadraticCurveTo(124,121,128,116);c.quadraticCurveTo(132,121,136,116);c.stroke()
c.fillStyle=c.strokeStyle='#111'
for(const d of[-1,1])if(bl)c.lineWidth=3,c.beginPath(),c.arc(128+d*17,95,5,.15*PI,.85*PI),c.stroke()
else c.beginPath(),c.arc(128+d*17,97,5.5,0,T),c.fill()
c.beginPath();c.ellipse(128,108,6,4,0,0,T);c.fill()
c.translate(0,-hb)
// arms reach in from the shoulders, a thin white gap keeps them readable against the body
const pw=[-1,1].map(d=>[128+d*(10-6*cos(f)),146+d*5*sin(f)+hb])
for(const[w,col]of[[16,'#fff'],[9,'#111']])for(const[d,[x,y]]of[[-1,pw[0]],[1,pw[1]]])
c.strokeStyle=col,c.lineWidth=w,c.beginPath(),c.moveTo(128+d*34,138),c.quadraticCurveTo(128+d*30,154,x,y),c.stroke()
for(const[x,y]of pw){
c.strokeStyle='#fff';c.lineWidth=11;c.beginPath();c.ellipse(x,y,8,7,0,0,T);c.stroke()
c.strokeStyle='#111';c.lineWidth=6;c.beginPath();c.ellipse(x,y,8,7,0,0,T);c.fill();c.stroke()
}
}
+25
View File
@@ -0,0 +1,25 @@
// seahorse standing upright in the current: sways side to side, its dorsal fin flutters, tail curled under, blinks
const{sin,cos,PI}=Math,T=2*PI
export const frames=24,delay=85,rim=3
export default(c,t)=>{
const s=sin(T*t),bl=t>.78&&t<.84,f=1+.18*sin(T*t*4)
c.fillStyle=c.strokeStyle='#111';c.lineWidth=6
c.translate(140,220);c.rotate(.07*s);c.translate(-140,-220+3*sin(T*t*2))
const shape=(x,y,r,f)=>{c.save();c.translate(x,y);c.rotate(r);c.beginPath();f();c.fill();c.stroke();c.restore()}
shape(104,128,0,()=>c.ellipse(0,0,15*f,18,0,PI/2,3*PI/2))
shape(0,0,0,()=>{
c.moveTo(118,52);c.bezierCurveTo(104,74,98,96,104,122);c.bezierCurveTo(110,150,118,166,128,182);c.lineTo(146,180)
c.bezierCurveTo(168,160,172,128,160,106);c.bezierCurveTo(152,92,150,80,152,70)})
shape(136,58,0,()=>c.arc(0,0,20,0,T))
shape(136,40,0,()=>{c.moveTo(-8,4);c.lineTo(-2,-10);c.lineTo(8,2)})
c.lineWidth=11;c.beginPath();c.moveTo(150,62);c.lineTo(176,70);c.stroke()
c.beginPath();c.arc(178,70,6.5,0,T);c.fill()
// tail: a tapering stroke going down and curling forward
const pts=[]
for(let i=0;i<=8;i++){const u=i/8;pts.push([137-3*u,178+26*u,17-4*u])}
for(let i=1;i<=32;i++){const u=i/32,a=PI-1.55*PI*u,r=16-11*u;pts.push([150+r*cos(a),206+r*sin(a),13-8*u])}
for(let i=1;i<pts.length;i++){c.lineWidth=pts[i][2];c.beginPath();c.moveTo(pts[i-1][0],pts[i-1][1]);c.lineTo(pts[i][0],pts[i][1]);c.stroke()}
c.fillStyle=c.strokeStyle='#fff'
if(bl)c.lineWidth=3,c.beginPath(),c.arc(138,55,5,.15*PI,.85*PI),c.stroke()
else c.beginPath(),c.arc(138,56,5,0,T),c.fill()
}
+29
View File
@@ -0,0 +1,29 @@
// frog wearing a mushroom cap with white spots: crouches, hops, lands with a squash; the cap gets pushed down on the way up
const{sin,cos,PI,max,min}=Math,T=2*PI
export const frames=24,delay=70,rim=3
export default(c,t)=>{
const air=t>.15&&t<.6,u=(t-.15)/.45,h=air?44*sin(PI*u):0,v=air?cos(PI*u):0,e=air?min(1,1.6*sin(PI*u)):0
const sq=t<.15?.12*sin(PI*t/.15):t>.6&&t<.78?.14*sin(PI*(t-.6)/.18):0,bl=t>.86&&t<.92
c.fillStyle=c.strokeStyle='#111';c.lineWidth=6
c.translate(128,218);c.scale(1.18,1.18);c.translate(-128,-218-h)
const cx=128,by=218,X=x=>cx+(x-cx)*(1+sq),Y=y=>by-(by-y)*(1-sq),shape=g=>{c.beginPath();g();c.fill();c.stroke()}
for(const d of[-1,1]){
const lx=X(cx+d*(46-8*e)),ly=Y(by-12)+20*e,ry=12+16*e,a=-d*.5*e
shape(()=>c.ellipse(lx,ly,20-11*e,ry,a,0,T))
if(e>.05)shape(()=>c.ellipse(lx+ry*d*sin(.5*e),ly+ry*cos(.5*e),12*e,6*e,0,0,T))
shape(()=>c.ellipse(X(cx+d*22),by-4+8*e,12,6,0,0,T))
}
shape(()=>c.ellipse(cx,Y(by-34),54*(1+sq),34*(1-sq),0,0,T))
for(const d of[-1,1])shape(()=>c.arc(X(cx+d*30),Y(by-58),14,0,T))
c.save();c.translate(cx,Y(by-70)+5*max(0,v));c.rotate(.06*v)
shape(()=>{c.moveTo(-62,4);c.bezierCurveTo(-60,-52,60,-52,62,4);c.quadraticCurveTo(0,-8,-62,4)})
c.fillStyle='#fff';for(const[x,y,r]of[[-30,-20,8],[8,-28,6],[34,-14,7],[-6,-10,4]])c.beginPath(),c.arc(x,y,r,0,T),c.fill()
c.restore()
c.fillStyle=c.strokeStyle='#fff'
for(const d of[-1,1]){
const x=X(cx+d*30),y=Y(by-56)
if(bl)c.lineWidth=3.5,c.beginPath(),c.arc(x,y-3,7,.15*PI,.85*PI),c.stroke()
else c.beginPath(),c.arc(x,y,8.5,0,T),c.fill(),c.fillStyle='#111',c.beginPath(),c.arc(x+d*1.5,y+1,4.5,0,T),c.fill(),c.fillStyle='#fff'
}
c.lineWidth=3.5;c.beginPath();c.moveTo(X(cx-26),Y(by-36));c.quadraticCurveTo(cx,Y(by-24),X(cx+26),Y(by-36));c.stroke()
}
+28
View File
@@ -0,0 +1,28 @@
// skunk standing with its big fluffy tail up: the tail swishes slowly, a white stripe runs from its nose to the tail tip
const{sin,PI}=Math,T=2*PI
export const frames=24,delay=85,rim=3
export default(c,t)=>{
const sw=.12*sin(T*t),tip=.1*sin(T*t-1.2),br=sin(T*t),bl=t>.7&&t<.76
c.fillStyle=c.strokeStyle='#111';c.lineWidth=6
const shape=(x,y,r,f)=>{c.save();c.translate(x,y);c.rotate(r);c.beginPath();f();c.fill();c.stroke();c.restore()}
c.translate(8,0);c.lineWidth=12
for(const[x0,x1]of[[106,104],[120,122],[158,156],[172,174]])c.beginPath(),c.moveTo(x0,180),c.lineTo(x1,206),c.stroke()
c.lineWidth=6
c.save();c.translate(100,154);c.rotate(sw);c.translate(-100,-154)
// fluffy plume: a chain of overlapping circles along a curve, fat in the middle, scalloped edges for free
const B=(u,a,b,d,e)=>(1-u)**3*a+3*(1-u)**2*u*b+3*(1-u)*u*u*d+u**3*e,P=[]
for(let i=0;i<=12;i++){const u=i/12,cu=tip*u*u;P.push([B(u,102,50,52,120)+cu*40,B(u,152,140,40,52)+cu*20,10+20*sin(PI*Math.min(1,u*1.15))+4])}
for(const[x,y,r]of P)c.beginPath(),c.arc(x,y,r,0,T),c.fill()
c.strokeStyle='#fff';c.lineWidth=6;c.beginPath();c.moveTo(100,150)
for(const[x,y]of P.slice(1,11))c.lineTo(x,y)
c.stroke()
c.restore();c.strokeStyle='#111'
shape(138,166,0,()=>c.ellipse(0,br*.5,44,28+br*.5,0,0,T))
shape(172,142,0,()=>c.arc(0,0,7,0,T))
shape(182,160,0,()=>c.ellipse(0,0,22,19,0,0,T))
shape(200,168,.3,()=>c.ellipse(0,0,12,9,0,0,T))
c.fillStyle=c.strokeStyle='#fff'
c.lineWidth=6;c.beginPath();c.moveTo(196,154);c.quadraticCurveTo(170,148,150,152);c.quadraticCurveTo(122,152,100,150);c.stroke()
if(bl)c.lineWidth=3,c.beginPath(),c.arc(188,158,3.5,.15*PI,.85*PI),c.stroke()
else c.beginPath(),c.arc(188,160,3.2,0,T),c.fill()
}
+28
View File
@@ -0,0 +1,28 @@
// deer whose antlers are bare branches: leaves sprout at the tips, then wither and drop off; it flicks an ear and its tail and blinks
const{sin,PI,max,min,atan2,cos}=Math,T=2*PI
export const frames=24,delay=90,rim=3
const br=[[[182,70],[180,48],[174,30],[170,14]],[[180,48],[198,38]],[[174,30],[190,20]],[[172,72],[156,58],[136,52]],[[156,58],[146,38]]]
const pulse=(t,a,b)=>t>a&&t<b?sin(PI*(t-a)/(b-a)):0
export default(c,t)=>{
const bl=t>.86&&t<.92
c.fillStyle=c.strokeStyle='#111'
c.translate(-6,4)
const shape=g=>{c.beginPath();g();c.fill();c.stroke()}
c.lineWidth=9;for(const x of[70,86,146,160])c.beginPath(),c.moveTo(x,160),c.lineTo(x+(x<100?-3:3),222),c.stroke()
c.lineWidth=6
shape(()=>c.ellipse(112,148,56,26+sin(T*t),0,0,T))
c.save();c.translate(62,134);c.rotate(-.5-.6*pulse(t,.74,.84));shape(()=>c.ellipse(-8,0,11,6,0,0,T));c.restore()
shape(()=>{c.moveTo(144,132);c.lineTo(164,84);c.lineTo(186,90);c.lineTo(168,150);c.closePath()})
shape(()=>c.ellipse(180,82,20,14,.35,0,T))
shape(()=>c.ellipse(198,94,11,8,.35,0,T))
c.save();c.translate(170,76);c.rotate(.35-.5*pulse(t,.6,.68));shape(()=>c.ellipse(-12,0,13,6,0,0,T));c.restore()
c.lineWidth=5;for(const s of br){c.beginPath();c.moveTo(...s[0]);for(const p of s.slice(1))c.lineTo(...p);c.stroke()}
br.forEach((s,i)=>{
const[x,y]=s.at(-1),[px,py]=s.at(-2),a=atan2(y-py,x-px)
const g=min(1,max(0,(t-i*.04)/.2)),q=min(1,max(0,(t-.5-i*.04)/.26)),z=g*(1-q)
if(z>.02){c.save();c.translate(x+cos(a)*4-10*q,y+sin(a)*4+36*q*q);c.rotate(a+q*2.5);c.scale(z,z);c.beginPath();c.ellipse(7,0,10,5.5,0,0,T);c.fill();c.restore()}
})
c.fillStyle=c.strokeStyle='#fff'
if(bl)c.lineWidth=3,c.beginPath(),c.arc(187,78,4,.15*PI,.85*PI),c.stroke()
else c.beginPath(),c.arc(187,80,3.5,0,T),c.fill()
}
+28
View File
@@ -0,0 +1,28 @@
// squid: squeezes its mantle, shoots upward with arms trailing, then drifts back down with arms fanning open
const{sin,cos,PI}=Math,T=2*PI
export const frames=24,delay=80,rim=3
const sm=x=>x<0?0:x>1?1:x*x*(3-2*x)
export default(c,t)=>{
const sq=t<.15?sm(t/.15):t<.3?1-sm((t-.15)/.15):0
const Y=t<.15?4*sm(t/.15):t<.35?4-30*(1-(1-(t-.15)/.2)**2):-26+26*sm((t-.35)/.65)
const s=t<.15?1-sm(t/.15):t<.4?0:sm((t-.4)/.4),cy=146+Y,w=44*(1-.2*sq)
c.fillStyle=c.strokeStyle='#111';c.lineWidth=6
const limb=(bx,a,L,w0,w1,bend,ph)=>{
let x=bx,y=cy+12,px,py
for(let i=1;i<=14;i++){const u=i/14,an=a+bend*sin(T*t*2+ph+u*3)*u;px=x;py=y;x+=sin(an)*L/14;y+=cos(an)*L/14
c.lineWidth=w0+(w1-w0)*u;c.beginPath();c.moveTo(px,py);c.lineTo(x,y);c.stroke()}
return[x,y]
}
for(const d of[-1,1]){
const[x,y]=limb(128+d*11,d*(.1+.25*s),60,7,3,.25*s,d)
c.beginPath();c.ellipse(x,y,6,8,-d*(.08+.25*s),0,T);c.fill()
}
for(let i=0;i<5;i++)limb(128+(i-2)*9,(i-2)*.2*s,40,12,3,.35*s,i*1.3)
c.lineWidth=6
c.beginPath();c.ellipse(128,cy+4,32*(1-.1*sq),18,0,0,T);c.fill();c.stroke()
const tip=cy-90-6*sq,fl=4*sin(T*t*3)
for(const d of[-1,1])c.beginPath(),c.moveTo(128+d*4,tip+6),c.lineTo(128+d*(48+fl),cy-60),c.lineTo(128+d*w*.8,cy-46),c.fill(),c.stroke()
c.beginPath();c.moveTo(128,tip);c.bezierCurveTo(128+w*.8,tip+16,128+w,cy-40,128+w*.85,cy-6);c.quadraticCurveTo(128,cy+2,128-w*.85,cy-6)
c.bezierCurveTo(128-w,cy-40,128-w*.8,tip+16,128,tip);c.fill();c.stroke()
c.fillStyle='#fff';for(const d of[-1,1])c.beginPath(),c.arc(128+d*16,cy+3,6.5,0,T),c.fill()
}
+18
View File
@@ -0,0 +1,18 @@
// starfish sitting on its two bottom arms, waving hello with its upper right arm, smiling and blinking
const{sin,cos,PI}=Math,T=2*PI
export const frames=24,delay=80,rim=3
export default(c,t)=>{
const cx=128,cy=134+2*sin(T*t*2),bl=t>.7&&t<.76,wv=.5+.5*sin(T*t*2)
c.fillStyle='#111'
const arm=(a,bend)=>{
let x=cx,y=cy
for(let i=0;i<=40;i++){const u=i/40;x+=cos(a+bend*u)*78/40;y+=sin(a+bend*u)*78/40;c.beginPath();c.arc(x,y,21-11*u,0,T);c.fill()}
}
arm(-PI/2,.06*sin(T*t));arm(-PI*.9,.08*sin(T*t+1));arm(PI*.7,.04*sin(T*t+2));arm(PI*.3,-.04*sin(T*t+2))
arm(-.31-.32*wv,-.55*sin(T*t*2-.9))
c.beginPath();c.arc(cx,cy,34,0,T);c.fill()
c.fillStyle=c.strokeStyle='#fff';c.lineWidth=3;c.lineCap='round'
for(const d of[-1,1])if(bl)c.lineWidth=3.5,c.beginPath(),c.arc(cx+d*13,cy-9,5,.15*PI,.85*PI),c.stroke()
else c.beginPath(),c.arc(cx+d*13,cy-7,5,0,T),c.fill()
c.lineWidth=3;c.beginPath();c.arc(cx,cy+3,9,.2*PI,.8*PI);c.stroke()
}
+22
View File
@@ -0,0 +1,22 @@
// swan gliding on still water: the neck sways and the head dips, ripples slide back under it, a blink
const{sin,cos,PI}=Math,T=2*PI
export const frames=24,delay=90,rim=3
export default(c,t)=>{
const s=sin(T*t),hx=172+5*s,hy=70+4*cos(T*t),bl=t>.55&&t<.62
c.fillStyle=c.strokeStyle='#111';c.lineWidth=6
const shape=f=>{c.beginPath();f();c.fill();c.stroke()}
c.translate(128,128);c.scale(1.1,1.1);c.translate(-128,-128)
shape(()=>{c.moveTo(42,116+2*s);c.quadraticCurveTo(62,142,94,140);c.bezierCurveTo(108,110,158,106,178,132)
c.bezierCurveTo(204,140,202,180,170,180);c.lineTo(80,180);c.bezierCurveTo(56,180,42,158,42,116+2*s)})
c.lineWidth=18;c.beginPath();c.moveTo(180,152);c.bezierCurveTo(206,126,hx-12,104,hx,hy+8);c.stroke()
c.lineWidth=6;shape(()=>c.arc(hx+4,hy,13,0,T))
shape(()=>{c.moveTo(hx+12,hy-6);c.lineTo(hx+34,hy+8);c.lineTo(hx+12,hy+8);c.closePath()})
c.lineWidth=4;c.beginPath();c.moveTo(26,180);c.lineTo(230,180);c.stroke()
for(let k=0;k<3;k++){
const f=(t+k/3)%1,x=176-150*f,y=194+10*(k%2),l=22*sin(PI*f)
if(l>1)c.beginPath(),c.moveTo(x-l,y),c.lineTo(x+l,y),c.stroke()
}
c.fillStyle=c.strokeStyle='#fff'
if(bl)c.lineWidth=2.5,c.beginPath(),c.arc(hx+7,hy-4,3.5,.15*PI,.85*PI),c.stroke()
else c.beginPath(),c.arc(hx+7,hy-3,3.5,0,T),c.fill()
}
+30
View File
@@ -0,0 +1,30 @@
// teapot dragon: the spout is its neck and head, the handle is its arrow-tipped tail; steam puffs from its snout and the lid rattles up
const{sin,PI,min}=Math,T=2*PI
export const frames=24,delay=85,rim=3
export default(c,t)=>{
const lh=t>.4&&t<.6?sin(PI*(t-.4)/.2):0,bl=t>.82&&t<.88
c.fillStyle=c.strokeStyle='#111';c.lineWidth=6
const shape=g=>{c.beginPath();g();c.fill();c.stroke()}
for(let i=0;i<2;i++){
const u=(t+i/2)%1,r=(3+9*u)*min(1,(1-u)*4)
if(r>.5)c.beginPath(),c.arc(226-14*u+4*sin(T*u),84-58*u,r,0,T),c.fill()
}
c.translate(126,200);c.rotate(.03*sin(T*t));c.translate(-126,-200)
for(const x of[92,150])shape(()=>c.ellipse(x,194,14,8,0,0,T))
c.lineWidth=14;c.beginPath();c.moveTo(74,172);c.bezierCurveTo(26,180,20,118,64,112);c.stroke();c.lineWidth=6
shape(()=>{c.moveTo(56,118);c.lineTo(76,96);c.lineTo(80,122);c.closePath()})
shape(()=>{c.moveTo(166,178);c.bezierCurveTo(198,170,210,140,204,104);c.lineTo(188,100);c.bezierCurveTo(188,122,182,132,170,132);c.closePath()})
shape(()=>c.ellipse(120,148,60,44,0,0,T))
c.save();c.translate(200,94);c.rotate(-.15+.05*sin(2*T*t))
shape(()=>{c.moveTo(-4,-10);c.lineTo(-18,-30);c.lineTo(6,-12);c.closePath()})
shape(()=>c.ellipse(4,0,18,13,0,0,T));shape(()=>c.ellipse(22,4,12,9,0,0,T))
c.fillStyle=c.strokeStyle='#fff'
if(bl)c.lineWidth=3,c.beginPath(),c.arc(6,-4,4.5,.15*PI,.85*PI),c.stroke()
else c.beginPath(),c.arc(6,-3,4,0,T),c.fill()
c.beginPath();c.arc(28,0,2,0,T);c.fill();c.restore()
c.save();c.translate(120,106-12*lh);c.rotate(.18*lh)
shape(()=>{c.ellipse(0,0,30,14,0,PI,0);c.closePath()});shape(()=>c.arc(0,-16,7,0,T));c.restore()
c.strokeStyle='#fff';c.lineWidth=3.5;c.beginPath()
for(let x=72;x<=168;x+=4)c.lineTo(x,146+4*sin((x-72)/8))
c.stroke()
}
+29
View File
@@ -0,0 +1,29 @@
// turtle that carries a little cottage as its shell: it plods in place, the chimney puffs smoke, window and door cut out in white
const{sin,PI,max,min}=Math,T=2*PI
export const frames=24,delay=90,rim=3
export default(c,t)=>{
const b=1.5*sin(2*T*t),bl=t>.8&&t<.86
c.fillStyle=c.strokeStyle='#111';c.lineWidth=6
c.translate(126,136);c.scale(1.1,1.1);c.translate(-128,-136)
const shape=g=>{c.beginPath();g();c.fill();c.stroke()}
for(const[x,p]of[[84,0],[100,PI],[140,PI],[156,0]]){const s=sin(T*t+p);shape(()=>c.roundRect(x-8+5*s,170+b,16,26-6*max(0,s)-b,7))}
c.translate(0,b)
for(let i=0;i<2;i++){
const u=(t+i/2)%1,r=(4+10*u)*min(1,(1-u)*5)
if(r>.5)c.beginPath(),c.arc(152+16*u+3*sin(T*u),68-50*u,r,0,T),c.fill()
}
shape(()=>{c.moveTo(62,168);c.lineTo(44,176);c.lineTo(64,178)})
c.lineWidth=18;c.beginPath();c.moveTo(166,168);c.quadraticCurveTo(184,166,192,152);c.stroke();c.lineWidth=6
c.save();c.translate(196,148);c.rotate(.08*sin(2*T*t));shape(()=>c.ellipse(8,-2,19,14,-.15,0,T))
c.fillStyle=c.strokeStyle='#fff'
if(bl)c.lineWidth=3,c.beginPath(),c.arc(14,-7,4,.15*PI,.85*PI),c.stroke()
else c.beginPath(),c.arc(14,-6,3.5,0,T),c.fill()
c.restore()
shape(()=>c.ellipse(120,174,64,12,0,0,T))
shape(()=>c.rect(74,114,92,56))
shape(()=>{c.moveTo(62,118);c.lineTo(120,72);c.lineTo(178,118);c.closePath()})
shape(()=>c.rect(144,74,14,26))
c.fillStyle='#fff'
for(const[x,y]of[[86,126],[99,126],[86,138],[99,138]])c.fillRect(x,y,10,9)
c.beginPath();c.moveTo(130,164);c.lineTo(130,146);c.arc(140,146,10,PI,0);c.lineTo(150,164);c.fill()
}
+17
View File
@@ -0,0 +1,17 @@
// will-o-wisp floating: a round flame spirit bobbing and tilting, its flame tips flicker, embers drift up beside it, it blinks
const{sin,PI}=Math,T=2*PI
export const frames=24,delay=80,rim=3
export default(c,t)=>{
const bl=t>.4&&t<.46,Mx=128+6*sin(T*t*2),My=40+4*sin(T*t*3),R=4*sin(T*t*2+1),L=4*sin(T*t*2+2.5)
c.fillStyle=c.strokeStyle='#111';c.lineWidth=6
for(const[i,x]of[-46,40,-30].entries()){const q=(t+i/3)%1;c.beginPath();c.arc(128+x+5*sin(T*q),110-74*q,5*sin(PI*q),0,T);c.fill()}
c.translate(128,132+8*sin(T*t));c.rotate(.05*sin(T*t+1));c.scale(1.15,1.15);c.translate(-128,-128)
c.beginPath();c.moveTo(82,148);c.arc(128,148,46,PI,0,true)
c.bezierCurveTo(176,122,176,96,170+R,74);c.quadraticCurveTo(158,84,150,94)
c.bezierCurveTo(152,72,140,54,Mx,My);c.bezierCurveTo(116,56,104,74,106,92)
c.quadraticCurveTo(98,86,88+L,80);c.bezierCurveTo(80,100,80,124,82,148);c.fill();c.stroke()
c.fillStyle=c.strokeStyle='#fff'
for(const d of[-1,1])if(bl)c.lineWidth=3.5,c.beginPath(),c.arc(128+d*16,136,7,.15*PI,.85*PI),c.stroke()
else c.beginPath(),c.ellipse(128+d*16,138,7,10,0,0,T),c.fill()
c.lineWidth=3.5;c.beginPath();c.arc(128,156,7,.2*PI,.8*PI);c.stroke()
}
+30
View File
@@ -0,0 +1,30 @@
// woodpecker on a trunk: four quick pecks with wood chips flying off, then it leans back, looks around and blinks
const{sin,PI}=Math,T=2*PI
export const frames=24,delay=80,rim=3
export default(c,t)=>{
const a=t<.5?-.13*sin(PI*8*t)**2:.04*sin(PI*(t-.5)/.5),bl=t>.76&&t<.84
c.fillStyle=c.strokeStyle='#111';c.lineWidth=6
const shape=f=>{c.beginPath();f();c.fill();c.stroke()}
c.translate(38,0)
shape(()=>c.roundRect(40,16,40,224,20))
c.lineWidth=7;c.beginPath();c.moveTo(44,72);c.lineTo(22,54);c.stroke()
c.lineWidth=6;shape(()=>{c.moveTo(22,54);c.quadraticCurveTo(10,30,22,18);c.quadraticCurveTo(34,40,22,54)})
for(let k=0;k<4;k++)for(const j of[0,1]){
const q=(t-(k+.5)/8)/.2
if(q>0&&q<1){
const x=84+(12+14*j)*q,y=84-(130-30*j)*q+70*q*q,r=6*(1-.6*q)
c.save();c.translate(x,y);c.rotate(6*q+j);c.fillRect(-r,-r*.6,2*r,1.2*r);c.restore()
}
}
c.save();c.translate(86,206);c.rotate(a);c.translate(-86,-206)
shape(()=>{c.moveTo(88,188);c.lineTo(102,196);c.lineTo(84,234);c.closePath()})
c.save();c.translate(106,146);c.rotate(.3);shape(()=>c.ellipse(0,0,26,54,0,0,T));c.restore()
shape(()=>c.arc(132,84,20,0,T))
shape(()=>{c.moveTo(134,64);c.lineTo(160,70);c.lineTo(148,88);c.closePath()})
shape(()=>{c.moveTo(114,77);c.lineTo(114,91);c.lineTo(96,85);c.closePath()})
c.fillStyle=c.strokeStyle='#fff'
if(bl)c.lineWidth=2.5,c.beginPath(),c.arc(134,79,4,.15*PI,.85*PI),c.stroke()
else c.beginPath(),c.arc(134,81,4.5,0,T),c.fill()
for(const[x,y]of[[120,130],[124,148],[120,166]])c.beginPath(),c.arc(x,y,3.5,0,T),c.fill()
c.restore()
}
+18
View File
@@ -0,0 +1,18 @@
// yeti waving hello: shaggy spiky fur all round, one arm waving, a big open smile and a blink
const{sin,cos,PI}=Math,T=2*PI
export const frames=24,delay=80,rim=3
export default(c,t)=>{
const bl=t>.6&&t<.66,br=sin(T*t),a=-2.65+.28*sin(T*t*2)
c.fillStyle=c.strokeStyle='#111';c.lineWidth=6
const fur=(x,y,r,oy,rx,ry,n,sp)=>{c.save();c.translate(x,y);c.rotate(r);c.beginPath()
for(let k=0;k<2*n;k++){const b=k*PI/n,q=k%2?1+sp:1;c.lineTo(rx*q*cos(b),oy+ry*q*sin(b))}
c.closePath();c.fill();c.stroke();c.restore()}
for(const x of[104,152])fur(x,206,0,0,20,15,8,.18)
fur(74,122,.35,36,16,40,10,.14)
fur(172,118,a,30,15,30,10,.14);fur(172,118,a,60,16,16,8,.2)
fur(128,136,0,0,62,74+1.5*br,24,.09)
c.fillStyle=c.strokeStyle='#fff'
for(const d of[-1,1])if(bl)c.lineWidth=3.5,c.beginPath(),c.arc(128+d*20,102,6,.15*PI,.85*PI),c.stroke()
else c.beginPath(),c.arc(128+d*20,104,6.5,0,T),c.fill()
c.beginPath();c.ellipse(128,124,17,12,0,0,PI);c.closePath();c.fill()
}
+21
View File
@@ -0,0 +1,21 @@
// baku asleep: slow breaths, a dream bubble above it with a star slowly turning and twinkling inside
const{sin,cos,PI}=Math,T=2*PI
export const frames=24,delay=90
const B='#b48cff',L='#dac6ff',S='#4ea8de',Y='#f4c03e',D='#2b2633'
export default(c,t)=>{
const br=sin(T*t),ry=38*(1+.04*br)
const fill=(col,g)=>{c.fillStyle=c.strokeStyle=col;c.lineWidth=6;c.beginPath();g();c.fill();c.stroke()}
for(const[x,y,r]of[[100,104,5],[114,84,8]])fill(S,()=>c.arc(x,y,r*(1+.08*br),0,T))
const R=32*(1+.05*br)
fill(S,()=>c.arc(150,56,R,0,T))
c.save();c.translate(150,56);c.rotate(T*t/5);const k=.85+.15*sin(2*T*t)
fill(Y,()=>{for(let i=0;i<10;i++){const a=i*PI/5-PI/2,r=(i%2?7:16)*k;c.lineTo(r*cos(a),r*sin(a))}c.closePath()});c.restore()
for(const x of[96,176])fill(B,()=>c.ellipse(x,194,12,7,0,0,T))
fill(B,()=>c.ellipse(142,162,66,ry,0,0,T))
c.save();c.beginPath();c.ellipse(142,162,66,ry,0,0,T);c.clip()
fill(L,()=>c.ellipse(186,154,44,60,0,0,T));c.restore()
c.strokeStyle=B;c.lineWidth=18;c.beginPath();c.moveTo(60,152+2*br);c.quadraticCurveTo(38,154+2*br,38,176+3*br);c.stroke()
fill(B,()=>c.arc(76,156+2*br,30,0,T))
fill(B,()=>c.ellipse(88,128+2*br,9,12,.4,0,T))
c.strokeStyle=D;c.lineWidth=3.5;c.beginPath();c.arc(78,150+2*br,6,.15*PI,.85*PI);c.stroke()
}
+26
View File
@@ -0,0 +1,26 @@
// bee-cat hovering: a striped cat body with buzzing wings, its tail curling and swaying, blinking
const{sin,cos,PI}=Math,T=2*PI
export const frames=24,delay=70
const B='#f4c03e',L='#f9e09e',A='#5b4a3f',W='#a7d4ef',D='#2b2633'
export default(c,t)=>{
const fill=(col,f)=>{c.fillStyle=c.strokeStyle=col;c.lineWidth=6;c.beginPath();f();c.fill();c.stroke()}
const wing=(r,s)=>{c.save();c.translate(118,104);c.rotate(r);fill(W,()=>c.ellipse(-4,-30,15*s,30,0,0,T));c.restore()}
const bl=t>.5&&t<.56,bz=sin(T*t*6)
c.translate(128,134+7*sin(T*t));c.rotate(.04*sin(T*t+1));c.translate(-128,-128)
c.lineCap='round';c.strokeStyle=B;c.lineWidth=13;c.beginPath();c.moveTo(70,150)
c.bezierCurveTo(38,160,26,128,40+5*sin(T*t-1),104+4*cos(T*t-1));c.stroke()
wing(-.75+.35*bz,.9)
fill(B,()=>c.ellipse(104,142,56,42,-.1,0,T))
c.save();c.beginPath();c.ellipse(104,142,50,36,-.1,0,T);c.clip();c.translate(104,142);c.rotate(-.1)
c.fillStyle=A;c.fillRect(-26,-50,14,100);c.fillRect(4,-50,14,100);c.restore()
for(const[x,r]of[[150,-.35],[190,.35]]){
c.save();c.translate(x,88);c.rotate(r);fill(B,()=>{c.moveTo(-14,8);c.lineTo(0,-22);c.lineTo(14,8)})
fill(L,()=>{c.moveTo(-5,5);c.lineTo(0,-8);c.lineTo(5,5)});c.restore()
}
fill(B,()=>c.arc(170,120,38,0,T))
wing(-.35+.35*bz,1)
c.fillStyle=c.strokeStyle=D
for(const x of[157,183])if(bl)c.lineWidth=4,c.beginPath(),c.arc(x,114,6,.15*PI,.85*PI),c.stroke()
else c.beginPath(),c.arc(x,116,5.5,0,T),c.fill()
c.lineWidth=3.5;for(const d of[-1,1])c.beginPath(),c.arc(170+d*4.5,126,4.5,.1*PI,.9*PI),c.stroke()
}
+19
View File
@@ -0,0 +1,19 @@
// beluga bobbing: pale grey-blue whale with a bulging melon head that squishes, smiling, tail and flipper paddling
const{sin,cos,PI}=Math,T=2*PI
export const frames=24,delay=85
const B='#9fb8d6',L='#d3dfed',D='#2b2633'
export default(c,t)=>{
const s=sin(T*t),m=4*sin(T*t*2),bl=t>.8&&t<.87,fill=(col,f)=>{c.fillStyle=c.strokeStyle=col;c.lineWidth=6;c.beginPath();f();c.fill();c.stroke()}
c.translate(128,130+6*s);c.rotate(.05*cos(T*t));c.translate(-132,-128)
c.save();c.translate(58,130);c.rotate(.3*sin(T*t-1.3))
fill(B,()=>{c.moveTo(6,-8);c.quadraticCurveTo(-12,-12,-30,-32);c.quadraticCurveTo(-22,-10,-24,0);c.quadraticCurveTo(-22,10,-30,32);c.quadraticCurveTo(-12,12,6,8)})
c.restore()
c.save();c.translate(150,150);c.rotate(-.4-.35*sin(T*t*2));fill(B,()=>c.ellipse(-14,10,20,9,.4,0,T));c.restore()
fill(B,()=>{c.moveTo(56,124);c.bezierCurveTo(88,90,136,78,166,82);c.bezierCurveTo(196+m,82,214+m,104,208,124)
c.quadraticCurveTo(220,128,218,134);c.quadraticCurveTo(206,146,196,144);c.bezierCurveTo(180,166,126,172,96,156);c.quadraticCurveTo(72,144,56,136)})
fill(L,()=>c.ellipse(140,152,40,9,-.05,0,T))
c.fillStyle=c.strokeStyle=D
if(bl)c.lineWidth=3.5,c.beginPath(),c.arc(180,112,5,.15*PI,.85*PI),c.stroke()
else c.beginPath(),c.arc(180,114,5,0,T),c.fill()
c.lineWidth=3;c.lineCap='round';c.beginPath();c.moveTo(214,137);c.quadraticCurveTo(204,145,192,138);c.stroke()
}
+18
View File
@@ -0,0 +1,18 @@
// betta fish swishing: small red body trailing big pink fins whose wavy edges ripple
const{sin,cos,PI}=Math,T=2*PI
export const frames=24,delay=80
const B='#ee5a5a',A='#ff85a8',L='#ffc2d4',D='#2b2633'
export default(c,t)=>{
const s=sin(T*t),sw=.12*sin(T*t-1),fill=(col,f)=>{c.fillStyle=c.strokeStyle=col;c.lineWidth=6;c.beginPath();f();c.fill();c.stroke()}
c.translate(128,128+6*s);c.rotate(.04*cos(T*t));c.translate(-120,-128)
const rim=i=>{const a=PI+1.15-2.3*i/32+sw,r=78+8*sin(i/32*T*1.5-T*t*2);return[114+r*cos(a),128+r*sin(a)]}
fill(A,()=>{c.moveTo(118,112);for(let i=0;i<=32;i++)c.lineTo(...rim(i));c.lineTo(118,144)})
c.strokeStyle=L;c.lineWidth=3;c.lineCap='round'
for(const i of[7,16,25]){const[x,y]=rim(i);c.beginPath();c.moveTo(114+(x-114)*.25,128+(y-128)*.25);c.lineTo(114+(x-114)*.75,128+(y-128)*.75);c.stroke()}
fill(A,()=>{c.moveTo(176,104);c.quadraticCurveTo(160,56,112+4*s,58+6*sin(T*t*2));c.quadraticCurveTo(124,92,130,106)})
fill(A,()=>{c.moveTo(172,142);c.quadraticCurveTo(162,196,110+4*s,196+5*sin(T*t*2+1));c.quadraticCurveTo(122,160,128,140)})
fill(B,()=>c.ellipse(152,124,44,26,0,0,T))
c.save();c.translate(156,136);c.rotate(2.3+.4*sin(T*t*2));fill(A,()=>c.ellipse(10,0,12,6,0,0,T));c.restore()
c.fillStyle=D;c.beginPath();c.arc(182,116,5,0,T);c.fill()
c.strokeStyle=D;c.lineWidth=3;c.beginPath();c.arc(190,126,5,.4*PI,.85*PI);c.stroke()
}
+30
View File
@@ -0,0 +1,30 @@
// bread-loaf corgi standing and wiggling its butt: nub tail wagging, back feet stepping, scored crust on its back
const{sin,cos,PI,max}=Math,T=2*PI
export const frames=24,delay=70
const B='#c9a27e',L='#ead7c3',D='#2b2633'
export default(c,t)=>{
const fill=(col,f)=>{c.fillStyle=c.strokeStyle=col;c.lineWidth=6;c.beginPath();f();c.fill();c.stroke()}
const bl=t>.3&&t<.36
c.translate(-12,4)
for(const x of[158,180])fill(B,()=>c.roundRect(x,180,14,26,6))
c.save();c.translate(186,196);c.rotate(.085*sin(T*t*2));c.translate(-186,-196)
for(const[x,d]of[[66,0],[90,PI]])fill(B,()=>c.roundRect(x,180-5*max(0,sin(T*t*2+d)),14,26,6))
c.save();c.translate(54,124);c.rotate(.6*sin(T*t*4));fill(B,()=>c.ellipse(-8,-4,13,9,-.5,0,T));c.restore()
fill(B,()=>{c.moveTo(60,194);c.lineTo(58,148);c.quadraticCurveTo(46,146,48,130);c.bezierCurveTo(52,96,96,92,126,92)
c.bezierCurveTo(156,92,198,96,202,130);c.quadraticCurveTo(204,146,192,148);c.lineTo(190,194);c.closePath()})
c.strokeStyle=L;c.lineWidth=8;c.lineCap='round'
for(const x of[82,114,146])c.beginPath(),c.moveTo(x,110),c.lineTo(x+16,124),c.stroke()
c.restore()
c.translate(210,150);c.rotate(.04*sin(T*t*2+1));c.translate(-210,-150)
fill(L,()=>c.ellipse(194,164,14,22,.2,0,T))
for(const[x,r]of[[194,-.28],[228,.32]]){
c.save();c.translate(x,94);c.rotate(r);fill(B,()=>{c.moveTo(-15,8);c.lineTo(-3,-34);c.lineTo(15,6)})
fill(L,()=>{c.moveTo(-5,4);c.lineTo(-1,-14);c.lineTo(5,3)});c.restore()
}
fill(B,()=>c.arc(211,122,34,0,T))
fill(L,()=>c.ellipse(229,136,18,13,0,0,T))
c.fillStyle=c.strokeStyle=D
c.beginPath();c.arc(244,130,5.5,0,T);c.fill()
if(bl)c.lineWidth=4,c.beginPath(),c.arc(214,114,6,.15*PI,.85*PI),c.stroke()
else c.beginPath(),c.arc(214,116,5.5,0,T),c.fill()
}
+24
View File
@@ -0,0 +1,24 @@
// bunny-bird hopping: round flat-color body, long ears that lag behind the hop, wings flapping in the air, blinks while resting
const{sin,cos,PI,max}=Math,T=2*PI
export const frames=24,delay=70
const B='#7b8cff',L='#c3cbff',A='#f4c03e',D='#2b2633'
export default(c,t)=>{
const air=t<.5,h=air?50*sin(PI*t/.5):0,v=air?cos(PI*t/.5):0,sq=t>.5&&t<.68?.13*sin(PI*(t-.5)/.18):0
const rx=50*(1+sq),ry=50*(1-sq),cx=128,cy=206-ry-h,bl=t>.82&&t<.88
const fill=(col,f)=>{c.fillStyle=c.strokeStyle=col;c.lineWidth=6;c.beginPath();f();c.fill();c.stroke()}
for(const d of[-1,1])fill(A,()=>c.ellipse(cx+d*16,cy+ry+4+(air?4*v:0),10,6,0,0,T))
for(const d of[-1,1]){
c.save();c.translate(cx+d*16,cy-ry+14);c.rotate(d*(.2+.35*v+.4*sq))
fill(B,()=>c.ellipse(0,-30,16,32,0,0,T));fill(L,()=>c.ellipse(0,-28,6,20,0,0,T));c.restore()
}
for(const d of[-1,1]){
c.save();c.translate(cx+d*(rx-6),cy+2);c.rotate(d*(air?.5+.5*sin(T*t*4):.15))
fill(B,()=>c.ellipse(d*12,6,10,20,-d*.5,0,T));c.restore()
}
fill(B,()=>c.ellipse(cx,cy,rx,ry,0,0,T))
fill(L,()=>c.ellipse(cx,cy+20*(1-sq),30*(1+sq),24*(1-sq),0,0,T))
c.fillStyle=c.strokeStyle=D
for(const d of[-1,1])if(bl)c.lineWidth=4,c.beginPath(),c.arc(cx+d*18,cy-12,6,.15*PI,.85*PI),c.stroke()
else c.beginPath(),c.arc(cx+d*18,cy-10,5.5,0,T),c.fill()
fill(A,()=>{c.moveTo(cx-7,cy-3);c.lineTo(cx+7,cy-3);c.lineTo(cx,cy+6);c.closePath()})
}
+28
View File
@@ -0,0 +1,28 @@
// three-headed puppy sitting, the heads pant one after another with tongues out, tail wagging
const{sin,PI}=Math,T=2*PI
export const frames=24,delay=80
const B='#8a94a6',L='#c5cad3',A='#ff85a8',D='#2b2633'
export default(c,t)=>{
const fill=(col,g)=>{c.fillStyle=c.strokeStyle=col;c.lineWidth=6;c.beginPath();g();c.fill();c.stroke()}
c.translate(0,-12)
c.save();c.translate(168,200);c.rotate(.35*sin(3*T*t))
c.strokeStyle=B;c.lineWidth=14;c.beginPath();c.moveTo(0,0);c.quadraticCurveTo(28,-6,30,-40);c.stroke();c.restore()
fill(B,()=>c.ellipse(128,184,56,36,0,0,T))
fill(L,()=>c.ellipse(128,192,26,22,0,0,T))
for(const d of[-1,1])fill(L,()=>c.ellipse(128+d*26,218,14,8,0,0,T))
for(const[i,x,y,r,a]of[[0,70,140,28,-.26],[2,186,140,28,.26],[1,128,106,31,0]]){
const p=sin(T*(3*t-i/3)),bl=i==1&&t>.6&&t<.66
c.save();c.translate(x,y+2.5*p);c.rotate(a)
for(const d of[-1,1])fill(B,()=>{c.moveTo(d*r*.3,-r*.8);c.lineTo(d*r*.82,-r*1.25);c.lineTo(d*r*.95,-r*.3)})
fill(B,()=>c.arc(0,0,r,0,T))
for(const d of[-1,1])fill(L,()=>{c.moveTo(d*r*.55,-r*.78);c.lineTo(d*r*.8,-r*1.05);c.lineTo(d*r*.82,-r*.62)})
fill(L,()=>c.ellipse(0,r*.36,r*.5,r*.36,0,0,T))
fill(D,()=>c.ellipse(0,r*.5,r*.16,r*.14+1.5*(p+1),0,0,PI))
fill(A,()=>c.roundRect(-r*.15,r*.52,r*.3,r*.34+4*(p+1),r*.15))
fill(D,()=>c.ellipse(0,r*.2,5.5,4,0,0,T))
c.fillStyle=c.strokeStyle=D
for(const d of[-1,1])if(bl)c.lineWidth=3.5,c.beginPath(),c.arc(d*r*.38,-r*.16,5,.15*PI,.85*PI),c.stroke()
else c.beginPath(),c.arc(d*r*.38,-r*.12,4.5,0,T),c.fill()
c.restore()
}
}
+34
View File
@@ -0,0 +1,34 @@
// chipmunk sitting up with an acorn, stuffs it in its mouth and its cheeks balloon out, blinks, then grabs another
const{sin,cos,PI,max,min}=Math,T=2*PI
export const frames=32,delay=70
const B='#e8955a',L='#f8d1b0',A='#8c4f2c',N='#f4c03e',D='#2b2633'
const cl=x=>max(0,min(1,x)),ease=x=>(x=cl(x),x*x*(3-2*x))
export default(c,t)=>{
const lift=ease((t-.26)/.16)-ease((t-.86)/.14)*0,eat=t>.42&&t<.86,ns=t<.42?1:t>.86?ease((t-.86)/.12):0
const puff=ease((t-.42)/.08)*(1+.12*sin(PI*cl((t-.42)/.16)))-ease((t-.86)/.1)
const cr=14+13*puff,nb=t<.26?1.5*sin(T*t*8):0,bl=t>.68&&t<.73
const nx=128,ny=150-30*lift+nb
const fill=(col,f)=>{c.fillStyle=c.strokeStyle=col;c.lineWidth=6;c.beginPath();f();c.fill();c.stroke()}
c.strokeStyle=B;c.lineWidth=26;c.beginPath();c.moveTo(150,200);c.bezierCurveTo(196,204,200,150,186,108+3*sin(T*t));c.stroke()
c.strokeStyle=A;c.lineWidth=7;c.beginPath();c.moveTo(166,200);c.bezierCurveTo(194,196,192,150,184,116+3*sin(T*t));c.stroke()
for(const d of[-1,1])fill(B,()=>c.ellipse(128+d*20,212,16,7,0,0,T))
fill(B,()=>c.ellipse(128,172,42,44,0,0,T))
fill(L,()=>c.ellipse(128,180,25,32,0,0,T))
for(const d of[-1,1])fill(B,()=>c.arc(128+d*29,70,11,0,T)),fill(L,()=>c.arc(128+d*29,70,5,0,T))
fill(B,()=>c.arc(128,102,36,0,T))
for(const x of[-14,0,14])c.strokeStyle=A,c.lineWidth=6,c.beginPath(),c.moveTo(128+x,70+abs(x)*.25),c.lineTo(128+x*.9,88),c.stroke()
for(const d of[-1,1])fill(L,()=>c.arc(128+d*(18+cr*.55),118,cr,0,T))
fill(L,()=>c.ellipse(128,120,10,8,0,0,T))
c.fillStyle=c.strokeStyle=D
for(const d of[-1,1])if(bl)c.lineWidth=4,c.beginPath(),c.arc(128+d*15,98,5,.15*PI,.85*PI),c.stroke()
else c.beginPath(),c.arc(128+d*15,100,5,0,T),c.fill()
c.beginPath();c.ellipse(128,114,4.5,3.5,0,0,T);c.fill()
if(ns>0){
c.save();c.translate(nx,ny);c.scale(ns,ns)
fill(N,()=>c.ellipse(0,5,11,13,0,0,T));fill(A,()=>c.ellipse(0,-7,13,7,0,0,T))
c.strokeStyle=A;c.lineWidth=3;c.beginPath();c.moveTo(0,-11);c.lineTo(2,-16);c.stroke();c.restore()
}
const hy=eat?150:ny+6
for(const d of[-1,1])fill(B,()=>c.ellipse(128+d*(eat?11:17),hy,7,9,d*.3,0,T))
}
const abs=Math.abs
+20
View File
@@ -0,0 +1,20 @@
// clownfish wiggle-swimming: orange body with white bands kept inside an orange edge, tail and fins fluttering
const{sin,PI}=Math,T=2*PI
export const frames=24,delay=70
const B='#ff9f43',L='#ffcfa1',W='#fff',D='#2b2633'
export default(c,t)=>{
const w=sin(T*t*2),fill=(col,f)=>{c.fillStyle=c.strokeStyle=col;c.lineWidth=6;c.beginPath();f();c.fill();c.stroke()}
c.translate(140,128+5*sin(T*t));c.rotate(.05*sin(T*t*2+1));c.scale(1.08,1.08);c.translate(-128,-128)
fill(B,()=>{c.moveTo(92,104);c.quadraticCurveTo(102,70+4*w,130,72);c.quadraticCurveTo(158,74,166,100)})
fill(B,()=>c.ellipse(116,164,22,12,-.3+.1*w,0,T))
c.save();c.translate(70,128);c.rotate(.3*w)
fill(B,()=>{c.moveTo(8,0);c.quadraticCurveTo(-12,-16,-40,-34);c.quadraticCurveTo(-28,0,-40,34);c.quadraticCurveTo(-12,16,8,0)})
c.restore()
fill(B,()=>c.ellipse(130,128,66,44,0,0,T))
c.save();c.beginPath();c.ellipse(130,128,57,35,0,0,T);c.clip()
c.fillStyle=W;for(const[x,r]of[[170,9],[124,12],[80,8]])c.beginPath(),c.ellipse(x,128,r,50,0,0,T),c.fill()
c.restore()
c.save();c.translate(144,142);c.rotate(.5+.4*sin(T*t*2+2));fill(L,()=>c.ellipse(12,0,14,8,0,0,T));c.restore()
c.fillStyle=D;c.beginPath();c.arc(184,118,6,0,T);c.fill()
c.strokeStyle=D;c.lineWidth=3;c.lineCap='round';c.beginPath();c.arc(190,134,6,.35*PI,.8*PI);c.stroke()
}
+25
View File
@@ -0,0 +1,25 @@
// cockatiel bobbing its crest: grey body, yellow head with orange cheeks, head tilting side to side, blinks
const{sin,PI}=Math,T=2*PI
export const frames=24,delay=80
const B='#8a94a6',L='#c5cad3',Y='#f4c03e',O='#ff9f43',D='#2b2633'
export default(c,t)=>{
const s=sin(T*t),tl=.12*s,cr=.22*sin(T*t-1.3)+.12*sin(2*T*t),br=1+.02*sin(2*T*t),bl=t>.42&&t<.48
const fill=(col,f)=>{c.fillStyle=c.strokeStyle=col;c.lineWidth=6;c.beginPath();f();c.fill();c.stroke()}
fill(B,()=>{c.moveTo(116,196);c.lineTo(140,196);c.lineTo(134,238);c.lineTo(122,238);c.closePath()})
for(const d of[-1,1])fill(L,()=>c.ellipse(128+d*18,220,11,6,0,0,T))
fill(B,()=>c.ellipse(128,162,50*br,56,0,0,T))
for(const d of[-1,1])fill(L,()=>c.ellipse(128+d*36,150,8,22,d*.2,0,T))
c.save();c.translate(128,124);c.rotate(tl)
c.strokeStyle=Y;c.lineCap='round'
for(const[a,l,w]of[[-.3,30,8],[0,42,10],[.3,34,8]]){
c.save();c.translate(a*20,-58);c.rotate(a+cr*(1+a));c.lineWidth=w
c.beginPath();c.moveTo(0,4);c.quadraticCurveTo(-8,-l*.7,6,-l);c.stroke();c.restore()
}
fill(Y,()=>c.arc(0,-28,36,0,T))
for(const d of[-1,1])fill(O,()=>c.arc(d*21,-16,9,0,T))
c.fillStyle=c.strokeStyle=D
for(const d of[-1,1])if(bl)c.lineWidth=4,c.beginPath(),c.arc(d*14,-34,5.5,.15*PI,.85*PI),c.stroke()
else c.beginPath(),c.arc(d*14,-32,5.5,0,T),c.fill()
fill(L,()=>{c.moveTo(-7,-22);c.lineTo(7,-22);c.lineTo(0,-11);c.closePath()})
c.restore()
}
+35
View File
@@ -0,0 +1,35 @@
// crocodile lying low: knobbly back, eyes on top of its head, jaws creak open then snap shut, tail swaying
const{sin,cos,PI,min,max}=Math,T=2*PI
export const frames=32,delay=70
const B='#8cc084',L='#c5e0c1',W='#fff',A='#ff85a8',D='#2b2633'
const e=x=>(x=min(1,max(0,x)),x*x*(3-2*x))
export default(c,t)=>{
const j=.42*e((t-.1)/.3)*(1-e((t-.46)/.05))+.2*e((t-.62)/.06)*(1-e((t-.72)/.04)),bl=t>.85&&t<.91
const fill=(col,f)=>{c.fillStyle=c.strokeStyle=col;c.lineWidth=6;c.beginPath();f();c.fill();c.stroke()}
c.translate(128,128+2*sin(T*t));c.scale(.92,.92);c.translate(-121,-150)
c.save();c.translate(70,150);c.rotate(.1*sin(T*t))
fill(B,()=>{c.moveTo(6,-18);c.quadraticCurveTo(-30,-14,-56,-2);c.lineTo(-58,2);c.quadraticCurveTo(-30,6,6,14)})
for(const x of[-12,-28,-42])fill(B,()=>c.arc(x,-12+(x+42)*-.12+2,4.5,0,T))
c.restore()
for(const x of[92,146])fill(B,()=>{c.moveTo(x-9,160);c.lineTo(x-10,184);c.lineTo(x+8,186);c.lineTo(x+9,160)})
fill(B,()=>c.ellipse(112,150,56,22,0,0,T))
for(const x of[78,96,114,132])fill(B,()=>c.arc(x,130,7,0,T))
fill(L,()=>c.ellipse(114,162,40,6,0,0,T))
const r=(l,a)=>[156+l*cos(a),150+l*sin(a)]
if(j>.01)fill(A,()=>{c.moveTo(158,150);c.lineTo(...r(58,-j*.35));c.lineTo(...r(56,j));c.closePath()})
c.save();c.translate(156,150);c.rotate(j)
fill(B,()=>{c.moveTo(-8,-2);c.lineTo(66,-2);c.quadraticCurveTo(74,2,66,8);c.lineTo(-8,12)})
c.fillStyle=c.strokeStyle=W;c.lineWidth=2;c.lineJoin='round'
for(const x of[18,32,46,58])c.beginPath(),c.moveTo(x-4,-1),c.lineTo(x,-8),c.lineTo(x+4,-1),c.closePath(),c.fill(),c.stroke()
c.restore()
c.save();c.translate(156,150);c.rotate(-j*.35)
fill(B,()=>{c.moveTo(-14,4);c.lineTo(-14,-20);c.quadraticCurveTo(0,-30,20,-24);c.lineTo(66,-12);c.quadraticCurveTo(78,-8,74,0);c.lineTo(-14,2)})
fill(B,()=>c.arc(10,-26,11,0,T));fill(B,()=>c.arc(66,-14,6,0,T))
c.fillStyle=c.strokeStyle=W;c.lineWidth=2
for(const x of[24,38,52,64])c.beginPath(),c.moveTo(x-4,1),c.lineTo(x,8),c.lineTo(x+4,1),c.closePath(),c.fill(),c.stroke()
c.fillStyle=c.strokeStyle=D
if(bl)c.lineWidth=3,c.beginPath(),c.moveTo(6,-28),c.lineTo(16,-28),c.stroke()
else c.beginPath(),c.arc(12,-28,4.5,0,T),c.fill()
c.beginPath();c.arc(68,-17,2,0,T);c.fill()
c.restore()
}
+25
View File
@@ -0,0 +1,25 @@
// dolphin leaping in a big arc out of a little rolling wave and diving back in, slowest at the top, quick under water
const{sin,cos,PI}=Math,T=2*PI
export const frames=32,delay=60
const B='#4ea8de',L='#a7d4ef',A='#2ec4b6',AL='#96e1da',D='#2b2633'
export default(c,t)=>{
const u=(t+.5)%1-.5,v=Math.abs(u)*2,th=Math.sign(u)*PI*(.525*v+.475*v**8)-PI/2,R=74,fill=(col,f)=>{c.fillStyle=c.strokeStyle=col;c.lineWidth=6;c.beginPath();f();c.fill();c.stroke()}
c.save();c.beginPath();c.rect(0,0,256,194);c.clip()
c.translate(128+R*cos(th),182+R*sin(th));c.rotate(th+PI/2)
c.save();c.translate(-60,0);c.rotate(.2*sin(T*t*2))
fill(B,()=>{c.moveTo(6,-4);c.quadraticCurveTo(-6,-6,-16,-18);c.quadraticCurveTo(-10,-4,-12,0);c.quadraticCurveTo(-10,4,-16,18);c.quadraticCurveTo(-6,6,6,4)})
c.restore()
fill(B,()=>{c.moveTo(6,-22);c.quadraticCurveTo(-2,-44,-16,-46);c.quadraticCurveTo(-12,-30,-20,-17)})
fill(B,()=>c.ellipse(14,17,14,6,.6,0,T))
fill(B,()=>{c.moveTo(60,4);c.quadraticCurveTo(58,-4,46,-6);c.bezierCurveTo(36,-24,0,-26,-22,-17);c.bezierCurveTo(-40,-10,-52,-5,-60,-3)
c.lineTo(-60,3);c.bezierCurveTo(-46,8,-4,22,26,15);c.quadraticCurveTo(44,10,60,4)})
fill(L,()=>c.ellipse(8,9,28,6,.05,0,T))
c.fillStyle=D;c.beginPath();c.arc(36,-7,3.5,0,T);c.fill()
c.restore()
c.fillStyle=c.strokeStyle=A;c.lineWidth=6;c.beginPath();c.moveTo(30,206)
for(let x=30;x<=226;x+=4)c.lineTo(x,182+5*sin(x*T/66-T*t))
c.lineTo(226,206);c.closePath();c.fill();c.stroke()
c.strokeStyle=AL;c.lineWidth=4;c.lineCap='round';c.beginPath()
for(let x=48;x<=208;x+=4)c.lineTo(x,194+3*sin(x*T/66-T*t+1.5))
c.stroke()
}
+27
View File
@@ -0,0 +1,27 @@
// fennec fox sitting with its tail wrapped round its feet, huge ears swivelling to listen, blinks
const{sin,cos,PI}=Math,T=2*PI
export const frames=24,delay=80
const B='#efb36a',L='#fadcb8',A='#8c5a36',D='#2b2633'
export default(c,t)=>{
const tw=t>.56&&t<.62?.22:0,el=-.55+.14*sin(T*t),er=.55+.14*sin(T*t+1.9)+tw,bl=t>.3&&t<.36,hr=.04*sin(T*t+.6)
const fill=(col,f)=>{c.fillStyle=c.strokeStyle=col;c.lineWidth=6;c.beginPath();f();c.fill();c.stroke()}
c.translate(0,7)
fill(B,()=>c.ellipse(128,172,38,44,0,0,T))
fill(L,()=>c.ellipse(128,174,22,32,0,0,T))
c.save();c.translate(128,128);c.rotate(hr);c.translate(-128,-128)
for(const[x,r]of[[110,el],[146,er]]){
c.save();c.translate(x,92);c.rotate(r)
fill(B,()=>{c.moveTo(-19,4);c.quadraticCurveTo(-24,-52,0,-86);c.quadraticCurveTo(24,-52,19,4);c.closePath()})
fill(L,()=>{c.moveTo(-9,-2);c.quadraticCurveTo(-12,-44,0,-68);c.quadraticCurveTo(12,-44,9,-2);c.closePath()});c.restore()
}
fill(B,()=>c.arc(128,108,32,0,T))
fill(B,()=>{c.moveTo(98,112);c.quadraticCurveTo(110,134,128,142);c.quadraticCurveTo(146,134,158,112);c.closePath()})
fill(L,()=>{c.moveTo(108,122);c.quadraticCurveTo(116,136,128,140);c.quadraticCurveTo(140,136,148,122);c.quadraticCurveTo(128,114,108,122)})
c.fillStyle=c.strokeStyle=D
for(const d of[-1,1])if(bl)c.lineWidth=4,c.beginPath(),c.arc(128+d*14,104,5.5,.15*PI,.85*PI),c.stroke()
else c.beginPath(),c.ellipse(128+d*14,107,5,6,0,0,T),c.fill()
c.beginPath();c.ellipse(128,138,5,3.5,0,0,T);c.fill()
c.restore()
fill(B,()=>c.ellipse(140,210,52,13,0,0,T))
fill(A,()=>c.ellipse(88,208,15,11,0,0,T))
}
+25
View File
@@ -0,0 +1,25 @@
// fire-fox sitting still while it burns: ears, chest and tail are flames that flicker, embers drift off the tail tip
const{sin,cos,PI}=Math,T=2*PI
export const frames=24,delay=80
const B='#ff9f43',A='#f4c03e',D='#2b2633'
export default(c,t)=>{
const fill=(col,f)=>{c.fillStyle=c.strokeStyle=col;c.lineWidth=6;c.beginPath();f();c.fill();c.stroke()}
// teardrop flame standing on (x,y): round base of radius w, tip h up and leaning by lean
const fl=(x,y,w,h,lean)=>{c.moveTo(x+lean,y-h);c.quadraticCurveTo(x+w*1.15,y-h*.45,x+w,y-w);c.arc(x,y-w,w,0,PI);c.quadraticCurveTo(x-w*1.15,y-h*.45,x+lean,y-h)}
c.translate(-12,0)
const f=k=>sin(T*t*2+k)*.7+sin(T*t*3+k*1.7)*.3,bl=t>.4&&t<.46
for(let k=0;k<2;k++){const q=(t+k/2)%1;fill(A,()=>c.arc(206+10*sin(T*q+k*2)+q*8,78-56*q,4.5*(1-q),0,T))}
fill(B,()=>fl(170,212,30,130+7*f(1),44+9*f(2)))
fill(A,()=>fl(172,206,15,84+6*f(2.5),26+8*f(3)))
for(const d of[-1,1])fill(B,()=>fl(118+d*22,84,13,52+5*f(d*2),d*12+5*f(d+4)))
for(const d of[-1,1])fill(A,()=>fl(118+d*22,82,6,34+3*f(d*2),d*9+4*f(d+4)))
fill(B,()=>fl(118,212,42,116,0))
fill(A,()=>fl(118,208,19,62+5*f(4),4*f(5)))
fill(B,()=>c.arc(118,106,36,0,T))
for(const d of[-1,1])fill(B,()=>{c.moveTo(118+d*30,98);c.lineTo(118+d*48,122+3*f(d*3));c.lineTo(118+d*24,122)})
fill(A,()=>c.ellipse(118,124,15,10,0,0,T))
c.fillStyle=c.strokeStyle=D
for(const d of[-1,1])if(bl)c.lineWidth=4,c.beginPath(),c.arc(118+d*15,102,6,.15*PI,.85*PI),c.stroke()
else c.beginPath(),c.arc(118+d*15,104,5.5,0,T),c.fill()
c.beginPath();c.ellipse(118,119,5,3.5,0,0,T);c.fill()
}
+28
View File
@@ -0,0 +1,28 @@
// goose honking: long neck stretches up and forward, beak honks twice, tail wags while it waits
const{sin,cos,PI,max}=Math,T=2*PI
export const frames=24,delay=80
const B='#4ea8de',L='#a7d4ef',O='#ff9f43',D='#2b2633'
export default(c,t)=>{
const u=(t-.25)/.5,on=u>0&&u<1,e=on?sin(PI*u)**2:0,op=on?.42*max(0,sin(4*PI*u))*e:0,bl=t>.86&&t<.92
const hx=168+18*e,hy=80-30*e,tw=on?0:.12*sin(4*PI*t)
const fill=(col,f)=>{c.fillStyle=c.strokeStyle=col;c.lineWidth=6;c.beginPath();f();c.fill();c.stroke()}
c.strokeStyle=O;c.lineWidth=6
for(const x of[98,124])c.beginPath(),c.moveTo(x,200),c.lineTo(x,226),c.stroke(),fill(O,()=>c.ellipse(x+6,228,10,4,0,0,T))
c.save();c.translate(52,160);c.rotate(tw);fill(B,()=>{c.moveTo(14,-14);c.lineTo(-30,-26);c.lineTo(-24,-12);c.lineTo(14,12);c.closePath()});c.restore()
fill(B,()=>c.ellipse(108,168,66,40,-.06,0,T))
fill(L,()=>c.ellipse(100,160,44,22,-.16,0,T))
c.strokeStyle=B;c.lineWidth=26;c.lineCap='round'
c.beginPath();c.moveTo(150,158);c.quadraticCurveTo(160+6*e,120-12*e,hx,hy);c.stroke()
fill(B,()=>c.arc(hx,hy,20,0,T))
for(const d of[-1,1]){
c.save();c.translate(hx+16,hy+3);c.rotate(d*op)
fill(O,()=>{c.moveTo(0,-7*(d<0)-1);c.lineTo(22,d*2);c.lineTo(0,d>0?7:1);c.closePath()});c.restore()
}
c.fillStyle=c.strokeStyle=D
if(bl)c.lineWidth=3.5,c.beginPath(),c.arc(hx+4,hy-7,4.5,.15*PI,.85*PI),c.stroke()
else c.beginPath(),c.arc(hx+4,hy-5,4.5,0,T),c.fill()
if(op>.15){
c.strokeStyle=O;c.lineWidth=4;c.lineCap='round'
for(const[a,r]of[[-.5,14],[0,18],[.5,14]])c.beginPath(),c.moveTo(hx+44+8*cos(a),hy+2+8*sin(a)),c.lineTo(hx+44+r*cos(a)+6,hy+2+r*sin(a)),c.stroke()
}
}
+25
View File
@@ -0,0 +1,25 @@
// hermit crab: eyes peek out of its shell, it climbs half out waving both claws, ducks back in and the shell wobbles
const{sin,cos,PI,min,max}=Math,T=2*PI
export const frames=32,delay=70
const B='#f4c03e',L='#f9e0a0',A='#ee5a5a',D='#2b2633'
const e=x=>(x=min(1,max(0,x)),x*x*(3-2*x))
export default(c,t)=>{
const o=e((t-.1)/.16)*(1-e((t-.7)/.14)),pe=e((t-.02)/.1)*(1-e((t-.8)/.08)),wob=.06*sin(PI*e((t-.84)/.16))*sin(T*t*8)
const fill=(col,f)=>{c.fillStyle=c.strokeStyle=col;c.lineWidth=6;c.beginPath();f();c.fill();c.stroke()},ox=146,oy=166
c.translate(110,198);c.rotate(wob);c.translate(-110,-198)
c.strokeStyle=A;c.lineCap='round';c.lineJoin='round'
for(let i=0;i<3;i++){c.lineWidth=7;c.beginPath();c.moveTo(ox-4+8*i,oy+8);c.lineTo(ox+o*(18+8*i),oy+8-o*4);c.lineTo(ox+o*(28+10*i),oy+30*o+4);c.stroke()}
fill(A,()=>c.ellipse(ox+o*16,oy-o*6,22,18,0,0,T))
for(const d of[-1,1]){c.strokeStyle=A;c.lineWidth=5;c.beginPath();c.moveTo(ox+4+d*4,oy-6);c.lineTo(ox+8+pe*10+d*7,oy-12-pe*30);c.stroke()
const ex=ox+8+pe*10+d*7,ey=oy-14-pe*30;c.fillStyle=A;c.beginPath();c.arc(ex,ey,7.5,0,T);c.fill();c.fillStyle=D;c.beginPath();c.arc(ex+1,ey,4.5,0,T);c.fill()}
for(const[dy,sz,p]of[[-22,1,0],[6,.75,1.3]]){
const px=ox+o*(52*sz+4),py=oy+o*(dy-18*sz+6*sin(T*t*4+p)),g=.35+.25*sin(T*t*8+p)
c.strokeStyle=A;c.lineWidth=9;c.beginPath();c.moveTo(ox+o*16,oy-o*6);c.lineTo(px,py);c.stroke()
c.save();c.translate(px,py);c.rotate(-.5+.25*sin(T*t*4+p));c.scale(sz*(.4+.6*o),sz*(.4+.6*o))
fill(A,()=>c.ellipse(12,-6,14,8,-g,0,T));fill(A,()=>c.ellipse(12,6,13,7,g,0,T));fill(A,()=>c.arc(0,0,10,0,T));c.restore()
}
for(const[x,y,r]of[[37,60,12],[50,76,20],[72,100,32],[108,140,56]])fill(B,()=>c.arc(x,y,r,0,T))
c.strokeStyle=L;c.lineWidth=4
for(const[x,y,r]of[[37,60,12],[50,76,20],[72,100,32]])c.beginPath(),c.arc(x,y,r,-.3,2.1),c.stroke()
fill(A,()=>c.ellipse(ox,oy,20,26,-.6,0,T))
}
+30
View File
@@ -0,0 +1,30 @@
// little imp giggling: quick bounces with squeezed eyes and a wide grin, then catches its breath, tail swishing
const{sin,abs,PI}=Math,T=2*PI
export const frames=24,delay=75
const B='#ff85a8',L='#ffc2d4',A='#f4c03e',D='#2b2633'
export default(c,t)=>{
const gg=t<.6,g=gg?abs(sin(4*PI*t/.6)):0,sq=gg?.06*(1-g):0,y=gg?-12*g:-2*sin(PI*(t-.6)/.4)
const fill=(col,f)=>{c.fillStyle=c.strokeStyle=col;c.lineWidth=6;c.beginPath();f();c.fill();c.stroke()}
c.translate(128,190+y);c.scale(1+sq,1-sq)
c.save();c.translate(36,-18);c.rotate(.3*sin(T*t))
c.strokeStyle=B;c.lineWidth=8;c.beginPath();c.moveTo(0,0);c.quadraticCurveTo(40,8,54,-16);c.stroke()
fill(B,()=>{c.moveTo(54,-12);c.lineTo(44,-24);c.lineTo(58,-38);c.lineTo(66,-20);c.closePath()});c.restore()
for(const d of[-1,1])fill(B,()=>c.ellipse(d*20,-4,12,8,0,0,T))
for(const d of[-1,1])fill(A,()=>{c.moveTo(d*22,-104);c.quadraticCurveTo(d*30,-130,d*44,-136);c.quadraticCurveTo(d*44,-118,d*40,-100)})
for(const d of[-1,1])fill(B,()=>{c.moveTo(d*48,-74);c.lineTo(d*76,-92);c.lineTo(d*54,-56)})
fill(B,()=>c.ellipse(0,-58,56,52,0,0,T))
fill(L,()=>c.ellipse(0,-30,28,20,0,0,T))
for(const d of[-1,1])fill(B,()=>c.ellipse(d*(50+(gg?3*g:0)),-30-(gg?6*g:0),9,14,d*.5,0,T))
c.fillStyle=c.strokeStyle=D;c.lineWidth=4.5
if(gg){
for(const d of[-1,1])c.beginPath(),c.moveTo(d*28,-70),c.lineTo(d*18,-78),c.lineTo(d*8,-70),c.stroke()
c.beginPath();c.moveTo(-18,-56);c.quadraticCurveTo(0,-56,18,-56);c.quadraticCurveTo(14,-36,0,-36);c.quadraticCurveTo(-14,-36,-18,-56);c.fill()
c.fillStyle=L;c.beginPath();c.moveTo(-12,-56);c.lineTo(-8,-50);c.lineTo(-4,-56);c.fill()
c.strokeStyle=B;c.lineWidth=4;for(const d of[-1,1])c.beginPath(),c.arc(d*84,-84,10+3*g,d>0?-.6:PI-.4+0,d>0?.4:PI+.6),c.stroke()
}else{
const bl=t>.86&&t<.92
for(const d of[-1,1])if(bl)c.lineWidth=3.5,c.beginPath(),c.arc(d*18,-74,5,.15*PI,.85*PI),c.stroke()
else c.beginPath(),c.arc(d*18,-72,5,0,T),c.fill()
c.lineWidth=3.5;c.beginPath();c.moveTo(-10,-54);c.quadraticCurveTo(2,-46,12,-56);c.stroke()
}
}
+26
View File
@@ -0,0 +1,26 @@
// jackalope hopping in place: little antlers, ears trailing the hop, stretched in the air, squashed on landing
const{sin,cos,PI}=Math,T=2*PI
export const frames=24,delay=75
const B='#c9a27e',L='#ead8c4',A='#8f6b4c',D='#2b2633'
export default(c,t)=>{
const air=t<.5,u=t/.5,h=air?38*sin(PI*u):0,v=air?cos(PI*u):0,sq=t>.5&&t<.66?sin(PI*(t-.5)/.16):0,bl=t>.84&&t<.9
const fill=(col,g)=>{c.fillStyle=c.strokeStyle=col;c.lineWidth=6;c.beginPath();g();c.fill();c.stroke()}
const line=(col,lw,g)=>{c.strokeStyle=col;c.lineWidth=lw;c.beginPath();g();c.stroke()}
c.translate(126,214-h);c.scale(1.15*(1+.07*sq),1.15*(1-.09*sq));c.rotate(air?-.22*v:0)
const ear=(x,col,lag)=>{c.save();c.translate(x,-92);c.rotate(-.95-.3*v+.2*sq+lag);fill(col,()=>c.ellipse(0,-24,10,26,0,0,T))
if(col==B)fill(L,()=>c.ellipse(0,-22,4,17,0,0,T));c.restore()}
ear(30,A,.12)
for(const x of[36,46])line(A,5,()=>{c.moveTo(x,-92);c.quadraticCurveTo(x+6,-108,x+2,-122);c.moveTo(x+3,-106);c.lineTo(x+13,-114)})
fill(B,()=>c.ellipse(-36,air?-12+6*v:-6,air?18:22,8,air?.5*v:0,0,T))
fill(B,()=>c.arc(-28,-32,24,0,T))
fill(B,()=>c.ellipse(-6,-42,48,30,-.1,0,T))
fill(L,()=>c.arc(-56,-50,10,0,T))
const px=air?38+3*v:30,py=air?-18:-8
line(B,12,()=>{c.moveTo(22,-32);c.lineTo(px-2,py)});fill(B,()=>c.ellipse(px,py,10,7,0,0,T))
fill(B,()=>c.arc(40,-70,26,0,T))
ear(38,B,0)
c.fillStyle=c.strokeStyle=D
if(bl)c.lineWidth=3.5,c.beginPath(),c.arc(48,-74,5,.15*PI,.85*PI),c.stroke()
else c.beginPath(),c.arc(48,-72,4.5,0,T),c.fill()
c.beginPath();c.arc(65,-62,3,0,T);c.fill()
}
+23
View File
@@ -0,0 +1,23 @@
// jelly-cat drifting: a cat-eared jellyfish bell that pulses as it bobs, tentacles waving behind
const{sin,cos,PI}=Math,T=2*PI
export const frames=24,delay=80
const B='#ff85a8',L='#ffc2d3',D='#2b2633'
export default(c,t)=>{
const s=sin(T*t),rx=62*(1-.07*s),ry=56*(1+.06*s),cx=128,cy=96-8*sin(T*t-1),bl=t>.72&&t<.78
const fill=(col,f)=>{c.fillStyle=c.strokeStyle=col;c.lineWidth=6;c.beginPath();f();c.fill();c.stroke()}
for(const[k,col,w,len]of[[-1.5,B,7,112],[-.5,L,10,84],[.5,L,10,84],[1.5,B,7,112]]){
c.strokeStyle=col;c.lineWidth=w;c.beginPath()
for(let j=0;j<=14;j++){const q=j/14;c.lineTo(cx+k*rx*.4+q*k*6+12*q*sin(T*t-q*4+k),cy+10+q*len*(1+.06*s))}
c.stroke()
}
for(const d of[-1,1]){
const a=-PI/2+d*.74
c.save();c.translate(cx+rx*cos(a),cy+ry*sin(a)+4);c.rotate(d*(.5+.06*s))
fill(B,()=>{c.moveTo(-16,10);c.lineTo(0,-24);c.lineTo(16,10)});fill(L,()=>{c.moveTo(-6,6);c.lineTo(0,-10);c.lineTo(6,6)});c.restore()
}
fill(B,()=>{c.ellipse(cx,cy,rx,ry,0,PI,T);for(let i=0;i<4;i++){const x0=cx+rx-i*rx/2;c.quadraticCurveTo(x0-rx/4,cy+18,x0-rx/2,cy)}})
c.fillStyle=c.strokeStyle=D
for(const d of[-1,1])if(bl)c.lineWidth=4,c.beginPath(),c.arc(cx+d*21,cy-14,6,.15*PI,.85*PI),c.stroke()
else c.beginPath(),c.arc(cx+d*21,cy-12,5.5,0,T),c.fill()
c.lineWidth=3.5;for(const d of[-1,1])c.beginPath(),c.arc(cx+d*4.5,cy-3,4.5,.1*PI,.9*PI),c.stroke()
}
+21
View File
@@ -0,0 +1,21 @@
// karakasa umbrella yokai hopping on its one leg: big single eye, long tongue flapping behind the hop
const{sin,cos,PI}=Math,T=2*PI
export const frames=24,delay=75
const B='#ff9f43',L='#ffcfa1',A='#ff85a8',G='#c9a27e',D='#2b2633'
export default(c,t)=>{
const air=t<.5,u=t/.5,h=air?28*sin(PI*u):0,v=air?cos(PI*u):0,sq=t>.5&&t<.66?sin(PI*(t-.5)/.16):0,bl=t>.8&&t<.86
const fill=(col,g)=>{c.fillStyle=c.strokeStyle=col;c.lineWidth=6;c.beginPath();g();c.fill();c.stroke()}
c.translate(128,212-h);c.scale(1+.08*sq,1-.08*sq);c.rotate(.07*sin(T*t))
fill(G,()=>c.roundRect(-22,-12,44,9,4));for(const x of[-14,8])fill(G,()=>c.rect(x,-4,6,4))
c.strokeStyle=B;c.lineWidth=9;c.beginPath();c.moveTo(0,-14);c.lineTo(0,-48-(air?6*v:0));c.stroke()
const hy=-56-(air?6*v:0)
fill(B,()=>{c.moveTo(0,hy-112);c.quadraticCurveTo(26,hy-72,56,hy-4)
for(const x of[28,0,-28,-56])c.quadraticCurveTo(x+14,hy+12,x,hy-4)
c.quadraticCurveTo(-26,hy-72,0,hy-112)})
c.strokeStyle=L;c.lineWidth=4;for(const x of[-28,28])c.beginPath(),c.moveTo(0,hy-106),c.quadraticCurveTo(x*.45,hy-60,x,hy-4),c.stroke()
// tongue: hangs out of the mouth and lags behind the hop
c.strokeStyle=A;c.lineWidth=12;c.beginPath();c.moveTo(0,hy-30);c.bezierCurveTo(4,hy-10,20+10*v,hy+4-10*v,30+8*sin(T*t*2),hy+20-16*v);c.stroke()
fill(D,()=>c.ellipse(0,hy-30,11,7,0,0,PI))
if(bl){c.strokeStyle=D;c.lineWidth=4;c.beginPath();c.arc(0,hy-64,13,.15*PI,.85*PI);c.stroke()}
else{fill('#fff',()=>c.arc(0,hy-62,14,0,T));c.fillStyle=D;c.beginPath();c.arc(3*sin(T*t),hy-60,7,0,T);c.fill()}
}
+53
View File
@@ -0,0 +1,53 @@
// kingfisher on a branch: bobs, tips forward and darts beak-first into the water, then pops back up to its perch
const{sin,PI,min,abs,atan2}=Math,T=2*PI
export const frames=32,delay=70
const B='#2ec4b6',L='#97e2da',O='#ff9f43',W='#c9a27e',K='#8a94a6',D='#2b2633'
const sm=x=>x<=0?0:x>=1?1:x*x*(3-2*x),lerp=(a,b,k)=>a+(b-a)*k
export default(c,t)=>{
const fill=(col,f)=>{c.fillStyle=c.strokeStyle=col;c.lineWidth=6;c.beginPath();f();c.fill();c.stroke()}
// pose: x, y, rotation, and whether it's stretched out into a dart
let x=96,y=100,r=0,dart=0,hide=0
const dn=(t-.4)/.14,uw=(t-.54)/.1,upk=(t-.64)/.16,st=(t-.8)/.08
if(dn>0&&dn<=.3)r=dn/.3*.7
else if(dn>.3&&dn<=1){const k=(dn-.3)/.7;x=lerp(110,160,k*k);y=lerp(118,290,k*k);r=atan2(172,50);dart=1}
else if(uw>0&&uw<=1)hide=1
else if(upk>0&&upk<=1){const k=sm(upk);x=lerp(156,96,k);y=lerp(206,100,k)-50*sin(PI*k);r=lerp(-.6,0,k)}
if(st>0&&st<1)y+=6*sin(PI*st)
const bl=t>.2&&t<.25,bob=dn<0?2*sin(4*T*t):0
c.strokeStyle=W;c.lineWidth=11;c.lineCap='round'
c.beginPath();c.moveTo(16,138);c.quadraticCurveTo(70,132,136,140);c.stroke()
c.lineWidth=6;c.beginPath();c.moveTo(40,136);c.lineTo(28,118);c.stroke()
if(!hide){
c.save();c.beginPath();c.rect(0,0,256,212);c.clip()
c.translate(x,y+bob);c.rotate(r);c.scale(1.2,1.2)
if(dart){
// stretched out along +x: tail, body, orange underside, head, beak
fill(B,()=>{c.moveTo(-30,0);c.lineTo(-56,-10);c.lineTo(-56,10);c.closePath()})
fill(B,()=>c.ellipse(-4,0,36,17,0,0,T))
c.save();c.beginPath();c.ellipse(-4,0,36,17,0,0,T);c.clip();fill(O,()=>c.ellipse(-4,16,36,12,0,0,T));c.restore()
fill(B,()=>c.arc(24,-2,18,0,T))
fill(K,()=>{c.moveTo(36,-8);c.lineTo(80,0);c.lineTo(36,6);c.closePath()})
fill(D,()=>c.arc(26,-8,3.5,0,T))
}else{
fill(B,()=>{c.moveTo(-14,24);c.lineTo(-30,50);c.lineTo(-18,54);c.lineTo(0,30);c.closePath()})
for(const d of[-6,8])fill(O,()=>c.ellipse(d,34,6,4,0,0,T))
fill(B,()=>c.ellipse(0,6,27,32,-.2,0,T))
c.save();c.beginPath();c.ellipse(0,6,27,32,-.2,0,T);c.clip();fill(O,()=>c.ellipse(16,18,22,28,0,0,T));c.restore()
fill(B,()=>c.arc(10,-28,24,0,T))
fill(L,()=>c.ellipse(22,-16,12,8,0,0,T))
fill(K,()=>{c.moveTo(28,-34);c.lineTo(78,-22);c.lineTo(30,-18);c.closePath()})
c.fillStyle=c.strokeStyle=D
if(bl)c.lineWidth=3.5,c.beginPath(),c.arc(16,-34,4.5,.15*PI,.85*PI),c.stroke()
else c.beginPath(),c.arc(16,-32,4.5,0,T),c.fill()
}
c.restore()
}
// water line, splash and ripple
c.strokeStyle=c.fillStyle=L;c.lineWidth=5;c.lineCap='round'
const sp=(t-.5)/.3
for(const[a,b]of[[40,96],[124,232]]){c.beginPath();for(let i=a;i<=b;i+=4)c.lineTo(i,214+3*sin(i/9+T*t*2));c.stroke()}
if(sp>0&&sp<1){
c.beginPath();c.ellipse(158,214,14+40*sp,4+6*sp,0,0,T);c.stroke()
for(const d of[-1,-.4,.4,1]){const q=min(1,sp*1.6),h=212-40*q*(1-abs(d)*.3)+50*q*q;if(h<208)c.beginPath(),c.arc(158+d*30*q,h,5*(1-q*.6),0,T),c.fill()}
}
}
+29
View File
@@ -0,0 +1,29 @@
// koi-dragon swimming in place, seen from above: its body waves in an S, whiskers and fins trail, little horns on its head
const{sin,cos,PI,hypot,atan2}=Math,T=2*PI
export const frames=24,delay=80
const B='#ff7a5c',L='#ffbcad',A='#f4c03e',D='#2b2633'
export default(c,t)=>{
const fill=(col,f)=>{c.fillStyle=c.strokeStyle=col;c.lineWidth=6;c.beginPath();f();c.fill();c.stroke()}
const N=40,P=[],Nr=[],hw=s=>s<.16?30*Math.sqrt(1-((.16-s)/.16)**2):30*(1-.84*((s-.16)/.84)**1.15)
for(let j=0;j<=N;j++){const s=j/N;P.push([214-150*s,128+(3+24*s**1.4)*sin(T*t-3.4*s)])}
for(let j=0;j<=N;j++){const a=P[Math.max(0,j-1)],b=P[Math.min(N,j+1)],dx=b[0]-a[0],dy=b[1]-a[1],l=hypot(dx,dy);Nr.push([-dy/l,dx/l])}
const at=(s,o=0)=>{const j=Math.round(s*N);return[P[j][0]+Nr[j][0]*o,P[j][1]+Nr[j][1]*o]}
const ang=s=>{const j=Math.round(s*N);return atan2(Nr[j][0],-Nr[j][1])}
// tail fan at the end, trailing the body's last bend
const[ex,ey]=P[N],ta=ang(1)
c.save();c.translate(ex,ey);c.rotate(ta)
fill(L,()=>{c.moveTo(0,0);c.quadraticCurveTo(-14,-30,-36,-30+6*sin(T*t-4));c.quadraticCurveTo(-26,-6,-30,0);c.quadraticCurveTo(-26,6,-36,30+6*sin(T*t-4));c.quadraticCurveTo(-14,30,0,0)})
c.restore()
for(const d of[-1,1]){const[x,y]=at(.3,d*hw(.3)*.8);c.save();c.translate(x,y);c.rotate(ang(.3)+PI+d*(.7+.3*sin(T*t*2+d)))
fill(L,()=>c.ellipse(14,0,18,8,0,0,T));c.restore()}
// dragon whiskers: flow from the snout out and back along the body, rippling
const[hx,hy]=P[0],fx=P[0][0]-P[4][0],fy=P[0][1]-P[4][1],fl=hypot(fx,fy),ux=fx/fl,uy=fy/fl
c.strokeStyle=A;c.lineCap='round';c.lineWidth=4
for(const d of[-1,1]){c.beginPath();for(let k=0;k<=16;k++){const q=k/16,al=2-80*q,la=d*(13+30*q**.6+8*q*sin(T*t*2-q*5+d))
c.lineTo(hx+ux*al-uy*la,hy+uy*al+ux*la)}c.stroke()}
fill(B,()=>{for(let j=0;j<=N;j++){const[x,y]=at(j/N,hw(j/N));c.lineTo(x,y)}for(let j=N;j>=0;j--){const[x,y]=at(j/N,-hw(j/N));c.lineTo(x,y)}c.closePath()})
for(const[s,rx,ry]of[[.46,17,11],[.68,10,7]]){const[x,y]=at(s);c.save();c.translate(x,y);c.rotate(ang(s));fill(L,()=>c.ellipse(0,0,rx,ry,0,0,T));c.restore()}
c.strokeStyle=A;c.lineWidth=7
for(const d of[-1,1]){const[x,y]=at(.13,d*12),[x2,y2]=at(.25,d*38);c.beginPath();c.moveTo(x,y);c.lineTo(x2,y2);c.stroke()}
c.fillStyle=D;for(const d of[-1,1]){const[x,y]=at(.06,d*hw(.06)*.72);c.beginPath();c.arc(x,y,4.5,0,T);c.fill()}
}
+28
View File
@@ -0,0 +1,28 @@
// lemon-duck floating: a lemon-shaped duck bobbing and rocking over two ripples, the leaf on its head twitching
const{sin,cos,PI}=Math,T=2*PI
export const frames=24,delay=85
const B='#f4c03e',L='#f9e09e',O='#ff9f43',G='#8cc084',W='#4ea8de',D='#2b2633'
export default(c,t)=>{
const fill=(col,f)=>{c.fillStyle=c.strokeStyle=col;c.lineWidth=6;c.beginPath();f();c.fill();c.stroke()}
const bl=t>.6&&t<.66
c.save();c.translate(122,150+5*sin(T*t));c.rotate(.05*sin(T*t+1.2));c.translate(-122,-150)
c.save();c.translate(166,74);c.rotate(.25*sin(T*t*2));fill(G,()=>c.ellipse(13,-6,15,7,-.4,0,T))
c.lineWidth=5;c.lineCap='round';c.beginPath();c.moveTo(0,4);c.lineTo(2,-4);c.stroke();c.restore()
fill(B,()=>{c.moveTo(60,134);c.lineTo(34,124);c.lineTo(56,158)})
fill(B,()=>c.ellipse(122,152,72,42,0,0,T))
fill(B,()=>c.arc(166,106,33,0,T))
// wing is a lemon slice: pale half-wheel with segment lines
c.save();c.translate(112,140);c.rotate(-.12+.1*sin(T*t*2));fill(L,()=>{c.arc(0,0,27,0,PI);c.closePath()})
c.strokeStyle=B;c.lineWidth=4;c.lineCap='round';c.beginPath();c.arc(0,0,19,.04*PI,.96*PI);c.stroke()
for(const a of[.25,.5,.75])c.beginPath(),c.moveTo(0,2),c.lineTo(19*cos(a*PI),19*sin(a*PI)),c.stroke()
c.restore()
fill(O,()=>c.ellipse(199,114,15,8,.12,0,T))
c.fillStyle=c.strokeStyle=D
if(bl)c.lineWidth=4,c.beginPath(),c.arc(174,98,6,.15*PI,.85*PI),c.stroke()
else c.beginPath(),c.arc(174,100,5.5,0,T),c.fill()
c.restore()
c.strokeStyle=W;c.lineWidth=5;c.lineCap='round'
for(const[x0,x1,y,k]of[[44,204,198,0],[84,170,212,2]]){
c.beginPath();for(let x=x0;x<=x1;x+=4)c.lineTo(x,y+3*sin(x/10-T*t+k));c.stroke()
}
}
+26
View File
@@ -0,0 +1,26 @@
// ring-tailed lemur sitting with big amber eyes, its long striped tail curling and uncurling over its head, blinks
const{sin,cos,PI}=Math,T=2*PI
export const frames=24,delay=85
const B='#8a94a6',L='#e2e6ed',A='#3d4352',Y='#f4c03e',D='#2b2633'
export default(c,t)=>{
const s=sin(T*t),bl=t>.62&&t<.68
const fill=(col,f)=>{c.fillStyle=c.strokeStyle=col;c.lineWidth=6;c.beginPath();f();c.fill();c.stroke()}
let x=140,y=200,a=0;const K=3.7+.55*s,n=40,Ls=215,pts=[[x,y]]
for(let i=1;i<=n;i++){const u=i/n;a=-.3+.08*s-K*u**1.6;x+=Ls/n*cos(a);y+=Ls/n*sin(a);pts.push([x,y])}
const tail=()=>{c.moveTo(...pts[0]);for(const p of pts)c.lineTo(...p)}
c.strokeStyle=L;c.lineWidth=20;c.beginPath();tail();c.stroke()
c.lineCap='butt';c.setLineDash([12,12]);c.lineDashOffset=-14;c.strokeStyle=A;c.beginPath();tail();c.stroke();c.setLineDash([]);c.lineCap='round'
for(const d of[-1,1])fill(B,()=>c.ellipse(108+d*16,212,14,7,0,0,T))
fill(B,()=>c.ellipse(108,170,36,42,0,0,T))
fill(L,()=>c.ellipse(108,178,20,28,0,0,T))
for(const d of[-1,1])fill(B,()=>c.ellipse(108+d*20,190,8,14,-d*.3,0,T))
for(const d of[-1,1])fill(B,()=>{c.moveTo(108+d*14,80);c.lineTo(108+d*32,52);c.lineTo(108+d*36,84);c.closePath()})
fill(B,()=>c.arc(108,100,30,0,T))
fill(L,()=>{c.moveTo(80,98);c.quadraticCurveTo(108,82,136,98);c.quadraticCurveTo(128,128,108,142);c.quadraticCurveTo(88,128,80,98)})
fill(A,()=>c.ellipse(108,140,6,5,0,0,T))
for(const d of[-1,1]){
fill(A,()=>{c.moveTo(108+d*4,94);c.quadraticCurveTo(108+d*22,82,108+d*24,104);c.quadraticCurveTo(108+d*14,114,108+d*6,108);c.closePath()})
if(bl)c.strokeStyle=Y,c.lineWidth=3.5,c.beginPath(),c.arc(108+d*14,98,6,.15*PI,.85*PI),c.stroke()
else fill(Y,()=>c.arc(108+d*14,100,5.5,0,T)),c.fillStyle=D,c.beginPath(),c.arc(108+d*14,100,3,0,T),c.fill()
}
}
+26
View File
@@ -0,0 +1,26 @@
// fluffy llama standing side-on in a little blanket, chewing away, one ear twitches, blinks
const{sin,cos,PI}=Math,T=2*PI
export const frames=24,delay=80
const B='#d6a77a',L='#efd5bd',A='#2ec4b6',D='#2b2633'
export default(c,t)=>{
const j=sin(T*t*4),s=sin(T*t),tw=t>.46&&t<.56?-.45:0,bl=t>.8&&t<.86,hy=1.5*s
const fill=(col,f)=>{c.fillStyle=c.strokeStyle=col;c.lineWidth=6;c.beginPath();f();c.fill();c.stroke()}
const line=(col,lw,f)=>{c.strokeStyle=col;c.lineWidth=lw;c.beginPath();f();c.stroke()}
for(const[x,o]of[[76,4],[94,0],[132,4],[150,0]])line(B,14,()=>{c.moveTo(x,160);c.lineTo(x+o*.5,222)})
fill(B,()=>c.arc(48,134,12,0,T))
fill(B,()=>c.ellipse(108,150,58,30,0,0,T))
for(let x=62;x<=156;x+=16)fill(B,()=>c.arc(x,126+(x%32?2:0),13,0,T))
for(let y=164;y<=178;y+=14)fill(B,()=>c.arc(56+(y-164)*.4,y,11,0,T))
line(B,30,()=>{c.moveTo(150,140);c.lineTo(166,72+hy)})
for(let y=86;y<=130;y+=15)fill(B,()=>c.arc(178-(y-86)*.25,y+hy*(130-y)/44,10,0,T))
fill(A,()=>c.roundRect(84,114,50,34,8))
for(const[x,r]of[[166,-.35+tw],[180,.05]])fill(B,()=>c.ellipse(x,34+hy,6,16,r,0,T))
fill(B,()=>c.ellipse(180,64+hy,24,17,0,0,T))
fill(B,()=>c.ellipse(200,72+hy,15,11,0,0,T))
fill(L,()=>c.ellipse(199+2*cos(T*t*4),80+hy+1.5*j,12,6,0,0,T))
fill(L,()=>{c.arc(170,50+hy,9,0,T);c.arc(182,46+hy,9,0,T)})
c.fillStyle=c.strokeStyle=D
if(bl)c.lineWidth=3.5,c.beginPath(),c.arc(186,60+hy,4.5,.15*PI,.85*PI),c.stroke()
else c.beginPath(),c.arc(186,62+hy,4.5,0,T),c.fill()
c.beginPath();c.ellipse(209,68+hy,2.5,3,0,0,T);c.fill()
}
+32
View File
@@ -0,0 +1,32 @@
// two lovebirds on a branch leaning in to nuzzle, eyes closing happily, a little heart floats up between them
const{sin,PI,max,min}=Math,T=2*PI
export const frames=24,delay=85
const B='#8cc084',L='#c6e0c1',F='#ff7a5c',W='#c9a27e',D='#2b2633'
const bump=(t,a,w)=>{const x=(t-a)/w;return x>0&&x<1?sin(PI*x)**2:0}
export default(c,t)=>{
const n=bump(t,.1,.36)+bump(t,.56,.36),wig=.05*sin(6*T*t)*n,hp=(t-.22)/.5
const fill=(col,f)=>{c.fillStyle=c.strokeStyle=col;c.lineWidth=6;c.beginPath();f();c.fill();c.stroke()}
c.strokeStyle=W;c.lineWidth=12;c.lineCap='round'
c.beginPath();c.moveTo(18,172);c.quadraticCurveTo(128,180,238,168);c.stroke()
c.lineWidth=6;c.beginPath();c.moveTo(210,170);c.lineTo(226,152);c.stroke()
for(const d of[1,-1]){
const x=128-d*56,a=d*(.2*n+wig)+(d>0?.02:-.02)*sin(T*t)
c.save();c.translate(x,170);c.scale(d,1);c.rotate(a*d)
fill(B,()=>{c.moveTo(-14,-14);c.lineTo(-32,26);c.lineTo(-18,30);c.lineTo(0,-6);c.closePath()})
fill(B,()=>c.ellipse(0,-36,30,38,0,0,T))
fill(L,()=>c.ellipse(8,-28,18,24,0,0,T))
fill(B,()=>c.arc(6,-82,26,0,T))
c.save();c.beginPath();c.arc(6,-82,29,0,T);c.clip();fill(F,()=>c.ellipse(28,-84,30,30,0,0,T));c.restore()
fill(W,()=>{c.moveTo(28,-90);c.quadraticCurveTo(44,-86,40,-74);c.lineTo(28,-76);c.closePath()})
c.fillStyle=c.strokeStyle=D
if(n>.5)c.lineWidth=3.5,c.beginPath(),c.arc(16,-90,5,1.15*PI,1.85*PI),c.stroke()
else c.beginPath(),c.arc(16,-88,5,0,T),c.fill()
c.restore()
}
for(const k of[0,1]){
const q=hp-k*.46;if(q<0||q>.5)continue
const z=min(1,6*min(q,.5-q)),y=72-60*q
c.save();c.translate(128,y);c.scale(z,z);c.fillStyle=c.strokeStyle=F;c.lineWidth=4
c.beginPath();c.moveTo(0,8);c.bezierCurveTo(-16,-4,-8,-16,0,-7);c.bezierCurveTo(8,-16,16,-4,0,8);c.fill();c.stroke();c.restore()
}
}
+27
View File
@@ -0,0 +1,27 @@
// macaw standing tall: long red tail, spreads blue-and-yellow wings and flaps twice, head bobbing
const{sin,cos,PI,max}=Math,T=2*PI
export const frames=24,delay=80
const B='#ee5a5a',L='#f6adad',S='#4ea8de',Y='#f4c03e',D='#2b2633'
export default(c,t)=>{
const u=(t-.18)/.6,on=u>0&&u<1,env=on?sin(PI*u)**.6:0,a=.14+env*(1.35+.45*sin(4*T*u)),hb=.08*sin(T*t*2),bl=t>.86&&t<.92
const fill=(col,f)=>{c.fillStyle=c.strokeStyle=col;c.lineWidth=6;c.beginPath();f();c.fill();c.stroke()}
fill(B,()=>{c.moveTo(118,170);c.lineTo(138,170);c.lineTo(134,240);c.lineTo(122,240);c.closePath()})
fill(S,()=>{c.moveTo(124,214);c.lineTo(132,214);c.lineTo(134,240);c.lineTo(122,240);c.closePath()})
for(const d of[-1,1])fill(L,()=>c.ellipse(128+d*13,186,10,6,0,0,T))
fill(B,()=>c.ellipse(128,134,34,52,0,0,T))
for(const d of[-1,1]){
c.save();c.translate(128+d*26,104);c.rotate(-d*a)
fill(S,()=>c.ellipse(0,40,19,42,0,0,T))
fill(Y,()=>c.ellipse(0,18,17,10,0,0,T))
fill(B,()=>c.ellipse(0,4,15,10,0,0,T))
c.restore()
}
c.save();c.translate(128,96);c.rotate(hb)
fill(B,()=>c.arc(0,-24,30,0,T))
for(const d of[-1,1])fill(L,()=>c.ellipse(d*14,-24,11,13,0,0,T))
c.fillStyle=c.strokeStyle=D
for(const d of[-1,1])if(bl)c.lineWidth=3.5,c.beginPath(),c.arc(d*14,-27,4.5,.15*PI,.85*PI),c.stroke()
else c.beginPath(),c.arc(d*14,-25,4.5,0,T),c.fill()
fill(L,()=>{c.moveTo(-9,-14);c.quadraticCurveTo(0,-22,9,-14);c.quadraticCurveTo(8,0,0,6);c.quadraticCurveTo(-8,0,-9,-14)})
c.restore()
}
+25
View File
@@ -0,0 +1,25 @@
// moon-cat sleeping: a crescent moon curled like a cat, rocking and breathing, its tail swaying and z's floating up
const{sin,cos,PI,atan2,sqrt,hypot}=Math,T=2*PI
export const frames=24,delay=90
const B='#f4c03e',L='#f9e09e',A='#b48cff',D='#2b2633'
export default(c,t)=>{
const fill=(col,f)=>{c.fillStyle=c.strokeStyle=col;c.lineWidth=6;c.beginPath();f();c.fill();c.stroke()}
// crescent: outer circle minus inner circle, horns pointing up
const[x1,y1,R,x2,y2,r]=[128,100,84,136,60,80],d=hypot(x2-x1,y2-y1),a=(R*R-r*r+d*d)/(2*d),h=sqrt(R*R-a*a)
const ux=(x2-x1)/d,uy=(y2-y1)/d,px=x1+a*ux,py=y1+a*uy,I=[[px+h*uy,py-h*ux],[px-h*uy,py+h*ux]].sort((p,q)=>p[0]-q[0])
const[l,rr]=I
c.translate(0,22);c.translate(128,120);c.rotate(.07*sin(T*t));c.scale(1+.02*sin(T*t*2),1+.02*sin(T*t*2));c.translate(-128,-120)
for(let k=0;k<3;k++){const q=(t+k/3)%1,s=8*sin(PI*q),x=88+26*q+6*sin(T*q),y=34-40*q
if(s<3)continue
c.strokeStyle=A;c.lineWidth=4.5;c.lineCap=c.lineJoin='round';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()}
c.strokeStyle=B;c.lineWidth=13;c.lineCap='round';c.beginPath();c.moveTo(rr[0]-4,rr[1]+4)
c.bezierCurveTo(rr[0]+14,rr[1]-10,rr[0]+22,rr[1]-26,rr[0]+12+6*sin(T*t-1),rr[1]-44+3*cos(T*t-1));c.stroke()
for(const[x,rt]of[[50,-.5],[76,.25]]){c.save();c.translate(x,64);c.rotate(rt);fill(B,()=>{c.moveTo(-12,8);c.lineTo(0,-20);c.lineTo(12,8)})
fill(L,()=>{c.moveTo(-5,5);c.lineTo(0,-8);c.lineTo(5,5)});c.restore()}
fill(B,()=>{c.arc(x1,y1,R,atan2(l[1]-y1,l[0]-x1),atan2(rr[1]-y1,rr[0]-x1),true);c.arc(x2,y2,r,atan2(rr[1]-y2,rr[0]-x2),atan2(l[1]-y2,l[0]-x2),false);c.closePath()})
fill(B,()=>c.arc(64,86,28,0,T))
c.fillStyle=L;for(const[x,y,s]of[[128,166,8],[96,158,6],[162,160,5.5]])c.beginPath(),c.arc(x,y,s,0,T),c.fill()
c.strokeStyle=D;c.lineWidth=4;c.lineCap='round'
for(const x of[54,76])c.beginPath(),c.arc(x,84,6,.15*PI,.85*PI),c.stroke()
c.lineWidth=3.5;for(const d of[-1,1])c.beginPath(),c.arc(65+d*4,97,4,.1*PI,.9*PI),c.stroke()
}
+28
View File
@@ -0,0 +1,28 @@
// moose facing you with big flat antlers, slowly nodding, an ear flick and a blink
const{sin,cos,PI}=Math,T=2*PI
export const frames=24,delay=90
const B='#b07d55',L='#d9b38c',A='#ecd3a5',D='#2b2633'
export default(c,t)=>{
const n=(1-cos(T*t))/2,fl=t>.6&&t<.66?.4:0,bl=t>.82&&t<.88
const fill=(col,f)=>{c.fillStyle=c.strokeStyle=col;c.lineWidth=6;c.beginPath();f();c.fill();c.stroke()}
const line=(col,lw,f)=>{c.strokeStyle=col;c.lineWidth=lw;c.beginPath();f();c.stroke()}
for(const x of[100,156])line(B,13,()=>{c.moveTo(x,170);c.lineTo(x,218)})
fill(B,()=>c.ellipse(128,166,60,34,0,0,T))
for(const x of[108,148])line(B,15,()=>{c.moveTo(x,180);c.lineTo(x,222)})
c.save();c.translate(128,140);c.scale(1,1-.05*n);c.translate(-128,-140+9*n)
for(const d of[-1,1]){
c.save();c.translate(128,0);c.scale(d,1);c.translate(-128,0)
line(A,11,()=>{c.moveTo(146,88);c.quadraticCurveTo(160,86,170,78)})
fill(A,()=>c.ellipse(190,64,32,17,-.45,0,T))
for(const[x,y]of[[170,48],[184,38],[199,32],[214,32],[224,44]])fill(A,()=>c.arc(x,y,6.5,0,T))
c.save();c.translate(156,100);c.rotate(.3+(d>0?fl:0))
fill(B,()=>c.ellipse(14,0,16,7,0,0,T));c.restore();c.restore()
}
fill(B,()=>c.ellipse(128,100,26,24,0,0,T))
fill(L,()=>c.ellipse(128,142,22,25,0,0,T))
c.fillStyle=c.strokeStyle=D
for(const d of[-1,1])if(bl)c.lineWidth=4,c.beginPath(),c.arc(128+d*15,96,4.5,.15*PI,.85*PI),c.stroke()
else c.beginPath(),c.arc(128+d*15,98,4.5,0,T),c.fill()
for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*8,154,3.5,5,d*.3,0,T),c.fill()
c.restore()
}
+25
View File
@@ -0,0 +1,25 @@
// ostrich running in place: long pink legs cycling, fluffy body bouncing, neck bobbing with each stride
const{sin,cos,PI,sqrt,max,min,hypot}=Math,T=2*PI
export const frames=24,delay=60
const B='#b48cff',L='#dac6ff',P='#ff85a8',Q='#ffc2d4',D='#2b2633'
export default(c,t)=>{
const f=2*T*t,bob=4*cos(2*f),hx=126,hy=136+bob
const fill=(col,g)=>{c.fillStyle=c.strokeStyle=col;c.lineWidth=6;c.beginPath();g();c.fill();c.stroke()}
const leg=(ph,col)=>{
const fx=hx+28*cos(ph),fy=224-16*max(0,sin(ph)),dx=fx-hx,dy=fy-hy,d=min(hypot(dx,dy),92),h=sqrt(max(0,46*46-(d/2)**2))
const ux=dx/hypot(dx,dy),uy=dy/hypot(dx,dy),kx=hx+ux*d/2-uy*h,ky=hy+uy*d/2+ux*h
c.strokeStyle=col;c.lineWidth=9;c.lineCap=c.lineJoin='round'
c.beginPath();c.moveTo(hx,hy);c.lineTo(kx,ky);c.lineTo(fx,fy);c.lineTo(fx+14,fy);c.stroke()
}
leg(f+PI,Q);leg(f,P)
c.translate(0,bob)
for(const[x,y,r]of[[58,96,16],[50,116,14],[64,124,13]])fill(L,()=>c.arc(x,y,r,0,T))
fill(B,()=>{c.ellipse(118,112,54,34,-.08,0,T);for(let x=78;x<=158;x+=16)c.moveTo(x+13,138-(x-118)*.08),c.arc(x,138-(x-118)*.08,13,0,T)})
fill(L,()=>c.ellipse(112,108,30,16,-.15,0,T))
const nx=176+5*sin(2*f),ny=40
c.strokeStyle=P;c.lineWidth=12;c.lineCap='round'
c.beginPath();c.moveTo(152,100);c.quadraticCurveTo(170,70,nx,ny+12);c.stroke()
fill(P,()=>c.ellipse(nx,ny,15,12,0,0,T))
fill(P,()=>{c.moveTo(nx+10,ny-2);c.lineTo(nx+26,ny+3);c.lineTo(nx+10,ny+7);c.closePath()})
c.fillStyle=D;c.beginPath();c.arc(nx+3,ny-3,4.5,0,T);c.fill()
}
+30
View File
@@ -0,0 +1,30 @@
// pangolin trundling, then curling up into a scaly ball that rocks, then unrolling again
const{sin,cos,PI,max,min,atan2}=Math,T=2*PI
export const frames=32,delay=70
const B='#c9a27e',A='#97734f',D='#2b2633'
const cl=x=>max(0,min(1,x)),ease=x=>(x=cl(x),x*x*(3-2*x))
const N=56,Ls=208,W=s=>s<.32?6+25*(s/.32)**.6:31-23*(s-.32)/.68
export default(c,t)=>{
const r=ease((t-.06)/.26)-ease((t-.62)/.26),rock=.22*sin(T*cl((t-.3)/.34))*r
const turn=.9+r*(T*1.06-.9),a0=PI+.42+rock+.6*r
let x=0,y=0;const P=[[0,0,a0]]
for(let i=1;i<=N;i++){const a=a0-turn*i/N;x+=Ls/N*cos(a);y+=Ls/N*sin(a);P.push([x,y,a])}
let x0=1e9,x1=-1e9,y1=-1e9
P.forEach(([px,py],i)=>{const w=W(i/N);x0=min(x0,px-w);x1=max(x1,px+w);y1=max(y1,py+w)})
const ox=128-(x0+x1)/2,oy=(1-r)*(196-y1)+r*(206-y1),wk=(1-r)*sin(T*t*3)
c.translate(ox,oy)
c.fillStyle=c.strokeStyle=B
if(r<.45)for(const[k,f]of[[.3,1],[.66,-1]]){
const[px,py]=P[Math.round(k*N)],l=(1-r/.45)*(18+2*f*wk)
c.lineWidth=12;c.beginPath();c.moveTo(px,py);c.lineTo(px+f*2*wk,py+W(k)+l-4);c.stroke()
}
P.forEach(([px,py],i)=>{c.beginPath();c.arc(px,py,W(i/N),0,T);c.fill()})
const f=ease((r-.7)/.3);if(f>0){let mx=0,my=0;for(const[px,py]of P)mx+=px/P.length,my+=py/P.length;c.beginPath();c.arc(mx,my,Ls/turn*f,0,T);c.fill()}
c.strokeStyle=A;c.lineWidth=3.5
for(let s=.13;s<.96;s+=.07)for(const j of[0,1]){
const q=s+j*.035,i=Math.round(q*N),[px,py,a]=P[i],w=W(q),dx=cos(a),dy=sin(a),nx=-dy,ny=dx,o=w*(.62-.5*j),rr=w*.42
const cx=px+nx*o-dx*rr*.3,cy=py+ny*o-dy*rr*.3
c.beginPath();c.arc(cx,cy,rr,a-1.25,a+1.25);c.stroke()
}
if(r<.6){const[px,py,a]=P[Math.round(.07*N)];c.fillStyle=D;c.beginPath();c.arc(px-sin(a)*3,py+cos(a)*3-4,3.5,0,T);c.fill()}
}
+30
View File
@@ -0,0 +1,30 @@
// pegasus flying in place: two slow wing beats per loop, legs tucked, mane and tail streaming, blinks
const{sin,PI}=Math,T=2*PI
export const frames=24,delay=80
const B='#4ea8de',L='#a7d4ef',A='#ff85a8',D='#2b2633'
export default(c,t)=>{
const f=sin(2*T*t),w=-.52+.66*f,bl=t>.7&&t<.76
const fill=(col,g)=>{c.fillStyle=c.strokeStyle=col;c.lineWidth=6;c.beginPath();g();c.fill();c.stroke()}
const line=(col,lw,g)=>{c.strokeStyle=col;c.lineWidth=lw;c.beginPath();g();c.stroke()}
const wing=(x,y,r,col)=>{c.save();c.translate(x,y);c.rotate(r);c.scale(.82,.82);fill(col,()=>{
c.moveTo(14,4);c.quadraticCurveTo(24,-46,-12,-86);c.quadraticCurveTo(-30,-80,-30,-64)
c.quadraticCurveTo(-44,-56,-38,-40);c.quadraticCurveTo(-46,-26,-34,-14);c.quadraticCurveTo(-30,4,-14,4)});c.restore()}
c.translate(8,-7*sin(2*T*t+1.2))
wing(116,110,w-.14,B)
// tail: a tube along a curve that ripples, fuller toward the end
const ty=152+8*sin(2*T*t-1)
for(let s=0;s<=1;s+=.02){const u=1-s,x=u*u*u*76+3*u*u*s*56+3*u*s*s*44+s*s*s*24,y=u*u*u*134+3*u*u*s*(124+5*f)+3*u*s*s*(150-6*f)+s*s*s*ty
c.fillStyle=A;c.beginPath();c.arc(x,y,5+5*sin(PI*s*.8),0,T);c.fill()}
for(const[x,k]of[[138,0],[156,1]])line(B,11,()=>{c.moveTo(x,150);c.quadraticCurveTo(x+16+6*k,166+3*f,x+4+8*k,186+2*f-6*k)})
for(const x of[84,94])line(B,11,()=>{c.moveTo(x,150);c.quadraticCurveTo(x-10,172,x-26,178-3*f)})
// mane scallops peek out along the back of the neck
for(let i=0;i<5;i++){const s=.15+.22*i,o=14+3*sin(2*T*t-i*.9);fill(A,()=>c.arc(146+30*s-.81*o,138-42*s-.58*o,9,0,T))}
line(B,32,()=>{c.moveTo(146,138);c.lineTo(176,96)})
fill(B,()=>c.ellipse(118,140,50,27,0,0,T))
fill(B,()=>c.ellipse(190,98,24,15,.55,0,T))
fill(B,()=>{c.moveTo(174,86);c.lineTo(178,62);c.lineTo(188,82)})
wing(126,114,w,L)
c.fillStyle=c.strokeStyle=D
if(bl)c.lineWidth=3.5,c.beginPath(),c.arc(187,90,5,.15*PI,.85*PI),c.stroke()
else c.beginPath(),c.arc(187,92,4.5,0,T),c.fill()
}
+27
View File
@@ -0,0 +1,27 @@
// pelican scooping: dips its long bill, comes up with a full pouch, tips back and gulps (a lump slides down the neck)
const{sin,PI,max,min}=Math,T=2*PI
export const frames=24,delay=85
const B='#7b8cff',L='#c3cbff',Y='#f4c03e',D='#2b2633'
const sm=x=>x<=0?0:x>=1?1:x*x*(3-2*x)
export default(c,t)=>{
const dip=sm(t/.18)-sm((t-.24)/.16),up=sm((t-.42)/.1)-sm((t-.66)/.14),r=.55*dip-.3*up
const p=sm((t-.2)/.1)*(1-sm((t-.5)/.12)),lump=(t-.5)/.22,bl=t>.84&&t<.9
const fill=(col,f)=>{c.fillStyle=c.strokeStyle=col;c.lineWidth=6;c.beginPath();f();c.fill();c.stroke()}
for(const x of[92,114])fill(Y,()=>{c.moveTo(x,196);c.lineTo(x+2,222);c.lineTo(x+14,224)})
fill(B,()=>{c.moveTo(56,150);c.lineTo(28,140);c.lineTo(40,166);c.closePath()})
// neck goes behind the body, head and bill on top
const head=()=>{c.translate(124,138);c.rotate(r)}
c.save();head();c.strokeStyle=B;c.lineWidth=28;c.lineCap='round'
c.beginPath();c.moveTo(-6,28);c.quadraticCurveTo(-10,-24,20,-62);c.stroke();c.restore()
fill(B,()=>c.ellipse(100,164,60,44,-.05,0,T))
fill(L,()=>c.ellipse(92,156,40,22,-.12,0,T))
c.save();head()
if(lump>0&&lump<1){const q=1-lump;fill(B,()=>c.arc(-4+24*q*q,-6-52*q,19,0,T))}
fill(B,()=>c.arc(22,-66,22,0,T))
fill(Y,()=>{c.moveTo(38,-74);c.lineTo(100,-56);c.quadraticCurveTo(108,-52,102,-46);c.lineTo(38,-60);c.closePath()})
fill(Y,()=>{c.moveTo(40,-58);c.lineTo(98,-46);c.quadraticCurveTo(76,-40+46*p,42,-40+12*p);c.closePath()})
c.fillStyle=c.strokeStyle=D
if(bl)c.lineWidth=3.5,c.beginPath(),c.arc(26,-72,4.5,.15*PI,.85*PI),c.stroke()
else c.beginPath(),c.arc(26,-70,4.5,0,T),c.fill()
c.restore()
}
+17
View File
@@ -0,0 +1,17 @@
// platypus swimming along: duck bill, flat beaver tail beating up and down, webbed feet paddling in turn
const{sin,cos,PI}=Math,T=2*PI
export const frames=24,delay=80
const B='#b5835a',L='#dcc0a6',A='#8a94a6',D='#2b2633'
export default(c,t)=>{
const s=sin(T*t),bl=t>.55&&t<.62,fill=(col,f)=>{c.fillStyle=c.strokeStyle=col;c.lineWidth=6;c.beginPath();f();c.fill();c.stroke()}
c.translate(128,124+5*s);c.rotate(.04*sin(T*t+1));c.translate(-124,-128)
for(const[x,ph]of[[92,0],[150,PI]]){c.save();c.translate(x,150);c.rotate(.5*sin(T*t*2+ph))
fill(A,()=>{c.moveTo(-4,0);c.lineTo(-14,26);c.quadraticCurveTo(0,34,14,26);c.lineTo(4,0)});c.restore()}
c.save();c.translate(66,130);c.rotate(.25*sin(T*t*2-1));fill(B,()=>c.ellipse(-22,0,26,14,0,0,T));c.restore()
fill(B,()=>c.ellipse(122,128,64,34,0,0,T))
fill(L,()=>c.ellipse(126,150,40,9,0,0,T))
fill(A,()=>c.ellipse(204,134,26,11,.08,0,T))
c.fillStyle=c.strokeStyle=D;c.beginPath();c.arc(218,129,2,0,T);c.fill()
if(bl)c.lineWidth=3.5,c.beginPath(),c.arc(174,114,5,.15*PI,.85*PI),c.stroke()
else c.beginPath(),c.arc(174,116,5,0,T),c.fill()
}
+25
View File
@@ -0,0 +1,25 @@
// round quokka doing a small happy hop on its long feet, then beaming with its big smile and squinting
const{sin,cos,PI,max,min}=Math,T=2*PI
export const frames=24,delay=80
const B='#b88a64',L='#ddc3ab',P='#ff85a8',D='#2b2633'
export default(c,t)=>{
const h=t<.32?20*sin(PI*t/.32):0,sq=t>.32&&t<.46?.08*sin(PI*(t-.32)/.14):0,g=max(0,min(1,(t-.42)/.12))*max(0,min(1,(.96-t)/.12))
const y=-h
const fill=(col,f)=>{c.fillStyle=c.strokeStyle=col;c.lineWidth=6;c.beginPath();f();c.fill();c.stroke()}
c.translate(128,214);c.scale(1+sq,1-sq);c.translate(-128,-214)
c.strokeStyle=B;c.lineWidth=15;c.beginPath();c.moveTo(150,196+y);c.quadraticCurveTo(178,204,196,212+y*.6);c.stroke()
for(const d of[-1,1])fill(B,()=>c.ellipse(128+d*22,211+y*.7,21,7,0,0,T))
fill(B,()=>{c.moveTo(128,120+y);c.bezierCurveTo(166,120+y,178,170+y,172,190+y);c.bezierCurveTo(166,210+y,90,210+y,84,190+y);c.bezierCurveTo(78,170+y,90,120+y,128,120+y)})
fill(L,()=>c.ellipse(128,180+y,26,26,0,0,T))
for(const d of[-1,1])fill(B,()=>c.ellipse(128+d*11,144+y,6,10,d*.4,0,T))
for(const d of[-1,1])fill(B,()=>c.arc(128+d*33,76+y,10,0,T)),fill(L,()=>c.arc(128+d*33,76+y,4.5,0,T))
fill(B,()=>{c.moveTo(128,62+y);c.bezierCurveTo(158,62+y,170,82+y,168,98+y);c.bezierCurveTo(166,114+y,140,128+y,128,128+y);c.bezierCurveTo(116,128+y,90,114+y,88,98+y);c.bezierCurveTo(86,82+y,98,62+y,128,62+y)})
c.fillStyle=c.strokeStyle=D
for(const d of[-1,1])if(g>.5)c.lineWidth=4,c.beginPath(),c.arc(128+d*18,95+y,5,1.15*PI,1.85*PI),c.stroke()
else c.beginPath(),c.arc(128+d*18,91+y,4.5,0,T),c.fill()
c.beginPath();c.ellipse(128,104+y,6.5,4.5,0,0,T);c.fill()
const w=8+5*g,m=3+4*g
c.beginPath();c.moveTo(128-w,113+y);c.quadraticCurveTo(128,113+y+m*2,128+w,113+y);c.quadraticCurveTo(128,115+y,128-w,113+y);c.fill()
c.lineWidth=3;c.beginPath();c.moveTo(128-w,113+y);c.lineTo(128-w-3,110+y);c.moveTo(128+w,113+y);c.lineTo(128+w+3,110+y);c.stroke()
if(g>.3)c.fillStyle=P,c.beginPath(),c.ellipse(128,114+y+m,4*g,2.5*g,0,0,T),c.fill()
}
+31
View File
@@ -0,0 +1,31 @@
// red panda sitting up and waving a paw, ringed tail swishing behind it, blinks
const{sin,cos,PI}=Math,T=2*PI
export const frames=24,delay=80
const B='#ff7a5c',L='#ffc4b5',A='#b84a32',D='#2b2633'
export default(c,t)=>{
const s=sin(T*t),w=sin(T*t*2),y=1.5*s,bl=t>.7&&t<.76,tx=204+10*s
const fill=(col,f)=>{c.fillStyle=c.strokeStyle=col;c.lineWidth=6;c.beginPath();f();c.fill();c.stroke()}
const line=(col,lw,f)=>{c.strokeStyle=col;c.lineWidth=lw;c.beginPath();f();c.stroke()}
const tail=()=>{c.moveTo(150,198);c.bezierCurveTo(208,204,226,168,tx,124)}
line(B,30,tail)
c.lineCap='butt';c.setLineDash([11,13]);c.lineDashOffset=-8;line(A,30,tail);c.setLineDash([]);c.lineCap='round'
for(const d of[-1,1])fill(A,()=>c.ellipse(128+d*18,212,15,9,0,0,T))
fill(B,()=>c.ellipse(128,166+y,44,46,0,0,T))
fill(A,()=>c.ellipse(128,176+y,26,30,0,0,T))
line(A,17,()=>{c.moveTo(150,150+y);c.quadraticCurveTo(158,172,142,184+y)})
const a=-2.15+.38*w,hx=102+46*cos(a),hy=150+y+46*sin(a)
line(A,17,()=>{c.moveTo(104,152+y);c.lineTo(hx,hy)});fill(A,()=>c.arc(hx,hy,9,0,T))
for(const d of[-1,1]){
c.save();c.translate(128+d*34,70+y);c.rotate(d*.35)
fill(B,()=>{c.moveTo(-16,12);c.quadraticCurveTo(-14,-18,0,-20);c.quadraticCurveTo(14,-18,16,12);c.closePath()})
fill(L,()=>{c.moveTo(-8,8);c.quadraticCurveTo(-6,-10,0,-11);c.quadraticCurveTo(6,-10,8,8);c.closePath()});c.restore()
}
fill(B,()=>c.ellipse(128,100+y,50,40,0,0,T))
fill(L,()=>c.ellipse(128,118+y,22,15,0,0,T))
for(const d of[-1,1])fill(L,()=>c.ellipse(128+d*20,80+y,8,5,d*.2,0,T))
c.fillStyle=c.strokeStyle=D
for(const d of[-1,1])if(bl)c.lineWidth=4,c.beginPath(),c.arc(128+d*20,93+y,5.5,.15*PI,.85*PI),c.stroke()
else c.beginPath(),c.arc(128+d*20,96+y,5.5,0,T),c.fill()
c.beginPath();c.ellipse(128,112+y,5,3.5,0,0,T);c.fill()
c.lineWidth=3;c.beginPath();c.moveTo(121,120+y);c.quadraticCurveTo(124.5,124+y,128,120+y);c.quadraticCurveTo(131.5,124+y,135,120+y);c.stroke()
}
+31
View File
@@ -0,0 +1,31 @@
// robin singing: red breast puffs out, head tips up, beak chirps three times and little notes float off
const{sin,PI,abs,max,min}=Math,T=2*PI
export const frames=24,delay=80
const B='#c9a27e',R='#ff7a5c',Y='#f4c03e',D='#2b2633'
export default(c,t)=>{
const u=(t-.05)/.6,on=u>0&&u<1,e=on?sin(PI*u):0,op=on?.32*abs(sin(3*PI*u)):0,bl=t>.8&&t<.86
const fill=(col,f)=>{c.fillStyle=c.strokeStyle=col;c.lineWidth=6;c.beginPath();f();c.fill();c.stroke()}
c.translate(0,3*sin(T*t))
c.strokeStyle=B;c.lineWidth=5
for(const[x,y]of[[112,196],[136,196]])c.beginPath(),c.moveTo(x,y),c.lineTo(x-2,224),c.moveTo(x-12,226),c.lineTo(x+8,226),c.stroke()
fill(B,()=>{c.moveTo(74,150);c.lineTo(24,120);c.lineTo(32,102);c.lineTo(86,128);c.closePath()})
c.save();c.translate(150,130);c.rotate(-.18*e);c.translate(-150,-130)
const body=()=>{c.moveTo(184,150);c.ellipse(122,150,62*(1+.05*e),52,0,0,T);c.moveTo(206,104);c.arc(170,104,36,0,T)}
fill(B,body)
c.save();c.beginPath();body();c.clip();c.fillStyle=R;c.beginPath();c.ellipse(170,146,40*(1+.15*e),48*(1+.1*e),0,0,T);c.fill();c.restore()
c.fillStyle=c.strokeStyle=D
if(bl)c.lineWidth=4,c.beginPath(),c.arc(180,94,5,.15*PI,.85*PI),c.stroke()
else c.beginPath(),c.arc(180,96,5,0,T),c.fill()
for(const d of[-1,1]){
c.save();c.translate(202,104);c.rotate(d*op);c.fillStyle=c.strokeStyle=Y;c.beginPath()
c.moveTo(0,0);c.lineTo(0,d*6);c.lineTo(22,d*1.5);c.closePath();c.lineWidth=3;c.fill();c.stroke();c.restore()
}
c.restore()
for(const k of[0,1]){
const q=(t-.12-k*.22)/.4;if(q<0||q>1)continue
const x=214-10*q+k*8,y=78-56*q,z=min(1,4*min(q,1-q))
c.save();c.translate(x,y);c.scale(z,z);c.fillStyle=c.strokeStyle=R;c.lineWidth=3
c.beginPath();c.ellipse(0,0,6,4.5,-.4,0,T);c.fill()
c.beginPath();c.moveTo(5,-2);c.lineTo(5,-20);c.quadraticCurveTo(12,-16,13,-10);c.stroke();c.restore()
}
}
+29
View File
@@ -0,0 +1,29 @@
// rooster crowing: puffs its chest out, throws its head back and opens its beak, tail feathers swaying
const{sin,cos,PI}=Math,T=2*PI
export const frames=24,delay=85
const B='#ff9f43',L='#ffcfa1',R='#ee5a5a',G='#2ec4b6',Y='#f4c03e',D='#2b2633'
const bump=(t,a,w)=>{const x=(t-a)/w;return x>0&&x<1?sin(PI*x)**2:0}
export default(c,t)=>{
const e=bump(t,.2,.56),op=.42*bump(t,.3,.36),sw=.06*sin(T*t),bl=t>.86&&t<.92
const fill=(col,f)=>{c.fillStyle=c.strokeStyle=col;c.lineWidth=6;c.beginPath();f();c.fill();c.stroke()}
c.strokeStyle=Y;c.lineWidth=6;c.lineCap='round'
for(const x of[104,128])c.beginPath(),c.moveTo(x,190),c.lineTo(x-2,224),c.moveTo(x-14,226),c.lineTo(x+10,226),c.stroke()
c.save();c.translate(70,140);c.rotate(sw-.1*e)
c.strokeStyle=G;c.lineCap='round'
for(const[w,a,b,x,y]of[[14,-20,-90,-48,-30],[12,-34,-70,-56,-6],[11,-40,-40,-50,14]])c.lineWidth=w,c.beginPath(),c.moveTo(0,0),c.quadraticCurveTo(a,b,x,y),c.stroke()
c.restore()
fill(B,()=>c.ellipse(108,152,58,44,.1,0,T))
fill(B,()=>c.arc(150+6*e,144-4*e,36*(1+.14*e),0,T))
fill(L,()=>c.ellipse(100,148,38,22,.25-.2*e,0,T))
c.save();c.translate(150,140);c.rotate(-.42*e)
c.strokeStyle=B;c.lineWidth=40;c.lineCap='round';c.beginPath();c.moveTo(0,0);c.lineTo(14,-46);c.stroke()
fill(B,()=>c.arc(18,-56,24,0,T))
for(const[x,y]of[[4,-80],[17,-84],[30,-78]])fill(R,()=>c.arc(x,y,9,0,T))
fill(R,()=>c.ellipse(38,-30,7,11,0,0,T))
for(const d of[-1,1]){c.save();c.translate(38,-54);c.rotate(d*op);fill(Y,()=>{c.moveTo(0,d*7*(d>0)-(d<0)*7);c.lineTo(18,d*2);c.lineTo(0,0);c.closePath()});c.restore()}
c.fillStyle=c.strokeStyle=D
if(bl)c.lineWidth=3.5,c.beginPath(),c.arc(26,-62,4.5,.15*PI,.85*PI),c.stroke()
else c.beginPath(),c.arc(26,-60,4.5,0,T),c.fill()
if(op>.15){c.strokeStyle=B;c.lineWidth=4;for(const a of[-.6,-.15,.3])c.beginPath(),c.moveTo(62+10*cos(a),-54+10*sin(a)),c.lineTo(62+24*cos(a),-54+24*sin(a)),c.stroke()}
c.restore()
}
+23
View File
@@ -0,0 +1,23 @@
// fire salamander resting: little flames flicker along its back, tail sways, slow breaths, blinks
const{sin,PI,sqrt}=Math,T=2*PI
export const frames=24,delay=80
const B='#ee5a5a',A='#ff9f43',Y='#f4c03e',D='#2b2633'
export default(c,t)=>{
const br=1+.03*sin(T*t),bl=t>.62&&t<.68,sw=10*sin(T*t)
c.translate(10,-14)
const fill=(col,g)=>{c.fillStyle=c.strokeStyle=col;c.lineWidth=6;c.beginPath();g();c.fill();c.stroke()}
const flame=(x,y,h,s,k)=>fill(k?Y:A,()=>{const w=k?5:10;c.moveTo(x-w,y+4);c.quadraticCurveTo(x-w-2,y-h*.5,x+s,y-h);c.quadraticCurveTo(x+w+2,y-h*.5,x+w,y+4)})
for(const[i,x]of[92,112,132,152,170].entries()){
const y=152-20*br*sqrt(1-((x-126)/52)**2)+4,h=26+7*sin(2*T*t+i*1.9)+4*sin(3*T*t+i),s=3*sin(2*T*t+i*2.3)
flame(x,y,h,s,0);flame(x,y,h*.55,s*.6,1)
}
// tail: tapering tube from the hips to a tip that curls up and sways
for(let s=0;s<=1;s+=.015){const u=1-s,x=u*u*84+2*u*s*44+s*s*20,y=u*u*154+2*u*s*(176+sw*.3)+s*s*(130+sw);c.fillStyle=B;c.beginPath();c.arc(x,y,15-12*s,0,T);c.fill()}
for(const[x,d]of[[96,-1],[106,-1],[150,1],[160,1]])fill(B,()=>{c.ellipse(x+d*6,178,6,14,d*.4,0,T)}),fill(B,()=>c.ellipse(x+d*12,190,9,5,0,0,T))
fill(B,()=>c.ellipse(126,152,52,20*br,0,0,T))
fill(B,()=>c.ellipse(186,146,28,18,-.08,0,T))
c.fillStyle=c.strokeStyle=D
if(bl)c.lineWidth=3.5,c.beginPath(),c.arc(192,138,5,.15*PI,.85*PI),c.stroke()
else c.beginPath(),c.arc(192,140,4.5,0,T),c.fill()
c.lineWidth=3;c.beginPath();c.moveTo(212,152);c.quadraticCurveTo(204,156,196,154);c.stroke()
}
+28
View File
@@ -0,0 +1,28 @@
// sea serpent: its humps roll up out of the water line and back under, tail fin flicks, head bobs
const{sin,PI}=Math,T=2*PI
export const frames=24,delay=80
const B='#2ec4b6',L='#97e2da',W='#4ea8de',D='#2b2633'
export default(c,t)=>{
const wy=188,hb=4*sin(T*t),bl=t>.3&&t<.36
const fill=(col,g)=>{c.fillStyle=c.strokeStyle=col;c.lineWidth=6;c.beginPath();g();c.fill();c.stroke()}
const dot=(x,y,r)=>{c.fillStyle=B;c.beginPath();c.arc(x,y,r,0,T);c.fill()}
c.save();c.beginPath();c.rect(0,0,256,wy);c.clip()
// humps: a wave travelling along the body, both ends stay under water
for(let x=54;x<=182;x+=1.5)dot(x,wy+16-64*sin(PI*(x-54)/128)**.6*sin(T*(x/104-t)),15)
// tail fin flicking up out of the water on the left
c.save();c.translate(42,wy+4);c.rotate(-.3+.2*sin(T*t+1));fill(B,()=>{c.moveTo(-8,0);c.quadraticCurveTo(-22,-36,-8,-52);c.quadraticCurveTo(8,-30,10,0)});c.restore()
// neck rising from the water, head looking left
for(let s=0;s<=1;s+=.02){
const u=1-s,x=u*u*u*208+3*u*u*s*222+3*u*s*s*180+s*s*s*194,y=u*u*u*(wy+18)+3*u*u*s*(wy-36)+3*u*s*s*(128+hb)+s*s*s*(104+hb)
dot(x,y,16-3*s)
}
c.restore()
for(let i=0;i<3;i++)fill(L,()=>{c.moveTo(204,88+hb+i*10);c.lineTo(234,76+hb+i*14);c.lineTo(208,100+hb+i*10)})
fill(B,()=>c.ellipse(186,96+hb,31,21,-.12,0,T))
c.fillStyle=c.strokeStyle=D
if(bl)c.lineWidth=3.5,c.beginPath(),c.arc(178,87+hb,5.5,.15*PI,.85*PI),c.stroke()
else c.beginPath(),c.arc(178,89+hb,5,0,T),c.fill()
c.strokeStyle=W;c.lineWidth=8;c.beginPath()
for(let x=18;x<=238;x+=2)c.lineTo(x,wy+3.5*sin(T*(x/44-2*t)))
c.stroke()
}
+19
View File
@@ -0,0 +1,19 @@
// sea turtle seen from above, gliding: front flippers sweep together, back flippers steer, head sways
const{sin,cos,PI}=Math,T=2*PI
export const frames=24,delay=85
const B='#2ec4b6',L='#96e1da',A='#8cc084',D='#2b2633'
export default(c,t)=>{
const s=sin(T*t),fill=(col,f)=>{c.fillStyle=c.strokeStyle=col;c.lineWidth=6;c.beginPath();f();c.fill();c.stroke()}
c.translate(128,130+4*sin(T*t*2));c.rotate(.08*sin(T*t-.5));c.scale(.94,.94);c.translate(-128,-128)
for(const d of[-1,1]){c.save();c.translate(128+d*36,100);c.rotate(d*(-.2+.5*s))
fill(A,()=>{c.moveTo(0,-10);c.quadraticCurveTo(d*44,-24,d*76,14);c.quadraticCurveTo(d*38,6,0,12)});c.restore()}
for(const d of[-1,1]){c.save();c.translate(128+d*30,176);c.rotate(d*.3*sin(T*t+1));fill(A,()=>c.ellipse(d*14,10,18,9,d*.6,0,T));c.restore()}
fill(A,()=>{c.moveTo(122,186);c.lineTo(134,186);c.lineTo(128,204);c.closePath()})
c.save();c.translate(128,82);c.rotate(.12*sin(T*t*2-1));fill(A,()=>c.ellipse(0,-16,17,22,0,0,T))
c.fillStyle=D;for(const d of[-1,1])c.beginPath(),c.arc(d*10,-20,3.5,0,T),c.fill();c.restore()
fill(B,()=>c.ellipse(128,134,52,62,0,0,T))
const hex=k=>[128+20*cos(k*PI/3+PI/6),132+20*sin(k*PI/3+PI/6)]
fill(L,()=>{for(let k=0;k<6;k++)c.lineTo(...hex(k));c.closePath()})
c.strokeStyle=L;c.lineWidth=4;c.lineCap='round'
for(let k=0;k<6;k++){const a=k*PI/3+PI/6,[x,y]=hex(k);c.beginPath();c.moveTo(x,y);c.lineTo(128+40*cos(a),132+50*sin(a));c.stroke()}
}
+24
View File
@@ -0,0 +1,24 @@
// shrimp curled in a c, flicking its long antennae and snapping its tail fan, little legs pedalling
const{sin,cos,PI,max,atan2}=Math,T=2*PI
export const frames=24,delay=75
const B='#ff7a5c',L='#ffbcad',D='#2b2633'
export default(c,t)=>{
const fl=max(0,sin(T*t*2))**3,cx=116,cy=130,R=48,n=7,a0=-.45,span=4+.3*fl
const fill=(col,f)=>{c.fillStyle=c.strokeStyle=col;c.lineWidth=6;c.beginPath();f();c.fill();c.stroke()}
const seg=k=>{const a=a0-span*k/n;return[cx+R*cos(a),cy+R*sin(a),a,26-12*k/n]}
c.translate(0,4*sin(T*t))
const[hx,hy]=seg(0),ax=hx+40,ay=hy-12
c.strokeStyle=B;c.lineWidth=3;c.lineCap='round'
for(const[ex,ey,qx,qy,p]of[[124,22,214,40,0],[176,16,236,60,1.5]]){const w=sin(T*t*2+p);c.beginPath();c.moveTo(ax-6,ay);c.quadraticCurveTo(qx,qy,ex+10*w,ey+4*w);c.stroke()}
c.lineWidth=4
for(let k=1;k<5;k++){const[x,y,a]=seg(k),b=a+PI+.35*sin(T*t*2-k);c.beginPath();c.moveTo(x,y);c.lineTo(x+32*cos(b),y+32*sin(b));c.stroke()}
const[tx,ty,ta]=seg(n);c.save();c.translate(tx,ty);c.rotate(atan2(-cos(ta),sin(ta))+.5*fl)
for(const a of[-.65,0,.65])fill(B,()=>c.ellipse(18*cos(a),18*sin(a),17,7,a,0,T))
c.restore()
for(let k=n;k>=0;k--){const[x,y,,r]=seg(k);fill(B,()=>c.arc(x,y,r,0,T))}
c.strokeStyle=L;c.lineWidth=3
for(let k=1;k<n;k++){const[x,y,a,r]=seg(k);c.beginPath();c.arc(x,y,r-6,a-.7,a+.7);c.stroke()}
fill(B,()=>{c.moveTo(hx-12,hy-20);c.quadraticCurveTo(hx+30,hy-30,hx+44,hy-10);c.quadraticCurveTo(hx+30,hy+16,hx-6,hy+18)})
c.strokeStyle=B;c.lineWidth=5;c.beginPath();c.moveTo(ax-8,ay+2);c.lineTo(ax+12,ay-6);c.stroke()
c.fillStyle=D;c.beginPath();c.arc(hx+24,hy-14,5,0,T);c.fill()
}
+29
View File
@@ -0,0 +1,29 @@
// cupcake-shell snail sliding along: its foot stretches and gathers, eye stalks wobble, the cherry on top jiggles
const{sin,cos,PI}=Math,T=2*PI
export const frames=24,delay=80
const B='#c9a27e',L='#ead7c3',P='#ff85a8',R='#ee5a5a',D='#2b2633'
export default(c,t)=>{
const fill=(col,f)=>{c.fillStyle=c.strokeStyle=col;c.lineWidth=6;c.beginPath();f();c.fill();c.stroke()}
const e=sin(T*t),hx=196+8*e,bl=t>.55&&t<.61
c.translate(-4,0)
fill(B,()=>c.roundRect(40,184,hx-22,22,11))
c.strokeStyle=B;c.lineCap='round'
for(const[x,y,k]of[[-8,98,0],[14,100,1.5]])c.lineWidth=6,c.beginPath(),c.moveTo(hx,134),c.lineTo(hx+x+4*sin(T*t*2+k),y+3*cos(T*t*2+k)),c.stroke()
c.lineWidth=36;c.beginPath();c.moveTo(hx-22,190);c.lineTo(hx,144);c.stroke()
c.fillStyle=c.strokeStyle=D
for(const[x,y,k]of[[-8,98,0],[14,100,1.5]]){const ex=hx+x+4*sin(T*t*2+k),ey=y+3*cos(T*t*2+k)
if(bl)c.lineWidth=3.5,c.beginPath(),c.moveTo(ex-5,ey),c.lineTo(ex+5,ey),c.stroke()
else c.beginPath(),c.arc(ex,ey,5.5,0,T),c.fill()}
c.lineWidth=3.5;c.beginPath();c.arc(hx+6,150,6,.1*PI,.7*PI);c.stroke()
const sy=2*sin(T*t-.8),j=k=>2.5*sin(T*t*2-k)
c.translate(0,sy)
fill(L,()=>{c.moveTo(78,186);c.lineTo(140,186);c.lineTo(152,138);c.lineTo(66,138);c.closePath()})
c.strokeStyle=B;c.lineWidth=4
for(const[a,b]of[[92,84],[109,109],[126,134]])c.beginPath(),c.moveTo(a,182),c.lineTo(b,142),c.stroke()
fill(P,()=>c.ellipse(109,132,50,15,0,0,T))
fill(P,()=>c.ellipse(109+j(1),113,38,14,0,0,T))
fill(P,()=>c.ellipse(109+j(2),96,25,12,0,0,T))
fill(P,()=>{c.moveTo(97+j(3),90);c.quadraticCurveTo(108+j(3),68,120+j(3),88)})
c.strokeStyle=B;c.lineWidth=4;c.beginPath();c.moveTo(111+j(4),58);c.quadraticCurveTo(114+j(4),46,124+j(4),40);c.stroke()
fill(R,()=>c.arc(110+j(4),66,10,0,T))
}
+27
View File
@@ -0,0 +1,27 @@
// strawberry frog hopping in place: crouch, leap with legs stretched, land with a squash, then its throat puffs
const{sin,cos,PI,max,min}=Math,T=2*PI
export const frames=24,delay=70
const B='#ee5a5a',L='#f7adad',S='#f4c03e',G='#8cc084',D='#2b2633'
export default(c,t)=>{
const fill=(col,f)=>{c.fillStyle=c.strokeStyle=col;c.lineWidth=6;c.beginPath();f();c.fill();c.stroke()}
const air=t>.12&&t<.56,ap=air?(t-.12)/.44:0,h=air?56*sin(PI*ap):0,ext=air?sin(PI*min(1,ap*1.6)):0
const sq=t<.12?.12*sin(PI*t/.12):t>.56&&t<.7?.14*sin(PI*(t-.56)/.14):0,g=t>.7?sin(PI*(t-.7)/.3):0
const cx=128,base=214-h,W=64*(1+sq),H=96*(1-sq)+10*ext,y0=base-H,bl=t>.9&&t<.96
for(const d of[-1,1]){
fill(B,()=>c.ellipse(cx+d*W*.78,base-14+8*ext,22-6*ext,15+10*ext,d*(.5-.5*ext),0,T))
fill(B,()=>c.ellipse(cx+d*(W*.98-14*ext),base+18*ext,17,6,d*.2,0,T))
}
fill(B,()=>{c.moveTo(cx,y0+4);c.bezierCurveTo(cx+W*.9,y0-4,cx+W*1.12,y0+H*.4,cx+W*.72,y0+H*.72)
c.bezierCurveTo(cx+W*.44,y0+H*1.02,cx+W*.14,base,cx,base);c.bezierCurveTo(cx-W*.14,base,cx-W*.44,y0+H*1.02,cx-W*.72,y0+H*.72)
c.bezierCurveTo(cx-W*1.12,y0+H*.4,cx-W*.9,y0-4,cx,y0+4)})
for(const d of[-1,1])fill(B,()=>c.arc(cx+d*32,y0+8,19,0,T))
for(const a of[-2.6,-2,-1.15,-.5])fill(G,()=>c.ellipse(cx+15*cos(a),y0+4+10*sin(a),14,6,a,0,T))
c.strokeStyle=G;c.lineWidth=5;c.lineCap='round';c.beginPath();c.moveTo(cx,y0);c.lineTo(cx+3,y0-14);c.stroke()
fill(S,()=>{for(const[x,y]of[[-30,.5],[30,.5],[-14,.64],[14,.64],[0,.78],[-24,.74],[24,.74]])c.moveTo(cx+x*W/64+2.5,y0+H*y),c.ellipse(cx+x*W/64,y0+H*y,2.5,4,0,0,T)})
if(g>0)fill(L,()=>c.ellipse(cx,y0+H*.46+6*g,14*g+4,11*g+3,0,0,T))
for(const d of[-1,1])fill(B,()=>c.ellipse(cx+d*16,base-2+6*ext,11,6,0,0,T))
c.fillStyle=c.strokeStyle=D
for(const d of[-1,1])if(bl)c.lineWidth=4,c.beginPath(),c.moveTo(cx+d*32-7,y0+8),c.lineTo(cx+d*32+7,y0+8),c.stroke()
else c.beginPath(),c.arc(cx+d*32,y0+8,7,0,T),c.fill()
c.lineWidth=4;c.beginPath();c.arc(cx,y0+H*.2,22,.2*PI,.8*PI);c.stroke()
}
+26
View File
@@ -0,0 +1,26 @@
// thunderbird hovering: lightning-bolt wings beat twice a loop and little sparks crackle off the tips at the top of each beat
const{sin,cos,PI,max}=Math,T=2*PI
export const frames=24,delay=80
const B='#ff7a5c',L='#ffbdad',A='#f4c03e',D='#2b2633'
export default(c,t)=>{
const f=sin(2*T*t-PI/2),bl=t>.66&&t<.72
const fill=(col,g)=>{c.fillStyle=c.strokeStyle=col;c.lineWidth=6;c.beginPath();g();c.fill();c.stroke()}
const bolt=()=>{for(const[x,y]of[[-14,0],[14,0],[0,32],[18,32],[-18,84],[-8,44],[-26,44]])c.lineTo(x,y);c.closePath()}
c.translate(0,5*sin(2*T*t+1.4))
for(const d of[-1,1]){c.save();c.translate(128+d*20,128);c.scale(d,1);c.rotate(-2.3+.32*f);fill(A,bolt);c.restore()}
c.save();c.translate(128,176);c.scale(.55,.55);fill(A,bolt);c.restore()
fill(B,()=>c.ellipse(128,144,30,38,0,0,T))
fill(L,()=>c.ellipse(128,152,18,24,0,0,T))
fill(A,()=>{for(const[x,y]of[[112,82],[116,60],[124,74],[130,50],[136,72],[144,58],[144,82]])c.lineTo(x,y);c.closePath()})
fill(B,()=>c.arc(128,98,26,0,T))
fill(A,()=>{c.moveTo(122,102);c.lineTo(134,102);c.lineTo(128,112);c.closePath()})
c.fillStyle=c.strokeStyle=D
for(const d of[-1,1])if(bl)c.lineWidth=3.5,c.beginPath(),c.arc(128+d*12,92,5,.15*PI,.85*PI),c.stroke()
else c.beginPath(),c.arc(128+d*12,94,4.5,0,T),c.fill()
// sparks: little four-point stars popping near each wing tip while the wings are up
const z=max(0,-f)**3
if(z>.05)for(const d of[-1,1])for(const[x,y,s]of[[92,40,1],[78,74,.7],[104,22,.6]]){
const r=10*s*z,cx=128+d*x,cy=y+6
fill(A,()=>{c.moveTo(cx,cy-r);c.quadraticCurveTo(cx,cy,cx+r,cy);c.quadraticCurveTo(cx,cy,cx,cy+r);c.quadraticCurveTo(cx,cy,cx-r,cy);c.quadraticCurveTo(cx,cy,cx,cy-r)})
}
}
+34
View File
@@ -0,0 +1,34 @@
// striped tiger doing a big cat stretch, rump up and front paws out, eyes shut and a little yawn, then back to standing
const{sin,cos,PI,hypot}=Math,T=2*PI
export const frames=24,delay=85
const B='#ff9f43',L='#ffd2a6',A='#b5531c',D='#2b2633'
const mix=(a,b,k)=>a+(b-a)*k
export default(c,t)=>{
const s=(1-cos(T*t))/2,k=s*s*(3-2*s)
const fill=(col,f)=>{c.fillStyle=c.strokeStyle=col;c.lineWidth=6;c.beginPath();f();c.fill();c.stroke()}
const line=(col,lw,f)=>{c.strokeStyle=col;c.lineWidth=lw;c.beginPath();f();c.stroke()}
const hx=mix(80,78,k),hy=mix(134,108,k),sx=mix(166,160,k),sy=mix(136,176,k),px=mix(170,214,k)
const tail=()=>{c.moveTo(hx-12,hy-6);c.quadraticCurveTo(mix(36,52,k),mix(132,90,k),mix(44,72,k),mix(88,46,k))}
line(B,13,tail);c.lineCap='butt';c.setLineDash([9,11]);line(A,13,tail);c.setLineDash([]);c.lineCap='round'
for(const[x,o]of[[hx+12,0],[hx-6,8]])line(B,19,()=>{c.moveTo(x,hy+10);c.lineTo(x+o-2,214)})
for(const o of[12,-4])line(B,19,()=>{c.moveTo(sx+o*.4,sy+6);c.lineTo(px+o,214)})
line(B,62,()=>{c.moveTo(hx,hy);c.lineTo(sx,sy)})
const dx=sx-hx,dy=sy-hy,l=hypot(dx,dy),ux=dx/l,uy=dy/l
for(const f of[.18,.38,.58,.78]){
const x=hx+dx*f,y=hy+dy*f
line(A,7,()=>{c.moveTo(x+uy*30,y-ux*30);c.lineTo(x+uy*12+ux*4,y-ux*12+uy*4)})
}
const ex=mix(196,204,k),ey=mix(104,166,k),r=mix(0,.25,k)
c.save();c.translate(ex,ey);c.rotate(r)
for(const d of[-1,1])fill(B,()=>{c.moveTo(d*8-9,-22);c.lineTo(d*20,-36);c.lineTo(d*28+(d>0?-2:2),-14);c.closePath()})
fill(B,()=>c.arc(0,0,30,0,T))
fill(L,()=>c.ellipse(10,12,18,12,0,0,T))
for(const y of[-2,8])line(A,5,()=>{c.moveTo(-28,y);c.lineTo(-16,y+2)})
line(A,5,()=>{c.moveTo(4,-30);c.lineTo(4,-20)})
c.fillStyle=c.strokeStyle=D
if(k>.55)c.lineWidth=4,c.beginPath(),c.arc(10,-4,5.5,.15*PI,.85*PI),c.stroke()
else c.beginPath(),c.arc(10,-4,5,0,T),c.fill()
c.beginPath();c.ellipse(22,6,4.5,3.5,0,0,T);c.fill()
if(k>.7)c.beginPath(),c.ellipse(14,18,6,7*(k-.7)/.3+1,0,0,T),c.fill()
c.restore()
}
+18
View File
@@ -0,0 +1,18 @@
// walrus sitting up: tusks and a whiskery muzzle, flopping both front flippers, head bobbing, a slow blink
const{sin,PI}=Math,T=2*PI
export const frames=24,delay=80
const B='#c9a27e',L='#e6d3bf',W='#fff',D='#2b2633'
export default(c,t)=>{
const f=sin(T*t*2),h=3*sin(T*t*2-1),bl=t>.7&&t<.78,fill=(col,g)=>{c.fillStyle=c.strokeStyle=col;c.lineWidth=6;c.beginPath();g();c.fill();c.stroke()}
for(const d of[-1,1])fill(B,()=>c.ellipse(128+d*40,212,26,9,d*.15,0,T))
for(const d of[-1,1]){c.save();c.translate(128+d*60,170);c.rotate(-d*(.35+.4*f));fill(B,()=>c.ellipse(d*22,8,14,30,-d*.7,0,T));c.restore()}
fill(B,()=>c.ellipse(128,162,70,52,0,0,T))
c.translate(0,h)
fill(B,()=>c.ellipse(128,108,52,44,0,0,T))
for(const d of[-1,1])fill(W,()=>{c.moveTo(128+d*8,132);c.quadraticCurveTo(128+d*12,160,128+d*16,178);c.quadraticCurveTo(128+d*22,156,128+d*22,130)})
for(const d of[-1,1])fill(L,()=>c.arc(128+d*15,124,17,0,T))
c.fillStyle=c.strokeStyle=D;c.beginPath();c.ellipse(128,109,7,5,0,0,T);c.fill()
for(const d of[-1,1])for(const[x,y]of[[9,123],[17,129],[22,120]])c.beginPath(),c.arc(128+d*x,y,2,0,T),c.fill()
for(const d of[-1,1])if(bl)c.lineWidth=3.5,c.beginPath(),c.arc(128+d*22,90,5,.15*PI,.85*PI),c.stroke()
else c.beginPath(),c.arc(128+d*22,92,5,0,T),c.fill()
}
+33
View File
@@ -0,0 +1,33 @@
// whale carrying a tiny island: it bobs, its palm leaves sway, its tail flicks and it puffs a spout now and then
const{sin,cos,PI,min,max}=Math,T=2*PI
export const frames=24,delay=85
const B='#4ea8de',L='#a7d4ef',S='#f4c03e',G='#8cc084',D='#2b2633'
export default(c,t)=>{
const fill=(col,f)=>{c.fillStyle=c.strokeStyle=col;c.lineWidth=6;c.beginPath();f();c.fill();c.stroke()}
const shape=(x,y,r,col,f)=>{c.save();c.translate(x,y);c.rotate(r);fill(col,f);c.restore()}
const k=sin(T*t-1.4),tx=210-4*k,ty=94-5*k,bl=t>.8&&t<.86,p=(t-.1)/.45,sw=.08*sin(T*t*2)
c.translate(128,134+6*sin(T*t));c.rotate(-.03*cos(T*t));c.scale(.86,.86);c.translate(-132,-128)
const body=()=>{c.moveTo(106,88);c.bezierCurveTo(60,86,30,114,32,148);c.bezierCurveTo(34,184,66,200,108,200)
c.bezierCurveTo(158,200,204,180,212,136);c.quadraticCurveTo(tx+10,116,tx+7,ty);c.lineTo(tx-7,ty)
c.quadraticCurveTo(tx-8,120,184,124);c.bezierCurveTo(172,100,144,88,106,88)}
shape(tx,ty,.12-.2*k,B,()=>{c.moveTo(0,6);c.quadraticCurveTo(16,0,36,-22);c.quadraticCurveTo(16,-22,0,-8)
c.quadraticCurveTo(-12,-20,-26,-20);c.quadraticCurveTo(-12,0,0,6)})
fill(B,body)
c.save();c.beginPath();body();c.clip();c.fillStyle=L;c.beginPath();c.ellipse(104,204,92,30,0,0,T);c.fill();c.restore()
shape(110,192,.6+.35*sin(T*t-2),B,()=>c.ellipse(20,0,24,9,0,0,T))
fill(S,()=>c.ellipse(126,94,36,15,0,PI,T))
c.strokeStyle=S;c.lineWidth=8;c.lineCap='round';c.beginPath();c.moveTo(130,86);c.quadraticCurveTo(126+30*sw,58,142+60*sw,34);c.stroke()
// palm crown: crescent fronds arching out and drooping
c.save();c.translate(142+60*sw,34);c.rotate(sw*1.5)
for(const[ex,ey]of[[-48,18],[-34,-12],[0,-24],[34,-12],[48,18]])fill(G,()=>{c.moveTo(0,0);c.quadraticCurveTo(ex*.5,ey*.5-(ey<0?6:18),ex,ey);c.quadraticCurveTo(ex*.5,ey*.5+(ey<0?6:-4),0,0)})
c.restore()
if(p>0&&p<1){
const g=sin(PI*p),x=66,y=96,h=46*g,q=max(0,(p-.25)/.75)
c.strokeStyle=L;c.lineWidth=10*g+2;c.beginPath();c.moveTo(x,y);c.lineTo(x,y-h);c.stroke()
c.fillStyle=L;for(const d of[-1,1])c.beginPath(),c.arc(x+d*24*q,y-h-8+30*q*q,6*(1-.4*q),0,T),c.fill()
}
c.fillStyle=c.strokeStyle=D
if(bl)c.lineWidth=4,c.beginPath(),c.arc(66,128,6,.15*PI,.85*PI),c.stroke()
else c.beginPath(),c.arc(66,130,6,0,T),c.fill()
c.lineWidth=3.5;c.beginPath();c.moveTo(40,160);c.quadraticCurveTo(50,168,62,166);c.stroke()
}
+29
View File
@@ -0,0 +1,29 @@
// chunky wombat digging nose-down into a dirt mound, paws scooping, clods flying back over its back
const{sin,cos,PI,max}=Math,T=2*PI
export const frames=24,delay=70
const B='#8a94a6',L='#c5cad3',A='#b5835a',D='#2b2633'
export default(c,t)=>{
const rk=.035*sin(T*t*2)
const fill=(col,f)=>{c.fillStyle=c.strokeStyle=col;c.lineWidth=6;c.beginPath();f();c.fill();c.stroke()}
c.translate(0,-14)
for(let k=0;k<3;k++){
const u=(t*2+k/3)%1,x=190-160*u,y=196-4*124*u*(1-u)+20*u,r=7-2*u
fill(A,()=>c.arc(x,y,r,0,T))
}
fill(A,()=>c.ellipse(46,212,28,10,0,0,T))
fill(A,()=>{c.moveTo(186,216);c.quadraticCurveTo(212,184,238,216);c.closePath()})
c.save();c.translate(160,210);c.rotate(rk);c.translate(-160,-210)
for(const[x,y]of[[70,206],[96,210]])fill(B,()=>c.ellipse(x,y,15,9,0,0,T))
for(const[k,o]of[[0,0],[1,PI]]){
const a=.7*sin(T*t*2+o)
c.save();c.translate(170+k*8,194);c.rotate(a);fill(B,()=>c.ellipse(10,12,9,15,-.4,0,T));c.restore()
}
fill(B,()=>c.ellipse(112,166,62,44,-.1,0,T))
fill(B,()=>c.arc(56,150,8,0,T))
for(const[x,r]of[[162,-.3],[176,.2]])fill(B,()=>c.ellipse(x,140,7,10,r,0,T))
fill(B,()=>c.ellipse(178,172,32,27,.25,0,T))
fill(L,()=>c.ellipse(198,182,14,12,.25,0,T))
c.fillStyle=D;c.beginPath();c.ellipse(205,180,9,7,.25,0,T);c.fill()
c.beginPath();c.arc(182,160,4.5,0,T);c.fill()
c.restore()
}
+32
View File
@@ -0,0 +1,32 @@
// little wyvern hovering: bat wings beating twice a loop, tail curling below, tiny horns, blinks
const{sin,PI}=Math,T=2*PI
export const frames=24,delay=80
const B='#8cc084',L='#c6e0c2',A='#f4c03e',D='#2b2633'
export default(c,t)=>{
const f=sin(2*T*t),bl=t>.42&&t<.48
const fill=(col,g)=>{c.fillStyle=c.strokeStyle=col;c.lineWidth=6;c.beginPath();g();c.fill();c.stroke()}
const line=(col,lw,g)=>{c.strokeStyle=col;c.lineWidth=lw;c.beginPath();g();c.stroke()}
c.translate(0,6*sin(2*T*t+1.3))
// tail curling down and to the side, tapering, with a spade tip
const sw=6*sin(T*t)
for(let s=0;s<=1;s+=.02){const u=1-s,x=u*u*128+2*u*s*(140+sw)+s*s*(176+sw),y=u*u*168+2*u*s*222+s*s*(196);c.fillStyle=B;c.beginPath();c.arc(x,y,9-5*s,0,T);c.fill()}
fill(B,()=>{c.moveTo(174+sw,198);c.lineTo(186+sw,178);c.lineTo(192+sw,200);c.closePath()})
for(const d of[-1,1]){
c.save();c.translate(128+d*16,122);c.scale(d*.92,.92);c.rotate(-.35*f)
fill(L,()=>{c.moveTo(0,12);c.lineTo(46,-40);c.lineTo(92,-30);c.quadraticCurveTo(76,-18,84,6);c.quadraticCurveTo(64,4,58,28);c.quadraticCurveTo(34,14,8,28)})
line(B,6,()=>{c.moveTo(0,0);c.lineTo(46,-40);c.lineTo(92,-30);c.moveTo(46,-40);c.lineTo(84,6);c.moveTo(46,-40);c.lineTo(58,28)})
c.restore()
}
for(const d of[-1,1])fill(B,()=>c.ellipse(128+d*14,174,8,10,0,0,T))
fill(B,()=>c.ellipse(128,140,30,36,0,0,T))
fill(L,()=>c.ellipse(128,148,17,23,0,0,T))
for(const d of[-1,1])fill(A,()=>{c.moveTo(128+d*10,72);c.quadraticCurveTo(128+d*16,52,128+d*28,48);c.quadraticCurveTo(128+d*24,62,128+d*24,76)})
fill(B,()=>c.arc(128,94,30,0,T))
fill(L,()=>c.ellipse(128,108,16,10,0,0,T))
c.fillStyle=c.strokeStyle=D
for(const d of[-1,1]){
c.beginPath();c.arc(128+d*5,106,2.2,0,T);c.fill()
if(bl)c.lineWidth=3.5,c.beginPath(),c.arc(128+d*14,86,5,.15*PI,.85*PI),c.stroke()
else c.beginPath(),c.arc(128+d*14,88,4.5,0,T),c.fill()
}
}

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