Documentación: particle-lsslib

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

bash
npm i particle-lsslib

Quick Start

Vanilla JavaScript/TypeScript

Option 1: Automatic Renderer Selection (Recommended)

typescript
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)

typescript
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

tsx
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

tsx
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

typescript
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:

typescript
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'

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

Get browser capabilities detected by the engine.

Returns:

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

Example:

typescript
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:

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

Example:

typescript
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

typescript
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
typescript
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.

typescript
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

typescript
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)

typescript
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

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

3. Custom padding (uniform or separate)

typescript
// 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):

typescript
{
  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):

typescript
{
  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:

typescript
{
  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:

typescript
{
  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):

typescript
{
  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:

typescript
{
  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:

typescript
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:

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

Performance Monitoring

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

typescript
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.

typescript
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.

typescript
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.

typescript
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.

typescript
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:

plaintext
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:

typescript
{
  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:

typescript
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:

ModeOld (v1.x)New (v2.0+)v3.1+Effect
Repel8-152-52-5Realistic bubble
Attract6-121-41-4Gravitational pull
Vortex10-203-83-8Spiral with turbulence
Chaos--3-6Dynamic blend (NEW)

Complete React Example

tsx
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:

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

Contained Effect with Custom Background

For standalone displays:

typescript
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:

typescript
// 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:

typescript
// 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:

typescript
// 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:

typescript
// 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:

typescript
// 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