Interaction Design WikiProgramming

p5.js Particle System


Particle System is a so called “emitter”, which emits the particles and is often used to display “fuzzy” things, such as galaxies or explosions. They can create complex, interactive graphics which interact with fields, physics etc.

Here’s a basic example of flow field particle system:

var inc = 0.1;
var scl = 100,zoff = 0;
var cols,rows,movement = 0;
var particles = [];
var flowfield;
var img;
var maxSpeed;
let lineMaxDist = 50;

function setup() {

  var canvas = createCanvas(820, 820);
  maxSpeed = 10.5;

  cols = floor(width / scl);
  rows = floor(height / scl);
  flowfield = new Array(cols);
  var numParticles = Math.floor(width/4);

  for (var i = 0; i < numParticles; i++) {
    particles[i] = new Particle();
  }
  background(245);
  colorMode(RGB);

  r = random(255);
  g = random(255);
  b = random(255);
}

function draw() {
  noStroke();
  fill(10, 10);
  rect(0, 0, width, height);
  noFill();

  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 = noise(xoff, yoff, zoff) * TWO_PI * 4;
      var v = p5.Vector.fromAngle(angle);
      v.setMag(1);
      flowfield[index] = v;
      xoff += inc;
    }
    yoff += inc;
    zoff += 0.001;
  }

  for (var i = 0; i < particles.length; i++) {
    particles[i].follow(flowfield);
    particles[i].update();
    particles[i].edges();
    particles[i].scatter();
    particles[i].show();
    //particles[i].connect(); //might be used to draw connections
  }
}

function Particle() {
  this.pos = createVector(random(width), random(height - 100));
  this.vel = createVector(0, 0);
  this.acc = createVector(0, 0);
  this.maxspeed = maxSpeed;
  this.prevPos = this.pos.copy();
  this.lifespan = 255;

  this.update = function () {
    this.vel.add(this.acc);
    this.vel.limit(this.maxspeed);
    this.pos.add(this.vel);
    this.acc.mult(0);
    this.lifespan -= 1;
  };

  this.follow = function (vectors) {
    var x = floor(this.pos.x / scl);
    var y = floor(this.pos.y / scl);
    var index = x + y * cols;
    var force = vectors[index];
    this.applyForce(force);
  };

  this.scatter = function (vectors) {
    this.pos.x += random(-0.9, 0.9);
    this.pos.y += random(-0.9, 0.9);
  };

  this.applyForce = function (force) {
    this.acc.add(force);
  };

  this.show = function () {
    stroke(r, g, b, this.lifespan);
    line(this.pos.x, this.pos.y, this.prevPos.x, this.prevPos.y);
    this.updatePrev();
  };

  this.updatePrev = function () {
    this.prevPos.x = this.pos.x;
    this.prevPos.y = this.pos.y;
  };

  this.edges = function () {
    if (this.pos.x > width) {
      this.pos.x = 0;
      this.updatePrev();
    }
    if (this.pos.x < 0) {
      this.pos.x = width;
      this.updatePrev();
    }

    if (this.pos.y > height) {
      this.pos.y = 0;
      this.updatePrev();
    }

    if (this.pos.y < 0) {
      this.pos.y = height;
      this.updatePrev();
    }
  };
  
  //uncomment this to draw connections between particles that are less than lineMaxDist
    /*
    this.connect = function() {
    particles.forEach(particle => {
      let distance = dist(this.pos.x, this.pos.y, particle.pos.x, particle.pos.y);
      if (distance < lineMaxDist) {
        stroke(r,g,b, map(distance, 0, lineMaxDist, 255, 0));
        strokeWeight(map(distance, 0, lineMaxDist, 2, 0));
        line(this.pos.x, this.pos.y, particle.pos.x, particle.pos.y);
      }
    })
  }
  */
}