# particle-network A canvas animation of drifting particles connected by proximity lines, with opacity modulated by a slowly-rolling fBm (fractal Brownian motion) noise field. No external dependencies. Pure ES module. --- ## Files | File | Purpose | |------|---------| | `particleNetwork.js` | The module — import this into your project | | `demo.html` | Minimal working example (requires a local server for ES module import) | --- ## Quick start Serve the folder with any static server, e.g.: ```bash npx serve . # or python3 -m http.server ``` Then open `demo.html`. --- ## Integration ### 1. Minimal — full-page background ```html ``` ### 2. With field preview (debug / design mode) ```html ``` ### 3. Bundler / framework Copy `particleNetwork.js` anywhere in your `src/` tree and import normally: ```js import { ParticleNetwork } from '@/lib/particleNetwork.js'; ``` No build config changes needed — it uses only standard browser APIs. --- ## Constructor ```js new ParticleNetwork(mainCanvas, fieldCanvas, options) ``` | Parameter | Type | Description | |-----------|------|-------------| | `mainCanvas` | `HTMLCanvasElement` | The particle animation canvas | | `fieldCanvas` | `HTMLCanvasElement \| null` | Optional debug field view | | `options` | `object` | See below | --- ## Options All options can be passed at construction time and updated at runtime via `setOption(key, value)`. | Key | Default | Description | |-----|---------|-------------| | `count` | `55` | Number of particles | | `reach` | `140` | Max connection distance in canvas px | | `flowSpeed` | `2` | Field drift speed on a 1–10 scale. At 1, motion takes several minutes to traverse the field — clearly present but takes time to perceive. | | `fieldScale` | `4` | Noise zoom on a 1–10 scale. Lower = larger rolling blobs. | | `dimAtMin` | `0.60` | Opacity reduction at field minimum. `0` = field has no opacity effect. `1` = particles in troughs are fully invisible. | | `particleSpeed` | `0.35` | Base particle velocity (px/frame) | | `particleMinR` | `1.4` | Minimum particle radius (px) | | `particleMaxR` | `2.8` | Maximum particle radius (px) | | `fieldUpdateMs` | `50` | How often the noise field recalculates (ms). Lower = smoother but more CPU. | | `fieldOctaves` | `4` | fBm octaves. More = finer detail, more CPU. | | `bgColor` | `'#040e1f'` | Canvas background colour | | `lineColor` | `'80,160,240'` | RGB string for particle–particle lines | | `lineColorMouse` | `'160,220,255'` | RGB string for mouse-proximity lines | | `canvasAspect` | `0.65` | Canvas height = width × aspect | | `timeConstantX` | `0.00084` | Primary axis time advance rate. Halve to slow further. | | `timeConstantY` | `0.0003145` | Secondary axis rate — kept at a different ratio for the swirling character. | --- ## Methods ```js net.start() // begin animation loop net.stop() // pause (preserves state) net.setOption(key, value) // update any option at runtime net.sampleField(x, y) // returns [0,1] field value at canvas coords net.destroy() // stop loop and remove all event listeners ``` --- ## How it works **Noise field** — a 4-octave fBm Perlin noise function is evaluated on a tiny 80×52 offscreen canvas every 50ms, then scaled up via `drawImage`. This is ~100× cheaper than evaluating at display resolution. **Opacity mapping** — each particle's field sample `fv ∈ [0,1]` maps to an opacity multiplier via: ``` multiplier = (1 - dimAtMin) + dimAtMin * fv ``` At `fv = 1` (field peak): multiplier = 1.0 — full brightness. At `fv = 0` (field trough): multiplier = 1 - dimAtMin. For connecting lines the multiplier is the geometric mean of both endpoint values, so a line straddling a bright and dark zone stays dim. **Distance falloff** — cubic: `(1 - dist/reach)³`, giving a sharp fade near the connection threshold rather than a linear fade across the full range. **Glow** — each particle renders a `createRadialGradient` halo sized to `r × (2.5 + fieldValue × 1.5)`, so dots in bright field regions bloom larger.