npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

particle-lsslib

v2.1.0

Published

A flexible particle effect library with realistic physics, spatial distortion, and interactive modes (repel, attract, vortex) for creating stunning image-based particle animations

Downloads

53

Readme

Particle Effect Library

A flexible, TypeScript-based particle effect library with realistic physics and interactive modes for creating stunning image-based particle animations. Works with vanilla JavaScript and React.

Features

  • Image-based particles: Convert any image into animated particles
  • Realistic physics: Inverse square law forces with smooth falloff (no hard edges)
  • Natural interactions: Smooth, organic mouse effects without visible radius boundaries
  • Spatial distortion effect: Particles have individual mass - some escape forces, others resist
  • Variable particle sizes: Each particle can have different size for organic look
  • Multiple interaction modes: Repel, Attract, Vortex, and Chaos effects with turbulence
  • Dynamic radius control: Adjust mouse interaction radius with mouse wheel/scroll in real-time
  • Enhanced Perlin noise: Organic, flowing movement with pseudo-Perlin noise instead of pure random
  • Chaos mode: Dynamic blend of repel and vortex with time-based oscillations
  • Momentum-based gravity: Physics that feel natural, not just a downward pull
  • React support: Built-in hook for easy React integration
  • TypeScript: Full type safety and IntelliSense support
  • Highly configurable: Intuitive 0-5 scale for density and size controls
  • Transparent backgrounds: Perfect for overlay effects
  • Flexible sizing: Set custom dimensions or maintain aspect ratio
  • Lightweight: No dependencies (except React for the hook)
  • Global mouse tracking: Mouse interactions work across entire window, not just canvas

Installation

npm i particle-lsslib

Quick Start

Vanilla JavaScript/TypeScript

Option 1: Automatic Renderer Selection (Recommended)

import { ParticleEngineHybrid } from 'particle-lsslib';

const canvas = document.getElementById('myCanvas') as HTMLCanvasElement;

const engine = new ParticleEngineHybrid({
  canvas,
  enablePerformanceMonitoring: true,  // Optional: track FPS and metrics
  settings: {
    particleSize: 3,
    density: 4,
    mode: 'repel',
    force: 5,
    width: 800,
    padding: 200,
    backgroundColor: 'transparent',
  },
});

// Load an image
await engine.loadImage('path/to/image.png');

// Start animation (automatically starts after loading)
engine.start();

// Check which renderer was selected
console.log(`Using renderer: ${engine.getRenderer()}`); // 'canvas2d', 'webgl', or 'webgl-worker'

// Monitor performance
console.log(engine.getPerformanceMetrics()); // { fps: 60, frameTime: 16.6, particleCount: 5000, renderer: 'canvas2d' }

Option 2: Canvas 2D Only (Direct ParticleEngine)

import { ParticleEngine } from 'particle-lsslib';

const canvas = document.getElementById('myCanvas') as HTMLCanvasElement;

const engine = new ParticleEngine({
  canvas,
  settings: {
    particleSize: 3,
    density: 4,
    mode: 'repel',
    force: 5,
    width: 800,
    padding: 200,
    backgroundColor: 'transparent',
  },
});

await engine.loadImage('path/to/image.png');
engine.start();

React

Basic Usage with React Hook

import { useParticleEffect } from 'particle-lsslib';

function ParticleCanvas() {
  const { canvasRef, loadImage, updateSettings } = useParticleEffect({
    settings: {
      particleSize: 2,
      density: 4,
      mode: 'vortex',
    },
  });

  const handleFileChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
    const file = e.target.files?.[0];
    if (file) await loadImage(file);
  };

  return (
    <div>
      <input type="file" accept="image/*" onChange={handleFileChange} />
      <canvas ref={canvasRef} />
    </div>
  );
}

Advanced: Using ParticleEngineHybrid in React

import { useEffect, useRef } from 'react';
import { ParticleEngineHybrid } from 'particle-lsslib';

