<div id="app"></div>

class ExplosionsSequence extends React.Component{
    constructor(props){
        super(props);
        this.canvasRef = React.createRef();
        this.clock = new THREE.Clock();
        this.simplex = new SimplexNoise();
    }

    componentDidMount = () => {
        this.init();
    }
    init = () => {
        const rand = (min,max) => min + Math.random()*(max-min);
        const canvas = this.canvasRef.current;
        this.color = new THREE.Color();
        //scene
        this.scene = new THREE.Scene();
       // this.texture = this.generateTexture();
        //renderer
        this.renderer = new THREE.WebGLRenderer({ canvas, antialias: true, alpha: true });
        this.renderer.setClearColor(0x666666);
        this.renderer.setSize( window.innerWidth, window.innerHeight )
        document.body.appendChild(this.renderer.domElement);
        //camera
        this.camera = new THREE.PerspectiveCamera(100, 0, 0.0001, 10000);
          this.camera.position.z = 30; 
          this.camera.position.x = 0;
          this.camera.position.y = 0;
          this.camera.updateProjectionMatrix();
          // this.controls = new THREE.OrbitControls(this.camera, this.renderer.domElement);
          // this.controls.enableDamping = true;
          // this.controls.dampingFactor = 0.2;
          // this.controls.enableKeys = false;
 
        this.gridHelper = new THREE.GridHelper(300, 60, 0xffffff, 0xffffff);
        this.gridHelper.material.transparent = true;
        this.gridHelper.material.opacity = .3;
        this.scene.add(this.gridHelper);


//         this.axisHelper = new AxisHelper(150, .6);
//         this.scene.add(this.axisHelper);

        this.camera.lookAt(new THREE.Vector3());
          //lights
          let hemiLight = new THREE.HemisphereLight(0xffffff,  0.91);
            hemiLight.position.set(0, 50, 0);
            // Add hemisphere light to scene
            this.scene.add(hemiLight);
  
  
            // geometry
            this.geometry = new THREE.BufferGeometry();
            this.particles = 10;
            const radius = 200;
            this.positions = new Float32Array(this.particles * 3);
			this.colors = new Float32Array(this.particles * 3);
            this.sizes = new Float32Array(this.particles);
            this.size = 1000;
            this.parts = [];
            //vec3 attributes
            this.geometry.setAttribute( 'position', new THREE.Float32BufferAttribute( this.positions, 3 ) );
            this.geometry.setAttribute( 'color', new THREE.Float32BufferAttribute( this.colors, 3 ) );
            this.geometry.setAttribute( 'size', new THREE.Float32BufferAttribute( this.sizes, 1 ));
       
            for ( var i = 0; i < this.particles; i ++ ) {
                let size = rand(10, 80);
                this.color.setHSL( i / this.particles, 1.0, 0.5 );
                let part = {
                    offset: 0,
                    position: new THREE.Vector3(
                        rand(-this.size / 2, this.size / 2),
                        rand(-this.size / 2, this.size / 2),
                        rand(-this.size / 2, this.size / 2)
                    ),
                    baseSize: size,
                    size: size,
                    r: this.color.r,
                    g: this.color.g,
                    b: this.color.b,
                    a: 0.6,
                    life: 2,
                    decay: rand(0.05, 0.15),
                    firstRun: true
                };
                this.parts.push(part);
            }
     
            
            this.material = new THREE.ShaderMaterial( {
                uniforms: {
                    pointTexture: { value: new THREE.TextureLoader().load( "../3d/particle.png" ) }
                    
                },
                vertexShader:'attribute float size; \
                        varying vec3 vColor; \
                        void main() {\
                            vColor = color;\
                            vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );\
                            gl_PointSize = size * ( 300.0 / -mvPosition.z );\
                            gl_Position = projectionMatrix * mvPosition;\
                        }',
                fragmentShader: 'uniform sampler2D pointTexture;\
                varying vec3 vColor;\
                void main() {\
                gl_FragColor = vec4( vColor, 0.8 );\
                gl_FragColor = gl_FragColor * texture2D( pointTexture, gl_PointCoord ); \
                }',
                blending: THREE.AdditiveBlending,
                depthTest: false,
                transparent: true,
                vertexColors: true
             });
            this.particleSystem = new THREE.Points(this.geometry, this.material);

            this.scene.add(this.particleSystem);
            this.updateParticleAttributes(true, true, true);
           
            window.addEventListener( 'resize', this.onWindowResize, false );
            this.update();

    }
    generateTexture=() =>{
		let c = document.createElement('canvas');
		let ctx = c.getContext('2d');
		let size = 256;
		c.width = size;
		c.height = size;

		let gradient = ctx.createRadialGradient(size * 0.5, size * 0.5, 0, size * 0.5, size * 0.5, size * 0.4);
		gradient.addColorStop(0, 'hsla(100, 80%, 60%, 1)');
		gradient.addColorStop(1, 'hsla(100, 80%, 60%, 0.6 )');

		ctx.beginPath();
		ctx.arc(size / 2, size / 2, size / 2, 0, Math.PI * 2);
		ctx.fillStyle = gradient;
		ctx.fill();

        let texture = new THREE.Texture(c);
		texture.needsUpdate = true;

		return texture;
	}
    updateParticleAttributes=(color, position, size) =>{
		let i = 0;
		while(i<this.particles) {
			let part = this.parts[i];
			if(color) {
				this.colors[i * 4 + 0] = part.r;
				this.colors[i * 4 + 1] = part.g;
				this.colors[i * 4 + 2] = part.b;
			}
			if(position) {
				this.positions[i * 3 + 0] = part.position.x;
				this.positions[i * 3 + 1] = part.position.y;
				this.positions[i * 3 + 2] = part.position.z;
			}
			if(size) {
				this.sizes[i] = part.size;
            }
            i++;
		}

		if(color) {
			this.geometry.attributes.color.needsUpdate = true;
		}
		if(position) {
			this.geometry.attributes.position.needsUpdate = true;
		}
		if(size) {
			this.geometry.attributes.size.needsUpdate = true;
        }
        this.geometry.computeBoundingSphere();
	}
    update=()=>{
        requestAnimationFrame(this.update);
        const rand = (min,max) => min + Math.random()*(max-min);
          let noiseTime = this.clock.getElapsedTime() * 0.0008;
          let noiseVelocity = this.simplex.noise2D(0,1);
         const noiseScale = 0.01;
        for ( var i = 0; i < this.particles; i ++ ) {
            let part = this.parts[i];
            // let xScaled = part.position.x * noiseScale;
			// let yScaled = part.position.y * noiseScale;
			// let zScaled = part.position.z * noiseScale;
            // let noise1 = this.simplex.noise4D(
            //     xScaled ,
			// 	yScaled,
			// 	zScaled,
			// 	50 + noiseTime
            // )* 0.5 + 0.5;
            // let noise2 = this.simplex.noise4D(
            //     xScaled +100,
			// 	yScaled+100,
			// 	zScaled+100,
			// 	50 + noiseTime
            // )* 0.5 + 0.5;
            // let noise3 = this.simplex.noise4D(
            //     xScaled +200,
			// 	yScaled+200,
			// 	zScaled+200,
			// 	50 + noiseTime
            // )* 0.5 + 0.5;
            // part.position.x += Math.sin(noise1 * Math.PI * 2) * this.clock.getDelta() * noiseVelocity;
            // part.position.y += Math.sin(noise2 * Math.PI * 2) * this.clock.getDelta() * noiseVelocity;
            // part.position.z += Math.sin(noise3 * Math.PI * 2) * this.clock.getDelta() * noiseVelocity;
            // this.parts[i] = part;
            if(part.life <= 0 || part.firstRun) {
				part.life = 2;
				part.position.x = rand(-this.size / 2, this.size / 2);
				part.position.y = rand(-this.size / 2, this.size / 2);
				part.position.z = rand(-this.size / 2, this.size / 2);

				let hue = (this.clock.elapsedTime / 25 + rand(90)) % 360 + 110;
				let lightness = Math.round(rand(10, 50));
				this.color.set(`hsl(${hue}, 85%, ${lightness}%)`);

				part.r = parseInt(this.color.r)+100;
				part.g = parseInt(this.color.g);
				part.b = parseInt(this.color.b);

                part.firstRun = false;
            }
            this.parts[i] = part;

        }
        this.updateParticleAttributes(true, true, true);
        this.renderer.render(this.scene, this.camera);
        
       
    }
    onWindowResize=()=> {

        this.camera.aspect = window.innerWidth / window.innerHeight;
        this.camera.updateProjectionMatrix();

        this.renderer.setSize( window.innerWidth, window.innerHeight );

    }
    render=()=>{
        return <div>
            <canvas ref={ref=>{this.canvasRef = ref}}></canvas>
        </div>
    }
}

ReactDOM.render(
  <ExplosionsSequence />,
  document.getElementById('app')
);
View Compiled

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js
  2. https://cdnjs.cloudflare.com/ajax/libs/three.js/110/three.min.js
  3. https://cdnjs.cloudflare.com/ajax/libs/simplex-noise/2.4.0/simplex-noise.min.js
  4. https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js