toggle menu

My Blogs

Creating a particle system using ES6 and p5.js


In this article, we are going to explore what a particle system is and how to create them in HTML5 using p5.js.


First, let's define and know what a particle system is.


Particle System is a term coined by William T. Reeves.

William "Bill" Reeves Pixar.jpg

William "Bill" Reeves (born May 5, 1959) is a Canadian animator and technical director known for working with John Lasseter on the animated shorts Luxo Jr. and The Adventures of Andre and Wally B.

Reference: https://en.wikipedia.org/wiki/William_Reeves_(animator)


According to Wikipedia :

particle system is a technique in game physics motion graphics, and computer graphics that uses a large number of very small sprites3D models, or other graphic objects to simulate certain kinds of "fuzzy" phenomena, which are otherwise very hard to reproduce with conventional rendering techniques - usually highly chaotic systems, natural phenomena, or processes caused by chemical reactions.

- According to Allen Martin here

The term, particle system is loosely defined in computer graphics. It has been used to describe modeling techniques, rendering techniques, and even types of animation


For me, Particle System is a group of particles banded together to give users feedback or emphasis based on the action they have made.


So you might be wondering what does a particle system looks like?


Image result for wondering


This is one of the pictures that I got from google, that best describes what a particle system is.

Image result for particle system in games

Now you might be thinking how does this weird looking animation or graphics, can be used?


Here's a sample video on what it looks when a game doesn't have a particle system:


If we see it the game looks bland and boring.


But if we add a particle system to it, it should look like this:


Now that we know what a particle system is. Let's create our own Particle System!

Image result for excited


First, let us download p5.js here


Under the Single Files, section click p5.js.

After that let's create our project folder, and paste the p5.js, downloaded file inside the project folder

After that create 2 files, named: index.html and sketch.js, then open the project folder inside our text editor.

The contents should be like this:




Let's now put contents inside our index.html:


<html lang="en">

<head>
<meta charset="UTF-8/">
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<meta http-equiv="X-UA-Compatible" content="ie=edge"/>
<title>Documenttitle>
>

<body>

<script src="p5.js"></script>
<script src="sketch.js"></script>
body>

html>


index.html's inside this file, we just need to reference our 2 javascript files.

Now let's proceed writing our p5.js codes

Inside our sketch.js file, write these codes:

function setup() {
createCanvas(innerWidth, innerHeight);
}

function draw() {
background(0);
}


Now let's discuss the two functions that we see on the screen:

  • setup - is being called once after the p5.js has been loaded
  • draw - is being called every frame after setup function has been called, p5.js by default has 60 frames per second meaning that draw is being called 60 times per second.

createCanvas - creates a canvas on our screen, with set width and height, by default we are setting it as the width and height of our browser's window.

background - defines the color of our canvas, 0 denotes that we have a black background.


Before creating our particle system, what properties should a particle system contain?

According to William T. Reeves each particle in the particle system should have the following properties

  • Position - (x, y, z) position of the particle system in computer screen
  • Velocity - denotes the speed and the direction of the particle system
  • Color - color of each particles
  • Lifetime - time when all particles should disappear 
  • Age - number that alters particle's size and transparency
  • Shape - shape of the particle
  • Size -  size of the paritcle
  • Transparency - opacity value of each particles


Now let's create our particle class having these properties, using ES6 class.

Create a new file named particle.js, and put the contents below:

class Particle {

constructor(pos, vel, col, life, shape, size, opacity, force) {
this.pos = pos; // Position
this.vel = vel; // Velocity
this.col = col; // Color
this.life = life; // Lifetime
this.shape = shape; // Shape p5.js 2d only supports rect and ellipse
this.size = size; // size
this.opacity = opacity; // transparency
    this.force = force; // additional property for adding gravity
}

}


After that let's import our particle.js file into our index.html

<script src="p5.js"></script>
<script src="sketch.js"></script>
<script src="particle.js"></script>


Now let's create a function to display our particle class.

class Particle {

constructor(pos, vel, col, life, shape, size, opacity, force) {
this.pos = pos;
this.vel = vel;
this.col = col;
this.life = life;
this.origLife= life; // saves the originally set life variable
this.shape = shape;
this.size = size;
this.opacity = opacity;
this.force = force;
}

render() {
push(); // push the state
translate(this.pos.x, this.pos.y); // translate the current screen position to x, y
stroke(this.col, this.opacity); // stroke color and its transparency
noFill(); // inidicates that our shape should have no fill color inside

if (this.shape === 'rect') {
rect(0, 0, this.size.x, this.size.y); // for displaying rect takes, x, y, width, height
} else {
ellipse(0, 0, this.size.x, this.size.y); // for displaying ellipse takes, x, y, width, height
}

pop(); // pops our state
}

}


Now let's create the Particle class inside our sketch.js

let particle;

function setup() {
createCanvas(innerWidth, innerHeight);

particle = new Particle(
createVector(width / 2, height / 2), // position at the center of the window
createVector(0, 1), // velocity
color(255, 87, 120), // color
100, // lifetime
'rect', // shape
createVector(15, 15), // size width and height
255, // transparency from 0 to 255
createVector(0, 0) // force
);
}

function draw() {
background(0);
particle.render(); // display the particle
}


After we reload our page we should see something like this:


Now let's make our particle move or be animated.