function HybridParticleCanvas() {
  const canvasRef = useRef<HTMLCanvasElement>(null);
  const engineRef = useRef<ParticleEngineHybrid | null>(null);

  useEffect(() => {
    if (!canvasRef.current) return;

    // Create hybrid engine with automatic renderer selection
    engineRef.current = new ParticleEngineHybrid({
      canvas: canvasRef.current,
      enablePerformanceMonitoring: true,
      settings: {
        particleSize: 2,
        density: 4,
        mode: 'chaos',
        force: 5,
        width: 450,
        padding: 200,
      },
    });

    // Load image
    engineRef.current.loadImage('/your-image.png');

    // Log renderer info
    console.log(`Renderer: ${engineRef.current.getRenderer()}`);
    console.log('Capabilities:', engineRef.current.getCapabilities());

    // Cleanup
    return () => {
      engineRef.current?.destroy();
    };
  }, []);

  return <canvas ref={canvasRef} />;
}

API Reference

ParticleEngineHybrid (Recommended)

The hybrid engine automatically selects the best renderer based on browser capabilities:

  • Canvas 2D: Default, works everywhere
  • WebGL: High-performance GPU rendering (coming soon)
  • WebGL Worker: WebGL in Web Worker with OffscreenCanvas for maximum performance (AVAILABLE NOW)

Constructor

new ParticleEngineHybrid(options: ParticleEngineOptions)

Options:

  • canvas: HTMLCanvasElement - The canvas element to render on
  • renderer?: 'canvas2d' | 'webgl' | 'webgl-worker' - Force specific renderer (optional)
  • enablePerformanceMonitoring?: boolean - Track FPS and performance metrics (default: false)
  • settings?: Partial settings object (see Settings below)
  • onParticlesGenerated?: Callback function called when particles are created

Example:

const engine = new ParticleEngineHybrid({
  canvas: document.getElementById('canvas') as HTMLCanvasElement,
  renderer: 'canvas2d',  // Optional: force specific renderer
  enablePerformanceMonitoring: true,
  settings: {
    particleSize: 2,
    density: 4,
    mode: 'chaos',
  },
});

Methods

loadImage(source: ImageSource): Promise<void>

Load and process an image. Accepts URL string, HTMLImageElement, or File object.

start(): void

Start the animation loop.

stop(): void

Stop the animation loop.

updateSettings(settings: PartialParticleSettings): void

Update one or more settings.

getSettings(): ParticleSettings | null

Get current settings (returns null if using WebGL Worker).

getParticleCount(): number

Get the current number of particles.

getRenderer(): RendererType

Get the current renderer being used.

Returns: 'canvas2d' | 'webgl' | 'webgl-worker'

const renderer = engine.getRenderer();
console.log(`Using: ${renderer}`);
getCapabilities(): BrowserCapabilities

Get browser capabilities detected by the engine.

Returns:

{
  supportsWebGL: boolean;
  supportsWebGL2: boolean;
  supportsOffscreenCanvas: boolean;
  supportsWebGLInWorker: boolean;
  maxTextureSize: number;
  recommendedRenderer: RendererType;
}

Example:

const caps = engine.getCapabilities();
console.log(`WebGL supported: ${caps.supportsWebGL}`);
console.log(`Recommended renderer: ${caps.recommendedRenderer}`);
getPerformanceMetrics(): PerformanceMetrics

Get current performance metrics (only if enablePerformanceMonitoring is true).

Returns:

{
  fps: number;           // Current frames per second
  frameTime: number;     // Average frame time in ms
  particleCount: number; // Current number of particles
  renderer: RendererType; // Active renderer
}

Example:

setInterval(() => {
  const metrics = engine.getPerformanceMetrics();
  console.log(`FPS: ${metrics.fps}, Particles: ${metrics.particleCount}`);
}, 1000);
destroy(): void

Clean up and remove event listeners.


ParticleEngine (Canvas 2D Only)

Constructor

new ParticleEngine(options: ParticleEngineOptions)

Options:

  • canvas: HTMLCanvasElement - The canvas element to render on
  • settings?: Partial settings object (see Settings below)
  • onParticlesGenerated?: Callback function called when particles are created

