Docs
ShaderLensBlur

ShaderLensBlur

blur go brr

Inverted mouse interaction using mouse or touch

Shader Configuration

Colors

Options

Dimensions

100%px
400pxpx

References

Installation

Copy and paste the following code into your project.

"use client"
 
import React, { useCallback, useEffect, useRef, useState } from "react"
import { motion } from "framer-motion"
import { atom, useAtom } from "jotai"
import { useTheme } from "next-themes"
import * as THREE from "three"
 
const fragmentShader = `
varying vec2 v_texcoord;
 
uniform vec2 u_mouse;
uniform vec2 u_resolution;
uniform float u_pixelRatio;
uniform float u_time;
uniform vec3 u_color1;
uniform vec3 u_color2;
uniform vec3 u_color3;
uniform vec3 u_color4;
uniform float u_hoverStrength;
uniform bool u_invertMouse;
uniform bool u_isDarkMode;
 
#define PI 3.1415926535897932384626433832795
#define TWO_PI 6.2831853071795864769252867665590
 
vec2 coord(in vec2 p) {
    p = p / u_resolution.xy;
    if (u_resolution.x > u_resolution.y) {
        p.x *= u_resolution.x / u_resolution.y;
        p.x += (u_resolution.y - u_resolution.x) / u_resolution.y / 2.0;
    } else {
        p.y *= u_resolution.y / u_resolution.x;
        p.y += (u_resolution.x - u_resolution.y) / u_resolution.x / 2.0;
    }
    p -= 0.5;
    p *= vec2(-1.0, 1.0);
    return p;
}
 
#define st0 coord(gl_FragCoord.xy)
#define mx coord(u_mouse)
 
float sdRoundRect(vec2 p, vec2 b, float r) {
    vec2 d = abs(p - 0.5) * 4.2 - b + vec2(r);
    return min(max(d.x, d.y), 0.0) + length(max(d, 0.0)) - r;
}
 
float sdCircle(in vec2 st, in vec2 center) {
    return length(st - center) * 2.0;
}
 
float sdPoly(in vec2 p, in float w, in int sides) {
    float a = atan(p.x, p.y) + PI;
    float r = TWO_PI / float(sides);
    float d = cos(floor(0.5 + a / r) * r - a) * length(max(abs(p) * 1.0, 0.0));
    return d * 2.0 - w;
}
 
float aastep(float threshold, float value) {
    float afwidth = length(vec2(dFdx(value), dFdy(value))) * 0.70710678118654757;
    return smoothstep(threshold - afwidth, threshold + afwidth, value);
}
 
float fill(float x, float size, float edge) {
    return 1.0 - smoothstep(size - edge, size + edge, x);
}
 
float stroke(float x, float size, float w, float edge) {
    float d = smoothstep(size - edge, size + edge, x + w * 0.5) - smoothstep(size - edge, size + edge, x - w * 0.5);
    return clamp(d, 0.0, 1.0);
}
 
void main() {
    vec2 st = st0 + 0.5;
    vec2 posMouse = mx + 0.5;
    
    float size = 1.2 + sin(u_time) * 0.1;
    float roundness = 0.4 + sin(u_time * 0.5) * 0.1;
    float borderSize = 0.05 + sin(u_time * 0.7) * 0.02;
    float circleSize = 0.3 + sin(u_time * 0.8) * 0.05;
    float circleEdge = 0.5 + sin(u_time * 0.6) * 0.1;
    
    float sdfCircle = fill(
        sdCircle(st, posMouse),
        circleSize,
        circleEdge
    );
    
    float sdf;
    if (VAR == 0) {
        sdf = sdRoundRect(st, vec2(size), roundness);
        sdf = stroke(sdf, 0.0, borderSize, sdfCircle) * 4.0;
    } else if (VAR == 1) {
        sdf = sdCircle(st, vec2(0.5));
        sdf = fill(sdf, 0.6, sdfCircle) * 1.2;
    } else if (VAR == 2) {
        sdf = sdCircle(st, vec2(0.5));
        sdf = stroke(sdf, 0.58, 0.02, sdfCircle) * 4.0;
    } else if (VAR == 3) {
        sdf = sdPoly(st - vec2(0.5, 0.45), 0.3, 3);
        sdf = fill(sdf, 0.05, sdfCircle) * 1.4;
    }
    
    vec3 gradient = mix(
        mix(u_color1, u_color2, 0.5 + 0.5 * cos(u_time + st.x + 0.0)),
        mix(u_color3, u_color4, 0.5 + 0.5 * cos(u_time + st.y + 2.0)),
        0.5 + 0.5 * cos(u_time + st.x + st.y + 4.0)
    );
    
    vec3 shapeColor = sdf * gradient;
    
    float mouseEffect = u_invertMouse ? 1.0 - sdfCircle : sdfCircle;
    shapeColor = mix(shapeColor, vec3(1.0) - shapeColor, u_hoverStrength * mouseEffect);
    
    if (u_isDarkMode) {
        shapeColor = mix(shapeColor, vec3(1.0), 0.2);
    } else {
        shapeColor = mix(shapeColor, vec3(0.0), 0.1);
    }
    
    gl_FragColor = vec4(shapeColor, sdf);
}
`
 
interface ShaderConfig {
  variation: number
  color1: string
  color2: string
  color3: string
  color4: string
  enableHover: boolean
  invertMouse: boolean
  width: string
  height: string
}
 
const initialState: ShaderConfig = {
  variation: 3,
  color1: "#D5F981",
  color2: "#A1BBE7",
  color3: "#F2BAE2",
  color4: "#68E8FA",
  enableHover: true,
  invertMouse: true,
  width: "100%",
  height: "400px",
}
 
export const configAtom = atom<ShaderConfig>(initialState)
 
export function ShaderLensBlur() {
  const [config] = useAtom(configAtom)
  const containerRef = useRef<HTMLDivElement>(null)
  const canvasRef = useRef<HTMLCanvasElement>(null)
  const rendererRef = useRef<THREE.WebGLRenderer | null>(null)
  const sceneRef = useRef<THREE.Scene | null>(null)
  const cameraRef = useRef<THREE.OrthographicCamera | null>(null)
  const materialRef = useRef<THREE.ShaderMaterial | null>(null)
  const rafRef = useRef<number | null>(null)
  const [isInteracting, setIsInteracting] = useState(false)
  const { theme } = useTheme()
 
  const updateSize = useCallback(() => {
    if (
      !containerRef.current ||
      !canvasRef.current ||
      !rendererRef.current ||
      !cameraRef.current ||
      !materialRef.current
    )
      return
 
    const { clientWidth: w, clientHeight: h } = containerRef.current
    const aspect = w / h
 
    cameraRef.current.left = -aspect
    cameraRef.current.right = aspect
    cameraRef.current.top = 1
    cameraRef.current.bottom = -1
    cameraRef.current.updateProjectionMatrix()
 
    rendererRef.current.setSize(w, h)
    materialRef.current.uniforms.u_resolution.value.set(w, h)
  }, [])
 
  const updateMousePosition = useCallback(
    (x: number, y: number) => {
      if (!containerRef.current || !materialRef.current) return
      const rect = containerRef.current.getBoundingClientRect()
      const mouseX = x - rect.left
      const mouseY = y - rect.top
      if (isInteracting || config.enableHover) {
        materialRef.current.uniforms.u_mouse.value.set(
          mouseX,
          rect.height - mouseY
        )
      }
    },
    [isInteracting, config.enableHover]
  )
 
  const animate = useCallback(
    (time: number) => {
      if (
        !rendererRef.current ||
        !sceneRef.current ||
        !cameraRef.current ||
        !materialRef.current
      )
        return
 
      materialRef.current.uniforms.u_time.value = time * 0.001
      materialRef.current.uniforms.u_hoverStrength.value =
        isInteracting || config.enableHover ? 0.3 : 0
 
      rendererRef.current.render(sceneRef.current, cameraRef.current)
      rafRef.current = requestAnimationFrame(animate)
    },
    [config.enableHover, isInteracting]
  )
 
  useEffect(() => {
    if (!containerRef.current || !canvasRef.current) return
 
    const scene = new THREE.Scene()
    sceneRef.current = scene
 
    const camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0.1, 1000)
    camera.position.z = 1
    cameraRef.current = camera
 
    const renderer = new THREE.WebGLRenderer({
      canvas: canvasRef.current,
      antialias: true,
      alpha: true,
    })
    renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
    renderer.setClearColor(0x000000, 0)
    rendererRef.current = renderer
 
    const geometry = new THREE.PlaneGeometry(2, 2)
    const material = new THREE.ShaderMaterial({
      vertexShader: `
        varying vec2 v_texcoord;
        void main() {
          gl_Position = vec4(position, 1.0);
          v_texcoord = uv;
        }
      `,
      fragmentShader,
      uniforms: {
        u_mouse: { value: new THREE.Vector2() },
        u_resolution: { value: new THREE.Vector2() },
        u_pixelRatio: { value: Math.min(window.devicePixelRatio, 2) },
        u_time: { value: 0 },
        u_color1: { value: new THREE.Color(config.color1) },
        u_color2: { value: new THREE.Color(config.color2) },
        u_color3: { value: new THREE.Color(config.color3) },
        u_color4: { value: new THREE.Color(config.color4) },
        u_hoverStrength: { value: 0 },
        u_invertMouse: { value: config.invertMouse },
        u_isDarkMode: { value: theme === "dark" },
      },
      defines: {
        VAR: config.variation,
      },
      transparent: true,
      blending: THREE.NormalBlending,
    })
    materialRef.current = material
 
    const mesh = new THREE.Mesh(geometry, material)
    scene.add(mesh)
 
    updateSize()
    rafRef.current = requestAnimationFrame(animate)
 
    const resizeObserver = new ResizeObserver(updateSize)
    resizeObserver.observe(containerRef.current)
 
    return () => {
      if (rafRef.current) cancelAnimationFrame(rafRef.current)
      if (rendererRef.current) rendererRef.current.dispose()
      if (containerRef.current) resizeObserver.unobserve(containerRef.current)
    }
  }, [config, updateSize, animate, theme])
 
  useEffect(() => {
    const container = containerRef.current
    if (!container) return
 
    const handlePointerMove = (event: PointerEvent) =>
      updateMousePosition(event.clientX, event.clientY)
    const handleTouchMove = (event: TouchEvent) => {
      if (event.touches.length > 0) {
        const touch = event.touches[0]
        updateMousePosition(touch.clientX, touch.clientY)
      }
    }
 
    container.addEventListener("pointermove", handlePointerMove)
    container.addEventListener("touchmove", handleTouchMove)
    container.addEventListener("pointerdown", () => setIsInteracting(true))
    container.addEventListener("pointerup", () => setIsInteracting(false))
    container.addEventListener("touchstart", () => setIsInteracting(true))
    container.addEventListener("touchend", () => setIsInteracting(false))
 
    return () => {
      container.removeEventListener("pointermove", handlePointerMove)
      container.removeEventListener("touchmove", handleTouchMove)
      container.removeEventListener("pointerdown", () => setIsInteracting(true))
      container.removeEventListener("pointerup", () => setIsInteracting(false))
      container.removeEventListener("touchstart", () => setIsInteracting(true))
      container.removeEventListener("touchend", () => setIsInteracting(false))
    }
  }, [updateMousePosition])
 
  useEffect(() => {
    if (materialRef.current) {
      materialRef.current.uniforms.u_color1.value.set(config.color1)
      materialRef.current.uniforms.u_color2.value.set(config.color2)
      materialRef.current.uniforms.u_color3.value.set(config.color3)
      materialRef.current.uniforms.u_color4.value.set(config.color4)
      materialRef.current.uniforms.u_invertMouse.value = config.invertMouse
      materialRef.current.uniforms.u_isDarkMode.value = theme === "dark"
      materialRef.current.defines.VAR = config.variation
      materialRef.current.needsUpdate = true
    }
  }, [config, theme])
 
  return (
    <motion.div
      ref={containerRef}
      initial={{ opacity: 0 }}
      animate={{ opacity: 1 }}
      transition={{ duration: 0.5 }}
      className="relative"
      style={{
        width: config.width,
        height: config.height,
        backgroundColor: "black",
        borderRadius: "8px",
      }}
    >
      <canvas ref={canvasRef} className="w-full h-full touch-none" />
      <div
        className={`absolute bottom-4 left-4 text-sm ${
          theme === "dark" ? "text-neutral-300" : "text-neutral-200"
        }`}
      >
        {config.enableHover
          ? `${
              config.invertMouse ? "Inverted mouse" : "Normal mouse"
            } interaction`
          : "Interact with the animation"}{" "}
        using mouse or touch
      </div>
    </motion.div>
  )
}
 
export ShaderLensBlur
// "use client"
 
// // npm install jotai three
// import React, { useCallback, useEffect, useRef, useState } from "react"
// import { motion } from "framer-motion"
// import { atom, useAtom } from "jotai"
// import * as THREE from "three"
 
// const fragmentShader = `
// varying vec2 v_texcoord;
 
// uniform vec2 u_mouse;
// uniform vec2 u_resolution;
// uniform float u_pixelRatio;
// uniform float u_time;
// uniform vec3 u_color1;
// uniform vec3 u_color2;
// uniform vec3 u_color3;
// uniform vec3 u_color4;
// uniform float u_hoverStrength;
// uniform bool u_invertMouse;
 
// #define PI 3.1415926535897932384626433832795
// #define TWO_PI 6.2831853071795864769252867665590
 
// vec2 coord(in vec2 p) {
//     p = p / u_resolution.xy;
//     if (u_resolution.x > u_resolution.y) {
//         p.x *= u_resolution.x / u_resolution.y;
//         p.x += (u_resolution.y - u_resolution.x) / u_resolution.y / 2.0;
//     } else {
//         p.y *= u_resolution.y / u_resolution.x;
//         p.y += (u_resolution.x - u_resolution.y) / u_resolution.x / 2.0;
//     }
//     p -= 0.5;
//     p *= vec2(-1.0, 1.0);
//     return p;
// }
 
// #define st0 coord(gl_FragCoord.xy)
// #define mx coord(u_mouse)
 
// float sdRoundRect(vec2 p, vec2 b, float r) {
//     vec2 d = abs(p - 0.5) * 4.2 - b + vec2(r);
//     return min(max(d.x, d.y), 0.0) + length(max(d, 0.0)) - r;
// }
 
// float sdCircle(in vec2 st, in vec2 center) {
//     return length(st - center) * 2.0;
// }
 
// float sdPoly(in vec2 p, in float w, in int sides) {
//     float a = atan(p.x, p.y) + PI;
//     float r = TWO_PI / float(sides);
//     float d = cos(floor(0.5 + a / r) * r - a) * length(max(abs(p) * 1.0, 0.0));
//     return d * 2.0 - w;
// }
 
// float aastep(float threshold, float value) {
//     float afwidth = length(vec2(dFdx(value), dFdy(value))) * 0.70710678118654757;
//     return smoothstep(threshold - afwidth, threshold + afwidth, value);
// }
 
// float fill(float x, float size, float edge) {
//     return 1.0 - smoothstep(size - edge, size + edge, x);
// }
 
// float stroke(float x, float size, float w, float edge) {
//     float d = smoothstep(size - edge, size + edge, x + w * 0.5) - smoothstep(size - edge, size + edge, x - w * 0.5);
//     return clamp(d, 0.0, 1.0);
// }
 
// void main() {
//     vec2 st = st0 + 0.5;
//     vec2 posMouse = mx + 0.5;
 
//     float size = 1.2 + sin(u_time) * 0.1;
//     float roundness = 0.4 + sin(u_time * 0.5) * 0.1;
//     float borderSize = 0.05 + sin(u_time * 0.7) * 0.02;
//     float circleSize = 0.3 + sin(u_time * 0.8) * 0.05;
//     float circleEdge = 0.5 + sin(u_time * 0.6) * 0.1;
 
//     float sdfCircle = fill(
//         sdCircle(st, posMouse),
//         circleSize,
//         circleEdge
//     );
 
//     float sdf;
//     if (VAR == 0) {
//         sdf = sdRoundRect(st, vec2(size), roundness);
//         sdf = stroke(sdf, 0.0, borderSize, sdfCircle) * 4.0;
//     } else if (VAR == 1) {
//         sdf = sdCircle(st, vec2(0.5));
//         sdf = fill(sdf, 0.6, sdfCircle) * 1.2;
//     } else if (VAR == 2) {
//         sdf = sdCircle(st, vec2(0.5));
//         sdf = stroke(sdf, 0.58, 0.02, sdfCircle) * 4.0;
//     } else if (VAR == 3) {
//         sdf = sdPoly(st - vec2(0.5, 0.45), 0.3, 3);
//         sdf = fill(sdf, 0.05, sdfCircle) * 1.4;
//     }
 
//     vec3 gradient = mix(
//         mix(u_color1, u_color2, 0.5 + 0.5 * cos(u_time + st.x + 0.0)),
//         mix(u_color3, u_color4, 0.5 + 0.5 * cos(u_time + st.y + 2.0)),
//         0.5 + 0.5 * cos(u_time + st.x + st.y + 4.0)
//     );
 
//     vec3 shapeColor = sdf * gradient;
 
//     float mouseEffect = u_invertMouse ? 1.0 - sdfCircle : sdfCircle;
//     shapeColor = mix(shapeColor, vec3(1.0) - shapeColor, u_hoverStrength * mouseEffect);
 
//     gl_FragColor = vec4(shapeColor, sdf);
// }
// `
 
// interface ShaderConfig {
//   variation: number
//   color1: string
//   color2: string
//   color3: string
//   color4: string
//   enableHover: boolean
//   invertMouse: boolean
//   width: string
//   height: string
// }
 
// const initialState: ShaderConfig = {
//   variation: 0,
//   color1: "#ff0000",
//   color2: "#00ff00",
//   color3: "#0000ff",
//   color4: "#ffff00",
//   enableHover: true,
//   invertMouse: true,
//   width: "100%",
//   height: "400px",
// }
 
// export const configAtom = atom<ShaderConfig>(initialState)
 
// export function ShaderLensBlur() {
//   const [config] = useAtom(configAtom)
//   const containerRef = useRef<HTMLDivElement>(null)
//   const canvasRef = useRef<HTMLCanvasElement>(null)
//   const rendererRef = useRef<THREE.WebGLRenderer | null>(null)
//   const sceneRef = useRef<THREE.Scene | null>(null)
//   const cameraRef = useRef<THREE.OrthographicCamera | null>(null)
//   const materialRef = useRef<THREE.ShaderMaterial | null>(null)
//   const rafRef = useRef<number | null>(null)
//   const [isInteracting, setIsInteracting] = useState(false)
 
//   const updateSize = useCallback(() => {
//     if (
//       !containerRef.current ||
//       !canvasRef.current ||
//       !rendererRef.current ||
//       !cameraRef.current ||
//       !materialRef.current
//     )
//       return
 
//     const { clientWidth: w, clientHeight: h } = containerRef.current
//     const aspect = w / h
 
//     cameraRef.current.left = -aspect
//     cameraRef.current.right = aspect
//     cameraRef.current.top = 1
//     cameraRef.current.bottom = -1
//     cameraRef.current.updateProjectionMatrix()
 
//     rendererRef.current.setSize(w, h)
//     materialRef.current.uniforms.u_resolution.value.set(w, h)
//   }, [])
 
//   const updateMousePosition = useCallback(
//     (x: number, y: number) => {
//       if (!containerRef.current || !materialRef.current) return
//       const rect = containerRef.current.getBoundingClientRect()
//       const mouseX = x - rect.left
//       const mouseY = y - rect.top
//       if (isInteracting || config.enableHover) {
//         materialRef.current.uniforms.u_mouse.value.set(
//           mouseX,
//           rect.height - mouseY
//         )
//       }
//     },
//     [isInteracting, config.enableHover]
//   )
 
//   const animate = useCallback(
//     (time: number) => {
//       if (
//         !rendererRef.current ||
//         !sceneRef.current ||
//         !cameraRef.current ||
//         !materialRef.current
//       )
//         return
 
//       materialRef.current.uniforms.u_time.value = time * 0.001
//       materialRef.current.uniforms.u_hoverStrength.value =
//         isInteracting || config.enableHover ? 0.3 : 0
 
//       rendererRef.current.render(sceneRef.current, cameraRef.current)
//       rafRef.current = requestAnimationFrame(animate)
//     },
//     [config.enableHover, isInteracting]
//   )
 
//   useEffect(() => {
//     if (!containerRef.current || !canvasRef.current) return
 
//     const scene = new THREE.Scene()
//     sceneRef.current = scene
 
//     const camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0.1, 1000)
//     camera.position.z = 1
//     cameraRef.current = camera
 
//     const renderer = new THREE.WebGLRenderer({
//       canvas: canvasRef.current,
//       antialias: true,
//       alpha: true,
//     })
//     renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
//     renderer.setClearColor(0x000000, 0)
//     rendererRef.current = renderer
 
//     const geometry = new THREE.PlaneGeometry(2, 2)
//     const material = new THREE.ShaderMaterial({
//       vertexShader: `
//         varying vec2 v_texcoord;
//         void main() {
//           gl_Position = vec4(position, 1.0);
//           v_texcoord = uv;
//         }
//       `,
//       fragmentShader,
//       uniforms: {
//         u_mouse: { value: new THREE.Vector2() },
//         u_resolution: { value: new THREE.Vector2() },
//         u_pixelRatio: { value: Math.min(window.devicePixelRatio, 2) },
//         u_time: { value: 0 },
//         u_color1: { value: new THREE.Color(config.color1) },
//         u_color2: { value: new THREE.Color(config.color2) },
//         u_color3: { value: new THREE.Color(config.color3) },
//         u_color4: { value: new THREE.Color(config.color4) },
//         u_hoverStrength: { value: 0 },
//         u_invertMouse: { value: config.invertMouse },
//       },
//       defines: {
//         VAR: config.variation,
//       },
//       transparent: true,
//       blending: THREE.NormalBlending,
//     })
//     materialRef.current = material
 
//     const mesh = new THREE.Mesh(geometry, material)
//     scene.add(mesh)
 
//     updateSize()
//     rafRef.current = requestAnimationFrame(animate)
 
//     const resizeObserver = new ResizeObserver(updateSize)
//     resizeObserver.observe(containerRef.current)
 
//     return () => {
//       if (rafRef.current) cancelAnimationFrame(rafRef.current)
//       if (rendererRef.current) rendererRef.current.dispose()
//       if (containerRef.current) resizeObserver.unobserve(containerRef.current)
//     }
//   }, [config, updateSize, animate])
 
//   useEffect(() => {
//     const container = containerRef.current
//     if (!container) return
 
//     const handlePointerMove = (event: PointerEvent) =>
//       updateMousePosition(event.clientX, event.clientY)
//     const handleTouchMove = (event: TouchEvent) => {
//       if (event.touches.length > 0) {
//         const touch = event.touches[0]
//         updateMousePosition(touch.clientX, touch.clientY)
//       }
//     }
 
//     container.addEventListener("pointermove", handlePointerMove)
//     container.addEventListener("touchmove", handleTouchMove)
//     container.addEventListener("pointerdown", () => setIsInteracting(true))
//     container.addEventListener("pointerup", () => setIsInteracting(false))
//     container.addEventListener("touchstart", () => setIsInteracting(true))
//     container.addEventListener("touchend", () => setIsInteracting(false))
 
//     return () => {
//       container.removeEventListener("pointermove", handlePointerMove)
//       container.removeEventListener("touchmove", handleTouchMove)
//       container.removeEventListener("pointerdown", () => setIsInteracting(true))
//       container.removeEventListener("pointerup", () => setIsInteracting(false))
//       container.removeEventListener("touchstart", () => setIsInteracting(true))
//       container.removeEventListener("touchend", () => setIsInteracting(false))
//     }
//   }, [updateMousePosition])
 
//   useEffect(() => {
//     if (materialRef.current) {
//       materialRef.current.uniforms.u_color1.value.set(config.color1)
//       materialRef.current.uniforms.u_color2.value.set(config.color2)
//       materialRef.current.uniforms.u_color3.value.set(config.color3)
//       materialRef.current.uniforms.u_color4.value.set(config.color4)
//       materialRef.current.uniforms.u_invertMouse.value = config.invertMouse
//       materialRef.current.defines.VAR = config.variation
//       materialRef.current.needsUpdate = true
//     }
//   }, [config])
 
//   return (
//     <motion.div
//       ref={containerRef}
//       initial={{ opacity: 0 }}
//       animate={{ opacity: 1 }}
//       transition={{ duration: 0.5 }}
//       className="relative"
//       style={{
//         width: config.width,
//         height: config.height,
//         backgroundColor: "transparent",
//         borderRadius: "8px",
//       }}
//     >
//       <canvas ref={canvasRef} className="w-full h-full touch-none" />
//       <div className="absolute bottom-4 left-4 text-white text-sm">
//         {config.enableHover
//           ? `${
//               config.invertMouse ? "Inverted mouse" : "Normal mouse"
//             } interaction`
//           : "Interact with the animation"}{" "}
//         using mouse or touch
//       </div>
//     </motion.div>
//   )
// }
 
// export ShaderLensBlur

Update the import paths to match your project setup.