Three.js University Sprite wave of particles

Create a wave of particles with Three.js

Preparation of the environment – Global variables

Let’s start by creating some global variables :

// Wave configuration
var wavespeed = 1;
var wavewidth = 200;
var waveheight = 100;
var objects_margin = 20;

//Array
var waveobjects = new Array();
  • wavespeed – Speed of the waves
  • wavewidth – Width of the waves
  • waveheight – Height of the waves
  • objects_margin – Spacing between particles
  • waveobjects – List of particles in the scene

Preparation of the environment – Creation of the particles

Then, using a nesting of for loops, we create the particles composing our wave.
Our group of particles will be ordered in square via a simple increment on the X and Z axes.

Each particle is a Three.js Sprite instance.

// ---------------- PARTICLES ----------------
const loader = new THREE.TextureLoader();
var particleTexture = loader.load( '../Particles/firefly.png' );
var spriteMaterial = new THREE.SpriteMaterial( { map: particleTexture, transparent : true, opacity :1, color: 0x000000 } );
                  
for ( var x = 0; x < 100; x ++ )
{
      for ( var y = 0; y < 100; y ++ )
      {            
            // Sprite creation
            var mesh = new THREE.Sprite( spriteMaterial );
            
            mesh.scale.set(10,10,10);                 // scale
            mesh.position.x = x * objects_margin;    // POSITION X
            mesh.position.y = 0;
            mesh.position.z = y * objects_margin;    //POSITION Y
            scene.add( mesh );
            waveobjects.push(mesh); 
      }
}

As explained, the position of each Sprite on the X and Z axes depends on its creation order in the for loops.

The Particles we create, instances of Sprite, are added to the scene and referenced in the waveobjects structure (JavaScript Array).

Wave creation

The motion of each particle is calculated using the Math.sin function, its position in the group and the elapsed time.
Thus, the combination of these three parameters is used to calculate the position of each particle in the scene, and thus create a wave animation.

Three.js Clock and Math.sin

So, to keep track of the elapsed time, we create a Three.js Clock class instance .

clock = new THREE.Clock();

Then, in our main loop :

function animate()
{
    var delta = clock.getDelta();
    var elapsed = clock.elapsedTime;

    [...]
}

Note that the elapsed variable contains the total number of elapsed seconds since the launch of the application.

Particle animation

Still in the main loop, we loop over the list of particles in order to modify the positions one by one :

for(var i = 0 ; i < waveobjects.length ; i++)
{
    waveobjects[i].position.y = Math.cos( (elapsed + (waveobjects[i].position.x /wavewidth) + (waveobjects[i].position.z /wavewidth) ) *wavespeed ) * waveheight;
}

As explained, we determine the new position of each particle by its position on the X and Z axes, the elapsed time and the Math.sin method.

Final code and result

// Wave configuration
var wavespeed = 1;
var wavewidth = 200;
var waveheight = 100;
var objects_margin = 20;

//Array
var waveobjects = new Array();

[...]

clock = new THREE.Clock();

[...]

// ---------------- PARTICLES ----------------

const loader = new THREE.TextureLoader();
var particleTexture = loader.load( '../Particles/firefly.png' );
var spriteMaterial = new THREE.SpriteMaterial( { map: particleTexture, transparent : true, opacity :1, color: 0x000000 } );
				  
for ( var x = 0; x < 100; x ++ )
{
	  for ( var y = 0; y < 100; y ++ )
	  {			
			// Sprite creation
			var mesh = new THREE.Sprite( spriteMaterial );
			
			mesh.scale.set(10,10,10); 				// scale
			mesh.position.x = x * objects_margin;	// POSITION X
			mesh.position.y = 0;
			mesh.position.z = y * objects_margin;	//POSITION Y
			scene.add( mesh );

			waveobjects.push(mesh); 
	  }
}

[...]

function animate()
{
	var delta = clock.getDelta();
	var elapsed = clock.elapsedTime;
        
	requestAnimationFrame( animate );

	for(var i = 0 ; i < waveobjects.length ; i++)
	{

	waveobjects[i].position.y = Math.cos( (elapsed + (waveobjects[i].position.x /wavewidth) + (waveobjects[i].position.z /wavewidth) ) *wavespeed ) * waveheight;
	}

	renderer.render( scene, camera );
}

2 comments

Comments are closed.