Perlin Flow Field Tapestry

📅 April 23, 2026 🏷️ art
worth-a-look perlin noise flow field particle system emergent behavior organic texture interactive art generative
Generated by GridFlow AI | Tags: perlin noise, flow field, particle system, emergent behavior, organic texture, interactive art, generative

💡 AI 提示词

Create an organic texture mapping system using Perlin noise as a driving force for a particle flow field, with emergent patterns, mouse interaction, and fluid-like dynamics.

🔧 核心算法要点

  1. Initialize a 2D flow field grid with resolution-based cells that sample 3D Perlin noise for angle generation
  2. Map noise values (0-1) to angles spanning multiple rotations for complex directional patterns
  3. Spawn particles that sample the flow field at their current position and follow the resulting vectors
  4. Add curl noise perturbation based on position-time relationships for additional organic complexity
  5. Implement mouse-based forces: radial repulsion within 150px and directional drift influence
  6. Apply velocity damping and acceleration limiting for smooth, organic particle movement
  7. Render particles with HSB colors derived from position, velocity magnitude, and time for chromatic emergence
  8. Fade background each frame to create ghostly trails that build complex layered textures

🎨 原始代码

var sketch = function(p) {
  var particles = [];
  var t = 0;
  var zoff = 0;
  var scale = 0.003;
  var noiseStrength = 1.5;
  var particleCount = 1200;
  var flowField = [];
  var cols, rows;
  var resolution = 20;

  p.setup = function() {
    var container = document.getElementById('p5-wrapper');
    p.createCanvas(container.offsetWidth, container.offsetHeight).parent(container);
    p.colorMode(p.HSB, 360, 100, 100, 100);
    p.background(0, 0, 8);
    p.noStroke();

    cols = Math.floor(p.width / resolution) + 1;
    rows = Math.floor(p.height / resolution) + 1;
    flowField = new Array(cols * rows);

    for (var i = 0; i < particleCount; i++) {
      particles.push(p.createParticle());
    }
  };

  p.createParticle = function() {
    return {
      pos: p.createVector(p.random(p.width), p.random(p.height)),
      vel: p.createVector(0, 0),
      acc: p.createVector(0, 0),
      maxSpeed: p.random(1.5, 4),
      hueOffset: p.random(360),
      size: p.random(1.5, 4),
      life: p.random(150, 350),
      maxLife: 0
    };
  };

  p.updateFlowField = function() {
    var yoff = 0;
    for (var y = 0; y < rows; y++) {
      var xoff = 0;
      for (var x = 0; x < cols; x++) {
        var index = x + y * cols;
        var angle = p.noise(xoff, yoff, zoff) * p.TWO_PI * 3;
        var v = p5.Vector.fromAngle(angle);
        v.setMag(noiseStrength);
        flowField[index] = v;
        xoff += scale;
      }
      yoff += scale;
    }
    zoff += 0.0008;
  };

  p.getFlowVector = function(x, y) {
    var xIndex = Math.floor(x / resolution);
    var yIndex = Math.floor(y / resolution);
    xIndex = p.constrain(xIndex, 0, cols - 1);
    yIndex = p.constrain(yIndex, 0, rows - 1);
    return flowField[xIndex + yIndex * cols];
  };

  p.draw = function() {
    p.background(0, 0, 8, 8);

    p.updateFlowField();

    var mouseInfluence = p.createVector(p.mouseX - p.width / 2, p.mouseY - p.height / 2);
    mouseInfluence.mult(0.003);

    for (var i = 0; i < particles.length; i++) {
      var part = particles[i];

      var flow = p.getFlowVector(part.pos.x, part.pos.y);
      part.acc.add(flow);
      part.acc.add(mouseInfluence);

      var distanceToMouse = p.dist(part.pos.x, part.pos.y, p.mouseX, p.mouseY);
      if (distanceToMouse < 150) {
        var repel = p5.Vector.sub(part.pos, p.createVector(p.mouseX, p.mouseY));
        repel.normalize();
        repel.mult(p.map(distanceToMouse, 0, 150, 2, 0));
        part.acc.add(repel);
      }

      var curlNoise = p.noise(part.pos.x * 0.002, part.pos.y * 0.002, t * 0.5);
      var curlAngle = curlNoise * p.TWO_PI * 2;
      var curlForce = p5.Vector.fromAngle(curlAngle);
      curlForce.mult(0.15);
      part.acc.add(curlForce);

      part.vel.add(part.acc);
      part.vel.limit(part.maxSpeed);
      part.vel.mult(0.98);
      part.pos.add(part.vel);
      part.acc.mult(0);

      part.life--;

      var lifeRatio = part.life / 350;
      var hue = (part.hueOffset + t * 80 + p.map(part.pos.x, 0, p.width, 0, 60)) % 360;
      var saturation = p.map(p.abs(part.vel.x) + p.abs(part.vel.y), 0, part.maxSpeed * 2, 50, 90);
      var brightness = p.map(lifeRatio, 0, 1, 40, 95);
      var alpha = p.map(lifeRatio, 0, 1, 10, 70);

      p.fill(hue, saturation, brightness, alpha);
      var drawSize = part.size * p.map(lifeRatio, 0, 1, 0.3, 1);
      p.ellipse(part.pos.x, part.pos.y, drawSize, drawSize);

      if (part.pos.x < 0 || part.pos.x > p.width || part.pos.y < 0 || part.pos.y > p.height || part.life <= 0) {
        var newPart = p.createParticle();
        if (Math.random() > 0.3) {
          var spawnSide = Math.floor(p.random(4));
          if (spawnSide === 0) {
            newPart.pos.x = 0;
            newPart.pos.y = p.random(p.height);
          } else if (spawnSide === 1) {
            newPart.pos.x = p.width;
            newPart.pos.y = p.random(p.height);
          } else if (spawnSide === 2) {
            newPart.pos.x = p.random(p.width);
            newPart.pos.y = 0;
          } else {
            newPart.pos.x = p.random(p.width);
            newPart.pos.y = p.height;
          }
        }
        particles[i] = newPart;
      }
    }

    t += 0.015;
  };

  p.windowResized = function() {
    var container = document.getElementById('p5-wrapper');
    p.resizeCanvas(container.offsetWidth, container.offsetHeight);
    cols = Math.floor(p.width / resolution) + 1;
    rows = Math.floor(p.height / resolution) + 1;
    flowField = new Array(cols * rows);
  };
};
// p5 init stripped

✨ AI 艺术解读

This piece explores the emergent beauty of noise-driven particle systems where simple local rules create complex global patterns. The flow field acts as an invisible conductor, guiding thousands of particles through a choreographed dance that never repeats. Mouse interaction introduces controlled chaos, allowing viewers to sculpt the composition in real-time while the accumulated particle trails reveal the hidden mathematics underlying organic forms. The resulting tapestry speaks to the fundamental connection between deterministic systems and apparent randomness in nature.

📝 补充说明

  • The flow field resolution (20px cells) balances visual smoothness with computational efficiency for real-time performance
  • Curl noise addition at 15% strength creates subtle swirling without overwhelming the primary flow direction
  • Background fade rate of 8/255 creates optimal trail persistence for the given particle density and lifetime
  • Spawning particles at screen edges with outward bias maintains consistent density while preventing clustering
  • Using HSB color mode with time-based hue rotation ensures smooth chromatic transitions across the entire spectrum