<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
<button id="btnRepeat" style="position: absolute; margin: 5px;">Repeat</button>
body {
  overflow: hidden;
  margin: 0;
}
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(
  60,
  window.innerWidth / window.innerHeight,
  1,
  1000
);
camera.position.set(0, 8, 13);
var renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0x101010);
document.body.appendChild(renderer.domElement);
window.addEventListener("resize", onWindowResize, false);

var controls = new THREE.OrbitControls(camera, renderer.domElement);

var grid = new THREE.GridHelper(10, 10);
//grid.position.z = -15;
grid.rotation.x = Math.PI * 0.5;
scene.add(grid);

//var pointsHolder = new THREE.Group();

var r = 0.5; // radius
var delayMax = 1; // seconds
var colors = [
  0xd50f30,
  0x039bdc,
  0xfc5411,
  0x1a2683,
  0xfbe23d,
  0x12923b
];
var c = new THREE.Color();
var MAX_POINTS = 5000;
var pointsCount = 0;
var pointsInitDist = 15;
var points = []; //3

var pointsDest = []; //3
var pointsPlaneDist = 5;
var sideSize = Math.tan(camera.fov * 0.5 * THREE.Math.DEG2RAD) * pointsPlaneDist;
sideSize *= window.innerWidth > window.innerHeight ? camera.aspect : 1;

var delay = [];  //1
var color = [];  //3
while( pointsCount < MAX_POINTS){
  let vec = new THREE.Vector3(THREE.Math.randFloat(-r, r), 
                              THREE.Math.randFloat(-r, r), 
                              THREE.Math.randFloat(-r, r)
                             );
  if (vec.length() <= r) {
    vec.z -= pointsInitDist;
    points.push(vec);
    
    pointsDest.push(
      THREE.Math.randFloat(-sideSize, sideSize),
      THREE.Math.randFloat(-sideSize, sideSize),
      - pointsPlaneDist
    );
    
    delay.push(THREE.Math.randFloat(-delayMax,0));
    c.set(colors[THREE.Math.randInt(0, colors.length - 1)]);
    color.push(c.r, c.g, c.b);
    
    pointsCount++;
  }
}

console.log(pointsDest);
console.log(delay);

var pointsGeom = new THREE.BufferGeometry().setFromPoints(points);
pointsGeom.setAttribute("color", new THREE.BufferAttribute(new Float32Array(color), 3));
pointsGeom.setAttribute("pointsDest", new THREE.BufferAttribute(new Float32Array(pointsDest), 3));
pointsGeom.setAttribute("delay", new THREE.BufferAttribute(new Float32Array(delay), 1));


var pointsMat = new THREE.PointsMaterial(
  {
  vertexColors: THREE.VertexColors, 
    size: 0.2, 
    map: new THREE.TextureLoader().load("https://threejs.org/examples/textures/sprites/ball.png", tex => {
      tex.center.setScalar(0.5);
      tex.rotation = -Math.PI * 0.5;
    }), 
    alphaTest: 0.5});

var uniforms = {
  time: {value: 0},
  delayMax: {value: delayMax},
  duration: {value: 0.5}, // seconds to move from init to dest (each particle)
  fall: {value: sideSize * 2.1},
  fallDelay: {value: .5},
  fallDuration: {value: 1} // seconds
}
pointsMat.onBeforeCompile = shader => {
  shader.uniforms.time = uniforms.time;
  shader.uniforms.delayMax = uniforms.delayMax;
  shader.uniforms.duration = uniforms.duration;
  shader.uniforms.fall = uniforms.fall;
  shader.uniforms.fallDelay = uniforms.fallDelay;
  shader.uniforms.fallDuration = uniforms.fallDuration;
  console.log(shader.vertexShader);
  shader.vertexShader = `
  uniform float time;
  uniform float delayMax;
  uniform float duration;
  uniform float fall;
  uniform float fallDelay;
  uniform float fallDuration;
  
  attribute vec3 pointsDest;
  attribute float delay;

  varying float vRatio;
` + shader.vertexShader;
  shader.vertexShader = shader.vertexShader.replace(
    `#include <begin_vertex>`,
    `#include <begin_vertex>
    
    float t = time + delay;
    t = t < 0. ? 0. : t;
    //t = 0.05;

    float tRatio = 0.;
    tRatio = clamp( t / duration, 0., 1.);
    vRatio = step(0.25, tRatio);
    transformed = mix(transformed, pointsDest, tRatio);
    
    float fallStart = delayMax + duration + fallDelay; 

    if (time >= fallStart) {
      float timeDiff = time - fallStart;
      float fRatio = clamp(timeDiff / fallDuration, 0., 1.);
      transformed.y -= fall * fRatio;
    }
    
`
  );
  shader.vertexShader = shader.vertexShader.replace(
    `gl_PointSize = size;`,
    `gl_PointSize = size * smoothstep(0.25, 0.5, tRatio);`
  );
  shader.fragmentShader = `
  varying float vRatio;
` + shader.fragmentShader;
  shader.fragmentShader = shader.fragmentShader.replace(
    `#include <clipping_planes_fragment>`,
    `
    if (floor(vRatio + 0.5) < 1.) discard;
    #include <clipping_planes_fragment>`
  );
}

var points = new THREE.Points(pointsGeom, pointsMat);
scene.add(points);


var clock = new THREE.Clock();
var t = 0;

btnRepeat.addEventListener("click", event => {t = 0;}, false);

renderer.setAnimationLoop(() => {
  
  t += clock.getDelta();
  uniforms.time.value = t;
  points.position.copy(camera.position);
  points.quaternion.copy(camera.quaternion);
  renderer.render(scene, camera);
  
});

function onWindowResize() {
  
  var width = window.innerWidth;
  var height = window.innerHeight;
  camera.aspect = width / height;
  camera.updateProjectionMatrix();
  renderer.setSize(width, height);
  
}
Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.