diff --git a/gen/color/arcade/blink.js b/gen/color/arcade/blink.js new file mode 100644 index 0000000..6d9be9a --- /dev/null +++ b/gen/color/arcade/blink.js @@ -0,0 +1,33 @@ +// arcade cabinet whose screen shows a blinking pixel face, joystick wiggling, buttons pressing, marquee lights chasing +const{sin,cos,PI,floor}=Math,T=2*PI +export const frames=16,delay=90 +const CAB='#9775fa',CD='#7048e8',MQ='#ffd43b',BUL='#ffa94d',BEZ='#495057',SCR='#212529',PX='#69db7c',HT='#ff6b6b',PANEL='#4dabf7',PD='#1c7ed6',BTN=['#ffd43b','#69db7c'],COIN='#ffa94d' +const FACE=['..........','..##..##..','..##..##..','..........','.#......#.','..######..'] +const BLINK=['..........','..........','..##..##..','..........','.#......#.','..######..'] +const HEART=['.#.#.','#####','.###.','..#..'] +const px=(c,rows,x,y,s,col)=>{c.fillStyle=col;rows.forEach((r,j)=>[...r].forEach((ch,k)=>{if(ch=='#')c.fillRect(x+k*s,y+j*s,s,s)}))} +export default(c,t,i)=>{ + const js=sin(T*t*2),b=floor(t*4)%2,pr=(t*4)%1<.5 + const body=()=>{c.beginPath();c.roundRect(62,40,132,208,12)} + c.fillStyle=CAB;body();c.fill();c.stroke() + c.save();body();c.clip();c.fillStyle=CD;c.fillRect(62,40,12,208);c.fillRect(182,40,12,208);c.restore() + c.lineWidth=5;for(const x of[74,182])c.beginPath(),c.moveTo(x,40),c.lineTo(x,248),c.stroke() + c.lineWidth=7;body();c.stroke() + c.fillStyle=MQ;c.beginPath();c.roundRect(52,12,152,44,12);c.fill();c.stroke() + for(let k=0;k<5;k++){const lit=(k+floor(t*16))%5<2;c.fillStyle=lit?'#fff':BUL;c.lineWidth=4;c.beginPath();c.arc(80+k*24,34,7,0,T);c.fill();c.stroke()} + c.lineWidth=7;c.fillStyle=BEZ;c.beginPath();c.roundRect(72,64,112,94,10);c.fill();c.stroke() + c.fillStyle=SCR;c.beginPath();c.roundRect(80,72,96,78,6);c.fill() + const fl=i==6 + px(c,i==11||i==12?BLINK:FACE,98,92,6,fl?'#fff':PX) + if(floor(t*8)%2==0)px(c,HEART,84,76,3,HT) + c.fillStyle='rgba(255,255,255,.06)';for(let y=74;y<150;y+=4)c.fillRect(80,y,96,1.5) + c.lineWidth=7;c.fillStyle=PANEL;c.beginPath();c.moveTo(48,162);c.lineTo(208,162);c.lineTo(200,194);c.lineTo(56,194);c.closePath();c.fill();c.stroke() + c.fillStyle=PD;c.beginPath();c.ellipse(96,178,15,6,0,0,T);c.fill();c.lineWidth=5;c.stroke() + const bx=96+12*js,by=150 + for(const[w,st]of[[10,'#111'],[4,'#ced4da']])c.lineWidth=w,c.strokeStyle=st,c.beginPath(),c.moveTo(96,177),c.lineTo(bx,by),c.stroke() + c.strokeStyle='#111';c.fillStyle=HT;c.lineWidth=5;c.beginPath();c.arc(bx,by,11,0,T);c.fill();c.stroke() + c.fillStyle='#fff';c.beginPath();c.arc(bx-4,by-4,3,0,T);c.fill() + BTN.forEach((col,k)=>{const x=146+k*28,y=178-k*3,on=k==b&≺c.fillStyle=col;c.beginPath();c.ellipse(x,y+(on?2:0),11,on?5:7,0,0,T);c.fill();c.lineWidth=5;c.stroke()}) + c.fillStyle=COIN;c.lineWidth=6;c.beginPath();c.roundRect(104,206,48,32,6);c.fill();c.stroke() + c.fillStyle='#343a40';for(const x of[114,136])c.beginPath(),c.roundRect(x,213,6,16,2),c.fill() +} diff --git a/gen/color/aurora/shimmer.js b/gen/color/aurora/shimmer.js new file mode 100644 index 0000000..4a8e1b9 --- /dev/null +++ b/gen/color/aurora/shimmer.js @@ -0,0 +1,31 @@ +// northern lights ripple over snowy hills inside a rounded night-sky sticker, stars twinkling +// the dark sky gets a white rim so it doesn't vanish on dark backgrounds +const{sin,max}=Math,T=2*Math.PI +export const frames=16,delay=90 +const SKY='#2b3a7a',R=[['#b197fc','#e5dbff',62,3.1,.1],['#38d9a9','#c3fae8',82,1.7,-.08],['#69db7c','#d3f9d8',104,0,.05]] +const spk=(c,x,y,s)=>{c.lineWidth=3;c.fillStyle='#fff';c.beginPath();c.moveTo(x,y-s);for(const[px,py,qx,qy]of[[s,0,s*.2,-s*.2],[0,s,s*.2,s*.2],[-s,0,-s*.2,s*.2],[0,-s,-s*.2,-s*.2]])c.quadraticCurveTo(x+qx,y+qy,x+px,y+py);c.fill();c.stroke()} +const pine=(c,x,y)=>{ + c.fillStyle='#a9744f';c.lineWidth=5;c.beginPath();c.rect(x-4,y-6,8,12);c.fill();c.stroke() + c.fillStyle='#2f9e44';for(const[w,y0,h]of[[18,-6,18],[14,-18,16],[10,-28,14]])c.beginPath(),c.moveTo(x-w,y+y0),c.lineTo(x,y+y0-h),c.lineTo(x+w,y+y0),c.closePath(),c.fill(),c.stroke() +} +export default(c,t)=>{ + const a=t*T,win=()=>{c.beginPath();c.roundRect(14,26,228,206,48)} + win();c.lineWidth=18;c.strokeStyle='#fff';c.stroke();c.fillStyle=SKY;c.fill();c.strokeStyle='#111' + c.save();win();c.clip() + for(const[x,y,o]of[[46,52,0],[208,48,.35],[134,40,.7],[226,100,.2],[34,112,.55]])spk(c,x,y,2+4*max(0,sin((t+o)*2*T))) + for(const[col,lt,b,p,sl]of R){ + const top=x=>b+(x-128)*sl+12*sin(x/38+p+a)+5*sin(x/17-p-2*a),h=x=>34+10*sin(x/29+p*2-a) + c.beginPath();for(let x=0;x<=256;x+=8)c.lineTo(x,top(x));for(let x=256;x>=0;x-=8)c.lineTo(x,top(x)+h(x));c.closePath() + c.fillStyle=col;c.fill();c.lineWidth=4;c.stroke() + c.save();c.clip();c.strokeStyle=lt;c.lineWidth=3 + for(let x=14*t-14;x<270;x+=14){const y0=top(x);c.beginPath();c.moveTo(x,y0+6);c.lineTo(x,y0+h(x)*.72);c.stroke()} + c.restore();c.strokeStyle='#111' + } + c.lineWidth=7 + for(const[x0,x1,px,py]of[[96,300,206,104],[-40,170,56,92]]){ + c.fillStyle='#fff';c.beginPath();c.moveTo(x0,250);c.quadraticCurveTo(px,py,x1,250);c.closePath();c.fill();c.stroke() + c.strokeStyle='#a5d8ff';c.lineWidth=5;c.beginPath();c.moveTo(px-30,(250+py)/2+22);c.quadraticCurveTo(px-6,(250+py)/2+12,px+16,(250+py)/2+20);c.stroke();c.strokeStyle='#111';c.lineWidth=7 + } + pine(c,190,186);pine(c,214,196);pine(c,58,182) + c.restore();c.lineWidth=7;win();c.stroke() +} diff --git a/gen/color/axolotl/wave.js b/gen/color/axolotl/wave.js new file mode 100644 index 0000000..42b6858 --- /dev/null +++ b/gen/color/axolotl/wave.js @@ -0,0 +1,38 @@ +// pink axolotl waving a little hand, its feathery gill fronds wiggling, bubbles drifting up +const{sin,cos,PI}=Math,T=2*PI +export const frames=16,delay=80 +const PK='#faa2c1',PD='#f06595',PL='#ffdeeb',BL='#ff8fab',BU='#a5d8ff' +export default(c,t)=>{ + const wv=sin(2*T*t),tl=.12*sin(T*t) + const D=(wd,cl,f)=>{for(const[l,s]of[[wd,'#111'],[wd-10,cl]])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),f(),c.stroke();c.lineWidth=7;c.strokeStyle='#111'} + for(const[x,y0,r,o]of[[204,0,9,0],[222,0,6,.4],[196,0,5,.75]]){const p=(t+o)%1,y=70-p*60;if(p<.85){c.fillStyle=BU;c.lineWidth=4;c.beginPath();c.arc(x+3*sin(p*T*2),y,r,0,T);c.fill();c.stroke();c.fillStyle='#fff';c.beginPath();c.arc(x+3*sin(p*T*2)-r*.35,y-r*.35,r*.25,0,T);c.fill()}} + c.lineWidth=7 + c.save();c.translate(150,200);c.rotate(tl) + c.fillStyle=PK;c.beginPath();c.moveTo(0,-14);c.quadraticCurveTo(70,-30,92,6);c.quadraticCurveTo(60,20,0,16);c.closePath();c.fill();c.stroke() + c.fillStyle=PL;c.beginPath();c.moveTo(20,-12);c.quadraticCurveTo(66,-24,84,0);c.quadraticCurveTo(60,-6,20,-4);c.closePath();c.fill() + c.restore() + c.fillStyle=PK;for(const[x,d]of[[96,-1],[160,1]])c.beginPath(),c.ellipse(x,226,15,10,d*.3,0,T),c.fill(),c.stroke() + c.beginPath();c.ellipse(128,196,48,36,0,0,T);c.fill();c.stroke() + c.fillStyle=PL;c.beginPath();c.ellipse(128,206,28,20,0,0,T);c.fill() + for(const d of[-1,1])for(let k=0;k<3;k++){ + const a=d*(-.55+k*.45)+.1*sin(2*T*t+k*1.3+(d>0?1:0)),bx=128+d*58,by=104+k*18-18 + c.save();c.translate(bx,by);c.rotate(d>0?a:PI+a) + D(14,PD,()=>{c.moveTo(0,0);c.lineTo(36,0)}) + c.fillStyle=PD;c.lineWidth=4;for(const x of[12,24])for(const s of[-1,1])c.beginPath(),c.ellipse(x,s*7,5,3,s*.6,0,T),c.fill(),c.stroke() + c.restore();c.lineWidth=7 + } + c.fillStyle=PK;c.beginPath();c.ellipse(128,118,70,52,0,0,T);c.fill();c.stroke() + for(const d of[-1,1]){c.fillStyle='#111';c.beginPath();c.arc(128+d*34,110,8,0,T);c.fill();c.fillStyle='#fff';c.beginPath();c.arc(128+d*34-2,107,3,0,T);c.fill()} + c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*46,128,10,6,0,0,T),c.fill() + c.lineWidth=5;c.beginPath();c.moveTo(104,126);c.quadraticCurveTo(128,146,152,126);c.stroke() + c.fillStyle=PK;c.lineWidth=7 + c.beginPath();c.ellipse(92,192,12,9,.4,0,T);c.fill();c.stroke() + c.save();c.translate(166,182);c.rotate(-.9+.35*wv) + D(20,PK,()=>{c.moveTo(0,0);c.lineTo(30,0)}) + c.translate(36,0);c.fillStyle=PK + c.lineWidth=5;for(const a of[-.7,0,.7])c.beginPath(),c.ellipse(cos(a)*9,sin(a)*9,6,4,a,0,T),c.fill(),c.stroke() + c.beginPath();c.arc(0,0,9,0,T);c.fill();c.stroke() + c.restore() + for(const d of[-1,1])c.lineWidth=4,c.beginPath(),c.arc(224,138+d*12,10,-.6,.6),c.stroke() + c.lineWidth=7 +} diff --git a/gen/color/blender/whirl.js b/gen/color/blender/whirl.js new file mode 100644 index 0000000..5dca6f6 --- /dev/null +++ b/gen/color/blender/whirl.js @@ -0,0 +1,33 @@ +// blender whirling a pink smoothie, fruit chunks circling in the vortex while the lid rattles and the base hums +const{sin,cos,PI,exp}=Math,T=2*PI +export const frames=16,delay=60 +const GL='#e7f5ff',SM='#f783ac',SL='#faa2c1',BA='#ff6b6b',BD='#e03131',LID='#b197fc',BL='#ffc9c9',FR=['#ff6b6b','#fff3bf','#69db7c','#748ffc','#ffa94d','#69db7c'] +const LV=[[96,34],[120,26],[142,18]] +const jar=c=>{c.beginPath();c.moveTo(70,42);c.lineTo(186,42);c.lineTo(172,164);c.lineTo(84,164);c.closePath()} +export default(c,t,i)=>{ + const sp=T*t*2,ly=i%2?-3:1,lr=i%2?.03:-.02 + for(const[w,s]of[[20,'#111'],[10,GL]])c.lineWidth=w,c.strokeStyle=s,c.beginPath(),c.moveTo(182,66),c.lineTo(208,72),c.lineTo(204,128),c.lineTo(176,136),c.stroke() + c.strokeStyle='#111';c.lineWidth=7 + jar(c);c.fillStyle=GL;c.fill() + const ch=(back)=>FR.forEach((col,k)=>{const[y,rx]=LV[k%3],a=sp+k*PI+(k%3)*1.1;if((sin(a)<0)!=back)return;const s=back?.75:1;c.save();c.translate(128+cos(a)*rx*1.6,y+sin(a)*6);c.rotate(a*2+k);c.scale(s,s);c.fillStyle=col;c.lineWidth=3.5;c.beginPath();c.roundRect(-7,-7,14,14,4);c.fill();c.stroke();c.restore()}) + c.save();jar(c);c.clip() + c.fillStyle=SM;c.beginPath();c.moveTo(60,72);for(let x=60;x<=196;x+=4)c.lineTo(x,72+30*exp(-(((x-128)/22)**2))+2*sin(x/9-sp*2));c.lineTo(196,170);c.lineTo(60,170);c.closePath();c.fill();c.lineWidth=5;c.stroke() + ch(true) + c.strokeStyle=SL;c.lineWidth=5;for(const[y,rx]of LV)for(const o of[0,PI])c.beginPath(),c.ellipse(128,y,rx*1.6,7,0,sp+o+.3,sp+o+1.5),c.stroke() + ch(false) + c.strokeStyle='#fff';c.lineWidth=7;c.beginPath();c.moveTo(86,58);c.lineTo(94,146);c.stroke() + c.restore();c.strokeStyle='#111';c.lineWidth=7;jar(c);c.stroke() + c.lineWidth=4;for(const y of[72,98,124])c.beginPath(),c.moveTo(164-(y-42)*.11,y),c.lineTo(176-(y-42)*.11,y),c.stroke() + c.lineWidth=7 + c.save();c.translate(128,36+ly);c.rotate(lr);c.fillStyle=LID;c.beginPath();c.roundRect(-66,-8,132,16,8);c.fill();c.stroke();c.beginPath();c.roundRect(-16,-20,32,14,6);c.fill();c.stroke();c.restore() + c.fillStyle=BD;c.beginPath();c.roundRect(78,160,100,14,6);c.fill();c.stroke() + c.fillStyle=BA;c.beginPath();c.roundRect(66,172,124,62,[12,12,18,18]);c.fill();c.stroke() + c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.arc(128+d*24,192,6,0,T),c.fill() + c.fillStyle='#fff';for(const d of[-1,1])c.beginPath(),c.arc(128+d*24-2,190,2.2,0,T),c.fill() + c.fillStyle='#111';c.beginPath();c.ellipse(128,206,7,8,0,0,T);c.fill() + c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*44,204,9,5,0,0,T),c.fill() + c.lineWidth=4;['#fff','#ffd43b','#69db7c'].forEach((col,k)=>{c.fillStyle=k==Math.floor(t*3)?col:'#dee2e6';c.beginPath();c.arc(104+k*24,224,4.5,0,T);c.fill();c.stroke()}) + c.lineWidth=7 + for(const d of[-1,1])for(const[w,s]of[[9,'#111'],[4,'#fff']]){c.lineWidth=w;c.strokeStyle=s;c.beginPath();const x=128+d*(i%2?78:82);c.moveTo(x,186);c.quadraticCurveTo(x+d*6,200,x,214);c.stroke()} + c.strokeStyle='#111';c.lineWidth=7 +} diff --git a/gen/color/capybara/soak.js b/gen/color/capybara/soak.js new file mode 100644 index 0000000..7be13f9 --- /dev/null +++ b/gen/color/capybara/soak.js @@ -0,0 +1,42 @@ +// capybara soaking in a hot spring with a yuzu on its head, eyes shut in bliss, bobbing gently as steam curls up +const{sin,cos,PI}=Math,T=2*PI +export const frames=16,delay=100 +const SN='#d9a77a',BR='#c08552',BD='#8d6346',WA='#74c0fc',WL='#d0ebff',ST='#ced4da',SD='#adb5bd',YZ='#ffd43b',YD='#fab005',LF='#51cf66',BL='#ffa8a8' +export default(c,t)=>{ + const b=Math.round(3*sin(T*t)),yw=.1*sin(T*t-.8) + const D=(wd,cl,f)=>{for(const[l,s]of[[wd,'#111'],[wd-8,cl]])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),f(),c.stroke();c.lineWidth=7;c.strokeStyle='#111'} + for(const[x,o]of[[56,0],[204,.5]]){const p=(t+o)%1,a=sin(PI*p) + if(a>.15)D(12,'#fff',()=>{for(let k=0;k<=8;k++){const u=k/8;c.lineTo(x+7*sin(u*T+p*T),70-p*24-u*40)}})} + c.fillStyle=ST + for(const[x,y,rx,ry]of[[36,184,26,18],[220,182,28,20],[70,226,30,16],[188,228,32,16]])c.beginPath(),c.ellipse(x,y,rx,ry,0,0,T),c.fill(),c.stroke() + c.fillStyle=WA;c.beginPath();c.ellipse(128,198,104,38,0,0,T);c.fill();c.stroke() + c.save();c.translate(0,b) + c.fillStyle=BR + for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*40,84,11,9,0,0,T),c.fill(),c.stroke() + c.beginPath();c.roundRect(68,80,120,130,[52,52,30,30]);c.fill();c.stroke() + c.fillStyle=BD;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*40,85,5,4,0,0,T),c.fill() + c.fillStyle=SN;c.beginPath();c.ellipse(128,156,40,28,0,0,T);c.fill();c.lineWidth=5;c.stroke();c.lineWidth=7 + c.fillStyle=BD;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*14,148,6,4,0,0,T),c.fill() + c.lineWidth=5;c.beginPath();c.moveTo(128,152);c.lineTo(128,162);c.moveTo(116,166);c.quadraticCurveTo(128,172,140,166);c.stroke() + for(const d of[-1,1])c.beginPath(),c.arc(128+d*30,118,9,1.15*PI,1.85*PI),c.stroke() + c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*44,132,8,5,0,0,T),c.fill() + c.save();c.translate(128,70);c.rotate(yw) + c.lineWidth=7;c.fillStyle=YZ;c.beginPath();c.ellipse(0,-14,26,22,0,0,T);c.fill();c.stroke() + c.fillStyle=YD;for(const[x,y]of[[-10,-20],[6,-8],[12,-22],[-4,-6]])c.beginPath(),c.arc(x,y,2,0,T),c.fill() + c.fillStyle='#fff';c.beginPath();c.ellipse(-10,-24,6,4,-.5,0,T);c.fill() + c.fillStyle=LF;c.lineWidth=5;c.beginPath();c.ellipse(10,-40,12,6,-.4,0,T);c.fill();c.stroke() + c.beginPath();c.moveTo(0,-36);c.lineTo(2,-42);c.stroke() + c.restore() + c.restore() + c.lineWidth=7 + const wy=x=>196+3*sin(x/14+T*t*2) + c.save();c.beginPath();c.ellipse(128,198,104,38,0,0,T);c.clip() + c.fillStyle=WA;c.beginPath();c.moveTo(20,240);for(let x=20;x<=236;x+=4)c.lineTo(x,wy(x));c.lineTo(236,240);c.closePath();c.fill() + c.beginPath();for(let x=60;x<=196;x+=4)c.lineTo(x,wy(x));c.stroke() + c.strokeStyle=WL;c.lineWidth=4 + for(const[x,w]of[[96,22],[160,18],[128,34]]){const y=x==128?220:212;c.beginPath();c.moveTo(x-w,y);c.lineTo(x+w,y);c.stroke()} + c.restore() + c.strokeStyle='#111';c.lineWidth=7;c.beginPath();c.ellipse(128,198,104,38,0,0,T);c.stroke() + c.fillStyle=ST;for(const[x,y,rx,ry]of[[70,226,30,16],[188,228,32,16]])c.beginPath(),c.ellipse(x,y,rx,ry,0,0,T),c.fill(),c.stroke() + c.fillStyle=SD;for(const[x,y]of[[62,222],[196,224]])c.beginPath(),c.ellipse(x,y,8,4,0,0,T),c.fill() +} diff --git a/gen/color/cassette/roll.js b/gen/color/cassette/roll.js new file mode 100644 index 0000000..8e850ea --- /dev/null +++ b/gen/color/cassette/roll.js @@ -0,0 +1,40 @@ +// cassette tape whose spinning reels are its eyes, tape ticking along between them, notes floating up +const{sin,cos,PI}=Math,T=2*PI +export const frames=24,delay=70 +const BODY='#4dabf7',BD='#1c7ed6',LAB='#fff9db',BAND='#ff6b6b',STR='#ffd43b',WIN='#495057',TAPE='#a9744f',TL='#d9a47c',BL='#faa2c1',NOTE=['#b197fc','#69db7c'] +const note=(c,x,y,s,col)=>{ + c.save();c.translate(x,y);c.scale(s,s);c.fillStyle=col + for(const[w,st]of[[9,'#111'],[3,col]])c.lineWidth=w,c.strokeStyle=st,c.beginPath(),c.moveTo(5,4),c.lineTo(5,-24),c.lineTo(17,-18),c.stroke() + c.strokeStyle='#111';c.lineWidth=4;c.beginPath();c.ellipse(0,5,8,6,-.4,0,T);c.fill();c.stroke();c.restore() +} +export default(c,t)=>{ + const a=T*t*2/3 + for(let k=0;k<2;k++){const p=(t+k/2)%1,s=p<.75?1:(1-p)*4;note(c,56+k*140+6*sin(p*T),58-p*40,s,NOTE[k])} + c.lineWidth=7;c.fillStyle=BODY;c.beginPath();c.roundRect(22,56,212,146,18);c.fill();c.stroke() + c.fillStyle='#e7f5ff';c.lineWidth=4;for(const[x,y]of[[38,72],[218,72],[38,186],[218,186]])c.beginPath(),c.arc(x,y,5,0,T),c.fill(),c.stroke() + const lab=()=>{c.beginPath();c.roundRect(42,68,172,98,10)} + c.fillStyle=LAB;lab();c.fill() + c.save();lab();c.clip();c.fillStyle=BAND;c.fillRect(42,68,172,16);c.fillStyle=STR;c.fillRect(42,84,172,6);c.restore() + c.lineWidth=5;lab();c.stroke();c.beginPath();c.moveTo(42,84);c.lineTo(214,84);c.stroke() + const win=()=>{c.beginPath();c.roundRect(68,98,120,46,23)} + c.fillStyle=WIN;win();c.fill() + c.save();win();c.clip() + c.fillStyle=TAPE;for(const x of[98,158])c.beginPath(),c.arc(x,121,19,0,T),c.fill() + c.strokeStyle=TAPE;c.lineWidth=5;c.beginPath();c.moveTo(98,138);c.lineTo(158,138);c.stroke() + c.strokeStyle=TL;c.lineWidth=2;for(let k=0;k<5;k++){const x=98+((k*12+t*24)%60);c.beginPath();c.moveTo(x,136);c.lineTo(x+4,136);c.stroke()} + c.strokeStyle='#111' + for(const x of[98,158]){ + c.save();c.translate(x,121);c.rotate(a) + c.fillStyle='#fff';c.lineWidth=4;c.beginPath();c.arc(0,0,12,0,T);c.fill();c.stroke() + c.fillStyle='#111';for(let k=0;k<6;k++)c.save(),c.rotate(k*PI/3),c.fillRect(-1.5,-12,3,5),c.restore() + c.beginPath();c.arc(0,0,4.5,0,T);c.fill() + c.restore() + } + c.restore() + c.lineWidth=5;win();c.stroke() + c.lineWidth=4;c.beginPath();c.arc(128,150,7,.15*PI,.85*PI);c.stroke() + c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*58,154,9,5,0,0,T),c.fill() + c.lineWidth=7;c.fillStyle=BD;c.beginPath();c.moveTo(62,202);c.lineTo(78,176);c.lineTo(178,176);c.lineTo(194,202);c.closePath();c.fill();c.stroke() + c.fillStyle=WIN;for(const x of[100,156])c.beginPath(),c.arc(x,190,5,0,T),c.fill() + c.beginPath();c.roundRect(120,184,16,12,3);c.fill() +} diff --git a/gen/color/chameleon/tongue.js b/gen/color/chameleon/tongue.js new file mode 100644 index 0000000..b01dfdc --- /dev/null +++ b/gen/color/chameleon/tongue.js @@ -0,0 +1,46 @@ +// chameleon on a branch slowly shifting color, eyeing a buzzing fly, then snapping it up with its long tongue +const{sin,cos,PI,min,max,hypot,atan2}=Math,T=2*PI +export const frames=20,delay=80 +const RING=['#69db7c','#38d9a9','#4dabf7','#b197fc','#f783ac','#ffa94d','#ffd43b'],BR='#a9744f',LF='#51cf66',TG='#ff8fab' +const hex=h=>[1,3,5].map(k=>parseInt(h.slice(k,k+2),16)) +const mix=(a,b,u,l=0)=>'rgb('+hex(a).map((v,k)=>Math.round((v+(hex(b)[k]-v)*u)*(1-l)+255*l)).join(',')+')' +const ease=x=>x<0?0:x>1?1:x*x*(3-2*x) +export default(c,t,i)=>{ + const q=t*RING.length,k=Math.floor(q),u=ease(q-k),A=RING[k%7],B=RING[(k+1)%7],col=mix(A,B,u),lit=mix(A,B,u,.45) + const orb=s=>[222+12*cos(4*T*s),70+8*sin(8*T*s)] + const D=(wd,cl,f)=>{for(const[l,s]of[[wd,'#111'],[wd-10,cl]])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),f(),c.stroke();c.lineWidth=7;c.strokeStyle='#111'} + const M=[210,158],hit=orb(.52) + let fly=null,tip=null,fs=1 + if(t<.42)fly=orb(t) + else if(t<.52){const p=ease((t-.42)/.1);fly=orb(t);tip=[M[0]+(hit[0]-M[0])*p,M[1]+(hit[1]-M[1])*p]} + else if(t<.64){const p=1-ease((t-.52)/.12);tip=[M[0]+(hit[0]-M[0])*p,M[1]+(hit[1]-M[1])*p];fly=tip} + else if(t>=.8){const p=ease((t-.8)/.2),e=orb(1);fly=[236+(e[0]-236)*p,30+(e[1]-30)*p+4*sin(p*PI*2)];fs=.35+.65*p} + const chew=t>.64&&t<.8?Math.abs(sin((t-.64)/.16*PI*2)):0 + D(20,BR,()=>{c.moveTo(12,206);c.quadraticCurveTo(128,196,246,200)}) + c.fillStyle=LF;c.beginPath();c.ellipse(40,218,14,7,.5,0,T);c.fill();c.stroke() + D(20,col,()=>{c.moveTo(70,160);c.bezierCurveTo(40,170,30,200,52,206);c.bezierCurveTo(70,210,72,186,56,184)}) + for(const[x0,y0,x1]of[[92,170,84],[150,172,158]])D(16,col,()=>{c.moveTo(x0,y0);c.lineTo(x1,190);c.lineTo(x1+(x1>120?8:-8),202)}) + c.fillStyle=col + const sil=()=>{c.beginPath();c.ellipse(118,150,58,32,-.05,0,T);c.moveTo(150,128);c.quadraticCurveTo(170,100,190,122);c.lineTo(214,150);c.quadraticCurveTo(214,170,190,172);c.lineTo(150,174);c.closePath()} + sil();c.lineWidth=14;c.stroke();sil();c.fill();c.lineWidth=7 + c.save();sil();c.clip();c.fillStyle=lit + for(const[x,y]of[[84,150],[112,146],[140,150]])c.beginPath(),c.ellipse(x,y,7,22,.25,0,T),c.fill() + c.restore() + c.lineWidth=5;c.beginPath();for(let k=0;k<6;k++)c.moveTo(70+k*16,121-(k%2?0:2)),c.lineTo(78+k*16,114);c.stroke() + if(tip){D(11,TG,()=>{c.moveTo(M[0]-6,M[1]);c.lineTo(tip[0],tip[1])});c.fillStyle=TG;c.beginPath();c.arc(tip[0],tip[1],8,0,T);c.fill();c.lineWidth=5;c.stroke();c.lineWidth=7} + c.lineWidth=5;c.beginPath();c.moveTo(212,156);c.quadraticCurveTo(200,164,186,160);c.stroke() + if(chew){c.fillStyle=col;c.beginPath();c.ellipse(194,166,8+3*chew,5+2*chew,0,0,T);c.fill();c.stroke()} + c.lineWidth=7 + const e=fly||orb(.9),ex=180,ey=138,a=atan2(e[1]-ey,e[0]-ex) + c.fillStyle=col;c.beginPath();c.arc(ex,ey,17,0,T);c.fill();c.stroke() + c.fillStyle='#fff';c.beginPath();c.arc(ex+cos(a)*5,ey+sin(a)*5,9,0,T);c.fill();c.lineWidth=4;c.stroke() + c.fillStyle='#111';c.beginPath();c.arc(ex+cos(a)*8,ey+sin(a)*8,4.5,0,T);c.fill();c.lineWidth=7 + if(fly){ + const[fx,fy]=fly,fl=i%2 + c.save();c.translate(fx,fy);c.scale(fs,fs);c.translate(-fx,-fy) + c.fillStyle='#fff';c.lineWidth=4 + for(const d of[-1,1])c.beginPath(),c.ellipse(fx+d*8,fy-9-fl*3,9,5+fl*2,d*(.6+fl*.3),0,T),c.fill(),c.stroke() + c.lineWidth=10;c.strokeStyle='#fff';c.beginPath();c.ellipse(fx,fy,9,7,0,0,T);c.stroke();c.fillStyle='#111';c.fill() + c.restore();c.strokeStyle='#111';c.lineWidth=7 + } +} diff --git a/gen/color/clam/pearl.js b/gen/color/clam/pearl.js new file mode 100644 index 0000000..7419bb3 --- /dev/null +++ b/gen/color/clam/pearl.js @@ -0,0 +1,37 @@ +// smiling clam opens up to show off a shining pearl, then shuts again, bubbles rising behind it +const{sin,cos,PI,max,abs}=Math,T=2*PI +export const frames=24,delay=70 +const SH='#faa2c1',SHD='#f783ac',IN='#e64980',PL='#f3f0ff',BL='#ff8787',BU='#e7f5ff' +const sm=x=>x<0?0:x>1?1:x*x*(3-2*x) +const spk=(c,x,y,s)=>{c.lineWidth=3;c.fillStyle='#fff';c.beginPath();c.moveTo(x,y-s);for(const[px,py,qx,qy]of[[s,0,s*.2,-s*.2],[0,s,s*.2,s*.2],[-s,0,-s*.2,s*.2],[0,-s,-s*.2,-s*.2]])c.quadraticCurveTo(x+qx,y+qy,x+px,y+py);c.fill();c.stroke()} +export default(c,t)=>{ + const o=sm((t-.1)/.2)*(1-sm((t-.72)/.2)),up=48*o,sc=1-.3*o,rim=152 + for(let k=0;k<3;k++){const p=(t+k/3)%1,r=5+2*k,x=[54,204,186][k]+5*sin(p*T*2+k),y=150-p*140;if(p<.92){c.fillStyle=BU;c.lineWidth=4;c.beginPath();c.arc(x,y,r,0,T);c.fill();c.stroke();c.strokeStyle='#fff';c.lineWidth=2.5;c.beginPath();c.arc(x,y,r*.55,PI*1.1,PI*1.5);c.stroke();c.strokeStyle='#111'}} + c.lineWidth=7 + if(up>1){ + const mouth=()=>{c.beginPath();c.ellipse(128,rim-up*.5,84,up*.5+8,0,0,T)} + mouth();c.fillStyle=IN;c.fill();c.stroke() + c.save();mouth();c.clip() + c.fillStyle=PL;c.lineWidth=5;c.beginPath();c.arc(128,rim-10,22,0,T);c.fill();c.stroke() + c.fillStyle='#fff';c.beginPath();c.ellipse(120,rim-18,7,4.5,-.5,0,T);c.fill() + c.restore() + } + c.lineWidth=7;c.beginPath() + for(let k=0;k<=56;k++){const q=PI+PI*k/56,b=1+.045*abs(sin(7*(q-PI)));c.lineTo(128+88*b*cos(q),rim-up+76*b*sin(q)*sc)} + c.quadraticCurveTo(128,rim-up+10,40,rim-up);c.closePath();c.fillStyle=SH;c.fill();c.stroke() + c.strokeStyle=SHD;c.lineWidth=3.5 + for(const q of[1.2,1.35,1.5,1.65,1.8].map(v=>v*PI))c.beginPath(),c.moveTo(128+18*cos(q),rim-up-8+12*sin(q)*sc),c.lineTo(128+74*cos(q),rim-up+66*sin(q)*sc),c.stroke() + c.strokeStyle='#111';c.lineWidth=7 + c.beginPath();c.moveTo(40,rim);c.quadraticCurveTo(128,rim+18,216,rim) + for(let k=0;k<=56;k++){const q=PI*k/56,b=1+.045*abs(sin(7*q));c.lineTo(128+88*b*cos(q),rim+68*b*sin(q))} + c.closePath();c.fillStyle=SH;c.fill();c.stroke() + c.strokeStyle=SHD;c.lineWidth=3.5 + for(const q of[.25,.36,.64,.75].map(v=>v*PI))c.beginPath(),c.moveTo(128+30*cos(q),rim+30*sin(q)),c.lineTo(128+76*cos(q),rim+60*sin(q)),c.stroke() + c.strokeStyle='#111' + const hap=o>.6 + c.fillStyle='#111';c.lineWidth=4.5 + for(const d of[-1,1])if(hap)c.beginPath(),c.arc(128+d*22,184,6,PI*1.15,PI*1.85),c.stroke();else c.beginPath(),c.arc(128+d*22,182,5,0,T),c.fill() + c.lineWidth=4;c.beginPath();c.arc(128,190,8,.15*PI,.85*PI);c.stroke() + c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*40,192,7,4,0,0,T),c.fill() + if(o>.6)for(const[x,y,q]of[[100,rim-up-6,0],[160,rim-up+2,.5]])spk(c,x,y,(o-.6)/.4*(4+4*max(0,sin((t+q)*4*T)))) +} diff --git a/gen/color/clover/spin.js b/gen/color/clover/spin.js new file mode 100644 index 0000000..6329d9c --- /dev/null +++ b/gen/color/clover/spin.js @@ -0,0 +1,27 @@ +// four-leaf clover spinning like a pinwheel on its stem with a smiling center, lucky sparkles twinkling around +const{sin,cos,PI,max}=Math,T=2*PI +export const frames=16,delay=70 +const L=['#69db7c','#51cf66'],V='#2f9e44',ST='#40c057',BL='#ffa8a8',SP='#ffd43b' +const CX=128,CY=106 +const leaf=c=>{c.beginPath();c.moveTo(0,-4);c.bezierCurveTo(-30,-20,-46,-46,-30,-60);c.bezierCurveTo(-18,-70,-4,-64,0,-52);c.bezierCurveTo(4,-64,18,-70,30,-60);c.bezierCurveTo(46,-46,30,-20,0,-4);c.closePath()} +const spark=(c,x,y,s)=>{c.beginPath();c.moveTo(x,y-s);for(const[px,py,qx,qy]of[[s,0,s*.18,-s*.18],[0,s,s*.18,s*.18],[-s,0,-s*.18,s*.18],[0,-s,-s*.18,-s*.18]])c.quadraticCurveTo(x+qx,y+qy,x+px,y+py);c.fill();c.stroke()} +export default(c,t)=>{ + // leaves alternate two greens, so half a turn per loop lands every leaf on one of the same color + for(const[w,s]of[[17,'#111'],[8,ST]])c.lineWidth=w,c.strokeStyle=s,c.beginPath(),c.moveTo(CX,CY),c.quadraticCurveTo(CX+6,190,CX+30,236),c.stroke() + c.strokeStyle='#111' + c.fillStyle=SP;c.lineWidth=4 + for(const[x,y,o]of[[38,52,0],[220,62,.25],[218,178,.5],[40,172,.75]]){const s=11*max(0,sin(PI*((t*2+o)%1)));if(s>1.5)spark(c,x,y,s)} + c.save();c.translate(CX,CY);c.rotate(t*PI) + for(let k=0;k<4;k++){ + c.save();c.rotate(k*PI/2);leaf(c);c.fillStyle=L[k%2];c.lineWidth=7;c.fill();c.stroke() + c.strokeStyle=V;c.lineWidth=4;c.beginPath();c.moveTo(0,-10);c.lineTo(0,-46);c.moveTo(0,-30);c.lineTo(-12,-42);c.moveTo(0,-30);c.lineTo(12,-42);c.stroke();c.strokeStyle='#111' + c.strokeStyle='#fff';c.lineWidth=4;c.beginPath();c.arc(-22,-50,8,PI*1.1,PI*1.6);c.stroke();c.strokeStyle='#111' + c.restore() + } + c.restore() + for(const[w,s]of[[10,'#111'],[3,'#fff']])c.lineWidth=w,c.strokeStyle=s,c.beginPath(),c.arc(CX,CY,92,-PI*.15+t*PI,PI*.15+t*PI),c.stroke(),c.beginPath(),c.arc(CX,CY,92,PI*.85+t*PI,PI*1.15+t*PI),c.stroke() + c.strokeStyle='#111';c.lineWidth=6;c.fillStyle=L[0];c.beginPath();c.arc(CX,CY,18,0,T);c.fill();c.stroke() + c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.ellipse(CX+d*6,CY-3,2.5,3.5,0,0,T),c.fill() + c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(CX+d*11,CY+4,4,2.5,0,0,T),c.fill() + c.lineWidth=3;c.beginPath();c.arc(CX,CY+2,4,.2*PI,.8*PI);c.stroke() +} diff --git a/gen/color/comet/streak.js b/gen/color/comet/streak.js new file mode 100644 index 0000000..c5453d1 --- /dev/null +++ b/gen/color/comet/streak.js @@ -0,0 +1,22 @@ +// comet with a glowing smiley head bobbing in a small loop, its long striped tail swishing, sparks peeling off +const{sin,cos,PI,max,hypot}=Math,T=2*PI +export const frames=16,delay=80 +const L=[['#9775fa',1],['#4dabf7',.74],['#d0ebff',.46]],SC=['#ffd43b','#fff','#faa2c1'],HD='#ffd43b',HALO='#fff3bf',BL='#ff8787' +const spk=(c,x,y,s,f)=>{c.lineWidth=3;c.fillStyle=f;c.beginPath();c.moveTo(x,y-s);for(const[px,py,qx,qy]of[[s,0,s*.2,-s*.2],[0,s,s*.2,s*.2],[-s,0,-s*.2,s*.2],[0,-s,-s*.2,-s*.2]])c.quadraticCurveTo(x+qx,y+qy,x+px,y+py);c.fill();c.stroke()} +export default(c,t)=>{ + const a=t*T,hx=84+8*cos(a),hy=170+6*sin(a),dx=236-hx,dy=24-hy,D=hypot(dx,dy),ux=dx/D,uy=dy/D,nx=-uy,ny=ux,sw=20*sin(a+.6) + const cen=v=>{const w=1-v,mx=hx+ux*D*.5+nx*sw,my=hy+uy*D*.5+ny*sw;return[w*w*hx+2*v*w*mx+v*v*(hx+ux*D),w*w*hy+2*v*w*my+v*v*(hy+uy*D)]} + for(const[col,f]of L){ + const A=[],B=[],n=24 + for(let j=0;j<=n;j++){const v=j/n*(.45+.55*f),[x,y]=cen(v),[x1,y1]=cen(v+.01),l=hypot(x1-x,y1-y),w=50*f*(1-v/(.45+.55*f))**.9;A.push([x-(y1-y)/l*w,y+(x1-x)/l*w]);B.unshift([x+(y1-y)/l*w,y-(x1-x)/l*w])} + c.beginPath();for(const[x,y]of[...A,...B])c.lineTo(x,y);c.closePath();c.fillStyle=col;c.fill();c.lineWidth=f==1?7:3;c.stroke() + } + for(let k=0;k<5;k++){const p=(t+k/5)%1,v=.3+.12*k+p*.06,sd=k%2?1:-1,[x,y]=cen(v),o=sd*(40*(1-v)+10+p*26),s=10*sin(PI*p);if(s>1.5)spk(c,x+nx*o,y+ny*o,s,SC[k%3])} + c.lineWidth=5;c.fillStyle=HALO;c.beginPath();c.arc(hx,hy,42,0,T);c.fill();c.stroke() + c.lineWidth=7;c.fillStyle=HD;c.beginPath();c.arc(hx,hy,30,0,T);c.fill();c.stroke() + c.fillStyle='#fff';c.beginPath();c.ellipse(hx+12,hy-14,7,4,-.6,0,T);c.fill() + c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.arc(hx+d*10-2,hy+1,4.5,0,T),c.fill() + c.fillStyle='#fff';for(const d of[-1,1])c.beginPath(),c.arc(hx+d*10-3,hy-1,1.6,0,T),c.fill() + c.lineWidth=4;c.beginPath();c.arc(hx-2,hy+8,6,.15*PI,.85*PI);c.stroke() + c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(hx+d*19-2,hy+9,5,3,0,0,T),c.fill() +} diff --git a/gen/color/corgi/wiggle.js b/gen/color/corgi/wiggle.js new file mode 100644 index 0000000..233c7a0 --- /dev/null +++ b/gen/color/corgi/wiggle.js @@ -0,0 +1,32 @@ +// corgi seen from behind, fluffy heart-shaped butt wiggling side to side on stubby trotting legs, smiling back over its shoulder +const{sin,cos,PI,max}=Math,T=2*PI +export const frames=16,delay=70 +const OR='#ffa94d',OD='#f08c00',PK='#faa2c1',TG='#ff8fab',BL='#ffa8a8' +export default(c,t)=>{ + const w=.1*sin(2*T*t),l1=max(0,sin(2*T*t)),l2=max(0,-sin(2*T*t)) + for(const[x,l]of[[112,l2],[164,l1]]){c.fillStyle=OR;c.beginPath();c.roundRect(x-13,196-l*8,26,34,10);c.fill();c.stroke();c.fillStyle='#fff';c.beginPath();c.ellipse(x,228-l*8,15,9,0,0,T);c.fill();c.stroke()} + c.save();c.translate(138,214);c.rotate(w);c.translate(-138,-214) + for(const[x,l]of[[92,l1],[184,l2]]){c.fillStyle=OR;c.beginPath();c.roundRect(x-15,186-l*10,30,40,12);c.fill();c.stroke();c.fillStyle='#fff';c.beginPath();c.ellipse(x,230-l*10,18,10,0,0,T);c.fill();c.stroke()} + c.fillStyle=OR;c.beginPath();c.ellipse(138,140,76,66,0,0,T);c.fill();c.stroke() + // the butt: two fluffy scalloped cheeks meeting in a heart + const fluff=(x,y,r,n=10)=>{c.beginPath();c.moveTo(x+r,y);for(let k=0;k{c.beginPath();for(let k=0;k<8;k++){const r=k%2?s*.38:s,a=k*PI/4;c.lineTo(x+cos(a)*r,y+sin(a)*r)}c.closePath();c.fill();c.stroke()} +export default(c,t)=>{ + c.fillStyle=CN;c.beginPath();c.moveTo(106,150);c.lineTo(150,150);c.lineTo(132,236);c.lineTo(124,236);c.closePath();c.fill() + c.save();c.clip();c.strokeStyle=ST;c.lineWidth=7;for(let k=-3;k<5;k++)c.beginPath(),c.moveTo(100,160+k*22),c.lineTo(156,140+k*22),c.stroke();c.restore() + c.beginPath();c.moveTo(106,150);c.lineTo(150,150);c.lineTo(132,236);c.lineTo(124,236);c.closePath();c.stroke() + const tf=[...Array(11)].map((_,k)=>{const a=k*T/11+T*t;return[128+cos(a)*68,96+sin(a)*50,26+4*sin(2*T*t+k*1.9),k%3?PK:BU]}) + const blob=()=>{c.beginPath();c.ellipse(128,96,62,48,0,0,T);for(const[x,y,r]of tf)c.moveTo(x+r,y),c.arc(x,y,r,0,T)} + c.lineWidth=16;blob();c.stroke();c.lineWidth=7 + for(const[x,y,r,col]of tf)c.fillStyle=col,c.beginPath(),c.arc(x,y,r,0,T),c.fill() + c.fillStyle=PK;c.beginPath();c.ellipse(128,96,62,48,0,0,T);c.fill() + c.strokeStyle=PD;c.lineWidth=4.5;for(const[r,o]of[[50,0],[50,PI],[34,1.6]])c.beginPath(),c.ellipse(128,96,r*1.2,r*.85,0,T*t*-1+o,T*t*-1+o+1.1),c.stroke() + c.strokeStyle='#111';c.lineWidth=7 + c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.arc(128+d*22,94,6,0,T),c.fill() + c.fillStyle='#fff';for(const d of[-1,1])c.beginPath(),c.arc(128+d*22-2,92,2.2,0,T),c.fill() + c.fillStyle='#111';c.beginPath();c.arc(128,104,9,0,PI);c.closePath();c.fill() + c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*40,110,9,5,0,0,T),c.fill() + c.fillStyle=SPK;c.lineWidth=4 + ;[[34,36],[222,42],[28,168],[226,160]].forEach(([x,y],k)=>{const s=14*max(0,sin(T*t*2+k*1.6));if(s>3)star(c,x,y,s)}) +} diff --git a/gen/color/crayon/scribble.js b/gen/color/crayon/scribble.js new file mode 100644 index 0000000..c7e3b9f --- /dev/null +++ b/gen/color/crayon/scribble.js @@ -0,0 +1,32 @@ +// purple crayon with a face scribbling a rainbow loop-de-loop, lifting off, then the line wipes away as it floats back +const{sin,cos,PI,min,max,floor}=Math,T=2*PI +export const frames=24,delay=80 +const RB=['#ff6b6b','#ffa94d','#ffd43b','#69db7c','#4dabf7','#b197fc'],CR='#9775fa',CL='#d0bfff',CD='#7048e8',BL='#faa2c1' +const X0=36,X1=158,Y0=186,B=17,W=4.25,NS=90 +const P=s=>[X0+(X1-X0)*s-B*sin(s*T*W),Y0-B*cos(s*T*W)+B*.3] +const sm=x=>x<0?0:x>1?1:x*x*(3-2*x) +const sparkle=(c,x,y,s)=>{c.fillStyle='#fff';c.lineWidth=3.5;c.beginPath();c.moveTo(x,y-s);for(const[px,py,qx,qy]of[[s,0,s*.18,-s*.18],[0,s,s*.18,s*.18],[-s,0,-s*.18,s*.18],[0,-s,-s*.18,-s*.18]])c.quadraticCurveTo(x+qx,y+qy,x+px,y+py);c.fill();c.stroke()} +export default(c,t)=>{ + let from=0,to=1,tip,lift=0 + if(t<.68)to=t/.68,tip=P(to) + else if(t<.78){const u=(t-.68)/.1;tip=P(1);lift=34*sm(u)} + else{const u=sm((t-.78)/.18);from=min(1,(t-.78)/.16);const a=P(1),b=P(0);tip=[a[0]+(b[0]-a[0])*u,a[1]+(b[1]-a[1])*u];lift=34*(1-sm((t-.9)/.1))} + if(to>from){ + const pts=[];for(let k=floor(from*NS);k<=to*NS;k++)pts.push([k/NS,...P(k/NS)]);pts.push([to,...P(to)]) + c.lineWidth=14;c.beginPath();for(const[,x,y]of pts)c.lineTo(x,y);c.stroke() + c.lineWidth=7;for(let r=0;r<6;r++){const seg=pts.filter(([s])=>s>=r/6-.012&&s<=(r+1)/6+.012);if(seg.length<2)continue;c.strokeStyle=RB[r];c.beginPath();for(const[,x,y]of seg)c.lineTo(x,y);c.stroke()} + c.strokeStyle='#111' + } + if(t>.68&&t<.84){const s=12*sin(PI*(t-.68)/.16),[x,y]=P(1);sparkle(c,x+20,y-24,s);sparkle(c,x-6,y-40,s*.6)} + const wob=t<.68?.05*sin(8*T*t):0 + c.save();c.translate(tip[0],tip[1]-lift);c.rotate(.55+wob);c.lineWidth=6 + c.fillStyle=CR;c.beginPath();c.moveTo(-3,-2);c.quadraticCurveTo(0,1,3,-2);c.lineTo(12,-28);c.lineTo(-12,-28);c.closePath();c.fill();c.stroke() + c.beginPath();c.roundRect(-15,-112,30,86,[4,4,3,3]);c.fill();c.stroke() + c.fillStyle=CL;c.beginPath();c.rect(-15,-96,30,58);c.fill();c.stroke() + c.lineWidth=4;for(const y of[-88,-46])c.beginPath(),c.moveTo(-15,y),c.lineTo(15,y),c.stroke() + c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.arc(d*6,-72,3.2,0,T),c.fill() + c.lineWidth=3;c.beginPath();c.arc(0,-66,4.5,.15*PI,.85*PI);c.stroke() + c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(d*10,-63,3.5,2,0,0,T),c.fill() + c.fillStyle=CD;c.lineWidth=5;c.beginPath();c.roundRect(-15,-118,30,10,4);c.fill();c.stroke() + c.restore() +} diff --git a/gen/color/crown/sparkle.js b/gen/color/crown/sparkle.js new file mode 100644 index 0000000..db9558f --- /dev/null +++ b/gen/color/crown/sparkle.js @@ -0,0 +1,25 @@ +// golden crown bobbing on a velvet cap, its jewels glinting one after another with little sparkles popping off +const{sin,cos,PI,max,round}=Math,T=2*PI +export const frames=20,delay=80 +const G='#ffd43b',GD='#fab005',VEL='#9775fa',PE='#fff' +const J=[[86,92,7,'#f783ac'],[76,172,10,'#4dabf7'],[128,172,14,'#ff6b6b'],[180,172,10,'#69db7c'],[170,92,7,'#4dabf7']] +const crown=c=>{c.beginPath();for(const[x,y]of[[52,156],[42,78],[66,118],[86,60],[107,110],[128,40],[149,110],[170,60],[190,118],[214,78],[204,156]])c.lineTo(x,y);c.closePath()} +const spark=(c,x,y,s)=>{c.beginPath();c.moveTo(x,y-s);for(const[px,py,qx,qy]of[[s,0,s*.18,-s*.18],[0,s,s*.18,s*.18],[-s,0,-s*.18,s*.18],[0,-s,-s*.18,-s*.18]])c.quadraticCurveTo(x+qx,y+qy,x+px,y+py);c.fill();c.stroke()} +export default(c,t)=>{ + const b=round(4*sin(t*T)),g=t*5|0,gp=t*5%1 + c.translate(0,b) + c.fillStyle=VEL;c.beginPath();c.ellipse(128,124,74,58,0,PI,0);c.lineTo(202,150);c.lineTo(54,150);c.closePath();c.fill();c.stroke() + crown(c);c.fillStyle=G;c.fill() + c.save();crown(c);c.clip();c.fillStyle=GD;for(const[x,y]of[[42,78],[128,40],[214,78]])c.beginPath(),c.moveTo(x,y),c.lineTo(x+22,y+80),c.lineTo(x+6,y+90),c.closePath(),c.fill();c.restore() + crown(c);c.stroke() + c.lineWidth=5;c.fillStyle=PE;for(const[x,y]of[[42,78],[86,60],[128,40],[170,60],[214,78]])c.beginPath(),c.arc(x,y-4,9,0,T),c.fill(),c.stroke() + c.lineWidth=7;c.fillStyle=GD;c.beginPath();c.roundRect(38,146,180,48,12);c.fill();c.stroke() + c.fillStyle=G;c.beginPath();c.roundRect(44,152,168,10,5);c.fill() + for(const[k,[x,y,s,col]]of J.entries()){ + c.lineWidth=5;c.fillStyle=col;c.beginPath() + if(k==2)c.ellipse(x,y,s,s*.8,0,0,T);else c.moveTo(x,y-s),c.lineTo(x+s*.8,y),c.lineTo(x,y+s),c.lineTo(x-s*.8,y),c.closePath() + c.fill();c.stroke() + c.fillStyle='#fff';c.beginPath();c.ellipse(x-s*.3,y-s*.3,s*.22,s*.3,-.5,0,T);c.fill() + if(k==g){const s2=4+12*sin(PI*gp);c.fillStyle='#fff';c.lineWidth=4;spark(c,x+s*.6,y-s*.7,s2)} + } +} diff --git a/gen/color/cupcake/sprinkle.js b/gen/color/cupcake/sprinkle.js new file mode 100644 index 0000000..6c04a73 --- /dev/null +++ b/gen/color/cupcake/sprinkle.js @@ -0,0 +1,26 @@ +// cupcake with a swirl of pink frosting, sprinkles raining down onto it and the cherry on top wobbling +const{sin,cos,PI}=Math,T=2*PI +export const frames=20,delay=70 +const WR='#38d9a9',WD='#0ca678',FR='#faa2c1',CH='#ff6b6b',STEM='#a9744f',BL='#ffa8a8',SP=['#ffd43b','#4dabf7','#fff','#69db7c','#b197fc','#ff922b'] +const rod=(c,x,y,a,col)=>{for(const[w,s]of[[9,'#111'],[4,col]])c.lineWidth=w,c.strokeStyle=s,c.beginPath(),c.moveTo(x-cos(a)*6,y-sin(a)*6),c.lineTo(x+cos(a)*6,y+sin(a)*6),c.stroke();c.lineWidth=7;c.strokeStyle='#111'} +export default(c,t)=>{ + c.fillStyle=WR;c.beginPath();c.moveTo(58,150);c.lineTo(198,150);c.lineTo(178,234);c.lineTo(78,234);c.closePath();c.fill();c.stroke() + c.strokeStyle=WD;c.lineWidth=5;for(let k=1;k<6;k++)c.beginPath(),c.moveTo(58+k*140/6,158),c.lineTo(78+k*100/6,228),c.stroke() + c.strokeStyle='#111';c.lineWidth=7 + c.fillStyle='#fff';for(const d of[-1,1])c.beginPath(),c.arc(128+d*24,190,10,0,T),c.fill() + c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.arc(128+d*24,190,6,0,T),c.fill() + c.fillStyle='#fff';for(const d of[-1,1])c.beginPath(),c.arc(128+d*24-2,188,2.2,0,T),c.fill() + c.fillStyle='#fff';c.beginPath();c.arc(128,200,9,.15*PI,.85*PI);c.closePath();c.fill();c.lineWidth=5;c.stroke();c.lineWidth=7 + c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*44,204,9,5,0,0,T),c.fill() + c.fillStyle=FR;for(const[y,rx,ry]of[[144,84,24],[116,66,22],[90,46,20]])c.beginPath(),c.ellipse(128,y,rx,ry,0,0,T),c.fill(),c.stroke() + c.beginPath();c.moveTo(108,84);c.quadraticCurveTo(116,58,132,60);c.quadraticCurveTo(150,66,148,86);c.closePath();c.fill();c.stroke() + c.strokeStyle='#fff';c.lineWidth=5;for(const[y,rx,ry]of[[144,72,14],[116,54,13],[90,34,11]])c.beginPath(),c.ellipse(128,y-2,rx,ry,0,PI*1.12,PI*1.32),c.stroke() + c.strokeStyle='#111';c.lineWidth=7 + ;[[90,154,.5],[118,162,-.6],[152,158,.9],[176,146,-.3],[74,140,1.2],[104,128,-1],[142,130,.4],[166,118,-.8],[112,104,.7],[140,100,-.2],[92,112,.2]].forEach(([x,y,a],k)=>rod(c,x,y,a,SP[k%6])) + c.save();c.translate(128,66);c.rotate(.3*sin(2*T*t)) + for(const[w,s]of[[9,'#111'],[4,STEM]])c.lineWidth=w,c.strokeStyle=s,c.beginPath(),c.moveTo(0,-24),c.quadraticCurveTo(2,-42,12,-50),c.stroke() + c.lineWidth=7;c.strokeStyle='#111';c.fillStyle=CH;c.beginPath();c.arc(0,-12,15,0,T);c.fill();c.stroke() + c.strokeStyle='#fff';c.lineWidth=4;c.beginPath();c.arc(0,-12,8,PI*1.1,PI*1.45);c.stroke() + c.restore();c.strokeStyle='#111';c.lineWidth=7 + ;[[84,128],[104,104],[152,102],[176,126],[120,86],[164,112]].forEach(([x,land],k)=>{const p=(t+k/6)%1;rod(c,x+4*sin(p*T+k),12+p*(land-12),k+p*9,SP[(k+2)%6])}) +} diff --git a/gen/color/disco/spin.js b/gen/color/disco/spin.js new file mode 100644 index 0000000..34ff1b5 --- /dev/null +++ b/gen/color/disco/spin.js @@ -0,0 +1,35 @@ +// mirror disco ball spinning on its chain, tiles catching the light, colored light spots blinking around it +// the tile colors repeat every 4 columns and the ball turns 4 columns per loop, so the loop is seamless +const{sin,cos,PI,max,min,floor}=Math,T=2*PI +export const frames=16,delay=90 +const TL=['#e7f5ff','#d0bfff','#c5f6fa','#ffdeeb'],TD=['#a5d8ff','#b197fc','#66d9e8','#faa2c1'],SP=['#ff6b6b','#ffd43b','#69db7c','#4dabf7','#b197fc','#f783ac'] +const star=(c,x,y,s,col)=>{c.fillStyle=col;c.beginPath();c.moveTo(x,y-s);for(const[px,py,qx,qy]of[[s,0,s*.18,-s*.18],[0,s,s*.18,s*.18],[-s,0,-s*.18,s*.18],[0,-s,-s*.18,-s*.18]])c.quadraticCurveTo(x+qx,y+qy,x+px,y+py);c.fill();c.stroke()} +export default(c,t,i)=>{ + const cx=128,cy=140,R=76,M=16,N=8,cw=T/M,rot=T*t/4 + for(let k=0;k<8;k++){ + const a=k*T/8+.39,r=104,on=(floor(t*8)+k*3)%4!=0 + if(on){c.lineWidth=4;star(c,cx+cos(a)*r,cy+sin(a)*r*.92,k%2?9:12,SP[k%6])} + } + for(const[w,st]of[[11,'#111'],[4,'#ced4da']])c.lineWidth=w,c.strokeStyle=st,c.beginPath(),c.moveTo(cx,0),c.lineTo(cx,58),c.stroke() + c.strokeStyle='#111';c.lineWidth=5;c.fillStyle='#ced4da';c.beginPath();c.roundRect(cx-15,52,30,14,4);c.fill();c.stroke() + c.fillStyle=TD[0];c.lineWidth=7;c.beginPath();c.arc(cx,cy,R,0,T);c.fill() + c.save();c.beginPath();c.arc(cx,cy,R,0,T);c.clip() + const P=(lo,la)=>[cx+R*cos(la)*sin(lo),cy+R*sin(la)] + c.lineWidth=1.5;c.strokeStyle='#495057' + for(let l=0;lPI/2)continue + const a0=max(lo0,-PI/2),a1=min(lo1,PI/2),lm2=(a0+a1)/2 + const lit=cos(lm2)*cos(lm)*.7-sin(lm2)*cos(lm)*.35-sin(lm)*.45,ci=(j+l*2)%4 + c.fillStyle=lit>.62?'#fff':lit>.25?TL[ci]:TD[ci] + c.beginPath();for(const[lo,la]of[[a0,la0],[a1,la0],[a1,la1],[a0,la1]])c.lineTo(...P(lo,la));c.closePath();c.fill();c.stroke() + } + } + c.restore();c.strokeStyle='#111' + c.lineWidth=7;c.beginPath();c.arc(cx,cy,R,0,T);c.stroke() + c.lineWidth=3 + for(const[x,y,p]of[[100,112,0],[156,154,.33],[116,178,.66]]){const s=13*max(0,sin(T*(2*t+p)));if(s>2)star(c,x,y,s,'#fff')} +} diff --git a/gen/color/ferris/turn.js b/gen/color/ferris/turn.js new file mode 100644 index 0000000..23e5173 --- /dev/null +++ b/gen/color/ferris/turn.js @@ -0,0 +1,27 @@ +// ferris wheel slowly turning, its candy-colored gondolas hanging upright, a smiling hub +const{sin,cos,PI}=Math,T=2*PI +export const frames=16,delay=100 +const G=['#ff6b6b','#ffd43b','#4dabf7','#69db7c'],RIM='#b197fc',LEG='#f783ac',BASE='#a9744f',HUB='#ffd43b',BL='#ffa8a8' +const CX=128,CY=104,R=78 +export default(c,t)=>{ + // turning by half a revolution per loop lines gondola k up with gondola k+4, which has the same color + const a0=t*PI + for(const[w,s]of[[16,'#111'],[8,LEG]])c.lineWidth=w,c.strokeStyle=s,c.beginPath(),c.moveTo(72,232),c.lineTo(CX,CY),c.lineTo(184,232),c.stroke() + c.strokeStyle='#111';c.lineWidth=7 + c.fillStyle=BASE;c.beginPath();c.roundRect(44,226,168,16,8);c.fill();c.stroke() + for(let k=0;k<8;k++){const a=a0+k*PI/4;for(const[w,s]of[[9,'#111'],[3,'#fff']])c.lineWidth=w,c.strokeStyle=s,c.beginPath(),c.moveTo(CX,CY),c.lineTo(CX+R*cos(a),CY+R*sin(a)),c.stroke()} + for(const[w,s]of[[16,'#111'],[8,RIM]])c.lineWidth=w,c.strokeStyle=s,c.beginPath(),c.arc(CX,CY,R,0,T),c.stroke() + c.strokeStyle='#111' + for(let k=0;k<8;k++){ + const a=a0+k*PI/4,x=CX+R*cos(a),y=CY+R*sin(a) + c.save();c.translate(x,y) + c.lineWidth=4;c.beginPath();c.moveTo(0,0);c.lineTo(0,9);c.stroke() + c.lineWidth=5;c.fillStyle=G[k%4];c.beginPath();c.roundRect(-14,8,28,22,7);c.fill();c.stroke() + c.fillStyle='#fff';c.beginPath();c.roundRect(-9,13,18,8,3);c.fill();c.lineWidth=3;c.stroke() + c.restore() + } + c.lineWidth=6;c.fillStyle=HUB;c.beginPath();c.arc(CX,CY,17,0,T);c.fill();c.stroke() + c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.arc(CX+d*6,CY-3,2.5,0,T),c.fill() + c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(CX+d*10,CY+4,3.5,2.5,0,0,T),c.fill() + c.lineWidth=3;c.beginPath();c.arc(CX,CY+2,4,.2*PI,.8*PI);c.stroke() +} diff --git a/gen/color/firetruck/siren.js b/gen/color/firetruck/siren.js new file mode 100644 index 0000000..1e7a7a6 --- /dev/null +++ b/gen/color/firetruck/siren.js @@ -0,0 +1,39 @@ +// fire truck rumbling along with its siren flashing red and blue, ladder bobbing, wheels turning, road dashes rolling past +const{sin,cos,PI,abs,round}=Math,T=2*PI +export const frames=16,delay=70 +const RD='#ff6b6b',RDD='#e03131',WIN='#a5d8ff',TIRE='#343a40',HUB='#ced4da',LAD='#dee2e6',SB='#4dabf7',SR='#fa5252',G='#ffd43b',BL='#ffa8a8' +export default(c,t,i)=>{ + const b=-round(3*abs(sin(2*T*t))),rot=t*T,red=(i>>1)%2==0,la=-.05-.04*sin(2*T*t+1) + c.fillStyle='#868e96';c.beginPath();c.roundRect(4,214,248,22,11);c.fill();c.stroke() + c.save();c.beginPath();c.rect(10,214,236,22);c.clip() + for(let k=-1;k<5;k++){const x=k*64-((t*128)%64)+20;c.fillStyle='#fff';c.beginPath();c.roundRect(x,222,30,6,3);c.fill()} + c.restore() + c.translate(0,b) + c.save();c.translate(34,104);c.rotate(la) + for(const[w,s]of[[11,'#111'],[4,LAD]])c.lineWidth=w,c.strokeStyle=s,c.beginPath(),c.moveTo(0,0),c.lineTo(136,0),c.moveTo(0,-14),c.lineTo(136,-14),c.stroke() + c.lineWidth=4;c.strokeStyle='#111';c.beginPath();for(let x=12;x<136;x+=16)c.moveTo(x,0),c.lineTo(x,-14);c.stroke() + c.restore();c.lineWidth=7 + c.fillStyle=RD;c.beginPath();c.roundRect(24,108,144,76,8);c.fill();c.stroke() + c.beginPath();c.moveTo(160,184);c.lineTo(160,96);c.quadraticCurveTo(160,86,170,86);c.lineTo(204,86);c.quadraticCurveTo(214,86,220,96);c.lineTo(232,128);c.lineTo(232,184);c.closePath();c.fill();c.stroke() + c.fillStyle=WIN;c.lineWidth=5;c.beginPath();c.moveTo(174,98);c.lineTo(206,98);c.lineTo(218,126);c.lineTo(174,126);c.closePath();c.fill();c.stroke() + c.fillStyle='#fff';c.beginPath();c.ellipse(184,108,3,7,.4,0,T);c.fill() + c.fillStyle='#fff';c.beginPath();c.rect(24,150,208,10);c.fill();c.beginPath();c.moveTo(24,150);c.lineTo(232,150);c.moveTo(24,160);c.lineTo(232,160);c.stroke() + c.lineWidth=5;c.beginPath();for(const x of[72,120])c.moveTo(x,116),c.lineTo(x,146);c.moveTo(166,134);c.lineTo(166,180);c.stroke() + c.fillStyle=G;c.beginPath();c.arc(48,130,13,0,T);c.fill();c.stroke();c.beginPath();c.arc(48,130,5,0,T);c.stroke() + c.fillStyle=G;c.lineWidth=5;c.beginPath();c.roundRect(222,132,14,12,4);c.fill();c.stroke() + c.fillStyle='#111';c.beginPath();c.arc(206,140,4,0,T);c.fill() + c.fillStyle=BL;c.beginPath();c.ellipse(198,170,7,4,0,0,T);c.fill() + c.lineWidth=4;c.beginPath();c.arc(214,166,6,.2*PI,.8*PI);c.stroke() + c.lineWidth=5;c.fillStyle='#ced4da';c.beginPath();c.roundRect(176,76,32,10,4);c.fill();c.stroke() + for(const[d,on,col,dim]of[[-1,red,SR,'#c92a2a'],[1,!red,SB,'#1c7ed6']]){ + c.fillStyle=on?col:dim;c.beginPath();c.moveTo(192,76);c.arc(192,76,14,d<0?PI:PI*1.5,d<0?PI*1.5:0);c.closePath();c.fill();c.stroke() + if(on)for(const a of[-.5,-.15,.2].map(v=>d<0?PI+v+.35:-v-.35))for(const[w,s]of[[9,'#111'],[4,col]])c.lineWidth=w,c.strokeStyle=s,c.beginPath(),c.moveTo(192+cos(a)*22,76+sin(a)*22),c.lineTo(192+cos(a)*34,76+sin(a)*34),c.stroke() + c.strokeStyle='#111';c.lineWidth=5 + } + c.fillStyle='#fff';c.beginPath();c.ellipse(187,69,2.5,4,-.5,0,T);c.fill() + for(const x of[64,112,196]){ + c.lineWidth=7;c.fillStyle=TIRE;c.beginPath();c.arc(x,190,22,0,T);c.fill();c.stroke() + c.fillStyle=HUB;c.beginPath();c.arc(x,190,10,0,T);c.fill();c.lineWidth=4;c.stroke() + for(let k=0;k<4;k++){const a=rot+k*PI/2;c.beginPath();c.moveTo(x+cos(a)*4,190+sin(a)*4);c.lineTo(x+cos(a)*9,190+sin(a)*9);c.stroke()} + } +} diff --git a/gen/color/flamingo/balance.js b/gen/color/flamingo/balance.js new file mode 100644 index 0000000..99f7af9 --- /dev/null +++ b/gen/color/flamingo/balance.js @@ -0,0 +1,29 @@ +// flamingo standing on one leg, wobbling to keep its balance while its neck sways the other way +const{sin,cos,PI}=Math,T=2*PI +export const frames=20,delay=80 +const PK='#f783ac',PL='#faa2c1',PD='#e64980',LG='#ff8fab',WA='#74c0fc',WL='#d0ebff',BL='#ff6b6b' +export default(c,t)=>{ + const a=.07*sin(T*t),n=-.12*sin(T*t-.5) + const D=(wd,col,f)=>{for(const[l,s]of[[wd,'#111'],[wd-10,col]])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),f(),c.stroke();c.lineWidth=7;c.strokeStyle='#111'} + c.fillStyle=WA;c.beginPath();c.ellipse(128,234,70,14,0,0,T);c.fill();c.stroke() + c.strokeStyle=WL;c.lineWidth=4;c.beginPath();c.ellipse(128,234,30+6*sin(2*T*t),6,0,0,T);c.stroke();c.strokeStyle='#111';c.lineWidth=7 + c.save();c.translate(128,232);c.rotate(a);c.translate(-128,-232) + D(17,LG,()=>{c.moveTo(128,232);c.lineTo(126,186);c.lineTo(122,150)}) + D(17,LG,()=>{c.moveTo(134,150);c.lineTo(156,180);c.lineTo(126,186)}) + c.fillStyle=LG;c.beginPath();c.ellipse(132,234,18,6,0,0,T);c.fill();c.lineWidth=5;c.stroke();c.lineWidth=7 + c.fillStyle=PK;c.beginPath();c.moveTo(64,118);c.lineTo(38,108);c.lineTo(50,128);c.lineTo(36,138);c.lineTo(66,136);c.closePath();c.fill();c.stroke() + c.beginPath();c.ellipse(112,128,56,34,-.12,0,T);c.fill();c.stroke() + c.fillStyle=PD;c.beginPath();c.moveTo(78,116);c.quadraticCurveTo(116,96,146,118);c.quadraticCurveTo(118,148,78,116);c.fill();c.lineWidth=5;c.stroke() + c.beginPath();c.moveTo(96,120);c.quadraticCurveTo(112,112,128,122);c.stroke();c.lineWidth=7 + c.save();c.translate(150,112);c.rotate(n) + D(26,PK,()=>{c.moveTo(0,0);c.bezierCurveTo(30,-18,-18,-50,2,-74)}) + c.translate(2,-80) + c.fillStyle=PK;c.beginPath();c.arc(0,0,20,0,T);c.fill();c.stroke() + c.fillStyle='#fff';c.beginPath();c.moveTo(14,-8);c.quadraticCurveTo(36,-6,40,8);c.lineTo(30,22);c.quadraticCurveTo(26,8,12,8);c.closePath();c.fill();c.stroke() + c.fillStyle='#111';c.beginPath();c.moveTo(36,2);c.quadraticCurveTo(44,12,32,30);c.quadraticCurveTo(30,20,28,16);c.closePath();c.fill();c.lineWidth=4;c.strokeStyle='#fff';c.stroke();c.strokeStyle='#111';c.lineWidth=7 + c.fillStyle='#111';c.beginPath();c.arc(2,-4,4.5,0,T);c.fill() + c.fillStyle='#fff';c.beginPath();c.arc(0.5,-5.5,1.6,0,T);c.fill() + c.fillStyle=BL;c.beginPath();c.ellipse(-4,8,6,4,0,0,T);c.fill() + c.restore() + c.restore() +} diff --git a/gen/color/fries/dance.js b/gen/color/fries/dance.js new file mode 100644 index 0000000..78d9382 --- /dev/null +++ b/gen/color/fries/dance.js @@ -0,0 +1,27 @@ +// french fries in a red carton bobbing and wiggling one after another like a wave, salt specks drifting down +const{sin,PI,max}=Math,T=2*PI +export const frames=20,delay=70 +const FY='#ffd43b',FD='#f59f00',CA='#ff6b6b',CB='#e03131',BL='#ffc9c9',PK='#ff8fab' +const FR=[[90,48],[118,40],[146,42],[172,52],[104,64],[132,58],[160,66]] +export default(c,t)=>{ + ;[[96,0],[138,.33],[164,.66]].forEach(([x,o])=>{const p=(t+o)%1;c.fillStyle='#fff';c.lineWidth=2.5;c.save();c.translate(x+4*sin(T*p*2),8+p*56);c.rotate(p*6);c.beginPath();c.rect(-3,-3,6,6);c.fill();c.stroke();c.restore()}) + c.lineWidth=7 + c.fillStyle=CB;c.beginPath();c.moveTo(60,128);c.quadraticCurveTo(128,100,196,128);c.lineTo(196,150);c.lineTo(60,150);c.closePath();c.fill();c.stroke() + FR.forEach(([x,top],k)=>{ + const ph=T*t*2-k*.9,dy=-14*max(0,sin(ph)),r=(x-128)/420+.1*sin(ph) + c.save();c.translate(x,150+dy);c.rotate(r);const L=150-top + c.fillStyle=FY;c.beginPath();c.roundRect(-9,-L,18,L+30,4);c.fill() + c.fillStyle=FD;c.fillRect(3,-L+4,6,L+24);c.beginPath();c.roundRect(-9,-L,18,8,4);c.fill() + c.beginPath();c.roundRect(-9,-L,18,L+30,4);c.stroke() + c.fillStyle='#fff';for(const[sx,sy]of[[-4,-L+18],[0,-L+34]])c.fillRect(sx,sy+(k%3)*4,3,3) + c.restore() + }) + c.fillStyle=CA;c.beginPath();c.moveTo(54,124);c.quadraticCurveTo(128,156,202,124);c.lineTo(180,236);c.lineTo(76,236);c.closePath();c.fill();c.stroke() + c.strokeStyle=CB;c.lineWidth=5;c.beginPath();c.moveTo(68,150);c.quadraticCurveTo(128,176,188,150);c.stroke();c.strokeStyle='#111';c.lineWidth=7 + c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.arc(128+d*24,186,6,0,T),c.fill() + c.fillStyle='#fff';for(const d of[-1,1])c.beginPath(),c.arc(128+d*24-2,184,2.2,0,T),c.fill() + const mo=.55+.45*max(0,sin(T*t*4)),mouth=()=>{c.beginPath();c.moveTo(116,196);c.lineTo(140,196);c.bezierCurveTo(140,196+18*mo,116,196+18*mo,116,196);c.closePath()} + c.fillStyle='#111';mouth();c.fill() + c.save();mouth();c.clip();c.fillStyle=PK;c.beginPath();c.ellipse(128,196+14*mo,7,5,0,0,T);c.fill();c.restore() + c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*44,198,9,5,0,0,T),c.fill() +} diff --git a/gen/color/galaxy/swirl.js b/gen/color/galaxy/swirl.js new file mode 100644 index 0000000..5215411 --- /dev/null +++ b/gen/color/galaxy/swirl.js @@ -0,0 +1,25 @@ +// spiral galaxy turning on a round night-sky sticker, two candy-striped arms around a smiley core, stars twinkling +// both arms match, so half a turn per loop is seamless; the dark sky gets a white rim for dark backgrounds +const{sin,cos,PI,exp,max,min,hypot}=Math,T=2*PI +export const frames=20,delay=90 +const SKY='#2b3a7a',ARM='#9775fa',STRIPE='#faa2c1',BL='#ff8787',SQ=.64,M=9.4 +const spk=(c,x,y,s)=>{c.lineWidth=3;c.fillStyle='#fff';c.beginPath();c.moveTo(x,y-s);for(const[px,py,qx,qy]of[[s,0,s*.2,-s*.2],[0,s,s*.2,s*.2],[-s,0,-s*.2,s*.2],[0,-s,-s*.2,-s*.2]])c.quadraticCurveTo(x+qx,y+qy,x+px,y+py);c.fill();c.stroke()} +export default(c,t)=>{ + const disc=()=>{c.beginPath();c.arc(128,128,112,0,T)},ph=-t*PI + disc();c.lineWidth=18;c.strokeStyle='#fff';c.stroke();c.fillStyle=SKY;c.fill();c.strokeStyle='#111' + c.save();disc();c.clip() + for(const[x,y,o]of[[44,62,0],[206,50,.3],[218,190,.6],[50,198,.15],[128,30,.8],[136,230,.45]])spk(c,x,y,2+4*max(0,sin((t+o)*2*T))) + const P=(th,k)=>{const r=16*exp(.19*th),q=th+k*PI+ph;return[128+r*cos(q),128+r*sin(q)*SQ]} + for(const k of[0,1]){ + const C=[];for(let th=0;th<=M;th+=.2)C.push([P(th,k),th]) + const side=(f,g)=>C.map(([[x,y],th],j)=>{const[[x0,y0]]=C[max(0,j-1)],[[x1,y1]]=C[min(C.length-1,j+1)],l=hypot(x1-x0,y1-y0),w=f*(24*(1-th/M)+2);return[x-(y1-y0)/l*w*g,y+(x1-x0)/l*w*g]}) + for(const[f,col,lw]of[[1,ARM,5],[.4,STRIPE,0]]){c.beginPath();for(const[x,y]of[...side(f,1),...side(f,-1).reverse()])c.lineTo(x,y);c.closePath();c.fillStyle=col;c.fill();if(lw)c.lineWidth=lw,c.stroke()} + c.fillStyle='#fff';for(const th of[3.4,5.2,7,8.4]){const[x,y]=P(th,k);c.beginPath();c.arc(x,y,2.6,0,T);c.fill()} + } + c.lineWidth=5;c.fillStyle='#fff3bf';c.beginPath();c.ellipse(128,128,36,28,0,0,T);c.fill();c.stroke() + c.fillStyle='#ffd43b';c.beginPath();c.ellipse(128,128,26,20,0,0,T);c.fill();c.lineWidth=4;c.stroke() + for(const d of[-1,1])c.beginPath(),c.arc(128+d*9,127,4.5,PI*1.15,PI*1.85),c.stroke() + c.lineWidth=3.5;c.beginPath();c.arc(128,130,5,.15*PI,.85*PI);c.stroke() + c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*16,133,4,2.5,0,0,T),c.fill() + c.restore();c.lineWidth=7;disc();c.stroke() +} diff --git a/gen/color/giraffe/munch.js b/gen/color/giraffe/munch.js new file mode 100644 index 0000000..6db7580 --- /dev/null +++ b/gen/color/giraffe/munch.js @@ -0,0 +1,33 @@ +// giraffe stretching its long neck up to a branch, munching leaves with its jaw working and an ear flicking +const{sin,cos,PI,abs}=Math,T=2*PI +export const frames=16,delay=80 +const GY='#ffc078',SP='#a9744f',MZ='#ffe8cc',BR='#8d6346',LF='#69db7c',LD='#51cf66',BL='#ffa8a8' +export default(c,t,i)=>{ + const ch=abs(sin(3*PI*t)),hx=-3*ch,fl=i==10||i==11?.7:i==9||i==12?.35:0 + const D=(wd,col,f)=>{for(const[l,s]of[[wd,'#111'],[wd-10,col]])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),f(),c.stroke();c.lineWidth=7;c.strokeStyle='#111'} + D(20,BR,()=>{c.moveTo(118,24);c.quadraticCurveTo(190,18,248,38)}) + c.fillStyle=LF;for(const[x,y,r]of[[150,34,.6],[176,40,.2],[236,52,-.4],[244,26,-1]])c.beginPath(),c.ellipse(x,y,14,8,r,0,T),c.fill(),c.stroke() + for(const[x,y]of[[54,196],[104,198]])D(18,GY,()=>{c.moveTo(x,y);c.lineTo(x-2,236)}) + for(const[x,y]of[[70,198],[120,196]])D(18,GY,()=>{c.moveTo(x,y);c.lineTo(x+2,238)}) + c.fillStyle=BR;for(const x of[52,72,102,122])c.beginPath(),c.roundRect(x-10,232,20,12,4),c.fill(),c.stroke() + D(10,GY,()=>{c.moveTo(42,170);c.quadraticCurveTo(26,184,30,206)}) + c.fillStyle=BR;c.beginPath();c.ellipse(30,210,6,10,0,0,T);c.fill();c.lineWidth=5;c.stroke();c.lineWidth=7 + for(const x of[188,200])D(10,GY,()=>{c.moveTo(x+hx,70);c.lineTo(x+hx-2,48)}) + c.fillStyle=BR;for(const x of[186,198])c.beginPath(),c.arc(x+hx,46,7,0,T),c.fill(),c.stroke() + D(16,BR,()=>{for(let k=0;k<=8;k++){const u=k/8,o=k%2?-7:0;c.lineTo(96+u*(70+hx)+o,158-u*86+o)}}) + const sil=()=>{c.beginPath();c.ellipse(86,180,50,28,-.08,0,T);c.moveTo(102,164);c.lineTo(174+hx,80);c.lineTo(196+hx,94);c.lineTo(130,176);c.closePath();c.moveTo(220+hx,80);c.ellipse(194+hx,80,27,18,-.35,0,T)} + sil();c.lineWidth=14;c.stroke();c.fillStyle=GY;sil();c.fill() + c.save();sil();c.clip();c.fillStyle=SP + for(const[x,y,r]of[[60,172,11],[86,186,12],[112,170,10],[78,160,8],[46,190,7],[122,190,8],[118,148,9],[136,128,9],[150,108,8],[132,150,6],[164,96,6]])c.beginPath(),c.ellipse(x,y,r,r*.8,.5,0,T),c.fill() + c.restore();c.lineWidth=7 + c.save();c.translate(178+hx,70);c.rotate(-.9-fl);c.fillStyle=GY;c.beginPath();c.ellipse(-12,0,14,7,0,0,T);c.fill();c.stroke();c.fillStyle=BL;c.beginPath();c.ellipse(-12,0,7,3,0,0,T);c.fill();c.restore() + c.save();c.translate(212+hx,74);c.rotate(-.35) + c.fillStyle=MZ;c.beginPath();c.ellipse(4,-2,13,11,0,0,T);c.fill();c.lineWidth=5;c.stroke() + c.beginPath();c.moveTo(8,8+ch*2);c.quadraticCurveTo(0,12+ch*5,-8,8);c.stroke() + c.fillStyle='#111';c.beginPath();c.ellipse(8,-4,2.5,2,0,0,T);c.fill() + c.restore() + D(10,LD,()=>{c.moveTo(224+hx,82+ch*2);c.quadraticCurveTo(232,70,236,56)}) + c.fillStyle=LF;c.beginPath();c.ellipse(232+hx*.5,84+ch*3,10,6,.5+ch*.3,0,T);c.fill();c.lineWidth=5;c.stroke();c.lineWidth=7 + c.lineWidth=5;c.beginPath();c.arc(190+hx,76,6,1.1*PI,1.9*PI);c.stroke();c.lineWidth=7 + c.fillStyle=BL;c.beginPath();c.ellipse(196+hx,88,7,4,-.3,0,T);c.fill() +} diff --git a/gen/color/helicopter/hover.js b/gen/color/helicopter/hover.js new file mode 100644 index 0000000..c479b57 --- /dev/null +++ b/gen/color/helicopter/hover.js @@ -0,0 +1,33 @@ +// cute helicopter hovering and gently tilting, main rotor whirring, tail rotor spinning, downwash puffs below +const{sin,cos,PI,abs,round}=Math,T=2*PI +export const frames=16,delay=70 +const B='#ff6b6b',BD='#e03131',GL='#a5d8ff',SK='#868e96',RT='#dee2e6',BL='#ffa8a8' +const body=c=>{c.beginPath();c.ellipse(140,138,62,44,0,0,T)} +export default(c,t)=>{ + const b=round(4*sin(t*T)),r=.05*sin(t*T+1),th=t*T*2 + c.lineWidth=5 + for(const d of[-1,1])for(let k=0;k<2;k++){const p=(t*2+k/2)%1,x=140+d*(30+64*p),y=226+8*p,l=12*sin(PI*p);if(l>2)for(const[w,s]of[[9,'#111'],[3,'#fff']])c.lineWidth=w,c.strokeStyle=s,c.beginPath(),c.moveTo(x-d*l,y-l*.3),c.lineTo(x+d*l,y+l*.3),c.stroke()} + c.strokeStyle='#111' + c.save();c.translate(140,140+b);c.rotate(r);c.translate(-140,-140) + for(const[lw,st]of[[12,'#111'],[5,SK]])c.lineWidth=lw,c.strokeStyle=st,c.beginPath(),c.moveTo(118,176),c.lineTo(112,204),c.moveTo(162,176),c.lineTo(168,204),c.moveTo(80,204),c.lineTo(196,204),c.quadraticCurveTo(212,204,214,192),c.stroke() + c.strokeStyle='#111';c.lineWidth=7 + c.fillStyle=B;c.beginPath();c.moveTo(92,122);c.lineTo(34,112);c.lineTo(34,128);c.lineTo(92,152);c.closePath();c.fill();c.stroke() + c.fillStyle=BD;c.beginPath();c.moveTo(28,126);c.lineTo(18,90);c.lineTo(38,94);c.lineTo(44,120);c.closePath();c.fill();c.stroke() + const tp=t*T*6 + for(const a of[tp,tp+PI/2])for(const[lw,st]of[[10,'#111'],[4,RT]])c.lineWidth=lw,c.strokeStyle=st,c.beginPath(),c.moveTo(26-14*cos(a),104-14*sin(a)),c.lineTo(26+14*cos(a),104+14*sin(a)),c.stroke() + c.strokeStyle='#111';c.lineWidth=5;c.fillStyle=SK;c.beginPath();c.arc(26,104,5,0,T);c.fill();c.stroke() + c.lineWidth=7;c.fillStyle=SK;c.beginPath();c.roundRect(132,80,16,18,4);c.fill();c.stroke() + body(c);c.fillStyle=B;c.fill() + c.save();body(c);c.clip();c.fillStyle=BD;c.fillRect(70,160,140,30) + c.fillStyle=GL;c.beginPath();c.ellipse(170,124,40,30,0,0,T);c.fill();c.lineWidth=5;c.stroke();c.restore() + c.lineWidth=7;body(c);c.stroke() + c.strokeStyle='#fff';c.lineWidth=5;c.beginPath();c.arc(170,124,26,PI*1.1,PI*1.4);c.stroke();c.strokeStyle='#111' + c.fillStyle='#111';for(const x of[156,182])c.beginPath(),c.ellipse(x,124,5,7,0,0,T),c.fill() + c.fillStyle='#fff';for(const x of[154,180])c.beginPath(),c.arc(x,121,2.5,0,T),c.fill() + c.fillStyle=BL;for(const x of[142,196])c.beginPath(),c.ellipse(x,150,8,5,0,0,T),c.fill() + c.lineWidth=4;c.beginPath();c.arc(169,146,7,.2*PI,.8*PI);c.stroke() + for(const[lw,st]of[[9,'#111'],[3,RT]])c.lineWidth=lw,c.strokeStyle=st,c.beginPath(),c.ellipse(140,78,104,7,0,0,T),c.stroke() + for(const a of[th,th+PI/2])for(const[lw,st]of[[11,'#111'],[5,SK]])c.lineWidth=lw,c.strokeStyle=st,c.beginPath(),c.moveTo(140-100*cos(a),78-5*sin(a)),c.lineTo(140+100*cos(a),78+5*sin(a)),c.stroke() + c.strokeStyle='#111';c.lineWidth=5;c.fillStyle=SK;c.beginPath();c.arc(140,78,8,0,T);c.fill();c.stroke() + c.restore() +} diff --git a/gen/color/hotair/rise.js b/gen/color/hotair/rise.js new file mode 100644 index 0000000..1e44526 --- /dev/null +++ b/gen/color/hotair/rise.js @@ -0,0 +1,31 @@ +// striped hot-air balloon with a happy face bobbing up and down, burner flame puffing, little clouds drifting by +const{sin,PI,round}=Math,T=2*PI +export const frames=20,delay=80 +const ST=['#ff6b6b','#ffd43b','#4dabf7','#fff'],SK='#fa5252',BK='#a9744f',BKD='#7f5539',FO='#ff922b',FY='#ffd43b',BL='#ffa8a8' +const env=c=>{c.beginPath();c.moveTo(100,170);c.bezierCurveTo(30,128,36,16,128,16);c.bezierCurveTo(220,16,226,128,156,170);c.closePath()} +// union outline: stroke every puff wide in black, then fill them all white +const cloud=(c,x,y)=>{const b=[[-16,4,12],[2,-4,16],[20,4,11]];c.lineWidth=14;for(const[a,d,r]of b)c.beginPath(),c.arc(x+a,y+d,r,0,T),c.stroke();c.fillStyle='#fff';for(const[a,d,r]of b)c.beginPath(),c.arc(x+a,y+d,r,0,T),c.fill();c.lineWidth=7} +export default(c,t)=>{ + const b=round(6*sin(t*T)),p=.5+.5*sin(4*T*t),fh=12+14*p + cloud(c,round(48+8*sin(t*T)),212);cloud(c,round(204-8*sin(t*T+1)),56) + c.translate(0,b) + for(const[w,s]of[[9,'#111'],[3,'#ffd8a8']])c.lineWidth=w,c.strokeStyle=s,c.beginPath(),c.moveTo(106,180),c.lineTo(112,206),c.moveTo(150,180),c.lineTo(144,206),c.stroke() + c.strokeStyle='#111' + c.fillStyle=FO;c.lineWidth=5;c.beginPath();c.moveTo(118,204);c.quadraticCurveTo(114,196-fh*.4,128,196-fh);c.quadraticCurveTo(142,196-fh*.4,138,204);c.closePath();c.fill();c.stroke() + c.fillStyle=FY;c.beginPath();c.moveTo(123,203);c.quadraticCurveTo(121,199-fh*.3,128,199-fh*.6);c.quadraticCurveTo(135,199-fh*.3,133,203);c.closePath();c.fill() + c.lineWidth=7;env(c);c.fillStyle=ST[0];c.fill() + c.save();env(c);c.clip() + for(const[k,rx]of[[1,70],[2,46],[3,20]])c.fillStyle=ST[k],c.beginPath(),c.ellipse(128,92,rx,120,0,0,T),c.fill() + c.lineWidth=4;for(const rx of[70,46,20])c.beginPath(),c.ellipse(128,92,rx,120,0,0,T),c.stroke() + c.restore() + c.lineWidth=7;env(c);c.stroke() + c.strokeStyle='#fff';c.lineWidth=6;c.beginPath();c.arc(128,96,62,PI*1.12,PI*1.3);c.stroke();c.strokeStyle='#111' + c.fillStyle='#111';for(const x of[108,148])c.beginPath(),c.ellipse(x,108,6,8,0,0,T),c.fill() + c.fillStyle='#fff';for(const x of[106,146])c.beginPath(),c.arc(x,105,2.5,0,T),c.fill() + c.fillStyle=BL;for(const x of[92,164])c.beginPath(),c.ellipse(x,124,9,5,0,0,T),c.fill() + c.lineWidth=5;c.beginPath();c.arc(128,118,10,.15*PI,.85*PI);c.stroke() + c.lineWidth=7;c.fillStyle=SK;c.beginPath();c.moveTo(100,170);c.lineTo(156,170);c.lineTo(150,184);c.lineTo(106,184);c.closePath();c.fill();c.stroke() + c.fillStyle=BKD;c.beginPath();c.roundRect(102,200,52,10,5);c.fill();c.stroke() + c.fillStyle=BK;c.beginPath();c.roundRect(106,208,44,30,6);c.fill();c.stroke() + c.lineWidth=4;c.beginPath();for(const y of[218,228])c.moveTo(108,y),c.lineTo(148,y);for(const x of[120,136])c.moveTo(x,210),c.lineTo(x,236);c.stroke() +} diff --git a/gen/color/island/sway.js b/gen/color/island/sway.js new file mode 100644 index 0000000..35bfef4 --- /dev/null +++ b/gen/color/island/sway.js @@ -0,0 +1,24 @@ +// tiny smiling island with a palm tree swaying in the breeze, waves lapping all around it, coconuts in the fronds +const{sin,cos,PI,abs}=Math,T=2*PI +export const frames=12,delay=120 +const SEA='#4dabf7',SL='#a5d8ff',SAND='#ffd8a8',SD='#ffc078',TR='#a9744f',TD='#7f5539',FR='#51cf66',FD='#2f9e44',CO='#8d6346',BL='#ff8787' +export default(c,t)=>{ + const a=t*T,s=sin(a),f=4*sin(2*a) + c.beginPath();for(let k=0;k<=96;k++){const q=k/96*T,r=1+.03*sin(8*q-2*a);c.lineTo(128+114*r*cos(q),198+42*r*sin(q))}c.closePath();c.fillStyle=SEA;c.fill();c.stroke() + c.strokeStyle=SL;c.lineWidth=5;c.beginPath();c.ellipse(128,198,90+f,31+f*.4,0,0,T);c.stroke() + c.strokeStyle='#fff';c.lineWidth=5;c.beginPath();c.ellipse(128,194,74+f*.7,26+f*.3,0,0,T);c.stroke() + for(const[x,y,o]of[[38,204,0],[214,196,2],[96,228,4],[170,226,1]]){const b=2*sin(2*a+o);c.lineWidth=4;c.beginPath();c.arc(x,y+b,7,PI*1.1,PI*1.9);c.stroke();c.beginPath();c.arc(x+13,y+b,7,PI*1.1,PI*1.9);c.stroke()} + c.strokeStyle='#111';c.lineWidth=7;c.fillStyle=SAND;c.beginPath();c.ellipse(128,190,64,22,0,0,T);c.fill();c.stroke() + c.fillStyle=SD;c.beginPath();c.ellipse(96,194,14,4,0,0,T);c.fill() + const px=150+10*s,py=84,bx=116,by=186,cx=110,cy=132 + for(const[w,col]of[[26,'#111'],[16,TR]])c.lineWidth=w,c.strokeStyle=col,c.beginPath(),c.moveTo(bx,by),c.quadraticCurveTo(cx,cy,px,py),c.stroke() + c.strokeStyle=TD;c.lineWidth=3 + for(let u=.12;u<.9;u+=.13){const v=1-u,x=v*v*bx+2*u*v*cx+u*u*px,y=v*v*by+2*u*v*cy+u*u*py,tx=2*v*(cx-bx)+2*u*(px-cx),ty=2*v*(cy-by)+2*u*(py-cy),l=Math.hypot(tx,ty),nx=-ty/l,ny=tx/l;c.beginPath();c.moveTo(x-nx*6,y-ny*6);c.quadraticCurveTo(x+tx/l*3,y+ty/l*3,x+nx*6,y+ny*6);c.stroke()} + c.strokeStyle='#111' + const frond=(d,q,L,k)=>{c.save();c.translate(px,py);c.scale(d,1);c.rotate(q+.1*sin(a+k*.7));c.fillStyle=FR;c.lineWidth=6;c.beginPath();c.moveTo(0,0);c.quadraticCurveTo(L*.5,-20,L,10);c.quadraticCurveTo(L*.5,4,0,0);c.fill();c.stroke();c.strokeStyle=FD;c.lineWidth=3;c.beginPath();c.moveTo(4,-2);c.quadraticCurveTo(L*.5,-10,L-6,8);c.stroke();c.restore();c.strokeStyle='#111'} + for(const[d,q,L,k]of[[-1,-.7,58,0],[1,-.75,56,1],[-1,.05,66,2],[1,.05,68,3],[-1,.62,52,4],[1,.6,54,5]])frond(d,q,L,k) + c.fillStyle=CO;c.lineWidth=5;for(const[x,y]of[[-9,10],[7,11],[-1,19]])c.beginPath(),c.arc(px+x,py+y,9,0,T),c.fill(),c.stroke() + c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.arc(146+d*12,196,4,0,T),c.fill() + c.lineWidth=3.5;c.beginPath();c.arc(146,200,6,.15*PI,.85*PI);c.stroke() + c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(146+d*22,202,5,3,0,0,T),c.fill() +} diff --git a/gen/color/latte/art.js b/gen/color/latte/art.js new file mode 100644 index 0000000..cde9288 --- /dev/null +++ b/gen/color/latte/art.js @@ -0,0 +1,32 @@ +// latte in a teal cup seen from above at an angle, a heart of foam blooming in the coffee and fading, steam curling up +const{sin,PI,min,max}=Math,T=2*PI +export const frames=24,delay=80 +const CU='#38d9a9',CD='#0ca678',SA='#fff',CO='#a9744f',CR='#c99a6e',FO='#fff4e6',BL='#ffa8a8' +const heart=(c,s)=>{c.beginPath();c.moveTo(0,s*.95);c.bezierCurveTo(-s*1.5,0,-s*.9,-s*1.05,0,-s*.45);c.bezierCurveTo(s*.9,-s*1.05,s*1.5,0,0,s*.95);c.closePath()} +export default(c,t)=>{ + c.fillStyle=SA;c.beginPath();c.ellipse(128,204,112,34,0,0,T);c.fill();c.stroke() + c.lineWidth=4;c.beginPath();c.ellipse(128,200,74,20,0,0,T);c.stroke();c.lineWidth=7 + for(const[w,s]of[[22,'#111'],[11,CU]])c.lineWidth=w,c.strokeStyle=s,c.beginPath(),c.ellipse(208,146,22,17,-.2,-1.2,1.6),c.stroke() + c.strokeStyle='#111';c.lineWidth=7 + c.fillStyle=CU;c.beginPath();c.moveTo(52,110);c.bezierCurveTo(56,230,200,230,204,110);c.closePath();c.fill();c.stroke() + c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.arc(128+d*24,174,6,0,T),c.fill() + c.fillStyle='#fff';for(const d of[-1,1])c.beginPath(),c.arc(128+d*24-2,172,2.2,0,T),c.fill() + c.lineWidth=5;c.beginPath();c.arc(128,180,8,.15*PI,.85*PI);c.stroke() + c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*44,184,9,5,0,0,T),c.fill() + c.lineWidth=7;c.fillStyle=CU;c.beginPath();c.ellipse(128,110,76,40,0,0,T);c.fill();c.stroke() + c.fillStyle=CO;c.beginPath();c.ellipse(128,112,62,31,0,0,T);c.fill();c.lineWidth=5;c.stroke() + c.fillStyle=CR;c.beginPath();c.ellipse(128,113,50,23,0,0,T);c.fill() + const p=t,g=min(1,p/.3),m=min(1,max(0,(p-.25)/.25)),f=p>.82?1-(p-.82)/.18:1 + if(p>.02){ + c.save();c.globalAlpha=f;c.translate(128,114);c.scale(1+.25*(1-f),.52*(1+.25*(1-f)));c.fillStyle=FO + const rc=22*g*(1-m);if(rc>.5)c.beginPath(),c.arc(0,4,rc,0,T),c.fill() + if(m>0){heart(c,30*m);c.fill();c.strokeStyle=CR;c.lineWidth=5;heart(c,17*m);c.stroke();c.fillStyle=FO;heart(c,11*m);c.fill()} + c.restore();c.strokeStyle='#111' + } + c.lineCap='round' + for(let k=0;k<3;k++){ + const x0=102+k*26,pts=[];for(let s=0;s<=1.001;s+=.1)pts.push([x0+7*sin(T*(s*1.1-t)+k*2),64-44*s]) + for(const[w,s]of[[9,'#111'],[4,'#fff']]){c.lineWidth=w;c.strokeStyle=s;c.beginPath();pts.forEach(([x,y])=>c.lineTo(x,y));c.stroke()} + } + c.strokeStyle='#111';c.lineWidth=7 +} diff --git a/gen/color/lavalamp/flow.js b/gen/color/lavalamp/flow.js new file mode 100644 index 0000000..b8e8b1f --- /dev/null +++ b/gen/color/lavalamp/flow.js @@ -0,0 +1,27 @@ +// lava lamp with a happy base, pink blobs rising, merging and sinking through blue goo +const{sin,cos,PI,abs}=Math,T=2*PI +export const frames=24,delay=90 +const CAP='#9775fa',CD='#7048e8',GOO='#74c0fc',BLOB='#f783ac',BH='#ffdeeb',BL='#faa2c1' +const glass=c=>{c.beginPath();c.moveTo(100,62);c.lineTo(156,62);c.lineTo(180,166);c.quadraticCurveTo(184,190,160,190);c.lineTo(96,190);c.quadraticCurveTo(72,190,76,166);c.closePath()} +export default(c,t)=>{ + c.fillStyle=CAP;c.beginPath();c.moveTo(94,186);c.lineTo(162,186);c.lineTo(194,232);c.quadraticCurveTo(198,244,184,244);c.lineTo(72,244);c.quadraticCurveTo(58,244,62,232);c.closePath();c.fill();c.stroke() + c.fillStyle=CD;c.beginPath();c.roundRect(86,182,84,12,6);c.fill();c.lineWidth=5;c.stroke() + c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.arc(128+d*18,214,5,0,T),c.fill() + c.fillStyle='#fff';for(const d of[-1,1])c.beginPath(),c.arc(126+d*18,212,1.8,0,T),c.fill() + c.lineWidth=4;c.beginPath();c.arc(128,219,7,.15*PI,.85*PI);c.stroke() + c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*34,224,8,4,0,0,T),c.fill() + c.lineWidth=7;glass(c);c.fillStyle=GOO;c.fill() + c.save();glass(c);c.clip() + const bl=[[0,124,20],[.3,110,16],[.55,146,14],[.8,132,11]].map(([p,x,r])=>{ + const s=sin(T*(t+p)),y=126+54*cos(T*(t+p)),v=abs(s) + return[x+7*sin(2*T*(t+p)),y,r*(1-.12*v),r*(1+.3*v)] + }) + const blobs=()=>{c.beginPath();c.ellipse(128,198,58,20,0,0,T);c.moveTo(158,58);c.ellipse(128,58,30,9,0,0,T);for(const[x,y,rx,ry]of bl)c.moveTo(x+rx,y),c.ellipse(x,y,rx,ry,0,0,T)} + c.lineWidth=10;blobs();c.stroke();c.fillStyle=BLOB;blobs();c.fill() + c.fillStyle=BH;for(const[x,y,rx,ry]of bl)c.beginPath(),c.ellipse(x-rx*.35,y-ry*.4,rx*.28,ry*.2,-.5,0,T),c.fill() + c.restore() + c.lineWidth=7;glass(c);c.stroke() + c.strokeStyle='#fff';c.lineWidth=6;c.beginPath();c.moveTo(108,78);c.lineTo(92,150);c.stroke();c.strokeStyle='#111' + c.lineWidth=7;c.fillStyle=CAP;c.beginPath();c.moveTo(96,64);c.lineTo(160,64);c.lineTo(146,22);c.quadraticCurveTo(144,16,138,16);c.lineTo(118,16);c.quadraticCurveTo(112,16,110,22);c.closePath();c.fill();c.stroke() + c.fillStyle=CD;c.beginPath();c.roundRect(92,58,72,10,5);c.fill();c.lineWidth=5;c.stroke() +} diff --git a/gen/color/lotus/bloom.js b/gen/color/lotus/bloom.js new file mode 100644 index 0000000..d29dfb0 --- /dev/null +++ b/gen/color/lotus/bloom.js @@ -0,0 +1,24 @@ +// pink lotus on a lily pad opening into full bloom and closing back to a bud, ripples spreading on the pond +const{sin,cos,PI,max,min}=Math,T=2*PI +export const frames=16,delay=100 +const W='#74c0fc',WL='#d0ebff',PAD='#51cf66',PD='#2f9e44',P1='#fcc2d7',P2='#f783ac',PV='#fff0f6',POD='#ffd43b',PDK='#f59f00' +const spk=(c,x,y,s)=>{c.lineWidth=3;c.fillStyle='#fff';c.beginPath();c.moveTo(x,y-s);for(const[px,py,qx,qy]of[[s,0,s*.2,-s*.2],[0,s,s*.2,s*.2],[-s,0,-s*.2,s*.2],[0,-s,-s*.2,-s*.2]])c.quadraticCurveTo(x+qx,y+qy,x+px,y+py);c.fill();c.stroke()} +export default(c,t)=>{ + const o=min(1,max(0,(1-cos(t*T))/2*1.3-.12)),bx=128,by=166 + const pond=()=>{c.beginPath();c.ellipse(128,174,118,48,0,0,T)} + pond();c.fillStyle=W;c.fill();c.stroke() + c.save();pond();c.clip() + for(let k=0;k<2;k++){const p=(t*2+k/2)%1,r=98+50*p;c.strokeStyle=WL;c.lineWidth=5*(1-p)+1;c.beginPath();c.ellipse(128,176,r,r*.36,0,0,T);c.stroke()} + c.restore();c.strokeStyle='#111';c.lineWidth=5 + c.fillStyle=PAD;c.beginPath();c.ellipse(204,204,16,6,0,0,T);c.fill();c.stroke() + c.lineWidth=7;c.beginPath();c.moveTo(128,176);c.ellipse(128,176,96,32,0,.4,.4+T-.45);c.closePath();c.fill();c.stroke() + c.strokeStyle=PD;c.lineWidth=3;for(const q of[1.2,1.9,2.6,3.4,4.2,5,5.7])c.beginPath(),c.moveTo(128,176),c.lineTo(128+82*cos(q),176+26*sin(q)),c.stroke() + c.strokeStyle='#111' + const petal=(a,L,w,col)=>{c.save();c.translate(bx,by);c.rotate(a);c.beginPath();c.moveTo(0,0);c.quadraticCurveTo(-w,-L*.5,0,-L);c.quadraticCurveTo(w,-L*.5,0,0);c.fillStyle=col;c.fill();c.lineWidth=7;c.stroke();c.strokeStyle=PV;c.lineWidth=4;c.beginPath();c.moveTo(0,-12);c.lineTo(0,-L*.68);c.stroke();c.restore();c.strokeStyle='#111'} + petal(0,117,55,P1);for(const d of[-1,1])petal(d*(.36+.95*o),107,55,P1) + c.fillStyle=POD;c.lineWidth=6;c.beginPath();c.ellipse(128,109,26,12,0,0,T);c.fill();c.stroke() + c.fillStyle=PDK;for(const x of[-13,0,13])c.beginPath(),c.arc(128+x,109,3,0,T),c.fill() + for(const d of[-1,1])petal(d*(.12+.72*o),96,49,P2) + petal(0,88*(1-.52*o),49,P2) + if(o>.7)for(const[x,y,q]of[[46,62,0],[210,54,.5]])spk(c,x,y,(o-.7)/.3*(5+5*max(0,sin((t+q)*4*T)))) +} diff --git a/gen/color/maneki/beckon.js b/gen/color/maneki/beckon.js new file mode 100644 index 0000000..c1f39e3 --- /dev/null +++ b/gen/color/maneki/beckon.js @@ -0,0 +1,42 @@ +// maneki-neko lucky cat beckoning with its raised paw, hugging a gold koban coin, collar bell swinging +const{sin,cos,PI,max}=Math,T=2*PI +export const frames=16,delay=80 +const W='#fff',OR='#ffa94d',DK='#495057',PK='#faa2c1',CO='#ff6b6b',G='#ffd43b',GD='#fab005',CU='#f783ac',CUD='#e64980' +export default(c,t)=>{ + // the paw curls down twice per loop: its height squashes by cos of the curl and the toe beans tip out of view + const cu=.5-.5*cos(2*T*t),k=cos(cu*1.2),bell=.35*sin(2*T*t-.8) + c.fillStyle=CU;c.beginPath();c.roundRect(34,206,188,36,16);c.fill();c.stroke() + c.fillStyle=CUD;for(const x of[44,212])c.beginPath(),c.arc(x,238,7,0,T),c.fill(),c.stroke() + c.fillStyle=W;c.beginPath();c.ellipse(128,176,64,50,0,0,T);c.fill();c.stroke() + c.fillStyle=OR;c.save();c.beginPath();c.ellipse(128,176,64,50,0,0,T);c.clip();c.beginPath();c.ellipse(78,206,30,26,0,0,T);c.fill();c.restore() + c.beginPath();c.ellipse(128,176,64,50,0,0,T);c.stroke() + c.fillStyle=W;for(const x of[100,156])c.beginPath(),c.ellipse(x,220,18,10,0,0,T),c.fill(),c.stroke() + c.fillStyle=W;c.beginPath();c.moveTo(78,74);c.lineTo(84,30);c.lineTo(114,58);c.closePath();c.fill();c.stroke() + c.fillStyle=DK;c.beginPath();c.moveTo(142,58);c.lineTo(172,30);c.lineTo(178,74);c.closePath();c.fill();c.stroke() + c.fillStyle=PK;c.lineWidth=4;c.beginPath();c.moveTo(88,60);c.lineTo(89,42);c.lineTo(104,56);c.closePath();c.fill() + c.lineWidth=7;c.fillStyle=W;c.beginPath();c.ellipse(128,100,64,52,0,0,T);c.fill() + c.save();c.clip();c.fillStyle=OR;c.beginPath();c.ellipse(82,66,34,26,.3,0,T);c.fill();c.fillStyle=DK;c.beginPath();c.ellipse(170,60,22,16,-.3,0,T);c.fill();c.restore() + c.beginPath();c.ellipse(128,100,64,52,0,0,T);c.stroke() + c.lineWidth=5;for(const x of[104,152])c.beginPath(),c.arc(x,104,10,PI*1.15,PI*1.85),c.stroke() + c.fillStyle=PK;for(const x of[90,166])c.beginPath(),c.ellipse(x,118,9,5,0,0,T),c.fill() + c.fillStyle=CUD;c.lineWidth=3;c.beginPath();c.moveTo(123,112);c.lineTo(133,112);c.lineTo(128,118);c.closePath();c.fill();c.stroke() + c.lineWidth=4;c.beginPath();c.arc(122,120,6,.1*PI,.9*PI);c.arc(134,120,6,.1*PI,.9*PI);c.stroke() + for(const d of[-1,1])for(const[dy,ang]of[[-4,-.12],[6,.12]])for(const[w,s]of[[7,'#111'],[2,'#fff']])c.lineWidth=w,c.strokeStyle=s,c.beginPath(),c.moveTo(128+d*48,116+dy),c.lineTo(128+d*78,116+dy+ang*30),c.stroke() + c.strokeStyle='#111' + c.lineWidth=6;c.fillStyle=CO;c.beginPath();c.roundRect(84,144,88,14,7);c.fill();c.stroke() + c.save();c.translate(128,156);c.rotate(bell) + c.fillStyle=G;c.lineWidth=5;c.beginPath();c.arc(0,12,11,0,T);c.fill();c.stroke() + c.lineWidth=3;c.beginPath();c.moveTo(-10,10);c.lineTo(10,10);c.moveTo(0,14);c.lineTo(0,22);c.stroke();c.beginPath();c.arc(0,16,2.5,0,T);c.fillStyle='#111';c.fill() + c.restore() + c.lineWidth=6;c.fillStyle=G;c.beginPath();c.ellipse(96,188,20,30,0,0,T);c.fill();c.stroke() + c.fillStyle=GD;c.beginPath();c.ellipse(96,188,13,22,0,0,T);c.fill() + c.lineWidth=3;c.beginPath();for(const y of[176,184,192,200])c.moveTo(88,y),c.lineTo(104,y);c.stroke() + c.strokeStyle='#fff';c.lineWidth=3;c.beginPath();c.arc(96,188,16,PI*1.15,PI*1.45);c.stroke();c.strokeStyle='#111' + c.lineWidth=6;c.fillStyle=W;c.beginPath();c.ellipse(108,176,14,12,.3,0,T);c.fill();c.stroke() + for(const[w,s]of[[30,'#111'],[18,W]])c.lineWidth=w,c.strokeStyle=s,c.beginPath(),c.moveTo(170,166),c.quadraticCurveTo(192,150,194,108),c.stroke() + c.strokeStyle='#111';c.lineWidth=6 + const py=96-14*k + c.fillStyle=W;c.beginPath();c.ellipse(194,py+14*(1-k)*.4,18,18*max(.45,k),0,0,T);c.fill();c.stroke() + if(k>.5){c.fillStyle=PK;c.beginPath();c.ellipse(194,py+4,7,6*k,0,0,T);c.fill();for(const d of[-9,0,9])c.beginPath(),c.ellipse(194+d,py-9*k,3.5,3.5*k,0,0,T),c.fill()} + else{c.lineWidth=4;c.beginPath();for(const d of[-6,6])c.moveTo(194+d,py+4),c.lineTo(194+d,py+12);c.stroke()} +} diff --git a/gen/color/maple/fall.js b/gen/color/maple/fall.js new file mode 100644 index 0000000..159ce60 --- /dev/null +++ b/gen/color/maple/fall.js @@ -0,0 +1,31 @@ +// big smiley maple leaf rocking side to side like a pendulum as it floats, two little leaves fluttering down beside it +const{sin,cos,PI,abs,min}=Math,T=2*PI +export const frames=20,delay=90 +const LEAF=[[0,-1],[.16,-.58],[.42,-.74],[.36,-.36],[.84,-.48],[.6,-.1],[.76,.08],[.4,.14],[.52,.42],[.12,.28],[0,.44]] +const PTS=[...LEAF,...LEAF.slice(0,-1).reverse().map(([x,y])=>[-x,y])],TIPS=[[0,-1],[.42,-.74],[.84,-.48],[.76,.08],[.52,.42]] +const CL=[['#ff6b6b','#c92a2a'],['#ff922b','#d9480f'],['#ffd43b','#e67700'],['#fa5252','#a61e1e']] +const leaf=(c,x,y,S,rot,fx,[col,dk],lw,v0=0)=>{ + const m=([px,py])=>{const X=px*S*fx,Y=py*S;return[x+X*cos(rot)-Y*sin(rot),y+X*sin(rot)+Y*cos(rot)]} + for(const[w,s]of[[lw*1.8,'#111'],[lw*.7,dk]]){const[a,b]=m([0,.4]),[e,f]=m([0,.95]);c.lineWidth=w;c.strokeStyle=s;c.beginPath();c.moveTo(a,b);c.lineTo(e,f);c.stroke()} + c.strokeStyle='#111';c.beginPath();for(const p of PTS)c.lineTo(...m(p));c.closePath();c.fillStyle=col;c.fill();c.lineWidth=lw;c.stroke() + c.strokeStyle=dk;c.lineWidth=lw*.5;const[ox,oy]=m([0,.3]) + for(const[tx,ty]of TIPS)for(const d of tx?[-1,1]:[1]){const[ex,ey]=m([tx*d*.62,ty*.62]),[sx,sy]=v0?m([tx*d*v0,ty*v0]):[ox,oy];c.beginPath();c.moveTo(sx,sy);c.lineTo(ex,ey);c.stroke()} + c.strokeStyle='#111';return m +} +export default(c,t)=>{ + const a=t*T,s=sin(a),x=128+38*s,y=126-16*s*s,rot=-.4*s + for(const[o,x0,k]of[[0,40,1],[.5,214,2]]){ + const p=(t+o)%1,S=24*min(1,sin(PI*p)*2.5) + if(S>2)leaf(c,x0+12*sin(p*T*2),34+p*188,S,.4*sin(p*T*2+o*6),cos(p*T*2+o*3),CL[k],4) + } + const v=cos(a),d=v>0?1:-1 + if(abs(v)>.5)for(const[dy,l]of[[-18,22],[6,30],[30,18]])for(const[w,st]of[[9,'#111'],[3.5,'#fff']])c.lineWidth=w,c.strokeStyle=st,c.beginPath(),c.moveTo(x-d*68,y+dy),c.lineTo(x-d*(68+l*abs(v)),y+dy),c.stroke() + c.strokeStyle='#111' + const m=leaf(c,x,y,68,rot,1,CL[0],7,.38),lk=v*.03 + for(const d of[-1,1]){ + const[ex,ey]=m([d*.2,-.18]);c.fillStyle='#111';c.beginPath();c.ellipse(ex,ey,5.5,7,rot,0,T);c.fill() + const[hx,hy]=m([d*.2-.03+lk,-.23]);c.fillStyle='#fff';c.beginPath();c.arc(hx,hy,2.2,0,T);c.fill() + const[bx,by]=m([d*.38,-.04]);c.fillStyle='#ffa8a8';c.beginPath();c.ellipse(bx,by,7,4,rot,0,T);c.fill() + } + const[a0,b0]=m([-.13,-.04]),[a1,b1]=m([0,.1]),[a2,b2]=m([.13,-.04]);c.lineWidth=4;c.beginPath();c.moveTo(a0,b0);c.quadraticCurveTo(a1,b1,a2,b2);c.stroke() +} diff --git a/gen/color/neon/flicker.js b/gen/color/neon/flicker.js new file mode 100644 index 0000000..67f64eb --- /dev/null +++ b/gen/color/neon/flicker.js @@ -0,0 +1,25 @@ +// neon smiley heart sign on a dark board, glowing pink then flickering off and on with a little buzz +const{sin,cos,PI}=Math,T=2*PI +export const frames=24,delay=70 +const BOARD='#343a40',SCREW='#5c636a',PK='#f783ac',PL='#fff0f6',OFF='#7a3352',WIRE='#ced4da',ZAP='#ffd43b' +const ON=[1,1,1,1,1,1,1,1,1,1,0,1,0,0,1,1,1,1,1,1,1,1,0,1] +const heart=c=>{c.beginPath();c.moveTo(128,196);c.bezierCurveTo(42,140,62,58,128,100);c.bezierCurveTo(194,58,214,140,128,196)} +const face=c=>{c.beginPath();for(const x of[110,146])c.moveTo(x,122),c.lineTo(x,132);c.moveTo(112,146);c.quadraticCurveTo(128,160,144,146)} +export default(c,t,i)=>{ + const on=ON[i],zap=ON[i]!=ON[(i+23)%24] + for(const[x0,x1]of[[58,74],[198,182]])for(const[w,st]of[[11,'#111'],[4,WIRE]])c.lineWidth=w,c.strokeStyle=st,c.beginPath(),c.moveTo(x0,2),c.lineTo(x1,46),c.stroke() + const board=()=>{c.beginPath();c.roundRect(22,42,212,190,22)} + c.lineWidth=18;c.strokeStyle='#fff';board();c.stroke() + c.fillStyle=BOARD;board();c.fill();c.lineWidth=7;c.strokeStyle='#111';c.stroke() + c.fillStyle=SCREW;for(const[x,y]of[[40,60],[216,60],[40,214],[216,214]])c.beginPath(),c.arc(x,y,5,0,T),c.fill() + if(on){ + c.save();board();c.clip();c.globalAlpha=.16;c.fillStyle=PK;c.beginPath();c.ellipse(128,140,100,82,0,0,T);c.fill() + c.strokeStyle=PK;for(const[w,al]of[[30,.22],[19,.4]]){c.globalAlpha=al;c.lineWidth=w;heart(c);c.stroke();face(c);c.stroke()} + c.restore() + } + c.strokeStyle='#111';c.lineWidth=13;heart(c);c.stroke();face(c);c.stroke() + c.strokeStyle=on?PK:OFF;c.lineWidth=7;heart(c);c.stroke();face(c);c.stroke() + if(on){c.strokeStyle=PL;c.lineWidth=2.5;heart(c);c.stroke();face(c);c.stroke()} + c.strokeStyle='#111' + if(zap)for(const[x,y,d]of[[190,84,1],[66,84,-1]])for(const[w,st]of[[9,'#111'],[3.5,ZAP]])c.lineWidth=w,c.strokeStyle=st,c.beginPath(),c.moveTo(x,y),c.lineTo(x+d*8,y-6),c.lineTo(x+d*4,y-12),c.lineTo(x+d*14,y-20),c.stroke() +} diff --git a/gen/color/otter/float.js b/gen/color/otter/float.js new file mode 100644 index 0000000..2fbe906 --- /dev/null +++ b/gen/color/otter/float.js @@ -0,0 +1,38 @@ +// otter floating on its back, banging a clam on the stone on its tummy while it bobs on the waves +const{sin,cos,PI,abs,max}=Math,T=2*PI +export const frames=16,delay=80 +const BR='#a9744f',BD='#8d6346',BE='#ffd8a8',WA='#4dabf7',WL='#a5d8ff',ST='#ced4da',SD='#868e96',CL='#faa2c1',CD='#f06595',BL='#ffa8a8' +export default(c,t)=>{ + const b=Math.round(4*sin(T*t)),r=.05*sin(T*t+.6),h=max(0,(abs(sin(T*t))-.4)/.6),bang=h==0 + const wy=(x,o=0)=>186+5*sin(x/20-T*t+o) + const sea=(o,dy)=>{c.fillStyle=WA;c.beginPath();c.moveTo(22,244);c.quadraticCurveTo(8,244,8,230);for(let x=8;x<=248;x+=4)c.lineTo(x,wy(x,o)+dy);c.lineTo(248,230);c.quadraticCurveTo(248,244,234,244);c.closePath();c.fill();c.stroke()} + sea(2,-14) + c.save();c.translate(128,170+b);c.rotate(r);c.translate(-128,-170) + c.fillStyle=BR + c.beginPath();c.moveTo(48,166);c.quadraticCurveTo(14,170,10,148);c.quadraticCurveTo(26,156,50,152);c.fill();c.stroke() + for(const[x,a]of[[56,-.5],[76,-.2]])c.save(),c.translate(x,150),c.rotate(a),c.beginPath(),c.ellipse(0,-14,9,16,0,0,T),c.fill(),c.stroke(),c.restore() + c.beginPath();c.ellipse(116,168,76,30,0,0,T);c.fill();c.stroke() + c.fillStyle=BE;c.beginPath();c.ellipse(118,160,56,18,0,0,T);c.fill() + c.fillStyle=BR;c.beginPath();c.arc(194,140,36,0,T);c.fill();c.stroke() + c.fillStyle=BE;c.beginPath();c.ellipse(204,150,22,16,0,0,T);c.fill() + for(const[x,y]of[[176,108],[216,112]])c.fillStyle=BR,c.beginPath(),c.arc(x,y,8,0,T),c.fill(),c.stroke() + c.fillStyle='#111';c.beginPath();c.ellipse(206,142,6,4,0,0,T);c.fill() + c.lineWidth=5;c.beginPath();c.moveTo(206,146);c.lineTo(206,152);c.moveTo(196,154);c.quadraticCurveTo(206,160,216,154);c.stroke() + for(const[x,y]of[[184,128],[220,130]])c.beginPath(),bang?(c.moveTo(x-6,y-4),c.lineTo(x+2,y),c.lineTo(x-6,y+4)):c.arc(x,y+2,6,1.15*PI,1.85*PI),c.stroke() + c.lineWidth=3;for(const d of[-1,1])c.beginPath(),c.moveTo(222,150+d*3),c.lineTo(236,148+d*6),c.stroke() + c.fillStyle=BL;c.beginPath();c.ellipse(180,148,7,4,0,0,T);c.fill() + c.lineWidth=7 + c.fillStyle=ST;c.beginPath();c.ellipse(118,140,22,12,0,0,T);c.fill();c.stroke() + c.fillStyle=SD;c.beginPath();c.ellipse(112,136,6,3,0,0,T);c.fill() + const cy=118-26*h + c.save();c.translate(122,cy);c.rotate(-.2) + c.fillStyle=CL;c.beginPath();c.moveTo(-18,4);c.quadraticCurveTo(0,-26,18,4);c.quadraticCurveTo(0,12,-18,4);c.fill();c.stroke() + c.lineWidth=3;c.strokeStyle=CD;for(const a of[-.5,0,.5])c.beginPath(),c.moveTo(0,6),c.lineTo(sin(a)*14,-10+abs(a)*6),c.stroke() + c.restore();c.lineWidth=7;c.strokeStyle='#111' + c.fillStyle=BR;for(const d of[-1,1])c.beginPath(),c.moveTo(d<0?98:146,154),c.quadraticCurveTo(d<0?94:150,cy+16,122+d*20,cy+4),c.lineWidth=16,c.stroke(),c.strokeStyle=BR,c.lineWidth=6,c.stroke(),c.strokeStyle='#111',c.lineWidth=7,c.beginPath(),c.arc(122+d*20,cy+4,9,0,T),c.fill(),c.stroke() + if(bang){c.lineWidth=4;for(const[a,l]of[[-2.5,14],[-1.9,16],[-.6,14],[-1.25,16]])c.beginPath(),c.moveTo(122+cos(a)*30,130+sin(a)*22),c.lineTo(122+cos(a)*(30+l),130+sin(a)*(22+l)),c.stroke();c.lineWidth=7} + c.restore() + sea(0,6) + c.strokeStyle=WL;c.lineWidth=4;for(const[x,y,w]of[[60,224,18],[150,230,24],[210,222,14]])c.beginPath(),c.moveTo(x-w,y+2*sin(T*t+x)),c.lineTo(x+w,y+2*sin(T*t+x)),c.stroke() + c.strokeStyle='#111';c.lineWidth=7 +} diff --git a/gen/color/pancake/syrup.js b/gen/color/pancake/syrup.js new file mode 100644 index 0000000..16568b6 --- /dev/null +++ b/gen/color/pancake/syrup.js @@ -0,0 +1,29 @@ +// stack of pancakes on a plate, the butter pat softening on top while blobs of syrup run down the sides +const{sin,PI,min,max,sqrt}=Math,T=2*PI +export const frames=20,delay=80 +const PC='#ffc078',PT='#ffd8a8',SY='#b5651d',SL='#e8a35a',BU='#ffe066',BS='#fcc419',PL='#e7f5ff',BL='#ffa8a8' +const DR=[[70,60],[88,64],[170,64],[190,56]] +export default(c,t)=>{ + c.fillStyle=PL;c.beginPath();c.ellipse(128,214,112,24,0,0,T);c.fill();c.stroke() + c.lineWidth=4;c.beginPath();c.ellipse(128,212,88,15,0,0,T);c.stroke();c.lineWidth=7 + c.fillStyle=SY;c.beginPath();c.ellipse(128,206,100,15,0,0,T);c.fill();c.stroke() + for(let k=0;k<3;k++){const y=200-k*28;c.fillStyle=PC;c.beginPath();c.ellipse(128,y,86,18,0,0,PI);c.lineTo(42,y-22);c.ellipse(128,y-22,86,18,0,PI,T);c.closePath();c.fill();c.stroke()} + c.fillStyle=PT;c.beginPath();c.ellipse(128,122,86,18,0,0,T);c.fill();c.stroke() + c.fillStyle=SY;c.beginPath();for(let a=0;a<=T+.01;a+=.1)c.lineTo(128+Math.cos(a)*(64+5*sin(a*5)),122+sin(a)*(12+2*sin(a*5)));c.closePath();c.fill();c.lineWidth=5;c.stroke();c.lineWidth=7 + const m=(1+sin(T*t))/2 + c.fillStyle='#fff3bf';c.beginPath();c.ellipse(128,118,30+6*m,8+2*m,0,0,T);c.fill() + c.fillStyle=BS;c.beginPath();c.moveTo(104,116);c.lineTo(128,128);c.lineTo(152,116);c.lineTo(152,116-10+3*m);c.lineTo(128,128-10+3*m);c.lineTo(104,116-10+3*m);c.closePath();c.fill();c.lineWidth=5;c.stroke() + const top=116-10+3*m;c.fillStyle=BU;c.beginPath();c.moveTo(104,top);c.quadraticCurveTo(116,top-14,128,top-13);c.quadraticCurveTo(142,top-14,152,top);c.lineTo(128,top+12);c.closePath();c.fill();c.stroke() + c.strokeStyle='#fff';c.lineWidth=3.5;c.beginPath();c.moveTo(116,top-4);c.lineTo(126,top-8);c.stroke();c.strokeStyle='#111' + DR.forEach(([x,L],k)=>{ + const p=(t+k*.27)%1,y0=122+18*sqrt(1-((x-128)/86)**2)-6,yb=y0+L*min(1,p/.6),ya=y0+L*max(0,(p-.6)/.4) + for(const[w,s]of[[15,'#111'],[8,SY]])c.lineWidth=w,c.strokeStyle=s,c.beginPath(),c.moveTo(x,ya),c.lineTo(x,yb),c.stroke() + c.fillStyle=SY;c.lineWidth=5;c.strokeStyle='#111';c.beginPath();c.arc(x,yb+2,8,0,PI);c.stroke();c.beginPath();c.arc(x,yb+1,6.5,0,T);c.fill() + c.strokeStyle=SL;c.lineWidth=3;c.beginPath();c.moveTo(x-2,ya+3);c.lineTo(x-2,max(ya+3,yb-6));c.stroke();c.strokeStyle='#111' + }) + c.lineWidth=7 + c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.arc(128+d*20,151,5.5,0,T),c.fill() + c.fillStyle='#fff';for(const d of[-1,1])c.beginPath(),c.arc(128+d*20-2,149,2,0,T),c.fill() + c.lineWidth=4.5;c.beginPath();c.arc(128,154,7,.15*PI,.85*PI);c.stroke() + c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*29,158,6,3.5,0,0,T),c.fill() +} diff --git a/gen/color/peacock/fan.js b/gen/color/peacock/fan.js new file mode 100644 index 0000000..c31e9e4 --- /dev/null +++ b/gen/color/peacock/fan.js @@ -0,0 +1,34 @@ +// peacock fanning its tail open into a ring of eye feathers, shimmying, then folding it back up +const{sin,cos,PI,min,max}=Math,T=2*PI +export const frames=12,delay=100 +const TL='#38d9a9',TD='#12b886',GO='#ffd43b',EB='#4dabf7',ED='#1864ab',BD='#4dabf7',BK='#1c7ed6',OR='#ffa94d',BL='#ffa8a8' +const ease=x=>x<0?0:x>1?1:x*x*(3-2*x) +export default(c,t)=>{ + const s=ease(t/.25)-ease((t-.7)/.25),sh=sin(4*T*t+.8)*s**6,N=9 + // all feather outlines first, so a folded bunch doesn't turn into a black mass, then fills with a thin teal seam + const fe=[...Array(N)].map((_,k)=>{const u=(k-(N-1)/2)/((N-1)/2);return[u*(.45+.95*s)+.05*sh*u,84+32*s]}) + const go=(a,f)=>{c.save();c.translate(128,170);c.rotate(a);f();c.restore()} + for(const[a,L]of fe)go(a,()=>{c.lineWidth=14;c.beginPath();c.ellipse(0,-L*.55,15+5*s,L*.55,0,0,T);c.stroke()}) + for(const[a,L]of fe)go(a,()=>{ + c.fillStyle=TL;c.strokeStyle=TD;c.lineWidth=3;c.beginPath();c.ellipse(0,-L*.55,15+5*s,L*.55,0,0,T);c.fill();c.stroke() + c.lineWidth=4;c.beginPath();c.moveTo(0,-8);c.lineTo(0,-L+20);c.stroke();c.strokeStyle='#111' + c.fillStyle=GO;c.lineWidth=5;c.beginPath();c.ellipse(0,-L+16,12,15,0,0,T);c.fill();c.stroke() + c.fillStyle=EB;c.beginPath();c.ellipse(0,-L+17,7,9,0,0,T);c.fill() + c.fillStyle=ED;c.beginPath();c.ellipse(0,-L+18,3.5,5,0,0,T);c.fill() + }) + c.lineWidth=7 + c.fillStyle=OR;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*14,228,12,6,0,0,T),c.fill(),c.stroke() + c.lineWidth=12;c.beginPath();for(const d of[-1,1])c.moveTo(128+d*12,206),c.lineTo(128+d*14,224);c.stroke() + c.strokeStyle=OR;c.lineWidth=5;c.beginPath();for(const d of[-1,1])c.moveTo(128+d*12,206),c.lineTo(128+d*14,224);c.stroke();c.strokeStyle='#111';c.lineWidth=7 + for(const[x,y]of[[116,52],[128,46],[140,52]])c.lineWidth=4,c.beginPath(),c.moveTo(128,78),c.lineTo(x,y),c.stroke(),c.lineWidth=5,c.fillStyle=BD,c.beginPath(),c.arc(x,y,6,0,T),c.fill(),c.stroke() + c.lineWidth=7 + const sil=()=>{c.beginPath();c.ellipse(128,176,34,40,0,0,T);c.moveTo(110,152);c.lineTo(114,104);c.lineTo(142,104);c.lineTo(146,152);c.closePath();c.moveTo(152,98);c.arc(128,98,24,0,T)} + sil();c.lineWidth=14;c.stroke();c.fillStyle=BD;sil();c.fill();c.lineWidth=7 + c.fillStyle=BK;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*26,180,10,26,-d*.2,0,T),c.fill(),c.lineWidth=5,c.stroke() + c.lineWidth=7 + c.fillStyle='#fff';for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*11,94,8,10,0,0,T),c.fill() + c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.arc(128+d*11,95,4.5,0,T),c.fill() + c.fillStyle='#fff';for(const d of[-1,1])c.beginPath(),c.arc(127+d*11,93,1.6,0,T),c.fill() + c.fillStyle=GO;c.lineWidth=4;c.beginPath();c.moveTo(122,104);c.lineTo(134,104);c.lineTo(128,114);c.closePath();c.fill();c.stroke() + c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*17,108,5,3,0,0,T),c.fill() +} diff --git a/gen/color/pinata/swing.js b/gen/color/pinata/swing.js new file mode 100644 index 0000000..8e1c465 --- /dev/null +++ b/gen/color/pinata/swing.js @@ -0,0 +1,56 @@ +// striped donkey piƱata swinging on its rope, bonked by a stick from below, candy popping out and falling +const{sin,cos,PI,min,max}=Math,T=2*PI +export const frames=16,delay=90 +const ST=['#f783ac','#ffd43b','#4dabf7','#69db7c'],PINK='#faa2c1',SN='#ffd8a8',ROPE='#e0a96d',STICK='#a9744f',CANDY=['#ff6b6b','#b197fc','#ffd43b','#38d9a9','#ffa94d','#4dabf7'] +const parts=[ + c=>c.roundRect(-54,0,94,56,16), + c=>{c.moveTo(12,10);c.lineTo(24,-30);c.lineTo(46,-26);c.lineTo(38,16);c.closePath()}, + c=>c.roundRect(22,-54,64,34,14), + c=>c.ellipse(36,-62,6,16,-.25,0,T), + c=>{c.moveTo(56,-62);c.ellipse(50,-62,6,16,.25,0,T)}, + ...[-46,-26,4,24].map(x=>c=>c.roundRect(x,48,14,28,4)) +] +const burst=(c,x,y,s)=>{c.fillStyle='#fff9db';c.lineWidth=5;c.beginPath();for(let k=0;k<16;k++){const r=k%2?s*.45:s,a=k*PI/8;c.lineTo(x+cos(a)*r,y+sin(a)*r)}c.closePath();c.fill();c.stroke()} +export default(c,t)=>{ + const a=.18*cos(T*t),w=(t+.12)%1,hit=t<.14 + c.save();c.translate(132,-40);c.rotate(a) + for(const[lw,st]of[[11,'#111'],[5,ROPE]])c.lineWidth=lw,c.strokeStyle=st,c.beginPath(),c.moveTo(0,0),c.lineTo(0,152),c.stroke() + c.strokeStyle='#111';c.translate(0,150);c.rotate(-a*.4);c.scale(.9,.9) + for(const[x,col]of[[-58,ST[0]],[-62,ST[1]],[-66,ST[2]]])for(const[lw,st]of[[9,'#111'],[4,col]])c.lineWidth=lw,c.strokeStyle=st,c.beginPath(),c.moveTo(-52,12),c.lineTo(x,40+(x+58)*-2),c.stroke() + c.strokeStyle='#111' + c.lineWidth=14;for(const p of parts)c.beginPath(),p(c),c.stroke() + c.fillStyle=PINK;for(const p of parts)c.beginPath(),p(c),c.fill() + c.save();c.beginPath();parts[0](c);parts[1](c);c.clip() + for(let k=0;k<8;k++){c.fillStyle=ST[k%4];c.beginPath();c.moveTo(-60,-34+k*12);for(let x=-60;x<=50;x+=10)c.lineTo(x+5,-34+k*12+6),c.lineTo(x+10,-34+k*12);c.lineTo(50,-34+k*12+14);c.lineTo(-60,-34+k*12+14);c.fill()} + c.restore() + c.save();c.beginPath();parts[2](c);c.clip();c.fillStyle=ST[1];c.beginPath();c.moveTo(22,-54);for(let x=22;x<=60;x+=8)c.lineTo(x+4,-42),c.lineTo(x+8,-54);c.fill() + c.fillStyle=SN;c.beginPath();c.ellipse(80,-36,14,15,0,0,T);c.fill();c.restore() + ;[-46,-26,4,24].forEach((x,k)=>{c.fillStyle=ST[(k+2)%4];c.beginPath();c.roundRect(x,48,14,28,4);c.fill()}) + c.lineWidth=4;for(const p of[parts[0],...parts.slice(5)])c.beginPath(),p(c),c.stroke() + c.fillStyle='#111';c.lineWidth=3.5 + if(hit){c.beginPath();c.moveTo(50,-44);c.lineTo(58,-39);c.lineTo(50,-34);c.stroke()} + else c.beginPath(),c.arc(54,-40,4,0,T),c.fill() + c.beginPath();c.arc(84,-38,2.2,0,T);c.fill() + c.beginPath();if(hit)c.ellipse(72,-26,4,3,0,0,T),c.fill();else c.arc(72,-30,5,.2*PI,.8*PI),c.stroke() + c.restore() + const sw=w<.12?sin(PI/2*w/.12):w<.3?cos(PI/2*(w-.12)/.18):0 + if(sw>0){ + const ph=-.08-1.356*sw + c.save();c.translate(40,300);c.rotate(ph);c.lineWidth=7 + c.fillStyle=STICK;c.beginPath();c.moveTo(0,-7);c.lineTo(150,-11);c.quadraticCurveTo(158,0,150,11);c.lineTo(0,7);c.closePath();c.fill();c.stroke() + c.lineWidth=4;for(const x of[96,116,136])c.beginPath(),c.moveTo(x,-10),c.lineTo(x-6,10),c.stroke() + c.restore() + } + if(t<.16)burst(c,60,152,22*sin(PI*t/.16)+8) + if(t<.62){ + const u=t + for(let k=0;k<6;k++){ + const vx=-30+k*32,x=93+vx*u,y=157-110*u+380*u*u,s=u<.45?1:(.62-u)/.17 + if(y>270||s<=0)continue + c.save();c.translate(x,y);c.rotate(k*1.3+u*9);c.scale(s,s);c.fillStyle=CANDY[k];c.lineWidth=4 + for(const d of[-1,1])c.beginPath(),c.moveTo(d*8,0),c.lineTo(d*17,-7),c.lineTo(d*17,7),c.closePath(),c.fill(),c.stroke() + c.beginPath();c.ellipse(0,0,10,8,0,0,T);c.fill();c.stroke() + c.restore() + } + } +} diff --git a/gen/color/pineapple/groove.js b/gen/color/pineapple/groove.js new file mode 100644 index 0000000..bd9a0dc --- /dev/null +++ b/gen/color/pineapple/groove.js @@ -0,0 +1,29 @@ +// pineapple in sunglasses grooving side to side to the beat, crown leaves swaying behind it and music notes popping up +const{sin,cos,PI,abs}=Math,T=2*PI +export const frames=14,delay=90 +const PA='#ffd43b',PD='#f59f00',LF='#69db7c',LD='#40c057',BL='#ffa8a8',NC=['#9775fa','#4dabf7'] +const note=(c,x,y,s,col)=>{ + c.save();c.translate(x,y);c.scale(s,s);c.rotate(-.2) + for(const[w,st]of[[10,'#111'],[4,col]])c.lineWidth=w,c.strokeStyle=st,c.beginPath(),c.moveTo(8,4),c.lineTo(8,-26),c.quadraticCurveTo(18,-20,20,-10),c.stroke() + c.strokeStyle='#111';c.lineWidth=5;c.fillStyle=col;c.beginPath();c.ellipse(0,4,10,7,-.4,0,T);c.fill();c.stroke();c.restore() +} +export default(c,t)=>{ + const r=.13*sin(T*t),b=-5*abs(sin(2*T*t)) + ;[0,1].forEach(k=>{const p=(t+k*.5)%1;note(c,k?54:202,104-64*p,sin(PI*p),NC[k])}) + c.strokeStyle='#111' + c.save();c.translate(128,234+b);c.rotate(r);c.translate(-128,-234) + ;[[-1.05,52],[1.05,52],[-.6,64],[.6,64],[-.25,76],[.25,76],[0,66]].forEach(([a,L],k)=>{ + c.save();c.translate(128,96);c.rotate(a+.14*sin(T*t-1.1-k*.2));c.fillStyle=k%2?LD:LF + c.beginPath();c.moveTo(-11,6);c.quadraticCurveTo(-9,-L*.6,0,-L);c.quadraticCurveTo(9,-L*.6,11,6);c.closePath();c.fill();c.lineWidth=6;c.stroke();c.restore() + }) + c.lineWidth=7;c.fillStyle=PA;c.beginPath();c.ellipse(128,164,62,70,0,0,T);c.fill() + c.save();c.clip();c.strokeStyle=PD;c.lineWidth=4 + for(let k=-8;k<9;k++)c.beginPath(),c.moveTo(128+k*24-90,90),c.lineTo(128+k*24+90,240),c.stroke(),c.beginPath(),c.moveTo(128+k*24+90,90),c.lineTo(128+k*24-90,240),c.stroke() + c.restore();c.strokeStyle='#111';c.beginPath();c.ellipse(128,164,62,70,0,0,T);c.stroke() + c.fillStyle='#343a40';for(const d of[-1,1])c.beginPath(),c.roundRect(128+d*24-21,128,42,28,[6,6,14,14]),c.fill(),c.stroke() + c.lineWidth=6;c.beginPath();c.moveTo(118,134);c.quadraticCurveTo(128,128,138,134);c.stroke() + c.strokeStyle='#fff';c.lineWidth=4;for(const d of[-1,1])c.beginPath(),c.moveTo(128+d*24-12,148),c.lineTo(128+d*24-2,136),c.stroke() + c.strokeStyle='#111';c.fillStyle='#111';c.beginPath();c.moveTo(112,176);c.quadraticCurveTo(128,196,146,174);c.quadraticCurveTo(128,182,112,176);c.closePath();c.fill();c.lineWidth=4;c.stroke() + c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*42,174,9,5,0,0,T),c.fill() + c.restore() +} diff --git a/gen/color/popsicle/drip.js b/gen/color/popsicle/drip.js new file mode 100644 index 0000000..adff2ab --- /dev/null +++ b/gen/color/popsicle/drip.js @@ -0,0 +1,26 @@ +// two-tone popsicle slowly melting, drips swelling on its bottom edge and falling into a little puddle +// the body and its drips are stroked wide first and filled after, so they read as one outline +const{sin,PI,min}=Math,T=2*PI +export const frames=24,delay=70 +const PK='#f783ac',YE='#ffd43b',STK='#e0a96d',BL='#ffa8a8',SW='#a5d8ff',DX=[98,154] +const body=c=>{c.beginPath();c.moveTo(178,170);c.lineTo(178,64);c.bezierCurveTo(178,20,78,20,78,64);c.lineTo(78,170);for(let k=0;k<5;k++)c.quadraticCurveTo(88+k*20,k%2?166:180,98+k*20,172);c.closePath()} +export default(c,t)=>{ + const dr=DX.map((x,k)=>{const p=(t+k*.5)%1,g=min(1,p/.65);return{x,p,L:p<.65?6+30*g:6,r:p<.65?5+4.5*g:5}}) + const tongue=({x,L,r})=>{c.beginPath();c.moveTo(x-12,164);c.quadraticCurveTo(x-7,172+L*.5,x-r,172+L);c.arc(x,172+L,r,PI,0,true);c.quadraticCurveTo(x+7,172+L*.5,x+12,164);c.closePath()} + c.fillStyle=YE;c.beginPath();c.ellipse(128,230,78,12,0,0,T);c.fill();c.stroke() + c.lineWidth=4;for(const d of dr)if(d.p>.8){const q=(d.p-.8)/.2;c.beginPath();c.ellipse(d.x,230,6+20*q,2+4*q,0,0,T);c.stroke()} + c.lineWidth=7 + c.fillStyle=STK;c.beginPath();c.roundRect(114,150,28,82,[0,0,12,12]);c.fill();c.stroke() + c.lineWidth=14;body(c);c.stroke();for(const d of dr)tongue(d),c.stroke() + c.save();body(c);c.clip();c.fillStyle=YE;c.fillRect(70,20,116,170);c.fillStyle=PK;c.beginPath();c.moveTo(70,20);c.lineTo(186,20);for(let x=186;x>=70;x-=4)c.lineTo(x,104+6*sin(x/11));c.closePath();c.fill() + c.strokeStyle='#fff';c.lineWidth=8;c.beginPath();c.moveTo(94,56);c.lineTo(94,86);c.stroke();c.restore() + c.fillStyle=YE;for(const d of dr)tongue(d),c.fill() + c.lineWidth=7;c.strokeStyle='#111' + c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.arc(128+d*22,128,6,0,T),c.fill() + c.fillStyle='#fff';for(const d of[-1,1])c.beginPath(),c.arc(128+d*22-2,126,2.2,0,T),c.fill() + c.lineWidth=4.5;c.beginPath();c.moveTo(116,144);for(let k=1;k<=4;k++)c.lineTo(116+k*6,k%2?140:144);c.stroke() + c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*38,142,8,4.5,0,0,T),c.fill() + c.fillStyle=SW;c.lineWidth=4;c.beginPath();c.moveTo(164,74);c.quadraticCurveTo(172,90,164,94);c.quadraticCurveTo(156,90,164,74);c.fill();c.stroke() + c.lineWidth=6 + for(const d of dr)if(d.p>=.65&&d.p<.8){const y=200+(d.p-.65)/.15*26;c.fillStyle=YE;c.beginPath();c.moveTo(d.x,y-12);c.quadraticCurveTo(d.x+7,y-2,d.x+6,y+2);c.arc(d.x,y+2,6,0,PI);c.quadraticCurveTo(d.x-7,y-2,d.x,y-12);c.fill();c.stroke()} +} diff --git a/gen/color/rollercoaster/loop.js b/gen/color/rollercoaster/loop.js new file mode 100644 index 0000000..c84129a --- /dev/null +++ b/gen/color/rollercoaster/loop.js @@ -0,0 +1,27 @@ +// coaster car with a cheering rider racing around a loop-the-loop on a track held up by striped supports +const{sin,cos,PI}=Math,T=2*PI +export const frames=24,delay=70 +const RA='#ff6b6b',SU='#4dabf7',CA='#ffd43b',CD='#fab005',SKIN='#ffd8a8',HAIR='#a9744f',GR='#69db7c',BL='#ffa8a8' +const CX=128,CY=106,R=68,Y0=CY+R,X0=-44,X1=300,L1=CX-X0,L2=T*R,L3=X1-CX +// position and heading along the track by distance: in from the left, once round the loop, out to the right +const at=s=>s[CX+R*cos(a),CY+R*sin(a),PI/2-a])(PI/2-(s-L1)/R):[CX+s-L1-L2,Y0,0] +export default(c,t)=>{ + c.fillStyle=GR;c.beginPath();c.roundRect(10,230,236,14,7);c.fill();c.stroke() + for(const[x,y]of[[30,Y0],[CX-46,CY+50],[CX,Y0],[CX+46,CY+50],[226,Y0]]){for(const[w,s]of[[13,'#111'],[5,SU]])c.lineWidth=w,c.strokeStyle=s,c.beginPath(),c.moveTo(x,y),c.lineTo(x,231),c.stroke()} + for(const[w,s]of[[15,'#111'],[7,RA]]){c.lineWidth=w;c.strokeStyle=s;c.beginPath();c.arc(CX,CY,R,0,T);c.stroke();c.beginPath();c.moveTo(4,Y0);c.lineTo(252,Y0);c.stroke()} + c.strokeStyle='#111' + const[x,y,a]=at((t*(L1+L2+L3)+90)%(L1+L2+L3)) + c.save();c.translate(x,y);c.rotate(-a) + for(const[w,s]of[[9,'#111'],[3,'#fff']])c.lineWidth=w,c.strokeStyle=s,c.beginPath(),c.moveTo(-30,-14),c.lineTo(-46,-14),c.moveTo(-30,-24),c.lineTo(-42,-24),c.stroke() + c.strokeStyle='#111' + for(const[w,s]of[[9,'#111'],[4,SKIN]])c.lineWidth=w,c.strokeStyle=s,c.beginPath(),c.moveTo(-10,-34),c.lineTo(-18,-50),c.moveTo(2,-34),c.lineTo(8,-50),c.stroke() + c.strokeStyle='#111';c.lineWidth=5 + c.fillStyle=HAIR;c.beginPath();c.arc(-4,-38,10,0,T);c.fill();c.stroke() + c.fillStyle=SKIN;c.beginPath();c.arc(-3,-36,8,0,PI);c.fill() + c.lineWidth=6;c.fillStyle=CA;c.beginPath();c.roundRect(-24,-30,48,22,7);c.fill();c.stroke() + c.fillStyle=CD;c.beginPath();c.roundRect(-24,-14,48,6,3);c.fill() + c.fillStyle='#111';c.beginPath();c.arc(12,-21,3,0,T);c.fill() + c.lineWidth=3;c.beginPath();c.arc(16,-18,4,.2*PI,.9*PI);c.stroke() + c.fillStyle='#495057';c.lineWidth=4;for(const wx of[-13,13])c.beginPath(),c.arc(wx,-6,5,0,T),c.fill(),c.stroke() + c.restore() +} diff --git a/gen/color/snowglobe/shake.js b/gen/color/snowglobe/shake.js new file mode 100644 index 0000000..bff7c3c --- /dev/null +++ b/gen/color/snowglobe/shake.js @@ -0,0 +1,37 @@ +// snow globe gets a shake, snow swirls around a tiny house and pine inside, then drifts down and settles +// each flake lifts off its resting spot, swirls, and lands back on the same spot, so the loop is seamless +const{sin,cos,PI,abs,min}=Math,T=2*PI +export const frames=24,delay=70 +const GL='#d0ebff',BS='#a9744f',BAND='#ffd43b',RF='#ff6b6b',WL='#ffd8a8',WIN='#ffd43b',PN='#51cf66',BR='#8d6346' +const F=[...Array(14)].map((_,j)=>({gx:62+j*9.6,gy:178-(j%3)*5,r:24+(j*37)%56,q:j*2.4})) +const sm=x=>x<0?0:x>1?1:x*x*(3-2*x) +export default(c,t)=>{ + const u=t/.32,sh=t<.32?.15*sin(PI*u)*sin(3*T*u):0,cx=128,cy=108,R=90 + if(abs(sh)>.05){const d=sh>0?1:-1;for(const r of[104,118])for(const[w,s]of[[10,'#111'],[4,'#fff']])c.lineWidth=w,c.strokeStyle=s,c.beginPath(),c.arc(128,140,r,d>0?-.3:PI-.3,d>0?.3:PI+.3),c.stroke()} + c.strokeStyle='#111';c.save();c.translate(128,240);c.rotate(sh);c.translate(-128,-240) + const glob=()=>{c.beginPath();c.arc(cx,cy,R,0,T)} + glob();c.fillStyle=GL;c.fill() + c.save();glob();c.clip() + c.lineWidth=6;c.fillStyle='#fff';c.beginPath();c.ellipse(128,192,100,32,0,0,T);c.fill();c.stroke() + c.fillStyle=BR;c.lineWidth=5;c.beginPath();c.rect(162,150,10,14);c.fill();c.stroke() + for(const[w,y0,h]of[[28,154,28],[22,134,26],[16,114,24]]){c.fillStyle=PN;c.beginPath();c.moveTo(167-w,y0);c.lineTo(167,y0-h);c.lineTo(167+w,y0);c.closePath();c.fill();c.stroke();c.fillStyle='#fff';c.beginPath();c.moveTo(167-w*.4,y0-h*.6);c.lineTo(167,y0-h);c.lineTo(167+w*.4,y0-h*.6);c.quadraticCurveTo(167,y0-h*.45,167-w*.4,y0-h*.6);c.fill()} + c.fillStyle=WL;c.beginPath();c.rect(80,128,46,36);c.fill();c.stroke() + c.fillStyle=BR;c.beginPath();c.rect(110,96,10,20);c.fill();c.stroke() + c.fillStyle=RF;c.beginPath();c.moveTo(72,132);c.lineTo(103,102);c.lineTo(134,132);c.closePath();c.fill();c.stroke() + c.fillStyle='#fff';c.beginPath();c.moveTo(84,120);c.lineTo(103,102);c.lineTo(122,120);c.quadraticCurveTo(103,112,84,120);c.fill();c.stroke() + c.fillStyle=WIN;c.beginPath();c.rect(86,138,14,12);c.fill();c.stroke();c.fillStyle=BR;c.beginPath();c.rect(106,144,12,20);c.fill();c.stroke() + for(const f of F){ + const sw=k=>[cx+f.r*cos(f.q+k*T*1.6),cy-8+f.r*.85*sin(f.q+k*T*1.6)] + let x,y,rr + if(t<.12){const b=sm(t/.12),[sx,sy]=sw(t);x=f.gx+(sx-f.gx)*b;y=f.gy+(sy-f.gy)*b;rr=min(1,b*4)} + else if(t<.4){[x,y]=sw(t);rr=1} + else{const e=sm((t-.4)/.6),[sx,sy]=sw(.4);x=sx+(f.gx-sx)*e+8*(1-e)*sin(e*T*2+f.q);y=sy+(f.gy-sy)*e;rr=min(1,(1-e)*5)} + if(rr>.05){c.fillStyle='#fff';c.lineWidth=2;c.beginPath();c.arc(x,y,3.8*rr,0,T);c.fill();c.stroke()} + } + c.restore() + c.strokeStyle='#fff';c.lineWidth=8;c.beginPath();c.arc(cx,cy,R-16,PI*1.08,PI*1.38);c.stroke();c.lineWidth=6;c.beginPath();c.arc(cx,cy,R-16,PI*1.45,PI*1.53);c.stroke() + c.strokeStyle='#111';c.lineWidth=7;glob();c.stroke() + c.fillStyle=BS;c.beginPath();c.moveTo(64,184);c.lineTo(192,184);c.lineTo(210,240);c.lineTo(46,240);c.closePath();c.fill();c.stroke() + c.fillStyle=BAND;c.beginPath();c.moveTo(58,202);c.lineTo(198,202);c.lineTo(203,218);c.lineTo(53,218);c.closePath();c.fill();c.lineWidth=5;c.stroke() + c.restore() +} diff --git a/gen/color/soccer/goal.js b/gen/color/soccer/goal.js new file mode 100644 index 0000000..065b5c9 --- /dev/null +++ b/gen/color/soccer/goal.js @@ -0,0 +1,39 @@ +// soccer ball kicked spinning into the goal, the net bulging back around it, then a fresh ball bounces in to start again +const{sin,cos,PI,abs,max,min,exp,hypot}=Math,T=2*PI +export const frames=24,delay=70 +const GR='#69db7c',GRD='#51cf66',NET='#adb5bd' +const ball=(c,x,y,r,a)=>{ + if(r<1)return + c.save();c.translate(x,y);c.rotate(a) + c.fillStyle='#fff';c.beginPath();c.arc(0,0,r,0,T);c.fill() + c.save();c.clip();c.fillStyle='#111' + const pent=(px,py,s)=>{c.beginPath();for(let k=0;k<5;k++){const b=k*T/5-PI/2;c.lineTo(px+cos(b)*s,py+sin(b)*s)}c.closePath();c.fill()} + pent(0,0,r*.34);c.lineWidth=max(2,r*.08);c.strokeStyle='#111' + for(let k=0;k<5;k++){const b=k*T/5-PI/2;pent(cos(b)*r*.98,sin(b)*r*.98,r*.32);c.beginPath();c.moveTo(cos(b)*r*.34,sin(b)*r*.34);c.lineTo(cos(b)*r*.7,sin(b)*r*.7);c.stroke()} + c.restore();c.lineWidth=max(3,r*.2);c.beginPath();c.arc(0,0,r,0,T);c.stroke();c.restore() +} +const lerp=(a,b,p)=>a+(b-a)*p +export default(c,t)=>{ + // 0-.4 flight, .4-.7 net bulge and drop, .7-.82 ball pops away, .82-1 a fresh ball bounces in at the kick spot + const S=[50,200,30],H=[178,124,17] + let x,y,r,a=0,m=0,pop=0 + if(t<.4){const p=t/.4;x=lerp(S[0],H[0],p);y=lerp(S[1],H[1],p)-46*sin(PI*p);r=lerp(S[2],H[2],p);a=-p*T*1.5} + else if(t<.7){const p=(t-.4)/.3;m=sin(PI*min(1,p*1.6))*(1-p*.3);x=H[0];y=lerp(H[1],178,p*p);r=H[2];a=-T*1.5-p*2} + else if(t<.82){const p=(t-.7)/.12;x=H[0];y=178;r=H[2]*(1-p);a=0;pop=p} + else{const p=(t-.82)/.18;x=S[0];y=S[1]-24*abs(sin(PI*p))*(1-p);r=S[2]*min(1,p*1.6)*(1+.15*sin(PI*min(1,p*1.6)));a=0} + c.lineWidth=7;c.fillStyle=GR;c.beginPath();c.roundRect(8,192,240,52,14);c.fill();c.stroke() + c.save();c.beginPath();c.roundRect(8,192,240,52,14);c.clip();c.fillStyle=GRD;for(const sx of[40,120,200])c.fillRect(sx,192,40,52);c.restore() + c.beginPath();c.roundRect(8,192,240,52,14);c.stroke() + // net: grid on the back panel, each point pulled toward the impact spot while the ball pushes in + const P=(px,py)=>{const d=hypot(px-H[0],py-H[1]),k=m*.45*exp(-d*d/1800);return[px+(H[0]-px)*k,py+(H[1]-py)*k]} + c.strokeStyle=NET;c.lineWidth=3 + for(let gx=100;gx<=228;gx+=16){c.beginPath();for(let gy=84;gy<=194;gy+=4)c.lineTo(...P(gx,gy));c.stroke()} + for(let gy=84;gy<=194;gy+=14){c.beginPath();for(let gx=100;gx<=228;gx+=4)c.lineTo(...P(gx,gy));c.stroke()} + c.beginPath();for(const[ax,ay,bx,by]of[[84,70,100,84],[244,70,228,84]])c.moveTo(ax,ay),c.lineTo(bx,by);c.stroke() + c.strokeStyle='#111' + if(t>=.4&&t<.82)ball(c,x,y,r,a) + if(pop>0){const s=8+18*pop;for(let k=0;k<6;k++){const b=k*T/6;for(const[w,st]of[[9,'#111'],[4,'#ffd43b']])c.lineWidth=w,c.strokeStyle=st,c.beginPath(),c.moveTo(x+cos(b)*s,y+sin(b)*s),c.lineTo(x+cos(b)*(s+10*(1-pop)+2),y+sin(b)*(s+10*(1-pop)+2)),c.stroke()}c.strokeStyle='#111'} + for(const[w,st]of[[16,'#111'],[8,'#fff']])c.lineWidth=w,c.strokeStyle=st,c.beginPath(),c.moveTo(84,196),c.lineTo(84,70),c.lineTo(244,70),c.lineTo(244,196),c.stroke() + c.strokeStyle='#111' + if(t<.4||t>=.82){c.fillStyle='#2f9e44';c.beginPath();c.ellipse(x,t<.4?lerp(226,196,t/.4):226,r*.8,r*.22,0,0,T);c.fill();ball(c,x,y,r,a)} +} diff --git a/gen/color/strawberry/bounce.js b/gen/color/strawberry/bounce.js new file mode 100644 index 0000000..c95e183 --- /dev/null +++ b/gen/color/strawberry/bounce.js @@ -0,0 +1,24 @@ +// strawberry bouncing twice a loop with squash and stretch, its seeds jiggling and its leafy cap flapping behind +const{sin,PI,abs,max,min}=Math,T=2*PI +export const frames=20,delay=60 +const RD='#ff6b6b',SD='#ffe066',LF='#69db7c',LD='#40c057',BL='#ffc9c9',SH='#ced4da' +export default(c,t)=>{ + const u=(t*2)%1,H=54,h=H*4*u*(1-u),q=max(0,1-min(u,1-u)/.1),s=.1*abs(1-2*u)*(1-q) + const sx=1+.18*q-.5*s,sy=1-.22*q+s,lag=.22*sin(2*T*t*2-1.2) + c.fillStyle=SH;c.beginPath();c.ellipse(128,232,48*(1-.4*h/H)*(1+.2*q),7,0,0,T);c.fill() + c.save();c.translate(128,228-h);c.scale(sx*.88,sy*.88);c.translate(0,-76) + c.fillStyle=RD;c.beginPath();c.moveTo(0,-44);c.bezierCurveTo(46,-62,80,-28,70,8);c.bezierCurveTo(60,44,22,72,0,76);c.bezierCurveTo(-22,72,-60,44,-70,8);c.bezierCurveTo(-80,-28,-46,-62,0,-44);c.closePath();c.fill();c.stroke() + c.lineWidth=2.5;c.fillStyle=SD + ;[[-46,-20],[-20,-30],[22,-30],[48,-18],[-56,6],[56,6],[-40,34],[40,34],[-18,52],[18,52],[0,64],[-26,-2],[28,0]].forEach(([x,y],k)=>{const j=1.6*sin(2*T*t*2+k*1.7);c.beginPath();c.ellipse(x+j,y,3,4.5,x/90,0,T);c.fill();c.stroke()}) + c.lineWidth=7 + for(const[a,L]of[[-1.25,38],[1.25,38],[-.65,42],[.65,42],[0,40]]){ + c.save();c.translate(0,-48);c.rotate(PI+a+lag*(a?Math.sign(a):1)*.6);c.fillStyle=a?LF:LD;c.beginPath();c.moveTo(-10,0);c.quadraticCurveTo(-11,L*.6,0,L);c.quadraticCurveTo(11,L*.6,10,0);c.closePath();c.fill();c.stroke();c.restore() + } + for(const[w,st]of[[13,'#111'],[6,LD]])c.lineWidth=w,c.strokeStyle=st,c.beginPath(),c.moveTo(0,-52),c.quadraticCurveTo(2,-66,10+lag*12,-74),c.stroke() + c.lineWidth=7;c.strokeStyle='#111' + c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.arc(d*22,6,6.5,0,T),c.fill() + c.fillStyle='#fff';for(const d of[-1,1])c.beginPath(),c.arc(d*22-2,4,2.4,0,T),c.fill() + c.lineWidth=5;c.beginPath();c.arc(0,14,9,.15*PI,.85*PI);c.stroke() + c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(d*40,20,9,5,0,0,T),c.fill() + c.restore() +} diff --git a/gen/color/submarine/dive.js b/gen/color/submarine/dive.js new file mode 100644 index 0000000..1020da3 --- /dev/null +++ b/gen/color/submarine/dive.js @@ -0,0 +1,33 @@ +// yellow submarine bobbing underwater, periscope turning, propeller spinning, bubbles rising past swaying seaweed +const{sin,cos,PI,abs,min,max,round}=Math,T=2*PI +export const frames=20,delay=80 +const Y='#ffd43b',YD='#fab005',GL='#a5d8ff',PR='#ced4da',SW='#51cf66',SA='#ffd8a8',BU='#d0ebff',BL='#ffa8a8' +export default(c,t)=>{ + const b=round(5*sin(t*T)),th=t*T*4,pa=t*T,w=20*cos(pa) + for(const[x,h,o]of[[34,74,0],[222,58,1.7]]){const s=8*sin(t*T+o);for(const[lw,st]of[[16,'#111'],[8,SW]])c.lineWidth=lw,c.strokeStyle=st,c.beginPath(),c.moveTo(x,238),c.quadraticCurveTo(x-12,238-h*.5,x+s,238-h),c.stroke()} + c.strokeStyle='#111';c.lineWidth=7 + c.fillStyle=SA;c.beginPath();c.moveTo(8,246);c.quadraticCurveTo(128,206,248,246);c.closePath();c.fill();c.stroke() + c.fillStyle='#fff';c.lineWidth=4 + for(let k=0;k<3;k++){const p=(t+k/3)%1,s=3+5*p;c.fillStyle=BU;c.beginPath();c.arc(36+8*sin(p*T*2+k),146+b-p*118,s,0,T);c.fill();c.stroke()} + c.save();c.translate(0,b) + c.lineWidth=7 + c.fillStyle=YD;c.beginPath();c.moveTo(56,124);c.lineTo(40,100);c.lineTo(66,104);c.lineTo(84,122);c.closePath();c.fill();c.stroke() + for(const[lw,st]of[[12,'#111'],[5,PR]])c.lineWidth=lw,c.strokeStyle=st,c.beginPath(),c.moveTo(144,100),c.lineTo(144,60),c.stroke() + c.strokeStyle='#111';c.lineWidth=5 + c.fillStyle=YD;c.beginPath();c.roundRect(144+min(0,w)-7,50,abs(w)+14,15,7);c.fill();c.stroke() + c.fillStyle=GL + if(abs(w)>5)c.beginPath(),c.ellipse(144+w,57.5,3,6,0,0,T),c.fill(),c.stroke() + else if(sin(pa)>0)c.beginPath(),c.arc(144,57.5,5,0,T),c.fill(),c.stroke() + c.lineWidth=7;c.fillStyle=YD;c.beginPath();c.roundRect(100,96,56,36,10);c.fill();c.stroke() + c.fillStyle=PR;c.lineWidth=5;c.beginPath();c.roundRect(20,140,22,22,6);c.fill();c.stroke() + for(const L of[22*cos(th),-22*cos(th),22*sin(th),-22*sin(th)])if(abs(L)>2)c.beginPath(),c.ellipse(18,151+L/2,5,abs(L/2),0,0,T),c.fill(),c.stroke() + c.lineWidth=7;c.fillStyle=Y;c.beginPath();c.roundRect(36,118,188,66,33);c.fill();c.stroke() + c.save();c.beginPath();c.roundRect(36,118,188,66,33);c.clip();c.fillStyle=YD;c.fillRect(30,168,200,20);c.restore() + c.beginPath();c.roundRect(36,118,188,66,33);c.stroke() + c.lineWidth=5;for(const x of[78,112,146]){c.fillStyle=GL;c.beginPath();c.arc(x,148,11,0,T);c.fill();c.stroke();c.fillStyle='#fff';c.beginPath();c.arc(x-4,144,3,0,T);c.fill()} + c.fillStyle='#111';c.beginPath();c.ellipse(194,142,5,7,0,0,T);c.fill() + c.fillStyle='#fff';c.beginPath();c.arc(192,139,2.5,0,T);c.fill() + c.fillStyle=BL;c.beginPath();c.ellipse(182,160,8,5,0,0,T);c.fill() + c.lineWidth=4;c.beginPath();c.arc(202,154,7,.2*PI,.8*PI);c.stroke() + c.restore() +} diff --git a/gen/color/sunflower/turn.js b/gen/color/sunflower/turn.js new file mode 100644 index 0000000..a304f91 --- /dev/null +++ b/gen/color/sunflower/turn.js @@ -0,0 +1,30 @@ +// potted sunflower turning its smiley face to follow a little sun swinging across the sky, petals swaying +const{sin,cos,PI,abs}=Math,T=2*PI +export const frames=16,delay=100 +const PT='#ff922b',PD='#e8590c',ST='#51cf66',SD='#2f9e44',PE=['#fcc419','#ffd43b'],CE='#a9744f',CD='#7f5539',BL='#ff8787' +const leaf=(c,x,y,a,L)=>{c.save();c.translate(x,y);c.rotate(a);c.fillStyle=ST;c.lineWidth=6;c.beginPath();c.moveTo(0,0);c.quadraticCurveTo(L*.5,-L*.38,L,0);c.quadraticCurveTo(L*.5,L*.38,0,0);c.fill();c.stroke();c.strokeStyle=SD;c.lineWidth=3;c.beginPath();c.moveTo(4,0);c.lineTo(L*.8,0);c.stroke();c.restore();c.strokeStyle='#111'} +export default(c,t)=>{ + const s=sin(t*T),sx=128+94*s,sy=32+14*s*s,hx=128+12*s,hy=104,sq=1-.16*abs(s),w=.08*sin(2*t*T) + c.save();c.translate(sx,sy);c.rotate(t*PI/4) + for(let k=0;k<8;k++){const q=k*PI/4;for(const[lw,col]of[[10,'#111'],[4,'#ffd43b']])c.lineWidth=lw,c.strokeStyle=col,c.beginPath(),c.moveTo(cos(q)*22,sin(q)*22),c.lineTo(cos(q)*28,sin(q)*28),c.stroke()} + c.restore();c.strokeStyle='#111';c.lineWidth=6;c.fillStyle='#ffd43b';c.beginPath();c.arc(sx,sy,16,0,T);c.fill();c.stroke() + c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.arc(sx+d*5,sy-1,2.2,0,T),c.fill() + c.lineWidth=2.5;c.beginPath();c.arc(sx,sy+3,4,.15*PI,.85*PI);c.stroke() + for(const[lw,col]of[[20,'#111'],[11,ST]])c.lineWidth=lw,c.strokeStyle=col,c.beginPath(),c.moveTo(128,200),c.quadraticCurveTo(128-6*s,160,hx,hy+20),c.stroke() + c.strokeStyle='#111' + leaf(c,127,176,-.5+w,44);leaf(c,127,160,PI+.45-w,40) + c.lineWidth=7;c.fillStyle=PT;c.beginPath();c.moveTo(92,200);c.lineTo(164,200);c.lineTo(154,244);c.lineTo(102,244);c.closePath();c.fill();c.stroke() + c.fillStyle=PD;c.beginPath();c.roundRect(84,190,88,20,8);c.fill();c.stroke() + c.save();c.translate(hx,hy);c.rotate(.2*s) + c.lineWidth=5 + for(const[r,o,col]of[[44,PI/14,PE[0]],[40,0,PE[1]]])for(let k=0;k<14;k++){const q=k*T/14+o+.05*sin(2*t*T+k);c.fillStyle=col;c.beginPath();c.ellipse(cos(q)*r*sq,sin(q)*r,15,6.5,q,0,T);c.fill();c.stroke()} + const fx=9*s + c.lineWidth=7;c.fillStyle=CE;c.beginPath();c.ellipse(0,0,31*sq,31,0,0,T);c.fill();c.stroke() + c.save();c.clip();c.fillStyle=CD;for(let y=-28;y<30;y+=9)for(let x=-28;x<30;x+=9)c.beginPath(),c.arc(x+(y/9&1)*4.5,y,1.8,0,T),c.fill();c.restore() + c.fillStyle='#fff3bf';c.beginPath();c.ellipse(fx,6,20*sq,15,0,0,T);c.fill() + c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.arc(fx+d*9*sq,1,4,0,T),c.fill() + c.fillStyle='#fff';for(const d of[-1,1])c.beginPath(),c.arc(fx+d*9*sq-1,-.5,1.5,0,T),c.fill() + c.lineWidth=3.5;c.beginPath();c.arc(fx,7,5,.15*PI,.85*PI);c.stroke() + c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(fx+d*15*sq,9,4,2.5,0,0,T),c.fill() + c.restore() +} diff --git a/gen/color/sunset/sink.js b/gen/color/sunset/sink.js new file mode 100644 index 0000000..353fa46 --- /dev/null +++ b/gen/color/sunset/sink.js @@ -0,0 +1,28 @@ +// sun sinks into the sea inside a round sticker while the sky warms to dusk, sparkles on the water, then it rises again +const{sin,cos,PI,max}=Math,T=2*PI +export const frames=16,delay=90 +const DAY=['#ffa94d','#ffc078','#ffd8a8','#fff3bf'],DUSK=['#7048e8','#9775fa','#f783ac','#ffa94d'],E=[0,60,98,130,156],BL='#ff8787' +const mix=(a,b,k)=>'#'+[1,3,5].map(i=>Math.round(parseInt(a.slice(i,i+2),16)*(1-k)+parseInt(b.slice(i,i+2),16)*k).toString(16).padStart(2,'0')).join('') +const spk=(c,x,y,s)=>{c.lineWidth=3;c.fillStyle='#fff';c.beginPath();c.moveTo(x,y-s);for(const[px,py,qx,qy]of[[s,0,s*.2,-s*.2],[0,s,s*.2,s*.2],[-s,0,-s*.2,s*.2],[0,-s,-s*.2,-s*.2]])c.quadraticCurveTo(x+qx,y+qy,x+px,y+py);c.fill();c.stroke()} +export default(c,t)=>{ + const k=(1-cos(t*T))/2,y=100+100*k,H=156,disc=()=>{c.beginPath();c.arc(128,128,112,0,T)} + c.save();disc();c.clip() + DAY.forEach((d,j)=>{c.fillStyle=mix(d,DUSK[j],k);c.fillRect(0,E[j],256,E[j+1]-E[j])}) + c.fillStyle=mix('#fff','#fcc2d7',k);c.lineWidth=5 + for(const[x,cy,w]of[[70,62,30],[186,86,24]])c.beginPath(),c.ellipse(x,cy,w,7,0,0,T),c.fill(),c.stroke() + c.lineWidth=7;c.fillStyle=mix('#ffd43b','#ff922b',k);c.beginPath();c.arc(128,y,36,0,T);c.fill();c.stroke() + c.fillStyle='#111' + if(k<.5)for(const d of[-1,1])c.beginPath(),c.arc(128+d*13,y-4,5,0,T),c.fill() + else{c.lineWidth=4;for(const d of[-1,1])c.beginPath(),c.arc(128+d*13,y-7,6,.15*PI,.85*PI),c.stroke()} + c.lineWidth=4;c.beginPath();c.arc(128,y+6,8,.15*PI,.85*PI);c.stroke() + c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(128+d*24,y+8,6,3.5,0,0,T),c.fill() + c.fillStyle='#4dabf7';c.fillRect(0,H,256,100);c.fillStyle='#339af0';c.fillRect(0,H+46,256,60) + c.lineWidth=7;c.beginPath();c.moveTo(0,H);c.lineTo(256,H);c.stroke() + const L=(1-k)*42+12,sun=mix('#ffe066','#ff922b',k) + for(const[j,dy]of[[0,14],[1,28],[2,42]]){const w=L*(1-j*.25),x=128+4*sin(2*t*T+j*2);for(const[lw,s]of[[11,'#111'],[5,sun]])c.lineWidth=lw,c.strokeStyle=s,c.beginPath(),c.moveTo(x-w,H+dy),c.lineTo(x+w,H+dy),c.stroke()} + c.strokeStyle='#d0ebff';c.lineWidth=4 + for(const[x,cy]of[[42,184],[196,180],[70,218],[170,224]])for(const dx of[0,14])c.beginPath(),c.arc(x+dx,cy,7,PI*1.1,PI*1.9),c.stroke() + c.strokeStyle='#111' + for(const[x,cy,o]of[[60,200,0],[198,206,.5]])spk(c,x,cy,3+5*max(0,sin((t+o)*2*T))) + c.restore();c.lineWidth=7;disc();c.stroke() +} diff --git a/gen/color/sushi/hop.js b/gen/color/sushi/hop.js new file mode 100644 index 0000000..46df9e7 --- /dev/null +++ b/gen/color/sushi/hop.js @@ -0,0 +1,30 @@ +// salmon nigiri hopping on its wooden board, squishing on landing while a little wasabi buddy watches it go up and down +const{sin,PI,abs}=Math,T=2*PI +export const frames=20,delay=60 +const SA='#ffa94d',SS='#fff4e6',WA='#8ce99a',BD='#d4a373',BDD='#a9744f',BL='#ffa8a8' +const salmon=c=>{c.beginPath();c.moveTo(-76,-50);c.bezierCurveTo(-72,-98,58,-106,82,-68);c.quadraticCurveTo(88,-50,72,-46);c.bezierCurveTo(32,-56,-34,-40,-66,-38);c.quadraticCurveTo(-80,-38,-76,-50);c.closePath()} +export default(c,t)=>{ + const air=t<.7,h=air?72*sin(PI*t/.7):0,q=air?0:sin(PI*(t-.7)/.3),st=air?.07*abs(sin(2*PI*t/.7)):0 + const sx=1+.16*q-st,sy=1-.2*q+st + c.fillStyle=BD;c.beginPath();c.roundRect(16,204,224,24,10);c.fill();c.stroke() + c.fillStyle=BDD;c.beginPath();c.ellipse(112,216,62*sx*(1-h/72*.45),5,0,0,T);c.fill() + c.lineWidth=4;c.strokeStyle=BDD;for(const[x,w]of[[34,26],[176,18]])c.beginPath(),c.moveTo(x,220),c.lineTo(x+w,220),c.stroke() + c.strokeStyle='#111';c.lineWidth=5 + c.fillStyle=WA;c.beginPath();c.moveTo(190,207);c.bezierCurveTo(188,184,200,178,210,170);c.quadraticCurveTo(206,162,214,154);c.quadraticCurveTo(220,166,222,174);c.bezierCurveTo(234,184,236,200,234,207);c.closePath();c.fill();c.stroke() + const ly=-2.5*h/72 + for(const x of[205,220]){c.fillStyle='#111';c.beginPath();c.arc(x-1.5,192+ly,3.6,0,T);c.fill();c.fillStyle='#fff';c.beginPath();c.arc(x-2.5,190.5+ly,1.3,0,T);c.fill()} + c.lineWidth=3.5;c.beginPath();c.arc(212,198,4,.15*PI,.85*PI);c.stroke() + c.lineWidth=7 + c.save();c.translate(112,210-h);c.scale(sx,sy) + c.fillStyle='#fff';c.beginPath();c.roundRect(-62,-66,124,66,[26,26,14,14]);c.fill();c.stroke() + c.lineWidth=3;for(const[x,y,a]of[[-46,-14,0],[46,-12,1],[-30,-6,1.5],[30,-5,2.5]])c.beginPath(),c.arc(x,y,5,a,a+1.6),c.stroke() + c.lineWidth=7 + c.fillStyle=SA;salmon(c);c.fill() + c.save();salmon(c);c.clip();c.strokeStyle=SS;c.lineWidth=7;for(let k=0;k<5;k++)c.beginPath(),c.moveTo(-70+k*36,-36),c.quadraticCurveTo(-58+k*36,-70,-42+k*36,-104),c.stroke();c.restore() + salmon(c);c.stroke() + c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.arc(d*20,-30,6,0,T),c.fill() + c.fillStyle='#fff';for(const d of[-1,1])c.beginPath(),c.arc(d*20-2,-32,2.2,0,T),c.fill() + c.lineWidth=5;c.beginPath();c.arc(0,-24,8,.15*PI,.85*PI);c.stroke() + c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(d*38,-18,8,4.5,0,0,T),c.fill() + c.restore() +} diff --git a/gen/color/toucan/bob.js b/gen/color/toucan/bob.js new file mode 100644 index 0000000..e292bd2 --- /dev/null +++ b/gen/color/toucan/bob.js @@ -0,0 +1,39 @@ +// toucan perched on a branch, bobbing its huge colorful beak up and down and blinking +// the black body gets a white rim so it doesn't vanish on dark backgrounds +const{sin,cos,PI}=Math,T=2*PI +export const frames=16,delay=80 +const BR='#a9744f',LF='#69db7c',BIB='#fff9db',OR='#ff922b',YE='#ffd43b',RD='#ff6b6b',TP='#e8590c',EB='#4dabf7',SK='#ffa94d' +export default(c,t,i)=>{ + const a=.1*sin(2*T*t),b=Math.round(2*sin(2*T*t)),shut=i==6||i==7 + const D=(wd,cl,f)=>{for(const[l,s]of[[wd,'#111'],[wd-10,cl]])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),f(),c.stroke();c.lineWidth=7;c.strokeStyle='#111'} + const dark=f=>{c.beginPath();f();c.lineWidth=16;c.strokeStyle='#fff';c.stroke();c.fillStyle='#111';c.fill();c.lineWidth=7;c.strokeStyle='#111';c.stroke()} + dark(()=>{c.moveTo(70,184);c.lineTo(52,240);c.lineTo(82,240);c.lineTo(94,190);c.closePath()}) + D(20,BR,()=>{c.moveTo(16,212);c.quadraticCurveTo(128,204,240,214)}) + c.fillStyle=LF;c.beginPath();c.ellipse(222,228,16,8,.5,0,T);c.fill();c.stroke() + c.save();c.translate(0,b) + // body and head rimmed together so there is no white seam between them + const bd=()=>{c.beginPath();c.ellipse(92,150,44,60,-.15,0,T)},hd=()=>{c.save();c.translate(104,120);c.rotate(a);c.beginPath();c.arc(0,-30,34,0,T);c.restore()} + c.lineWidth=16;c.strokeStyle='#fff';bd();c.stroke();hd();c.stroke();c.fillStyle='#111';bd();c.fill();hd();c.fill();c.lineWidth=7;c.strokeStyle='#111' + c.fillStyle=RD;c.beginPath();c.ellipse(96,198,18,10,-.2,0,T);c.fill();c.lineWidth=5;c.stroke();c.lineWidth=7 + c.save();c.translate(104,120);c.rotate(a) + c.fillStyle=BIB;c.beginPath();c.ellipse(8,4,26,30,-.1,0,T);c.fill();c.stroke() + c.fillStyle=YE;c.beginPath();c.ellipse(10,24,18,8,0,0,T);c.fill() + c.fillStyle=SK;c.beginPath();c.ellipse(12,-36,17,15,0,0,T);c.fill();c.lineWidth=5;c.stroke() + c.fillStyle=EB;c.beginPath();c.arc(14,-36,10,0,T);c.fill();c.stroke() + if(shut){c.beginPath();c.arc(14,-38,7,.15*PI,.85*PI);c.stroke()} + else{c.fillStyle='#111';c.beginPath();c.arc(15,-36,5.5,0,T);c.fill();c.fillStyle='#fff';c.beginPath();c.arc(13,-38,2,0,T);c.fill()} + c.lineWidth=7 + const beak=()=>{c.beginPath();c.moveTo(26,-44);c.bezierCurveTo(80,-66,128,-48,138,-14);c.quadraticCurveTo(136,-2,124,-6);c.quadraticCurveTo(80,-4,28,-8);c.closePath()} + c.fillStyle=OR;beak();c.fill() + c.save();beak();c.clip() + c.fillStyle=YE;c.beginPath();c.moveTo(20,-80);c.quadraticCurveTo(80,-60,140,-40);c.lineTo(140,-80);c.closePath();c.fill() + c.fillStyle=TP;c.beginPath();c.arc(136,-14,22,0,T);c.fill() + c.fillStyle=RD;c.fillRect(20,-60,10,60) + c.restore() + c.lineWidth=5;c.beginPath();c.moveTo(30,-24);c.quadraticCurveTo(80,-26,132,-14);c.stroke();c.lineWidth=7 + beak();c.stroke() + c.restore() + c.restore() + c.fillStyle=EB;for(const x of[84,104])c.lineWidth=5,c.beginPath(),c.ellipse(x,210,8,6,0,0,T),c.fill(),c.stroke() + c.lineWidth=7 +} diff --git a/gen/color/traffic/cycle.js b/gen/color/traffic/cycle.js new file mode 100644 index 0000000..719f553 --- /dev/null +++ b/gen/color/traffic/cycle.js @@ -0,0 +1,33 @@ +// traffic light cycling green, yellow, red, the lit lamp has a face that grins, panics, then glares +const{sin,cos,PI}=Math,T=2*PI +export const frames=24,delay=90 +const HOUSE='#495057',HD='#343a40',POLE='#adb5bd',L=[['#ff6b6b','#6b3434'],['#ffd43b','#6b5f2c'],['#51cf66','#2c5a37']],DROP='#a5d8ff' +export default(c,t,i)=>{ + const k=t<.42?2:t<.58?1:0 + c.fillStyle=POLE;c.beginPath();c.roundRect(114,200,28,52,6);c.fill();c.stroke() + const h=()=>{c.beginPath();c.roundRect(70,8,116,206,30)} + c.lineWidth=18;c.strokeStyle='#fff';h();c.stroke();c.fillStyle=HOUSE;h();c.fill();c.lineWidth=7;c.strokeStyle='#111';c.stroke() + for(let j=0;j<3;j++){ + const y=46+j*65,on=j==k,[lc,dc]=L[j] + c.fillStyle=HD;c.beginPath();c.arc(128,y,31,0,T);c.fill() + if(on)for(const a of[-.5,0,.5,PI-.5,PI,PI+.5]){for(const[w,st]of[[9,'#111'],[3.5,lc]])c.lineWidth=w,c.strokeStyle=st,c.beginPath(),c.moveTo(128+cos(a)*34,y+sin(a)*34),c.lineTo(128+cos(a)*42,y+sin(a)*42),c.stroke()} + c.strokeStyle='#111';c.fillStyle=on?lc:dc;c.lineWidth=5;c.beginPath();c.arc(128,y,25,0,T);c.fill();c.stroke() + c.fillStyle=on?'#fff':'#868e96';c.beginPath();c.ellipse(118,y-12,6,3.5,-.6,0,T);c.fill() + if(!on)continue + c.fillStyle='#111';c.lineWidth=3.5 + if(j==2){ + for(const d of[-1,1])c.beginPath(),c.arc(128+d*9,y-2,5,PI*1.1,PI*1.9),c.stroke() + c.beginPath();c.moveTo(118,y+5);c.quadraticCurveTo(128,y+20,138,y+5);c.closePath();c.fill() + c.fillStyle='#ff8787';c.beginPath();c.ellipse(128,y+12,4,2.5,0,0,T);c.fill() + }else if(j==1){ + c.fillStyle='#fff';for(const d of[-1,1])c.beginPath(),c.arc(128+d*9,y-3,6,0,T),c.fill(),c.stroke() + c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.arc(128+d*9,y-3+(i%2),2.5,0,T),c.fill() + c.beginPath();c.ellipse(128,y+11,3.5,4.5,0,0,T);c.fill() + c.fillStyle=DROP;c.lineWidth=3;const dy=(i%3)*2;c.beginPath();c.moveTo(150,y-16+dy);c.quadraticCurveTo(157,y-6+dy,150,y-4+dy);c.quadraticCurveTo(143,y-6+dy,150,y-16+dy);c.fill();c.stroke() + }else{ + c.lineWidth=3.5;for(const d of[-1,1])c.beginPath(),c.moveTo(128+d*15,y-14),c.lineTo(128+d*5,y-9),c.stroke() + for(const d of[-1,1])c.beginPath(),c.arc(128+d*9,y-3,3.5,0,T),c.fill() + c.beginPath();c.arc(128,y+14,7,PI*1.15,PI*1.85);c.stroke() + } + } +} diff --git a/gen/color/unicorn/prance.js b/gen/color/unicorn/prance.js new file mode 100644 index 0000000..f43af36 --- /dev/null +++ b/gen/color/unicorn/prance.js @@ -0,0 +1,38 @@ +// unicorn prancing in place, front hooves lifting in turn, rainbow mane and tail flowing, a sparkle off the horn +const{sin,cos,PI,abs,max}=Math,T=2*PI +export const frames=16,delay=80 +const RB=['#ff6b6b','#ffa94d','#ffd43b','#69db7c','#4dabf7','#b197fc'],HN='#ffd43b',HO='#b197fc',FAR='#e9ecef',PK='#faa2c1' +export default(c,t)=>{ + const b=-5*abs(sin(T*t)),l1=max(0,sin(T*t)),l2=max(0,-sin(T*t)),w=sin(T*t) + const D=(wd,col,f)=>{for(const[l,s]of[[wd,'#111'],[wd-12,col]])c.lineWidth=l,c.strokeStyle=s,c.beginPath(),f(),c.stroke();c.lineWidth=7;c.strokeStyle='#111'} + const leg=(x,y,l,col)=>{ + const kx=x+16*l,ky=y+30-6*l,fx=x+4*l,fy=226-30*l + D(26,col,()=>{c.moveTo(x,y);c.lineTo(kx,ky);c.lineTo(fx,fy)}) + c.fillStyle=HO;c.beginPath();c.roundRect(fx-12,fy-4,24,14,5);c.fill();c.stroke() + } + // rainbow bundle: every strand stroked black and wide first, then the colors on top, so it reads as one outlined ribbon + const bundle=(f)=>{ + for(let k=0;k<6;k++)c.lineWidth=20,c.beginPath(),f(k),c.stroke() + for(let k=0;k<6;k++)c.lineWidth=8,c.strokeStyle=RB[k],c.beginPath(),f(k),c.stroke() + c.lineWidth=7;c.strokeStyle='#111' + } + c.translate(0,b) + leg(98,160,0,FAR);leg(164,160,l2,FAR) + bundle(k=>{const o=k*6-15;c.moveTo(64,138+o*.4);c.bezierCurveTo(30+o,140+o*.3,26+8*w+o*.6,186,46+12*w-o*.3,214+o*.2)}) + leg(80,164,0,'#fff');leg(150,164,l1,'#fff') + c.fillStyle='#fff';c.beginPath();c.moveTo(170,68);c.lineTo(176,40);c.lineTo(190,62);c.closePath();c.fill();c.stroke() + const sil=()=>{c.beginPath();c.ellipse(118,146,62,36,0,0,T);c.moveTo(144,152);c.lineTo(160,96);c.lineTo(196,92);c.lineTo(182,150);c.closePath();c.moveTo(232,96);c.ellipse(198,86,34,24,.45,0,T)} + sil();c.lineWidth=14;c.stroke();c.fillStyle='#fff';sil();c.fill();c.lineWidth=7 + c.fillStyle=PK;c.beginPath();c.ellipse(222,104,4,3,.4,0,T);c.fill() + c.fillStyle=PK;c.beginPath();c.ellipse(196,100,9,5,.3,0,T);c.fill() + c.lineWidth=5;c.beginPath();c.arc(188,82,8,.15*PI,.85*PI);c.stroke() + c.beginPath();c.moveTo(181,86);c.lineTo(176,90);c.moveTo(184,89);c.lineTo(181,94);c.stroke() + c.beginPath();c.moveTo(212,110);c.quadraticCurveTo(218,114,224,110);c.stroke();c.lineWidth=7 + bundle(k=>{const o=k*5-12;c.moveTo(184-o*.4,60+o*.6);c.bezierCurveTo(160-o*.8,64+o*.4,150+5*w-o*.9,96,140+7*w-o,136+o*.3)}) + c.save();c.translate(200,66);c.rotate(.5) + c.fillStyle=HN;c.beginPath();c.moveTo(-11,0);c.lineTo(0,-54);c.lineTo(11,0);c.closePath();c.fill();c.stroke() + c.lineWidth=4;for(const y of[-14,-28,-40])c.beginPath(),c.moveTo(-9-y*.2,y+4),c.lineTo(9+y*.2,y-4),c.stroke() + c.restore();c.lineWidth=7 + const s=abs(sin(2*T*t+.6)),sx=236,sy=24 + if(s>.25){c.fillStyle=HN;c.lineWidth=4;c.beginPath();for(let k=0;k<8;k++){const r=k%2?6*s:20*s,a=k*PI/4-PI/2;c.lineTo(sx+cos(a)*r,sy+sin(a)*r)}c.closePath();c.fill();c.stroke()} +} diff --git a/gen/color/vinyl/spin.js b/gen/color/vinyl/spin.js new file mode 100644 index 0000000..d05a482 --- /dev/null +++ b/gen/color/vinyl/spin.js @@ -0,0 +1,34 @@ +// record player spinning a vinyl whose label has a little face, tonearm riding the grooves, notes floating up +const{sin,cos,PI}=Math,T=2*PI +export const frames=20,delay=80 +const BASE='#ffa94d',BD='#e8590c',REC='#343a40',GR='#5c636a',SH='#868e96',LAB='#ff6b6b',ARM='#dee2e6',HEAD='#ffd43b',NOTE=['#4dabf7','#b197fc'] +const note=(c,x,y,s,col)=>{ + c.save();c.translate(x,y);c.scale(s,s);c.fillStyle=col + for(const[w,st]of[[9,'#111'],[3,col]])c.lineWidth=w,c.strokeStyle=st,c.beginPath(),c.moveTo(5,4),c.lineTo(5,-24),c.lineTo(17,-18),c.stroke() + c.strokeStyle='#111';c.lineWidth=4;c.beginPath();c.ellipse(0,5,8,6,-.4,0,T);c.fill();c.stroke();c.restore() +} +export default(c,t)=>{ + const cx=110,cy=150 + c.fillStyle=BASE;c.beginPath();c.roundRect(22,64,212,168,22);c.fill();c.stroke() + c.fillStyle=BD;c.lineWidth=5;c.beginPath();c.roundRect(186,206,30,12,6);c.fill();c.stroke() + c.fillStyle='#fff';c.beginPath();c.arc(202,184,10,0,T);c.fill();c.stroke();c.beginPath();c.moveTo(202,184);c.lineTo(202,176);c.stroke() + c.lineWidth=7;c.fillStyle=REC;c.beginPath();c.arc(cx,cy,76,0,T);c.fill();c.stroke() + c.strokeStyle=GR;c.lineWidth=2;for(const r of[66,58,50,42])c.beginPath(),c.arc(cx,cy,r,0,T),c.stroke() + c.strokeStyle=SH;c.lineWidth=5;for(const[r,a0]of[[62,-2.5],[54,.65]])c.beginPath(),c.arc(cx,cy,r,a0,a0+.5),c.stroke() + c.strokeStyle='#111' + c.save();c.translate(cx,cy);c.rotate(T*t) + c.fillStyle=LAB;c.lineWidth=5;c.beginPath();c.arc(0,0,28,0,T);c.fill();c.stroke() + c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.arc(d*10,-9,3.5,0,T),c.fill() + c.lineWidth=3.5;c.beginPath();c.arc(0,8,7,.15*PI,.85*PI);c.stroke() + c.fillStyle='#ffc9c9';for(const d of[-1,1])c.beginPath(),c.ellipse(d*17,2,4,2.5,0,0,T),c.fill() + c.restore() + c.fillStyle=ARM;c.lineWidth=3;c.beginPath();c.arc(cx,cy,4,0,T);c.fill();c.stroke() + const hx=166,hy=196 + for(const[w,st]of[[13,'#111'],[6,ARM]])c.lineWidth=w,c.strokeStyle=st,c.beginPath(),c.moveTo(206,92),c.lineTo(208,160),c.lineTo(hx,hy),c.stroke() + c.strokeStyle='#111';c.lineWidth=5 + c.save();c.translate(hx,hy);c.rotate(.75);c.fillStyle=HEAD;c.beginPath();c.roundRect(-9,-6,22,14,4);c.fill();c.stroke();c.restore() + c.fillStyle=ARM;c.beginPath();c.roundRect(196,64,20,16,4);c.fill();c.stroke() + c.lineWidth=7;c.beginPath();c.arc(206,92,14,0,T);c.fill();c.stroke() + c.fillStyle='#868e96';c.beginPath();c.arc(206,92,5,0,T);c.fill() + for(let k=0;k<2;k++){const p=(t+k/2)%1,s=p<.2?p*5:p<.8?1:(1-p)*5;note(c,108+(k?10:-12)+6*sin(p*T),112-p*94,s,NOTE[k])} +} diff --git a/gen/color/wateringcan/sprinkle.js b/gen/color/wateringcan/sprinkle.js new file mode 100644 index 0000000..efba7b3 --- /dev/null +++ b/gen/color/wateringcan/sprinkle.js @@ -0,0 +1,45 @@ +// watering can with a face tips forward and sprinkles a droopy sprout in a pot, which perks up, then gets thirsty again +const{sin,cos,PI,min,max}=Math,T=2*PI +export const frames=24,delay=80 +const CAN='#4dabf7',CD='#1c7ed6',POT='#ff922b',PD='#e8590c',SOIL='#a9744f',LF='#69db7c',LD='#2f9e44',DROP='#a5d8ff',BL='#faa2c1' +const sm=x=>x<0?0:x>1?1:x*x*(3-2*x) +const sparkle=(c,x,y,s)=>{c.fillStyle='#fff9db';c.lineWidth=3.5;c.beginPath();c.moveTo(x,y-s);for(const[px,py,qx,qy]of[[s,0,s*.18,-s*.18],[0,s,s*.18,s*.18],[-s,0,-s*.18,s*.18],[0,-s,-s*.18,-s*.18]])c.quadraticCurveTo(x+qx,y+qy,x+px,y+py);c.fill();c.stroke()} +export default(c,t)=>{ + const tip=sm((t-.04)/.14)-sm((t-.6)/.14),a=.5*tip,perk=sm((t-.34)/.2)-sm((t-.8)/.18) + const PX=74,PY=112,rose=[PX+86*cos(a)+34*sin(a),PY+86*sin(a)-34*cos(a)] + c.fillStyle=SOIL;c.beginPath();c.ellipse(190,190,38,7,0,0,T);c.fill();c.stroke() + const tx=214-24*perk,ty=170-34*perk,qx=204-14*perk,qy=176-24*perk + for(const[w,st]of[[11,'#111'],[5,LD]])c.lineWidth=w,c.strokeStyle=st,c.beginPath(),c.moveTo(190,190),c.quadraticCurveTo(qx,qy,tx,ty),c.stroke() + c.strokeStyle='#111' + for(const[up,dn]of[[3.78,2.2],[-.6,.9]]){ + const th=dn+(up-dn)*perk,x=tx+15*cos(th),y=ty+15*sin(th) + c.fillStyle=LF;c.lineWidth=5;c.beginPath();c.ellipse(x,y,15,8,th,0,T);c.fill();c.stroke() + c.lineWidth=2.5;c.beginPath();c.moveTo(tx,ty);c.lineTo(x+8*cos(th),y+8*sin(th));c.stroke() + } + if(perk>.7){const s=10*(perk-.7)/.3;sparkle(c,166,132,s);sparkle(c,226,142,s*.7)} + c.lineWidth=7;c.fillStyle=POT;c.beginPath();c.moveTo(154,204);c.lineTo(226,204);c.lineTo(216,246);c.lineTo(164,246);c.closePath();c.fill();c.stroke() + c.fillStyle=PD;c.beginPath();c.roundRect(146,188,88,18,6);c.fill();c.stroke() + c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.arc(190+d*13,222,4,0,T),c.fill() + c.lineWidth=3.5;c.beginPath();if(perk>.5)c.arc(190,227,6,.15*PI,.85*PI);else c.moveTo(184,232),c.quadraticCurveTo(190,227,196,232);c.stroke() + c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(190+d*24,230,5,3,0,0,T),c.fill() + if(t>.18&&t<.74)for(let j=0;j<12;j++){ + const s0=.18+j*.04,u=(t-s0)/.12;if(u<0||u>1||s0>.62)continue + const off=((j*5)%3-1)*6,x=rose[0]+off+22*u,y=rose[1]+4+(188-rose[1]-4)*u + c.fillStyle=DROP;c.lineWidth=3;c.beginPath();c.moveTo(x,y-7);c.quadraticCurveTo(x+5,y,x,y+3);c.quadraticCurveTo(x-5,y,x,y-7);c.fill();c.stroke() + } + c.save();c.translate(PX,PY);c.rotate(a);c.lineWidth=7 + for(const[w,st]of[[15,'#111'],[7,CD]])c.lineWidth=w,c.strokeStyle=st,c.beginPath(),c.arc(-18,-30,22,PI*1.05,PI*1.95),c.stroke() + c.strokeStyle='#111';c.lineWidth=7 + c.fillStyle=CAN;c.beginPath();c.moveTo(30,4);c.lineTo(80,-30);c.lineTo(88,-20);c.lineTo(34,24);c.closePath();c.fill();c.stroke() + c.save();c.translate(86,-32);c.rotate(-.6);c.fillStyle=CD;c.beginPath();c.roundRect(-6,-14,14,28,5);c.fill();c.stroke() + c.fillStyle='#e7f5ff';for(const y of[-6,0,6])c.beginPath(),c.arc(8,y,1.8,0,T),c.fill();c.restore() + c.fillStyle=CAN;c.beginPath();c.roundRect(-50,-32,86,72,16);c.fill();c.stroke() + c.fillStyle=CD;c.beginPath();c.roundRect(-50,-32,86,14,[16,16,0,0]);c.fill();c.lineWidth=5;c.stroke() + const happy=tip>.5 + c.fillStyle='#111';c.lineWidth=4 + if(happy)for(const d of[-1,1])c.beginPath(),c.arc(-7+d*14,4,5,PI*1.1,PI*1.9),c.stroke() + else for(const d of[-1,1])c.beginPath(),c.arc(-7+d*14,2,4.5,0,T),c.fill() + c.beginPath();c.arc(-7,12,7,.15*PI,.85*PI);c.stroke() + c.fillStyle=BL;for(const d of[-1,1])c.beginPath(),c.ellipse(-7+d*26,16,5,3,0,0,T),c.fill() + c.restore() +} diff --git a/public/arcade/blink.gif b/public/arcade/blink.gif new file mode 100644 index 0000000..34fe76f Binary files /dev/null and b/public/arcade/blink.gif differ diff --git a/public/arcade/blink.webp b/public/arcade/blink.webp new file mode 100644 index 0000000..18e873e Binary files /dev/null and b/public/arcade/blink.webp differ diff --git a/public/aurora/shimmer.gif b/public/aurora/shimmer.gif new file mode 100644 index 0000000..3a8bb95 Binary files /dev/null and b/public/aurora/shimmer.gif differ diff --git a/public/aurora/shimmer.webp b/public/aurora/shimmer.webp new file mode 100644 index 0000000..cadd30b Binary files /dev/null and b/public/aurora/shimmer.webp differ diff --git a/public/axolotl/wave.gif b/public/axolotl/wave.gif new file mode 100644 index 0000000..41d364d Binary files /dev/null and b/public/axolotl/wave.gif differ diff --git a/public/axolotl/wave.webp b/public/axolotl/wave.webp new file mode 100644 index 0000000..3440952 Binary files /dev/null and b/public/axolotl/wave.webp differ diff --git a/public/blender/whirl.gif b/public/blender/whirl.gif new file mode 100644 index 0000000..c0f5c6c Binary files /dev/null and b/public/blender/whirl.gif differ diff --git a/public/blender/whirl.webp b/public/blender/whirl.webp new file mode 100644 index 0000000..13c8087 Binary files /dev/null and b/public/blender/whirl.webp differ diff --git a/public/capybara/soak.gif b/public/capybara/soak.gif new file mode 100644 index 0000000..9069393 Binary files /dev/null and b/public/capybara/soak.gif differ diff --git a/public/capybara/soak.webp b/public/capybara/soak.webp new file mode 100644 index 0000000..4073d9e Binary files /dev/null and b/public/capybara/soak.webp differ diff --git a/public/cassette/roll.gif b/public/cassette/roll.gif new file mode 100644 index 0000000..84eb9c3 Binary files /dev/null and b/public/cassette/roll.gif differ diff --git a/public/cassette/roll.webp b/public/cassette/roll.webp new file mode 100644 index 0000000..9e558de Binary files /dev/null and b/public/cassette/roll.webp differ diff --git a/public/chameleon/tongue.gif b/public/chameleon/tongue.gif new file mode 100644 index 0000000..2c0a906 Binary files /dev/null and b/public/chameleon/tongue.gif differ diff --git a/public/chameleon/tongue.webp b/public/chameleon/tongue.webp new file mode 100644 index 0000000..25a9e19 Binary files /dev/null and b/public/chameleon/tongue.webp differ diff --git a/public/clam/pearl.gif b/public/clam/pearl.gif new file mode 100644 index 0000000..585729f Binary files /dev/null and b/public/clam/pearl.gif differ diff --git a/public/clam/pearl.webp b/public/clam/pearl.webp new file mode 100644 index 0000000..7bb2423 Binary files /dev/null and b/public/clam/pearl.webp differ diff --git a/public/clover/spin.gif b/public/clover/spin.gif new file mode 100644 index 0000000..4651c11 Binary files /dev/null and b/public/clover/spin.gif differ diff --git a/public/clover/spin.webp b/public/clover/spin.webp new file mode 100644 index 0000000..6a128c2 Binary files /dev/null and b/public/clover/spin.webp differ diff --git a/public/comet/streak.gif b/public/comet/streak.gif new file mode 100644 index 0000000..700b17b Binary files /dev/null and b/public/comet/streak.gif differ diff --git a/public/comet/streak.webp b/public/comet/streak.webp new file mode 100644 index 0000000..0412c41 Binary files /dev/null and b/public/comet/streak.webp differ diff --git a/public/corgi/wiggle.gif b/public/corgi/wiggle.gif new file mode 100644 index 0000000..a8924d7 Binary files /dev/null and b/public/corgi/wiggle.gif differ diff --git a/public/corgi/wiggle.webp b/public/corgi/wiggle.webp new file mode 100644 index 0000000..d526594 Binary files /dev/null and b/public/corgi/wiggle.webp differ diff --git a/public/cottoncandy/spin.gif b/public/cottoncandy/spin.gif new file mode 100644 index 0000000..5a66111 Binary files /dev/null and b/public/cottoncandy/spin.gif differ diff --git a/public/cottoncandy/spin.webp b/public/cottoncandy/spin.webp new file mode 100644 index 0000000..eeeb16f Binary files /dev/null and b/public/cottoncandy/spin.webp differ diff --git a/public/crayon/scribble.gif b/public/crayon/scribble.gif new file mode 100644 index 0000000..4527788 Binary files /dev/null and b/public/crayon/scribble.gif differ diff --git a/public/crayon/scribble.webp b/public/crayon/scribble.webp new file mode 100644 index 0000000..5b3a51c Binary files /dev/null and b/public/crayon/scribble.webp differ diff --git a/public/crown/sparkle.gif b/public/crown/sparkle.gif new file mode 100644 index 0000000..4f99c7a Binary files /dev/null and b/public/crown/sparkle.gif differ diff --git a/public/crown/sparkle.webp b/public/crown/sparkle.webp new file mode 100644 index 0000000..f7ccd76 Binary files /dev/null and b/public/crown/sparkle.webp differ diff --git a/public/cupcake/sprinkle.gif b/public/cupcake/sprinkle.gif new file mode 100644 index 0000000..e2ff1ff Binary files /dev/null and b/public/cupcake/sprinkle.gif differ diff --git a/public/cupcake/sprinkle.webp b/public/cupcake/sprinkle.webp new file mode 100644 index 0000000..5c0a128 Binary files /dev/null and b/public/cupcake/sprinkle.webp differ diff --git a/public/disco/spin.gif b/public/disco/spin.gif new file mode 100644 index 0000000..cea19e3 Binary files /dev/null and b/public/disco/spin.gif differ diff --git a/public/disco/spin.webp b/public/disco/spin.webp new file mode 100644 index 0000000..0410d11 Binary files /dev/null and b/public/disco/spin.webp differ diff --git a/public/ferris/turn.gif b/public/ferris/turn.gif new file mode 100644 index 0000000..65242d6 Binary files /dev/null and b/public/ferris/turn.gif differ diff --git a/public/ferris/turn.webp b/public/ferris/turn.webp new file mode 100644 index 0000000..e9331d9 Binary files /dev/null and b/public/ferris/turn.webp differ diff --git a/public/firetruck/siren.gif b/public/firetruck/siren.gif new file mode 100644 index 0000000..1490ec2 Binary files /dev/null and b/public/firetruck/siren.gif differ diff --git a/public/firetruck/siren.webp b/public/firetruck/siren.webp new file mode 100644 index 0000000..d9cf601 Binary files /dev/null and b/public/firetruck/siren.webp differ diff --git a/public/flamingo/balance.gif b/public/flamingo/balance.gif new file mode 100644 index 0000000..4452b73 Binary files /dev/null and b/public/flamingo/balance.gif differ diff --git a/public/flamingo/balance.webp b/public/flamingo/balance.webp new file mode 100644 index 0000000..94552dc Binary files /dev/null and b/public/flamingo/balance.webp differ diff --git a/public/fries/dance.gif b/public/fries/dance.gif new file mode 100644 index 0000000..3b9c6a3 Binary files /dev/null and b/public/fries/dance.gif differ diff --git a/public/fries/dance.webp b/public/fries/dance.webp new file mode 100644 index 0000000..6ef2b2f Binary files /dev/null and b/public/fries/dance.webp differ diff --git a/public/galaxy/swirl.gif b/public/galaxy/swirl.gif new file mode 100644 index 0000000..076921a Binary files /dev/null and b/public/galaxy/swirl.gif differ diff --git a/public/galaxy/swirl.webp b/public/galaxy/swirl.webp new file mode 100644 index 0000000..a6bd053 Binary files /dev/null and b/public/galaxy/swirl.webp differ diff --git a/public/giraffe/munch.gif b/public/giraffe/munch.gif new file mode 100644 index 0000000..90b999c Binary files /dev/null and b/public/giraffe/munch.gif differ diff --git a/public/giraffe/munch.webp b/public/giraffe/munch.webp new file mode 100644 index 0000000..96f0533 Binary files /dev/null and b/public/giraffe/munch.webp differ diff --git a/public/helicopter/hover.gif b/public/helicopter/hover.gif new file mode 100644 index 0000000..324b776 Binary files /dev/null and b/public/helicopter/hover.gif differ diff --git a/public/helicopter/hover.webp b/public/helicopter/hover.webp new file mode 100644 index 0000000..6a15971 Binary files /dev/null and b/public/helicopter/hover.webp differ diff --git a/public/hotair/rise.gif b/public/hotair/rise.gif new file mode 100644 index 0000000..9c2cd9c Binary files /dev/null and b/public/hotair/rise.gif differ diff --git a/public/hotair/rise.webp b/public/hotair/rise.webp new file mode 100644 index 0000000..2c4415d Binary files /dev/null and b/public/hotair/rise.webp differ diff --git a/public/island/sway.gif b/public/island/sway.gif new file mode 100644 index 0000000..bc4a4d8 Binary files /dev/null and b/public/island/sway.gif differ diff --git a/public/island/sway.webp b/public/island/sway.webp new file mode 100644 index 0000000..5a60b36 Binary files /dev/null and b/public/island/sway.webp differ diff --git a/public/latte/art.gif b/public/latte/art.gif new file mode 100644 index 0000000..d9c8701 Binary files /dev/null and b/public/latte/art.gif differ diff --git a/public/latte/art.webp b/public/latte/art.webp new file mode 100644 index 0000000..74fab32 Binary files /dev/null and b/public/latte/art.webp differ diff --git a/public/lavalamp/flow.gif b/public/lavalamp/flow.gif new file mode 100644 index 0000000..9337aa5 Binary files /dev/null and b/public/lavalamp/flow.gif differ diff --git a/public/lavalamp/flow.webp b/public/lavalamp/flow.webp new file mode 100644 index 0000000..85e9a9c Binary files /dev/null and b/public/lavalamp/flow.webp differ diff --git a/public/lotus/bloom.gif b/public/lotus/bloom.gif new file mode 100644 index 0000000..11edc50 Binary files /dev/null and b/public/lotus/bloom.gif differ diff --git a/public/lotus/bloom.webp b/public/lotus/bloom.webp new file mode 100644 index 0000000..922638b Binary files /dev/null and b/public/lotus/bloom.webp differ diff --git a/public/maneki/beckon.gif b/public/maneki/beckon.gif new file mode 100644 index 0000000..6e7c1e6 Binary files /dev/null and b/public/maneki/beckon.gif differ diff --git a/public/maneki/beckon.webp b/public/maneki/beckon.webp new file mode 100644 index 0000000..0e8c773 Binary files /dev/null and b/public/maneki/beckon.webp differ diff --git a/public/maple/fall.gif b/public/maple/fall.gif new file mode 100644 index 0000000..85559ce Binary files /dev/null and b/public/maple/fall.gif differ diff --git a/public/maple/fall.webp b/public/maple/fall.webp new file mode 100644 index 0000000..e8b832d Binary files /dev/null and b/public/maple/fall.webp differ diff --git a/public/neon/flicker.gif b/public/neon/flicker.gif new file mode 100644 index 0000000..066fd32 Binary files /dev/null and b/public/neon/flicker.gif differ diff --git a/public/neon/flicker.webp b/public/neon/flicker.webp new file mode 100644 index 0000000..1a509dd Binary files /dev/null and b/public/neon/flicker.webp differ diff --git a/public/otter/float.gif b/public/otter/float.gif new file mode 100644 index 0000000..5b8d68c Binary files /dev/null and b/public/otter/float.gif differ diff --git a/public/otter/float.webp b/public/otter/float.webp new file mode 100644 index 0000000..19bac7b Binary files /dev/null and b/public/otter/float.webp differ diff --git a/public/pancake/syrup.gif b/public/pancake/syrup.gif new file mode 100644 index 0000000..82ccce6 Binary files /dev/null and b/public/pancake/syrup.gif differ diff --git a/public/pancake/syrup.webp b/public/pancake/syrup.webp new file mode 100644 index 0000000..0a41da9 Binary files /dev/null and b/public/pancake/syrup.webp differ diff --git a/public/peacock/fan.gif b/public/peacock/fan.gif new file mode 100644 index 0000000..de4507d Binary files /dev/null and b/public/peacock/fan.gif differ diff --git a/public/peacock/fan.webp b/public/peacock/fan.webp new file mode 100644 index 0000000..426c833 Binary files /dev/null and b/public/peacock/fan.webp differ diff --git a/public/pinata/swing.gif b/public/pinata/swing.gif new file mode 100644 index 0000000..00d265f Binary files /dev/null and b/public/pinata/swing.gif differ diff --git a/public/pinata/swing.webp b/public/pinata/swing.webp new file mode 100644 index 0000000..10fd015 Binary files /dev/null and b/public/pinata/swing.webp differ diff --git a/public/pineapple/groove.gif b/public/pineapple/groove.gif new file mode 100644 index 0000000..e616501 Binary files /dev/null and b/public/pineapple/groove.gif differ diff --git a/public/pineapple/groove.webp b/public/pineapple/groove.webp new file mode 100644 index 0000000..99be976 Binary files /dev/null and b/public/pineapple/groove.webp differ diff --git a/public/popsicle/drip.gif b/public/popsicle/drip.gif new file mode 100644 index 0000000..ad1e5c0 Binary files /dev/null and b/public/popsicle/drip.gif differ diff --git a/public/popsicle/drip.webp b/public/popsicle/drip.webp new file mode 100644 index 0000000..bef8991 Binary files /dev/null and b/public/popsicle/drip.webp differ diff --git a/public/rollercoaster/loop.gif b/public/rollercoaster/loop.gif new file mode 100644 index 0000000..d19f071 Binary files /dev/null and b/public/rollercoaster/loop.gif differ diff --git a/public/rollercoaster/loop.webp b/public/rollercoaster/loop.webp new file mode 100644 index 0000000..0b1a8fb Binary files /dev/null and b/public/rollercoaster/loop.webp differ diff --git a/public/snowglobe/shake.gif b/public/snowglobe/shake.gif new file mode 100644 index 0000000..228b9d3 Binary files /dev/null and b/public/snowglobe/shake.gif differ diff --git a/public/snowglobe/shake.webp b/public/snowglobe/shake.webp new file mode 100644 index 0000000..6827c5e Binary files /dev/null and b/public/snowglobe/shake.webp differ diff --git a/public/soccer/goal.gif b/public/soccer/goal.gif new file mode 100644 index 0000000..cd09ad7 Binary files /dev/null and b/public/soccer/goal.gif differ diff --git a/public/soccer/goal.webp b/public/soccer/goal.webp new file mode 100644 index 0000000..4efad46 Binary files /dev/null and b/public/soccer/goal.webp differ diff --git a/public/strawberry/bounce.gif b/public/strawberry/bounce.gif new file mode 100644 index 0000000..a183407 Binary files /dev/null and b/public/strawberry/bounce.gif differ diff --git a/public/strawberry/bounce.webp b/public/strawberry/bounce.webp new file mode 100644 index 0000000..14aae5d Binary files /dev/null and b/public/strawberry/bounce.webp differ diff --git a/public/submarine/dive.gif b/public/submarine/dive.gif new file mode 100644 index 0000000..204956f Binary files /dev/null and b/public/submarine/dive.gif differ diff --git a/public/submarine/dive.webp b/public/submarine/dive.webp new file mode 100644 index 0000000..276b7e2 Binary files /dev/null and b/public/submarine/dive.webp differ diff --git a/public/sunflower/turn.gif b/public/sunflower/turn.gif new file mode 100644 index 0000000..ee5c994 Binary files /dev/null and b/public/sunflower/turn.gif differ diff --git a/public/sunflower/turn.webp b/public/sunflower/turn.webp new file mode 100644 index 0000000..12ed280 Binary files /dev/null and b/public/sunflower/turn.webp differ diff --git a/public/sunset/sink.gif b/public/sunset/sink.gif new file mode 100644 index 0000000..c573134 Binary files /dev/null and b/public/sunset/sink.gif differ diff --git a/public/sunset/sink.webp b/public/sunset/sink.webp new file mode 100644 index 0000000..01e38a2 Binary files /dev/null and b/public/sunset/sink.webp differ diff --git a/public/sushi/hop.gif b/public/sushi/hop.gif new file mode 100644 index 0000000..28812b0 Binary files /dev/null and b/public/sushi/hop.gif differ diff --git a/public/sushi/hop.webp b/public/sushi/hop.webp new file mode 100644 index 0000000..1a86e35 Binary files /dev/null and b/public/sushi/hop.webp differ diff --git a/public/toucan/bob.gif b/public/toucan/bob.gif new file mode 100644 index 0000000..123bab7 Binary files /dev/null and b/public/toucan/bob.gif differ diff --git a/public/toucan/bob.webp b/public/toucan/bob.webp new file mode 100644 index 0000000..fb074e6 Binary files /dev/null and b/public/toucan/bob.webp differ diff --git a/public/traffic/cycle.gif b/public/traffic/cycle.gif new file mode 100644 index 0000000..9984614 Binary files /dev/null and b/public/traffic/cycle.gif differ diff --git a/public/traffic/cycle.webp b/public/traffic/cycle.webp new file mode 100644 index 0000000..6513048 Binary files /dev/null and b/public/traffic/cycle.webp differ diff --git a/public/unicorn/prance.gif b/public/unicorn/prance.gif new file mode 100644 index 0000000..02f162e Binary files /dev/null and b/public/unicorn/prance.gif differ diff --git a/public/unicorn/prance.webp b/public/unicorn/prance.webp new file mode 100644 index 0000000..e9c9bf1 Binary files /dev/null and b/public/unicorn/prance.webp differ diff --git a/public/vinyl/spin.gif b/public/vinyl/spin.gif new file mode 100644 index 0000000..a5ef1b5 Binary files /dev/null and b/public/vinyl/spin.gif differ diff --git a/public/vinyl/spin.webp b/public/vinyl/spin.webp new file mode 100644 index 0000000..8e8ee4d Binary files /dev/null and b/public/vinyl/spin.webp differ diff --git a/public/wateringcan/sprinkle.gif b/public/wateringcan/sprinkle.gif new file mode 100644 index 0000000..45071de Binary files /dev/null and b/public/wateringcan/sprinkle.gif differ diff --git a/public/wateringcan/sprinkle.webp b/public/wateringcan/sprinkle.webp new file mode 100644 index 0000000..1bc104c Binary files /dev/null and b/public/wateringcan/sprinkle.webp differ