Methods

loadImage(source: ImageSource): Promise<void>

Load and process an image. Accepts:

  • URL string
  • HTMLImageElement
  • File object
await engine.loadImage('image.png');
await engine.loadImage(fileInput.files[0]);
start(): void

Start the animation loop.

stop(): void

Stop the animation loop.

updateSettings(settings: Partial<ParticleSettings>): void

Update one or more settings.

engine.updateSettings({
  mode: 'attract',
  force: 10,
  gravity: 0.1,
});
getSettings(): ParticleSettings

Get current settings.

getParticleCount(): number

Get the current number of particles.

destroy(): void

Clean up and remove event listeners.

Settings

interface ParticleSettings {
  particleSize: number;         // Particle size scale 0-5 (0=tiny 0.5px, 5=large 4px) (default: 2.5)
  density: number;              // Particle density 0-5 (0=sparse, 5=very dense) (default: 2.5)
  gravity: number;              // Momentum-based gravity force (default: 0)
  mouseRadius: number;          // Interaction radius - adjustable with mouse wheel (default: 100)
  scale: number;                // Image scale multiplier (default: 1)
  mode: InteractionMode;        // 'repel' | 'attract' | 'vortex' | 'chaos' (default: 'repel')
  force: number;                // Interaction force strength (default: 5)
  padding: number;              // Canvas padding in pixels (default: 200)
  paddingX?: number;            // Horizontal padding - overrides padding if set
  paddingY?: number;            // Vertical padding - overrides padding if set
  backgroundColor: string;      // Background color or 'transparent' (default: 'transparent')
  width?: number;               // Custom width - overrides scale if set
  height?: number;              // Custom height - overrides scale if set
  noise?: number;               // Noise/randomness factor 0-1 (default: 0.3) - now uses Perlin noise
  minParticleSize?: number;     // Minimum particle size for variation
  maxParticleSize?: number;     // Maximum particle size for variation
}

Physics & Visual Effects

New in v2.0: The library now uses realistic inverse square law physics (like real gravity) instead of linear forces. Each particle has:

  • Individual mass (0.5-2.0): Affects resistance to forces
  • Individual size: Random size between minParticleSize and maxParticleSize
  • Escape probability: Some particles escape forces based on distance and mass (spatial distortion effect)
  • Brownian motion: Subtle random movement controlled by noise parameter

Image Sizing Options

You can control the image size in three ways:

1. Using width/height (maintains aspect ratio if only one is set)

engine.updateSettings({ width: 800 });  // Height calculated automatically
engine.updateSettings({ height: 600 }); // Width calculated automatically
engine.updateSettings({ width: 800, height: 600 }); // Both specified

2. Using scale multiplier

engine.updateSettings({ scale: 1.5 }); // 150% of original size

3. Custom padding (uniform or separate)

// Uniform padding
engine.updateSettings({
  padding: 300,                    // Same padding on all sides
  backgroundColor: '#000000'       // Black background
});

// Separate horizontal and vertical padding
engine.updateSettings({
  paddingX: 400,                   // Horizontal padding
  paddingY: 200,                   // Vertical padding
  backgroundColor: '#000000'
});

Recommended Settings for Best Results

For realistic spatial distortion (Recommended):

{
  particleSize: 2,
  density: 4,              // Dense for smooth effect (0-5 scale: higher = more dense)
  mode: 'repel',
  force: 3,                // Lower force with inverse square law physics
  gravity: 0,              // Keep particles stable
  mouseRadius: 200,        // Large interaction area with smooth falloff
  width: 450,
  padding: 250,            // Space for particles to move freely
  backgroundColor: 'transparent',
  noise: 0.4,              // Spatial distortion and turbulence
  minParticleSize: 0.3,    // Very small particles
  maxParticleSize: 1.5,    // Varied sizes for organic look
}

For high-performance (many particles):

