# Canvas Particles JS

<span class="badge-npmversion"><a href="https://npmjs.org/package/canvasparticles-js" title="View this project on NPM"><img src="https://img.shields.io/npm/v/canvasparticles-js.svg" alt="NPM version" /></a></span>
<span class="badge-npmversion"><a href="https://npmjs.org/package/canvasparticles-js" title="View this project on NPM"><img src="https://img.shields.io/npm/d18m/canvasparticles-js.svg" alt="NPM downloads" /></a></span>
<span><a href="https://www.jsdelivr.com/package/npm/canvasparticles-js" title="View this project on jsDelivr"><img src="https://data.jsdelivr.com/v1/package/npm/canvasparticles-js/badge?style=rounded" alt="jsDelivr hits" /></a></span>
<span><a href="https://github.com/Khoeckman/canvasparticles-js/actions" title="View GitHub workflows"><img src="https://github.com/Khoeckman/canvasparticles-js/actions/workflows/node.js.yml/badge.svg" alt="GitHub workflows" /></a></span>

## Description

In an HTML canvas, a bunch of floating particles connected with lines when they approach each other.
Creating a fun and interactive background. Colors, interaction and gravity can be customized!

[Showcase](#showcase)<br>
[Import](#import)<br>
[Implementation](#implementation)<br>
[Class Instantiation](#class-instantiation)<br>
[Options](#options)<br>
[Manually creating particles](#manually-creating-particles)<br>
[One Pager Example](#one-pager-example)

---

## Showcase

If you dont like reading documentation this website is for you:<br>

[https://khoeckman.github.io/canvasparticles-js/](https://khoeckman.github.io/canvasparticles-js/)

[![Banner with particles and title: Canvas Particles](./demo/banner.webp)](https://khoeckman.github.io/canvasparticles-js/)

---

## Import

Particles will be drawn onto a `<canvas>` element.

```html
<canvas id="my-canvas"></canvas>
```

### npm

```bash
npm install canvasparticles-js
# or
pnpm add canvasparticles-js
```

**ES Module import**

```js
import CanvasParticles from 'canvasparticles-js'
```

If you don't have a bundler:

```js
import CanvasParticles from './node_modules/canvasparticles-js/dist/index.mjs'
```

**Global import**

```html
<script src="./node_modules/canvasparticles-js/dist/index.umd.js" defer></script>
```

No import required in each JavaScript file!

### Import with jsDelivr

**ES Module import**

```js
import CanvasParticles from 'https://cdn.jsdelivr.net/npm/canvasparticles-js/dist/index.min.mjs'
```

**Global import**

```html
<script src="https://cdn.jsdelivr.net/npm/canvasparticles-js/dist/index.umd.min.js" defer></script>
```

---

## Implementation

### Start animating

```js
const selector = '#my-canvas' // Query Selector for the canvas
const options = { ... } // See #options
new CanvasParticles(selector, options).start()
```

### Starting and stopping animation

```js
const particles = new CanvasParticles(selector, options)
particles.start()
particles.stop()
```

To keep the last frame visible when stopping the animation:

```js
particles.stop({ clear: false }) // Default: true
```

### Destruction

Gracefully destroy the instance and remove the canvas element.

```js
particles.destroy()
delete particles // Optional
```

---

## Class Instantiation

### Valid ways to instantiate `CanvasParticles`

```js
const selector = '#my-canvas'
const options = {}
const canvasElement = document.querySelector(selector)

let instance, canvas

// Basic instantiation
instance = new CanvasParticles(selector)
instance = new CanvasParticles(canvasElement)

// Instantiation with custom options
instance = new CanvasParticles(selector, options)
instance = new CanvasParticles(canvasElement, options)
```

### Chaining methods

You can chain `.start()` for a cleaner syntax:

```js
instance = new CanvasParticles(selector).start()

// Access the canvas directly
canvas = new CanvasParticles(selector).canvas
canvas = new CanvasParticles(selector).start().canvas
```

### Without chaining

If you prefer not to chain methods, you can instantiate first and start later:

```js
instance = new CanvasParticles(selector)
instance.start()
canvas = instance.canvas
```

### Incorrect usage

The following will not return the expected value because `CanvasParticles` only supports method chaining for `.start()`:

```js
instance = new CanvasParticles(selector).anyOtherMethod()
canvas = new CanvasParticles(selector).anyOtherMethod().canvas
```

## Options

Options to change the particles and their behavior aswell as what happens on `MouseMove` or `Resize` events.<br>
Play around with these values in the [Sandbox](https://khoeckman.github.io/canvasparticles-js/#sandbox).

### Options Object

The default value will be used when an option is assigned an invalid value.

**TypeScript Usage**:
Both `float` and `integer` mean `number`.<br>
`integer` is used to specify that the number is internally floored to a whole number.

### Root options

| Option       | Type              | Default | Description                                                                         |
| ------------ | ----------------- | ------- | ----------------------------------------------------------------------------------- |
| `background` | `string \| false` | `false` | Canvas background. Any valid CSS `background` value. Ignored when strictly `false`. |

---

### `animation`

It's best to not touch these values if it's unclear what it does.

| Option                   | Type      | Default | Description                                          |
| ------------------------ | --------- | ------- | ---------------------------------------------------- |
| `animation.startOnEnter` | `boolean` | `true`  | Start animation when the canvas enters the viewport. |
| `animation.stopOnLeave`  | `boolean` | `true`  | Stop animation when the canvas leaves the viewport.  |

---

### `mouse`

| Option                  | Type          | Default | Description                                                                                                  |
| ----------------------- | ------------- | ------- | ------------------------------------------------------------------------------------------------------------ |
| `mouse.interactionType` | `0 \| 1 \| 2` | `2`     | Mouse interaction mode.<br>`0 = NONE`, `1 = SHIFT`, `2 = MOVE`.                                              |
| `mouse.connectDistMult` | `float`       | `2/3`   | Multiplier applied to `particles.connectDistance` to compute mouse interaction distance.                     |
| `mouse.distRatio`       | `float`       | `2/3`   | Controls how strongly particles are pulled toward the mouse. Keep equal to or above `mouse.connectDistMult`. |

**Interaction types** (enum)

- `NONE (0)` – No interaction
- `SHIFT (1)` – Visual displacement only
- `MOVE (2)` – Actual particle movement (default)

---

### `particles`

| Option                      | Type          | Default    | Description                                                                                       |
| --------------------------- | ------------- | ---------- | ------------------------------------------------------------------------------------------------- |
| `particles.generationType`  | `0 \| 1 \| 2` | `false`    | Auto-generate particles on initialization and when the canvas resizes. `1 = OFF`, `2 = MATCH`     |
| `particles.color`           | `string`      | `'black'`  | Particle and connection color. Any CSS color format.                                              |
| `particles.ppm`             | `integer`     | `100`      | Particles per million pixels.                                                                     |
| `particles.max`             | `integer`     | `Infinity` | Maximum number of particles allowed.                                                              |
| `particles.maxWork`         | `integer`     | `Infinity` | Maximum total connection length per particle. Lower values stabilize performance but may flicker. |
| `particles.connectDistance` | `integer`     | `150`      | Maximum distance for particle connections (px).                                                   |
| `particles.relSpeed`        | `float`       | `1`        | Relative particle speed multiplier.                                                               |
| `particles.relSize`         | `float`       | `1`        | Relative particle size multiplier.                                                                |
| `particles.rotationSpeed`   | `float`       | `2`        | Direction change speed.                                                                           |
| `particles.drawLines`       | `boolean`     | `true`     | Whether to draw lines between particles.                                                          |

**Generation types** (enum)

- `OFF (0)` – Never auto-generate particles
- `NEW (1)` – Generate all particles from scratch
- `MATCH (2)` – Add or remove some particles to match the new count (default)

---

### `gravity`

Enabling gravity (`repulsive` or `pulling` > 0) performs an extra **O(n²)** gravity computations per frame.

| Option                | Type    | Default    | Description                                                                                          |
| --------------------- | ------- | ---------- | ---------------------------------------------------------------------------------------------------- |
| `gravity.repulsive`   | `float` | `0`        | Repulsive force between particles.                                                                   |
| `gravity.pulling`     | `float` | `0`        | Attractive force between particles. Requires sufficient repulsion to avoid clustering.               |
| `gravity.friction`    | `float` | `0.8`      | Damping factor applied to gravitational velocity each update (`0.0 – 1.0`).                          |
| `gravity.maxVelocity` | `float` | `Infinity` | Clamp the velocity of each particle to this value to prevent explosions under heavy pulling gravity. |

---

### `debug`

Intended for development and debugging. Can be useful to understand internal behavior.

| Option              | Type      | Default | Description                                     |
| ------------------- | --------- | ------- | ----------------------------------------------- |
| `debug.drawGrid`    | `boolean` | `false` | Renders the spatial grid used for partitioning. |
| `debug.drawIndexes` | `boolean` | `false` | Displays particle indices on the canvas.        |

---

### Update options on the fly

You can update every option while an instance is animating and it works great; but some options require an extra step.

#### Using the available setter

These options are the only ones that have and require a dedicated setter to ensure proper integration:

- `background`
- `mouse.connectDistMult`
- `particles.color`

```js
const instance = new CanvasParticles(selector, options)

// Use the setters to update these specific options
instance.setBackground('red')
instance.setMouseConnectDistMult(0.8)
instance.setParticleColor('hsl(149, 100%, 50%)')
```

#### Changing the particle count

After updating the following options, the number of particles is **not automatically adjusted**:

- `particles.ppm`
- `particles.max`

```js
// Note: the backing field is called `option` not `options`!
instance.option.particles.ppm = 100
instance.option.particles.max = 300
```

The changes are only applied when one of the following methods is called:

```js
instance.newParticles() // Remove all particles and create the correct amount of new ones
instance.matchParticleCount() // Add or remove some particles to match the count
```

#### Changing particle properties

After updating the following options, the particles are **not automatically updated**:

- `particles.relSize`
- `particles.relSpeed`

```js
// Note: the backing field is called `option` not `options`!
instance.option.particles.relSize = 2
instance.option.particles.relSpeed = 3
```

The changes are only applied when the following method is called:

```js
instance.updateParticles() // Updates the particle.speed and particle.size properties without regenerating any particles
```

#### Modifying object properties

**All** other options can be updated by only modifying the `option` internal field properties, with changes taking effect immediately.

> The new option values are not validated. Assigning invalid values will lead to unexpected behavior. It is therefore recommended to use the [options setter](#updating-entire-options-object).

```js
// Note: the backing field is called `option` not `options`!
instance.option.mouse.interactionType = 0
instance.option.particles.connectDist = 200
instance.option.gravity.repulsive = 1
```

#### Updating entire options object

To reinitialize all options, pass a new options object to the `options` setter.

> Existing particles their properties will not be updated automatically. [Changing particle properties](#changing-particle-properties)

```js
instance.options = { ... }
```

---

## Manually creating particles

```ts
createParticle(posX?: number, posY?: number, dir?: number, speed?: number, size?: number)
```

By default `particles.ppm` and `particles.max` are used to auto-generate random particles. To turn this off, set at least one of these properties to `0` or set `particles.generationType` to `OFF (0)` (slightly more efficient).

```js
const canvas = '#my-canvas'
const options = {
  particles: {
    generationType: CanvasParticles.generationType.OFF, // = 0
    rotationSpeed: 0,
  },
}
const instance = new CanvasParticles(canvas, options).start()

// Create a horizontal line of particles moving down
for (let x = 0; x < instance.width; x += 4) {
  instance.createParticle(x, 100, 0, 1, 5)
}

// Keep automatically generated particles and remove manually created ones
instance.newParticles({ keepAuto: true, keepManual: false })
```

---

## One Pager Example

```html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <style>
      #canvas-particles {
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        z-index: -1;
      }
    </style>
  </head>

  <body>
    <canvas id="canvas-particles"></canvas>

    <script src="https://cdn.jsdelivr.net/npm/canvasparticles-js/dist/index.umd.js"></script>
    <script>
      const selector = '#canvas-particles'
      const options = {
        background: 'hsl(125, 42%, 35%)',
        mouse: {
          interactionType: CanvasParticles.interactionType.SHIFT, // = 1
        },
        particles: {
          color: 'rgba(150, 255, 105, 0.95)',
          max: 200,
        },
      }
      new CanvasParticles(selector, options).start()
    </script>
  </body>
</html>
```
