Particles.js is a lightweight JavaScript library that creates a captivating particle animation effect on web pages. It’s perfect for creating interactive backgrounds, dynamic visualizations, and engaging user interfaces. This guide will walk you through everything you need to know about implementing Particles.js in various JavaScript environments.
What is Particles.js?
This is an open-source library that enables developers to create particle systems with customizable behaviors. These particles can:
- Move around the screen in various patterns
- Respond to user interactions (hover, click)
- Connect with lines based on proximity
- Change colors, sizes, and shapes
- Create engaging visual effects

Installation Methods
1: Direct Download
- Visit https://vincentgarreau.com/particles.js/
- Download the library
- Unzip the package
- Copy
particles.js
to your project’s assets folder
2: NPM Installation
npm install particles.js
# or
yarn add particles.js
3: For React Projects
npm install react-particles-js
# or
yarn add react-particles-js
Basic Implementation in Vanilla JavaScript
HTML Setup
<!DOCTYPE html>
<html>
<head>
<title>Particles.js Demo</title>
<style>
#particles-js {
width: 100%;
height: 100vh;
background: #000;
position: fixed;
top: 0;
left: 0;
}
</style>
</head>
<body>
<div id="particles-js"></div>
<script src="particles.js"></script>
<script src="app.js"></script>
</body>
</html>
JavaScript Configuration (app.js)
particlesJS('particles-js', {
particles: {
number: {
value: 80,
density: {
enable: true,
value_area: 800
}
},
color: {
value: '#ffffff'
},
shape: {
type: 'circle'
},
opacity: {
value: 0.5,
random: false
},
size: {
value: 3,
random: true
},
line_linked: {
enable: true,
distance: 150,
color: '#ffffff',
opacity: 0.4,
width: 1
},
move: {
enable: true,
speed: 6,
direction: 'none',
random: false,
straight: false,
out_mode: 'out',
bounce: false
}
},
interactivity: {
detect_on: 'canvas',
events: {
onhover: {
enable: true,
mode: 'repulse'
},
onclick: {
enable: true,
mode: 'push'
},
resize: true
}
},
retina_detect: true
});
React Implementation
Using react-particles-js
import React from 'react';
import Particles from 'react-particles-js';
const ParticleBackground = () => {
return (
<Particles
params={{
particles: {
number: {
value: 80,
density: {
enable: true,
value_area: 800
}
},
color: {
value: '#ffffff'
},
shape: {
type: 'circle'
},
// ... rest of configuration similar to vanilla JS
}
}}
style={{
position: 'fixed',
top: 0,
left: 0,
width: '100%',
height: '100%'
}}
/>
);
};
export default ParticleBackground;
Next.js Implementation
Creating a Particle Component
// components/ParticleBackground.js
import { useEffect } from 'react';
import dynamic from 'next/dynamic';
const ParticleBackground = () => {
useEffect(() => {
const particlesJS = require('particles.js');
window.particlesJS.load('particles-js', '/particles-config.json', function() {
console.log('particles.js loaded');
});
}, []);
return <div id="particles-js" style={{
position: 'fixed',
top: 0,
left: 0,
width: '100%',
height: '100%',
zIndex: -1
}} />;
};
// Prevent SSR for this component
export default dynamic(() => Promise.resolve(ParticleBackground), {
ssr: false
});
Particles Configuration (public/particles-config.json)
{
"particles": {
"number": {
"value": 80,
"density": {
"enable": true,
"value_area": 800
}
},
// ... rest of configuration
}
}
Text Animation with Particles
import React, { useEffect, useRef } from "react";
const TextParticleHero = () => {
const canvasRef = useRef(null);
class Particle {
constructor(canvas, targetX, targetY) {
this.canvas = canvas;
// Start positions adjusted for better entry
const edge = Math.floor(Math.random() * 4);
switch (edge) {
case 0: // top
this.x = Math.random() * canvas.width;
this.y = -20;
break;
case 1: // right
this.x = canvas.width + 20;
this.y = Math.random() * canvas.height;
break;
case 2: // bottom
this.x = Math.random() * canvas.width;
this.y = canvas.height + 20;
break;
case 3: // left
this.x = -20;
this.y = Math.random() * canvas.height;
break;
}
this.targetX = targetX;
this.targetY = targetY;
this.size = Math.random() * 2 + 1.5; // Slightly larger particles
this.velocityX = 0;
this.velocityY = 0;
this.baseSpeed = Math.random() * 3 + 2; // Increased base speed
this.speed = this.baseSpeed;
this.inPosition = false;
this.exitAngle = Math.random() * Math.PI * 2;
this.isExiting = false;
this.hasReachedTarget = false;
}
update(currentTime) {
if (!this.isExiting) {
const dx = this.targetX - this.x;
const dy = this.targetY - this.y;
const distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 3) {
// Reduced distance threshold
this.velocityX = (dx / distance) * this.speed;
this.velocityY = (dy / distance) * this.speed;
this.hasReachedTarget = false;
} else {
this.velocityX *= 0.3;
this.velocityY *= 0.3;
this.hasReachedTarget = true;
this.x = this.targetX; // Snap to position
this.y = this.targetY;
}
} else {
// Faster exit animation
const exitSpeed = this.baseSpeed * 4;
this.velocityX = Math.cos(this.exitAngle) * exitSpeed;
this.velocityY = Math.sin(this.exitAngle) * exitSpeed;
}
this.x += this.velocityX;
this.y += this.velocityY;
}
draw(ctx) {
const gradient = ctx.createRadialGradient(
this.x,
this.y,
0,
this.x,
this.y,
this.size
);
gradient.addColorStop(0, "rgb(74, 226, 87)");
gradient.addColorStop(1, "#008374");
ctx.fillStyle = gradient;
ctx.beginPath();
ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
ctx.fill();
}
isOffScreen() {
return (
this.x < -50 ||
this.x > this.canvas.width + 50 ||
this.y < -50 ||
this.y > this.canvas.height + 50
);
}
}
useEffect(() => {
const canvas = canvasRef.current;
const ctx = canvas.getContext("2d");
let particles = [];
let animationFrameId;
let startTime;
let formationComplete = false;
const init = () => {
canvas.width = canvas.offsetWidth;
canvas.height = canvas.offsetHeight;
// Create text points
ctx.font = "bold 72px Arial"; // Slightly smaller font
ctx.fillStyle = "white";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
const text = "MernStackDEV.com";
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillText(text, canvas.width / 2, canvas.height / 2);
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const pixels = imageData.data;
const points = [];
// More precise sampling
const sampleRate = 3; // Increased sampling density
for (let y = 0; y < canvas.height; y += sampleRate) {
for (let x = 0; x < canvas.width; x += sampleRate) {
const index = (y * canvas.width + x) * 4;
if (pixels[index + 3] > 128) {
points.push({ x, y });
}
}
}
particles = points.map((point) => new Particle(canvas, point.x, point.y));
formationComplete = false;
ctx.clearRect(0, 0, canvas.width, canvas.height);
};
const animate = (timestamp) => {
if (!startTime) startTime = timestamp;
const elapsed = timestamp - startTime;
ctx.fillStyle = "rgba(0, 0, 0, 0.15)"; // Slightly more fade
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Check if all particles have reached their targets
if (!formationComplete) {
formationComplete = particles.every((p) => p.hasReachedTarget);
}
// Start exit animation after text has formed and stayed for 2 seconds
const shouldExit = formationComplete && elapsed > 7000; // Increased delay
let allExited = true;
particles.forEach((particle) => {
if (shouldExit && !particle.isExiting) {
particle.isExiting = true;
}
particle.update(elapsed);
particle.draw(ctx);
if (!particle.isExiting || !particle.isOffScreen()) {
allExited = false;
}
});
// Reset animation when all particles have exited
if (allExited) {
startTime = null;
init();
}
animationFrameId = requestAnimationFrame(animate);
};
// Handle resize
const handleResize = () => {
canvas.width = canvas.offsetWidth;
canvas.height = canvas.offsetHeight;
init();
};
window.addEventListener("resize", handleResize);
init();
animationFrameId = requestAnimationFrame(animate);
return () => {
window.removeEventListener("resize", handleResize);
cancelAnimationFrame(animationFrameId);
};
}, []);
return (
<div className="relative w-full min-h-[800px] bg-gradient-to-br from-gray-900 to-black overflow-hidden">
<canvas ref={canvasRef} className="absolute top-0 left-0 w-full h-full" />
</div>
);
};
export default TextParticleHero;
Output
Conclusion
Particles.js is a versatile library that can add stunning visual effects to your web projects. Whether you’re using vanilla JavaScript, React, or Next.js, implementing particle effects can greatly enhance user experience and create memorable interfaces. Remember to optimize your configuration for performance, especially on mobile devices, and test thoroughly across different browsers and devices.