Creating particles which moves from front to back

hi i saw this https://nomy.club/
which looks like the particles in word nomy and it behaves like the particles are coming from front and goes back and fades away how can we create that?
i have already created the particles in word, but unable to think of this coming from front and fading in back or we can say its like a shadow

const createParticle = (
  x,
  y,
  width,
  height,
  ctx,
  color,
  particleRadius = 1,
  repulsionStrength = 50
) => {
  const current = {
    x: Math.random() * width,
    y: Math.random() * height
  };

  const destination = {
    x,
    y
  };
  let vx = (Math.random() - 0.5) * 10;
  let vy = (Math.random() - 0.5) * 10;
  const friction = Math.random() * 0.01 + 0.909;
  let hasBounced = false; // New flag for single bounce
  // Parse baseColor and adjust lightness
  const randomLightness = Math.random() * 0.4 + 0.6; // 60% to 100% brightness
  const particleColor = adjustColorBrightness("#136173", randomLightness);

  return {
    render(mouseX, mouseY) {
      const a = current.x - mouseX;
      const b = current.y - mouseY;
      const distance = Math.sqrt(a * a + b * b);
      let accX = (destination.x - current.x) / 200;
      let accY = (destination.y - current.y) / 200;
      if (!hasBounced && distance < repulsionStrength) {
        // If within repulsion radius and hasn't bounced yet
        accX = -(a / 40);
        accY = -(b / 40);
        hasBounced = false; // Mark as bounced
      }
      
      // Add small random offsets to create random "wave-like" motion
      const randomOffsetX = (Math.random() - 0.5) * 2; // Random offset for X
      const randomOffsetY = (Math.random() - 0.5) * 2; // Random offset for Y
      current.x += randomOffsetX;
      current.y += randomOffsetY;

      vx += accX;
      vy += accY;
      vx *= friction;
      vy *= friction;
      current.x += vx;
      current.y += vy;

      // Use the dynamic color
      ctx.fillStyle = particleColor;
      // ctx.fillStyle = color;
      ctx.beginPath();
      ctx.arc(current.x, current.y, particleRadius, Math.PI * 2, 0);
      ctx.fill();

      if (distance < repulsionStrength) {
        accX = -(a / 40);
        accY = -(b / 40);
        vx += accX;
        vy += accY;
      }
    }
  };
};
// Helper function to adjust brightness
const adjustColorBrightness = (hexColor, brightnessFactor) => {
  // Convert hex to RGB
  const r = parseInt(hexColor.slice(1, 3), 16);
  const g = parseInt(hexColor.slice(3, 5), 16);
  const b = parseInt(hexColor.slice(5, 7), 16);

  // Adjust brightness
  const adjust = (color) =>
    Math.min(255, Math.max(0, Math.floor(color * brightnessFactor)));

  const baselineIncrease = 10; // Lighten by 20%
  const newR = adjust(r * baselineIncrease);
  const newG = adjust(g * baselineIncrease);
  const newB = adjust(b * baselineIncrease);

  // Convert back to hex
  return `rgb(${newR}, ${newG}, ${newB})`;
};

export default createParticle;

this is my particle.js

import createParticle from "./particle";
import fromEvent from "../../hooks/fromEvent";
import createPositionTracker from "./positionTracker";

const { getPosition, setPosition } = createPositionTracker();

const numberOfParticlesPerCharacter = 100;

const createTextParticlesAnimation = (
  canvas,
  colors,
  text
) => {
  const { setCanvasDimensions, getCanvasDimensions, ctx } = canvas;

  if (!ctx) {
    return;
  }

  const { width, height } = getCanvasDimensions();
  const colorsLength = colors.length;
  const textLength = text?.length;
  let particles = [];

  const clearCanvas = (width, height) =>
    ctx.clearRect(0, 0, width, height);
  
  let time = 0; // Internal time tracker for oscillation

  const drawParticles = () => {
    const particlesAmount = particles.length;
    const { x, y } = getPosition();
    time += 1; // Increment the internal time counter

    requestAnimationFrame(drawParticles);
    clearCanvas(width, height);

    for (let i = 0; i < particlesAmount; i++) {
      particles[i].render(x, y, time);
    }
  };

  const setUpText = () => {
    const canvas = ctx.canvas; // Access the canvas dimensions directly
    const width = canvas.width / window.devicePixelRatio; // Adjust for DPI
    const height = canvas.height / window.devicePixelRatio;
  
    ctx.font = "bold " + width / 10 + "px sans-serif"; // Dynamic font size
    ctx.textAlign = "center"; // Horizontal alignment
    ctx.textBaseline = "middle"; // Vertical alignment
  
    const x = width / 2; // Center horizontally
    const y = height / 2; // Center vertically
  
    ctx.fillText(text, x, y); // Draw text at the center
    ctx.globalCompositeOperation = "screen"; // Composite operation
  };
  

  const refreshCanvasDimensions = () => {
    const { innerWidth, innerHeight } = window;
    setCanvasDimensions(innerWidth, innerHeight);
  };

  const getRandomIndex = (max) => Math.floor(Math.random() * max);

  const createParticles = () => {
    refreshCanvasDimensions();
    const { width, height } = getCanvasDimensions();

    clearCanvas(width, height);

    setUpText(width, height);

    const { data } = ctx.getImageData(0, 0, width, height);

    particles = [];

    const numberOfParticles = textLength * numberOfParticlesPerCharacter;

    for (let i = 0; i < width; i += Math.round(width / numberOfParticles)) {
      for (let j = 0; j < height; j += Math.round(width / numberOfParticles)) {
        if (data[(i + j * width) * 4 + 3] > 150) {
          const color = colors[getRandomIndex(colorsLength)];
          particles.push(createParticle(i, j, width, height, ctx, color));
        }
      }
    }
  };

  let eventHandlers = null;

  const addEventListeners = () => {
    const onMouseMove = (e) => {
      const rect = canvas.ctx.canvas.getBoundingClientRect(); // Get canvas position
      const offsetX = e.clientX - rect.left; // Adjust for canvas position
      const offsetY = e.clientY - rect.top;  // Adjust for canvas position
      setPosition(offsetX, offsetY);
    };
  
    const onTouchMove = (e) => {
      const rect = canvas.ctx.canvas.getBoundingClientRect(); // Get canvas position
      const touch = e.touches[0]; // Get the first touch
      const offsetX = touch.clientX - rect.left; // Adjust for canvas position
      const offsetY = touch.clientY - rect.top;  // Adjust for canvas position
      setPosition(offsetX, offsetY);
    };
  
    const onTouchEnd = () => setPosition(-9999, -9999);
  
    eventHandlers = [
      fromEvent(window, "resize", createParticles),
      fromEvent(window, "mousemove", onMouseMove),
      fromEvent(window, "touchmove", onTouchMove),
      fromEvent(window, "touchend", onTouchEnd),
    ];
  
    eventHandlers.forEach(({ subscribe }) => subscribe());
  };
  

  const start = () => {
    if (!eventHandlers) {
      addEventListeners();
      createParticles();
      drawParticles();
    }
  };

  const stop = () => {
    if (eventHandlers) {
      eventHandlers.forEach(({ unsubscribe }) => unsubscribe());
    }
  };

  return { start, stop };
};

