Chanomic Sketch

Flicker

let img, gridCircles;
let lightColorBase;

function setup() {
  createCanvas(windowWidth, windowHeight);
  img = noiseOverlay();

  push();
  colorMode(HSB);
  lightColorBase = color('#FFFFD0');
  pop();

  const [N, M] = [3, 3];
  const r = 10;
  const sep = 2;
  const [rectHeight, rectWidth] = [2*r*N, 2*r*M];
  const rectSep = 40;
  const rectRowNum = ceil(height / (rectHeight + rectSep));
  const rectColNum = ceil(width / (rectWidth + rectSep));
  const rectNum = 30;

  const rectNumMax = rectRowNum * rectColNum;
  const indicies = choice(Array.from({ length: rectNumMax })
                              .map((_, i) => i),
                         rectNum);
  gridCircles = indicies
    .map((i) =>{
    const x = (i % rectColNum) * (rectWidth + rectSep);
    const y = floor(i / rectColNum) * (rectHeight + rectSep);
    return new GridCircles(x, y, N, M, r, sep, 20, random(0,100))
  });

}

function draw() {
  background(0);
  gridCircles.map((c) => c.draw());
  image(img, 0, 0);
}

class GridCircles {
  constructor(x, y, N, M, r, sep, period, phase) {
    this.x = x;
    this.y = y;
    this.N = N;
    this.M = M;
    this.r = r;
    this.sep = sep;
    this.period = period
    this.phase = phase;
  }

  draw() {
    push();

    const t = constrain(sin((frameCount - this.phase)/this.period), -0.9, 0.9);
    drawingContext.shadowBlur = map(t, -0.9, 0.9, 0, 10);
    drawingContext.shadowColor = color('white');
    noStroke();
    fill(lightColor(t));

    translate(this.x + this.r + this.sep,
              this.y + this.r + this.sep);
    for(let i = 0; i < this.N; i++) {
      for (let j = 0; j < this.M; j++) {
        const cx = j * (2*this.r + this.sep);
        const cy = i * (2*this.r + this.sep);
        circle(cx, cy, 2*this.r);
      }
    }

    pop();
  }
}

const lightColor = (t) => {
  push();
  colorMode(HSB, 360, 100, 100, 1);
  const col = color(hue(lightColorBase),
                    saturation(lightColorBase),
                    map(t, -1, 1, 0, brightness(lightColorBase)));
  pop();
  return col;
};

const noiseOverlay = () => {
  const img = createGraphics(width, height);
  push();
  colorMode(HSB);
  for (let i = 0; i < width; i++) {
    for (let j = 0; j < height; j++) {
      const ct = color(random(0, 360), random(0, 100), random(0, 100), 0.15);
      img.set(i, j, ct);
    }
  }
  pop();
  img.updatePixels();
  return img
}

const choice = (arr, N) => {
  return shuffle(arr).slice(0, N);
}