{
  particleSize: 2.5,
  density: 4.5,            // Very dense (0-5 scale: 4.5 = lots of particles)
  force: 2,                // Lower with new physics
  mouseRadius: 180,
  padding: 200,
  noise: 0.3,
  minParticleSize: 0.5,
  maxParticleSize: 2,
}

For low-resource devices:

{
  particleSize: 3,
  density: 1.5,            // Sparse (0-5 scale: 1.5 = fewer particles, better performance)
  force: 4,
  mouseRadius: 150,
  padding: 150,
  noise: 0.2,              // Less computation
  minParticleSize: 1,
  maxParticleSize: 3,
}

For dramatic vortex effect:

{
  particleSize: 2.5,
  density: 3.5,            // Medium-high density
  mode: 'vortex',
  force: 5,
  mouseRadius: 250,
  noise: 0.6,              // High turbulence
  minParticleSize: 0.2,
  maxParticleSize: 2,
}

For mesmerizing chaos effect (NEW):

{
  particleSize: 2,
  density: 4,              // Dense for smooth transitions
  mode: 'chaos',
  force: 4,                // Balanced force for mixed effects
  mouseRadius: 220,
  noise: 0.5,              // High noise enhances chaotic behavior
  minParticleSize: 0.3,
  maxParticleSize: 1.8,
  padding: 250,
}

Important Notes (v3.0+):

  • particleSize: Intuitive 0-5 scale where higher = larger particles (0=0.5px tiny, 5=4px large). Default: 2.5
  • density: Intuitive 0-5 scale where higher = more dense (0=sparse, 5=very dense). Default: 2.5. Recommended: 3-5 for smooth effects
  • force: With inverse square law physics, use lower values (2-5). Old linear physics needed higher values
  • noise: Controls randomness and spatial distortion (0 = no randomness, 1 = maximum chaos)
  • minParticleSize / maxParticleSize: Override automatic size calculation for custom variation
  • padding: Ensures particles can move outside image bounds without being cut off
  • gravity: Momentum-based - moving particles feel more gravity. Set to 0 for stable effects
  • mouseRadius: Actual effect radius is 1.5x larger with smooth falloff (no hard edges)

React Hook

useParticleEffect(options?: UseParticleEffectOptions)

Options:

  • settings?: Initial particle settings
  • autoStart?: Auto-start animation (default: true)
  • onParticlesGenerated?: Callback when particles are generated

Returns:

{
  canvasRef: React.RefObject<HTMLCanvasElement>;
  loadImage: (source: ImageSource) => Promise<void>;
  start: () => void;
  stop: () => void;
  updateSettings: (settings: PartialParticleSettings) => void;
  getParticleCount: () => number;
  getEngine: () => ParticleEngine | null;
}

Browser Capabilities Detection

The library includes automatic browser capabilities detection to select the best renderer:

import { detectCapabilities, logCapabilities } from 'particle-lsslib';

// Detect browser capabilities
const capabilities = detectCapabilities();

console.log(capabilities);
// {
//   supportsWebGL: true,
//   supportsWebGL2: true,
//   supportsOffscreenCanvas: true,
//   supportsWebGLInWorker: true,
//   maxTextureSize: 16384,
//   recommendedRenderer: 'webgl-worker'
// }

// Pretty-print capabilities to console
logCapabilities(capabilities);

Capabilities Explained:

  • supportsWebGL: Browser supports WebGL 1.0 (GPU rendering)
  • supportsWebGL2: Browser supports WebGL 2.0 (better GPU rendering)
  • supportsOffscreenCanvas: Browser supports OffscreenCanvas (rendering in workers)
  • supportsWebGLInWorker: Browser can use WebGL inside Web Workers
  • maxTextureSize: Maximum texture size supported by GPU
  • recommendedRenderer: Best renderer for this browser

Renderer Selection Logic:

  1. WebGL Worker (webgl-worker): If browser supports WebGL + OffscreenCanvas + Workers
  2. WebGL (webgl): If browser supports WebGL but not workers (coming soon)
  3. Canvas 2D (canvas2d): Fallback for all browsers

Force a Specific Renderer:

