Pure JavaScript animation.
The basic concept for these particle animations started with some basic physics after being curious what the terminal velocity of rain is.
Originally code was written in 2013 in AS3 (Flash ActionScript 3).
//simple idea of a force using distance
stage.frameRate = 60;
var particlesVector: Vector. < Sprite > =new Vector. < Sprite > ();
var framesVector: Vector. < Number > =new Vector. < Number > ();
var velocityVector: Vector. < Number > =new Vector. < Number > ();
var numOfParticles: Number = 500;
var targetX = stage.stageWidth / 2;
var targetY = stage.stageHeight / 2;
var gravity: Number = 9.8;
var forceStrength: Number = .05;
var forceSize: Number = 150;
var forceSpeed: Number = .2;
var density: Number = .1;
var airDensity: Number = 1.29;
var dragCoefficient: Number = 2.1;
var particleScaleMax: Number = 2;
var particleScaleMin: Number = .1;
var timeCounter: Number = 0;
//visit http://www.flashandmath.com/intermediate/ghost/index.html
//www.flashandmath.com/intermediate/ghost/GhostingEffect.fla
//credit for ghosting effect Dan Gries your kinda awesome :)
var bitmapData: BitmapData;
var bitmap: Bitmap;;
var displayWidth: Number;
var displayHeight: Number;
var display: Sprite;
var origin: Point;
var colorTransform: ColorTransform;
display = new Sprite();
displayWidth = stage.stageWidth;
displayHeight = stage.stageHeight;
bitmapData = new BitmapData(displayWidth, displayHeight, true, 0x00000000);
bitmap = new Bitmap(bitmapData);
var blur: BlurFilter = new BlurFilter();
blur.blurX = 5;
blur.blurY = 5;
colorTransform = new ColorTransform(0.999, 0.90, 0.70, 1);
origin = new Point(0, 0);
bitmap.x = 0;
bitmap.y = 0;
addChild(bitmap);
for (var i: Number = 0; i < numOfParticles; i++) {
var particle: Sprite = new Sprite();
particle.graphics.beginFill(0xFF0000);
particle.graphics.drawCircle(0, 0, 1);
particle.graphics.endFill();
particle.x = Math.random() * stage.stageWidth;
particle.y = Math.random() * stage.stageHeight;
particle.scaleX = Math.random() * (particleScaleMax - particleScaleMin) + particleScaleMin;
particle.scaleY = particle.scaleX;
particlesVector.push(particle);
framesVector.push(0);
velocityVector.push(0);
}
addEventListener(Event.ENTER_FRAME, animateParticles);
var forceField: Sprite = new Sprite();
forceField.graphics.lineStyle(2, 0xFFFFFFF, .75);
forceField.graphics.drawCircle(0, 0, forceSize);
forceField.graphics.endFill();
addChild(forceField);
forceField.x = targetX;
forceField.y = targetY;
var forceFieldArrow: Sprite = new Sprite();
forceFieldArrow.graphics.lineStyle(2, 0xFFFFFFF, .75);
forceFieldArrow.graphics.moveTo(0, 0);
forceFieldArrow.graphics.lineTo(forceSize / 1.5 * -1, 0);
forceFieldArrow.graphics.moveTo(forceSize / 1.5 * -1, 0);
forceFieldArrow.graphics.lineTo(forceSize / 2 * -1, forceSize / 10);
forceFieldArrow.graphics.moveTo(forceSize / 1.5 * -1, 0);
forceFieldArrow.graphics.lineTo(forceSize / 2 * -1, forceSize / 10 * -1);
forceField.addChild(forceFieldArrow);
function animateParticles(event: Event) {
timeCounter++;
for (var n: Number = 0; n < particlesVector.length; n++) {
framesVector[n] = framesVector[n] + 1;
var time: Number = framesVector[n] / 60;
var velocity: Number = .5 * (gravity * Math.pow(time, 2));
var volume_: Number = ((4 / 3) * (Math.PI * Math.pow((particlesVector[n].width / 2), 3)));
var mass: Number = density * volume_;
var weight: Number = gravity * mass;
var area: Number = Math.PI * Math.pow(particlesVector[n].width / 2, 2);
var drag: Number = dragCoefficient * (.5 * (airDensity * Math.pow(velocity, 2))) * area;
var terminalVelocity: Number = (weight - drag) / mass;
var forceDrag: Number = dragCoefficient * (.5 * (airDensity * Math.pow(forceSpeed, 2))) * area;
var forceVelocity: Number = forceDrag / mass;
if (terminalVelocity >= 0) {
velocityVector[n] = velocity;
}
particlesVector[n].y += velocityVector[n];
var distance: Number = Math.sqrt(Math.pow(particlesVector[n].x - forceField.x, 2) + Math.pow(particlesVector[n].y - forceField.y, 2));
var pullStrength: Number = (forceField.width / 2 - distance) * forceStrength;
var forceAngle: Number = forceField.rotation * Math.PI / 180;
if (distance < forceField.width / 2) {
particlesVector[n].y += forceVelocity * Math.sin(forceAngle) * -1 * pullStrength;
particlesVector[n].x += forceVelocity * Math.cos(forceAngle) * -1 * pullStrength;
}
if (particlesVector[n].x > stage.stageWidth || particlesVector[n].x < 0 || particlesVector[n].y > stage.stageHeight) {
framesVector[n] = 0;
particlesVector[n].x = Math.random() * stage.stageWidth;
particlesVector[n].y = particlesVector[n].height * -1;
particlesVector[n].scaleX = Math.random() * (particleScaleMax - particleScaleMin) + particleScaleMin;
particlesVector[n].scaleY = particlesVector[n].scaleX;
}
display.addChild(particlesVector[n]);
}
if (timeCounter % 60 == 0) {
var clockInterval: Number = 360 / 60;
forceField.rotation += clockInterval;
}
bitmapData.applyFilter(bitmapData, bitmapData.rect, origin, blur);
bitmapData.colorTransform(bitmapData.rect, colorTransform);
bitmapData.draw(display);
}