🚀Ship faster with UI-Layouts Pro

Neural Glow Cursor Effect

An interactive React component that adds a dynamic bubble effect, visually tracking cursor movement in real time.

1
import React from 'react'
2
import NeuralNoise from './neural-glow'
3
4
function index() {
5
return (
6
<div className='absolute w-full h-full overflow-hidden'>
7
<NeuralNoise/>
8
</div>
9
)
10
}
11
12
export default index
13
neural-glow.tsx
1
2
// @ts-nocheck
3
"use client"
4
import React, { useEffect, useRef, useState } from 'react';
5
6
const NeuralGlow = () => {
7
const canvasRef = useRef(null);
8
const animationRef = useRef(null);
9
const glRef = useRef(null);
10
const uniformsRef = useRef(null);
11
const pointerRef = useRef({
12
x: 0,
13
y: 0,
14
tX: 0,
15
tY: 0,
16
});
17
18
const vertexShaderSource = `
19
precision mediump float;
20
21
varying vec2 vUv;
22
attribute vec2 a_position;
23
24
void main() {
25
vUv = .5 * (a_position + 1.);
26
gl_Position = vec4(a_position, 0.0, 1.0);
27
}
28
`;
29
30
const fragmentShaderSource = `
31
precision mediump float;
32
33
varying vec2 vUv;
34
uniform float u_time;
35
uniform float u_ratio;
36
uniform vec2 u_pointer_position;
37
uniform float u_scroll_progress;
38
39
vec2 rotate(vec2 uv, float th) {
40
return mat2(cos(th), sin(th), -sin(th), cos(th)) * uv;
41
}
42
43
float neuro_shape(vec2 uv, float t, float p) {
44
vec2 sine_acc = vec2(0.);
45
vec2 res = vec2(0.);
46
float scale = 8.;
47
48
for (int j = 0; j < 15; j++) {
49
uv = rotate(uv, 1.);
50
sine_acc = rotate(sine_acc, 1.);
51
vec2 layer = uv * scale + float(j) + sine_acc - t;
52
sine_acc += sin(layer) + 2.4 * p;
53
res += (.5 + .5 * cos(layer)) / scale;
54
scale *= (1.2);
55
}
56
return res.x + res.y;
57
}
58
59
void main() {
60
vec2 uv = .5 * vUv;
61
uv.x *= u_ratio;
62
63
vec2 pointer = vUv - u_pointer_position;
64
pointer.x *= u_ratio;
65
float p = clamp(length(pointer), 0., 1.);
66
p = .5 * pow(1. - p, 2.);
67
68
float t = .001 * u_time;
69
vec3 color = vec3(0.);
70
71
float noise = neuro_shape(uv, t, p);
72
73
noise = 1.2 * pow(noise, 3.);
74
noise += pow(noise, 10.);
75
noise = max(.0, noise - .5);
76
noise *= (1. - length(vUv - .5));
77
78
// Blue/indigo color palette
79
color = vec3(0.1, 0.2, 0.8); // Base blue color
80
color += vec3(0.0, 0.1, 0.4) * sin(3.0 * u_scroll_progress + 1.5); // Indigo variation
81
82
color = color * noise;
83
84
gl_FragColor = vec4(color, noise);
85
}
86
`;
87
88
const createShader = (gl: any, sourceCode: any, type: any) => {
89
const shader = gl.createShader(type);
90
gl.shaderSource(shader, sourceCode);
91
gl.compileShader(shader);
92
93
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
94
console.error("An error occurred compiling the shaders: " + gl.getShaderInfoLog(shader));
95
gl.deleteShader(shader);
96
return null;
97
}
98
99
return shader;
100
};
101
102
const createShaderProgram = (gl: any, vertexShader: any, fragmentShader: any) => {
103
const program = gl.createProgram();
104
gl.attachShader(program, vertexShader);
105
gl.attachShader(program, fragmentShader);
106
gl.linkProgram(program);
107
108
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
109
console.error("Unable to initialize the shader program: " + gl.getProgramInfoLog(program));
110
return null;
111
}
112
113
return program;
114
};
115
116
const getUniforms = (gl: any, program: any) => {
117
let uniforms = [];
118
let uniformCount = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS);
119
for (let i = 0; i < uniformCount; i++) {
120
let uniformName = gl.getActiveUniform(program, i).name;
121
uniforms[uniformName] = gl.getUniformLocation(program, uniformName);
122
}
123
return uniforms;
124
};
125
126
const initShader = () => {
127
const canvas = canvasRef.current;
128
const gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
129
130
if (!gl) {
131
alert("WebGL is not supported by your browser.");
132
return null;
133
}
134
135
const vertexShader = createShader(gl, vertexShaderSource, gl.VERTEX_SHADER);
136
const fragmentShader = createShader(gl, fragmentShaderSource, gl.FRAGMENT_SHADER);
137
138
const shaderProgram = createShaderProgram(gl, vertexShader, fragmentShader);
139
uniformsRef.current = getUniforms(gl, shaderProgram);
140
141
const vertices = new Float32Array([-1., -1., 1., -1., -1., 1., 1., 1.]);
142
143
const vertexBuffer = gl.createBuffer();
144
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
145
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
146
147
gl.useProgram(shaderProgram);
148
149
const positionLocation = gl.getAttribLocation(shaderProgram, "a_position");
150
gl.enableVertexAttribArray(positionLocation);
151
152
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
153
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
154
155
return gl;
156
};
157
158
const resizeCanvas = () => {
159
const canvas = canvasRef.current;
160
const gl = glRef.current;
161
if (!canvas || !gl) return;
162
163
const devicePixelRatio = Math.min(window.devicePixelRatio, 2);
164
canvas.width = window.innerWidth * devicePixelRatio;
165
canvas.height = window.innerHeight * devicePixelRatio;
166
167
gl.uniform1f(uniformsRef.current.u_ratio, canvas.width / canvas.height);
168
gl.viewport(0, 0, canvas.width, canvas.height);
169
};
170
171
const render = () => {
172
const gl = glRef.current;
173
const uniforms = uniformsRef.current;
174
const pointer = pointerRef.current;
175
176
if (!gl || !uniforms) return;
177
178
const currentTime = performance.now();
179
180
pointer.x += (pointer.tX - pointer.x) * 0.2;
181
pointer.y += (pointer.tY - pointer.y) * 0.2;
182
183
gl.uniform1f(uniforms.u_time, currentTime);
184
gl.uniform2f(uniforms.u_pointer_position, pointer.x / window.innerWidth, 1 - pointer.y / window.innerHeight);
185
gl.uniform1f(uniforms.u_scroll_progress, window.pageYOffset / (2 * window.innerHeight));
186
187
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
188
animationRef.current = requestAnimationFrame(render);
189
};
190
191
const updateMousePosition = (x, y) => {
192
pointerRef.current.tX = x;
193
pointerRef.current.tY = y;
194
};
195
196
const handlePointerMove = (e) => {
197
updateMousePosition(e.clientX, e.clientY);
198
};
199
200
const handleTouchMove = (e) => {
201
updateMousePosition(e.touches[0].clientX, e.touches[0].clientY);
202
};
203
204
const handleClick = (e) => {
205
updateMousePosition(e.clientX, e.clientY);
206
};
207
208
useEffect(() => {
209
glRef.current = initShader();
210
resizeCanvas();
211
render();
212
213
const handleResize = () => {
214
resizeCanvas();
215
};
216
217
window.addEventListener("resize", handleResize);
218
window.addEventListener("pointermove", handlePointerMove);
219
window.addEventListener("touchmove", handleTouchMove);
220
window.addEventListener("click", handleClick);
221
222
return () => {
223
if (animationRef.current) {
224
cancelAnimationFrame(animationRef.current);
225
}
226
window.removeEventListener("resize", handleResize);
227
window.removeEventListener("pointermove", handlePointerMove);
228
window.removeEventListener("touchmove", handleTouchMove);
229
window.removeEventListener("click", handleClick);
230
};
231
}, []);
232
233
return (
234
<>
235
<canvas
236
ref={canvasRef}
237
className="absolute top-0 left-0 w-full h-full pointer-events-none opacity-95"
238
style={{ backgroundColor: '#000000' }}
239
/>
240
</>
241
);
242
};
243
244
export default NeuralGlow;
245