const engine = new ParticleEngineHybrid({
  canvas,
  renderer: 'canvas2d',  // Override automatic selection
  settings: { /* ... */ },
});

Performance Monitoring

Enable performance monitoring to track FPS, frame time, and particle count:

const engine = new ParticleEngineHybrid({
  canvas,
  enablePerformanceMonitoring: true,
  settings: { /* ... */ },
});

// Check metrics every second
setInterval(() => {
  const metrics = engine.getPerformanceMetrics();
  console.log(`
    FPS: ${metrics.fps}
    Frame Time: ${metrics.frameTime}ms
    Particles: ${metrics.particleCount}
    Renderer: ${metrics.renderer}
  `);
}, 1000);

Performance Metrics:

  • fps: Current frames per second (60 = smooth)
  • frameTime: Average time to render one frame in milliseconds
  • particleCount: Total number of active particles
  • renderer: Which renderer is being used

Performance Tips:

  • Lower density (0-2) for fewer particles and better FPS
  • Use particleSize 2-3 for balanced visual quality and performance
  • Lower noise (0-0.3) reduces computation
  • Canvas 2D: Good for < 10,000 particles
  • WebGL: Excellent for 10,000+ particles (coming soon)
  • WebGL Worker: Best for 50,000+ particles without blocking UI (coming soon)

Interaction Modes

Repel Mode

Particles are pushed away from the cursor using inverse square law (like magnetic repulsion). Creates a realistic spatial distortion effect where some particles escape and others resist based on their mass.

engine.updateSettings({
  mode: 'repel',
  force: 3,      // Lower values with new physics
  noise: 0.4,    // Adds turbulence and escape behavior
});

Attract Mode

Particles are pulled toward the cursor using gravitational physics. Lighter particles are pulled more strongly, heavier ones resist.

engine.updateSettings({
  mode: 'attract',
  force: 2,
  noise: 0.3,
});

Vortex Mode

Particles spiral around the cursor with tangential rotation and radial pull. High turbulence creates chaotic, organic movement.

engine.updateSettings({
  mode: 'vortex',
  force: 5,
  noise: 0.6,    // High turbulence for dramatic effect
});

Chaos Mode ⚡ NEW

A dynamic hybrid mode that seamlessly blends repel and vortex effects with time-based oscillations. Creates unpredictable, organic motion patterns that evolve over time. Each particle has its own unique phase, resulting in mesmerizing, ever-changing formations.

engine.updateSettings({
  mode: 'chaos',
  force: 4,      // Balanced force for mixed effects
  noise: 0.5,    // High noise enhances chaotic behavior
});

Chaos Mode Features:

  • Time-based blending: Dynamically oscillates between repel and vortex over time
  • Unique particle phases: Each particle follows its own oscillation pattern
  • Perpendicular waves: Additional wave forces create flowing, liquid-like motion
  • Enhanced turbulence: Extra randomness for truly chaotic, organic effects
  • Best for: Abstract art, mesmerizing animations, unpredictable interactions

Mouse Wheel Control:

All modes now support dynamic radius adjustment with the mouse wheel/scroll:

  • Scroll up: Increase interaction radius (up to 800px)
  • Scroll down: Decrease interaction radius (minimum 20px)
  • Changes take effect immediately without page reload

Physics Explained

Realistic Force Model (v2.0+)

The library now uses inverse square law physics, which means force strength follows the formula:

Force = (strength × mass_effect) / distance²

This creates more realistic interactions compared to linear forces:

Benefits:

  • Particles close to cursor experience exponentially stronger forces
  • Particles far away are barely affected
  • Creates natural "event horizon" where some particles escape
  • Matches real-world physics (gravity, magnetism, electrostatics)

Spatial Distortion Effect

Each particle has individual properties:

{
  mass: 0.5 to 2.0,              // Random mass affects resistance
  size: minParticleSize to maxParticleSize,  // Random size
  escapeChance: random()          // Probability to resist force
}

