Skip to Content

flows

2025

Made with p5.js.This series of generative works investigates the aesthetics of emergent order within computational systems. Utilizing perlin noise based flow fields, stippled grids of varying scale and density are layered to reveal the latent trajectories embedded within the underlying invisible vector landscape. The resulting forms are a dynamic equilibrium of algorithmic precision and organic turbulence.

//more dots, now with perlin noise
//by tyler porter

//perlin noise setup
let noiseScale = 0.0001;
let jitter = 0.2;

let overDraw;

//palettes array setup
let palettes = [
  // warm red and complimentary blues/greens
  ["#FF6B6B", "#6BFFBB", "#1A535C", "#FF9F1C", "#1C70FF"],

  // indigo and complimentary pastel oranges/yellows
  ["#5B5F97", "#F67280", "#FFF2A6", "#454873", "#FFBA49"],

  // red + blue
  ["#3c3c3c", "#f5f5f5", "#087e8b", "#ff5a5f", "#f03a47"],

  // purple and aqua-centric with high contrast neons
  ["#FF6B6B", "#4ECDC4", "#FFE66D", "#1A535C", "#FF9F1C"],

  // earth tones
  ["#161513", "#656256", "#9EBC9F", "#D3B88C", "#F4F2F3"],

  // patagonia
  ["#eae0cc", "#c9ada1", "#a0a083", "#798478", "#4d6a6d"],

  // red orange highlights
  ["#111111", "#fffffc", "#beb7a4", "#ff7f11", "#ff1b1c"],

  // grey
  ["#bcabae", "#0f0f0f", "#2d2e2e", "#716969", "#fbfbfb"],

  // watermelon
  ["#42403c", "#ef2d56", "#ed7d3a", "#8cd867", "#2fbf71"],

  // wabi-sabi
  ["#f4dbd8", "#bea8a7", "#c09891", "#775144", "#262322"],
];

let paletteSelection;
let colorSelection;

//agent setup
let freq = 1;
let size = 2;
let posX = 0,
  posY = 0,
  startX = 0,
  startY = 0;
let newSize;
let repeats = 10;
let repeatCount = 0;
let steps = 20;
let stepCount = 0;
let totalAgents = 50;
let agentCount = 0;

//setup
function setup() {
//  createCanvas(600, 800, SVG); //vector rendering
  createCanvas(600, 800); //raster rendering
  background(254);
  frameRate(15);
  noStroke();

  //define an overdraw "bleed" based off the overall width of the image
  overDraw = width / 4;
  paletteSelection = int(random(palettes.length));
  colorSelection = 0;

  fill(palettes[paletteSelection][colorSelection]);
  rect(0, 0, width, height);

  resetAgent();
}

//draw loop
function draw() {
  fill(palettes[paletteSelection][colorSelection]);

  while (repeatCount < repeats) {
    newSize = size + size * random(-jitter, jitter);
    ellipse(posX, posY, newSize, newSize);

    //poll flow field for the vector at the current position, separate into consituent x and y components
    let angle = noise(posX * noiseScale, posY * noiseScale) * TWO_PI;
    let vx = cos(angle);
    let vy = sin(angle);

    // move agent using those components
    posX += vx * freq + random(-jitter, jitter);
    posY += vy * freq + random(-jitter, jitter);
    stepCount++;

    // Restart agent and move it perpendicular to the flow field if off screen
    if (
      posX < -overDraw ||
      posX > width + overDraw ||
      posY < -overDraw ||
      posY > height + overDraw ||
      stepCount > steps
    ) {
      let startAngle = noise(startX * noiseScale, startY * noiseScale) * TWO_PI;
      let px = -sin(startAngle);
      let py = cos(startAngle);

      startX += px * freq;
      startY += py * freq;
      posX = startX;
      posY = startY;
      repeatCount++;
      stepCount = 0;
    }
  }

  resetAgent();
}

//routine for resetting agent to new starting position with all new parameters
function resetAgent() {
  agentCount++;
  if (agentCount > totalAgents) {
    noLoop();
    //    save('canvas.svg');
    console.log("loop stopped at " + agentCount);
  }

  posX = random(-overDraw, width + overDraw);
  posY = random(-overDraw, height + overDraw);
  startX = posX;
  startY = posY;
  freq = random(1, 10);
  size = random(1, freq);
  repeats = round(random(1, 40));
  repeatCount = 0;
  steps = round(random(1, 500));
  stepCount = 0;
  colorSelection = int(random(palettes[paletteSelection].length));

}

//keyboard bindings
function keyPressed() {
  if (key === "s") {
    loop();
    save("canvas.svg");
    noLoop();
  }

  if (key === "n") {
    agentCount = 0;

    loop();
    clear();
    paletteSelection = int(random(palettes.length));
    colorSelection = 0;

    fill(palettes[paletteSelection][colorSelection]);
    rect(0, 0, width, height);

    resetAgent();
  }
}