Chanomic Sketch

Happy Grid

const vs = `
attribute vec3 aPosition;
attribute vec2 aTexCoord;
varying vec2 vTexCoord;

void main() {
  vec4 positionVec4 = vec4(aPosition, 1.0);
  positionVec4.xy = positionVec4.xy * 2.0 - 1.0;
  gl_Position = positionVec4;
  vTexCoord = aTexCoord;
}
`
const fs = `
precision mediump float;

uniform sampler2D uTexture;
uniform float uTime;

varying vec2 vTexCoord;

float hash(vec2 p) {
  return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453123);
}

void main() {
  vec2 uv = vTexCoord;

  vec2 center = uv - 0.5;
  float dist = length(center);

  vec2 cell = floor(uv * 12.0);
  float cellNoise = hash(cell);

  float wave =
    sin(dist * 10.0 - uTime * 1.2 + cellNoise * 3.14)
    * mix(0.02, 0.004, dist);

  uv += normalize(center) * wave;

  gl_FragColor = texture2D(uTexture, uv);
}
`

const palette = [
  "#5A9CB5",
  "#FACE68",
  "#FAAC68",
  "#FA6868",
  "#FF8A8A",
  "#A3DC9A",
  "#DEE791",
  "#687FE5"
];

let pg;
let distortShader;

function preload() {
  distortShader = createShader(vs, fs);
}

function setup() {
  createCanvas(windowWidth, windowHeight, WEBGL);
  noStroke();

  pg = createGraphics(windowWidth, windowHeight);
  drawPosterToBuffer(pg);
}

function windowResized() {
  resizeCanvas(windowWidth, windowHeight);
  pg = createGraphics(windowWidth, windowHeight);
  drawPosterToBuffer(pg);
}

function draw() {
  shader(distortShader);

  distortShader.setUniform("uTexture", pg);
  distortShader.setUniform("uTime", millis() * 0.001);
  distortShader.setUniform("uResolution", [width, height]);

  rect(-width / 2, -height / 2, width, height);
}

function drawPosterToBuffer(g) {
  g.background(245);

  const cols = 12;
  const rows = 15;

  const margin = min(width, height) * 0.06;
  const gap = min(width, height) * 0.01;

  const maxCellW =
    (width - margin * 2 - gap * (cols - 1)) / cols;
  const maxCellH =
    (height - margin * 2 - gap * (rows - 1)) / rows;

  const cell = min(maxCellW, maxCellH);
  const r = cell * 0.5;

  const gridW = cell * cols + gap * (cols - 1);
  const gridH = cell * rows + gap * (rows - 1);

  const offsetX = (width - gridW) / 2;
  const offsetY = (height - gridH) / 2;

  g.noStroke();

  for (let y = 0; y < rows; y++) {
    for (let x = 0; x < cols; x++) {
      const cx = offsetX + x * (cell + gap) + r;
      const cy = offsetY + y * (cell + gap) + r;

      const vertical = random() < 0.5;
      const stripes = int(random(4, 9));
      const colors = shuffle([...palette]).slice(0, 3);

      drawStripedCircle(g, cx, cy, r, vertical, stripes, colors);
    }
  }
}


function drawStripedCircle(g, cx, cy, r, vertical, stripes, colors) {
  g.push();
  g.translate(cx, cy);

  g.drawingContext.save();
  g.drawingContext.beginPath();
  g.drawingContext.arc(0, 0, r, 0, TAU);
  g.drawingContext.clip();

  if (vertical) {
    const w = (r * 2) / stripes;
    for (let i = 0; i < stripes; i++) {
      g.fill(random(colors));
      g.rect(-r + i * w, -r, w + 1, r * 2);
    }
  } else {
    const h = (r * 2) / stripes;
    for (let i = 0; i < stripes; i++) {
      g.fill(random(colors));
      g.rect(-r, -r + i * h, r * 2, h + 1);
    }
  }

  g.drawingContext.restore();

  g.noFill();
  g.stroke(30, 40);
  g.strokeWeight(1);
  g.ellipse(0, 0, r * 2);

  g.pop();
}