How it works:

  1. Lighter particles (mass < 1.0) are more affected by forces
  2. Heavier particles (mass > 1.0) resist forces better
  3. Distance matters: Particles further from cursor escape easier
  4. Randomness: Each frame, particles randomly resist based on their escape threshold

This creates the spatial distortion effect where:

  • Some particles are pulled/pushed strongly
  • Others barely move or escape entirely
  • Creates organic, non-uniform behavior
  • Looks like space-time is warping around the cursor

Noise & Turbulence

The noise parameter (0-1) adds several effects:

noise: 0.4  // Recommended for realistic distortion

Effects of noise:

  • Perpendicular forces: Particles get pushed at angles, not just radially
  • Brownian motion: Subtle random movement even when mouse is away
  • Turbulence: In vortex mode, creates chaotic spiral patterns
  • Escape behavior: Higher noise = more particles escape forces

Recommended Force Values

Due to inverse square law, use much lower force values than v1.x:

| Mode | Old (v1.x) | New (v2.0+) | v3.1+ | Effect | |------|-----------|-------------|-------|--------| | Repel | 8-15 | 2-5 | 2-5 | Realistic bubble | | Attract | 6-12 | 1-4 | 1-4 | Gravitational pull | | Vortex | 10-20 | 3-8 | 3-8 | Spiral with turbulence | | Chaos | - | - | 3-6 | Dynamic blend (NEW) |

Complete React Example

import React, { useState, useEffect } from 'react';
import { useParticleEffect, InteractionMode } from 'particle-lsslib';

function App() {
  const [mode, setMode] = useState<InteractionMode>('repel');
  const [force, setForce] = useState(3);
  const [noise, setNoise] = useState(0.4);

  const { canvasRef, loadImage, updateSettings } = useParticleEffect({
    settings: {
      mode,
      force,
      particleSize: 2,
      density: 4,              // Dense for smooth effect (0-5 scale: higher = more dense)
      width: 450,
      padding: 250,            // Space for particles to move freely
      mouseRadius: 200,        // Large interaction radius with smooth falloff
      gravity: 0,              // No gravity keeps particles stable
      backgroundColor: 'transparent',
      noise,                   // Spatial distortion and turbulence
      minParticleSize: 0.3,    // Very small particles
      maxParticleSize: 1.5,    // Varied sizes for organic look
    },
  });

  useEffect(() => {
    // Load default image on mount
    loadImage('/your-image.png');
  }, [loadImage]);

  const handleFileChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
    const file = e.target.files?.[0];
    if (file) await loadImage(file);
  };

  const handleModeChange = (newMode: InteractionMode) => {
    setMode(newMode);
    updateSettings({ mode: newMode });
  };

  const handleForceChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const newForce = parseFloat(e.target.value);
    setForce(newForce);
    updateSettings({ force: newForce });
  };

  const handleNoiseChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const newNoise = parseFloat(e.target.value);
    setNoise(newNoise);
    updateSettings({ noise: newNoise });
  };

  return (
    <div style={{ background: '#000', minHeight: '100vh', padding: '20px' }}>
      <div style={{ marginBottom: '20px', color: 'white' }}>
        <input type="file" accept="image/*" onChange={handleFileChange} />

        <div style={{ marginTop: '10px' }}>
          <label>Mode: </label>
          <select value={mode} onChange={(e) => handleModeChange(e.target.value as InteractionMode)}>
            <option value="repel">Repel</option>
            <option value="attract">Attract</option>
            <option value="vortex">Vortex</option>
            <option value="chaos">Chaos</option>
          </select>
        </div>

        <div style={{ marginTop: '10px' }}>
          <label>Force: {force} </label>
          <input
            type="range"
            min="1"
            max="10"
            step="0.5"
            value={force}
            onChange={handleForceChange}
          />
        </div>

        <div style={{ marginTop: '10px' }}>
          <label>Noise/Turbulence: {noise.toFixed(1)} </label>
          <input
            type="range"
            min="0"
            max="1"
            step="0.1"
            value={noise}
            onChange={handleNoiseChange}
          />
        </div>
      </div>

      <canvas ref={canvasRef} />
    </div>
  );
}

