<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
    <defs>
        <filter id="goo">
        <feGaussianBlur in="SourceGraphic" stdDeviation="10" result="blur" />
        <feColorMatrix in="blur" mode="matrix" values="1 0 0 0 0  0 1 0 0 0  0 0 1 0 0  0 0 0 19 -9" result="goo" />
        <feComposite in="SourceGraphic" in2="goo" operator="atop"/>
        </filter>
    </defs>
</svg>
<canvas id="canvas1"></canvas>

<canvas id="canvas2"></canvas>

<article>
  To create this effect I use two overlapping canvas elements. Particles on the front layer are being blurred using SVG filter for liquid appearance. The layer behind it hides the particles themselves but connects their current position with fading lines.<br>
</article>
<h1>Move the mouse across canvas</h1>
<footer>Created by <a href="https://www.youtube.com/channel/UCEqc149iR-ALYkGM6TG-7vQ" target='blank'>Frank's Laboratory</a> @2020.
</footer>
#canvas1 {
  position: absolute;
  width: 100%;
  height: 100%;
  top: 0;
  left: 0;
  z-index: -4;
  filter:url(#goo);
  opacity: 1;
}
#canvas2 {
  position: absolute;
  width: 100%;
  height: 100%;
  top: 0;
  left: 0;
  z-index: -5;
  opacity: 1;
}
h1 {
  font-size: 25px;
  text-align: center;
  margin-top: 50px;
  font-family: monospace;
  width: 100%;
  color: black;
 }
footer, article {
  font-family: monospace;
  width: 100%;
  color: black;
  font-size: 15px;
  margin-left: 5px;
  text-shadow: 1px 1px 1px #737373; 
}
footer {
  position: absolute;
  bottom: 0;
  width: 95%;
  text-align: center;
}
article {
  width: 50%;
  font-size: 15px;
}
svg {
  display: none;
}
window.addEventListener('load', function(){
const canvas = document.getElementById("canvas1");
const ctx1 = canvas.getContext("2d");
ctx1.canvas.width  = window.innerWidth;
ctx1.canvas.height = window.innerHeight;
ctx1.fillStyle = 'black';
ctx1.strokeStyle = 'yellow';

const canvas2 = document.getElementById("canvas2");
const ctx2 = canvas2.getContext("2d");
ctx2.canvas.width  = window.innerWidth;
ctx2.canvas.height = window.innerHeight;
ctx2.fillStyle = 'blue';
ctx2.strokeStyle = 'red';

let particleArray = [];
let canvasCenterX = window.innerWidth/2;
let canvasCenterY = window.innerHeight/2;
let radius = window.innerWidth/5;
let angle = 0;

// GET MOUSE POSITION ///////////////////////////////
const mouse = {
  x: null,
  y: null
}
window.addEventListener('mousemove', function(event){
    mouse.x = event.x;
    mouse.y = event.y;
    //console.log(mouse);
});
// SET MOUSE POSITION AS UNDEFINED EVERY 5 SEC(to prevent effect getting stuck in corners when mouse leaves window)//////
setInterval(function(){
  mouse.x = undefined;
  mouse.y = undefined;
}, 10);

// CREATE PARTICLE OBJECT ///////////////////
class Particle {
    constructor(x, y, size, color, weight){
        this.x = x;
        this.y = y;
        this.size = size;
        this.minSize = size;
        this.color = color;
        this.weight = weight;
    }
  draw(){
    ctx1.beginPath();
    ctx1.arc(this.x,this.y,this.size,0,Math.PI * 2, false);
    ctx1.fill();
    ctx1.closePath();
  }
  update(){
        // autopilot when mouse leaves canvas
        if ((mouse.x == undefined) && (mouse.y == undefined)){
          let newX  = radius * 2 * Math.cos(angle * (Math.PI/180));
          let newY = radius * 0.9 * Math.sin(angle * (Math.PI/90));
          mouse.x = newX + canvasCenterX;
          mouse.y = newY + canvasCenterY;
        }
        
        angle+= (Math.random() * 0.020) + 0.001;//0.001 - 0.021
        this.size-=0.15;
        if (this.size < 0) {
            this.x = (mouse.x + ((Math.random() * 20) - 10));
            this.y = (mouse.y + ((Math.random() * 20) - 10));
            this.size = (Math.random()*25);
            this.weight = (Math.random() * 2) + 0.1;
        }
        this.y += this.weight;
        this.weight += 0.05;
                            
        // if it reaches bottom bounce
        if (this.y > canvas.height-this.size){
                this.weight *= -0.5;
        };
  }
}

function init() {
    particleArray = [];
    for (let i = 0; i < 150; i++){
        let size = (Math.random() * 10) + 5;
        let x = Math.random() * (innerWidth - size * 2) + size;
        let y = Math.random() * (innerHeight - size * 2) + size;
        let color = 'black';
        let weight = 1;
        particleArray.push(new Particle(x, y, size, color, weight));
    }

}

function animate(){
    ctx1.clearRect(0, 0, canvas.width, canvas.height);
    ctx2.clearRect(0, 0, canvas.width, canvas.height);
    for (let i = 0; i < particleArray.length; i++) {
        particleArray[i].update();
        particleArray[i].draw();
    }
    connect();
    requestAnimationFrame(animate);
}
init();
animate();

// check if particles are close enough to draw line between them
function connect() {
    let opacityValue = 1;
    for (let a = 0; a < particleArray.length; a++) {
        for (let b = a; b < particleArray.length; b++){
            let distance = Math.sqrt(((particleArray[a].x - particleArray[b].x) * (particleArray[a].x - particleArray[b].x))
            +   ((particleArray[a].y - particleArray[b].y) * (particleArray[a].y - particleArray[b].y)));
            if  (distance < 110)
            {   
                opacityValue = 1-(distance/100);
                ctx2.strokeStyle='rgba(0,0,0,' + opacityValue +')';
                ctx2.beginPath();
                ctx2.lineWidth = 2;
                ctx2.moveTo(particleArray[a].x, particleArray[a].y);
                ctx2.lineTo(particleArray[b].x, particleArray[b].y);
                ctx2.stroke();

            }    
    }
    }
}
window.addEventListener('resize', function(){
  canvas.width = window.innerWidth;
  canvas.height = window.innerHeight;
  canvasCenterX = window.innerWidth/2;
  canvasCenterY = window.innerHeight/2;
  radius = window.innerWidth/5;
  ctx1.canvas.width  = window.innerWidth;
  ctx1.canvas.height = window.innerHeight;
  ctx2.canvas.width  = window.innerWidth;
  ctx2.canvas.height = window.innerHeight;
  init();
})
});

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.