Particles.js: Create Amazing Particle Effects

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

  1. Visit https://vincentgarreau.com/particles.js/
  2. Download the library
  3. Unzip the package
  4. 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.

For Read about Ant-Design – Click Here
For Read About Headless UI – Click Here

Common Issues and FAQ

Leave a Comment

Your email address will not be published. Required fields are marked *

wpChatIcon
wpChatIcon
Scroll to Top