Chanomic Sketch

Fabric

"use strict";

let palette, colBg;

function setup() {
  pixelDensity(1);
  createCanvas(windowWidth, windowHeight);

  colBg = color('#FCFFE7');
  palette = ['#D2001A', '#2B3467', '#EF5B0C'].map((c) => 
    lerpColor(color(c), colBg, 0.25)
  );
}

function draw() {
  noLoop();
  background(colBg);

  translate(width/2, height/2);
  noStroke(255);

  let shapeNum = 3;
  const r0 = min(map(width, 0, 720, 0, 100), 100);
  const L = max(width, height);
  const rs = [];
  for (let r = r0; r < L; r += r0) {
    rs.push(r);
  }

  for (let i = 0; i < rs.length; i++) {
    if (i < rs.length - 1) {
      const r0 = rs[i] * cos(TWO_PI / shapeNum / 2);
      const r1 = rs[i + 1] * cos(TWO_PI / (shapeNum + 1) / 2);
      drawRegularShape(rs[i], shapeNum, r1 - r0, rs[i], random(palette));
    }
    shapeNum++;
  }
  
  filterGrain();
}

const drawRegularShape = (R, N, hMax, offset, col) => {
  const edges = Array.from({ length: N }).map((_, i) => {
    const angs = i / N * TWO_PI - HALF_PI;
    const angt = (i + 1) / N * TWO_PI - HALF_PI;
    const xs = R*cos(angs)
    const ys = R*sin(angs);
    const xt = R*cos(angt)
    const yt = R*sin(angt);

    const pos = createVector(xs, ys);
    const vec = createVector(xt - xs, yt - ys);

    return { pos, vec };
  });

  edges.forEach(({ pos, vec }, i) => {
    push();
    translate(pos.x, pos.y);
    rotate(vec.heading());
    drawEdge(vec.mag(), hMax, i + offset, col);
    pop();
  })

}

const drawEdge = (W, hMax, offset, col) => {
  const N = 100;

  const positions = Array.from({ length: N }).map((_, i) => {
    if (i === 0) {
      return { x: 0, y: 0 };
    } else if (i === N - 1) {
      return { x: W, y: 0 };
    } else {
      const t = i / N;
      const x = t * W;
      const y = -noise(i*0.05, 0, offset) * hMax * sin(t*PI);
      return { x, y };
    }
  });

  fill(col);
  beginShape();
  positions.forEach(({x, y}) => {
    vertex(x, y);
  });
  endShape();

  const i = floor(random(1, N-2));
  const r = 0.1*W;
  fill(random(palette));
  circle(positions[i].x, positions[i].y - r, 2*r, 2*r);
}

const filterGrain = () => {
  const cMax = 5;
  const cMin = -5;
  loadPixels();
  for (let y = 0; y < height; y++) {
    const delta = random(cMin, cMax);
    for (let x = 0; x < width; x++) {
      const i = (y * width + x) * 4;
      pixels[i + 0] += delta;
      pixels[i + 1] += delta;
      pixels[i + 2] += delta;
    }
  }
  for (let x = 0; x < width; x++) {
    const delta = random(cMin, cMax);
    for (let y = 0; y < height; y++) {
      const i = (y * width + x) * 4;
      pixels[i + 0] += delta;
      pixels[i + 1] += delta;
      pixels[i + 2] += delta;
    }
  }
  updatePixels();
}