@jayf0x/fluidity-js 0.2.1 → 0.2.3

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/AGENTS.md ADDED
@@ -0,0 +1,148 @@
1
+ # AGENTS.md — fluidity-js
2
+
3
+ > Universal agent instructions for GitHub Copilot, Cursor, GPT-4, Gemini, and all AI coding assistants.
4
+ > Claude Code users: see [CLAUDE.md](./CLAUDE.md) for deeper architecture notes.
5
+
6
+ ---
7
+
8
+ ## What this repo is
9
+
10
+ `@jayf0x/fluidity-js` — a WebGPU-first fluid simulation library for React. Implements a real-time Navier-Stokes solver (advection, divergence, pressure, vorticity confinement) that renders onto a `<canvas>` element. Falls back automatically to WebGL2 → WebGL1. Runs the heavy simulation loop in a Web Worker via OffscreenCanvas by default.
11
+
12
+ Two components: `<FluidText>` (text as obstacle/background) and `<FluidImage>` (bitmap as obstacle/background).
13
+
14
+ **Stack:** TypeScript · React 17+ · Vite 4 · Vitest · Bun
15
+
16
+ ---
17
+
18
+ ## Repo layout (critical paths)
19
+
20
+ ```
21
+ src/
22
+ index.ts ← public exports
23
+ globals.d.ts ← ambient global types (FluidConfig, FluidHandle…)
24
+ core/
25
+ config.ts ← DEFAULT_CONFIG, presets, mergeConfig
26
+ gl-utils.ts ← WebGL context + FBO helpers
27
+ gpu-utils.ts ← WebGPU helpers
28
+ shaders.ts ← GLSL shader strings
29
+ wgsl-shaders.ts ← WGSL shader strings
30
+ simulation.ts ← FluidSimulation class (dual WebGPU/WebGL)
31
+ textures.ts ← texture creation for text + image modes
32
+ worker/index.ts ← Web Worker message handler
33
+ fluid-controller.ts ← worker vs main-thread abstraction
34
+ react/
35
+ FluidText.tsx ← React component
36
+ FluidImage.tsx ← React component
37
+ useFluid.ts ← core hook: canvas lifecycle + controller
38
+ tests/ ← Vitest + jsdom (83 tests)
39
+ demo/ ← standalone Vite 5 demo site (NOT the library)
40
+ dist/ ← built output (do not edit)
41
+ ```
42
+
43
+ ---
44
+
45
+ ## Commands
46
+
47
+ ```bash
48
+ # Run tests (library root, Node 16 compat)
49
+ bun test:claude # preferred: runs vitest, tails last 8 lines
50
+
51
+ # Build library
52
+ bun build
53
+
54
+ # Demo (requires Node 20)
55
+ cd demo
56
+ PATH=/Users/me/.nvm/versions/node/v20.19.6/bin:$PATH bun dev
57
+ PATH=/Users/me/.nvm/versions/node/v20.19.6/bin:$PATH bun build
58
+ PATH=/Users/me/.nvm/versions/node/v20.19.6/bin:$PATH bun deploy # → gh-pages
59
+ ```
60
+
61
+ ---
62
+
63
+ ## Public API
64
+
65
+ ### Components
66
+
67
+ ```tsx
68
+ <FluidText text="Hello" fontSize={120} color="#fff" algorithm="glass" preset="neon" />
69
+ <FluidImage src="/hero.jpg" algorithm="aurora" quality={{ dpr: 1, sim: 0.5 }} />
70
+ ```
71
+
72
+ ### FluidHandle ref
73
+
74
+ ```ts
75
+ interface FluidHandle {
76
+ reset(): void;
77
+ move(opts: { x: number; y: number; strength?: number }): void;
78
+ splat(x: number, y: number, vx: number, vy: number, strength?: number): void;
79
+ updateConfig(config: Partial<FluidConfig>): void;
80
+ }
81
+ ```
82
+
83
+ ### Algorithms
84
+ `'standard'` · `'glass'` · `'ink'` · `'aurora'` · `'ripple'`
85
+
86
+ ### Presets
87
+ `'calm'` · `'sand'` · `'wave'` · `'neon'` · `'smoke'`
88
+
89
+ ---
90
+
91
+ ## Code style / conventions
92
+
93
+ - **TypeScript strict** — no `any`, no non-null assertions without a comment
94
+ - **No file extensions** on imports within `src/` (e.g. `from './config'`)
95
+ Exception: worker import keeps `.js?worker&inline` for Vite query string
96
+ - **No default exports** from library modules; named exports only
97
+ - **No comments** unless the *why* is non-obvious (hidden constraint, invariant, workaround)
98
+ - **Formatting:** Prettier with `@trivago/prettier-plugin-sort-imports`
99
+ - **Tests:** All 83 must pass before any commit. Add tests for new behaviour.
100
+
101
+ ---
102
+
103
+ ## Architecture invariants (do not violate)
104
+
105
+ | Rule | Reason |
106
+ |------|--------|
107
+ | `transferControlToOffscreen` called at most once per canvas | Irreversible — double-mount in StrictMode would crash |
108
+ | `useFluid` creates a **fresh** `<canvas>` element each mount inside a container `<div>` | StrictMode safety |
109
+ | `createBlit` sets up vertex buffers **once** | Performance — never call `vertexAttribPointer` per draw |
110
+ | Worker `.terminate()` always deferred 50ms after `postMessage({type:'destroy'})` | Lets the worker flush its destroy sequence |
111
+ | Display shader outputs **premultiplied alpha** | Transparent canvas compositing correctness |
112
+ | Coverage texture ref: `text` mode → `coverageTex === obstacleTex`; guard against double-delete | Memory safety |
113
+
114
+ ---
115
+
116
+ ## What agents MUST NOT do
117
+
118
+ - **Do not** edit files under `dist/` — these are build artefacts
119
+ - **Do not** add `// eslint-disable` or `@ts-ignore` without explaining why in the PR
120
+ - **Do not** introduce new peer dependencies without updating `peerDependencies` in `package.json`
121
+ - **Do not** call `vertexAttribPointer` inside the render loop
122
+ - **Do not** call `transferControlToOffscreen` more than once per canvas element
123
+ - **Do not** use `window.*` APIs directly inside `worker/index.ts` (worker context)
124
+ - **Do not** commit if `bun test:claude` fails
125
+ - **Do not** change the module format from ESM to CJS — consumers depend on tree-shaking
126
+ - **Do not** modify `CLAUDE.md` unless explicitly asked — it is the authoritative agent guide
127
+
128
+ ---
129
+
130
+ ## Testing notes
131
+
132
+ - Tests run under jsdom with a WebGL mock (`tests/setup.js`). `navigator.gpu` is absent — all tests exercise the WebGL path.
133
+ - `FluidText`/`FluidImage` are `forwardRef` ExoticComponents — check `$$typeof`, not `typeof`.
134
+ - React component mocks require `.ts` extension: `vi.mock('../../src/fluid-controller.ts', ...)`
135
+ - Worker mock: `vi.mock('../src/worker/index.ts?worker&inline', ...)`
136
+
137
+ ---
138
+
139
+ ## Where to find more
140
+
141
+ | Resource | Path |
142
+ |----------|------|
143
+ | Deep architecture guide (Claude-specific) | [CLAUDE.md](./CLAUDE.md) |
144
+ | Prioritised bug/feature backlog | [backlog.md](./backlog.md) |
145
+ | Version history | [changelog.md](./changelog.md) |
146
+ | Live demo source | [demo/src/examples/](./demo/src/examples/) |
147
+ | npm package | https://www.npmjs.com/package/@jayf0x/fluidity-js |
148
+ | Live demo | https://jayf0x.github.io/fluidity |
@@ -0,0 +1,45 @@
1
+ # Contributing to fluidity-js
2
+
3
+ ## Setup
4
+
5
+ ```bash
6
+ git clone https://github.com/jayf0x/fluidity
7
+ cd fluidity
8
+ bun install
9
+ ```
10
+
11
+ Run tests:
12
+
13
+ ```bash
14
+ bun test:claude
15
+ ```
16
+
17
+ Run the demo (requires Node 20):
18
+
19
+ ```bash
20
+ cd demo
21
+ PATH=/Users/me/.nvm/versions/node/v20.19.6/bin:$PATH bun install
22
+ PATH=/Users/me/.nvm/versions/node/v20.19.6/bin:$PATH bun dev
23
+ ```
24
+
25
+ ## Before opening a PR
26
+
27
+ - All 83 tests pass (`bun test:claude`)
28
+ - No new peer dependencies without prior discussion
29
+ - Code formatted with `bun format`
30
+ - See [AGENTS.md](./AGENTS.md) for code style and architecture constraints
31
+
32
+ ## Where to look
33
+
34
+ | Goal | Files |
35
+ |------|-------|
36
+ | Add/change a visual effect | `src/core/shaders.ts`, `src/core/wgsl-shaders.ts`, `src/core/simulation.ts` |
37
+ | Add a new prop | `src/globals.d.ts`, `src/react/FluidText.tsx` or `FluidImage.tsx`, `src/core/config.ts` |
38
+ | Add a preset | `src/core/config.ts` |
39
+ | Fix a simulation bug | `src/core/simulation.ts`, `src/core/gl-utils.ts`, `src/core/gpu-utils.ts` |
40
+ | Fix React lifecycle | `src/react/useFluid.ts`, `src/fluid-controller.ts` |
41
+ | Fix worker communication | `src/worker/index.ts`, `src/fluid-controller.ts` |
42
+
43
+ ## Reporting issues
44
+
45
+ Use the GitHub issue templates — include a minimal repro snippet and your browser/renderer info.
package/README.md CHANGED
@@ -1,32 +1,51 @@
1
- # fluidity-js - 🔥Upgrade your UX🔥
1
+ # fluidity-js WebGPU Fluid Simulation for React
2
2
 