Use Cases

Overlay Effect with Transparent Background

Perfect for overlaying on other content:

const engine = new ParticleEngine({
  canvas,
  settings: {
    backgroundColor: 'transparent',
    padding: 300,
    width: 800,
  },
});

Contained Effect with Custom Background

For standalone displays:

const engine = new ParticleEngine({
  canvas,
  settings: {
    backgroundColor: '#0a0a0a',
    padding: 150,
    height: 600,  // Width auto-calculated
  },
});

Browser Support

Works in all modern browsers that support:

  • Canvas API
  • ES2020
  • RequestAnimationFrame

Changelog

v2.0.10 - WebGL Worker Implementation & Enhanced Padding Control (Current)

New Features:

  • WebGL Worker Renderer: Fully implemented WebGL rendering in Web Worker
    • Off-main-thread rendering for maximum performance
    • Uses OffscreenCanvas for zero-copy rendering
    • Handles 50,000+ particles without blocking UI
    • Automatic mouse event forwarding to worker
    • Graceful cleanup and fallback handling
  • Separate Padding Controls: New paddingX and paddingY options
    • Set different horizontal and vertical padding
    • Overrides uniform padding when specified
    • Useful for wide or tall canvas layouts

Improvements:

  • Fixed canvas dimension synchronization for WebGL Worker
  • Canvas dimensions now set before transferring to offscreen
  • Eliminates visual artifacts and rendering issues
  • Better error handling in worker initialization
  • Improved TypeScript types for padding options

API Additions:

// Separate padding control
engine.updateSettings({
  paddingX: 400,  // Horizontal padding
  paddingY: 200,  // Vertical padding
});

Performance:

  • WebGL Worker now production-ready
  • Up to 10x performance improvement for high particle counts
  • Smooth 60 FPS with 50,000+ particles
  • Main thread remains responsive during heavy rendering

v2.0.9 - Hybrid Rendering System

New Features:

  • ParticleEngineHybrid: New hybrid engine with automatic renderer selection
    • Detects browser capabilities (WebGL, WebGL2, OffscreenCanvas, Web Workers)
    • Automatically selects best renderer: Canvas 2D, WebGL, or WebGL Worker
    • Graceful fallback if preferred renderer not supported
  • Browser Capabilities Detection: detectCapabilities() and logCapabilities() utilities
    • Detect WebGL support and version
    • Check OffscreenCanvas and Web Worker support
    • Get maximum GPU texture size
    • Automatic renderer recommendation
  • Performance Monitoring: Track FPS, frame time, and particle count
    • Enable with enablePerformanceMonitoring: true
    • Access metrics with getPerformanceMetrics()
    • Monitor renderer performance in real-time
  • Enhanced Package Exports: Better ESM/CommonJS compatibility
    • Explicit exports field in package.json
    • Improved module resolution for bundlers

API Additions:

// New hybrid engine
new ParticleEngineHybrid({ canvas, renderer?, enablePerformanceMonitoring? })
engine.getRenderer()          // Get active renderer
engine.getCapabilities()       // Get browser capabilities
engine.getPerformanceMetrics() // Get FPS and performance data

// New utilities
detectCapabilities()  // Detect what browser supports
logCapabilities()     // Pretty-print capabilities

v3.1.0 - Chaos Mode & Enhanced Organics

New Features:

  • Chaos Mode: New 'chaos' interaction mode that dynamically blends repel and vortex effects with time-based oscillations
    • Time-based oscillation creates evolving, unpredictable patterns
    • Each particle has unique phase for organic variation
    • Perpendicular wave forces add flowing, liquid-like motion
    • Enhanced turbulence for truly chaotic effects
  • Dynamic Radius Control: Adjust mouse interaction radius with mouse wheel/scroll in real-time
    • Scroll up to increase radius (up to 800px)
    • Scroll down to decrease radius (minimum 20px - reduced from 50px)
    • Changes apply instantly without page reload
  • Enhanced Perlin Noise System: Upgraded brownian motion with pseudo-Perlin noise
    • Smoother, more organic movement patterns
    • Combines random noise with Perlin noise for natural flow
    • Subtle swirling motion based on distance from particle origin
    • Much more fluid and less chaotic than pure random

