{ "name": "dither-prism-hero", "type": "registry:ui", "title": "Dither Prism Hero", "dependencies": [ "@react-three/fiber", "@react-three/drei", "three", "framer-motion" ], "devDependencies": [ "@types/three" ], "registryDependencies": [], "description": "A stunning WebGL hero background featuring advanced dithering patterns, prismatic color refraction, holographic iridescence, and center-focused ripple energy.", "files": [ { "path": "components/ui/dither-prism-hero.tsx", "content": "\"use client\";\n\n/* eslint-disable react/no-unknown-property */\nimport { useRef, useMemo, useEffect, useState } from \"react\";\nimport { Canvas, useFrame, ThreeElements, useThree } from \"@react-three/fiber\";\nimport * as THREE from \"three\";\nimport { motion } from \"framer-motion\";\n\nimport { cn } from \"@workspace/ui/lib/utils\";\n\n// Type augmentation for R3F\ndeclare global {\n // eslint-disable-next-line @typescript-eslint/no-namespace\n namespace JSX {\n type IntrinsicElements = ThreeElements;\n }\n}\n\n// ═══════════════════════════════════════════════════════════════════════════════\n// VERTEX SHADER\n// ═══════════════════════════════════════════════════════════════════════════════\nconst vertexShader = `\nvarying vec2 vUv;\nvarying vec3 vPosition;\n\nvoid main() {\n vUv = uv;\n vPosition = position;\n gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\n}\n`;\n\n// ═══════════════════════════════════════════════════════════════════════════════\n// FRAGMENT SHADER - The magic happens here!\n// ═══════════════════════════════════════════════════════════════════════════════\nconst fragmentShader = `\nuniform float uTime;\nuniform vec2 uResolution;\nuniform vec2 uMouse;\nuniform float uMouseIntensity;\nuniform vec3 uColor1;\nuniform vec3 uColor2;\nuniform vec3 uColor3;\nuniform float uDitherIntensity;\nuniform float uPrismIntensity;\nvarying vec2 vUv;\nvarying vec3 vPosition;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Hash functions for procedural noise\n// ─────────────────────────────────────────────────────────────────────────────\nfloat hash(vec2 p) {\n return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453);\n}\n\nfloat hash3(vec3 p) {\n return fract(sin(dot(p, vec3(127.1, 311.7, 74.7))) * 43758.5453);\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Simplex 2D Noise\n// ─────────────────────────────────────────────────────────────────────────────\nvec3 permute(vec3 x) { return mod(((x*34.0)+1.0)*x, 289.0); }\n\nfloat snoise(vec2 v) {\n const vec4 C = vec4(0.211324865405187, 0.366025403784439,\n -0.577350269189626, 0.024390243902439);\n vec2 i = floor(v + dot(v, C.yy));\n vec2 x0 = v - i + dot(i, C.xx);\n vec2 i1;\n i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);\n vec4 x12 = x0.xyxy + C.xxzz;\n x12.xy -= i1;\n i = mod(i, 289.0);\n vec3 p = permute( permute( i.y + vec3(0.0, i1.y, 1.0))\n + i.x + vec3(0.0, i1.x, 1.0));\n vec3 m = max(0.5 - vec3(dot(x0,x0), dot(x12.xy,x12.xy), dot(x12.zw,x12.zw)), 0.0);\n m = m*m;\n m = m*m;\n vec3 x = 2.0 * fract(p * C.www) - 1.0;\n vec3 h = abs(x) - 0.5;\n vec3 ox = floor(x + 0.5);\n vec3 a0 = x - ox;\n m *= 1.79284291400159 - 0.85373472095314 * (a0*a0 + h*h);\n vec3 g;\n g.x = a0.x * x0.x + h.x * x0.y;\n g.yz = a0.yz * x12.xz + h.yz * x12.yw;\n return 130.0 * dot(m, g);\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// FBM (Fractal Brownian Motion) for layered noise\n// ─────────────────────────────────────────────────────────────────────────────\nfloat fbm(vec2 p, int octaves) {\n float value = 0.0;\n float amplitude = 0.5;\n float frequency = 1.0;\n for (int i = 0; i < 6; i++) {\n if (i >= octaves) break;\n value += amplitude * snoise(p * frequency);\n frequency *= 2.0;\n amplitude *= 0.5;\n }\n return value;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Advanced 8x8 Bayer Matrix for ordered dithering\n// ─────────────────────────────────────────────────────────────────────────────\nfloat bayer8x8(vec2 uv) {\n ivec2 p = ivec2(mod(uv, 8.0));\n int matrix[64];\n matrix[0] = 0; matrix[1] = 32; matrix[2] = 8; matrix[3] = 40; matrix[4] = 2; matrix[5] = 34; matrix[6] = 10; matrix[7] = 42;\n matrix[8] = 48; matrix[9] = 16; matrix[10] = 56; matrix[11] = 24; matrix[12] = 50; matrix[13] = 18; matrix[14] = 58; matrix[15] = 26;\n matrix[16] = 12; matrix[17] = 44; matrix[18] = 4; matrix[19] = 36; matrix[20] = 14; matrix[21] = 46; matrix[22] = 6; matrix[23] = 38;\n matrix[24] = 60; matrix[25] = 28; matrix[26] = 52; matrix[27] = 20; matrix[28] = 62; matrix[29] = 30; matrix[30] = 54; matrix[31] = 22;\n matrix[32] = 3; matrix[33] = 35; matrix[34] = 11; matrix[35] = 43; matrix[36] = 1; matrix[37] = 33; matrix[38] = 9; matrix[39] = 41;\n matrix[40] = 51; matrix[41] = 19; matrix[42] = 59; matrix[43] = 27; matrix[44] = 49; matrix[45] = 17; matrix[46] = 57; matrix[47] = 25;\n matrix[48] = 15; matrix[49] = 47; matrix[50] = 7; matrix[51] = 39; matrix[52] = 13; matrix[53] = 45; matrix[54] = 5; matrix[55] = 37;\n matrix[56] = 63; matrix[57] = 31; matrix[58] = 55; matrix[59] = 23; matrix[60] = 61; matrix[61] = 29; matrix[62] = 53; matrix[63] = 21;\n return float(matrix[p.y * 8 + p.x]) / 64.0;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Blue Noise approximation for organic dithering\n// ─────────────────────────────────────────────────────────────────────────────\nfloat blueNoise(vec2 uv, float time) {\n float n1 = hash(uv + vec2(time * 0.1, 0.0));\n float n2 = hash(uv * 2.1 + vec2(0.0, time * 0.13));\n float n3 = hash(uv * 4.3 + vec2(time * 0.07, time * 0.11));\n return fract(n1 + n2 * 0.5 + n3 * 0.25);\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Prismatic color refraction - creates rainbow light scattering\n// ─────────────────────────────────────────────────────────────────────────────\nvec3 prism(vec2 uv, float time, float intensity) {\n float angle = atan(uv.y - 0.5, uv.x - 0.5);\n float dist = length(uv - 0.5);\n \n // Create rotating prismatic effect\n float prismAngle = angle + time * 0.3 + dist * 3.0;\n \n // RGB separation based on angle\n float r = 0.5 + 0.5 * sin(prismAngle);\n float g = 0.5 + 0.5 * sin(prismAngle + 2.094); // 120 degrees\n float b = 0.5 + 0.5 * sin(prismAngle + 4.188); // 240 degrees\n \n return vec3(r, g, b) * intensity;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Holographic iridescence effect\n// ─────────────────────────────────────────────────────────────────────────────\nvec3 iridescence(vec2 uv, float time) {\n float t = time * 0.5;\n vec2 p = uv * 3.0;\n \n float n1 = snoise(p + vec2(t, 0.0));\n float n2 = snoise(p * 1.3 + vec2(0.0, t * 0.7));\n float n3 = snoise(p * 0.7 + vec2(t * 0.5, t * 0.3));\n \n vec3 col1 = vec3(0.5 + 0.5 * sin(n1 * 3.14159 + t));\n vec3 col2 = vec3(0.5 + 0.5 * sin(n2 * 3.14159 + t * 1.3 + 2.0));\n vec3 col3 = vec3(0.5 + 0.5 * sin(n3 * 3.14159 + t * 0.7 + 4.0));\n \n return (col1 + col2 + col3) / 3.0;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Geometrically morphing shapes - creates flowing crystal/diamond patterns\n// ─────────────────────────────────────────────────────────────────────────────\nfloat diamond(vec2 p) {\n return abs(p.x) + abs(p.y);\n}\n\nfloat morphShape(vec2 uv, float time) {\n float morph = sin(time * 0.4) * 0.5 + 0.5;\n \n vec2 p = uv * 4.0 - 2.0;\n p = p + vec2(sin(time * 0.3), cos(time * 0.4)) * 0.5;\n \n // Morphing between circle and diamond\n float circle = length(p) - 1.0;\n float diam = diamond(p) - 1.4;\n \n float shape = mix(circle, diam, morph);\n \n // Create multiple copies\n vec2 q = mod(uv * 8.0, 2.0) - 1.0;\n float multiShape = mix(length(q), diamond(q), morph) - 0.3;\n \n return min(shape, multiShape);\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Mouse interaction - creates DRAMATIC ripple effect from cursor\n// ─────────────────────────────────────────────────────────────────────────────\nfloat mouseRipple(vec2 uv, vec2 mouse, float time, float intensity) {\n float dist = length(uv - mouse);\n // Multiple concentric ripples\n float ripple1 = sin(dist * 40.0 - time * 5.0) * exp(-dist * 3.0);\n float ripple2 = sin(dist * 25.0 - time * 3.5 + 1.0) * exp(-dist * 4.0);\n float ripple3 = sin(dist * 60.0 - time * 7.0) * exp(-dist * 5.0);\n return (ripple1 + ripple2 * 0.5 + ripple3 * 0.3) * intensity;\n}\n\n// Mouse glow - creates bright aura around cursor\nvec3 mouseGlow(vec2 uv, vec2 mouse, float time, float intensity, vec3 glowColor) {\n float dist = length(uv - mouse);\n \n // Inner bright core\n float core = exp(-dist * 15.0) * 1.5;\n \n // Outer soft glow\n float outer = exp(-dist * 5.0) * 0.8;\n \n // Pulsing effect\n float pulse = 0.8 + 0.2 * sin(time * 3.0);\n \n // Rainbow chromatic aberration around cursor\n float chromatic = sin(dist * 30.0 + time * 2.0) * exp(-dist * 8.0);\n vec3 rainbow = vec3(\n sin(time * 2.0) * 0.5 + 0.5,\n sin(time * 2.0 + 2.094) * 0.5 + 0.5,\n sin(time * 2.0 + 4.188) * 0.5 + 0.5\n );\n \n vec3 glow = glowColor * (core + outer) * pulse * intensity;\n glow += rainbow * chromatic * intensity * 0.5;\n \n return glow;\n}\n\n// Lens distortion around cursor\nvec2 mouseLensDistort(vec2 uv, vec2 mouse, float intensity) {\n vec2 delta = uv - mouse;\n float dist = length(delta);\n float distortion = exp(-dist * 6.0) * intensity * 0.15;\n return uv + normalize(delta + 0.001) * distortion;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// MAIN SHADER\n// ─────────────────────────────────────────────────────────────────────────────\nvoid main() {\n vec2 uv = vUv;\n vec2 pixelCoord = gl_FragCoord.xy;\n float time = uTime;\n \n // ═══════════════════════════════════════════════════════════════════\n // Layer 0: Apply mouse lens distortion to UV coordinates FIRST\n // ═══════════════════════════════════════════════════════════════════\n vec2 distortedUv = mouseLensDistort(uv, uMouse, uMouseIntensity);\n \n // ═══════════════════════════════════════════════════════════════════\n // Layer 1: Base flowing gradient with noise (using distorted UVs)\n // ═══════════════════════════════════════════════════════════════════\n float noise1 = fbm(distortedUv * 2.0 + vec2(time * 0.05, time * 0.03), 4);\n float noise2 = fbm(distortedUv * 3.0 + vec2(-time * 0.04, time * 0.06), 3);\n \n float diagonal = (distortedUv.x + distortedUv.y) * 0.5;\n float flow = diagonal + noise1 * 0.3 + noise2 * 0.2;\n flow += sin(time * 0.2) * 0.1;\n \n // ═══════════════════════════════════════════════════════════════════\n // Layer 2: Color mixing with tri-color gradient\n // ═══════════════════════════════════════════════════════════════════\n vec3 col;\n float t1 = smoothstep(0.0, 0.5, flow);\n float t2 = smoothstep(0.5, 1.0, flow);\n \n col = mix(uColor1, uColor2, t1);\n col = mix(col, uColor3, t2);\n \n // ═══════════════════════════════════════════════════════════════════\n // Layer 3: Prismatic light refraction\n // ═══════════════════════════════════════════════════════════════════\n vec3 prismColor = prism(distortedUv, time, uPrismIntensity);\n \n // Apply prism only at edges/transitions\n float edgeMask = abs(fract(flow * 5.0) - 0.5) * 2.0;\n edgeMask = smoothstep(0.3, 0.7, edgeMask);\n col += prismColor * edgeMask * 0.4;\n \n // ═══════════════════════════════════════════════════════════════════\n // Layer 4: Iridescent holographic overlay\n // ═══════════════════════════════════════════════════════════════════\n vec3 iris = iridescence(distortedUv, time);\n float irisMask = snoise(distortedUv * 5.0 + time * 0.1);\n irisMask = smoothstep(-0.2, 0.8, irisMask) * 0.15;\n col = mix(col, iris, irisMask);\n \n // ═══════════════════════════════════════════════════════════════════\n // Layer 5: Geometric crystal patterns\n // ═══════════════════════════════════════════════════════════════════\n float shape = morphShape(distortedUv, time);\n float shapeMask = 1.0 - smoothstep(-0.1, 0.1, shape);\n col = mix(col, col * 1.15 + vec3(0.08), shapeMask * 0.3);\n \n // ═══════════════════════════════════════════════════════════════════\n // Layer 6: VISIBLE Mouse interaction - ripples + glow + color shift\n // ═══════════════════════════════════════════════════════════════════\n float ripple = mouseRipple(uv, uMouse, time, uMouseIntensity);\n \n // Add dramatic ripple color changes\n col += ripple * prismColor * 1.2;\n col += ripple * vec3(0.3, 0.2, 0.4);\n \n // Add bright glowing cursor aura\n vec3 glow = mouseGlow(uv, uMouse, time, uMouseIntensity, vec3(1.0, 0.8, 1.0));\n col += glow;\n \n // Color shift near cursor - make area around mouse more vibrant\n float mouseDist = length(uv - uMouse);\n float proximityBoost = exp(-mouseDist * 4.0) * uMouseIntensity;\n col = mix(col, col * 1.5 + prismColor * 0.3, proximityBoost);\n \n // ═══════════════════════════════════════════════════════════════════\n // Layer 7: ADVANCED DITHERING - The signature look!\n // ═══════════════════════════════════════════════════════════════════\n \n // 8x8 Bayer ordered dithering\n float bayer = bayer8x8(pixelCoord);\n \n // Animated blue noise\n float blue = blueNoise(pixelCoord * 0.1, time);\n \n // Combine dithering patterns\n float ditherPattern = mix(bayer, blue, 0.3 + 0.2 * sin(time * 0.5));\n \n // Apply dithering to create the signature grainy/retro look\n vec3 ditherOffset = (vec3(ditherPattern) - 0.5) * uDitherIntensity;\n col += ditherOffset;\n \n // Quantize colors for retro dithered appearance\n float levels = 16.0;\n vec3 quantized = floor(col * levels + ditherPattern) / levels;\n col = mix(col, quantized, uDitherIntensity * 0.5);\n \n // ═══════════════════════════════════════════════════════════════════\n // Layer 8: Scanline effect for extra depth\n // ═══════════════════════════════════════════════════════════════════\n float scanline = sin(pixelCoord.y * 2.0 + time * 2.0) * 0.02;\n col += scanline * uDitherIntensity;\n \n // ═══════════════════════════════════════════════════════════════════\n // Layer 9: Vignette for cinematic depth\n // ═══════════════════════════════════════════════════════════════════\n float vignette = 1.0 - length((uv - 0.5) * 1.2);\n vignette = smoothstep(0.0, 0.7, vignette);\n col *= 0.85 + vignette * 0.15;\n \n // ═══════════════════════════════════════════════════════════════════\n // Final output\n // ═══════════════════════════════════════════════════════════════════\n col = clamp(col, 0.0, 1.0);\n gl_FragColor = vec4(col, 1.0);\n}\n`;\n\n// ═══════════════════════════════════════════════════════════════════════════════\n// The WebGL Plane Component\n// ═══════════════════════════════════════════════════════════════════════════════\nconst DitherPrismPlane = ({\n color1,\n color2,\n color3,\n speed = 1,\n ditherIntensity = 0.15,\n prismIntensity = 0.5,\n}: {\n color1: string;\n color2: string;\n color3: string;\n speed?: number;\n ditherIntensity?: number;\n prismIntensity?: number;\n}) => {\n const meshRef = useRef(null);\n const { size } = useThree();\n\n const uniforms = useMemo(\n () => ({\n uTime: { value: 0 },\n uResolution: { value: new THREE.Vector2(1000, 1000) },\n uMouse: { value: new THREE.Vector2(0.5, 0.5) },\n uMouseIntensity: { value: 0.8 },\n uColor1: { value: new THREE.Color(color1) },\n uColor2: { value: new THREE.Color(color2) },\n uColor3: { value: new THREE.Color(color3) },\n uDitherIntensity: { value: ditherIntensity },\n uPrismIntensity: { value: prismIntensity },\n }),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [] // Depend on nothing to keep reference stable\n );\n\n useFrame((state) => {\n const { clock } = state;\n\n uniforms.uTime.value = clock.getElapsedTime() * speed;\n uniforms.uResolution.value.set(size.width, size.height);\n uniforms.uMouse.value.set(0.5, 0.5);\n uniforms.uMouseIntensity.value = 0.8;\n uniforms.uColor1.value.set(color1);\n uniforms.uColor2.value.set(color2);\n uniforms.uColor3.value.set(color3);\n uniforms.uDitherIntensity.value = ditherIntensity;\n uniforms.uPrismIntensity.value = prismIntensity;\n });\n\n return (\n \n \n \n \n );\n};\n\n// ═══════════════════════════════════════════════════════════════════════════════\n// Floating Particles Layer - Adds depth and interactivity\n// ═══════════════════════════════════════════════════════════════════════════════\nconst FloatingParticles = ({\n count = 50,\n color = \"#ffffff\",\n}: {\n count?: number;\n color?: string;\n}) => {\n const pointsRef = useRef(null);\n\n const particles = useMemo(() => {\n const positions = new Float32Array(count * 3);\n const sizes = new Float32Array(count);\n const phases = new Float32Array(count);\n\n for (let i = 0; i < count; i++) {\n positions[i * 3] = (Math.random() - 0.5) * 4;\n positions[i * 3 + 1] = (Math.random() - 0.5) * 4;\n positions[i * 3 + 2] = (Math.random() - 0.5) * 2;\n sizes[i] = Math.random() * 3 + 1;\n phases[i] = Math.random() * Math.PI * 2;\n }\n\n return { positions, sizes, phases };\n }, [count]);\n\n useFrame(({ clock }) => {\n if (!pointsRef.current?.geometry?.attributes?.position) return;\n const time = clock.getElapsedTime();\n const positionAttr = pointsRef.current.geometry.attributes.position;\n const positions = positionAttr.array as Float32Array;\n\n for (let i = 0; i < count; i++) {\n const phase = particles.phases[i] ?? 0;\n const yIdx = i * 3 + 1;\n const xIdx = i * 3;\n positions[yIdx] = (positions[yIdx] ?? 0) + Math.sin(time + phase) * 0.001;\n positions[xIdx] = (positions[xIdx] ?? 0) + Math.cos(time * 0.5 + phase) * 0.0005;\n\n // Wrap particles\n if ((positions[yIdx] ?? 0) > 2) positions[yIdx] = -2;\n if ((positions[yIdx] ?? 0) < -2) positions[yIdx] = 2;\n }\n\n positionAttr.needsUpdate = true;\n });\n\n return (\n \n \n \n \n \n \n \n );\n};\n\n// ═══════════════════════════════════════════════════════════════════════════════\n// Main Component Props\n// ═══════════════════════════════════════════════════════════════════════════════\ninterface DitherPrismHeroProps extends React.HTMLAttributes {\n /** First line of headline */\n title1?: string;\n /** Second line of headline */\n title2?: string;\n /** Primary color (deep/dark) */\n color1?: string;\n /** Secondary color (mid) */\n color2?: string;\n /** Tertiary color (light/accent) */\n color3?: string;\n /** Animation speed multiplier */\n speed?: number;\n /** Dithering intensity (0-1) */\n ditherIntensity?: number;\n /** Prismatic refraction intensity (0-1) */\n prismIntensity?: number;\n /** Number of floating particles */\n particleCount?: number;\n /** Show floating particles */\n showParticles?: boolean;\n /** Particle color */\n particleColor?: string;\n /** Children to render on top */\n children?: React.ReactNode;\n}\n\nconst HERO_HEADLINE_CLASS =\n \"pb-[0.08em] text-[12cqi] md:text-[8cqi] lg:text-[6cqi] leading-[0.96] tracking-tighter font-bold text-transparent bg-clip-text bg-gradient-to-b from-zinc-900 via-zinc-500 to-zinc-800\";\n\n// ═══════════════════════════════════════════════════════════════════════════════\n// Main Export Component\n// ═══════════════════════════════════════════════════════════════════════════════\nexport default function DitherPrismHero({\n title1,\n title2,\n color1 = \"#0f0f23\",\n color2 = \"#6366f1\",\n color3 = \"#ec4899\",\n speed = 1,\n ditherIntensity = 0.15,\n prismIntensity = 0.5,\n particleCount = 50,\n showParticles = true,\n particleColor = \"#ffffff\",\n className,\n children,\n style,\n ...props\n}: DitherPrismHeroProps) {\n const [mounted, setMounted] = useState(false);\n\n useEffect(() => {\n setMounted(true);\n }, []);\n\n return (\n \n {/* WebGL Background */}\n {mounted && (\n
\n \n \n {showParticles && (\n \n )}\n \n
\n )}\n\n {/* Content Overlay */}\n {(title1 || title2 || children) && (\n
\n
\n {/* Headline */}\n {(title1 || title2) && (\n
\n {title1 && (\n
\n \n \n {title1}\n \n \n
\n )}\n {title2 && (\n
\n \n {title2}\n \n
\n )}\n
\n )}\n\n {/* Custom Children */}\n {children && (\n \n {children}\n \n )}\n
\n
\n )}\n \n );\n}\n\n// Named export for easier imports\nexport { DitherPrismHero };\n", "type": "registry:ui" } ] }