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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@basementuniverse/particles-2d

v1.2.1

Published

A component for animating and rendering particles in 2d games

Downloads

16

Readme

Game Component: Particle System 2D

A basic particle system component for use in 2d games.

Installation

npm install @basementuniverse/particles-2d

How to use

See /demos for some examples.

  1. Import the component and any other classes you need:
import {
  ParticleSystem,
  Emitter,
  Attractor,
  ForceField,
  Collider,
} from '@basementuniverse/particles-2d';
  1. Create a ParticleSystem instance:
const particleSystem = new ParticleSystem();
  1. Add emitters, attractors, force fields, and colliders as needed:
particleSystem.emitters.push(
  new Emitter(/* See below for options */)
);

particleSystem.attractors.push(
  new Attractor(/* See below for options */)
);

particleSystem.forceFields.push(
  new ForceField(/* See below for options */)
);

particleSystem.colliders.push(
  new Collider(/* See below for options */)
);
  1. Update and render the particle system in your game loop:
function update(deltaTime) {
  particleSystem.update(deltaTime);

  // Other game logic...
}

function draw(context) {
  particleSystem.draw(context);

  // Other rendering logic...
}

Utility types

vec2

{ x: number, y: number }

Color

{ r: number, g: number, b: number, a?: number }

Emitters

Emitters are responsible for generating and emitting particles.

new Emitter(
  position: vec2, // { x: number, y: number }
  size: vec2, // { x: number, y: number }
  lifespan: number, // in seconds, use -1 for infinite lifespan
  options?: EmitterOptions // see below...
);

Emitter Options

{
  particles: {
    position
    speed
    direction
    size
    rotation
    lifespan
    style
    options
  },

  emission: {
    type
    rate, n, delay, f // depends on type
  }
}

particles.position