export default createTextParticlesAnimation;

this is my createTextParticlesAnimation.js

can anyone suggest me how can we do that??

To achieve the effect of particles appearing to come from the front and fade away to the back, you can modify your particle rendering logic to adjust the particle size and opacity based on their depth. Here’s how you can do it: Official Site

  1. Add Depth Property: Add a z property to represent the depth of each particle.
  2. Adjust Particle Size and Opacity: Modify the rendering function to change the size and opacity based on the z value.

Here’s a modified version of your createParticle function:

const createParticle = (
  x,
  y,
  width,
  height,
  ctx,
  color,
  particleRadius = 1,
  repulsionStrength = 50
) => {
  const current = {
    x: Math.random() * width,
    y: Math.random() * height,
    z: Math.random() * width // Add depth
  };

  const destination = {
    x,
    y,
    z: 0 // Destination depth
  };
  let vx = (Math.random() - 0.5) * 10;
  let vy = (Math.random() - 0.5) * 10;
  let vz = (Math.random() - 0.5) * 10; // Velocity in the z direction
  const friction = Math.random() * 0.01 + 0.909;
  let hasBounced = false; // New flag for single bounce
  // Parse baseColor and adjust lightness
  const randomLightness = Math.random() * 0.4 + 0.6; // 60% to 100% brightness
  const particleColor = adjustColorBrightness("#136173", randomLightness);

  return {
    render(mouseX, mouseY) {
      const a = current.x - mouseX;
      const b = current.y - mouseY;
      const distance = Math.sqrt(a * a + b * b);
      let accX = (destination.x - current.x) / 200;
      let accY = (destination.y - current.y) / 200;
      let accZ = (destination.z - current.z) / 200; // Acceleration in the z direction
      if (!hasBounced && distance < repulsionStrength) {
        // If within repulsion radius and hasn't bounced yet
        accX = -(a / 40);
        accY = -(b / 40);
        accZ = -(b / 40);
        hasBounced = false; // Mark as bounced
      }
      
      // Add small random offsets to create random "wave-like" motion
      const randomOffsetX = (Math.random() - 0.5) * 2; // Random offset for X
      const randomOffsetY = (Math.random() - 0.5) * 2; // Random offset for Y
      current.x += randomOffsetX;
      current.y += randomOffsetY;

      vx += accX;
      vy += accY;
      vz += accZ; // Update velocity in the z direction
      vx *= friction;
      vy *= friction;
      vz *= friction; // Apply friction to z direction
      current.x += vx;
      current.y += vy;
      current.z += vz; // Update position in the z direction

      // Adjust size and opacity based on z
      const size = particleRadius * (1 - current.z / width);
      const opacity = 1 - current.z / width;
      ctx.fillStyle = `rgba(${parseInt(particleColor.slice(4, 7))}, ${parseInt(particleColor.slice(8, 11))}, ${parseInt(particleColor.slice(12, 15))}, ${opacity})`;
      
      ctx.beginPath();
      ctx.arc(current.x, current.y, size, Math.PI * 2, 0);
      ctx.fill();

      if (distance < repulsionStrength) {
        accX = -(a / 40);
        accY = -(b / 40);
        vx += accX;
        vy += accY;
      }
    }
  };
};

With these changes, your particles should now appear to come from the front, go back, and fade away, creating a sense of depth and movement.

this doesn’t changes anything! its all same, can you please look into https://nomy.club website,
I want to create the same.