react-smokey-fluid-cursor 1.0.2

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,181 @@
1
+ <h1 align="center">💨 React Smokey Fluid Cursor</h1>
2
+
3
+ <p align="center">
4
+ A beautiful, interactive fluid simulation that creates stunning visual effects following your cursor movements for your React and Next.js application on web. Built with WebGL for high-performance real-time fluid dynamics.
5
+ </p>
6
+
7
+ ![npm version](https://img.shields.io/npm/v/react-smokey-fluid-cursor.svg)
8
+ ![package size minified](https://img.shields.io/bundlephobia/min/react-smokey-fluid-cursor?style=plastic)
9
+ [![Badge](https://data.jsdelivr.com/v1/package/npm/react-smokey-fluid-cursor/badge)](https://www.jsdelivr.com/package/npm/react-smokey-fluid-cursor)
10
+ [![JavaScript Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://standardjs.com)
11
+
12
+ ![total downloads](https://img.shields.io/npm/dt/react-smokey-fluid-cursor.svg)
13
+ ![total downloads per year](https://img.shields.io/npm/dy/react-smokey-fluid-cursor.svg)
14
+ ![total downloads per week](https://img.shields.io/npm/dw/react-smokey-fluid-cursor.svg)
15
+ ![total downloads per month](https://img.shields.io/npm/dm/react-smokey-fluid-cursor.svg)
16
+ ![download-image](https://img.shields.io/npm/dm/react-smokey-fluid-cursor.svg)
17
+
18
+ [![react-smokey-fluid-cursor](https://nodei.co/npm/react-smokey-fluid-cursor.png)](https://npmjs.org/package/react-smokey-fluid-cursor)
19
+
20
+ ---
21
+
22
+ ## 📦 Installation
23
+
24
+ ```bash
25
+ npm i react-smokey-fluid-cursor
26
+
27
+ yarn add react-smokey-fluid-cursor
28
+
29
+ pnpm i react-smokey-fluid-cursor
30
+
31
+ bun add react-smokey-fluid-cursor
32
+ ```
33
+
34
+ ---
35
+
36
+ ## 📸 Demo
37
+
38
+ Also see more details in [Example](https://github.com/faraasat/react-smokey-fluid-cursor/tree/main/example):
39
+
40
+ ![Demo](https://github.com/faraasat/react-smokey-fluid-cursor/blob/main/images/demo.gif)
41
+
42
+ ---
43
+
44
+ ## 🚀 Quick Start
45
+
46
+ ### **React (CRA)**
47
+
48
+ ```tsx
49
+ // src/App.tsx|jsx
50
+ import React from "react";
51
+
52
+ import { SmokeyFluidCursor } from "react-smokey-fluid-cursor";
53
+
54
+ function App() {
55
+ return (
56
+ <div className="App">
57
+ {/* Place observer once globally */}
58
+ <SmokeyFluidCursor />
59
+ </div>
60
+ );
61
+ }
62
+ ```
63
+
64
+ ### **Vite + React**
65
+
66
+ ```tsx
67
+ // src/main.tsx|jsx
68
+ import React from "react";
69
+
70
+ import { SmokeyFluidCursor } from "react-smokey-fluid-cursor";
71
+
72
+ function Main() {
73
+ return (
74
+ <>
75
+ <SmokeyFluidCursor />
76
+ </>
77
+ );
78
+ }
79
+
80
+ export default Main;
81
+ ```
82
+
83
+ ### **Next.js Pages Router**
84
+
85
+ ```tsx
86
+ // pages/_app.tsx|jsx
87
+ import type { AppProps } from "next/app";
88
+
89
+ import { SmokeyFluidCursor } from "react-smokey-fluid-cursor";
90
+
91
+ function MyApp({ Component, pageProps }: AppProps) {
92
+ return (
93
+ <>
94
+ {/* Global ad observer */}
95
+ <SmokeyFluidCursor />
96
+ <Component {...pageProps} />
97
+ </>
98
+ );
99
+ }
100
+
101
+ export default MyApp;
102
+ ```
103
+
104
+ ### **Next.js (App Router)**
105
+
106
+ ```tsx
107
+ // app/layout.tsx|jsx
108
+ import "./globals.css";
109
+
110
+ import { SmokeyFluidCursor } from "react-smokey-fluid-cursor";
111
+
112
+ export default function RootLayout({
113
+ children,
114
+ }: {
115
+ children: React.ReactNode;
116
+ }) {
117
+ return (
118
+ <html lang="en">
119
+ <body>
120
+ <SmokeyFluidCursor />
121
+ {children}
122
+ </body>
123
+ </html>
124
+ );
125
+ }
126
+ ```
127
+
128
+ ---
129
+
130
+ ## ⚙️ Configuration Options
131
+
132
+ Customize the fluid simulation with these configuration options:
133
+
134
+ | Property | Default Value | Description |
135
+ | --------------------- | ----------------------------- | ---------------------------------------------- |
136
+ | `id` | `"react-smokey-fluid-cursor"` | Canvas element ID |
137
+ | `simResolution` | `128` | Simulation resolution (higher = more detailed) |
138
+ | `dyeResolution` | `512` | Dye/color resolution |
139
+ | `densityDissipation` | `0.98` | How quickly colors fade (0–1) |
140
+ | `velocityDissipation` | `0.98` | How quickly movement slows down |
141
+ | `pressureIteration` | `10` | Pressure solver iterations |
142
+ | `curl` | `30` | Vorticity/swirl intensity |
143
+ | `splatRadius` | `0.25` | Size of cursor splats |
144
+ | `splatForce` | `6000` | Force of cursor movements |
145
+ | `shading` | `true` | Enable 3D lighting effects |
146
+ | `colorUpdateSpeed` | `0.5` | Speed of color transitions |
147
+ | `transparent` | `false` | Transparent background |
148
+
149
+ ---
150
+
151
+ ## 🌟 Features
152
+
153
+ - **Real-time Fluid Dynamics**: Physics-based simulation using Navier-Stokes equations
154
+ - **WebGL Accelerated**: High-performance rendering for smooth 60fps
155
+ - **Interactive**: Responds to mouse and touch movements
156
+ - **Customizable**: Extensive configuration options
157
+ - **Mobile Support**: Touch-optimized interactions
158
+ - **Auto-scaling**: Adapts to screen size and pixel ratio
159
+ - **Color Cycling**: Dynamic, evolving color palettes
160
+ - **3D Lighting**: Optional shading for depth perception
161
+
162
+ ---
163
+
164
+ ## 🎯 Use Cases
165
+
166
+ - **Website Backgrounds**: Immersive animated backgrounds
167
+ - **Cursor Effects**: Enhanced user interaction feedback
168
+ - **Data Visualization**: Fluid-based data representations
169
+ - **Art Installations**: Digital art and creative coding
170
+ - **Game Effects**: Atmospheric and UI effects
171
+ - **Product Demos**: Eye-catching technology showcases
172
+
173
+ ---
174
+
175
+ ## 🧑‍💻 Author
176
+
177
+ Built and maintained by [**Farasat Ali**](https://www.farasat.me)
178
+
179
+ - Website: [www.farasat.me](https://www.farasat.me)
180
+ - LinkedIn: [linkedin.com/in/faraasat](https://linkedin.com/in/faraasat)
181
+ - GitHub: [github.com/faraasat](https://github.com/faraasat)
@@ -0,0 +1,55 @@
1
+ import React from 'react';
2
+
3
+ interface ISmokeyFluidConfig {
4
+ /** Simulation grid resolution for velocity/pressure fields (lower = faster but coarser) */
5
+ simResolution: number;
6
+ /** Visual color/dye buffer resolution (affects rendering detail) */
7
+ dyeResolution: number;
8
+ /** Resolution used for high-quality screenshots or recording */
9
+ captureResolution: number;
10
+ /** Rate at which color fades from the fluid (higher = faster fade) */
11
+ densityDissipation: number;
12
+ /** Rate at which velocity energy dissipates (higher = faster slowdown) */
13
+ velocityDissipation: number;
14
+ /** Initial pressure clear multiplier (affects stability of solver) */
15
+ pressure: number;
16
+ /** Number of Jacobi iterations when solving pressure (higher = more accurate but slower) */
17
+ pressureIteration: number;
18
+ /** Strength of vorticity confinement (adds swirling, curl-like motion) */
19
+ curl: number;
20
+ /** Base normalized radius of user splats (0–1 range relative to screen size) */
21
+ splatRadius: number;
22
+ /** Force multiplier applied when user interacts (e.g., mouse/touch input) */
23
+ splatForce: number;
24
+ /** Enable or disable lighting/shading effects for visual depth */
25
+ shading: boolean;
26
+ /** Speed at which the dynamic color palette rotates during simulation */
27
+ colorUpdateSpeed: number;
28
+ /** If true, pauses the main simulation loop (used for debugging or static rendering) */
29
+ paused: boolean;
30
+ /**
31
+ * Canvas background color (can be in 0–1 normalized range or 0–255)
32
+ * Example: `{ r: 0, g: 0, b: 0 }` for black.
33
+ */
34
+ backColor: {
35
+ r: number;
36
+ g: number;
37
+ b: number;
38
+ };
39
+ /**
40
+ * Determines whether the canvas should preserve alpha transparency.
41
+ * If `true`, blending with HTML backgrounds is allowed.
42
+ */
43
+ transparent: boolean;
44
+ /**
45
+ * ID assigned to the canvas element.
46
+ * Default is "smokey-fluid-canvas".
47
+ */
48
+ id: string;
49
+ }
50
+
51
+ declare const SmokeyFluidCursor: React.FC<{
52
+ config: Partial<ISmokeyFluidConfig>;
53
+ }>;
54
+
55
+ export { SmokeyFluidCursor };
package/dist/index.js ADDED
@@ -0,0 +1,292 @@
1
+ "use client";"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }var Me=Object.defineProperty;var ie=Object.getOwnPropertySymbols;var Oe=Object.prototype.hasOwnProperty,Ye=Object.prototype.propertyIsEnumerable;var oe=(f,c,v)=>c in f?Me(f,c,{enumerable:!0,configurable:!0,writable:!0,value:v}):f[c]=v,N=(f,c)=>{for(var v in c||(c={}))Oe.call(c,v)&&oe(f,v,c[v]);if(ie)for(var v of ie(c))Ye.call(c,v)&&oe(f,v,c[v]);return f};var _react = require('react'); var _react2 = _interopRequireDefault(_react);var ne=f=>{let c=document.getElementById(f.id);if(!c)return;let v=document.createElement("style");v.textContent=`
2
+ #${f.id} {
3
+ position: fixed;
4
+ top: 0;
5
+ left: 0;
6
+ width: 100%;
7
+ height: 100%;
8
+ pointer-events: none;
9
+ z-index: -9999;
10
+ }
11
+ `,document.head.appendChild(v),j();class ue{constructor(){this.id=-1;this.texcoordX=0;this.texcoordY=0;this.prevTexcoordX=0;this.prevTexcoordY=0;this.deltaX=0;this.deltaY=0;this.down=!1;this.moved=!1;this.color=[30,0,300]}}let S=[];S.push(new ue);let{gl:t,ext:T}=ce(c);T.supportLinearFiltering||(f.dyeResolution=512,f.shading=!1);function ce(e){let r={alpha:!0,depth:!1,stencil:!1,antialias:!1,preserveDrawingBuffer:!1},i=e.getContext("webgl2",r),o=!!i;if(o||(i=e.getContext("webgl",r)||e.getContext("experimental-webgl",r)),!i)throw new Error("WebGL not supported");let n=null,u,a;if(o){let l=i;l.getExtension("EXT_color_buffer_float"),u=!!l.getExtension("OES_texture_float_linear"),a=l.HALF_FLOAT}else{let l=i;if(n=l.getExtension("OES_texture_half_float"),u=!!l.getExtension("OES_texture_half_float_linear"),!n)throw new Error("OES_texture_half_float not supported on WebGL1");a=n.HALF_FLOAT_OES}i.clearColor(0,0,0,1);let m=null,d=null,y=null;if(o){let l=i;m=F(l,l.RGBA16F,l.RGBA,a),d=F(l,l.RG16F,l.RG,a),y=F(l,l.R16F,l.RED,a)}else{let l=i;m=F(l,l.RGBA,l.RGBA,a),d=F(l,l.RGBA,l.RGBA,a),y=F(l,l.RGBA,l.RGBA,a)}return{gl:i,ext:{formatRGBA:m,formatRG:d,formatR:y,halfFloatTexType:a,supportLinearFiltering:u,isWebGL2:o}}}function F(e,r,i,o){if(!se(e,r,i,o)){if(e.RGBA16F!==void 0){let n=e;switch(r){case n.R16F:return F(n,n.RG16F,n.RG,o);case n.RG16F:return F(n,n.RGBA16F,n.RGBA,o);default:return null}}return null}return{internalFormat:r,format:i}}function se(e,r,i,o){let n=e.createTexture();if(!n)return!1;e.bindTexture(e.TEXTURE_2D,n),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MIN_FILTER,e.NEAREST),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MAG_FILTER,e.NEAREST),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_S,e.CLAMP_TO_EDGE),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_T,e.CLAMP_TO_EDGE),e.texImage2D(e.TEXTURE_2D,0,r,4,4,0,i,o,null);let u=e.createFramebuffer();if(!u)return!1;e.bindFramebuffer(e.FRAMEBUFFER,u),e.framebufferTexture2D(e.FRAMEBUFFER,e.COLOR_ATTACHMENT0,e.TEXTURE_2D,n,0);let a=e.checkFramebufferStatus(e.FRAMEBUFFER);return e.bindFramebuffer(e.FRAMEBUFFER,null),e.deleteFramebuffer(u),e.deleteTexture(n),a===e.FRAMEBUFFER_COMPLETE}class le{constructor(r,i){this.vertexShader=r,this.fragmentShaderSource=i,this.programs={},this.activeProgram=null,this.uniforms={}}setKeywords(r){let i=0;for(let n=0;n<r.length;n++)i+=ze(r[n]);let o=this.programs[i];if(!o){let n=x(t,t.FRAGMENT_SHADER,V(this.fragmentShaderSource,r));o=I(t,this.vertexShader,n),this.programs[i]=o}o!==this.activeProgram&&(this.uniforms=W(t,o),this.activeProgram=o)}bind(){this.activeProgram&&t.useProgram(this.activeProgram)}}class E{constructor(r,i){this.program=I(t,r,i),this.uniforms=W(t,this.program)}bind(){this.program&&t.useProgram(this.program)}}function I(e,r,i){let o=e.createProgram();return e.attachShader(o,r),e.attachShader(o,i),e.bindAttribLocation(o,0,"aPosition"),e.linkProgram(o),e.getProgramParameter(o,e.LINK_STATUS)||console.trace(e.getProgramInfoLog(o)),o}function W(e,r){let i={},o=e.getProgramParameter(r,e.ACTIVE_UNIFORMS);for(let n=0;n<o;n++){let u=e.getActiveUniform(r,n);if(!u)continue;let a=u.name,m=e.getUniformLocation(r,a);m&&(i[a]=m)}return i}function x(e,r,i){let o=e.createShader(r);return e.shaderSource(o,i),e.compileShader(o),e.getShaderParameter(o,e.COMPILE_STATUS)||console.trace(e.getShaderInfoLog(o)),o}function V(e,r){if(!r||r.length===0)return e;let i="";return r.forEach(o=>{i+="#define "+o+`
12
+ `}),i+e}let b=x(t,t.VERTEX_SHADER,`
13
+ precision highp float;
14
+
15
+ attribute vec2 aPosition;
16
+ varying vec2 vUv;
17
+ varying vec2 vL;
18
+ varying vec2 vR;
19
+ varying vec2 vT;
20
+ varying vec2 vB;
21
+ uniform vec2 texelSize;
22
+
23
+ void main () {
24
+ vUv = aPosition * 0.5 + 0.5;
25
+ vL = vUv - vec2(texelSize.x, 0.0);
26
+ vR = vUv + vec2(texelSize.x, 0.0);
27
+ vT = vUv + vec2(0.0, texelSize.y);
28
+ vB = vUv - vec2(0.0, texelSize.y);
29
+ gl_Position = vec4(aPosition, 0.0, 1.0);
30
+ }
31
+ `),fe=x(t,t.FRAGMENT_SHADER,`
32
+ precision mediump float;
33
+ precision mediump sampler2D;
34
+
35
+ varying highp vec2 vUv;
36
+ uniform sampler2D uTexture;
37
+
38
+ void main () {
39
+ gl_FragColor = texture2D(uTexture, vUv);
40
+ }
41
+ `),me=x(t,t.FRAGMENT_SHADER,`
42
+ precision mediump float;
43
+ precision mediump sampler2D;
44
+
45
+ varying highp vec2 vUv;
46
+ uniform sampler2D uTexture;
47
+ uniform float value;
48
+
49
+ void main () {
50
+ gl_FragColor = value * texture2D(uTexture, vUv);
51
+ }
52
+ `),de=`
53
+ precision highp float;
54
+ precision highp sampler2D;
55
+
56
+ varying vec2 vUv;
57
+ varying vec2 vL;
58
+ varying vec2 vR;
59
+ varying vec2 vT;
60
+ varying vec2 vB;
61
+ uniform sampler2D uTexture;
62
+ uniform vec2 texelSize;
63
+
64
+ void main () {
65
+ vec3 c = texture2D(uTexture, vUv).rgb;
66
+
67
+ #ifdef shading
68
+ // Calculate normal from neighboring pixels for lighting
69
+ vec3 lc = texture2D(uTexture, vL).rgb;
70
+ vec3 rc = texture2D(uTexture, vR).rgb;
71
+ vec3 tc = texture2D(uTexture, vT).rgb;
72
+ vec3 bc = texture2D(uTexture, vB).rgb;
73
+
74
+ float dx = length(rc) - length(lc);
75
+ float dy = length(tc) - length(bc);
76
+
77
+ vec3 n = normalize(vec3(dx, dy, length(texelSize)));
78
+ vec3 l = vec3(0.0, 0.0, 1.0);
79
+
80
+ // Apply diffuse lighting
81
+ float diffuse = clamp(dot(n, l) + 0.7, 0.7, 1.0);
82
+ c *= diffuse;
83
+ #endif
84
+
85
+ // Use brightest channel for alpha
86
+ float a = max(c.r, max(c.g, c.b));
87
+ gl_FragColor = vec4(c, a);
88
+ }
89
+ `,ve=x(t,t.FRAGMENT_SHADER,`
90
+ precision highp float;
91
+ precision highp sampler2D;
92
+
93
+ varying vec2 vUv;
94
+ uniform sampler2D uTarget;
95
+ uniform float aspectRatio;
96
+ uniform vec3 color;
97
+ uniform vec2 point;
98
+ uniform float radius;
99
+
100
+ void main () {
101
+ // Calculate Gaussian splat
102
+ vec2 p = vUv - point.xy;
103
+ p.x *= aspectRatio;
104
+ vec3 splat = exp(-dot(p, p) / radius) * color;
105
+ vec3 base = texture2D(uTarget, vUv).xyz;
106
+ gl_FragColor = vec4(base + splat, 1.0);
107
+ }
108
+ `),he=x(t,t.FRAGMENT_SHADER,V(`
109
+ precision highp float;
110
+ precision highp sampler2D;
111
+
112
+ varying vec2 vUv;
113
+ uniform sampler2D uVelocity;
114
+ uniform sampler2D uSource;
115
+ uniform vec2 texelSize;
116
+ uniform vec2 dyeTexelSize;
117
+ uniform float dt;
118
+ uniform float dissipation;
119
+
120
+ // Manual bilinear filtering for WebGL1 without linear filtering support
121
+ vec4 bilerp (sampler2D sam, vec2 uv, vec2 tsize) {
122
+ vec2 st = uv / tsize - 0.5;
123
+
124
+ vec2 iuv = floor(st);
125
+ vec2 fuv = fract(st);
126
+
127
+ vec4 a = texture2D(sam, (iuv + vec2(0.5, 0.5)) * tsize);
128
+ vec4 b = texture2D(sam, (iuv + vec2(1.5, 0.5)) * tsize);
129
+ vec4 c = texture2D(sam, (iuv + vec2(0.5, 1.5)) * tsize);
130
+ vec4 d = texture2D(sam, (iuv + vec2(1.5, 1.5)) * tsize);
131
+
132
+ return mix(mix(a, b, fuv.x), mix(c, d, fuv.x), fuv.y);
133
+ }
134
+
135
+ void main () {
136
+ #ifdef MANUAL_FILTERING
137
+ // Manual filtering for devices without linear filtering
138
+ vec2 coord = vUv - dt * bilerp(uVelocity, vUv, texelSize).xy * texelSize;
139
+ vec4 result = bilerp(uSource, coord, dyeTexelSize);
140
+ #else
141
+ // Standard texture lookup with linear filtering
142
+ vec2 coord = vUv - dt * texture2D(uVelocity, vUv).xy * texelSize;
143
+ vec4 result = texture2D(uSource, coord);
144
+ #endif
145
+ // Apply dissipation (fade over time)
146
+ float decay = 1.0 + dissipation * dt;
147
+ gl_FragColor = result / decay;
148
+ }
149
+ `,T.supportLinearFiltering?null:["MANUAL_FILTERING"])),pe=x(t,t.FRAGMENT_SHADER,`
150
+ precision mediump float;
151
+ precision mediump sampler2D;
152
+
153
+ varying highp vec2 vUv;
154
+ varying highp vec2 vL;
155
+ varying highp vec2 vR;
156
+ varying highp vec2 vT;
157
+ varying highp vec2 vB;
158
+ uniform sampler2D uVelocity;
159
+
160
+ void main () {
161
+ // Sample neighboring velocities
162
+ float L = texture2D(uVelocity, vL).x;
163
+ float R = texture2D(uVelocity, vR).x;
164
+ float T = texture2D(uVelocity, vT).y;
165
+ float B = texture2D(uVelocity, vB).y;
166
+
167
+ vec2 C = texture2D(uVelocity, vUv).xy;
168
+
169
+ // Boundary conditions
170
+ if (vL.x < 0.0) { L = -C.x; }
171
+ if (vR.x > 1.0) { R = -C.x; }
172
+ if (vT.y > 1.0) { T = -C.y; }
173
+ if (vB.y < 0.0) { B = -C.y; }
174
+
175
+ // Calculate divergence
176
+ float div = 0.5 * (R - L + T - B);
177
+ gl_FragColor = vec4(div, 0.0, 0.0, 1.0);
178
+ }
179
+ `),xe=x(t,t.FRAGMENT_SHADER,`
180
+ precision mediump float;
181
+ precision mediump sampler2D;
182
+
183
+ varying highp vec2 vUv;
184
+ varying highp vec2 vL;
185
+ varying highp vec2 vR;
186
+ varying highp vec2 vT;
187
+ varying highp vec2 vB;
188
+ uniform sampler2D uVelocity;
189
+
190
+ void main () {
191
+ // Sample velocity components for curl calculation
192
+ float L = texture2D(uVelocity, vL).y;
193
+ float R = texture2D(uVelocity, vR).y;
194
+ float T = texture2D(uVelocity, vT).x;
195
+ float B = texture2D(uVelocity, vB).x;
196
+ float vorticity = R - L - T + B;
197
+ gl_FragColor = vec4(0.5 * vorticity, 0.0, 0.0, 1.0);
198
+ }
199
+ `),ge=x(t,t.FRAGMENT_SHADER,`
200
+ precision highp float;
201
+ precision highp sampler2D;
202
+
203
+ varying vec2 vUv;
204
+ varying vec2 vL;
205
+ varying vec2 vR;
206
+ varying vec2 vT;
207
+ varying vec2 vB;
208
+ uniform sampler2D uVelocity;
209
+ uniform sampler2D uCurl;
210
+ uniform float curl;
211
+ uniform float dt;
212
+
213
+ void main () {
214
+ // Sample curl from neighbors
215
+ float L = texture2D(uCurl, vL).x;
216
+ float R = texture2D(uCurl, vR).x;
217
+ float T = texture2D(uCurl, vT).x;
218
+ float B = texture2D(uCurl, vB).x;
219
+ float C = texture2D(uCurl, vUv).x;
220
+
221
+ // Calculate vorticity force
222
+ vec2 force = 0.5 * vec2(abs(T) - abs(B), abs(R) - abs(L));
223
+ force /= length(force) + 0.0001; // Normalize
224
+ force *= curl * C; // Scale by curl strength
225
+ force.y *= -1.0; // Adjust coordinate system
226
+
227
+ // Apply force to velocity
228
+ vec2 velocity = texture2D(uVelocity, vUv).xy;
229
+ velocity += force * dt;
230
+ velocity = min(max(velocity, -1000.0), 1000.0); // Clamp to prevent explosion
231
+ gl_FragColor = vec4(velocity, 0.0, 1.0);
232
+ }
233
+ `),be=x(t,t.FRAGMENT_SHADER,`
234
+ precision mediump float;
235
+ precision mediump sampler2D;
236
+
237
+ varying highp vec2 vUv;
238
+ varying highp vec2 vL;
239
+ varying highp vec2 vR;
240
+ varying highp vec2 vT;
241
+ varying highp vec2 vB;
242
+ uniform sampler2D uPressure;
243
+ uniform sampler2D uDivergence;
244
+
245
+ void main () {
246
+ // Sample pressure from neighbors
247
+ float L = texture2D(uPressure, vL).x;
248
+ float R = texture2D(uPressure, vR).x;
249
+ float T = texture2D(uPressure, vT).x;
250
+ float B = texture2D(uPressure, vB).x;
251
+ float C = texture2D(uPressure, vUv).x;
252
+ float divergence = texture2D(uDivergence, vUv).x;
253
+
254
+ // Jacobi iteration for pressure solve
255
+ float pressure = (L + R + B + T - divergence) * 0.25;
256
+ gl_FragColor = vec4(pressure, 0.0, 0.0, 1.0);
257
+ }
258
+ `),Re=x(t,t.FRAGMENT_SHADER,`
259
+ precision mediump float;
260
+ precision mediump sampler2D;
261
+
262
+ varying highp vec2 vUv;
263
+ varying highp vec2 vL;
264
+ varying highp vec2 vR;
265
+ varying highp vec2 vT;
266
+ varying highp vec2 vB;
267
+ uniform sampler2D uPressure;
268
+ uniform sampler2D uVelocity;
269
+
270
+ void main () {
271
+ // Calculate pressure gradient
272
+ float L = texture2D(uPressure, vL).x;
273
+ float R = texture2D(uPressure, vR).x;
274
+ float T = texture2D(uPressure, vT).x;
275
+ float B = texture2D(uPressure, vB).x;
276
+ vec2 velocity = texture2D(uVelocity, vUv).xy;
277
+
278
+ // Subtract gradient to make velocity divergence-free
279
+ velocity.xy -= vec2(R - L, T - B);
280
+ gl_FragColor = vec4(velocity, 0.0, 1.0);
281
+ }
282
+ `),p=(()=>{let e=t.createBuffer();t.bindBuffer(t.ARRAY_BUFFER,e),t.bufferData(t.ARRAY_BUFFER,new Float32Array([-1,-1,-1,1,1,1,1,-1]),t.STATIC_DRAW);let r=t.createBuffer();return t.bindBuffer(t.ELEMENT_ARRAY_BUFFER,r),t.bufferData(t.ELEMENT_ARRAY_BUFFER,new Uint16Array([0,1,2,0,2,3]),t.STATIC_DRAW),t.vertexAttribPointer(0,2,t.FLOAT,!1,0,0),t.enableVertexAttribArray(0),(i,o=!1)=>{i==null?(t.viewport(0,0,t.drawingBufferWidth,t.drawingBufferHeight),t.bindFramebuffer(t.FRAMEBUFFER,null)):(t.viewport(0,0,i.width,i.height),t.bindFramebuffer(t.FRAMEBUFFER,i.fbo)),o&&(t.clearColor(0,0,0,1),t.clear(t.COLOR_BUFFER_BIT)),t.drawElements(t.TRIANGLES,6,t.UNSIGNED_SHORT,0)}})(),h,s,P,G,D,H=new E(b,fe),X=new E(b,me),L=new E(b,ve),g=new E(b,he),z=new E(b,pe),M=new E(b,xe),_=new E(b,ge),B=new E(b,be),w=new E(b,Re),U=new le(b,de);function k(){let e=re(f.simResolution),r=re(f.dyeResolution),i=T.halfFloatTexType,o=T.formatRGBA,n=T.formatRG,u=T.formatR,a=T.supportLinearFiltering?t.LINEAR:t.NEAREST;t.disable(t.BLEND),h==null?h=O(r.width,r.height,o.internalFormat,o.format,i,a):h=K(h,r.width,r.height,o.internalFormat,o.format,i,a),s==null?s=O(e.width,e.height,n.internalFormat,n.format,i,a):s=K(s,e.width,e.height,n.internalFormat,n.format,i,a),P=A(e.width,e.height,u.internalFormat,u.format,i,t.NEAREST),G=A(e.width,e.height,u.internalFormat,u.format,i,t.NEAREST),D=O(e.width,e.height,u.internalFormat,u.format,i,t.NEAREST)}function A(e,r,i,o,n,u){t.activeTexture(t.TEXTURE0);let a=t.createTexture();t.bindTexture(t.TEXTURE_2D,a),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_MIN_FILTER,u),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_MAG_FILTER,u),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_WRAP_S,t.CLAMP_TO_EDGE),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_WRAP_T,t.CLAMP_TO_EDGE),t.texImage2D(t.TEXTURE_2D,0,i,e,r,0,o,n,null);let m=t.createFramebuffer();t.bindFramebuffer(t.FRAMEBUFFER,m),t.framebufferTexture2D(t.FRAMEBUFFER,t.COLOR_ATTACHMENT0,t.TEXTURE_2D,a,0),t.viewport(0,0,e,r),t.clear(t.COLOR_BUFFER_BIT);let d=1/e,y=1/r;return{texture:a,fbo:m,width:e,height:r,texelSizeX:d,texelSizeY:y,attach(l){return t.activeTexture(t.TEXTURE0+l),t.bindTexture(t.TEXTURE_2D,a),l}}}function O(e,r,i,o,n,u){let a=A(e,r,i,o,n,u),m=A(e,r,i,o,n,u);return{width:e,height:r,texelSizeX:a.texelSizeX,texelSizeY:a.texelSizeY,get read(){return a},set read(d){a=d},get write(){return m},set write(d){m=d},swap(){let d=a;a=m,m=d}}}function Te(e,r,i,o,n,u,a){let m=A(r,i,o,n,u,a);return H.bind(),t.uniform1i(H.uniforms.uTexture,e.attach(0)),p(m),m}function K(e,r,i,o,n,u,a){return e.width===r&&e.height===i||(e.read=Te(e.read,r,i,o,n,u,a),e.write=A(r,i,o,n,u,a),e.width=r,e.height=i,e.texelSizeX=1/r,e.texelSizeY=1/i),e}function Ee(){let e=[];f.shading&&e.push("shading"),U.setKeywords(e)}Ee(),k();let q=Date.now(),C=0;function $(){let e=ye();j()&&k(),Se(e),Fe(),De(e),Le(null),requestAnimationFrame($)}function ye(){let e=Date.now(),r=(e-q)/1e3;return r=Math.min(r,.016666),q=e,r}function j(){let e=R(c.clientWidth),r=R(c.clientHeight);return c.width!==e||c.height!==r?(c.width=e,c.height=r,!0):!1}function Se(e){C+=e*f.colorUpdateSpeed,C>=1&&(C=Xe(C,0,1),S.forEach(r=>{r.color=te(Y())}))}function Fe(){S.forEach(e=>{e.moved&&(e.moved=!1,Ae(e))})}function De(e){t.disable(t.BLEND),M.bind(),t.uniform2f(M.uniforms.texelSize,s.texelSizeX,s.texelSizeY),t.uniform1i(M.uniforms.uVelocity,s.read.attach(0)),p(G),_.bind(),t.uniform2f(_.uniforms.texelSize,s.texelSizeX,s.texelSizeY),t.uniform1i(_.uniforms.uVelocity,s.read.attach(0)),t.uniform1i(_.uniforms.uCurl,G.attach(1)),t.uniform1f(_.uniforms.curl,f.curl),t.uniform1f(_.uniforms.dt,e),p(s.write),s.swap(),z.bind(),t.uniform2f(z.uniforms.texelSize,s.texelSizeX,s.texelSizeY),t.uniform1i(z.uniforms.uVelocity,s.read.attach(0)),p(P),X.bind(),t.uniform1i(X.uniforms.uTexture,D.read.attach(0)),t.uniform1f(X.uniforms.value,f.pressure),p(D.write),D.swap(),B.bind(),t.uniform2f(B.uniforms.texelSize,s.texelSizeX,s.texelSizeY),t.uniform1i(B.uniforms.uDivergence,P.attach(0));for(let i=0;i<f.pressureIteration;i++)t.uniform1i(B.uniforms.uPressure,D.read.attach(1)),p(D.write),D.swap();w.bind(),t.uniform2f(w.uniforms.texelSize,s.texelSizeX,s.texelSizeY),t.uniform1i(w.uniforms.uPressure,D.read.attach(0)),t.uniform1i(w.uniforms.uVelocity,s.read.attach(1)),p(s.write),s.swap(),g.bind(),t.uniform2f(g.uniforms.texelSize,s.texelSizeX,s.texelSizeY),T.supportLinearFiltering||t.uniform2f(g.uniforms.dyeTexelSize,s.texelSizeX,s.texelSizeY);let r=s.read.attach(0);t.uniform1i(g.uniforms.uVelocity,r),t.uniform1i(g.uniforms.uSource,r),t.uniform1f(g.uniforms.dt,e),t.uniform1f(g.uniforms.dissipation,f.velocityDissipation),p(s.write),s.swap(),T.supportLinearFiltering||t.uniform2f(g.uniforms.dyeTexelSize,h.texelSizeX,h.texelSizeY),t.uniform1i(g.uniforms.uVelocity,s.read.attach(0)),t.uniform1i(g.uniforms.uSource,h.read.attach(1)),t.uniform1f(g.uniforms.dissipation,f.densityDissipation),p(h.write),h.swap()}function Le(e){t.blendFunc(t.ONE,t.ONE_MINUS_SRC_ALPHA),t.enable(t.BLEND),_e(e)}function _e(e){let r=e==null?t.drawingBufferWidth:e.width,i=e==null?t.drawingBufferHeight:e.height;U.bind(),f.shading&&t.uniform2f(U.uniforms.texelSize,1/r,1/i),t.uniform1i(U.uniforms.uTexture,h.read.attach(0)),p(e)}function Ae(e){let r=e.deltaX*f.splatForce,i=e.deltaY*f.splatForce,o=Pe(e.color);Q(e.texcoordX,e.texcoordY,r,i,o)}function J(e){let r=Y();r.r*=10,r.g*=10,r.b*=10;let i=10*(Math.random()-.5),o=30*(Math.random()-.5);Q(e.texcoordX,e.texcoordY,i,o,r)}function Q(e,r,i,o,n){L.bind(),t.uniform1i(L.uniforms.uTarget,s.read.attach(0)),t.uniform1f(L.uniforms.aspectRatio,c.width/c.height),t.uniform2f(L.uniforms.point,e,r),t.uniform3f(L.uniforms.color,i,o,0),t.uniform1f(L.uniforms.radius,Be(f.splatRadius/100)),p(s.write),s.swap(),t.uniform1i(L.uniforms.uTarget,h.read.attach(0)),t.uniform3f(L.uniforms.color,n.r,n.g,n.b),p(h.write),h.swap()}function Be(e){let r=c.width/c.height;return r>1&&(e*=r),e}window.addEventListener("mousedown",e=>{let r=S[0],i=c.getBoundingClientRect(),o=R(e.clientX-i.left),n=R(e.clientY-i.top);Z(r,-1,o,n),J(r)}),window.addEventListener("mousemove",e=>{let r=S[0],i=c.getBoundingClientRect(),o=R(e.clientX-i.left),n=R(e.clientY-i.top),u=r.color;ee(r,o,n,u)}),window.addEventListener("touchstart",e=>{let r=e.targetTouches,i=c.getBoundingClientRect(),o=S[0];for(let n=0;n<r.length;n++){let u=R(r[n].clientX-i.left),a=R(r[n].clientY-i.top);Z(o,r[n].identifier,u,a),J(o)}}),window.addEventListener("touchmove",e=>{e.preventDefault();let r=e.targetTouches,i=c.getBoundingClientRect(),o=S[0];for(let n=0;n<r.length;n++){let u=R(r[n].clientX-i.left),a=R(r[n].clientY-i.top);ee(o,u,a,o.color)}},{passive:!1}),window.addEventListener("touchend",e=>{let r=e.changedTouches,i=S[0];for(let o=0;o<r.length;o++)we(i)});function Z(e,r,i,o){e.id=r,e.down=!0,e.moved=!1,e.texcoordX=i/c.width,e.texcoordY=1-o/c.height,e.prevTexcoordX=e.texcoordX,e.prevTexcoordY=e.texcoordY,e.deltaX=0,e.deltaY=0,e.color=te(Y())}function ee(e,r,i,o){e.prevTexcoordX=e.texcoordX,e.prevTexcoordY=e.texcoordY,e.texcoordX=r/c.width,e.texcoordY=1-i/c.height,e.deltaX=Ue(e.texcoordX-e.prevTexcoordX),e.deltaY=Ce(e.texcoordY-e.prevTexcoordY),e.moved=Math.abs(e.deltaX)>0||Math.abs(e.deltaY)>0,e.color=o}function we(e){e.down=!1}function Ue(e){let r=c.width/c.height;return r<1&&(e*=r),e}function Ce(e){let r=c.width/c.height;return r>1&&(e/=r),e}function Y(){let e=Ge(Math.random(),1,1);return e.r*=.15,e.g*=.15,e.b*=.15,e}function te(e){return[e.r,e.g,e.b]}function Pe(e){return{r:e[0],g:e[1],b:e[2]}}function Ge(e,r,i){let o=0,n=0,u=0,a=Math.floor(e*6),m=e*6-a,d=i*(1-r),y=i*(1-m*r),l=i*(1-(1-m)*r);switch(a%6){case 0:o=i,n=l,u=d;break;case 1:o=y,n=i,u=d;break;case 2:o=d,n=i,u=l;break;case 3:o=d,n=y,u=i;break;case 4:o=l,n=d,u=i;break;case 5:o=i,n=d,u=y;break}return{r:o,g:n,b:u}}function Xe(e,r,i){let o=i-r;return o===0?r:(e-r)%o+r}function re(e){let r=t.drawingBufferWidth/t.drawingBufferHeight;r<1&&(r=1/r);let i=Math.round(e),o=Math.round(e*r);return t.drawingBufferWidth>t.drawingBufferHeight?{width:o,height:i}:{width:i,height:o}}function R(e){let r=window.devicePixelRatio||1;return Math.floor(e*r)}function ze(e){if(e.length===0)return 0;let r=0;for(let i=0;i<e.length;i++)r=(r<<5)-r+e.charCodeAt(i),r|=0;return r}$()};var Ie={simResolution:128,dyeResolution:1440,captureResolution:512,densityDissipation:3.5,velocityDissipation:2,pressure:.1,pressureIteration:20,curl:10,splatRadius:.5,splatForce:6e3,shading:!0,colorUpdateSpeed:10,paused:!1,backColor:{r:0,g:0,b:0},transparent:!0,id:"smokey-fluid-canvas"},qe= exports.SmokeyFluidCursor =({config:f})=>{let c=N(N({},Ie),f);return _react2.default.useEffect(()=>{if(document){let v=document.createElement("style");v.textContent=`
283
+ #${c.id} {
284
+ position: fixed;
285
+ top: 0;
286
+ left: 0;
287
+ width: 100%;
288
+ height: 100%;
289
+ pointer-events: none;
290
+ z-index: -9999;
291
+ }
292
+ `,document.head.appendChild(v),ne(c)}},[]),_react2.default.createElement("canvas",{id:c.id})};exports.SmokeyFluidCursor = qe;
package/dist/index.mjs ADDED
@@ -0,0 +1,293 @@
1
+ "use client";
2
+ var Me=Object.defineProperty;var ie=Object.getOwnPropertySymbols;var Oe=Object.prototype.hasOwnProperty,Ye=Object.prototype.propertyIsEnumerable;var oe=(f,c,v)=>c in f?Me(f,c,{enumerable:!0,configurable:!0,writable:!0,value:v}):f[c]=v,N=(f,c)=>{for(var v in c||(c={}))Oe.call(c,v)&&oe(f,v,c[v]);if(ie)for(var v of ie(c))Ye.call(c,v)&&oe(f,v,c[v]);return f};import ae from"react";var ne=f=>{let c=document.getElementById(f.id);if(!c)return;let v=document.createElement("style");v.textContent=`
3
+ #${f.id} {
4
+ position: fixed;
5
+ top: 0;
6
+ left: 0;
7
+ width: 100%;
8
+ height: 100%;
9
+ pointer-events: none;
10
+ z-index: -9999;
11
+ }
12
+ `,document.head.appendChild(v),j();class ue{constructor(){this.id=-1;this.texcoordX=0;this.texcoordY=0;this.prevTexcoordX=0;this.prevTexcoordY=0;this.deltaX=0;this.deltaY=0;this.down=!1;this.moved=!1;this.color=[30,0,300]}}let S=[];S.push(new ue);let{gl:t,ext:T}=ce(c);T.supportLinearFiltering||(f.dyeResolution=512,f.shading=!1);function ce(e){let r={alpha:!0,depth:!1,stencil:!1,antialias:!1,preserveDrawingBuffer:!1},i=e.getContext("webgl2",r),o=!!i;if(o||(i=e.getContext("webgl",r)||e.getContext("experimental-webgl",r)),!i)throw new Error("WebGL not supported");let n=null,u,a;if(o){let l=i;l.getExtension("EXT_color_buffer_float"),u=!!l.getExtension("OES_texture_float_linear"),a=l.HALF_FLOAT}else{let l=i;if(n=l.getExtension("OES_texture_half_float"),u=!!l.getExtension("OES_texture_half_float_linear"),!n)throw new Error("OES_texture_half_float not supported on WebGL1");a=n.HALF_FLOAT_OES}i.clearColor(0,0,0,1);let m=null,d=null,y=null;if(o){let l=i;m=F(l,l.RGBA16F,l.RGBA,a),d=F(l,l.RG16F,l.RG,a),y=F(l,l.R16F,l.RED,a)}else{let l=i;m=F(l,l.RGBA,l.RGBA,a),d=F(l,l.RGBA,l.RGBA,a),y=F(l,l.RGBA,l.RGBA,a)}return{gl:i,ext:{formatRGBA:m,formatRG:d,formatR:y,halfFloatTexType:a,supportLinearFiltering:u,isWebGL2:o}}}function F(e,r,i,o){if(!se(e,r,i,o)){if(e.RGBA16F!==void 0){let n=e;switch(r){case n.R16F:return F(n,n.RG16F,n.RG,o);case n.RG16F:return F(n,n.RGBA16F,n.RGBA,o);default:return null}}return null}return{internalFormat:r,format:i}}function se(e,r,i,o){let n=e.createTexture();if(!n)return!1;e.bindTexture(e.TEXTURE_2D,n),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MIN_FILTER,e.NEAREST),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MAG_FILTER,e.NEAREST),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_S,e.CLAMP_TO_EDGE),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_T,e.CLAMP_TO_EDGE),e.texImage2D(e.TEXTURE_2D,0,r,4,4,0,i,o,null);let u=e.createFramebuffer();if(!u)return!1;e.bindFramebuffer(e.FRAMEBUFFER,u),e.framebufferTexture2D(e.FRAMEBUFFER,e.COLOR_ATTACHMENT0,e.TEXTURE_2D,n,0);let a=e.checkFramebufferStatus(e.FRAMEBUFFER);return e.bindFramebuffer(e.FRAMEBUFFER,null),e.deleteFramebuffer(u),e.deleteTexture(n),a===e.FRAMEBUFFER_COMPLETE}class le{constructor(r,i){this.vertexShader=r,this.fragmentShaderSource=i,this.programs={},this.activeProgram=null,this.uniforms={}}setKeywords(r){let i=0;for(let n=0;n<r.length;n++)i+=ze(r[n]);let o=this.programs[i];if(!o){let n=x(t,t.FRAGMENT_SHADER,V(this.fragmentShaderSource,r));o=I(t,this.vertexShader,n),this.programs[i]=o}o!==this.activeProgram&&(this.uniforms=W(t,o),this.activeProgram=o)}bind(){this.activeProgram&&t.useProgram(this.activeProgram)}}class E{constructor(r,i){this.program=I(t,r,i),this.uniforms=W(t,this.program)}bind(){this.program&&t.useProgram(this.program)}}function I(e,r,i){let o=e.createProgram();return e.attachShader(o,r),e.attachShader(o,i),e.bindAttribLocation(o,0,"aPosition"),e.linkProgram(o),e.getProgramParameter(o,e.LINK_STATUS)||console.trace(e.getProgramInfoLog(o)),o}function W(e,r){let i={},o=e.getProgramParameter(r,e.ACTIVE_UNIFORMS);for(let n=0;n<o;n++){let u=e.getActiveUniform(r,n);if(!u)continue;let a=u.name,m=e.getUniformLocation(r,a);m&&(i[a]=m)}return i}function x(e,r,i){let o=e.createShader(r);return e.shaderSource(o,i),e.compileShader(o),e.getShaderParameter(o,e.COMPILE_STATUS)||console.trace(e.getShaderInfoLog(o)),o}function V(e,r){if(!r||r.length===0)return e;let i="";return r.forEach(o=>{i+="#define "+o+`
13
+ `}),i+e}let b=x(t,t.VERTEX_SHADER,`
14
+ precision highp float;
15
+
16
+ attribute vec2 aPosition;
17
+ varying vec2 vUv;
18
+ varying vec2 vL;
19
+ varying vec2 vR;
20
+ varying vec2 vT;
21
+ varying vec2 vB;
22
+ uniform vec2 texelSize;
23
+
24
+ void main () {
25
+ vUv = aPosition * 0.5 + 0.5;
26
+ vL = vUv - vec2(texelSize.x, 0.0);
27
+ vR = vUv + vec2(texelSize.x, 0.0);
28
+ vT = vUv + vec2(0.0, texelSize.y);
29
+ vB = vUv - vec2(0.0, texelSize.y);
30
+ gl_Position = vec4(aPosition, 0.0, 1.0);
31
+ }
32
+ `),fe=x(t,t.FRAGMENT_SHADER,`
33
+ precision mediump float;
34
+ precision mediump sampler2D;
35
+
36
+ varying highp vec2 vUv;
37
+ uniform sampler2D uTexture;
38
+
39
+ void main () {
40
+ gl_FragColor = texture2D(uTexture, vUv);
41
+ }
42
+ `),me=x(t,t.FRAGMENT_SHADER,`
43
+ precision mediump float;
44
+ precision mediump sampler2D;
45
+
46
+ varying highp vec2 vUv;
47
+ uniform sampler2D uTexture;
48
+ uniform float value;
49
+
50
+ void main () {
51
+ gl_FragColor = value * texture2D(uTexture, vUv);
52
+ }
53
+ `),de=`
54
+ precision highp float;
55
+ precision highp sampler2D;
56
+
57
+ varying vec2 vUv;
58
+ varying vec2 vL;
59
+ varying vec2 vR;
60
+ varying vec2 vT;
61
+ varying vec2 vB;
62
+ uniform sampler2D uTexture;
63
+ uniform vec2 texelSize;
64
+
65
+ void main () {
66
+ vec3 c = texture2D(uTexture, vUv).rgb;
67
+
68
+ #ifdef shading
69
+ // Calculate normal from neighboring pixels for lighting
70
+ vec3 lc = texture2D(uTexture, vL).rgb;
71
+ vec3 rc = texture2D(uTexture, vR).rgb;
72
+ vec3 tc = texture2D(uTexture, vT).rgb;
73
+ vec3 bc = texture2D(uTexture, vB).rgb;
74
+
75
+ float dx = length(rc) - length(lc);
76
+ float dy = length(tc) - length(bc);
77
+
78
+ vec3 n = normalize(vec3(dx, dy, length(texelSize)));
79
+ vec3 l = vec3(0.0, 0.0, 1.0);
80
+
81
+ // Apply diffuse lighting
82
+ float diffuse = clamp(dot(n, l) + 0.7, 0.7, 1.0);
83
+ c *= diffuse;
84
+ #endif
85
+
86
+ // Use brightest channel for alpha
87
+ float a = max(c.r, max(c.g, c.b));
88
+ gl_FragColor = vec4(c, a);
89
+ }
90
+ `,ve=x(t,t.FRAGMENT_SHADER,`
91
+ precision highp float;
92
+ precision highp sampler2D;
93
+
94
+ varying vec2 vUv;
95
+ uniform sampler2D uTarget;
96
+ uniform float aspectRatio;
97
+ uniform vec3 color;
98
+ uniform vec2 point;
99
+ uniform float radius;
100
+
101
+ void main () {
102
+ // Calculate Gaussian splat
103
+ vec2 p = vUv - point.xy;
104
+ p.x *= aspectRatio;
105
+ vec3 splat = exp(-dot(p, p) / radius) * color;
106
+ vec3 base = texture2D(uTarget, vUv).xyz;
107
+ gl_FragColor = vec4(base + splat, 1.0);
108
+ }
109
+ `),he=x(t,t.FRAGMENT_SHADER,V(`
110
+ precision highp float;
111
+ precision highp sampler2D;
112
+
113
+ varying vec2 vUv;
114
+ uniform sampler2D uVelocity;
115
+ uniform sampler2D uSource;
116
+ uniform vec2 texelSize;
117
+ uniform vec2 dyeTexelSize;
118
+ uniform float dt;
119
+ uniform float dissipation;
120
+
121
+ // Manual bilinear filtering for WebGL1 without linear filtering support
122
+ vec4 bilerp (sampler2D sam, vec2 uv, vec2 tsize) {
123
+ vec2 st = uv / tsize - 0.5;
124
+
125
+ vec2 iuv = floor(st);
126
+ vec2 fuv = fract(st);
127
+
128
+ vec4 a = texture2D(sam, (iuv + vec2(0.5, 0.5)) * tsize);
129
+ vec4 b = texture2D(sam, (iuv + vec2(1.5, 0.5)) * tsize);
130
+ vec4 c = texture2D(sam, (iuv + vec2(0.5, 1.5)) * tsize);
131
+ vec4 d = texture2D(sam, (iuv + vec2(1.5, 1.5)) * tsize);
132
+
133
+ return mix(mix(a, b, fuv.x), mix(c, d, fuv.x), fuv.y);
134
+ }
135
+
136
+ void main () {
137
+ #ifdef MANUAL_FILTERING
138
+ // Manual filtering for devices without linear filtering
139
+ vec2 coord = vUv - dt * bilerp(uVelocity, vUv, texelSize).xy * texelSize;
140
+ vec4 result = bilerp(uSource, coord, dyeTexelSize);
141
+ #else
142
+ // Standard texture lookup with linear filtering
143
+ vec2 coord = vUv - dt * texture2D(uVelocity, vUv).xy * texelSize;
144
+ vec4 result = texture2D(uSource, coord);
145
+ #endif
146
+ // Apply dissipation (fade over time)
147
+ float decay = 1.0 + dissipation * dt;
148
+ gl_FragColor = result / decay;
149
+ }
150
+ `,T.supportLinearFiltering?null:["MANUAL_FILTERING"])),pe=x(t,t.FRAGMENT_SHADER,`
151
+ precision mediump float;
152
+ precision mediump sampler2D;
153
+
154
+ varying highp vec2 vUv;
155
+ varying highp vec2 vL;
156
+ varying highp vec2 vR;
157
+ varying highp vec2 vT;
158
+ varying highp vec2 vB;
159
+ uniform sampler2D uVelocity;
160
+
161
+ void main () {
162
+ // Sample neighboring velocities
163
+ float L = texture2D(uVelocity, vL).x;
164
+ float R = texture2D(uVelocity, vR).x;
165
+ float T = texture2D(uVelocity, vT).y;
166
+ float B = texture2D(uVelocity, vB).y;
167
+
168
+ vec2 C = texture2D(uVelocity, vUv).xy;
169
+
170
+ // Boundary conditions
171
+ if (vL.x < 0.0) { L = -C.x; }
172
+ if (vR.x > 1.0) { R = -C.x; }
173
+ if (vT.y > 1.0) { T = -C.y; }
174
+ if (vB.y < 0.0) { B = -C.y; }
175
+
176
+ // Calculate divergence
177
+ float div = 0.5 * (R - L + T - B);
178
+ gl_FragColor = vec4(div, 0.0, 0.0, 1.0);
179
+ }
180
+ `),xe=x(t,t.FRAGMENT_SHADER,`
181
+ precision mediump float;
182
+ precision mediump sampler2D;
183
+
184
+ varying highp vec2 vUv;
185
+ varying highp vec2 vL;
186
+ varying highp vec2 vR;
187
+ varying highp vec2 vT;
188
+ varying highp vec2 vB;
189
+ uniform sampler2D uVelocity;
190
+
191
+ void main () {
192
+ // Sample velocity components for curl calculation
193
+ float L = texture2D(uVelocity, vL).y;
194
+ float R = texture2D(uVelocity, vR).y;
195
+ float T = texture2D(uVelocity, vT).x;
196
+ float B = texture2D(uVelocity, vB).x;
197
+ float vorticity = R - L - T + B;
198
+ gl_FragColor = vec4(0.5 * vorticity, 0.0, 0.0, 1.0);
199
+ }
200
+ `),ge=x(t,t.FRAGMENT_SHADER,`
201
+ precision highp float;
202
+ precision highp sampler2D;
203
+
204
+ varying vec2 vUv;
205
+ varying vec2 vL;
206
+ varying vec2 vR;
207
+ varying vec2 vT;
208
+ varying vec2 vB;
209
+ uniform sampler2D uVelocity;
210
+ uniform sampler2D uCurl;
211
+ uniform float curl;
212
+ uniform float dt;
213
+
214
+ void main () {
215
+ // Sample curl from neighbors
216
+ float L = texture2D(uCurl, vL).x;
217
+ float R = texture2D(uCurl, vR).x;
218
+ float T = texture2D(uCurl, vT).x;
219
+ float B = texture2D(uCurl, vB).x;
220
+ float C = texture2D(uCurl, vUv).x;
221
+
222
+ // Calculate vorticity force
223
+ vec2 force = 0.5 * vec2(abs(T) - abs(B), abs(R) - abs(L));
224
+ force /= length(force) + 0.0001; // Normalize
225
+ force *= curl * C; // Scale by curl strength
226
+ force.y *= -1.0; // Adjust coordinate system
227
+
228
+ // Apply force to velocity
229
+ vec2 velocity = texture2D(uVelocity, vUv).xy;
230
+ velocity += force * dt;
231
+ velocity = min(max(velocity, -1000.0), 1000.0); // Clamp to prevent explosion
232
+ gl_FragColor = vec4(velocity, 0.0, 1.0);
233
+ }
234
+ `),be=x(t,t.FRAGMENT_SHADER,`
235
+ precision mediump float;
236
+ precision mediump sampler2D;
237
+
238
+ varying highp vec2 vUv;
239
+ varying highp vec2 vL;
240
+ varying highp vec2 vR;
241
+ varying highp vec2 vT;
242
+ varying highp vec2 vB;
243
+ uniform sampler2D uPressure;
244
+ uniform sampler2D uDivergence;
245
+
246
+ void main () {
247
+ // Sample pressure from neighbors
248
+ float L = texture2D(uPressure, vL).x;
249
+ float R = texture2D(uPressure, vR).x;
250
+ float T = texture2D(uPressure, vT).x;
251
+ float B = texture2D(uPressure, vB).x;
252
+ float C = texture2D(uPressure, vUv).x;
253
+ float divergence = texture2D(uDivergence, vUv).x;
254
+
255
+ // Jacobi iteration for pressure solve
256
+ float pressure = (L + R + B + T - divergence) * 0.25;
257
+ gl_FragColor = vec4(pressure, 0.0, 0.0, 1.0);
258
+ }
259
+ `),Re=x(t,t.FRAGMENT_SHADER,`
260
+ precision mediump float;
261
+ precision mediump sampler2D;
262
+
263
+ varying highp vec2 vUv;
264
+ varying highp vec2 vL;
265
+ varying highp vec2 vR;
266
+ varying highp vec2 vT;
267
+ varying highp vec2 vB;
268
+ uniform sampler2D uPressure;
269
+ uniform sampler2D uVelocity;
270
+
271
+ void main () {
272
+ // Calculate pressure gradient
273
+ float L = texture2D(uPressure, vL).x;
274
+ float R = texture2D(uPressure, vR).x;
275
+ float T = texture2D(uPressure, vT).x;
276
+ float B = texture2D(uPressure, vB).x;
277
+ vec2 velocity = texture2D(uVelocity, vUv).xy;
278
+
279
+ // Subtract gradient to make velocity divergence-free
280
+ velocity.xy -= vec2(R - L, T - B);
281
+ gl_FragColor = vec4(velocity, 0.0, 1.0);
282
+ }
283
+ `),p=(()=>{let e=t.createBuffer();t.bindBuffer(t.ARRAY_BUFFER,e),t.bufferData(t.ARRAY_BUFFER,new Float32Array([-1,-1,-1,1,1,1,1,-1]),t.STATIC_DRAW);let r=t.createBuffer();return t.bindBuffer(t.ELEMENT_ARRAY_BUFFER,r),t.bufferData(t.ELEMENT_ARRAY_BUFFER,new Uint16Array([0,1,2,0,2,3]),t.STATIC_DRAW),t.vertexAttribPointer(0,2,t.FLOAT,!1,0,0),t.enableVertexAttribArray(0),(i,o=!1)=>{i==null?(t.viewport(0,0,t.drawingBufferWidth,t.drawingBufferHeight),t.bindFramebuffer(t.FRAMEBUFFER,null)):(t.viewport(0,0,i.width,i.height),t.bindFramebuffer(t.FRAMEBUFFER,i.fbo)),o&&(t.clearColor(0,0,0,1),t.clear(t.COLOR_BUFFER_BIT)),t.drawElements(t.TRIANGLES,6,t.UNSIGNED_SHORT,0)}})(),h,s,P,G,D,H=new E(b,fe),X=new E(b,me),L=new E(b,ve),g=new E(b,he),z=new E(b,pe),M=new E(b,xe),_=new E(b,ge),B=new E(b,be),w=new E(b,Re),U=new le(b,de);function k(){let e=re(f.simResolution),r=re(f.dyeResolution),i=T.halfFloatTexType,o=T.formatRGBA,n=T.formatRG,u=T.formatR,a=T.supportLinearFiltering?t.LINEAR:t.NEAREST;t.disable(t.BLEND),h==null?h=O(r.width,r.height,o.internalFormat,o.format,i,a):h=K(h,r.width,r.height,o.internalFormat,o.format,i,a),s==null?s=O(e.width,e.height,n.internalFormat,n.format,i,a):s=K(s,e.width,e.height,n.internalFormat,n.format,i,a),P=A(e.width,e.height,u.internalFormat,u.format,i,t.NEAREST),G=A(e.width,e.height,u.internalFormat,u.format,i,t.NEAREST),D=O(e.width,e.height,u.internalFormat,u.format,i,t.NEAREST)}function A(e,r,i,o,n,u){t.activeTexture(t.TEXTURE0);let a=t.createTexture();t.bindTexture(t.TEXTURE_2D,a),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_MIN_FILTER,u),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_MAG_FILTER,u),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_WRAP_S,t.CLAMP_TO_EDGE),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_WRAP_T,t.CLAMP_TO_EDGE),t.texImage2D(t.TEXTURE_2D,0,i,e,r,0,o,n,null);let m=t.createFramebuffer();t.bindFramebuffer(t.FRAMEBUFFER,m),t.framebufferTexture2D(t.FRAMEBUFFER,t.COLOR_ATTACHMENT0,t.TEXTURE_2D,a,0),t.viewport(0,0,e,r),t.clear(t.COLOR_BUFFER_BIT);let d=1/e,y=1/r;return{texture:a,fbo:m,width:e,height:r,texelSizeX:d,texelSizeY:y,attach(l){return t.activeTexture(t.TEXTURE0+l),t.bindTexture(t.TEXTURE_2D,a),l}}}function O(e,r,i,o,n,u){let a=A(e,r,i,o,n,u),m=A(e,r,i,o,n,u);return{width:e,height:r,texelSizeX:a.texelSizeX,texelSizeY:a.texelSizeY,get read(){return a},set read(d){a=d},get write(){return m},set write(d){m=d},swap(){let d=a;a=m,m=d}}}function Te(e,r,i,o,n,u,a){let m=A(r,i,o,n,u,a);return H.bind(),t.uniform1i(H.uniforms.uTexture,e.attach(0)),p(m),m}function K(e,r,i,o,n,u,a){return e.width===r&&e.height===i||(e.read=Te(e.read,r,i,o,n,u,a),e.write=A(r,i,o,n,u,a),e.width=r,e.height=i,e.texelSizeX=1/r,e.texelSizeY=1/i),e}function Ee(){let e=[];f.shading&&e.push("shading"),U.setKeywords(e)}Ee(),k();let q=Date.now(),C=0;function $(){let e=ye();j()&&k(),Se(e),Fe(),De(e),Le(null),requestAnimationFrame($)}function ye(){let e=Date.now(),r=(e-q)/1e3;return r=Math.min(r,.016666),q=e,r}function j(){let e=R(c.clientWidth),r=R(c.clientHeight);return c.width!==e||c.height!==r?(c.width=e,c.height=r,!0):!1}function Se(e){C+=e*f.colorUpdateSpeed,C>=1&&(C=Xe(C,0,1),S.forEach(r=>{r.color=te(Y())}))}function Fe(){S.forEach(e=>{e.moved&&(e.moved=!1,Ae(e))})}function De(e){t.disable(t.BLEND),M.bind(),t.uniform2f(M.uniforms.texelSize,s.texelSizeX,s.texelSizeY),t.uniform1i(M.uniforms.uVelocity,s.read.attach(0)),p(G),_.bind(),t.uniform2f(_.uniforms.texelSize,s.texelSizeX,s.texelSizeY),t.uniform1i(_.uniforms.uVelocity,s.read.attach(0)),t.uniform1i(_.uniforms.uCurl,G.attach(1)),t.uniform1f(_.uniforms.curl,f.curl),t.uniform1f(_.uniforms.dt,e),p(s.write),s.swap(),z.bind(),t.uniform2f(z.uniforms.texelSize,s.texelSizeX,s.texelSizeY),t.uniform1i(z.uniforms.uVelocity,s.read.attach(0)),p(P),X.bind(),t.uniform1i(X.uniforms.uTexture,D.read.attach(0)),t.uniform1f(X.uniforms.value,f.pressure),p(D.write),D.swap(),B.bind(),t.uniform2f(B.uniforms.texelSize,s.texelSizeX,s.texelSizeY),t.uniform1i(B.uniforms.uDivergence,P.attach(0));for(let i=0;i<f.pressureIteration;i++)t.uniform1i(B.uniforms.uPressure,D.read.attach(1)),p(D.write),D.swap();w.bind(),t.uniform2f(w.uniforms.texelSize,s.texelSizeX,s.texelSizeY),t.uniform1i(w.uniforms.uPressure,D.read.attach(0)),t.uniform1i(w.uniforms.uVelocity,s.read.attach(1)),p(s.write),s.swap(),g.bind(),t.uniform2f(g.uniforms.texelSize,s.texelSizeX,s.texelSizeY),T.supportLinearFiltering||t.uniform2f(g.uniforms.dyeTexelSize,s.texelSizeX,s.texelSizeY);let r=s.read.attach(0);t.uniform1i(g.uniforms.uVelocity,r),t.uniform1i(g.uniforms.uSource,r),t.uniform1f(g.uniforms.dt,e),t.uniform1f(g.uniforms.dissipation,f.velocityDissipation),p(s.write),s.swap(),T.supportLinearFiltering||t.uniform2f(g.uniforms.dyeTexelSize,h.texelSizeX,h.texelSizeY),t.uniform1i(g.uniforms.uVelocity,s.read.attach(0)),t.uniform1i(g.uniforms.uSource,h.read.attach(1)),t.uniform1f(g.uniforms.dissipation,f.densityDissipation),p(h.write),h.swap()}function Le(e){t.blendFunc(t.ONE,t.ONE_MINUS_SRC_ALPHA),t.enable(t.BLEND),_e(e)}function _e(e){let r=e==null?t.drawingBufferWidth:e.width,i=e==null?t.drawingBufferHeight:e.height;U.bind(),f.shading&&t.uniform2f(U.uniforms.texelSize,1/r,1/i),t.uniform1i(U.uniforms.uTexture,h.read.attach(0)),p(e)}function Ae(e){let r=e.deltaX*f.splatForce,i=e.deltaY*f.splatForce,o=Pe(e.color);Q(e.texcoordX,e.texcoordY,r,i,o)}function J(e){let r=Y();r.r*=10,r.g*=10,r.b*=10;let i=10*(Math.random()-.5),o=30*(Math.random()-.5);Q(e.texcoordX,e.texcoordY,i,o,r)}function Q(e,r,i,o,n){L.bind(),t.uniform1i(L.uniforms.uTarget,s.read.attach(0)),t.uniform1f(L.uniforms.aspectRatio,c.width/c.height),t.uniform2f(L.uniforms.point,e,r),t.uniform3f(L.uniforms.color,i,o,0),t.uniform1f(L.uniforms.radius,Be(f.splatRadius/100)),p(s.write),s.swap(),t.uniform1i(L.uniforms.uTarget,h.read.attach(0)),t.uniform3f(L.uniforms.color,n.r,n.g,n.b),p(h.write),h.swap()}function Be(e){let r=c.width/c.height;return r>1&&(e*=r),e}window.addEventListener("mousedown",e=>{let r=S[0],i=c.getBoundingClientRect(),o=R(e.clientX-i.left),n=R(e.clientY-i.top);Z(r,-1,o,n),J(r)}),window.addEventListener("mousemove",e=>{let r=S[0],i=c.getBoundingClientRect(),o=R(e.clientX-i.left),n=R(e.clientY-i.top),u=r.color;ee(r,o,n,u)}),window.addEventListener("touchstart",e=>{let r=e.targetTouches,i=c.getBoundingClientRect(),o=S[0];for(let n=0;n<r.length;n++){let u=R(r[n].clientX-i.left),a=R(r[n].clientY-i.top);Z(o,r[n].identifier,u,a),J(o)}}),window.addEventListener("touchmove",e=>{e.preventDefault();let r=e.targetTouches,i=c.getBoundingClientRect(),o=S[0];for(let n=0;n<r.length;n++){let u=R(r[n].clientX-i.left),a=R(r[n].clientY-i.top);ee(o,u,a,o.color)}},{passive:!1}),window.addEventListener("touchend",e=>{let r=e.changedTouches,i=S[0];for(let o=0;o<r.length;o++)we(i)});function Z(e,r,i,o){e.id=r,e.down=!0,e.moved=!1,e.texcoordX=i/c.width,e.texcoordY=1-o/c.height,e.prevTexcoordX=e.texcoordX,e.prevTexcoordY=e.texcoordY,e.deltaX=0,e.deltaY=0,e.color=te(Y())}function ee(e,r,i,o){e.prevTexcoordX=e.texcoordX,e.prevTexcoordY=e.texcoordY,e.texcoordX=r/c.width,e.texcoordY=1-i/c.height,e.deltaX=Ue(e.texcoordX-e.prevTexcoordX),e.deltaY=Ce(e.texcoordY-e.prevTexcoordY),e.moved=Math.abs(e.deltaX)>0||Math.abs(e.deltaY)>0,e.color=o}function we(e){e.down=!1}function Ue(e){let r=c.width/c.height;return r<1&&(e*=r),e}function Ce(e){let r=c.width/c.height;return r>1&&(e/=r),e}function Y(){let e=Ge(Math.random(),1,1);return e.r*=.15,e.g*=.15,e.b*=.15,e}function te(e){return[e.r,e.g,e.b]}function Pe(e){return{r:e[0],g:e[1],b:e[2]}}function Ge(e,r,i){let o=0,n=0,u=0,a=Math.floor(e*6),m=e*6-a,d=i*(1-r),y=i*(1-m*r),l=i*(1-(1-m)*r);switch(a%6){case 0:o=i,n=l,u=d;break;case 1:o=y,n=i,u=d;break;case 2:o=d,n=i,u=l;break;case 3:o=d,n=y,u=i;break;case 4:o=l,n=d,u=i;break;case 5:o=i,n=d,u=y;break}return{r:o,g:n,b:u}}function Xe(e,r,i){let o=i-r;return o===0?r:(e-r)%o+r}function re(e){let r=t.drawingBufferWidth/t.drawingBufferHeight;r<1&&(r=1/r);let i=Math.round(e),o=Math.round(e*r);return t.drawingBufferWidth>t.drawingBufferHeight?{width:o,height:i}:{width:i,height:o}}function R(e){let r=window.devicePixelRatio||1;return Math.floor(e*r)}function ze(e){if(e.length===0)return 0;let r=0;for(let i=0;i<e.length;i++)r=(r<<5)-r+e.charCodeAt(i),r|=0;return r}$()};var We={simResolution:128,dyeResolution:1440,captureResolution:512,densityDissipation:3.5,velocityDissipation:2,pressure:.1,pressureIteration:20,curl:10,splatRadius:.5,splatForce:6e3,shading:!0,colorUpdateSpeed:10,paused:!1,backColor:{r:0,g:0,b:0},transparent:!0,id:"smokey-fluid-canvas"},$e=({config:f})=>{let c=N(N({},We),f);return ae.useEffect(()=>{if(document){let v=document.createElement("style");v.textContent=`
284
+ #${c.id} {
285
+ position: fixed;
286
+ top: 0;
287
+ left: 0;
288
+ width: 100%;
289
+ height: 100%;
290
+ pointer-events: none;
291
+ z-index: -9999;
292
+ }
293
+ `,document.head.appendChild(v),ne(c)}},[]),ae.createElement("canvas",{id:c.id})};export{$e as SmokeyFluidCursor};
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "react-smokey-fluid-cursor",
3
+ "version": "1.0.2",
4
+ "description": "A customizable fluid cursor effect for React & Next.js Apps running on the web. Enhance user experience with smooth, interactive cursor animations. Perfect for modern web designs.",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/faraasat/react-smokey-fluid-cursor.git"
8
+ },
9
+ "main": "./dist/index.js",
10
+ "module": "./dist/index.mjs",
11
+ "types": "./dist/index.d.ts",
12
+ "files": [
13
+ "dist"
14
+ ],
15
+ "scripts": {
16
+ "build": "tsup && rm -rf dist/*.d.mts",
17
+ "release": "standard-version"
18
+ },
19
+ "keywords": [
20
+ "react",
21
+ "nextjs",
22
+ "next.js",
23
+ "smokey cursor",
24
+ "fluid cursor",
25
+ "animated cursor",
26
+ "smokey fluid",
27
+ "javascript",
28
+ "typescript",
29
+ "animated smoke cursor",
30
+ "react cursor",
31
+ "cursor effects",
32
+ "custom cursor",
33
+ "react component",
34
+ "next.js cursor"
35
+ ],
36
+ "author": "Farasat Ali <63093876+faraasat@users.noreply.github.com>",
37
+ "license": "MIT",
38
+ "peerDependencies": {
39
+ "react": ">=17.0.0",
40
+ "react-dom": ">=17.0.0"
41
+ },
42
+ "devDependencies": {
43
+ "@types/node": "^24.0.15",
44
+ "@types/react": "^19.1.8",
45
+ "@types/react-dom": "^19.1.6",
46
+ "standard-version": "^9.5.0",
47
+ "tsup": "^8.5.0",
48
+ "typescript": "^5.0.0"
49
+ },
50
+ "publishConfig": {
51
+ "access": "public"
52
+ }
53
+ }