{
	"$schema": "https://ui.shadcn.com/schema/registry-item.json",
	"name": "SplashCursor-JS-TW",
	"title": "SplashCursor",
	"description": "Liquid splash burst at cursor with curling ripples and waves.",
	"type": "registry:component",
	"files": [
		{
			"type": "registry:component",
			"path": "SplashCursor/SplashCursor.jsx",
			"content": "'use client';\nimport { useEffect, useRef } from 'react';\n\nfunction SplashCursor({\n  SIM_RESOLUTION = 128,\n  DYE_RESOLUTION = 1440,\n  CAPTURE_RESOLUTION = 512,\n  DENSITY_DISSIPATION = 3.5,\n  VELOCITY_DISSIPATION = 2,\n  PRESSURE = 0.1,\n  PRESSURE_ITERATIONS = 20,\n  CURL = 3,\n  SPLAT_RADIUS = 0.2,\n  SPLAT_FORCE = 6000,\n  SHADING = true,\n  COLOR_UPDATE_SPEED = 10,\n  BACK_COLOR = { r: 0.5, g: 0, b: 0 },\n  TRANSPARENT = true,\n  RAINBOW_MODE = true,\n  COLOR = '#ff0000'\n}) {\n  const canvasRef = useRef(null);\n\n  useEffect(() => {\n    const canvas = canvasRef.current;\n    if (!canvas) return;\n\n    function pointerPrototype() {\n      this.id = -1;\n      this.texcoordX = 0;\n      this.texcoordY = 0;\n      this.prevTexcoordX = 0;\n      this.prevTexcoordY = 0;\n      this.deltaX = 0;\n      this.deltaY = 0;\n      this.down = false;\n      this.moved = false;\n      this.color = [0, 0, 0];\n    }\n\n    let config = {\n      SIM_RESOLUTION,\n      DYE_RESOLUTION,\n      CAPTURE_RESOLUTION,\n      DENSITY_DISSIPATION,\n      VELOCITY_DISSIPATION,\n      PRESSURE,\n      PRESSURE_ITERATIONS,\n      CURL,\n      SPLAT_RADIUS,\n      SPLAT_FORCE,\n      SHADING,\n      COLOR_UPDATE_SPEED,\n      PAUSED: false,\n      BACK_COLOR,\n      TRANSPARENT,\n      RAINBOW_MODE,\n      COLOR\n    };\n\n    let pointers = [new pointerPrototype()];\n\n    const { gl, ext } = getWebGLContext(canvas);\n    if (!ext.supportLinearFiltering) {\n      config.DYE_RESOLUTION = 256;\n      config.SHADING = false;\n    }\n\n    function getWebGLContext(canvas) {\n      const params = {\n        alpha: true,\n        depth: false,\n        stencil: false,\n        antialias: false,\n        preserveDrawingBuffer: false\n      };\n      let gl = canvas.getContext('webgl2', params);\n      const isWebGL2 = !!gl;\n      if (!isWebGL2) gl = canvas.getContext('webgl', params) || canvas.getContext('experimental-webgl', params);\n      let halfFloat;\n      let supportLinearFiltering;\n      if (isWebGL2) {\n        gl.getExtension('EXT_color_buffer_float');\n        supportLinearFiltering = gl.getExtension('OES_texture_float_linear');\n      } else {\n        halfFloat = gl.getExtension('OES_texture_half_float');\n        supportLinearFiltering = gl.getExtension('OES_texture_half_float_linear');\n      }\n      gl.clearColor(0.0, 0.0, 0.0, 1.0);\n      const halfFloatTexType = isWebGL2 ? gl.HALF_FLOAT : halfFloat && halfFloat.HALF_FLOAT_OES;\n      let formatRGBA;\n      let formatRG;\n      let formatR;\n\n      if (isWebGL2) {\n        formatRGBA = getSupportedFormat(gl, gl.RGBA16F, gl.RGBA, halfFloatTexType);\n        formatRG = getSupportedFormat(gl, gl.RG16F, gl.RG, halfFloatTexType);\n        formatR = getSupportedFormat(gl, gl.R16F, gl.RED, halfFloatTexType);\n      } else {\n        formatRGBA = getSupportedFormat(gl, gl.RGBA, gl.RGBA, halfFloatTexType);\n        formatRG = getSupportedFormat(gl, gl.RGBA, gl.RGBA, halfFloatTexType);\n        formatR = getSupportedFormat(gl, gl.RGBA, gl.RGBA, halfFloatTexType);\n      }\n\n      return {\n        gl,\n        ext: {\n          formatRGBA,\n          formatRG,\n          formatR,\n          halfFloatTexType,\n          supportLinearFiltering\n        }\n      };\n    }\n\n    function getSupportedFormat(gl, internalFormat, format, type) {\n      if (!supportRenderTextureFormat(gl, internalFormat, format, type)) {\n        switch (internalFormat) {\n          case gl.R16F:\n            return getSupportedFormat(gl, gl.RG16F, gl.RG, type);\n          case gl.RG16F:\n            return getSupportedFormat(gl, gl.RGBA16F, gl.RGBA, type);\n          default:\n            return null;\n        }\n      }\n      return {\n        internalFormat,\n        format\n      };\n    }\n\n    function supportRenderTextureFormat(gl, internalFormat, format, type) {\n      const texture = gl.createTexture();\n      gl.bindTexture(gl.TEXTURE_2D, texture);\n      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);\n      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);\n      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);\n      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);\n      gl.texImage2D(gl.TEXTURE_2D, 0, internalFormat, 4, 4, 0, format, type, null);\n      const fbo = gl.createFramebuffer();\n      gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);\n      gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);\n      const status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);\n      return status === gl.FRAMEBUFFER_COMPLETE;\n    }\n\n    class Material {\n      constructor(vertexShader, fragmentShaderSource) {\n        this.vertexShader = vertexShader;\n        this.fragmentShaderSource = fragmentShaderSource;\n        this.programs = [];\n        this.activeProgram = null;\n        this.uniforms = [];\n      }\n      setKeywords(keywords) {\n        let hash = 0;\n        for (let i = 0; i < keywords.length; i++) hash += hashCode(keywords[i]);\n        let program = this.programs[hash];\n        if (program == null) {\n          let fragmentShader = compileShader(gl.FRAGMENT_SHADER, this.fragmentShaderSource, keywords);\n          program = createProgram(this.vertexShader, fragmentShader);\n          this.programs[hash] = program;\n        }\n        if (program === this.activeProgram) return;\n        this.uniforms = getUniforms(program);\n        this.activeProgram = program;\n      }\n      bind() {\n        gl.useProgram(this.activeProgram);\n      }\n    }\n\n    class Program {\n      constructor(vertexShader, fragmentShader) {\n        this.uniforms = {};\n        this.program = createProgram(vertexShader, fragmentShader);\n        this.uniforms = getUniforms(this.program);\n      }\n      bind() {\n        gl.useProgram(this.program);\n      }\n    }\n\n    function createProgram(vertexShader, fragmentShader) {\n      let program = gl.createProgram();\n      gl.attachShader(program, vertexShader);\n      gl.attachShader(program, fragmentShader);\n      gl.linkProgram(program);\n      if (!gl.getProgramParameter(program, gl.LINK_STATUS)) console.trace(gl.getProgramInfoLog(program));\n      return program;\n    }\n\n    function getUniforms(program) {\n      let uniforms = [];\n      let uniformCount = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS);\n      for (let i = 0; i < uniformCount; i++) {\n        let uniformName = gl.getActiveUniform(program, i).name;\n        uniforms[uniformName] = gl.getUniformLocation(program, uniformName);\n      }\n      return uniforms;\n    }\n\n    function compileShader(type, source, keywords) {\n      source = addKeywords(source, keywords);\n      const shader = gl.createShader(type);\n      gl.shaderSource(shader, source);\n      gl.compileShader(shader);\n      if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) console.trace(gl.getShaderInfoLog(shader));\n      return shader;\n    }\n\n    function addKeywords(source, keywords) {\n      if (!keywords) return source;\n      let keywordsString = '';\n      keywords.forEach(keyword => {\n        keywordsString += '#define ' + keyword + '\\n';\n      });\n      return keywordsString + source;\n    }\n\n    const baseVertexShader = compileShader(\n      gl.VERTEX_SHADER,\n      `\n        precision highp float;\n        attribute vec2 aPosition;\n        varying vec2 vUv;\n        varying vec2 vL;\n        varying vec2 vR;\n        varying vec2 vT;\n        varying vec2 vB;\n        uniform vec2 texelSize;\n\n        void main () {\n            vUv = aPosition * 0.5 + 0.5;\n            vL = vUv - vec2(texelSize.x, 0.0);\n            vR = vUv + vec2(texelSize.x, 0.0);\n            vT = vUv + vec2(0.0, texelSize.y);\n            vB = vUv - vec2(0.0, texelSize.y);\n            gl_Position = vec4(aPosition, 0.0, 1.0);\n        }\n      `\n    );\n\n    const copyShader = compileShader(\n      gl.FRAGMENT_SHADER,\n      `\n        precision mediump float;\n        precision mediump sampler2D;\n        varying highp vec2 vUv;\n        uniform sampler2D uTexture;\n\n        void main () {\n            gl_FragColor = texture2D(uTexture, vUv);\n        }\n      `\n    );\n\n    const clearShader = compileShader(\n      gl.FRAGMENT_SHADER,\n      `\n        precision mediump float;\n        precision mediump sampler2D;\n        varying highp vec2 vUv;\n        uniform sampler2D uTexture;\n        uniform float value;\n\n        void main () {\n            gl_FragColor = value * texture2D(uTexture, vUv);\n        }\n     `\n    );\n\n    const displayShaderSource = `\n      precision highp float;\n      precision highp sampler2D;\n      varying vec2 vUv;\n      varying vec2 vL;\n      varying vec2 vR;\n      varying vec2 vT;\n      varying vec2 vB;\n      uniform sampler2D uTexture;\n      uniform sampler2D uDithering;\n      uniform vec2 ditherScale;\n      uniform vec2 texelSize;\n\n      vec3 linearToGamma (vec3 color) {\n          color = max(color, vec3(0));\n          return max(1.055 * pow(color, vec3(0.416666667)) - 0.055, vec3(0));\n      }\n\n      void main () {\n          vec3 c = texture2D(uTexture, vUv).rgb;\n          #ifdef SHADING\n              vec3 lc = texture2D(uTexture, vL).rgb;\n              vec3 rc = texture2D(uTexture, vR).rgb;\n              vec3 tc = texture2D(uTexture, vT).rgb;\n              vec3 bc = texture2D(uTexture, vB).rgb;\n\n              float dx = length(rc) - length(lc);\n              float dy = length(tc) - length(bc);\n\n              vec3 n = normalize(vec3(dx, dy, length(texelSize)));\n              vec3 l = vec3(0.0, 0.0, 1.0);\n\n              float diffuse = clamp(dot(n, l) + 0.7, 0.7, 1.0);\n              c *= diffuse;\n          #endif\n\n          float a = max(c.r, max(c.g, c.b));\n          gl_FragColor = vec4(c, a);\n      }\n    `;\n\n    const splatShader = compileShader(\n      gl.FRAGMENT_SHADER,\n      `\n        precision highp float;\n        precision highp sampler2D;\n        varying vec2 vUv;\n        uniform sampler2D uTarget;\n        uniform float aspectRatio;\n        uniform vec3 color;\n        uniform vec2 point;\n        uniform float radius;\n\n        void main () {\n            vec2 p = vUv - point.xy;\n            p.x *= aspectRatio;\n            vec3 splat = exp(-dot(p, p) / radius) * color;\n            vec3 base = texture2D(uTarget, vUv).xyz;\n            gl_FragColor = vec4(base + splat, 1.0);\n        }\n      `\n    );\n\n    const advectionShader = compileShader(\n      gl.FRAGMENT_SHADER,\n      `\n        precision highp float;\n        precision highp sampler2D;\n        varying vec2 vUv;\n        uniform sampler2D uVelocity;\n        uniform sampler2D uSource;\n        uniform vec2 texelSize;\n        uniform vec2 dyeTexelSize;\n        uniform float dt;\n        uniform float dissipation;\n\n        vec4 bilerp (sampler2D sam, vec2 uv, vec2 tsize) {\n            vec2 st = uv / tsize - 0.5;\n            vec2 iuv = floor(st);\n            vec2 fuv = fract(st);\n\n            vec4 a = texture2D(sam, (iuv + vec2(0.5, 0.5)) * tsize);\n            vec4 b = texture2D(sam, (iuv + vec2(1.5, 0.5)) * tsize);\n            vec4 c = texture2D(sam, (iuv + vec2(0.5, 1.5)) * tsize);\n            vec4 d = texture2D(sam, (iuv + vec2(1.5, 1.5)) * tsize);\n\n            return mix(mix(a, b, fuv.x), mix(c, d, fuv.x), fuv.y);\n        }\n\n        void main () {\n            #ifdef MANUAL_FILTERING\n                vec2 coord = vUv - dt * bilerp(uVelocity, vUv, texelSize).xy * texelSize;\n                vec4 result = bilerp(uSource, coord, dyeTexelSize);\n            #else\n                vec2 coord = vUv - dt * texture2D(uVelocity, vUv).xy * texelSize;\n                vec4 result = texture2D(uSource, coord);\n            #endif\n            float decay = 1.0 + dissipation * dt;\n            gl_FragColor = result / decay;\n        }\n      `,\n      ext.supportLinearFiltering ? null : ['MANUAL_FILTERING']\n    );\n\n    const divergenceShader = compileShader(\n      gl.FRAGMENT_SHADER,\n      `\n        precision mediump float;\n        precision mediump sampler2D;\n        varying highp vec2 vUv;\n        varying highp vec2 vL;\n        varying highp vec2 vR;\n        varying highp vec2 vT;\n        varying highp vec2 vB;\n        uniform sampler2D uVelocity;\n\n        void main () {\n            float L = texture2D(uVelocity, vL).x;\n            float R = texture2D(uVelocity, vR).x;\n            float T = texture2D(uVelocity, vT).y;\n            float B = texture2D(uVelocity, vB).y;\n\n            vec2 C = texture2D(uVelocity, vUv).xy;\n            if (vL.x < 0.0) { L = -C.x; }\n            if (vR.x > 1.0) { R = -C.x; }\n            if (vT.y > 1.0) { T = -C.y; }\n            if (vB.y < 0.0) { B = -C.y; }\n\n            float div = 0.5 * (R - L + T - B);\n            gl_FragColor = vec4(div, 0.0, 0.0, 1.0);\n        }\n      `\n    );\n\n    const curlShader = compileShader(\n      gl.FRAGMENT_SHADER,\n      `\n        precision mediump float;\n        precision mediump sampler2D;\n        varying highp vec2 vUv;\n        varying highp vec2 vL;\n        varying highp vec2 vR;\n        varying highp vec2 vT;\n        varying highp vec2 vB;\n        uniform sampler2D uVelocity;\n\n        void main () {\n            float L = texture2D(uVelocity, vL).y;\n            float R = texture2D(uVelocity, vR).y;\n            float T = texture2D(uVelocity, vT).x;\n            float B = texture2D(uVelocity, vB).x;\n            float vorticity = R - L - T + B;\n            gl_FragColor = vec4(0.5 * vorticity, 0.0, 0.0, 1.0);\n        }\n      `\n    );\n\n    const vorticityShader = compileShader(\n      gl.FRAGMENT_SHADER,\n      `\n        precision highp float;\n        precision highp sampler2D;\n        varying vec2 vUv;\n        varying vec2 vL;\n        varying vec2 vR;\n        varying vec2 vT;\n        varying vec2 vB;\n        uniform sampler2D uVelocity;\n        uniform sampler2D uCurl;\n        uniform float curl;\n        uniform float dt;\n\n        void main () {\n            float L = texture2D(uCurl, vL).x;\n            float R = texture2D(uCurl, vR).x;\n            float T = texture2D(uCurl, vT).x;\n            float B = texture2D(uCurl, vB).x;\n            float C = texture2D(uCurl, vUv).x;\n\n            vec2 force = 0.5 * vec2(abs(T) - abs(B), abs(R) - abs(L));\n            force /= length(force) + 0.0001;\n            force *= curl * C;\n            force.y *= -1.0;\n\n            vec2 velocity = texture2D(uVelocity, vUv).xy;\n            velocity += force * dt;\n            velocity = min(max(velocity, -1000.0), 1000.0);\n            gl_FragColor = vec4(velocity, 0.0, 1.0);\n        }\n      `\n    );\n\n    const pressureShader = compileShader(\n      gl.FRAGMENT_SHADER,\n      `\n        precision mediump float;\n        precision mediump sampler2D;\n        varying highp vec2 vUv;\n        varying highp vec2 vL;\n        varying highp vec2 vR;\n        varying highp vec2 vT;\n        varying highp vec2 vB;\n        uniform sampler2D uPressure;\n        uniform sampler2D uDivergence;\n\n        void main () {\n            float L = texture2D(uPressure, vL).x;\n            float R = texture2D(uPressure, vR).x;\n            float T = texture2D(uPressure, vT).x;\n            float B = texture2D(uPressure, vB).x;\n            float C = texture2D(uPressure, vUv).x;\n            float divergence = texture2D(uDivergence, vUv).x;\n            float pressure = (L + R + B + T - divergence) * 0.25;\n            gl_FragColor = vec4(pressure, 0.0, 0.0, 1.0);\n        }\n      `\n    );\n\n    const gradientSubtractShader = compileShader(\n      gl.FRAGMENT_SHADER,\n      `\n        precision mediump float;\n        precision mediump sampler2D;\n        varying highp vec2 vUv;\n        varying highp vec2 vL;\n        varying highp vec2 vR;\n        varying highp vec2 vT;\n        varying highp vec2 vB;\n        uniform sampler2D uPressure;\n        uniform sampler2D uVelocity;\n\n        void main () {\n            float L = texture2D(uPressure, vL).x;\n            float R = texture2D(uPressure, vR).x;\n            float T = texture2D(uPressure, vT).x;\n            float B = texture2D(uPressure, vB).x;\n            vec2 velocity = texture2D(uVelocity, vUv).xy;\n            velocity.xy -= vec2(R - L, T - B);\n            gl_FragColor = vec4(velocity, 0.0, 1.0);\n        }\n      `\n    );\n\n    const blit = (() => {\n      gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer());\n      gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, -1, -1, 1, 1, 1, 1, -1]), gl.STATIC_DRAW);\n      gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, gl.createBuffer());\n      gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array([0, 1, 2, 0, 2, 3]), gl.STATIC_DRAW);\n      gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);\n      gl.enableVertexAttribArray(0);\n      return (target, clear = false) => {\n        if (target == null) {\n          gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);\n          gl.bindFramebuffer(gl.FRAMEBUFFER, null);\n        } else {\n          gl.viewport(0, 0, target.width, target.height);\n          gl.bindFramebuffer(gl.FRAMEBUFFER, target.fbo);\n        }\n        if (clear) {\n          gl.clearColor(0.0, 0.0, 0.0, 1.0);\n          gl.clear(gl.COLOR_BUFFER_BIT);\n        }\n        gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0);\n      };\n    })();\n\n    let dye, velocity, divergence, curl, pressure;\n\n    const copyProgram = new Program(baseVertexShader, copyShader);\n    const clearProgram = new Program(baseVertexShader, clearShader);\n    const splatProgram = new Program(baseVertexShader, splatShader);\n    const advectionProgram = new Program(baseVertexShader, advectionShader);\n    const divergenceProgram = new Program(baseVertexShader, divergenceShader);\n    const curlProgram = new Program(baseVertexShader, curlShader);\n    const vorticityProgram = new Program(baseVertexShader, vorticityShader);\n    const pressureProgram = new Program(baseVertexShader, pressureShader);\n    const gradienSubtractProgram = new Program(baseVertexShader, gradientSubtractShader);\n    const displayMaterial = new Material(baseVertexShader, displayShaderSource);\n\n    function initFramebuffers() {\n      let simRes = getResolution(config.SIM_RESOLUTION);\n      let dyeRes = getResolution(config.DYE_RESOLUTION);\n      const texType = ext.halfFloatTexType;\n      const rgba = ext.formatRGBA;\n      const rg = ext.formatRG;\n      const r = ext.formatR;\n      const filtering = ext.supportLinearFiltering ? gl.LINEAR : gl.NEAREST;\n      gl.disable(gl.BLEND);\n\n      if (!dye)\n        dye = createDoubleFBO(dyeRes.width, dyeRes.height, rgba.internalFormat, rgba.format, texType, filtering);\n      else\n        dye = resizeDoubleFBO(dye, dyeRes.width, dyeRes.height, rgba.internalFormat, rgba.format, texType, filtering);\n\n      if (!velocity)\n        velocity = createDoubleFBO(simRes.width, simRes.height, rg.internalFormat, rg.format, texType, filtering);\n      else\n        velocity = resizeDoubleFBO(\n          velocity,\n          simRes.width,\n          simRes.height,\n          rg.internalFormat,\n          rg.format,\n          texType,\n          filtering\n        );\n\n      divergence = createFBO(simRes.width, simRes.height, r.internalFormat, r.format, texType, gl.NEAREST);\n      curl = createFBO(simRes.width, simRes.height, r.internalFormat, r.format, texType, gl.NEAREST);\n      pressure = createDoubleFBO(simRes.width, simRes.height, r.internalFormat, r.format, texType, gl.NEAREST);\n    }\n\n    function createFBO(w, h, internalFormat, format, type, param) {\n      gl.activeTexture(gl.TEXTURE0);\n      let texture = gl.createTexture();\n      gl.bindTexture(gl.TEXTURE_2D, texture);\n      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, param);\n      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, param);\n      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);\n      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);\n      gl.texImage2D(gl.TEXTURE_2D, 0, internalFormat, w, h, 0, format, type, null);\n\n      let fbo = gl.createFramebuffer();\n      gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);\n      gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);\n      gl.viewport(0, 0, w, h);\n      gl.clear(gl.COLOR_BUFFER_BIT);\n\n      let texelSizeX = 1.0 / w;\n      let texelSizeY = 1.0 / h;\n      return {\n        texture,\n        fbo,\n        width: w,\n        height: h,\n        texelSizeX,\n        texelSizeY,\n        attach(id) {\n          gl.activeTexture(gl.TEXTURE0 + id);\n          gl.bindTexture(gl.TEXTURE_2D, texture);\n          return id;\n        }\n      };\n    }\n\n    function createDoubleFBO(w, h, internalFormat, format, type, param) {\n      let fbo1 = createFBO(w, h, internalFormat, format, type, param);\n      let fbo2 = createFBO(w, h, internalFormat, format, type, param);\n      return {\n        width: w,\n        height: h,\n        texelSizeX: fbo1.texelSizeX,\n        texelSizeY: fbo1.texelSizeY,\n        get read() {\n          return fbo1;\n        },\n        set read(value) {\n          fbo1 = value;\n        },\n        get write() {\n          return fbo2;\n        },\n        set write(value) {\n          fbo2 = value;\n        },\n        swap() {\n          let temp = fbo1;\n          fbo1 = fbo2;\n          fbo2 = temp;\n        }\n      };\n    }\n\n    function resizeFBO(target, w, h, internalFormat, format, type, param) {\n      let newFBO = createFBO(w, h, internalFormat, format, type, param);\n      copyProgram.bind();\n      gl.uniform1i(copyProgram.uniforms.uTexture, target.attach(0));\n      blit(newFBO);\n      return newFBO;\n    }\n\n    function resizeDoubleFBO(target, w, h, internalFormat, format, type, param) {\n      if (target.width === w && target.height === h) return target;\n      target.read = resizeFBO(target.read, w, h, internalFormat, format, type, param);\n      target.write = createFBO(w, h, internalFormat, format, type, param);\n      target.width = w;\n      target.height = h;\n      target.texelSizeX = 1.0 / w;\n      target.texelSizeY = 1.0 / h;\n      return target;\n    }\n\n    function updateKeywords() {\n      let displayKeywords = [];\n      if (config.SHADING) displayKeywords.push('SHADING');\n      displayMaterial.setKeywords(displayKeywords);\n    }\n\n    updateKeywords();\n    initFramebuffers();\n    let lastUpdateTime = Date.now();\n    let colorUpdateTimer = 0.0;\n\n    function updateFrame() {\n      const dt = calcDeltaTime();\n      if (resizeCanvas()) initFramebuffers();\n      updateColors(dt);\n      applyInputs();\n      step(dt);\n      render(null);\n      requestAnimationFrame(updateFrame);\n    }\n\n    function calcDeltaTime() {\n      let now = Date.now();\n      let dt = (now - lastUpdateTime) / 1000;\n      dt = Math.min(dt, 0.016666);\n      lastUpdateTime = now;\n      return dt;\n    }\n\n    function resizeCanvas() {\n      let width = scaleByPixelRatio(canvas.clientWidth);\n      let height = scaleByPixelRatio(canvas.clientHeight);\n      if (canvas.width !== width || canvas.height !== height) {\n        canvas.width = width;\n        canvas.height = height;\n        return true;\n      }\n      return false;\n    }\n\n    function updateColors(dt) {\n      colorUpdateTimer += dt * config.COLOR_UPDATE_SPEED;\n      if (colorUpdateTimer >= 1) {\n        colorUpdateTimer = wrap(colorUpdateTimer, 0, 1);\n        pointers.forEach(p => {\n          p.color = generateColor();\n        });\n      }\n    }\n\n    function applyInputs() {\n      pointers.forEach(p => {\n        if (p.moved) {\n          p.moved = false;\n          splatPointer(p);\n        }\n      });\n    }\n\n    function step(dt) {\n      gl.disable(gl.BLEND);\n      curlProgram.bind();\n      gl.uniform2f(curlProgram.uniforms.texelSize, velocity.texelSizeX, velocity.texelSizeY);\n      gl.uniform1i(curlProgram.uniforms.uVelocity, velocity.read.attach(0));\n      blit(curl);\n\n      vorticityProgram.bind();\n      gl.uniform2f(vorticityProgram.uniforms.texelSize, velocity.texelSizeX, velocity.texelSizeY);\n      gl.uniform1i(vorticityProgram.uniforms.uVelocity, velocity.read.attach(0));\n      gl.uniform1i(vorticityProgram.uniforms.uCurl, curl.attach(1));\n      gl.uniform1f(vorticityProgram.uniforms.curl, config.CURL);\n      gl.uniform1f(vorticityProgram.uniforms.dt, dt);\n      blit(velocity.write);\n      velocity.swap();\n\n      divergenceProgram.bind();\n      gl.uniform2f(divergenceProgram.uniforms.texelSize, velocity.texelSizeX, velocity.texelSizeY);\n      gl.uniform1i(divergenceProgram.uniforms.uVelocity, velocity.read.attach(0));\n      blit(divergence);\n\n      clearProgram.bind();\n      gl.uniform1i(clearProgram.uniforms.uTexture, pressure.read.attach(0));\n      gl.uniform1f(clearProgram.uniforms.value, config.PRESSURE);\n      blit(pressure.write);\n      pressure.swap();\n\n      pressureProgram.bind();\n      gl.uniform2f(pressureProgram.uniforms.texelSize, velocity.texelSizeX, velocity.texelSizeY);\n      gl.uniform1i(pressureProgram.uniforms.uDivergence, divergence.attach(0));\n      for (let i = 0; i < config.PRESSURE_ITERATIONS; i++) {\n        gl.uniform1i(pressureProgram.uniforms.uPressure, pressure.read.attach(1));\n        blit(pressure.write);\n        pressure.swap();\n      }\n\n      gradienSubtractProgram.bind();\n      gl.uniform2f(gradienSubtractProgram.uniforms.texelSize, velocity.texelSizeX, velocity.texelSizeY);\n      gl.uniform1i(gradienSubtractProgram.uniforms.uPressure, pressure.read.attach(0));\n      gl.uniform1i(gradienSubtractProgram.uniforms.uVelocity, velocity.read.attach(1));\n      blit(velocity.write);\n      velocity.swap();\n\n      advectionProgram.bind();\n      gl.uniform2f(advectionProgram.uniforms.texelSize, velocity.texelSizeX, velocity.texelSizeY);\n      if (!ext.supportLinearFiltering)\n        gl.uniform2f(advectionProgram.uniforms.dyeTexelSize, velocity.texelSizeX, velocity.texelSizeY);\n      let velocityId = velocity.read.attach(0);\n      gl.uniform1i(advectionProgram.uniforms.uVelocity, velocityId);\n      gl.uniform1i(advectionProgram.uniforms.uSource, velocityId);\n      gl.uniform1f(advectionProgram.uniforms.dt, dt);\n      gl.uniform1f(advectionProgram.uniforms.dissipation, config.VELOCITY_DISSIPATION);\n      blit(velocity.write);\n      velocity.swap();\n\n      if (!ext.supportLinearFiltering)\n        gl.uniform2f(advectionProgram.uniforms.dyeTexelSize, dye.texelSizeX, dye.texelSizeY);\n      gl.uniform1i(advectionProgram.uniforms.uVelocity, velocity.read.attach(0));\n      gl.uniform1i(advectionProgram.uniforms.uSource, dye.read.attach(1));\n      gl.uniform1f(advectionProgram.uniforms.dissipation, config.DENSITY_DISSIPATION);\n      blit(dye.write);\n      dye.swap();\n    }\n\n    function render(target) {\n      gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);\n      gl.enable(gl.BLEND);\n      drawDisplay(target);\n    }\n\n    function drawDisplay(target) {\n      let width = target == null ? gl.drawingBufferWidth : target.width;\n      let height = target == null ? gl.drawingBufferHeight : target.height;\n      displayMaterial.bind();\n      if (config.SHADING) gl.uniform2f(displayMaterial.uniforms.texelSize, 1.0 / width, 1.0 / height);\n      gl.uniform1i(displayMaterial.uniforms.uTexture, dye.read.attach(0));\n      blit(target);\n    }\n\n    function splatPointer(pointer) {\n      let dx = pointer.deltaX * config.SPLAT_FORCE;\n      let dy = pointer.deltaY * config.SPLAT_FORCE;\n      splat(pointer.texcoordX, pointer.texcoordY, dx, dy, pointer.color);\n    }\n\n    function clickSplat(pointer) {\n      const color = generateColor();\n      color.r *= 10.0;\n      color.g *= 10.0;\n      color.b *= 10.0;\n      let dx = 10 * (Math.random() - 0.5);\n      let dy = 30 * (Math.random() - 0.5);\n      splat(pointer.texcoordX, pointer.texcoordY, dx, dy, color);\n    }\n\n    function splat(x, y, dx, dy, color) {\n      splatProgram.bind();\n      gl.uniform1i(splatProgram.uniforms.uTarget, velocity.read.attach(0));\n      gl.uniform1f(splatProgram.uniforms.aspectRatio, canvas.width / canvas.height);\n      gl.uniform2f(splatProgram.uniforms.point, x, y);\n      gl.uniform3f(splatProgram.uniforms.color, dx, dy, 0.0);\n      gl.uniform1f(splatProgram.uniforms.radius, correctRadius(config.SPLAT_RADIUS / 100.0));\n      blit(velocity.write);\n      velocity.swap();\n\n      gl.uniform1i(splatProgram.uniforms.uTarget, dye.read.attach(0));\n      gl.uniform3f(splatProgram.uniforms.color, color.r, color.g, color.b);\n      blit(dye.write);\n      dye.swap();\n    }\n\n    function correctRadius(radius) {\n      let aspectRatio = canvas.width / canvas.height;\n      if (aspectRatio > 1) radius *= aspectRatio;\n      return radius;\n    }\n\n    function updatePointerDownData(pointer, id, posX, posY) {\n      pointer.id = id;\n      pointer.down = true;\n      pointer.moved = false;\n      pointer.texcoordX = posX / canvas.width;\n      pointer.texcoordY = 1.0 - posY / canvas.height;\n      pointer.prevTexcoordX = pointer.texcoordX;\n      pointer.prevTexcoordY = pointer.texcoordY;\n      pointer.deltaX = 0;\n      pointer.deltaY = 0;\n      pointer.color = generateColor();\n    }\n\n    function updatePointerMoveData(pointer, posX, posY, color) {\n      pointer.prevTexcoordX = pointer.texcoordX;\n      pointer.prevTexcoordY = pointer.texcoordY;\n      pointer.texcoordX = posX / canvas.width;\n      pointer.texcoordY = 1.0 - posY / canvas.height;\n      pointer.deltaX = correctDeltaX(pointer.texcoordX - pointer.prevTexcoordX);\n      pointer.deltaY = correctDeltaY(pointer.texcoordY - pointer.prevTexcoordY);\n      pointer.moved = Math.abs(pointer.deltaX) > 0 || Math.abs(pointer.deltaY) > 0;\n      pointer.color = color;\n    }\n\n    function updatePointerUpData(pointer) {\n      pointer.down = false;\n    }\n\n    function correctDeltaX(delta) {\n      let aspectRatio = canvas.width / canvas.height;\n      if (aspectRatio < 1) delta *= aspectRatio;\n      return delta;\n    }\n\n    function correctDeltaY(delta) {\n      let aspectRatio = canvas.width / canvas.height;\n      if (aspectRatio > 1) delta /= aspectRatio;\n      return delta;\n    }\n\n    function hexToRGB(hex) {\n      let val = hex.replace('#', '');\n      if (val.length === 3) val = val[0] + val[0] + val[1] + val[1] + val[2] + val[2];\n      const r = parseInt(val.slice(0, 2), 16) / 255;\n      const g = parseInt(val.slice(2, 4), 16) / 255;\n      const b = parseInt(val.slice(4, 6), 16) / 255;\n      return { r: r * 0.15, g: g * 0.15, b: b * 0.15 };\n    }\n\n    function generateColor() {\n      if (!config.RAINBOW_MODE) {\n        return hexToRGB(config.COLOR);\n      }\n      let c = HSVtoRGB(Math.random(), 1.0, 1.0);\n      c.r *= 0.15;\n      c.g *= 0.15;\n      c.b *= 0.15;\n      return c;\n    }\n\n    function HSVtoRGB(h, s, v) {\n      let r, g, b, i, f, p, q, t;\n      i = Math.floor(h * 6);\n      f = h * 6 - i;\n      p = v * (1 - s);\n      q = v * (1 - f * s);\n      t = v * (1 - (1 - f) * s);\n      switch (i % 6) {\n        case 0:\n          r = v;\n          g = t;\n          b = p;\n          break;\n        case 1:\n          r = q;\n          g = v;\n          b = p;\n          break;\n        case 2:\n          r = p;\n          g = v;\n          b = t;\n          break;\n        case 3:\n          r = p;\n          g = q;\n          b = v;\n          break;\n        case 4:\n          r = t;\n          g = p;\n          b = v;\n          break;\n        case 5:\n          r = v;\n          g = p;\n          b = q;\n          break;\n        default:\n          break;\n      }\n      return { r, g, b };\n    }\n\n    function wrap(value, min, max) {\n      const range = max - min;\n      if (range === 0) return min;\n      return ((value - min) % range) + min;\n    }\n\n    function getResolution(resolution) {\n      let aspectRatio = gl.drawingBufferWidth / gl.drawingBufferHeight;\n      if (aspectRatio < 1) aspectRatio = 1.0 / aspectRatio;\n      const min = Math.round(resolution);\n      const max = Math.round(resolution * aspectRatio);\n      if (gl.drawingBufferWidth > gl.drawingBufferHeight) return { width: max, height: min };\n      else return { width: min, height: max };\n    }\n\n    function scaleByPixelRatio(input) {\n      const pixelRatio = window.devicePixelRatio || 1;\n      return Math.floor(input * pixelRatio);\n    }\n\n    function hashCode(s) {\n      if (s.length === 0) return 0;\n      let hash = 0;\n      for (let i = 0; i < s.length; i++) {\n        hash = (hash << 5) - hash + s.charCodeAt(i);\n        hash |= 0;\n      }\n      return hash;\n    }\n\n    window.addEventListener('mousedown', e => {\n      let pointer = pointers[0];\n      let posX = scaleByPixelRatio(e.clientX);\n      let posY = scaleByPixelRatio(e.clientY);\n      updatePointerDownData(pointer, -1, posX, posY);\n      clickSplat(pointer);\n    });\n\n    document.body.addEventListener('mousemove', function handleFirstMouseMove(e) {\n      let pointer = pointers[0];\n      let posX = scaleByPixelRatio(e.clientX);\n      let posY = scaleByPixelRatio(e.clientY);\n      let color = generateColor();\n      updateFrame();\n      updatePointerMoveData(pointer, posX, posY, color);\n      document.body.removeEventListener('mousemove', handleFirstMouseMove);\n    });\n\n    window.addEventListener('mousemove', e => {\n      let pointer = pointers[0];\n      let posX = scaleByPixelRatio(e.clientX);\n      let posY = scaleByPixelRatio(e.clientY);\n      let color = pointer.color;\n      updatePointerMoveData(pointer, posX, posY, color);\n    });\n\n    document.body.addEventListener('touchstart', function handleFirstTouchStart(e) {\n      const touches = e.targetTouches;\n      let pointer = pointers[0];\n      for (let i = 0; i < touches.length; i++) {\n        let posX = scaleByPixelRatio(touches[i].clientX);\n        let posY = scaleByPixelRatio(touches[i].clientY);\n        updateFrame();\n        updatePointerDownData(pointer, touches[i].identifier, posX, posY);\n      }\n      document.body.removeEventListener('touchstart', handleFirstTouchStart);\n    });\n\n    window.addEventListener('touchstart', e => {\n      const touches = e.targetTouches;\n      let pointer = pointers[0];\n      for (let i = 0; i < touches.length; i++) {\n        let posX = scaleByPixelRatio(touches[i].clientX);\n        let posY = scaleByPixelRatio(touches[i].clientY);\n        updatePointerDownData(pointer, touches[i].identifier, posX, posY);\n      }\n    });\n\n    window.addEventListener(\n      'touchmove',\n      e => {\n        const touches = e.targetTouches;\n        let pointer = pointers[0];\n        for (let i = 0; i < touches.length; i++) {\n          let posX = scaleByPixelRatio(touches[i].clientX);\n          let posY = scaleByPixelRatio(touches[i].clientY);\n          updatePointerMoveData(pointer, posX, posY, pointer.color);\n        }\n      },\n      false\n    );\n\n    window.addEventListener('touchend', e => {\n      const touches = e.changedTouches;\n      let pointer = pointers[0];\n      for (let i = 0; i < touches.length; i++) {\n        updatePointerUpData(pointer);\n      }\n    });\n\n    updateFrame();\n  }, [\n    SIM_RESOLUTION,\n    DYE_RESOLUTION,\n    CAPTURE_RESOLUTION,\n    DENSITY_DISSIPATION,\n    VELOCITY_DISSIPATION,\n    PRESSURE,\n    PRESSURE_ITERATIONS,\n    CURL,\n    SPLAT_RADIUS,\n    SPLAT_FORCE,\n    SHADING,\n    COLOR_UPDATE_SPEED,\n    BACK_COLOR,\n    TRANSPARENT,\n    RAINBOW_MODE,\n    COLOR\n  ]);\n\n  return (\n    <div className=\"fixed top-0 left-0 z-50 pointer-events-none w-full h-full\">\n      <canvas ref={canvasRef} id=\"fluid\" className=\"w-screen h-screen block\"></canvas>\n    </div>\n  );\n}\n\nexport default SplashCursor;\n"
		}
	],
	"registryDependencies": [],
	"dependencies": []
}