Define the initial position of particles:

  • 'uniform': particles will be uniformly distributed within the emitter's area as defined by its position (this is the center) and size
  • 'normal': particles will be normally distributed within the emitter's area, i.e. more particles will be generated near the center of the emitter and fewer towards the edges
  • (n: number) => vec2: a function that returns a position for each particle, where n is the index of the particle being emitted (maybe useful if we're emitting multiple particles in a single frame)

particles.speed

Define the initial speed of particles:

  • number: a constant speed for all particles
  • { min: number, max: number }: a range of speeds for particles, each particle will have a random speed within this range
  • (n: number) => number: a function that returns a speed for each particle, where n is the index of the particle being emitted

particles.direction

Define the initial direction of particles in radians (0 is right):

  • number: a constant direction for all particles
  • { min: number, max: number }: a range of directions for particles, each particle will have a random direction within this range
  • (n: number) => number: a function that returns a direction for each particle, where n is the index of the particle being emitted

particles.size

Define the initial size of particles:

  • { x: number, y: number }: a constant size for all particles
  • { min: { x, y }, max: { x, y } }: a range of sizes for particles, each particle will have a random size within this range
  • (n: number) => { x: number, y: number }: a function that returns a size for each particle, where n is the index of the particle being emitted

particles.rotation

Define the initial rotation of particles in radians (0 is right):

  • null: rotation will be calculated based on the particle's velocity
  • number: a constant rotation for all particles
  • { min: number, max: number }: a range of rotations for particles, each particle will have a random rotation within this range
  • (n: number) => number: a function that returns a rotation for each particle, where n is the index of the particle being emitted

particles.lifespan

Define the lifespan of particles in seconds:

  • number: a constant lifespan for all particles
  • { min: number, max: number }: a range of lifespans for particles, each particle will have a random lifespan within this range
  • (n: number) => number: a function that returns a lifespan for each particle, where n is the index of the particle being emitted

particles.style

There are a few default "built-in" styles:

{
  // the size of the dot will be max(size.x, size.y)
  style: 'dot';
  color: Color | string | Color[] | string[]; // fixed or random color
  glow?: {
    color: Color | string | Color[] | string[];
    amount: number;
  };
  fade?: {
    in: number; // fade in duration in seconds
    out: number; // fade out duration in seconds
  };
}
{
  // the size of the radial gradient will be max(size.x, size.y)
  style: 'radial';
  color: Color | string | Color[] | string[]; // fixed or random color
  fade?: {
    in: number; // fade in duration in seconds
    out: number; // fade out duration in seconds
  };
}
{
  // the length of the line will be size.x, and the width will be size.y
  style: 'line';
  color: Color | string | Color[] | string[];
  rotationOffset?: number; // how much to offset the particle's rotation in radians
  glow?: {
    color: Color | string | Color[] | string[];
    amount: number;
  };
  fade?: {
    in: number; // fade in duration in seconds
    out: number; // fade out duration in seconds
  };
}
{
  // the size of the image is defined by size.x and size.y
  style: 'image';
  image: HTMLImageElement; // the image to render
  rotationOffset?: number; // how much to offset the particle's rotation in radians
  fade?: {
    in: number; // fade in duration in seconds
    out: number; // fade out duration in seconds
  };
}

particles.style.trail

Particles can optionally have a trail effect.

{
  // the size of the image is defined by size.x and size.y
  style: '...';
  // other style options (see above)...
  trail: {
    length: number; // how many trail segments to keep
    color?: Color | string | Color[] | string[]; // fixed or random color for the trail, if not provided we use the particle's color
    width?: number; // width of the trail segments, if not provided we use the particle's size
    widthDecay?: number; // how much to decay the width of the trail segments, 0 means no decay, 1 means full decay (the trail will disappear over its length), negative numbers cause the trail to grow wider over its length
    segmentFade?: {
      in: number; // how many segments to fade in at the start of the trail
      out: number; // how many segments to fade out at the end of the trail
    };
  }
}

particles.options

{
  useAttractors: boolean; // whether particles from this emitter should be affected by attractors
  useForceFields: boolean; // whether particles from this emitter should be affected by force fields
  useColliders: boolean; // whether particles from this emitter should be affected by colliders

  defaultUpdates: 'none' | 'all' | ParticleDefaultUpdateTypes;
  update?: (system: ParticleSystem, dt: number) => void;

  defaultDraws: 'none' | 'all' | ParticleDefaultDrawTypes;
  preDraw?: (system: ParticleSystem, context: CanvasRenderingContext2D) => void;
  postDraw?: (system: ParticleSystem, context: CanvasRenderingContext2D) => void;
}

Default update types:

  • age: update the age of particles and handle disposal
  • physics: update the velocity of particles based on forces, collisions, etc.
  • direction: update the direction of particles based on their velocity
  • position: integrate the position of particles based on their velocity

Default draw types:

  • transforms: apply transformations like translation and rotation
  • fade: apply fade in/out effects to particles
  • styles: render the particle's style (dot, radial, line, image)

emission

Various particle emission types are available:

{
  // emit some number of particles per second
  type: 'rate';
  rate: number | { min: number, max: number };
}

If rate is a random range, the rate will be updated every second.

{
  // emit some number of particles immediately and then automatically dispose the emitter
  type: 'burst';
  n: number | { min: number, max: number };
  delay?: number; // optional delay in seconds before emitting
}
{
  // custom function, this will be called every frame and should return the number of particles to emit
  type: 'custom';
  f: () => number;
}

Attractors

Attractors can attract or repel particles in range of the attractor.

new Attractor(
  position: vec2, // { x: number, y: number }
  range: number,
  force: number, // negative for repulsion, positive for attraction
  falloff: number,
  lifespan: number // use -1 for infinite lifespan
);

Force Fields

Force fields apply a force to all particles.

new ForceField(
  force: vec2, // { x: number, y: number }
  lifespan: number // use -1 for infinite lifespan
);

Colliders

Colliders are used for detecting and handling collisions with particles.

new Collider(
  geometry, // see below...
  restitution: number, // how bouncy the collider is, 0 is no bounce, 1 is full bounce
  friction: number, // how much friction the collider has, 0 is no friction,  1 is full friction
  randomness: number // how much to randomly offset the direction of particles when they collide, 0 is no randomness, 1 is full randomness (the particle will be offset randomly +/- PI radians)
);

Collider Geometry

Collider geometry can be defined in various ways:

{
  type: 'circle';
  position: vec2; // { x: number, y: number }
  radius: number;
}
{
  type: 'rectangle';
  position: vec2; // { x: number, y: number }
  size: vec2; // { x: number, y: number }
  rotation?: number; // optional rotation in radians
}
{
  type: 'polygon';
  vertices: vec2[]; // array of vertices defining the polygon
}