{
const options = {
columns: 13,
rows: 5,
size: 80,
randomness: .2,
minRadius: .15,
maxRadius: .4,
timeStep: .007,
bgColor: 'hsl(0,0%,0%)',
fillColor: 'hsl(0,0%,100%)',
lineColor: 'hsl(0,0%,50%)',
lineWidth: 2,
};
const {
bgColor, columns: w, rows: h, size: s,
minRadius: rMin, maxRadius: rMax, timeStep: step,
lineWidth, randomness: rand} = options;
const cx = (w-1)/2, cy = (h-1)/2, PI = Math.PI;
const c = DOM.context2d(w*s, h*s, 1);
c.canvas.style.maxWidth = '100%';
c.fillStyle = options.fillColor;
c.strokeStyle = options.lineColor;
c.lineWidth = lineWidth;
const offsets = [];
for(let [x,y] of walkGrid(w,h)) {
const tlbr = (!x&&!y) || (x+1==w&&y+1==h);
const trbl = (!x&&y+1==h) || (x+1==w&&!y);
offsets.push([
((x>cx&&y<=cy) || (x<=cx&&y>cy)) && !trbl || tlbr,
((x<cx&&y<=cy) || (x>=cx&&y>cy)) && !tlbr || trbl,
((x<cx&&y>=cy) || (x>=cx&&y<cy)) && !trbl || tlbr,
((x>cx&&y>=cy) || (x<=cx&&y<cy)) && !tlbr || trbl,
Math.random()*rand
]);
}
let t = 0;
while(true) {
c.save();
c.fillStyle = bgColor;
c.fillRect(0, 0, w*s, h*s);
c.restore();
c.beginPath();
for(let [x,y] of walkGrid(w,h)) {
const oi = x+y*w;
for(let i = 0; i < 4; i++) {
const o = offsets[oi][i] + offsets[oi][4];
arc(x, y, i, rMin + (.5 + .5 * Math.cos((1 + o + t*2) * PI)) * (rMax-rMin));
}
}
c.fill();
if(lineWidth > 0) {
c.beginPath();
for(let [x,y] of walkGrid(w,h)) c.rect(x*s, y*s, x*s+s, y*s+s);
c.stroke();
}
yield c.canvas;
t = (t + step) % 1;
}
function* walkGrid(w, h) {
for(let y = 0; y < h; y++) for(let x = 0; x < w; x++) yield [x,y];
}
function arc(x, y, i, r) {
const Q = Math.PI/2;
const map = [[0,0],[1,0],[1,1],[0,1]];
const px = (x + map[i][0])*s;
const py = (y + map[i][1])*s;
c.moveTo(px, py);
c.arc(px, py, r*s, Q*i, Q*(i+1));
c.lineTo(px, py);
}
}