Improvements:

  • Lower minimum radius (20px instead of 50px) for finer control
  • Perlin noise creates more organic, flowing particle movement
  • Better noise implementation with layered sine waves
  • Chaos mode provides new creative possibilities for abstract animations

Usage:

// Try the new Chaos mode
engine.updateSettings({
  mode: 'chaos',
  force: 4,
  noise: 0.5,
  mouseRadius: 220,
});

// Adjust radius with mouse wheel while running
// Scroll up = larger radius, Scroll down = smaller radius

v3.0.0 - Natural Interactions & Intuitive Controls

Breaking Changes:

  • particleSize now uses 0-5 scale where higher = larger (was arbitrary pixel values)
  • density now uses 0-5 scale where higher = more dense (was inverted - higher meant less dense)
  • Effective mouse radius is now 1.5x the mouseRadius setting (creates smooth falloff zone)

New Features:

  • Smooth falloff function: Eliminates hard edges on mouse interactions using smoothstep interpolation
  • Natural interactions: Mouse effects now have invisible, organic boundaries (no visible radius circle)
  • Enhanced particle escape: 25-65% of particles now escape/ignore forces (was ~30%)
  • Momentum-based gravity: Gravity now amplifies with particle velocity for natural physics
  • Intuitive 0-5 scales: Both particleSize and density use logical scales
    • particleSize 0-5: 0=0.5px (tiny) → 5=4px (large)
    • density 0-5: 0=gap 10px (sparse) → 5=gap 1px (very dense)

Improvements:

  • Smoother, more organic mouse interactions without visible boundaries
  • More particles visually escape the mouse effect for better realism
  • Gravity feels more like momentum transfer than constant downward pull
  • Much more intuitive configuration - higher numbers = more/bigger
  • Better visual clarity - no harsh cutoff at interaction radius edge

Migration Guide:

// Old (v2.x) - confusing inverted density
{
  particleSize: 2,    // Arbitrary pixel value
  density: 4,         // Lower = MORE dense (confusing!)
}

// New (v3.0+) - intuitive scales
{
  particleSize: 2.5,  // 0-5 scale: higher = larger
  density: 2.5,       // 0-5 scale: higher = MORE dense (logical!)
  mouseRadius: 100,   // Actual effect is 1.5x with smooth falloff
}

v2.0.0 - Physics Overhaul

Breaking Changes:

  • Force values now use inverse square law - use lower values (2-5 instead of 8-15)
  • Global mouse tracking on window instead of canvas element

New Features:

  • ✨ Realistic inverse square law physics (like real gravity/magnetism)
  • ✨ Individual particle mass (0.5-2.0) affects resistance to forces
  • ✨ Variable particle sizes with minParticleSize and maxParticleSize
  • ✨ Spatial distortion effect - some particles escape, others resist
  • ✨ Configurable noise/turbulence with noise parameter (0-1)
  • ✨ Brownian motion for subtle random movement
  • ✨ Mass-based air friction (lighter particles have more drag)
  • ✨ Global mouse tracking works anywhere on page

Improvements:

  • More organic, realistic particle behavior
  • Better visual variation with size differences
  • Dramatic spatial warping effects
  • Improved physics simulation accuracy

Migration Guide:

// Old (v1.x)
{
  force: 10,
  mouseRadius: 100,
}

// New (v2.0+) - Use lower force values
{
  force: 3,           // Much lower with inverse square law
  mouseRadius: 200,   // Can increase radius for dramatic effect
  noise: 0.4,         // Add spatial distortion
  minParticleSize: 0.3,
  maxParticleSize: 1.5,
}

License

MIT

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Author

Created by lucqa.com by kdonjs

Keywords

particles canvas animation interactive physics react typescript webgl graphics spatial-distortion inverse-square-law realistic-physics