npm package: @sangdonpark/falling-effects
Description: Lightweight falling effects (snow, flower, leaf) for any website
Latest version: 0.1.3

# ❄️ falling-effects

Lightweight, dependency-free falling effects for any website.  
Snow, flowers, and leaves with smooth diagonal motion and natural sway.

- No framework required (Vanilla JS)
- Zero dependencies
- ESM & CJS supported
- Smooth, continuous animation (no pauses)

---

## ✨ Features

- ❄️ Snow / 🌸 Flower / 🍃 Leaf particles
- Vertical or diagonal falling direction
- Continuous spawn mode (no sudden gaps)
- Natural sway motion (wind-like movement)
- SSR-safe (does nothing on the server)
- Cleanup function included

---

## 📦 Installation

```bash
npm install @sangdonpark/falling-effects
```

## 🚀 Usage

### Next.js (App Router)

This library must be used in a Client Component.

```ts
"use client";

import { useEffect } from "react";
import { startFalling } from "@sangdonpark/falling-effects";

export default function FallingEffect() {
  useEffect(() => {
    const cleanup = startFalling({
      type: "flower",
      mode: "rate",
      rate: 30,
      maxParticles: 120,
      rampUpSeconds: 1.5,
      duration: 10,
      direction: "diagonal",
      diagonalDirection: "left-to-right",
      diagonalStrength: 200,
      sway: true,
    });

    return () => cleanup();
  }, []);

  return null;
}
```

Add the component to app/layout.tsx to enable the effect globally.

```ts
import FallingEffect from "@/components/FallingEffect";

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html>
      <body>
        <FallingEffect />
        {children}
      </body>
    </html>
  );
}
```

### React

Use inside useEffect.
startFalling() returns a cleanup function

```ts
import { useEffect } from "react";
import { startFalling } from "@sangdonpark/falling-effects";

export default function FallingEffect() {
  useEffect(() => {
    const cleanup = startFalling({
      type: "flower",
      mode: "rate",
      rate: 30,
      maxParticles: 120,
      rampUpSeconds: 1.5,
      duration: 10,
      direction: "diagonal",
      diagonalDirection: "left-to-right",
      diagonalStrength: 200,
      sway: true,
    });

    return () => cleanup();
  }, []);

  return null;
}
```

### Vanilla JavaScript (Browser)

Call `startFalling()` after the DOM is ready.

```html
<!DOCTYPE html>
<html>
  <body>
    <script type="module">
      import { startFalling } from "https://unpkg.com/@sangdonpark/falling-effects";

      const cleanup = startFalling({
        type: "flower",
        mode: "rate",
        rate: 30,
        maxParticles: 120,
        rampUpSeconds: 1.5,
        duration: 10,
        direction: "diagonal",
        diagonalDirection: "left-to-right",
        diagonalStrength: 200,
        sway: true,
      });

      // Stop the effect later
      // cleanup();
    </script>
  </body>
</html>
```

## ⚙️ Options

### Particle

| Option | Type                           | Default  | Description             |
| ------ | ------------------------------ | -------- | ----------------------- |
| `type` | `"snow" \| "flower" \| "leaf"` | `"snow"` | Particle type to render |

---

### Spawn

| Option          | Type                | Default     | Description                                            |
| --------------- | ------------------- | ----------- | ------------------------------------------------------ |
| `mode`          | `"rate" \| "count"` | `"rate"`    | Spawn mode                                             |
| `rate`          | `number`            | `10`        | Particles spawned per second (rate mode)               |
| `density`       | `number`            | `30`        | Target particle count (count mode)                     |
| `maxParticles`  | `number`            | `150`       | Maximum number of active particles                     |
| `duration`      | `number`            | `undefined` | Spawn duration in seconds (omit for continuous effect) |
| `rampUpSeconds` | `number`            | `0`         | Smooth ramp-up time in seconds                         |

---

### Motion

| Option              | Type                                 | Default           | Description                   |
| ------------------- | ------------------------------------ | ----------------- | ----------------------------- |
| `direction`         | `"vertical" \| "diagonal"`           | `"vertical"`      | Falling direction             |
| `diagonalDirection` | `"left-to-right" \| "right-to-left"` | `"left-to-right"` | Diagonal movement direction   |
| `diagonalStrength`  | `number`                             | `120`             | Strength of diagonal movement |
| `speedMultiplier`   | `number`                             | `1`               | Global speed multiplier       |

---

### Sway

| Option               | Type               | Default      | Description                |
| -------------------- | ------------------ | ------------ | -------------------------- |
| `sway`               | `boolean`          | `false`      | Enable natural sway motion |
| `swayAmplitudeRange` | `[number, number]` | `[10, 30]`   | Sway amplitude range (px)  |
| `swayFrequencyRange` | `[number, number]` | `[0.2, 0.8]` | Sway frequency range (Hz)  |

---

### Layout

| Option   | Type     | Default | Description                     |
| -------- | -------- | ------- | ------------------------------- |
| `zIndex` | `number` | `9999`  | z-index of the effect container |

---

## 🎛 Presets

### ❄️ Snow / 🌸 Flower / 🍃 Leaf (Classic)

```ts
startFalling({
  type: "snow | flower | leaf",
  mode: "rate",
  rate: 30,
  maxParticles: 120,
  rampUpSeconds: 1.5,
  duration: 10,
  direction: "diagonal",
  diagonalDirection: "left-to-right",
  diagonalStrength: 200,
  sway: true,
});
```