{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "grainient",
  "type": "registry:block",
  "title": "Grainient",
  "description": "Grainient",
  "files": [
    {
      "path": "components/usages/grainientusage.tsx",
      "content": "\"use client\";\n\nimport React from \"react\";\nimport Grainient from '@/registry/open-source/grainient';\n\nexport default function Usage() {\n    return (\n        <div className=\"h-screen w-full flex items-center justify-center relative overflow-hidden bg-background\">\n            <Grainient />\n        </div>\n    );\n}\n",
      "type": "registry:block",
      "target": "~/example.tsx"
    },
    {
      "path": "components/usages/grainientusage.tsx",
      "content": "\"use client\";\n\nimport React from \"react\";\nimport Grainient from '@/registry/open-source/grainient';\n\nexport default function Usage() {\n    return (\n        <div className=\"h-screen w-full flex items-center justify-center relative overflow-hidden bg-background\">\n            <Grainient />\n        </div>\n    );\n}\n",
      "type": "registry:ui"
    },
    {
      "path": "registry/open-source/grainient.tsx",
      "content": "import React, { useEffect, useRef } from 'react';\nimport { Renderer, Program, Mesh, Triangle } from 'ogl';\n\ninterface GrainientProps {\n    timeSpeed?: number;\n    colorBalance?: number;\n    warpStrength?: number;\n    warpFrequency?: number;\n    warpSpeed?: number;\n    warpAmplitude?: number;\n    blendAngle?: number;\n    blendSoftness?: number;\n    rotationAmount?: number;\n    noiseScale?: number;\n    grainAmount?: number;\n    grainScale?: number;\n    grainAnimated?: boolean;\n    contrast?: number;\n    gamma?: number;\n    saturation?: number;\n    centerX?: number;\n    centerY?: number;\n    zoom?: number;\n    color1?: string;\n    color2?: string;\n    color3?: string;\n    className?: string;\n}\n\nconst hexToRgb = (hex: string): [number, number, number] => {\n    const result = /^#?([a-f\\d]{2})([a-f\\d]{2})([a-f\\d]{2})$/i.exec(hex);\n    if (!result) return [1, 1, 1];\n    return [parseInt(result[1], 16) / 255, parseInt(result[2], 16) / 255, parseInt(result[3], 16) / 255];\n};\n\nconst vertex = `#version 300 es\nin vec2 position;\nvoid main() {\n  gl_Position = vec4(position, 0.0, 1.0);\n}\n`;\n\nconst fragment = `#version 300 es\nprecision highp float;\nuniform vec2 iResolution;\nuniform float iTime;\nuniform float uTimeSpeed;\nuniform float uColorBalance;\nuniform float uWarpStrength;\nuniform float uWarpFrequency;\nuniform float uWarpSpeed;\nuniform float uWarpAmplitude;\nuniform float uBlendAngle;\nuniform float uBlendSoftness;\nuniform float uRotationAmount;\nuniform float uNoiseScale;\nuniform float uGrainAmount;\nuniform float uGrainScale;\nuniform float uGrainAnimated;\nuniform float uContrast;\nuniform float uGamma;\nuniform float uSaturation;\nuniform vec2 uCenterOffset;\nuniform float uZoom;\nuniform vec3 uColor1;\nuniform vec3 uColor2;\nuniform vec3 uColor3;\nout vec4 fragColor;\n#define S(a,b,t) smoothstep(a,b,t)\nmat2 Rot(float a){float s=sin(a),c=cos(a);return mat2(c,-s,s,c);} \nvec2 hash(vec2 p){p=vec2(dot(p,vec2(2127.1,81.17)),dot(p,vec2(1269.5,283.37)));return fract(sin(p)*43758.5453);} \nfloat noise(vec2 p){vec2 i=floor(p),f=fract(p),u=f*f*(3.0-2.0*f);float n=mix(mix(dot(-1.0+2.0*hash(i+vec2(0.0,0.0)),f-vec2(0.0,0.0)),dot(-1.0+2.0*hash(i+vec2(1.0,0.0)),f-vec2(1.0,0.0)),u.x),mix(dot(-1.0+2.0*hash(i+vec2(0.0,1.0)),f-vec2(0.0,1.0)),dot(-1.0+2.0*hash(i+vec2(1.0,1.0)),f-vec2(1.0,1.0)),u.x),u.y);return 0.5+0.5*n;}\nvoid mainImage(out vec4 o, vec2 C){\n  float t=iTime*uTimeSpeed;\n  vec2 uv=C/iResolution.xy;\n  float ratio=iResolution.x/iResolution.y;\n  vec2 tuv=uv-0.5+uCenterOffset;\n  tuv/=max(uZoom,0.001);\n\n  float degree=noise(vec2(t*0.1,tuv.x*tuv.y)*uNoiseScale);\n  tuv.y*=1.0/ratio;\n  tuv*=Rot(radians((degree-0.5)*uRotationAmount+180.0));\n  tuv.y*=ratio;\n\n  float frequency=uWarpFrequency;\n  float ws=max(uWarpStrength,0.001);\n  float amplitude=uWarpAmplitude/ws;\n  float warpTime=t*uWarpSpeed;\n  tuv.x+=sin(tuv.y*frequency+warpTime)/amplitude;\n  tuv.y+=sin(tuv.x*(frequency*1.5)+warpTime)/(amplitude*0.5);\n\n  vec3 colLav=uColor1;\n  vec3 colOrg=uColor2;\n  vec3 colDark=uColor3;\n  float b=uColorBalance;\n  float s=max(uBlendSoftness,0.0);\n  mat2 blendRot=Rot(radians(uBlendAngle));\n  float blendX=(tuv*blendRot).x;\n  float edge0=-0.3-b-s;\n  float edge1=0.2-b+s;\n  float v0=0.5-b+s;\n  float v1=-0.3-b-s;\n  vec3 layer1=mix(colDark,colOrg,S(edge0,edge1,blendX));\n  vec3 layer2=mix(colOrg,colLav,S(edge0,edge1,blendX));\n  vec3 col=mix(layer1,layer2,S(v0,v1,tuv.y));\n\n  vec2 grainUv=uv*max(uGrainScale,0.001);\n  if(uGrainAnimated>0.5){grainUv+=vec2(iTime*0.05);} \n  float grain=fract(sin(dot(grainUv,vec2(12.9898,78.233)))*43758.5453);\n  col+=(grain-0.5)*uGrainAmount;\n\n  col=(col-0.5)*uContrast+0.5;\n  float luma=dot(col,vec3(0.2126,0.7152,0.0722));\n  col=mix(vec3(luma),col,uSaturation);\n  col=pow(max(col,0.0),vec3(1.0/max(uGamma,0.001)));\n  col=clamp(col,0.0,1.0);\n\n  o=vec4(col,1.0);\n}\nvoid main(){\n  vec4 o=vec4(0.0);\n  mainImage(o,gl_FragCoord.xy);\n  fragColor=o;\n}\n`;\n\nconst Grainient: React.FC<GrainientProps> = ({\n    timeSpeed = 0.25,\n    colorBalance = 0.0,\n    warpStrength = 1.0,\n    warpFrequency = 5.0,\n    warpSpeed = 2.0,\n    warpAmplitude = 50.0,\n    blendAngle = 0.0,\n    blendSoftness = 0.05,\n    rotationAmount = 500.0,\n    noiseScale = 2.0,\n    grainAmount = 0.1,\n    grainScale = 2.0,\n    grainAnimated = false,\n    contrast = 1.5,\n    gamma = 1.0,\n    saturation = 1.0,\n    centerX = 0.0,\n    centerY = 0.0,\n    zoom = 0.9,\n    color1 = '#FF9FFC',\n    color2 = '#5227FF',\n    color3 = '#B19EEF',\n    className = ''\n}) => {\n    const containerRef = useRef<HTMLDivElement | null>(null);\n\n    useEffect(() => {\n        if (!containerRef.current) return;\n\n        const renderer = new Renderer({\n            webgl: 2,\n            alpha: true,\n            antialias: false,\n            dpr: Math.min(window.devicePixelRatio || 1, 2)\n        });\n\n        const gl = renderer.gl;\n        const canvas = gl.canvas as HTMLCanvasElement;\n        canvas.style.width = '100%';\n        canvas.style.height = '100%';\n        canvas.style.display = 'block';\n\n        const container = containerRef.current;\n        container.appendChild(canvas);\n\n        const geometry = new Triangle(gl);\n        const program = new Program(gl, {\n            vertex,\n            fragment,\n            uniforms: {\n                iTime: { value: 0 },\n                iResolution: { value: new Float32Array([1, 1]) },\n                uTimeSpeed: { value: timeSpeed },\n                uColorBalance: { value: colorBalance },\n                uWarpStrength: { value: warpStrength },\n                uWarpFrequency: { value: warpFrequency },\n                uWarpSpeed: { value: warpSpeed },\n                uWarpAmplitude: { value: warpAmplitude },\n                uBlendAngle: { value: blendAngle },\n                uBlendSoftness: { value: blendSoftness },\n                uRotationAmount: { value: rotationAmount },\n                uNoiseScale: { value: noiseScale },\n                uGrainAmount: { value: grainAmount },\n                uGrainScale: { value: grainScale },\n                uGrainAnimated: { value: grainAnimated ? 1.0 : 0.0 },\n                uContrast: { value: contrast },\n                uGamma: { value: gamma },\n                uSaturation: { value: saturation },\n                uCenterOffset: { value: new Float32Array([centerX, centerY]) },\n                uZoom: { value: zoom },\n                uColor1: { value: new Float32Array(hexToRgb(color1)) },\n                uColor2: { value: new Float32Array(hexToRgb(color2)) },\n                uColor3: { value: new Float32Array(hexToRgb(color3)) }\n            }\n        });\n\n        const mesh = new Mesh(gl, { geometry, program });\n\n        const setSize = () => {\n            const rect = container.getBoundingClientRect();\n            const width = Math.max(1, Math.floor(rect.width));\n            const height = Math.max(1, Math.floor(rect.height));\n            renderer.setSize(width, height);\n            const res = program.uniforms.iResolution.value as Float32Array;\n            res[0] = gl.drawingBufferWidth;\n            res[1] = gl.drawingBufferHeight;\n        };\n\n        const ro = new ResizeObserver(setSize);\n        ro.observe(container);\n        setSize();\n\n        let raf = 0;\n        const t0 = performance.now();\n        const loop = (t: number) => {\n            (program.uniforms.iTime as any).value = (t - t0) * 0.001;\n            renderer.render({ scene: mesh });\n            raf = requestAnimationFrame(loop);\n        };\n        raf = requestAnimationFrame(loop);\n\n        return () => {\n            cancelAnimationFrame(raf);\n            ro.disconnect();\n            try {\n                container.removeChild(canvas);\n            } catch {\n                // Ignore\n            }\n        };\n    }, [\n        timeSpeed,\n        colorBalance,\n        warpStrength,\n        warpFrequency,\n        warpSpeed,\n        warpAmplitude,\n        blendAngle,\n        blendSoftness,\n        rotationAmount,\n        noiseScale,\n        grainAmount,\n        grainScale,\n        grainAnimated,\n        contrast,\n        gamma,\n        saturation,\n        centerX,\n        centerY,\n        zoom,\n        color1,\n        color2,\n        color3\n    ]);\n\n    return <div ref={containerRef} className={`relative h-full w-full overflow-hidden ${className}`.trim()} />;\n};\n\nexport default Grainient;\n",
      "type": "registry:ui"
    }
  ]
}