class Particle {

constructor(pos, vel, col, life, shape, size, opacity, force) {
this.pos = pos;
this.vel = vel;
this.col = col;
this.life = life;
this.origLife = life;
this.shape = shape;
this.size = size;
this.opacity = opacity;
this.force = force;
}

render() {
push();
translate(this.pos.x, this.pos.y);
stroke(this.col, this.opacity);
noFill();

const lifePercentage = this.life / this.origLife;

if (this.shape === 'rect') {
rect(0, 0, this.size.x * lifePercentage, this.size.y * lifePercentage);
} else {
ellipse(0, 0, this.size.x * lifePercentage, this.size.y * lifePercentage);
}

pop();
}

update() {
this.vel.add(this.force);
this.pos.add(this.vel);
this.vel.limit(3); // limits the velocity speed to 3
this.life--; // decrements life as time passes
this.opacity = map(this.life, 0, this.origLife, 0, 255); // age opacity as life decreases
}

isDead() {
return this.life <= 0;
}

}


Let's discuss the codes here one by one. 

Update():

update() {
this.vel.add(this.force);
this.pos.add(this.vel);
this.vel.limit(3); // limits the velocity speed to 3
this.life--; // decrements life as time passes
this.opacity = map(this.life, 0, this.origLife, 0, 255); // age opacity as life decreases
}


add - adds two vector together, these two lines are responsible for acceleration and movement of our particle.

this.vel.add(this.force);
this.pos.add(this.vel);


As you have noticed we don't have yet the age property of the particle that according to William T. Reeves, should have.

These two lines of code are responsible for that:

As we defined earlier age, defines the alteration of transparency and size of a particle

This line of code denotes the shrinkage of a particle as life decreases.


const lifePercentage = this.life / this.origLife; // gets the life percentage

if (this.shape === 'rect') {
rect(0, 0, this.size.x * lifePercentage, this.size.y * lifePercentage);
} else {
ellipse(0, 0, this.size.x * lifePercentage, this.size.y * lifePercentage);
}

This line of code denotes the decrease of opacity value of a particle as life decreases.

this.life--; // decrements life as time passes
this.opacity = map(this.life, 0, this.origLife, 0, 255); // age opacity as life decreases


After that let's call the Particle class' update function inside our sketch.js class.

function draw() {
background(0);
particle.update();
particle.render();
}


Now we should be able to see something like this: 


Cool, now let's move to create our ParticleSystem class, it would be responsible for creating multiple particles and animating them.

Create a new file named particle-system.js, and put the contents below:

class ParticleSystem {

constructor(pos, life, size, opacity, force, count, generationSpeed) {
this.pos = pos;
this.life = life;
this.origLife = life;
this.size = size;
this.opacity = opacity;
this.force = force;
this.count = count;
this.generationSpeed = generationSpeed;
this.particles = [];
this.generateParticles();
}

generateParticles() {
this.particles = [
...Array(this.count).fill(1).map(n => new Particle(
this.pos.copy(),
createVector(random(-1, 1), random(-1, 1)),
color(random(180, 255), random(100, 255), random(100, 255)),
this.life,
random(['rect', 'ellipse']),
this.size,
this.opacity,
this.force
)),
...this.particles
]
}

update() {
this.particles = this.particles.filter(p => !p.isDead());
if (frameCount % this.generationSpeed === 0) {
this.generateParticles();
}
}

render() {
this.particles.forEach(p => {
p.update();
p.render();
});
}
}


Let's discuss the codes here one by one. 

We have added here two new properties inside the particle-system.js:

  • generationSpeed - frame speed when should we create number of particles.
  • count - n number of particles per generation 

For the following codes, I will explain it's use thru comments

generateParticles():

generateParticles() {
this.particles = [
...Array(this.count).fill(1).map(n => new Particle(
this.pos.copy(), // copy function clones the position object to avoid reference copy to the source
createVector(random(-1, 1), random(-1, 1)), // creates random velocity between -1,1
color(random(180, 255), random(100, 255), random(100, 255)), // creates random reddish color
this.life,
random(['rect', 'ellipse']), // selects a random shape between rect or ellipse
this.size,
this.opacity,
this.force
)),
...this.particles
] // concatenate two arrays
}


update():

update() {
this.particles = this.particles.filter(p => !p.isDead());// checks if particle is dead and remove it in array
if (frameCount % this.generationSpeed === 0) { // check if the current frameCount is in generationSpeed
this.generateParticles();
}
}


After that let's import our particle-system.js file inside our index.html:

<script src="p5.js"></script>
<script src="sketch.js"></script>
<script src="particle.js"></script>
<script src="particle-system.js"></script>


And now let's create a particle system every mouse click.

Let's update our sketch.js file:

let particleSystems = []; // Holds the particle system that we will create

p5.disableFriendlyErrors = true; // for optimization

function setup() {
createCanvas(innerWidth, innerHeight);
}

function draw() {
background(0);
renderParticleSystem(); // displays particle system
}

function mousePressed() {
particleSystems.push(
new ParticleSystem(
createVector(mouseX, mouseY),// creates particle system based mouse location
80, // particle dies every 80th frame
createVector(10, 10), // size
255, // transparency 255
createVector(0, -0.05), force make the particle going up
10, // count of particle per generation
10 // number of frames per generation
)
);
}

function renderParticleSystem() {
particleSystems.forEach(pS => {
pS.update();
pS.render();
});
}


After that, it should look like this:


Cool, now we're done, you are free to use the codes and use them.


Image result for accomplished picture 


Try it https://www.openprocessing.org/sketch/745356

Download the code: https://github.com/onecompileman/particle-system



Author

MY Profile

Stephen G. Vinuya
Aug 17,2019



Leave a comment below...