2013-08-11

Canvas Disco Rain

Intro

Another post in the "canvas drawing series". You can look up the basics of drawing etc. in the previous posts:

  1. Canvas Crossroad
  2. Canvas Balloons
  3. Canvas Bus
  4. Canvas Trains
  5. Canvas, circular motions and networks
  6. Canvas Wipers
  7. DIY Canvas Fountain
  8. Canvas Random Spherical Effects
  9. Canvas Ant Colony Optimization
  10. Canvas Space Battle
  11. Canvas Simple Game

Canvas Disco Rain

The idea behind it is pretty random. I saw a couple of demos about rain falling and stars moving in the space and thought ... why not make it a bit more "funky" :) Every drop is glowing while falling and there is an option to show all drops in the same color or display them random.

Flash math

The flash is modeled with a help of a logarithmic function. The flash starts at it's maximum size and then decays reaching 0. When the flash reaches 0 it starts to glow again. To add a bit more noise to example it expands to a random width. There is also a Flash Factor so that we can influence the general length of a burst.

 this.r = flashfactor * (Math.log(this.r) / Math.LN10);

 if (this.r <= 0) {
  this.r = randomMax(radiusmax);
 }

Flash looks

Every flash is brightest at it's middle and then darker the further away from the center. To achieve this effect a gradient is used:

 ctx.beginPath();

 var fillStyle = ctx.createRadialGradient(
  this.x,
  this.y, 
  this.r * 0.2, 
  this.x, 
  this.y, 
  this.r
 );
 
 fillStyle.addColorStop(0, this.color);
 fillStyle.addColorStop(1, particleBackground);

 ctx.fillStyle = fillStyle;
 ctx.beginPath();
 ctx.arc(this.x, this.y, this.r, 0, Math.PI * 2);
 ctx.fill();

The light properties of flash emitting body, human eye or camera lens cause us to perceive the flashes with a cross centered in the middle of the flash. This animation rotates this angle to add a bit more randomness to the example (it looks nicer). The cross drawing code looks like this:

 ctx.strokeStyle = this.color;
 
 ctx.beginPath();
 ctx.moveTo(
  this.x - Math.cos(this.angle) * this.r,
  this.y - Math.sin(this.angle) * this.r
 );
 ctx.lineTo(
  this.x + Math.cos(this.angle) * this.r,
  this.y + Math.sin(this.angle) * this.r
 );
 ctx.stroke();

 ctx.beginPath();
 ctx.moveTo(
  this.x - Math.cos(this.angle + Math.PI / 2) * this.r,
  this.y - Math.sin(this.angle + Math.PI / 2) * this.r
 );
 ctx.lineTo(
  this.x + Math.cos(this.angle + Math.PI / 2) * this.r,
  this.y + Math.sin(this.angle + Math.PI / 2) * this.r
 );
 ctx.stroke();

We use gradients, but the flash is in the end just a circle shrinking. That causes one flashes light to "cover" the light of the other flash and we all know this is not happening in the real world. To compensate for that we use the globalCompositeOperation on the 2d canvas context. Explaining this is a bit out of the scope of this post, put there is a very nice code pen explaining that made by aptary. Disco rain uses 'lighter' mode:

 ctx.globalCompositeOperation = 'lighter';

And here's my example on:
CSSDeck
Codepen

No comments: