{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "Beams-TS-CSS", "type": "registry:block", "title": "Beams", "description": "Crossing animated ribbons with customizable properties.", "dependencies": [ "@react-three/drei", "@react-three/fiber", "three" ], "files": [ { "path": "public/ts/default/src/ts-default/Backgrounds/Beams/Beams.tsx", "content": "import { forwardRef, useImperativeHandle, useEffect, useRef, useMemo, FC, ReactNode } from 'react';\r\n\r\nimport * as THREE from 'three';\r\n\r\nimport { Canvas, useFrame } from '@react-three/fiber';\r\nimport { PerspectiveCamera } from '@react-three/drei';\r\nimport { degToRad } from 'three/src/math/MathUtils.js';\r\n\r\nimport './Beams.css';\r\n\r\ntype UniformValue = THREE.IUniform | unknown;\r\n\r\ninterface ExtendMaterialConfig {\r\n header: string;\r\n vertexHeader?: string;\r\n fragmentHeader?: string;\r\n material?: THREE.MeshPhysicalMaterialParameters & { fog?: boolean };\r\n uniforms?: Record;\r\n vertex?: Record;\r\n fragment?: Record;\r\n}\r\n\r\ntype ShaderWithDefines = THREE.ShaderLibShader & {\r\n defines?: Record;\r\n};\r\n\r\nfunction extendMaterial(\r\n BaseMaterial: new (params?: THREE.MaterialParameters) => T,\r\n cfg: ExtendMaterialConfig\r\n): THREE.ShaderMaterial {\r\n const physical = THREE.ShaderLib.physical as ShaderWithDefines;\r\n const { vertexShader: baseVert, fragmentShader: baseFrag, uniforms: baseUniforms } = physical;\r\n const baseDefines = physical.defines ?? {};\r\n\r\n const uniforms: Record = THREE.UniformsUtils.clone(baseUniforms);\r\n\r\n const defaults = new BaseMaterial(cfg.material || {}) as T & {\r\n color?: THREE.Color;\r\n roughness?: number;\r\n metalness?: number;\r\n envMap?: THREE.Texture;\r\n envMapIntensity?: number;\r\n };\r\n\r\n if (defaults.color) uniforms.diffuse.value = defaults.color;\r\n if ('roughness' in defaults) uniforms.roughness.value = defaults.roughness;\r\n if ('metalness' in defaults) uniforms.metalness.value = defaults.metalness;\r\n if ('envMap' in defaults) uniforms.envMap.value = defaults.envMap;\r\n if ('envMapIntensity' in defaults) uniforms.envMapIntensity.value = defaults.envMapIntensity;\r\n\r\n Object.entries(cfg.uniforms ?? {}).forEach(([key, u]) => {\r\n uniforms[key] =\r\n u !== null && typeof u === 'object' && 'value' in u\r\n ? (u as THREE.IUniform)\r\n : ({ value: u } as THREE.IUniform);\r\n });\r\n\r\n let vert = `${cfg.header}\\n${cfg.vertexHeader ?? ''}\\n${baseVert}`;\r\n let frag = `${cfg.header}\\n${cfg.fragmentHeader ?? ''}\\n${baseFrag}`;\r\n\r\n for (const [inc, code] of Object.entries(cfg.vertex ?? {})) {\r\n vert = vert.replace(inc, `${inc}\\n${code}`);\r\n }\r\n for (const [inc, code] of Object.entries(cfg.fragment ?? {})) {\r\n frag = frag.replace(inc, `${inc}\\n${code}`);\r\n }\r\n\r\n const mat = new THREE.ShaderMaterial({\r\n defines: { ...baseDefines },\r\n uniforms,\r\n vertexShader: vert,\r\n fragmentShader: frag,\r\n lights: true,\r\n fog: !!cfg.material?.fog\r\n });\r\n\r\n return mat;\r\n}\r\n\r\nconst CanvasWrapper: FC<{ children: ReactNode }> = ({ children }) => (\r\n \r\n {children}\r\n \r\n);\r\n\r\nconst hexToNormalizedRGB = (hex: string): [number, number, number] => {\r\n const clean = hex.replace('#', '');\r\n const r = parseInt(clean.substring(0, 2), 16);\r\n const g = parseInt(clean.substring(2, 4), 16);\r\n const b = parseInt(clean.substring(4, 6), 16);\r\n return [r / 255, g / 255, b / 255];\r\n};\r\n\r\nconst noise = `\r\nfloat random (in vec2 st) {\r\n return fract(sin(dot(st.xy,\r\n vec2(12.9898,78.233)))*\r\n 43758.5453123);\r\n}\r\nfloat noise (in vec2 st) {\r\n vec2 i = floor(st);\r\n vec2 f = fract(st);\r\n float a = random(i);\r\n float b = random(i + vec2(1.0, 0.0));\r\n float c = random(i + vec2(0.0, 1.0));\r\n float d = random(i + vec2(1.0, 1.0));\r\n vec2 u = f * f * (3.0 - 2.0 * f);\r\n return mix(a, b, u.x) +\r\n (c - a)* u.y * (1.0 - u.x) +\r\n (d - b) * u.x * u.y;\r\n}\r\nvec4 permute(vec4 x){return mod(((x*34.0)+1.0)*x, 289.0);}\r\nvec4 taylorInvSqrt(vec4 r){return 1.79284291400159 - 0.85373472095314 * r;}\r\nvec3 fade(vec3 t) {return t*t*t*(t*(t*6.0-15.0)+10.0);}\r\nfloat cnoise(vec3 P){\r\n vec3 Pi0 = floor(P);\r\n vec3 Pi1 = Pi0 + vec3(1.0);\r\n Pi0 = mod(Pi0, 289.0);\r\n Pi1 = mod(Pi1, 289.0);\r\n vec3 Pf0 = fract(P);\r\n vec3 Pf1 = Pf0 - vec3(1.0);\r\n vec4 ix = vec4(Pi0.x, Pi1.x, Pi0.x, Pi1.x);\r\n vec4 iy = vec4(Pi0.yy, Pi1.yy);\r\n vec4 iz0 = Pi0.zzzz;\r\n vec4 iz1 = Pi1.zzzz;\r\n vec4 ixy = permute(permute(ix) + iy);\r\n vec4 ixy0 = permute(ixy + iz0);\r\n vec4 ixy1 = permute(ixy + iz1);\r\n vec4 gx0 = ixy0 / 7.0;\r\n vec4 gy0 = fract(floor(gx0) / 7.0) - 0.5;\r\n gx0 = fract(gx0);\r\n vec4 gz0 = vec4(0.5) - abs(gx0) - abs(gy0);\r\n vec4 sz0 = step(gz0, vec4(0.0));\r\n gx0 -= sz0 * (step(0.0, gx0) - 0.5);\r\n gy0 -= sz0 * (step(0.0, gy0) - 0.5);\r\n vec4 gx1 = ixy1 / 7.0;\r\n vec4 gy1 = fract(floor(gx1) / 7.0) - 0.5;\r\n gx1 = fract(gx1);\r\n vec4 gz1 = vec4(0.5) - abs(gx1) - abs(gy1);\r\n vec4 sz1 = step(gz1, vec4(0.0));\r\n gx1 -= sz1 * (step(0.0, gx1) - 0.5);\r\n gy1 -= sz1 * (step(0.0, gy1) - 0.5);\r\n vec3 g000 = vec3(gx0.x,gy0.x,gz0.x);\r\n vec3 g100 = vec3(gx0.y,gy0.y,gz0.y);\r\n vec3 g010 = vec3(gx0.z,gy0.z,gz0.z);\r\n vec3 g110 = vec3(gx0.w,gy0.w,gz0.w);\r\n vec3 g001 = vec3(gx1.x,gy1.x,gz1.x);\r\n vec3 g101 = vec3(gx1.y,gy1.y,gz1.y);\r\n vec3 g011 = vec3(gx1.z,gy1.z,gz1.z);\r\n vec3 g111 = vec3(gx1.w,gy1.w,gz1.w);\r\n vec4 norm0 = taylorInvSqrt(vec4(dot(g000,g000),dot(g010,g010),dot(g100,g100),dot(g110,g110)));\r\n g000 *= norm0.x; g010 *= norm0.y; g100 *= norm0.z; g110 *= norm0.w;\r\n vec4 norm1 = taylorInvSqrt(vec4(dot(g001,g001),dot(g011,g011),dot(g101,g101),dot(g111,g111)));\r\n g001 *= norm1.x; g011 *= norm1.y; g101 *= norm1.z; g111 *= norm1.w;\r\n float n000 = dot(g000, Pf0);\r\n float n100 = dot(g100, vec3(Pf1.x,Pf0.yz));\r\n float n010 = dot(g010, vec3(Pf0.x,Pf1.y,Pf0.z));\r\n float n110 = dot(g110, vec3(Pf1.xy,Pf0.z));\r\n float n001 = dot(g001, vec3(Pf0.xy,Pf1.z));\r\n float n101 = dot(g101, vec3(Pf1.x,Pf0.y,Pf1.z));\r\n float n011 = dot(g011, vec3(Pf0.x,Pf1.yz));\r\n float n111 = dot(g111, Pf1);\r\n vec3 fade_xyz = fade(Pf0);\r\n vec4 n_z = mix(vec4(n000,n100,n010,n110),vec4(n001,n101,n011,n111),fade_xyz.z);\r\n vec2 n_yz = mix(n_z.xy,n_z.zw,fade_xyz.y);\r\n float n_xyz = mix(n_yz.x,n_yz.y,fade_xyz.x);\r\n return 2.2 * n_xyz;\r\n}\r\n`;\r\n\r\ninterface BeamsProps {\r\n beamWidth?: number;\r\n beamHeight?: number;\r\n beamNumber?: number;\r\n lightColor?: string;\r\n speed?: number;\r\n noiseIntensity?: number;\r\n scale?: number;\r\n rotation?: number;\r\n}\r\n\r\nconst Beams: FC = ({\r\n beamWidth = 2,\r\n beamHeight = 15,\r\n beamNumber = 12,\r\n lightColor = '#ffffff',\r\n speed = 2,\r\n noiseIntensity = 1.75,\r\n scale = 0.2,\r\n rotation = 0\r\n}) => {\r\n const meshRef = useRef>(null!);\r\n\r\n const beamMaterial = useMemo(\r\n () =>\r\n extendMaterial(THREE.MeshStandardMaterial, {\r\n header: `\r\n varying vec3 vEye;\r\n varying float vNoise;\r\n varying vec2 vUv;\r\n varying vec3 vPosition;\r\n uniform float time;\r\n uniform float uSpeed;\r\n uniform float uNoiseIntensity;\r\n uniform float uScale;\r\n ${noise}`,\r\n vertexHeader: `\r\n float getPos(vec3 pos) {\r\n vec3 noisePos =\r\n vec3(pos.x * 0., pos.y - uv.y, pos.z + time * uSpeed * 3.) * uScale;\r\n return cnoise(noisePos);\r\n }\r\n vec3 getCurrentPos(vec3 pos) {\r\n vec3 newpos = pos;\r\n newpos.z += getPos(pos);\r\n return newpos;\r\n }\r\n vec3 getNormal(vec3 pos) {\r\n vec3 curpos = getCurrentPos(pos);\r\n vec3 nextposX = getCurrentPos(pos + vec3(0.01, 0.0, 0.0));\r\n vec3 nextposZ = getCurrentPos(pos + vec3(0.0, -0.01, 0.0));\r\n vec3 tangentX = normalize(nextposX - curpos);\r\n vec3 tangentZ = normalize(nextposZ - curpos);\r\n return normalize(cross(tangentZ, tangentX));\r\n }`,\r\n fragmentHeader: '',\r\n vertex: {\r\n '#include ': `transformed.z += getPos(transformed.xyz);`,\r\n '#include ': `objectNormal = getNormal(position.xyz);`\r\n },\r\n fragment: {\r\n '#include ': `\r\n float randomNoise = noise(gl_FragCoord.xy);\r\n gl_FragColor.rgb -= randomNoise / 15. * uNoiseIntensity;`\r\n },\r\n material: { fog: true },\r\n uniforms: {\r\n diffuse: new THREE.Color(...hexToNormalizedRGB('#000000')),\r\n time: { shared: true, mixed: true, linked: true, value: 0 },\r\n roughness: 0.3,\r\n metalness: 0.3,\r\n uSpeed: { shared: true, mixed: true, linked: true, value: speed },\r\n envMapIntensity: 10,\r\n uNoiseIntensity: noiseIntensity,\r\n uScale: scale\r\n }\r\n }),\r\n [speed, noiseIntensity, scale]\r\n );\r\n\r\n return (\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n );\r\n};\r\n\r\nfunction createStackedPlanesBufferGeometry(\r\n n: number,\r\n width: number,\r\n height: number,\r\n spacing: number,\r\n heightSegments: number\r\n): THREE.BufferGeometry {\r\n const geometry = new THREE.BufferGeometry();\r\n const numVertices = n * (heightSegments + 1) * 2;\r\n const numFaces = n * heightSegments * 2;\r\n const positions = new Float32Array(numVertices * 3);\r\n const indices = new Uint32Array(numFaces * 3);\r\n const uvs = new Float32Array(numVertices * 2);\r\n\r\n let vertexOffset = 0;\r\n let indexOffset = 0;\r\n let uvOffset = 0;\r\n const totalWidth = n * width + (n - 1) * spacing;\r\n const xOffsetBase = -totalWidth / 2;\r\n\r\n for (let i = 0; i < n; i++) {\r\n const xOffset = xOffsetBase + i * (width + spacing);\r\n const uvXOffset = Math.random() * 300;\r\n const uvYOffset = Math.random() * 300;\r\n\r\n for (let j = 0; j <= heightSegments; j++) {\r\n const y = height * (j / heightSegments - 0.5);\r\n const v0 = [xOffset, y, 0];\r\n const v1 = [xOffset + width, y, 0];\r\n positions.set([...v0, ...v1], vertexOffset * 3);\r\n\r\n const uvY = j / heightSegments;\r\n uvs.set([uvXOffset, uvY + uvYOffset, uvXOffset + 1, uvY + uvYOffset], uvOffset);\r\n\r\n if (j < heightSegments) {\r\n const a = vertexOffset,\r\n b = vertexOffset + 1,\r\n c = vertexOffset + 2,\r\n d = vertexOffset + 3;\r\n indices.set([a, b, c, c, b, d], indexOffset);\r\n indexOffset += 6;\r\n }\r\n vertexOffset += 2;\r\n uvOffset += 4;\r\n }\r\n }\r\n\r\n geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));\r\n geometry.setAttribute('uv', new THREE.BufferAttribute(uvs, 2));\r\n geometry.setIndex(new THREE.BufferAttribute(indices, 1));\r\n geometry.computeVertexNormals();\r\n return geometry;\r\n}\r\n\r\nconst MergedPlanes = forwardRef<\r\n THREE.Mesh,\r\n {\r\n material: THREE.ShaderMaterial;\r\n width: number;\r\n count: number;\r\n height: number;\r\n }\r\n>(({ material, width, count, height }, ref) => {\r\n const mesh = useRef>(null!);\r\n useImperativeHandle(ref, () => mesh.current);\r\n const geometry = useMemo(\r\n () => createStackedPlanesBufferGeometry(count, width, height, 0, 100),\r\n [count, width, height]\r\n );\r\n useFrame((_, delta) => {\r\n mesh.current.material.uniforms.time.value += 0.1 * delta;\r\n });\r\n return ;\r\n});\r\nMergedPlanes.displayName = 'MergedPlanes';\r\n\r\nconst PlaneNoise = forwardRef<\r\n THREE.Mesh,\r\n {\r\n material: THREE.ShaderMaterial;\r\n width: number;\r\n count: number;\r\n height: number;\r\n }\r\n>((props, ref) => (\r\n \r\n));\r\nPlaneNoise.displayName = 'PlaneNoise';\r\n\r\nconst DirLight: FC<{ position: [number, number, number]; color: string }> = ({ position, color }) => {\r\n const dir = useRef(null!);\r\n useEffect(() => {\r\n if (!dir.current) return;\r\n const cam = dir.current.shadow.camera as THREE.Camera & {\r\n top: number;\r\n bottom: number;\r\n left: number;\r\n right: number;\r\n far: number;\r\n };\r\n cam.top = 24;\r\n cam.bottom = -24;\r\n cam.left = -24;\r\n cam.right = 24;\r\n cam.far = 64;\r\n dir.current.shadow.bias = -0.004;\r\n }, []);\r\n return ;\r\n};\r\n\r\nexport default Beams;\r\n", "type": "registry:component" }, { "path": "public/ts/default/src/ts-default/Backgrounds/Beams/Beams.css", "content": ".beams-container {\r\n position: relative;\r\n width: 100%;\r\n height: 100%;\r\n}\r\n", "type": "registry:item" } ] }