3
- [![npm](https://img.shields.io/npm/v/@jayf0x/fluidity-js)](https://www.npmjs.com/package/@jayf0x/fluidity-js)
3
+ [![npm version](https://img.shields.io/npm/v/@jayf0x/fluidity-js)](https://www.npmjs.com/package/@jayf0x/fluidity-js)
4
+ [![npm downloads](https://img.shields.io/npm/dm/@jayf0x/fluidity-js)](https://www.npmjs.com/package/@jayf0x/fluidity-js)
5
+ [![bundle size](https://img.shields.io/bundlephobia/minzip/@jayf0x/fluidity-js)](https://bundlephobia.com/package/@jayf0x/fluidity-js)
4
6
  [![license](https://img.shields.io/npm/l/@jayf0x/fluidity-js)](./LICENSE)
5
- [![size](https://img.shields.io/bundlephobia/minzip/@jayf0x/fluidity-js)](https://bundlephobia.com/package/@jayf0x/fluidity-js)
7
+ [![TypeScript](https://img.shields.io/badge/TypeScript-strict-blue)](./tsconfig.json)
8
+ [![CI](https://github.com/jayf0x/fluidity/actions/workflows/ci.yml/badge.svg)](https://github.com/jayf0x/fluidity/actions/workflows/ci.yml)
9
+
10
+ **Real-time Navier-Stokes fluid dynamics for React — interactive water, ink, glass, and aurora effects on text and images. WebGPU-first with automatic WebGL2/WebGL1 fallback. Runs in a Web Worker for zero jank.**
6
11
 
7
12
  <a href="https://jayf0x.github.io/fluidity">
8
13
  <p align="center">
9
- <img src="assets/preview.gif" alt="preview" height="300px"/>
14
+ <img src="assets/preview.gif" alt="WebGPU fluid simulation — fluid text and image effects in React" height="300px"/>
10
15
  </p>
11
16
  <p align="center"><strong>Live demo →</strong></p>
12
17
  </a>
13
18
 
14
19
  ```bash
15
- bun add @jayf0x/fluidity-js
16
- # or: npm install / yarn add / pnpm add
20
+ npm install @jayf0x/fluidity-js
21
+ # bun add / yarn add / pnpm add
17
22
  ```
18
23
 
19
- > **WebGPU-first** 🔥 — falls back automatically to WebGL2 / WebGL1.
24
+ > **WebGPU-first** — falls back automatically to WebGL2 WebGL1. No configuration required.
20
25
 
21
26
  ---
22
27
 
23
- ## React examples
28
+ ## Features
29
+
30
+ - **Fluid text effects** — wrap fluid around text as obstacle + background
31
+ - **Fluid image effects** — apply fluid over any bitmap with `cover`/`contain` sizing
32
+ - **5 visual algorithms** — standard, glass, ink, aurora, ripple
33
+ - **5 presets** — calm, sand, wave, neon, smoke
34
+ - **Web Worker** — simulation runs off the main thread via OffscreenCanvas
35
+ - **WebGPU + WebGL2 + WebGL1** — widest browser compatibility
36
+ - **Fully typed** — ambient TypeScript types, no import needed
37
+ - **React 17+** — forwardRef components, StrictMode safe
38
+ - **Programmatic API** — `splat()`, `move()`, `updateConfig()`, `reset()`
39
+ - **Reactive props** — change algorithm, preset, quality, config at runtime
40
+
41
+ ---
24
42
 
25
- **Hero text that moves with your cursor:**
43
+ ## Quickstart
26
44
 
27
45
  ```tsx
28
- import { FluidText } from '@jayf0x/fluidity-js';
46
+ import { FluidText, FluidImage } from '@jayf0x/fluidity-js';
29
47
 
48
+ // Fluid text — reacts to cursor movement
30
49
  export function Hero() {
31
50
  return (
32
51
  <div style={{ width: '100%', height: 400 }}>
@@ -34,13 +53,8 @@ export function Hero() {
34
53
  </div>
35
54
  );
36
55
  }
37
- ```
38
-
39
- **Full-bleed image cover — one line to make it alive:**
40
-
41
- ```tsx
42
- import { FluidImage } from '@jayf0x/fluidity-js';
43
56
 
57
+ // Full-bleed fluid image
44
58
  export function Cover() {
45
59
  return (
46
60
  <div style={{ width: '100%', height: '100vh' }}>
@@ -50,7 +64,7 @@ export function Cover() {
50
64
  }
51
65
  ```
52
66
 
53
- **Go further — presets, algorithms, live config:**
67
+ **Presets and algorithms:**
54
68
 
55
69
  ```tsx
56
70
  <FluidText text="Wicked" preset="neon" algorithm="glass" />
@@ -58,7 +72,11 @@ export function Cover() {
58
72
  <FluidText text="Chill" preset="calm" quality={{ dpr: 1, sim: 0.75 }} />
59
73
  ```
60
74
 
61
- **Trigger effects programmatically:**
75
+ ---
76
+
77
+ ## Programmatic control
78
+
79
+ Use `ref` to trigger effects from code — attractors, scroll-driven splats, click bursts:
62
80
 
63
81
  ```tsx
64
82
  import { useRef } from 'react';
@@ -80,7 +98,14 @@ export function Interactive() {
80
98
  }
81
99
  ```
82
100
 
83
- Official examples → [`demo/src/examples/`](./demo/src/examples/)
101
+ ### FluidHandle methods
102
+
103
+ | Method | Description |
104
+ |--------|-------------|
105
+ | `reset()` | Re-initialise simulation and reload source |
106
+ | `updateConfig(patch)` | Merge a partial config update into the running sim |
107
+ | `move({ x, y, strength? })` | Programmatic pointer input (canvas-relative px) |
108
+ | `splat(x, y, vx, vy, strength?)` | Inject a fluid splat directly — safe to call N×/frame |
84
109
 
85
110
  ---
86
111
 
@@ -88,50 +113,50 @@ Official examples → [`demo/src/examples/`](./demo/src/examples/)
88
113
 
89
114
  ### FluidText
90
115
 
91
- | Prop | Type | Default |
92
- | ------------ | ------------------ | -------------- |
93
- | `text` | `string` | — |
94
- | `fontSize` | `number` | `100` |
95
- | `color` | `string` | `'#ffffff'` |
96
- | `fontFamily` | `string` | `'sans-serif'` |
97
- | `fontWeight` | `string \| number` | `900` |
116
+ | Prop | Type | Default |
117
+ |------|------|---------|
118
+ | `text` | `string` | — |
119
+ | `fontSize` | `number` | `100` |
120
+ | `color` | `string` | `'#ffffff'` |
121
+ | `fontFamily` | `string` | `'sans-serif'` |
122
+ | `fontWeight` | `string \| number` | `900` |
98
123
 
99
124
  ### FluidImage
100
125
 
101
- | Prop | Type | Default |
102
- | ----------- | ------------------ | --------- |
103
- | `src` | `string` | — |
126
+ | Prop | Type | Default |
127
+ |------|------|---------|
128
+ | `src` | `string` | — |
104
129
  | `imageSize` | `string \| number` | `'cover'` |
105
- | `effect` | `number` | `0` |
130
+ | `effect` | `number` | `0` |
106
131
 
107
132
  ### Shared props
108
133
 
109
- | Prop | Type | Default |
110
- | ----------------- | ---------------------- | ---------------------- |
111
- | `config` | `Partial<FluidConfig>` | — |
112
- | `preset` | `PresetKey` | — |
113
- | `algorithm` | `FluidAlgorithm` | `'standard'` |
114
- | `quality` | `FluidQuality` | `{ dpr: 1, sim: 0.5 }` |
115
- | `backgroundColor` | `string` | `'#0a0a0a'` |
116
- | `backgroundSrc` | `string` | — |
117
- | `backgroundSize` | `string \| number` | `'cover'` |
118
- | `isMouseEnabled` | `boolean` | `true` |
119
- | `isWorkerEnabled` | `boolean` | `true` |
120
- | `useWebGPU` | `boolean` | `true` |
121
- | `className` | `string` | — |
122
- | `style` | `CSSProperties` | — |
134
+ | Prop | Type | Default |
135
+ |------|------|---------|
136
+ | `config` | `Partial<FluidConfig>` | — |
137
+ | `preset` | `PresetKey` | — |
138
+ | `algorithm` | `FluidAlgorithm` | `'standard'` |
139
+ | `quality` | `FluidQuality` | `{ dpr: 1, sim: 0.5 }` |
140
+ | `backgroundColor` | `string` | `'#0a0a0a'` |
141
+ | `backgroundSrc` | `string` | — |
142
+ | `backgroundSize` | `string \| number` | `'cover'` |
143
+ | `isMouseEnabled` | `boolean` | `true` |
144
+ | `isWorkerEnabled` | `boolean` | `true` |
145
+ | `useWebGPU` | `boolean` | `true` |
146
+ | `className` | `string` | — |
147
+ | `style` | `CSSProperties` | — |
123
148
 
124
149
  ---
125
150
 
126
151
  ## Algorithms
127
152
 
128
- | Value | Character |
129
- | ------------ | ------------------------------------------------- |
130
- | `'standard'` | Colour overlay + gentle refraction (default) |
131
- | `'glass'` | Strong UV distortion only — bent-glass, no colour |
132
- | `'ink'` | Dense opaque pigment that accumulates and stains |
133
- | `'aurora'` | Velocity-field UV warp — liquid metal / lava-lamp |
134
- | `'ripple'` | Exaggerated normals + Fresnel rim — still water |
153
+ | Value | Character |
154
+ |-------|-----------|
155
+ | `'standard'` | Colour overlay + gentle refraction (default) |
156
+ | `'glass'` | UV distortion only — bent-glass, no colour |
157
+ | `'ink'` | Dense opaque pigment that accumulates and stains |
158
+ | `'aurora'` | Velocity-field UV warp — liquid metal / lava-lamp |
159
+ | `'ripple'` | Exaggerated normals + Fresnel rim — still water |
135
160
 
136
161
  ```tsx
137
162
  <FluidImage src="/photo.jpg" algorithm="aurora" />
@@ -140,74 +165,125 @@ Official examples → [`demo/src/examples/`](./demo/src/examples/)
140
165
 
141
166
  ---
142
167
 
143
- ## Quality
144
-
145
- `quality` controls rendering resolution on two independent axes. Both props are reactive — you can adjust them at runtime.
146
-
147
- | Field | Range | Default | Description |
148
- | ----- | ------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
149
- | `dpr` | 0.1 – 1 | `1` | Canvas backing resolution as a fraction of `devicePixelRatio`. `0.5` on a Retina screen renders at 1× instead of 2×, saving ~75% fill rate. |
150
- | `sim` | 0.1 – 1 | `0.5` | Simulation FBO size as a fraction of canvas size. Lower = cheaper GPU, less fluid detail. |
168
+ ## Presets
151
169
 
152
170
  ```tsx
153
- <FluidText text="hello" quality={{ dpr: 0.75, sim: 0.25 }} />
171
+ <FluidText text="Wicked" preset="neon" />
172
+ <FluidText text="Calm vibes" preset="calm" />
154
173
  ```
155
174
 
156
- `dpr` and `sim` are independent you can run a sharp canvas at a coarser simulation:
175
+ Available: `calm` · `sand` · `wave` · `neon` · `smoke`
176
+
177
+ Presets are reactive — changing the prop at runtime re-applies the config. Any `config` values you pass override the preset. The `algorithm` prop also overrides the preset's algorithm.
178
+
179
+ ---
180
+
181
+ ## Quality
182
+
183
+ `quality` controls rendering resolution on two independent axes. Both are reactive at runtime.
184
+
185
+ | Field | Range | Default | Description |
186
+ |-------|-------|---------|-------------|
187
+ | `dpr` | 0.1–1 | `1` | Canvas backing resolution as fraction of `devicePixelRatio`. `0.5` on Retina saves ~75% fill rate. |
188
+ | `sim` | 0.1–1 | `0.5` | Simulation FBO size as fraction of canvas size. Lower = cheaper GPU, less fluid detail. |
157
189
 
158
190
  ```tsx
159
- // Sharp display, cheap simulation
191
+ // Sharp canvas, cheap simulation
160
192
  <FluidImage src="/hero.jpg" quality={{ dpr: 1, sim: 0.2 }} />
161
193
 
162
- // Lower display res, full simulation quality
194
+ // Lower canvas res, full simulation quality
163
195
  <FluidImage src="/hero.jpg" quality={{ dpr: 0.5, sim: 1 }} />
164
196
  ```
165
197
 
166
198
  ---
167
199
 
168
- ## FluidConfig
169
-
170
- | Key | Default | Description |
171
- | --------------------- | ------------------ | ----------------------------------------- |
172
- | `densityDissipation` | `0.992` | How long ink lingers (0–1) |
173
- | `velocityDissipation` | `0.93` | How fast velocity decays (0–1) |
174
- | `pressureIterations` | `1` | Jacobi iterations — accuracy vs. cost |
175
- | `curl` | `0.0001` | Vorticity / swirl. `0.2`–`0.5` for eddies |
176
- | `splatRadius` | `0.004` | Brush radius |
177
- | `splatForce` | `0.91` | Force applied by brush |
178
- | `refraction` | `0.25` | Background warp strength |
179
- | `specularExp` | `1.01` | Specular highlight sharpness |
180
- | `shine` | `0.01` | Highlight intensity |
181
- | `waterColor` | `[0, 0, 0]` | Base fluid colour `[R, G, B]` (0–1) |
182
- | `glowColor` | `[0.7, 0.85, 1.0]` | Glow / specular colour `[R, G, B]` (0–1) |
183
- | `warpStrength` | `0.015` | UV warp intensity (`aurora` algorithm) |
200
+ ## FluidConfig reference
201
+
202
+ | Key | Default | Description |
203
+ |-----|---------|-------------|
204
+ | `densityDissipation` | `0.992` | How long ink lingers (0–1) |
205
+ | `velocityDissipation` | `0.93` | How fast velocity decays (0–1) |
206
+ | `pressureIterations` | `1` | Jacobi iterations — accuracy vs. cost |
207
+ | `curl` | `0.0001` | Vorticity / swirl. `0.2`–`0.5` for visible eddies |
208
+ | `splatRadius` | `0.004` | Brush radius |
209
+ | `splatForce` | `0.91` | Force applied by brush |
210
+ | `refraction` | `0.25` | Background warp strength |
211
+ | `specularExp` | `1.01` | Specular highlight sharpness |
212
+ | `shine` | `0.01` | Highlight intensity |
213
+ | `waterColor` | `[0, 0, 0]` | Base fluid colour `[R, G, B]` (0–1) |
214
+ | `glowColor` | `[0.7, 0.85, 1.0]` | Glow / specular colour `[R, G, B]` (0–1) |
215
+ | `warpStrength` | `0.015` | UV warp intensity (`aurora` algorithm) |
184
216
 
185
217
  ---
186
218
 
187
- ## FluidHandle (ref)
219
+ ## TypeScript
188
220
 
189
- | Method | Description |
190
- | -------------------------------- | ----------------------------------------------------- |
191
- | `reset()` | Re-initialise simulation and reload source |
192
- | `updateConfig(patch)` | Merge a partial config update into running sim |
193
- | `move({ x, y, strength? })` | Programmatic pointer input (canvas-relative px) |
194
- | `splat(x, y, vx, vy, strength?)` | Inject a fluid splat directly — safe to call N×/frame |
221
+ All types are globally ambient — no import required:
222
+
223
+ ```ts
224
+ // Available globally after installing the package:
225
+ // FluidConfig, FluidHandle, FluidAlgorithm, FluidQuality, PresetKey
226
+ ```
195
227
 
196
228
  ---
197
229
 
198
- ## Presets
230
+ ## How it works
199
231
 
200
- ```tsx
201
- <FluidText text="Wicked" preset="neon" />
202
- <FluidText text="Wicked" preset="calm" />
203
- ```
232
+ fluidity-js runs a real-time **Navier-Stokes fluid simulation** entirely on the GPU:
204
233
 
205
- Available: `calm` · `sand` · `wave` · `neon` · `smoke`
234
+ 1. **Advect velocity** move velocity field along itself; obstacle mask zeroes inside text/image
235
+ 2. **Advect density** — move ink/colour field with velocity
236
+ 3. **Curl → vorticity confinement** — preserve swirling detail
237
+ 4. **Splat** — inject velocity + density at cursor or programmatic positions
238
+ 5. **Divergence → pressure solve** — N Jacobi iterations for incompressibility
239
+ 6. **Gradient subtract** — enforce divergence-free velocity
240
+ 7. **Display pass** — 5 texture units: density, obstacle, background, coverage mask, velocity; 5 algorithms selectable by uniform
241
+
242
+ The simulation runs in a **Web Worker** by default, communicating with the React component via `postMessage`. The canvas is transferred to the worker via `OffscreenCanvas.transferControlToOffscreen()` for true off-thread rendering.
243
+
244
+ **WebGPU path** uses render pipelines via `@webgpu/types`. **WebGL path** uses double-framebuffer objects (ping-pong FBOs) with GLSL shaders.
245
+
246
+ ---
247
+
248
+ ## Browser support
249
+
250
+ | Browser | WebGPU | WebGL2 | WebGL1 |
251
+ |---------|--------|--------|--------|
252
+ | Chrome 113+ | ✅ | ✅ | ✅ |
253
+ | Edge 113+ | ✅ | ✅ | ✅ |
254
+ | Firefox | — | ✅ | ✅ |
255
+ | Safari 17+ | ✅ (partial) | ✅ | ✅ |
256
+ | Safari < 17 | — | ✅ | ✅ |
257
+ | Mobile Chrome | — | ✅ | ✅ |
258
+
259
+ Fallback is automatic — no configuration needed.
260
+
261
+ ---
262
+
263
+ ## Examples
264
+
265
+ Full working examples live in [`demo/src/examples/`](./demo/src/examples/):
266
+
267
+ - `TextExample.tsx` — FluidText with Leva controls
268
+ - `ImageExample.tsx` — FluidImage with backgroundSrc
269
+ - `SplashExample.tsx` — full-page fluid hero
270
+ - `SplitExample.tsx` — split-screen comparison
271
+ - `PresetsExample.tsx` — all presets side by side
272
+
273
+ [**Open live demo →**](https://jayf0x.github.io/fluidity)
274
+
275
+ ---
276
+
277
+ ## Contributing
278
+
279
+ Issues and PRs welcome. See [AGENTS.md](./AGENTS.md) for agent-specific instructions and code conventions.
206
280
 
207
- `preset` is reactive — changing it re-applies the preset config. Any `config` values you pass override the preset. `algorithm` prop also overrides the preset's algorithm.
281
+ 1. Fork branch PR against `main`
282
+ 2. Run `bun test:claude` before pushing — all 83 tests must pass
283
+ 3. No new dependencies without discussion
208
284
 
209
285
  ---
210
286
 
211
287
  ## License
212
288
 
213
- [MIT](./LICENSE)
289
+ [MIT](./LICENSE) © [jayf0x](https://github.com/jayf0x)
package/dist/globals.d.ts CHANGED
@@ -2,6 +2,9 @@
2
2
 
3
3
  type FluidAlgorithm = 'standard' | 'glass' | 'ink' | 'aurora' | 'ripple';
4
4
 
5
+ /** RGB tuple (values 0–1) or a CSS hex string (#RGB, #RRGGBB, #RRGGBBAA — alpha stripped). */
6
+ type FluidColor = [number, number, number] | `#${string}`;
7
+
5
8
  /**
6
9
  * Granular performance/quality controls. Both axes are independent — you can
7
10
  * run a sharp display at a coarser simulation, or vice versa.
@@ -24,8 +27,8 @@ interface FluidConfig {
24
27
  refraction: number;
25
28
  specularExp: number;
26
29
  shine: number;
27
- waterColor: [number, number, number];
28
- glowColor: [number, number, number];
30
+ waterColor: FluidColor;
31
+ glowColor: FluidColor;
29
32
  algorithm: FluidAlgorithm;
30
33
  warpStrength: number;
31
34
  }
@@ -55,6 +58,8 @@ interface FluidBaseProps {
55
58
  backgroundSize?: string | number;
56
59
  /** enabled greater performance, but not every browser supports it */
57
60
  useWebGPU?: boolean;
61
+ /** Enable transparent canvas (default true). Set false for a minor perf gain when transparency is not needed. */
62
+ enableAlpha?: boolean;
58
63
  }
59
64
 
60
65
  interface FluidTextProps extends FluidBaseProps {