jl-particle-interactive 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,236 @@
1
+ # jl-particle-interactive — React Canvas Particle Animations
2
+
3
+ [![npm version](https://img.shields.io/npm/v/jl-particle-interactive)](https://www.npmjs.com/package/jl-particle-interactive)
4
+ [![npm downloads](https://img.shields.io/npm/dm/jl-particle-interactive)](https://www.npmjs.com/package/jl-particle-interactive)
5
+ [![bundle size](https://img.shields.io/bundlephobia/minzip/jl-particle-interactive)](https://bundlephobia.com/package/jl-particle-interactive)
6
+ [![license](https://img.shields.io/npm/l/jl-particle-interactive)](https://github.com/cjorgeluis122333/jl-particles-interactive/blob/main/LICENSE)
7
+ [![TypeScript](https://img.shields.io/badge/TypeScript-strict-blue)](https://www.typescriptlang.org/)
8
+
9
+ A canvas-based React library for rendering text and backgrounds as thousands of animated particles. Letters form from particle swarms, respond to magnetic hover, attract or repel on click, and backgrounds come alive with NET graphs, JELLYFISH glows, or pointer-following swarms. Built with zero runtime dependencies, full TypeScript support, and DPR-aware rendering for sharp output on retina displays.
10
+
11
+ > Requires React 18+. No global CSS. Zero runtime dependencies.
12
+
13
+ ---
14
+
15
+ ## Why jl-particle-interactive?
16
+
17
+ | Feature | jl-particle-interactive | tsparticles | particles.js |
18
+ |---|---|---|---|
19
+ | **Text that forms from particles** | ✓ (native, spatial coherence) | Plugin only (complex setup) | ✗ |
20
+ | **Spring physics + float noise** | ✓ | ✗ | ✗ |
21
+ | **Magnetic hover / click interact** | ✓ (attract & repel) | ✗ | ✗ |
22
+ | **Ready-made background presets** | ✓ NET, JELLYFISH, FOLLOW_POINTER | General engine (DIY) | General engine (DIY) |
23
+ | **Runtime dependencies** | **Zero** | 14+ packages | 0 (vanilla JS only) |
24
+ | **React integration** | Native React hooks | Wrapper package needed | Manual integration |
25
+ | **TypeScript** | Strict mode | Partial | ✗ |
26
+ | **DPR-aware (retina)** | ✓ | ✗ | ✗ |
27
+
28
+ ---
29
+
30
+ ## Use cases
31
+
32
+ - **Hero section animated titles** — Text materializes from particle chaos on page load
33
+ - **Loading screens** — Words form and dissolve while content loads
34
+ - **Interactive word carousels** — Cycle through words; particles re-form smoothly
35
+ - **Animated backgrounds** — NET graph, JELLYFISH glow, or pointer-following swarm behind any content
36
+
37
+ ---
38
+
39
+ ## Installation
40
+
41
+ ```bash
42
+ npm install jl-particle-interactive
43
+ ```
44
+
45
+ ---
46
+
47
+ ## Framework compatibility
48
+
49
+ | Framework | Supported |
50
+ |---|---|
51
+ | Vite + React | ✓ |
52
+ | Next.js (App Router & Pages) | ✓ (client components only — add `'use client'`) |
53
+ | Create React App | ✓ |
54
+ | Remix | ✓ (client-side only) |
55
+ | Astro | ✓ (inside `client:only` components) |
56
+ | TypeScript | ✓ (strict mode, declarations included) |
57
+
58
+ ---
59
+
60
+ ## What it offers
61
+
62
+ | Feature | Description |
63
+ |---|---|
64
+ | **Text particles** | Thousands of canvas particles that form any text string using pixel-sampling with spatial coherence — letters look sharp at any size |
65
+ | **Magnetic hover** | Particles are attracted to the cursor on hover (~173px radius spring force) |
66
+ | **Click interactions** | Attract or repel particles on click/tap — particles flee or swarm toward the pointer |
67
+ | **Animated backgrounds** | NET (connected node graph), JELLYFISH (organic glow rings), and FOLLOW_POINTER (swarm) modes |
68
+ | **Spring physics** | Each particle uses spring + friction + float-noise physics — movement feels natural, never robotic |
69
+ | **Customizable** | Colors (hex or RGB palettes), shapes (circle, square, bean), density, speed, ease, and more |
70
+
71
+ ---
72
+
73
+ ## Quick start
74
+
75
+ ```tsx
76
+ import { ParticleCanvas, TextParticleEngine } from 'jl-particle-interactive';
77
+
78
+ export default function App() {
79
+ return (
80
+ <ParticleCanvas height="60vh">
81
+ <TextParticleEngine text="Hello" />
82
+ </ParticleCanvas>
83
+ );
84
+ }
85
+ ```
86
+
87
+ ---
88
+
89
+ ## Examples
90
+
91
+ ### Text with a color palette
92
+
93
+ Assign multiple colors and each particle picks one at random.
94
+
95
+ ```tsx
96
+ <ParticleCanvas height="60vh">
97
+ <TextParticleEngine
98
+ text="React"
99
+ particleColor={['#ff6b6b', '#feca57', '#48dbfb']}
100
+ particleSize={1.5}
101
+ />
102
+ </ParticleCanvas>
103
+ ```
104
+
105
+ ---
106
+
107
+ ### Repel on click
108
+
109
+ Particles flee from the cursor while the mouse button is held down.
110
+
111
+ ```tsx
112
+ <ParticleCanvas height="60vh">
113
+ <TextParticleEngine
114
+ text="Boom"
115
+ clickMode="repel"
116
+ particleEase={2}
117
+ />
118
+ </ParticleCanvas>
119
+ ```
120
+
121
+ ---
122
+
123
+ ### Attract on click
124
+
125
+ The opposite — particles swarm toward the cursor on press.
126
+
127
+ ```tsx
128
+ <ParticleCanvas height="60vh">
129
+ <TextParticleEngine
130
+ text="Pull"
131
+ clickMode="attract"
132
+ isMagnet={false}
133
+ />
134
+ </ParticleCanvas>
135
+ ```
136
+
137
+ ---
138
+
139
+ ### Animated NET background
140
+
141
+ A connected particle network moves behind your content.
142
+
143
+ ```tsx
144
+ <ParticleCanvas
145
+ height="80vh"
146
+ background={{
147
+ name: 'NET',
148
+ color: '#4ecdc4',
149
+ lineDistance: 120,
150
+ density: 0.8,
151
+ }}
152
+ >
153
+ <TextParticleEngine text="Network" particleColor="#ffffff" />
154
+ </ParticleCanvas>
155
+ ```
156
+
157
+ ---
158
+
159
+ ### Jellyfish background
160
+
161
+ Smooth, organic blobs that drift across the canvas.
162
+
163
+ ```tsx
164
+ <ParticleCanvas
165
+ height="80vh"
166
+ background={{
167
+ name: 'JELLYFISH',
168
+ colors: ['#ff6b6b', '#a29bfe', '#00cec9'],
169
+ colorMode: 'wave',
170
+ }}
171
+ >
172
+ <TextParticleEngine text="Fluid" particleColor="#ffffff" />
173
+ </ParticleCanvas>
174
+ ```
175
+
176
+ ---
177
+
178
+ ### Dynamic text
179
+
180
+ Change the `text` prop and the particles re-form automatically.
181
+
182
+ ```tsx
183
+ const words = ['Hello', 'World', 'React'];
184
+
185
+ export default function Carousel() {
186
+ const [index, setIndex] = useState(0);
187
+
188
+ useEffect(() => {
189
+ const id = setInterval(() => setIndex(i => (i + 1) % words.length), 2000);
190
+ return () => clearInterval(id);
191
+ }, []);
192
+
193
+ return (
194
+ <ParticleCanvas height="60vh">
195
+ <TextParticleEngine text={words[index]} />
196
+ </ParticleCanvas>
197
+ );
198
+ }
199
+ ```
200
+
201
+ ---
202
+
203
+ ## API reference
204
+
205
+ ### `<ParticleCanvas>`
206
+
207
+ | Prop | Type | Default | Description |
208
+ |---|---|---|---|
209
+ | `width` | `string \| number` | `'100%'` | Container width |
210
+ | `height` | `string \| number` | `'60vh'` | Container height |
211
+ | `backgroundColor` | `string` | `'#050505'` | Background color |
212
+ | `background` | `BackgroundCanvas` | `{ name: 'NONE' }` | Animated background config |
213
+ | `className` | `string` | `''` | Additional CSS class |
214
+ | `style` | `CSSProperties` | — | Inline style overrides |
215
+
216
+ ### `<TextParticleEngine>`
217
+
218
+ | Prop | Type | Default | Description |
219
+ |---|---|---|---|
220
+ | `text` | `string` | **required** | Text the particles form |
221
+ | `particleColor` | `string \| string[]` | `'255, 255, 255'` | RGB string or array of hex colors |
222
+ | `particleSize` | `number` | `1` | Size multiplier |
223
+ | `particleDensity` | `number` | `1` | Particle count multiplier |
224
+ | `particleEase` | `number` | `1` | Return speed multiplier |
225
+ | `isMagnet` | `boolean` | `true` | Hover attraction effect |
226
+ | `clickMode` | `'none' \| 'attract' \| 'repel'` | `'none'` | Click/tap interaction |
227
+ | `particleShape` | `'circle' \| 'square' \| 'bean'` | `'circle'` | Particle shape |
228
+ | `backgroundColor` | `string` | `'#050505'` | Canvas background (hex) |
229
+
230
+ > **Color note:** `particleColor` accepts `'R, G, B'` strings (e.g. `'255, 100, 50'`) or hex strings in an array (e.g. `['#ff0000', '#00ff00']`). Alpha is handled internally.
231
+
232
+ ---
233
+
234
+ ## License
235
+
236
+ MIT
@@ -1,4 +1,5 @@
1
1
  import { ReactNode, CSSProperties } from 'react';
2
+ import { BackgroundCanvas } from '../types/background';
2
3
  export interface ParticleCanvasProps {
3
4
  children?: ReactNode;
4
5
  width?: string | number;
@@ -6,5 +7,6 @@ export interface ParticleCanvasProps {
6
7
  backgroundColor?: string;
7
8
  className?: string;
8
9
  style?: CSSProperties;
10
+ background?: BackgroundCanvas;
9
11
  }
10
- export default function ParticleCanvas({ children, width, height, backgroundColor, className, style, }: ParticleCanvasProps): import("react/jsx-runtime").JSX.Element;
12
+ export default function ParticleCanvas({ children, width, height, backgroundColor, className, style, background, }: ParticleCanvasProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,6 @@
1
+ import { BackgroundCanvas } from '../../types/background';
2
+ export interface BackgroundParticleEngineProps {
3
+ config: BackgroundCanvas;
4
+ backgroundColor: string;
5
+ }
6
+ export default function BackgroundParticleEngine({ config, backgroundColor }: BackgroundParticleEngineProps): import("react/jsx-runtime").JSX.Element | null;
@@ -0,0 +1,23 @@
1
+ import { ParticleOrientation } from '../../types/background';
2
+ export declare class FollowPointerParticle {
3
+ x: number;
4
+ y: number;
5
+ baseX: number;
6
+ baseY: number;
7
+ z: number;
8
+ vx: number;
9
+ vy: number;
10
+ color: string;
11
+ targetColor: string | null;
12
+ colorDelay: number;
13
+ angleTarget: number;
14
+ randomSpeed: number;
15
+ sizeBias: number;
16
+ scale: number;
17
+ dirX: number;
18
+ dirY: number;
19
+ initialized: boolean;
20
+ constructor(relX: number, relY: number, color: string);
21
+ update(mouseX: number | null, mouseY: number | null, swarmX: number, swarmY: number, screenW: number, screenH: number, time: number, orientation?: ParticleOrientation): void;
22
+ draw(ctx: CanvasRenderingContext2D, time: number, shape?: 'circle' | 'square' | 'bean'): void;
23
+ }
@@ -0,0 +1,6 @@
1
+ import { BackgroundCanvas } from '../../types/background';
2
+ export interface JellyfishParticleEngineProps {
3
+ config: BackgroundCanvas;
4
+ backgroundColor: string;
5
+ }
6
+ export default function JellyfishParticleEngine({ config, backgroundColor }: JellyfishParticleEngineProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,6 @@
1
+ import { BackgroundCanvas } from '../../types/background';
2
+ export interface NetParticleEngineProps {
3
+ config: BackgroundCanvas;
4
+ backgroundColor: string;
5
+ }
6
+ export default function NetParticleEngine({ config, backgroundColor }: NetParticleEngineProps): import("react/jsx-runtime").JSX.Element;
@@ -1,4 +1,5 @@
1
- import { ClickMode } from '../hooks/useParticleInteraction';
1
+ import { ClickMode } from '../../hooks/useParticleInteraction';
2
+ import { ParticleShape } from '../../types';
2
3
  export declare class Particle {
3
4
  x: number;
4
5
  y: number;
@@ -15,7 +16,8 @@ export declare class Particle {
15
16
  easeMultiplier: number;
16
17
  floatSpeed: number;
17
18
  floatOffset: number;
19
+ randomSpeed: number;
18
20
  constructor(w: number, h: number, color?: string | string[]);
19
21
  update(time: number, isActive: boolean, mx?: number | null, my?: number | null, isMouseDown?: boolean, isMagnet?: boolean, clickMode?: ClickMode): void;
20
- draw(ctx: CanvasRenderingContext2D, shape?: 'circle' | 'square'): void;
22
+ draw(ctx: CanvasRenderingContext2D, shape?: ParticleShape, time?: number): void;
21
23
  }
@@ -1,5 +1,5 @@
1
- import { ClickMode } from '../hooks/useParticleInteraction';
2
- import { ParticleShape } from '../types';
1
+ import { ClickMode } from '../../hooks/useParticleInteraction';
2
+ import { ParticleShape } from '../../types';
3
3
  export interface TextParticleEngineProps {
4
4
  text: string;
5
5
  particleColor?: string | string[];
@@ -11,4 +11,4 @@ export interface TextParticleEngineProps {
11
11
  particleShape?: ParticleShape;
12
12
  backgroundColor?: string;
13
13
  }
14
- export default function TextParticleEngine({ text, particleColor, particleSize, particleDensity, particleEase, isMagnet, clickMode, particleShape, backgroundColor }: TextParticleEngineProps): import("react/jsx-runtime").JSX.Element;
14
+ export default function TextParticleEngine({ text, particleColor, particleSize, particleDensity, particleEase, isMagnet, clickMode, particleShape, backgroundColor, }: TextParticleEngineProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,5 @@
1
+ import { FollowPointerParticle } from '../../components/background/FollowPointerParticle';
2
+ export declare function useParticleMovement(particle: FollowPointerParticle, swarmX: number, swarmY: number, time: number): {
3
+ forceX: number;
4
+ forceY: number;
5
+ };
@@ -0,0 +1,3 @@
1
+ import { FollowPointerParticle } from '../../components/background/FollowPointerParticle';
2
+ import { ParticleOrientation } from '../../types/background';
3
+ export declare function useParticleOrientation(particle: FollowPointerParticle, dxCenter: number, dyCenter: number, distToCenter: number, orientation: ParticleOrientation, time: number): void;
@@ -0,0 +1,2 @@
1
+ import { FollowPointerParticle } from '../../components/background/FollowPointerParticle';
2
+ export declare function useParticleScaling(particle: FollowPointerParticle, distToCenter: number): void;
@@ -0,0 +1,8 @@
1
+ import { FollowPointerParticle } from '../../components/background/FollowPointerParticle';
2
+ export declare function usePointerTracking(particle: FollowPointerParticle, mouseX: number | null, mouseY: number | null, swarmX: number, swarmY: number, time: number): {
3
+ forceX: number;
4
+ forceY: number;
5
+ dxCenter: number;
6
+ dyCenter: number;
7
+ distToCenter: number;
8
+ };
@@ -1,5 +1,5 @@
1
1
  import { default as React } from 'react';
2
- import { Particle } from '../components/Particle';
2
+ import { Particle } from '../../components/text/Particle';
3
3
  declare function getPixelsForText(text: string, width: number, height: number): {
4
4
  x: number;
5
5
  y: number;
package/dist/index.d.ts CHANGED
@@ -1,8 +1,9 @@
1
1
  export { default as ParticleCanvas } from './components/ParticleCanvas';
2
2
  export type { ParticleCanvasProps } from './components/ParticleCanvas';
3
- export { default as TextParticleEngine } from './components/TextParticleEngine';
4
- export type { TextParticleEngineProps } from './components/TextParticleEngine';
3
+ export { default as TextParticleEngine } from './components/text/TextParticleEngine';
4
+ export type { TextParticleEngineProps } from './components/text/TextParticleEngine';
5
5
  export { useParticleInteraction, getMagnetTarget } from './hooks/useParticleInteraction';
6
6
  export type { ClickMode } from './hooks/useParticleInteraction';
7
- export { useTextParticles } from './hooks/useTextParticles';
7
+ export { useTextParticles } from './hooks/text/useTextParticles';
8
8
  export type { ParticleShape, ColorMode } from './types';
9
+ export type { BackgroundModeName, BackgroundCanvas, ParticleOrientation } from './types/background';