Trailing and motion basics with p5js

Let’s start with a basic object in the middle of the screen. Let’s call it particle. We’ll give it a few properties and a few methods.

var particle;
function setup() {
createCanvas(windowWidth, windowHeight);
particle = new Particle();
}

function draw() {
background(51);
particle.display();//places the object on canvas
particle.update(); // updates object properties
}


//Particle constructor
function Particle() {
this.pos = createVector(width / 2, height / 2) //create a vector object to store coordinate componenents
this.vel = createVector(0, 0);
this.acc = createVector(0, 0);

this.update = function() {
this.vel.add(this.acc);
this.pos.add(this.vel);
}


this.display = function() {
strokeWeight(2);
stroke("black");


fill(255);
ellipse(this.pos.x, this.pos.y, 48, 48);
}
}

 

Now, let’s apply some movement, and add more particles to the program. I’m going to replicate Daniel Shiffman’s approach, with some variations. You can see his work The Nature of Code for a more in depth understanding of physics simulations.

var particles = [];
var mass;

function setup() {
createCanvas(600, 400);
}
function mousePressed() {
particles.push(new Particle(mouseX, mouseY,mass));//add a new partice on mousePressed
}

function draw() {
background(51);
background("black");
var randomForce = createVector(random(-0.01,0.01), random(-0.01,0.01));
for (var i = 0; i < particles.length; i++) {
if(mouseIsPressed){
particles[i].applyForce(randomForce);
}
particles[i].display(); //places the object on canvas
particles[i].update(); // updates object properties
particles[i].checkEdges();//check the paticle is within canvas
//check if they particles are intersecting
for (var j = 0; j < particles.length; j++){
if(i!=j && particles[i].intersects(particles[j])){
//print("intersects");
}
}//end of loop j
}//end of loop i

//Particle constructor
function Particle(x, y) {
//OBJECT PROPERTIES
this.x = x;
this.y = y;
this.history = []; //we'll use this later to implement path trailing
this.pos = createVector(x, y); //create a vector object to store coordinate componenents
this.vel = createVector(0, 0);
this.acc = createVector(0, 0);
this.r = random(5, 10);
this.radius = this.r;
this.mass = this.r * 0.01;


//OBJECT METHODS
this.applyForce = function(force) {
//MAKE A DUPLICATE OF THE FORCE, SO ITS ORIGINAL VALUE GETS PASSED TO ALL THE OBJECTS
f = force.copy();
f.div(this.mass); // acc = force/mass
this.acc = f; // newton's 2nd law of motion
}
this.update = function() {
this.vel.add(this.acc);
this.pos.add(this.vel);
}


this.display = function() {
strokeWeight(2);
stroke("black");
fill(255);
ellipse(this.pos.x, this.pos.y, this.radius * 2, this.radius * 2);
}
this.checkEdges = function() {
if (this.pos.y > height) {
this.vel.y *= -0.9; // A little dampening when hitting the bottom
this.pos.y = height;
}
if (this.pos.y < 0) {
this.vel.y *= -0.9;
this.pos.y = 0;
}
if (this.pos.x < 0) { this.vel.x *= -0.9; this.pos.x = 0; } if (this.pos.x > width) {
this.vel.x *= -0.9;
this.pos.x = width;
}
}

this.intersects = function(other) {
var d = dist(this.pos.x, this.pos.y, other.pos.x, other.pos.y);
if (d < this.radius + other.radius) {
this.vel.x *= -1;
other.vel.x *= -1;
return true;
} else {
return false;
}
}
}

Click below to add some particles to the stage

Nice, now let’s add some extra functionality. We’ll create a path trailing effect, and also lets add the ability to remove objects from the canvas.

var particles = [];
var mass;
function setup() {
createCanvas(600, 400);
}


function mousePressed() {
particles.push(new Particle(mouseX, mouseY, mass));
}


function keyPressed() {
if (key == ' ') {
particles.splice(0, 1);
}
}


function draw() {
background(51);
background("black");
var randomForce = createVector(random(-0.01, 0.01), random(-0.01, 0.01));
for (var i = 0; i < particles.length; i++) {
if (mouseIsPressed) {
particles[i].applyForce(randomForce);
}
particles[i].display(); //places the object on canvas
particles[i].update(); // updates object properties
particles[i].checkEdges(); //check the obejct is within canvas
//check if they particles are intersecting
for (var j = 0; j < particles.length; j++) {
if (i != j && particles[i].intersects(particles[j])) {
print("intersects");
}
} //end of loop j
} //end of loop i
}


//Particle constructor
function Particle(x, y) {
//OBJECT PROPERTIES
this.x = x;
this.y = y;
this.history = []; //store the position history of each particle
this.pos = createVector(x, y); //create a vector object to store coordinate componenents
this.vel = createVector(0, 0);
this.acc = createVector(0, 0);
this.r = random(5, 10);
this.radius = this.r;
this.mass = this.r * 0.01;


//OBJECT METHODS
this.applyForce = function(force) {
//MAKE A DUPLICATE OF THE FORCE, SO ITS ORIGINAL VALUE GETS PASSED TO ALL THE OBJECTS
f = force.copy();
f.div(this.mass); // acc = force/mass
this.acc = f; // newton's 2nd law of motion
}
this.update = function() {
this.vel.add(this.acc);
this.pos.add(this.vel);


// trailing update
for (i = 0; i < this.history.length; i++) { //add easing var easing = 0.2; var diffX = mouseX - this.history[i].x; var diffY = mouseY - this.history[i].y; //this.history[i].x += random(-1,1); //this.history[i].y+=random(-1,1); //this.history[i].x += diffX*easing; // this.history[i].y+=diffY*easing; this.history[i].x += random(diffX * easing); this.history[i].y += random(diffY * easing); } //store the current position into history[] var v = createVector(this.pos.x, this.pos.y); this.history.push(v); if (this.history.length > 100) {
this.history.splice(0, 1);
}
}
this.display = function() {
strokeWeight(2);
stroke("black");
fill(255);
ellipse(this.pos.x, this.pos.y, this.radius * 2, this.radius * 2);
//draw trailing
beginShape();
strokeWeight(0.5);
stroke("white");
noFill();
for (i = 0; i < this.history.length; i++) {
var pos = this.history[i];
vertex(pos.x, pos.y);
}
endShape();
}


this.checkEdges = function() {
if (this.pos.y > height) {
this.vel.y *= -0.9; // A little dampening when hitting the bottom
this.pos.y = height;
}
if (this.pos.y < 0) {
this.vel.y *= -0.9; // A little dampening when hitting the top
this.pos.y = 0; // at the border
}
if (this.pos.x < 0) { this.vel.x *= -0.9; // A little dampening when hitting the left this.pos.x = 0; // at the border } if (this.pos.x > width) {
this.vel.x *= -0.9; // A little dampening when hitting the right
this.pos.x = width; // at the border
}
}


this.intersects = function(other) {
var d = dist(this.pos.x, this.pos.y, other.pos.x, other.pos.y);
if (d < this.radius + other.radius) {
this.vel.x *= -1;
other.vel.x *= -1;
return true;
} else {
return false;
}
}
}

Click below to add some particles to the stage, press space bar to remove them