Just a few more posts on particles, I promise.

This article is part of my ongoing series of medium difficulty ThreeJS tutorials. I’ve long wanted something in between the intro “How to draw a cube” and “Let’s fill the screen with shader madness” levels. So here it is.

So far we have used only one texture image for every effect. This one:

A nearly invisible round and blurry texture

This one works fine for a lot of effects, but there’s so much more we could do using different textures. I downloaded this amazing set of images from master game art creator Kenney. Just look at all of the cool images in there!

Amazing Particle Textures from Kenney.nl

Now we can let our imagination run wild. Here are five different examples using one of the images from Kenney, along with the settings I used for each.

Particles of Magic

Magical effect

Live demo

const options = {

position: new THREE.Vector3(0,2,0),

positionRandomness: 0.0,

velocity: new THREE.Vector3(0.0, -0.5, 0.0),

velocityRandomness: 1.0,

acceleration: new THREE.Vector3(0.0,0.0,0.0),



color: new THREE.Color(1.0,0.0,1.0),

endColor: new THREE.Color(0.0,1.0,1.0),

colorRandomness: 0.0,



lifetime: 2.0,

fadeIn:0.001,

fadeOut:0.001,

size: 60,

sizeRandomness: 0.0,

}



parts = new GPUParticleSystem({

maxParticles: 10000,

particleSpriteTex: texture,

blending: THREE.AdditiveBlending,

onTick:(system,time) => {

for (let i = 0; i < 10; i++) {

options.velocity.set(rand(-1,1),rand(-1,1), 0)

system.spawnParticle(options);

}

}})

A Rainbow of Stars

A rainbow of stars

Live demo

const options = {

position: new THREE.Vector3(0,2,0),

positionRandomness: 0.0,

velocity: new THREE.Vector3(0.0, -0.5, 0.0),

velocityRandomness: 1.0,

acceleration: new THREE.Vector3(0.0,-1.0,0.0),



color: new THREE.Color(1.0,0.0,1.0),

endColor: new THREE.Color(0.0,1.0,1.0),



lifetime: 20.0,

fadeIn:0.001,

fadeOut:0.001,

size: 100,

sizeRandomness: 0.0,

}



parts = new GPUParticleSystem({

maxParticles: 10000,

particleSpriteTex: texture,

blending: THREE.AdditiveBlending,

onTick:(system,time) => {

for (let i = 0; i < 1; i++) {

options.color.setHSL(rand(0,1),1.0,0.5)

options.endColor = options.color

options.position.set(rand(-2.5,-2),2,0)

options.velocity.set(rand(1.2,1.4),1, 0)

system.spawnParticle(options);

}

}})

Periodic Fireballs

Live Demo

This one is slightly different in that it uses a conditional to decide when to fire. The code (floor(time)%6 === 0) means every six seconds it will fire continuously for one second.

const options = {

position: new THREE.Vector3(0,2,0),

positionRandomness: 0.0,

velocity: new THREE.Vector3(0.0, 0, 0.0),

velocityRandomness: 1.0,

acceleration: new THREE.Vector3(0.0,0.0,0.0),



color: new THREE.Color(1.0,0.0,1.0),

endColor: new THREE.Color(0.0,1.0,1.0),



lifetime: 2.0,

fadeIn:0.001,

fadeOut:3.0,

size: 60,

sizeRandomness: 0.0,

}



parts = new GPUParticleSystem({

maxParticles: 10000,

particleSpriteTex: texture,

blending: THREE.AdditiveBlending,

onTick:(system,time) => {

if(Math.floor(time) %6 == 0) for (let i = 0; i < 6; i++) {

options.color.setHSL(0.1, 1.0, 0.5)

options.endColor.setHSL(0.0, 0.0, 0.8)

options.position.set(0,0,0)

const s = 0.05

options.velocity.set(rand(-s,s), rand(-s,s), rand(-s,s))

system.spawnParticle(options);

}



}})

Transporter Effect

Transporter-like effect

Live Demo

const options = {

position: new THREE.Vector3(0,2,0),

positionRandomness: 0.0,

velocity: new THREE.Vector3(0.0, 0, 0.0),

velocityRandomness: 1.0,

acceleration: new THREE.Vector3(0.0,0.0,0.0),



color: new THREE.Color(1.0,0.0,1.0),

endColor: new THREE.Color(0.0,1.0,1.0),



lifetime: 2.0,

fadeIn:0.001,

fadeOut:3.0,

size: 200,

sizeRandomness: 0.0,

}



parts = new GPUParticleSystem({

maxParticles: 10000,

particleSpriteTex: texture,

blending: THREE.AdditiveBlending,

onTick: (system, time) => {

if(Math.floor(time/1) % 2 === 0) {

options.color.setHSL(0.1, 1.0, 0.5)

options.endColor.setHSL(0.0, 0.0, 0.8)

options.position.set(0, 0, 0)

const s = 0.05

options.velocity.set(rand(-s, s), rand(-s, s), rand(-s, s))

system.spawnParticle(options);

}



}

})

Sparkler Ring

a ring shaped sparkler

Live Demo

Notice the code below calculates the starting position from a random angle using sin and cos. This is what creates the circle shape. A square or other shape would be just as easy.

const options = {

position: new THREE.Vector3(0,2,0),

positionRandomness: 0.0,

velocity: new THREE.Vector3(0.0, 0, 0.0),

velocityRandomness: 1.0,

acceleration: new THREE.Vector3(0.0,0.0,0.0),



color: new THREE.Color(1.0,0.0,1.0),

endColor: new THREE.Color(0.0,1.0,1.0),



lifetime: 2.0,

fadeIn:0.001,

fadeOut:3.0,

size: 30,

sizeRandomness: 0.0,

}



parts = new GPUParticleSystem({

maxParticles: 10000,

particleSpriteTex: texture,

blending: THREE.AdditiveBlending,

onTick: (system, time) => {

for(let i=0; i<10; i++) {

options.color.setHSL(0.1, 1.0, 0.5)

options.endColor.setHSL(0.0, 0.0, 0.8)

const theta = rand(0,Math.PI*2)

const size = 0.4

options.position.set(Math.sin(theta)*size,Math.cos(theta)*size, 0)

const s = 0.02

options.velocity.set(rand(-s, s), rand(-s, s), rand(-s, s))

system.spawnParticle(options);

}

}

})

Until Next Time

Remember, if you are working on a cool WebVR experience that you’d like to have showcased right in Firefox Reality, let me know.