<head>
	<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
</head>
<body>
<canvas id="canvas_1"></canvas>
<canvas id="canvas_2"></canvas>
<div class="label">PARTICLE TEXT</div>
<div class="instructions">MOVE MOUSE AROUND</div>
	</body>

body {
	background-color: #fff;    
	margin: 0;
	overflow: hidden;
	background-repeat: no-repeat;
}
.label {
	position: absolute;
	top: 0;
	left: 0;
	padding: 5px 15px;
	color: #eee;
	font-size: 13px;
	background-color: rgba(0, 0, 0, .5);
}
.instructions {
	position: absolute;
	bottom: 0%;
	left: 0;
	padding: 5px 15px;
	color: #eee;
	font-size: 13px;
	background-color: rgba(0, 0, 0, .5);
}
canvas { display:block; }
canvas_2 { display:block; }
//Main display canvas
var ctx = canvas_1.getContext("2d");
//Hidden canvas
var ctx_2 = canvas_2.getContext("2d");

canvas_1.width = window.innerWidth;
canvas_1.height = window.innerHeight;

canvas_2.width = window.innerWidth;
canvas_2.height = window.innerHeight;

var particles = [];
var discs = [];
var TWO_PI = 2 * Math.PI;

var particleCount = 0;
var discCount = 5;
var radius = 130;
var animate = true;

for(i = 0; i < discCount; i++){
	var disc = {
		x: Math.random()*canvas_1.width,
		y: Math.random()*canvas_1.height,
		radius: radius/2 + Math.random() * radius/2,
		x_vel: 1-Math.random()*0.5,
		y_vel: 1-Math.random()*0.5
	};
	discs.push(disc);
}

ctx_2.fillStyle = "#000";
var fontSize = 20;
ctx_2.font = "" + fontSize + "px Helvetica";
ctx_2.textBaseline = 'middle';
var sentence = "THE EXTRAORDINARY PROPERTIES OF DARKNESS";
var words = sentence.split(" ");
var density = 1;
var delta = 3.5;
var length = 0; 
var l_;
for(i = 0; i < words.length; i++){
	l_ = ctx_2.measureText(words[i]).width * delta;
	if(length < l_){
		length = l_;
	}
}
for(i = 0; i < words.length; i++){
	l_ = ctx_2.measureText(words[i]).width * delta;
	ctx_2.fillText(words[i], length/delta/2-l_/delta/2, (i+1) * fontSize);
}

var textImageData = ctx_2.getImageData(0, 0, length, fontSize + fontSize * words.length);
var xOffset = canvas_2.width/2 - length/2;
var yOffset = canvas_2.height/2.5 - fontSize * delta * words.length/2;

for (i = 0; i < textImageData.width; i+=density) {
	for (j = 0; j < textImageData.height; j+=density) {

		var alpha = textImageData.data[(j * (textImageData.width * 4) + (i*4) + 3)];
		if (alpha > 0) {
			var particle = {
				x: xOffset + i * delta,
				y: yOffset + j * delta,
				x_target: xOffset + i * delta,
				y_target: yOffset + j * delta,
				radius: alpha/255*(delta),
				x_vel: 0,
				y_vel: 0,
				offset: 0
			};
			particles.push(particle);
			particleCount++;
		}
	}
}

canvas_1.addEventListener("mousemove", mouse_move);
canvas_1.addEventListener('mouseleave', mouse_leave);
canvas_1.addEventListener('mouseenter', mouse_enter);

var mouse_pos_x;
var mouse_pos_y;

function dist(dx, dy){
	return Math.sqrt(dx * dx + dy * dy);
}
var scale = canvas_1.width/canvas_1.scrollWidth;
function mouse_move(event){
	mouse_pos_x = event.offsetX * scale;
	mouse_pos_y = event.offsetY * scale;
}

function mouse_leave(){
	mouse_pos_x = -100;
	mouse_pos_y = -100; 
	animate = true;
}

function mouse_enter(){
	animate = false;
}

function move() {
	for(i = 0; i < discCount; i++){
		if(discs[i].x < discs[i].radius){
			discs[i].x = discs[i].radius;
			discs[i].x_vel = -discs[i].x_vel;
		}  
		if(discs[i].y < discs[i].radius){
			discs[i].y = discs[i].radius;
			discs[i].y_vel = -discs[i].y_vel;
		}
		if(discs[i].x > canvas_1.width-discs[i].radius){
			discs[i].x = canvas_1.width-discs[i].radius;
			discs[i].x_vel = -discs[i].x_vel;
		}  
		if(discs[i].y > canvas_1.height-discs[i].radius){
			discs[i].y = canvas_1.height-discs[i].radius;
			discs[i].y_vel = -discs[i].y_vel;
		}

		discs[i].x += discs[i].x_vel;
		discs[i].y += discs[i].y_vel;
	}

	var d;
	for(i = 0; i < particleCount; i++){
		d = dist(particles[i].x_target - particles[i].x, particles[i].y_target - particles[i].y);
		if(d > 8){
			particles[i].offset = d;
		}else{
			particles[i].offset = 0;
		}
		particles[i].x_vel = (particles[i].x_target - particles[i].x)/20;
		particles[i].y_vel = (particles[i].y_target - particles[i].y)/20;
		if(animate){
			for(j = 0; j < discCount; j++){
				d = dist(discs[j].x - particles[i].x_target, discs[j].y - particles[i].y_target);
				if (d < discs[j].radius){
					particles[i].x = discs[j].x - ((discs[j].x - particles[i].x_target)/d)*(2*d-discs[j].radius);
					particles[i].y = discs[j].y - ((discs[j].y - particles[i].y_target)/d)*(2*d-discs[j].radius);
				}
			}
		}else{
		
		d = dist(mouse_pos_x - particles[i].x_target, mouse_pos_y - particles[i].y_target);
		
		if (d < radius){
			particles[i].x = mouse_pos_x - ((mouse_pos_x - particles[i].x_target)/d)*(2*d-radius);
			particles[i].y = mouse_pos_y - ((mouse_pos_y - particles[i].y_target)/d)*(2*d-radius);
		}
	}
		particles[i].x += particles[i].x_vel;
		particles[i].y += particles[i].y_vel;
	}
}

ctx.fillStyle = 'white';
ctx.fillRect(0,0,canvas_1.width, canvas_1.height);

function draw() {
	canvas_1.width = window.innerWidth;
	canvas_1.height = window.innerHeight;
	scale = canvas_1.width/canvas_1.scrollWidth;

	ctx.fillStyle = "rgba(255,255,255,1)";
	ctx.fillRect(0,0,canvas_1.width, canvas_1.height);
	move();

	var grd;
	var offset;
	
	ctx.fillStyle = 'black';
	for(i = 0; i < particleCount; i++){
		ctx.beginPath();
		ctx.arc(particles[i].x, particles[i].y, particles[i].radius, 0, TWO_PI);
		ctx.fill();
	}

	window.requestAnimationFrame(draw);
}
draw();

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.