Neural Glow Cursor Effect
An interactive React component that adds a dynamic bubble effect, visually tracking cursor movement in real time.
An interactive React component that adds a dynamic bubble effect, visually tracking cursor movement in real time.
1import React from 'react'2import NeuralNoise from './neural-glow'34function index() {5return (6<div className='absolute w-full h-full overflow-hidden'>7<NeuralNoise/>8</div>9)10}1112export default index13
12// @ts-nocheck3"use client"4import React, { useEffect, useRef, useState } from 'react';56const NeuralGlow = () => {7const canvasRef = useRef(null);8const animationRef = useRef(null);9const glRef = useRef(null);10const uniformsRef = useRef(null);11const pointerRef = useRef({12x: 0,13y: 0,14tX: 0,15tY: 0,16});1718const vertexShaderSource = `19precision mediump float;2021varying vec2 vUv;22attribute vec2 a_position;2324void main() {25vUv = .5 * (a_position + 1.);26gl_Position = vec4(a_position, 0.0, 1.0);27}28`;2930const fragmentShaderSource = `31precision mediump float;3233varying vec2 vUv;34uniform float u_time;35uniform float u_ratio;36uniform vec2 u_pointer_position;37uniform float u_scroll_progress;3839vec2 rotate(vec2 uv, float th) {40return mat2(cos(th), sin(th), -sin(th), cos(th)) * uv;41}4243float neuro_shape(vec2 uv, float t, float p) {44vec2 sine_acc = vec2(0.);45vec2 res = vec2(0.);46float scale = 8.;4748for (int j = 0; j < 15; j++) {49uv = rotate(uv, 1.);50sine_acc = rotate(sine_acc, 1.);51vec2 layer = uv * scale + float(j) + sine_acc - t;52sine_acc += sin(layer) + 2.4 * p;53res += (.5 + .5 * cos(layer)) / scale;54scale *= (1.2);55}56return res.x + res.y;57}5859void main() {60vec2 uv = .5 * vUv;61uv.x *= u_ratio;6263vec2 pointer = vUv - u_pointer_position;64pointer.x *= u_ratio;65float p = clamp(length(pointer), 0., 1.);66p = .5 * pow(1. - p, 2.);6768float t = .001 * u_time;69vec3 color = vec3(0.);7071float noise = neuro_shape(uv, t, p);7273noise = 1.2 * pow(noise, 3.);74noise += pow(noise, 10.);75noise = max(.0, noise - .5);76noise *= (1. - length(vUv - .5));7778// Blue/indigo color palette79color = vec3(0.1, 0.2, 0.8); // Base blue color80color += vec3(0.0, 0.1, 0.4) * sin(3.0 * u_scroll_progress + 1.5); // Indigo variation8182color = color * noise;8384gl_FragColor = vec4(color, noise);85}86`;8788const createShader = (gl: any, sourceCode: any, type: any) => {89const shader = gl.createShader(type);90gl.shaderSource(shader, sourceCode);91gl.compileShader(shader);9293if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {94console.error("An error occurred compiling the shaders: " + gl.getShaderInfoLog(shader));95gl.deleteShader(shader);96return null;97}9899return shader;100};101102const createShaderProgram = (gl: any, vertexShader: any, fragmentShader: any) => {103const program = gl.createProgram();104gl.attachShader(program, vertexShader);105gl.attachShader(program, fragmentShader);106gl.linkProgram(program);107108if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {109console.error("Unable to initialize the shader program: " + gl.getProgramInfoLog(program));110return null;111}112113return program;114};115116const getUniforms = (gl: any, program: any) => {117let uniforms = [];118let uniformCount = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS);119for (let i = 0; i < uniformCount; i++) {120let uniformName = gl.getActiveUniform(program, i).name;121uniforms[uniformName] = gl.getUniformLocation(program, uniformName);122}123return uniforms;124};125126const initShader = () => {127const canvas = canvasRef.current;128const gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");129130if (!gl) {131alert("WebGL is not supported by your browser.");132return null;133}134135const vertexShader = createShader(gl, vertexShaderSource, gl.VERTEX_SHADER);136const fragmentShader = createShader(gl, fragmentShaderSource, gl.FRAGMENT_SHADER);137138const shaderProgram = createShaderProgram(gl, vertexShader, fragmentShader);139uniformsRef.current = getUniforms(gl, shaderProgram);140141const vertices = new Float32Array([-1., -1., 1., -1., -1., 1., 1., 1.]);142143const vertexBuffer = gl.createBuffer();144gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);145gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);146147gl.useProgram(shaderProgram);148149const positionLocation = gl.getAttribLocation(shaderProgram, "a_position");150gl.enableVertexAttribArray(positionLocation);151152gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);153gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);154155return gl;156};157158const resizeCanvas = () => {159const canvas = canvasRef.current;160const gl = glRef.current;161if (!canvas || !gl) return;162163const devicePixelRatio = Math.min(window.devicePixelRatio, 2);164canvas.width = window.innerWidth * devicePixelRatio;165canvas.height = window.innerHeight * devicePixelRatio;166167gl.uniform1f(uniformsRef.current.u_ratio, canvas.width / canvas.height);168gl.viewport(0, 0, canvas.width, canvas.height);169};170171const render = () => {172const gl = glRef.current;173const uniforms = uniformsRef.current;174const pointer = pointerRef.current;175176if (!gl || !uniforms) return;177178const currentTime = performance.now();179180pointer.x += (pointer.tX - pointer.x) * 0.2;181pointer.y += (pointer.tY - pointer.y) * 0.2;182183gl.uniform1f(uniforms.u_time, currentTime);184gl.uniform2f(uniforms.u_pointer_position, pointer.x / window.innerWidth, 1 - pointer.y / window.innerHeight);185gl.uniform1f(uniforms.u_scroll_progress, window.pageYOffset / (2 * window.innerHeight));186187gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);188animationRef.current = requestAnimationFrame(render);189};190191const updateMousePosition = (x, y) => {192pointerRef.current.tX = x;193pointerRef.current.tY = y;194};195196const handlePointerMove = (e) => {197updateMousePosition(e.clientX, e.clientY);198};199200const handleTouchMove = (e) => {201updateMousePosition(e.touches[0].clientX, e.touches[0].clientY);202};203204const handleClick = (e) => {205updateMousePosition(e.clientX, e.clientY);206};207208useEffect(() => {209glRef.current = initShader();210resizeCanvas();211render();212213const handleResize = () => {214resizeCanvas();215};216217window.addEventListener("resize", handleResize);218window.addEventListener("pointermove", handlePointerMove);219window.addEventListener("touchmove", handleTouchMove);220window.addEventListener("click", handleClick);221222return () => {223if (animationRef.current) {224cancelAnimationFrame(animationRef.current);225}226window.removeEventListener("resize", handleResize);227window.removeEventListener("pointermove", handlePointerMove);228window.removeEventListener("touchmove", handleTouchMove);229window.removeEventListener("click", handleClick);230};231}, []);232233return (234<>235<canvas236ref={canvasRef}237className="absolute top-0 left-0 w-full h-full pointer-events-none opacity-95"238style={{ backgroundColor: '#000000' }}239/>240</>241);242};243244export default NeuralGlow;245