===== COMPONENT: 3d-folder ===== Title: 3D Folder Description: A 3D animated folder component that reveals project cards on hover and expands into a smooth image lightbox with keyboard navigation. --- file: gammaui/3d-folder.tsx --- "use client" import { forwardRef, useCallback, useEffect, useLayoutEffect, useRef, useState, } from "react" import type React from "react" import { IconChevronLeft, IconChevronRight, IconExternalLink, IconX, } from "@tabler/icons-react" import { cn } from "@/lib/utils" interface Project { id: string image: string title: string } interface AnimatedFolderProps { title: string projects: Project[] className?: string } export function AnimatedFolder({ title, projects, className, }: AnimatedFolderProps) { const [isOpened, setIsOpened] = useState(false) const [selectedIndex, setSelectedIndex] = useState(null) const [sourceRect, setSourceRect] = useState(null) const [hiddenCardId, setHiddenCardId] = useState(null) const cardRefs = useRef<(HTMLDivElement | null)[]>([]) const handleProjectClick = (project: Project, index: number) => { const cardEl = cardRefs.current[index] if (cardEl) { setSourceRect(cardEl.getBoundingClientRect()) } setSelectedIndex(index) setHiddenCardId(project.id) } const handleCloseLightbox = () => { setSelectedIndex(null) setSourceRect(null) } const handleCloseComplete = () => { setHiddenCardId(null) } const handleNavigate = (newIndex: number) => { setSelectedIndex(newIndex) setHiddenCardId(projects[newIndex]?.id || null) } return ( <>
{/* Subtle background glow on hover */}
setIsOpened((prev) => !prev)} > {/* Folder back layer - z-index 10 */}
{/* Folder tab - z-index 10 */}
{/* Project cards - z-index 20, between back and front */}
{projects.slice(0, 3).map((project, index) => ( { cardRefs.current[index] = el }} image={project.image} title={project.title} delay={index * 80} isVisible={isOpened} index={index} onClick={() => handleProjectClick(project, index)} isSelected={hiddenCardId === project.id} /> ))}
{/* Folder front layer - z-index 30 */}
{/* Folder shine effect - z-index 31 */}
{/* Folder title */}

{title}

{/* Project count */}

{projects.length} Documents

{/* Hover hint */}
Click to explore
) } interface Project { id: string image: string title: string } interface ImageLightboxProps { projects: Project[] currentIndex: number isOpen: boolean onClose: () => void sourceRect: DOMRect | null onCloseComplete?: () => void onNavigate: (index: number) => void } export function ImageLightbox({ projects, currentIndex, isOpen, onClose, sourceRect, onCloseComplete, onNavigate, }: ImageLightboxProps) { const [animationPhase, setAnimationPhase] = useState< "initial" | "animating" | "complete" >("initial") const [isClosing, setIsClosing] = useState(false) const [shouldRender, setShouldRender] = useState(false) const [internalIndex, setInternalIndex] = useState(currentIndex) const [prevIndex, setPrevIndex] = useState(currentIndex) const [isSliding, setIsSliding] = useState(false) const [slideDirection, setSlideDirection] = useState<"left" | "right">( "right" ) const containerRef = useRef(null) const totalProjects = projects.length const hasNext = internalIndex < totalProjects - 1 const hasPrev = internalIndex > 0 const currentProject = projects[internalIndex] useEffect(() => { if (isOpen && currentIndex !== internalIndex && !isSliding) { const direction = currentIndex > internalIndex ? "left" : "right" setSlideDirection(direction) setPrevIndex(internalIndex) setIsSliding(true) const timer = setTimeout(() => { setInternalIndex(currentIndex) setIsSliding(false) }, 400) return () => clearTimeout(timer) } }, [currentIndex, isOpen]) useEffect(() => { if (isOpen) { setInternalIndex(currentIndex) setPrevIndex(currentIndex) setIsSliding(false) } }, [isOpen]) const navigateNext = useCallback(() => { if (internalIndex >= totalProjects - 1 || isSliding) return onNavigate(internalIndex + 1) }, [internalIndex, totalProjects, isSliding, onNavigate]) const navigatePrev = useCallback(() => { if (internalIndex <= 0 || isSliding) return onNavigate(internalIndex - 1) }, [internalIndex, isSliding, onNavigate]) const handleClose = useCallback(() => { setIsClosing(true) onClose() setTimeout(() => { setIsClosing(false) setShouldRender(false) setAnimationPhase("initial") onCloseComplete?.() }, 400) }, [onClose, onCloseComplete]) useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { if (!isOpen) return if (e.key === "Escape") handleClose() if (e.key === "ArrowRight") navigateNext() if (e.key === "ArrowLeft") navigatePrev() } window.addEventListener("keydown", handleKeyDown) if (isOpen) { document.body.style.overflow = "hidden" } return () => { window.removeEventListener("keydown", handleKeyDown) document.body.style.overflow = "" } }, [isOpen, handleClose, navigateNext, navigatePrev]) useLayoutEffect(() => { if (isOpen && sourceRect) { setShouldRender(true) setAnimationPhase("initial") setIsClosing(false) requestAnimationFrame(() => { requestAnimationFrame(() => { setAnimationPhase("animating") }) }) const timer = setTimeout(() => { setAnimationPhase("complete") }, 500) return () => clearTimeout(timer) } }, [isOpen, sourceRect]) const handleDotClick = (idx: number) => { if (isSliding || idx === internalIndex) return onNavigate(idx) } if (!shouldRender || !currentProject) return null const getInitialStyles = (): React.CSSProperties => { if (!sourceRect) return {} const viewportWidth = window.innerWidth const viewportHeight = window.innerHeight const targetWidth = Math.min(768, viewportWidth - 64) const targetHeight = Math.min(viewportHeight * 0.85, 600) const targetX = (viewportWidth - targetWidth) / 2 const targetY = (viewportHeight - targetHeight) / 2 const scaleX = sourceRect.width / targetWidth const scaleY = sourceRect.height / targetHeight const scale = Math.max(scaleX, scaleY) const translateX = sourceRect.left + sourceRect.width / 2 - (targetX + targetWidth / 2) const translateY = sourceRect.top + sourceRect.height / 2 - (targetY + targetHeight / 2) return { transform: `translate(${translateX}px, ${translateY}px) scale(${scale})`, opacity: 1, } } const getFinalStyles = (): React.CSSProperties => { return { transform: "translate(0, 0) scale(1)", opacity: 1, } } const currentStyles = animationPhase === "initial" && !isClosing ? getInitialStyles() : getFinalStyles() return (
{/* Close button */}
e.stopPropagation()} style={{ ...currentStyles, transform: isClosing ? "translate(0, 0) scale(0.95)" : currentStyles.transform, transition: animationPhase === "initial" && !isClosing ? "none" : "transform 400ms cubic-bezier(0.16, 1, 0.3, 1), opacity 400ms ease-out", transformOrigin: "center center", }} >
{projects.map((project, idx) => ( {project.title} ))}
{/* Subtle vignette effect */}

{currentProject?.title}

{" "} to navigate

{projects.map((_, idx) => (
) } interface ProjectCardProps { image: string title: string delay: number isVisible: boolean index: number onClick: () => void isSelected: boolean } export const ProjectCard = forwardRef( ({ image, title, delay, isVisible, index, onClick, isSelected }, ref) => { const rotations = [-12, 0, 12] const translations = [-55, 0, 55] return (
{ e.stopPropagation() onClick() }} > {title}

{title}

) } ) ProjectCard.displayName = "ProjectCard" ===== EXAMPLE: 3d-folder-demo ===== Title: 3D Folder Demo --- file: example/3d-folder-demo.tsx --- import { AnimatedFolder } from "@/registry/gammaui/3d-folder" const portfolioData = [ { title: "Folder Name", projects: [ { id: "1", image: "https://images.pexels.com/photos/29531041/pexels-photo-29531041.jpeg", title: "Tokyo", }, { id: "2", image: "https://images.pexels.com/photos/569416/pexels-photo-569416.jpeg", title: "Berlin", }, { id: "3", image: "https://images.pexels.com/photos/1461974/pexels-photo-1461974.jpeg", title: "Pris", }, ], }, ] export default function AnimatedFolderDemo() { return (
{/* Main content */}
{portfolioData.map((folder) => ( ))}
) } ===== COMPONENT: activity-dropdown ===== Title: Activity Dropdown Description: A modern activity/notification dropdown with smooth animations and responsive motion. --- file: gammaui/activity-dropdown.tsx --- "use client" import type React from "react" import { useState } from "react" import { IconAward, IconBell, IconCalendar, IconCheckbox, IconChevronUp, IconMessageCircle, IconTag, } from "@tabler/icons-react" import { cn } from "@/lib/utils" interface Activity { id: number icon: React.ReactNode iconBg: string title: string description: string time: string } const activities: Activity[] = [ { id: 1, icon: , iconBg: "bg-neutral-700 dark:bg-neutral-700 bg-neutral-200", title: "New Message!", description: "Sarah sent you a message.", time: "Just Now", }, { id: 2, icon: , iconBg: "bg-neutral-700 dark:bg-neutral-700 bg-neutral-200", title: "Level Up!", description: "You've unlocked a new achievement.", time: "2 min ago", }, { id: 3, icon: , iconBg: "bg-neutral-700 dark:bg-neutral-700 bg-neutral-200", title: "Reminder: Meeting Today", description: "Your team meeting starts in 30 minutes.", time: "3 hour ago", }, { id: 4, icon: , iconBg: "bg-neutral-700 dark:bg-neutral-700 bg-neutral-200", title: "Special Offer!", description: "Save 20% off on subscription upgrade.", time: "12 hours ago", }, { id: 5, icon: , iconBg: "bg-neutral-700 dark:bg-neutral-700 bg-neutral-200", title: "Task Assigned!", description: "A new task is awaiting your action.", time: "Yesterday", }, ] export default function ActivityDropdown() { const [isOpen, setIsOpen] = useState(false) return (
setIsOpen(!isOpen)} > {/* Header */}

5 New Activities

What's happening around you

{/* Activity List */}
{activities.map((activity, index) => (
{activity.icon}

{activity.title}

{activity.description}

{activity.time}
))}
) } ===== EXAMPLE: activity-dropdown-demo ===== Title: Activity Dropdown Demo --- file: example/activity-dropdown-demo.tsx --- import ActivityDropdown from "@/registry/gammaui/activity-dropdown" export default function ActivityDropdownDemo() { return (
) } ===== COMPONENT: animated-list ===== Title: Animated List Description: A dynamic list component with smooth animations that forms a column and scrolls through items continuously. --- file: gammaui/animated-list.tsx --- "use client" import React, { useEffect, useMemo, useState } from "react" import { AnimatePresence, motion, useAnimationControls } from "motion/react" import { cn } from "@/lib/utils" type AnimationPhase = "idle" | "forming_column" | "scrolling_down" | "resetting" interface AnimatedListProps { children: React.ReactNode className?: string stackGap?: number columnGap?: number scaleFactor?: number scrollDownDuration?: number formationDuration?: number } interface AnimatedListItemProps { children: React.ReactNode className?: string index: number listLength: number stackGap?: number columnGap?: number scaleFactor?: number } function InternalAnimatedListItem({ children, className, index, listLength, animationPhase, onFormationComplete, stackGap = 10, columnGap = 100, scaleFactor = 0.1, formationDuration = 1, visibleItemsCount = 4, resetSpringStiffness = 120, resetSpringDamping = 20, }: AnimatedListItemProps & { animationPhase: AnimationPhase onFormationComplete?: () => void formationDuration: number visibleItemsCount: number resetSpringStiffness: number resetSpringDamping: number }) { const reverseIndex = listLength - 1 - index const isVisible = reverseIndex < visibleItemsCount const lastItemOffset = (listLength - 1) * columnGap const isLastItem = index === listLength - 1 let initialScale = 1 let columnScale = 0.8 if (typeof window !== "undefined") { const w = window.innerWidth if (w < 480) { // 📱 Mobile initialScale = 0.5 columnScale = 0.8 } else if (w < 768) { // 📲 Tablet initialScale = 0.45 columnScale = 0.7 } else { // 🖥 Desktop (default) initialScale = 1 columnScale = 0.8 } } const itemVariants = { initial: { scale: initialScale + index * scaleFactor, y: reverseIndex * stackGap, opacity: isVisible ? 1 : 0, }, column: { scale: columnScale, y: index * columnGap - lastItemOffset, opacity: 1, }, } const target = animationPhase === "idle" || animationPhase === "resetting" ? "initial" : "column" const getTransition = () => { if (animationPhase === "resetting") { return { type: "spring" as const, stiffness: resetSpringStiffness, damping: resetSpringDamping, } } else { return { duration: formationDuration, ease: [0.4, 0, 0.2, 1] as const } } } const handleAnimationComplete = (definition: string) => { if ( isLastItem && definition === "column" && animationPhase === "forming_column" ) { onFormationComplete?.() } } return ( {children} ) } export function AnimatedList({ children, className, stackGap = 20, columnGap = 85, scaleFactor = 0.05, scrollDownDuration = 5, formationDuration = 1, }: AnimatedListProps) { const initialDelayValue = 500 const loopPauseDurationValue = 100 const listResetSpringStiffness = 100 const listResetSpringDamping = 25 const itemResetSpringStiffness = 120 const itemResetSpringDamping = 20 const visibleItemsCountValue = 4 const [animationPhase, setAnimationPhase] = useState("idle") const listControls = useAnimationControls() const childrenArray = useMemo( () => React.Children.toArray(children), [children] ) const listLength = childrenArray.length const totalHeight = listLength * columnGap useEffect(() => { let timer: NodeJS.Timeout if (animationPhase === "idle") { timer = setTimeout( () => { setAnimationPhase("forming_column") }, animationPhase === "idle" ? loopPauseDurationValue : initialDelayValue ) } return () => clearTimeout(timer) }, [animationPhase, loopPauseDurationValue, initialDelayValue]) const handleFormationComplete = () => { if (animationPhase === "forming_column") setAnimationPhase("scrolling_down") } const handleScrollDownComplete = () => { if (animationPhase === "scrolling_down") setAnimationPhase("resetting") } const handleScrollUpComplete = () => { if (animationPhase === "resetting") setAnimationPhase("idle") } useEffect(() => { if (animationPhase === "scrolling_down") { listControls.start({ y: totalHeight, transition: { duration: scrollDownDuration, ease: [0.4, 0, 0.2, 1] as const, }, }) } else if (animationPhase === "resetting") { listControls.start({ y: 0, transition: { type: "spring" as const, stiffness: listResetSpringStiffness, damping: listResetSpringDamping, }, }) } else { listControls.set({ y: 0 }) } }, [ animationPhase, listControls, totalHeight, scrollDownDuration, listResetSpringStiffness, listResetSpringDamping, ]) const handleListAnimationComplete = (definition: { y?: number }) => { if (definition.y === totalHeight && animationPhase === "scrolling_down") { handleScrollDownComplete() } else if (definition.y === 0 && animationPhase === "resetting") { handleScrollUpComplete() } } return ( {childrenArray.map((child, index) => ( {child} ))} ) } ===== EXAMPLE: animated-list-demo ===== Title: Animated List Demo --- file: example/animated-list-demo.tsx --- import { AnimatedList } from "@/registry/gammaui/animated-list" export function AnimatedListDemo() { const notifications = [ { name: "Location", message: "Thomas has arrived home", time: "2h ago" }, { name: "Fitness", message: "Daily step goal reached!", time: "1h ago" }, { name: "Calendar", message: "Team meeting in 30 minutes", time: "45m ago", }, { name: "Tasks", message: "3 tasks due today", time: "1d ago" }, { name: "Health", message: "Heart rate elevated", time: "3h ago" }, { name: "Email", message: "New message from manager", time: "5m ago" }, { name: "Social", message: "Video got 1000 likes!", time: "2d ago" }, { name: "Family", message: "How are you doing?", time: "1w ago" }, { name: "Friends", message: "Coffee tomorrow?", time: "2d ago" }, { name: "Movies", message: "Did you see the new movie?", time: "4h ago" }, ] return (
{notifications.map((notification, index) => (
{notification.name.charAt(0)}
{notification.name} {notification.time}
{notification.message}
))}
) } ===== COMPONENT: aurora-glass ===== Title: Aurora Glass Description: A shimmering glass tile background with ripple layers, bevel shading, and chromatic spread. --- file: gammaui/aurora-glass.tsx --- "use client" import { useEffect, useRef } from "react" import type { ReactNode } from "react" /** * AuroraGlass * A shimmering background of colorful glass tiles, rendered with raw WebGL2. * * A handful of broad directional ripple layers sweep across the whole tile * lattice in one continuous field (sampled per-fragment in grid-space), so * the highlight band flows smoothly from tile to tile rather than each tile * animating independently. Each tile's rounded-rect bevel normal warps the * sample point near its edges, giving a refraction-like bend as the band * crosses every tile. * * Props * ---------------------------------------------------------------------- * width string | number "100%" Container width * height string | number "100%" Container height * className string "" Additional CSS classes * children ReactNode undefined Content rendered above the effect * speed number 1 Animation speed multiplier (0-3) * tileDensity number 4 Tiles across the surface (1-16) * rippleLayers number 6 Stacked ripple layers (1-8) * warpStrength number 0.33 Per-tile inverse-distance warp (0-0.6) * bandSharpness number 3 Highlight peak sharpness (0.5-10) * chromaticSpread number 0 Per-channel separation (0-1) * colorA string "#1E00FF" Gradient stop (deep color) * colorB string "#D765E6" Gradient stop (bright color) * backgroundColor string "#FFFFFF" Fill where the field is dark * opacity number 1 Master alpha (0-1) * dpr number 1.5 Max device pixel ratio (1-3) * * Usage * *

Your content, rendered above the effect

*
*/ const MAX_RIPPLE_LAYERS = 8 const VERTEX_SRC = `#version 300 es in vec2 a_position; void main() { gl_Position = vec4(a_position, 0.0, 1.0); } ` const FRAGMENT_SRC = `#version 300 es precision highp float; uniform vec2 u_resolution; uniform float u_time; uniform float u_speed; uniform float u_tileDensity; uniform int u_rippleLayers; uniform float u_warpStrength; uniform float u_bandSharpness; uniform float u_chromaticSpread; uniform vec3 u_colorA; uniform vec3 u_colorB; uniform vec3 u_backgroundColor; uniform float u_opacity; out vec4 fragColor; float hash21(vec2 p) { p = fract(p * vec2(123.34, 456.21)); p += dot(p, p + 45.32); return fract(p.x * p.y); } vec2 hash22(vec2 p) { float n = hash21(p); float n2 = hash21(p + 17.13); return vec2(n, n2); } float sdRoundRect(vec2 p, vec2 halfSize, float r) { vec2 q = abs(p) - halfSize + r; return length(max(q, 0.0)) + min(max(q.x, q.y), 0.0) - r; } // Height field for the tile surface: flat in the middle, with a wide // rounded bevel that rises toward the border -- like a slightly domed pane // of glass set in a frame. d is the rounded-rect SDF (negative inside, 0 at // the border). Near the border height is highest; deep inside it flattens. float tileHeight(float d, float r) { float bevelWidth = r * 4.0; float distFromEdge = clamp(-d / bevelWidth, 0.0, 1.0); float bevel = 1.0 - distFromEdge; return bevel * bevel * (3.0 - 2.0 * bevel); } // One directional ripple band. Layer 0-2 are broad, slow, coherent sweeps // (the main visible "ribbons"); layers beyond that add progressively finer, // dimmer secondary detail so increasing rippleLayers enriches the texture // without diluting the primary bands. float rippleLayer(vec2 guv, float t, int i) { float fi = float(i); float dirAngle = 0.55 + fi * 0.7; vec2 dir = vec2(cos(dirAngle), sin(dirAngle)); vec2 perp = vec2(-dir.y, dir.x); float freqBase = 0.16 + fract(fi * 0.31) * 0.1; float freq = freqBase * (1.0 + fi * 0.22); float speedVar = 0.35 + fract(fi * 0.53) * 0.35; float phase = fi * 2.7; float bow = 1.6 + fract(fi * 0.19) * 1.4; float along = dot(guv, dir); float across = dot(guv, perp); float bend = sin(along * freq + t * speedVar + phase) * bow + cos(t * speedVar * 0.5 + phase * 1.3) * 1.2; float d = abs(across - bend); float sharpness = 6.0 + fi * 1.5; float core = exp(-d * d * sharpness); float halo = exp(-d * d * 1.1) * 0.15; float weight = 1.0 / (1.0 + fi * 0.55); return (core + halo) * weight; } // Sum of N ripple layers sampled at this grid-space position. The field is // continuous across the whole grid (not per-tile), so highlights flow // smoothly from one tile into its neighbor. float lightField(vec2 guv, float t) { float field = 0.0; for (int i = 0; i < ${MAX_RIPPLE_LAYERS}; i++) { if (i >= u_rippleLayers) break; field += rippleLayer(guv, t, i); } return field; } void main() { vec2 fragCoord = gl_FragCoord.xy; vec2 res = u_resolution; // tileDensity = how many tile cells fit across the SHORTER side of the // surface, so density reads consistently regardless of aspect ratio. float shortSide = min(res.x, res.y); float cell = shortSide / max(u_tileDensity, 1.0); float gap = cell * 0.06; vec2 gridPos = fragCoord / cell; vec2 cellId = floor(gridPos); vec2 localUv = fract(gridPos) - 0.5; vec2 localPx = localUv * cell; float tileHalf = (cell - gap) * 0.5; float radius = tileHalf * 0.32; float d = sdRoundRect(localPx, vec2(tileHalf), radius); if (d > 0.0) { fragColor = vec4(u_backgroundColor, u_opacity); return; } vec2 rnd = hash22(cellId); // --- Always-on ambient bevel shading --- // A true 3D surface normal derived from a domed height field, lit by a // fixed upper-left studio light. This runs on EVERY tile regardless of // the colorful streak, so dark tiles still read as curved glass rather // than flat black squares. float epsH = 1.2; float dCx1 = sdRoundRect(localPx + vec2(epsH, 0.0), vec2(tileHalf), radius); float dCx0 = sdRoundRect(localPx - vec2(epsH, 0.0), vec2(tileHalf), radius); float dCy1 = sdRoundRect(localPx + vec2(0.0, epsH), vec2(tileHalf), radius); float dCy0 = sdRoundRect(localPx - vec2(0.0, epsH), vec2(tileHalf), radius); float hR = tileHeight(dCx1, radius); float hL = tileHeight(dCx0, radius); float hU = tileHeight(dCy1, radius); float hD = tileHeight(dCy0, radius); vec2 heightGrad = vec2(hR - hL, hU - hD) / (2.0 * epsH); vec3 surfaceNormal = normalize(vec3(-heightGrad * 4.0, 1.0)); vec3 lightDir = normalize(vec3(-0.45, 0.55, 0.7)); vec3 viewDir = vec3(0.0, 0.0, 1.0); vec3 halfDir = normalize(lightDir + viewDir); float diffuse = max(dot(surfaceNormal, lightDir), 0.0); float specular = pow(max(dot(surfaceNormal, halfDir), 0.0), 14.0); // Tinted by the background color rather than neutral gray, so it reads as // a hint of glass curvature rather than a separate plastic/keycap layer. vec3 ambientBevel = u_backgroundColor * diffuse * 0.16 + mix(u_backgroundColor, vec3(0.7, 0.65, 0.78), 0.5) * specular * 0.22; // Bevel normal from the rounded-rect SDF gradient, used only to bend // (warp) the light sample point near tile edges -- a refraction cue. float eps = 1.5; float dx = sdRoundRect(localPx + vec2(eps, 0.0), vec2(tileHalf), radius) - sdRoundRect(localPx - vec2(eps, 0.0), vec2(tileHalf), radius); float dy = sdRoundRect(localPx + vec2(0.0, eps), vec2(tileHalf), radius) - sdRoundRect(localPx - vec2(0.0, eps), vec2(tileHalf), radius); vec2 gradDir = vec2(dx, dy) / (2.0 * eps); float rim = smoothstep(-radius * 1.6, 0.0, d); // Per-tile inverse-distance warp: the closer to the tile edge, the more // the sample point bends, like light refracting through curved glass. float distFromCenter = length(localPx) / max(tileHalf, 0.001); float invDist = 1.0 / max(1.0 - distFromCenter * 0.85, 0.15); vec2 warpOffset = gradDir * rim * u_warpStrength * invDist * 0.4; float t = u_time * u_speed; vec2 sampleUv = gridPos + warpOffset; float fieldR = lightField(sampleUv + vec2(u_chromaticSpread * 0.6, 0.0), t); float fieldG = lightField(sampleUv, t); float fieldB = lightField(sampleUv - vec2(u_chromaticSpread * 0.6, 0.0), t); float shaped = pow(clamp(fieldG, 0.0, 1.6), u_bandSharpness); float shapedR = pow(clamp(fieldR, 0.0, 1.6), u_bandSharpness); float shapedB = pow(clamp(fieldB, 0.0, 1.6), u_bandSharpness); float variance = mix(0.75, 1.15, rnd.y); shaped *= variance; shapedR *= variance; shapedB *= variance; vec3 gradColor = mix(u_colorA, u_colorB, clamp(shaped, 0.0, 1.0)); vec3 lit = gradColor * shaped; float chromaMix = clamp(u_chromaticSpread * 3.0, 0.0, 1.0); lit.r = mix(lit.r, gradColor.r * shapedR, chromaMix); lit.b = mix(lit.b, gradColor.b * shapedB, chromaMix); vec3 color = u_backgroundColor * 0.03 + ambientBevel + lit; // Soft recessed contact shadow right at the rounded border float innerEdge = smoothstep(-2.5, 0.0, d); color *= (1.0 - innerEdge * 0.55); fragColor = vec4(color, u_opacity); } ` function compileShader( gl: WebGL2RenderingContext, type: number, source: string ): WebGLShader { const shader = gl.createShader(type)! gl.shaderSource(shader, source) gl.compileShader(shader) if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { const info = gl.getShaderInfoLog(shader) gl.deleteShader(shader) throw new Error("Shader compile error: " + info) } return shader } function createProgram( gl: WebGL2RenderingContext, vertexSrc: string, fragmentSrc: string ): WebGLProgram { const vs = compileShader(gl, gl.VERTEX_SHADER, vertexSrc) const fs = compileShader(gl, gl.FRAGMENT_SHADER, fragmentSrc) const program = gl.createProgram()! gl.attachShader(program, vs) gl.attachShader(program, fs) gl.linkProgram(program) if (!gl.getProgramParameter(program, gl.LINK_STATUS)) { const info = gl.getProgramInfoLog(program) gl.deleteProgram(program) throw new Error("Program link error: " + info) } gl.deleteShader(vs) gl.deleteShader(fs) return program } function hexToRgb(hex: string) { const clean = (hex || "#000000").replace("#", "") const full = clean.length === 3 ? clean .split("") .map((c) => c + c) .join("") : clean const bigint = parseInt(full, 16) || 0 const r = ((bigint >> 16) & 255) / 255 const g = ((bigint >> 8) & 255) / 255 const b = (bigint & 255) / 255 return [r, g, b] } const clamp = (value: number, min: number, max: number) => Math.min(max, Math.max(min, value)) interface AuroraGlassProps { width?: string | number height?: string | number className?: string children?: ReactNode speed?: number tileDensity?: number rippleLayers?: number warpStrength?: number bandSharpness?: number chromaticSpread?: number colorA?: string colorB?: string backgroundColor?: string opacity?: number dpr?: number } export default function AuroraGlass({ width = "100%", height = "100%", className = "", children, speed = 1, tileDensity = 4, rippleLayers = 6, warpStrength = 0.33, bandSharpness = 3, chromaticSpread = 0, colorA = "#1E00FF", colorB = "#D765E6", backgroundColor = "#FFFFFF", opacity = 1, dpr = 1.5, }: AuroraGlassProps) { const canvasRef = useRef(null) const rafRef = useRef(0) // Keep the latest prop values in a ref so the render loop (started once) // always reads current values without needing to be torn down and // rebuilt on every prop change. interface PropsSnapshot { speed: number tileDensity: number rippleLayers: number warpStrength: number bandSharpness: number chromaticSpread: number colorA: string colorB: string backgroundColor: string opacity: number } const propsRef = useRef({} as PropsSnapshot) propsRef.current = { speed, tileDensity: clamp(tileDensity, 1, 16), rippleLayers: Math.round(clamp(rippleLayers, 1, MAX_RIPPLE_LAYERS)), warpStrength: clamp(warpStrength, 0, 0.6), bandSharpness: clamp(bandSharpness, 0.5, 10), chromaticSpread: clamp(chromaticSpread, 0, 1), colorA, colorB, backgroundColor, opacity: clamp(opacity, 0, 1), } const dprRef = useRef(clamp(dpr, 1, 3)) dprRef.current = clamp(dpr, 1, 3) useEffect(() => { const canvas = canvasRef.current if (!canvas) return const gl = canvas.getContext("webgl2", { antialias: true, alpha: true }) if (!gl) { console.warn( "WebGL2 is not supported in this browser; AuroraGlass cannot render." ) return } let program: WebGLProgram try { program = createProgram(gl, VERTEX_SRC, FRAGMENT_SRC) } catch (err) { console.error(err) return } gl.useProgram(program) gl.enable(gl.BLEND) gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA) const positionBuffer = gl.createBuffer() gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer) gl.bufferData( gl.ARRAY_BUFFER, new Float32Array([-1, -1, 3, -1, -1, 3]), gl.STATIC_DRAW ) const positionLoc = gl.getAttribLocation(program, "a_position") gl.enableVertexAttribArray(positionLoc) gl.vertexAttribPointer(positionLoc, 2, gl.FLOAT, false, 0, 0) const u: Record = {} ;[ "u_resolution", "u_time", "u_speed", "u_tileDensity", "u_rippleLayers", "u_warpStrength", "u_bandSharpness", "u_chromaticSpread", "u_colorA", "u_colorB", "u_backgroundColor", "u_opacity", ].forEach((name) => { u[name] = gl.getUniformLocation(program, name) }) let w = 0 let h = 0 function resize() { const cv = canvas! const parent = cv.parentElement const rect = parent ? parent.getBoundingClientRect() : cv.getBoundingClientRect() const ratio = dprRef.current w = Math.max(1, Math.floor(rect.width * ratio)) h = Math.max(1, Math.floor(rect.height * ratio)) if (cv.width !== w || cv.height !== h) { cv.width = w cv.height = h } gl!.viewport(0, 0, w, h) } const resizeObserver = new ResizeObserver(resize) if (canvas.parentElement) resizeObserver.observe(canvas.parentElement) resize() const start = performance.now() function frame(now: number) { const t = (now - start) / 1000 const p = propsRef.current const g = gl! g.useProgram(program) g.clearColor(0, 0, 0, 0) g.clear(g.COLOR_BUFFER_BIT) g.uniform2f(u.u_resolution, w, h) g.uniform1f(u.u_time, t) g.uniform1f(u.u_speed, p.speed) g.uniform1f(u.u_tileDensity, p.tileDensity) g.uniform1i(u.u_rippleLayers, p.rippleLayers) g.uniform1f(u.u_warpStrength, p.warpStrength) g.uniform1f(u.u_bandSharpness, p.bandSharpness) g.uniform1f(u.u_chromaticSpread, p.chromaticSpread) g.uniform3fv(u.u_colorA, hexToRgb(p.colorA)) g.uniform3fv(u.u_colorB, hexToRgb(p.colorB)) g.uniform3fv(u.u_backgroundColor, hexToRgb(p.backgroundColor)) g.uniform1f(u.u_opacity, p.opacity) g.drawArrays(g.TRIANGLES, 0, 3) rafRef.current = requestAnimationFrame(frame) } rafRef.current = requestAnimationFrame(frame) return () => { cancelAnimationFrame(rafRef.current) resizeObserver.disconnect() gl.deleteProgram(program) gl.deleteBuffer(positionBuffer) } // Render loop is started once; per-frame values are read from propsRef // and dprRef so prop changes don't require tearing down the GL context. }, []) return (
{children != null && (
{children}
)}
) } ===== EXAMPLE: aurora-glass-demo ===== Title: Aurora Glass Demo --- file: example/aurora-glass-demo.tsx --- "use client" import AuroraGlass from "@/registry/gammaui/aurora-glass" export const AuroraGlassDemo = () => { return (
) } ===== COMPONENT: badge ===== Title: Badge Description: A flexible and customizable Badge component for labels, statuses, and indicators. --- file: gammaui/badge.tsx --- import * as React from "react" import { Slot } from "@radix-ui/react-slot" import { cva, type VariantProps } from "class-variance-authority" import { cn } from "@/lib/utils" const badgeVariants = cva( "inline-flex items-center justify-center rounded-full border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden", { variants: { variant: { default: "border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90", secondary: "border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90", destructive: "border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60", outline: "text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground", }, }, defaultVariants: { variant: "default", }, } ) function Badge({ className, variant, asChild = false, ...props }: React.ComponentProps<"span"> & VariantProps & { asChild?: boolean }) { const Comp = asChild ? Slot : "span" return ( ) } export { Badge, badgeVariants } ===== EXAMPLE: badge-demo ===== Title: Badge Demo --- file: example/badge-demo.tsx --- import { IconCircleCheck } from "@tabler/icons-react" import { Badge } from "@/registry/gammaui/badge" export function BadgeDemo() { return (
Badge Secondary Destructive Outline
Verified 8 99 20+
) } ===== COMPONENT: border-button ===== Title: Border Button Description: A reactive border animation button that follows cursor movement using Framer Motion and CSS masks. --- file: gammaui/border-button.tsx --- "use client" import { MouseEvent, useState } from "react" import { motion } from "motion/react" interface Position { x: number y: number } interface BorderButtonProps { label: string } export default function BorderButton({ label }: BorderButtonProps) { const [hoveredIndex, setHoveredIndex] = useState(null) const [positions, setPositions] = useState>({}) const [opacities, setOpacities] = useState>({}) const handleMouseMove = (e: MouseEvent, index: number) => { const rect = e.currentTarget.getBoundingClientRect() setPositions((prev) => ({ ...prev, [index]: { x: e.clientX - rect.left, y: e.clientY - rect.top, }, })) } const handleMouseEnter = (index: number) => { setHoveredIndex(index) setOpacities((prev) => ({ ...prev, [index]: 1, })) } const handleMouseLeave = () => { if (hoveredIndex !== null) { setOpacities((prev) => ({ ...prev, [hoveredIndex]: 0, })) } setHoveredIndex(null) } const i = 0 return (
handleMouseMove(e, i)} onMouseEnter={() => handleMouseEnter(i)} onMouseLeave={handleMouseLeave} className="relative flex flex-col items-center justify-center rounded-xl px-2 py-1" >

{label}

) } ===== EXAMPLE: border-button-demo ===== Title: Border Button Demo --- file: example/border-button-demo.tsx --- import BorderButton from "../gammaui/border-button" export default function BorderButtonDemo() { return } ===== COMPONENT: cloud-flow ===== Title: Cloud Flow Description: An interactive cloud architecture visualization component featuring animated SVG flow paths, glowing data particles, node badges, and a central hub with layered motion effects—ideal for showcasing distributed systems, API traffic, and real-time infrastructure flows. --- file: gammaui/cloud-flow.tsx --- "use client" import React from "react" import { IconCloud, IconDatabase, IconServer } from "@tabler/icons-react" import { motion } from "motion/react" import { cn } from "@/lib/utils" interface CloudFlowProps { className?: string centerText?: string nodeLabels?: { topLeft: string topRight: string bottomLeft: string bottomRight: string } badges?: { left: string right: string } title?: string accentColor?: string } export default function CloudFlow({ className, centerText, nodeLabels, badges, title, accentColor = "#00A6F5", }: CloudFlowProps) { return (
{/* SVG Flow Diagram */} {/* Top Left to Center */} {/* Top Right to Center */} {/* Center to Bottom Left */} {/* Center to Bottom Right */} {/* Animation */} {/* Node Badges */} {/* Top Left Node */} {nodeLabels?.topLeft || "API"} {/* Top Right Node */} {nodeLabels?.topRight || "CDN"} {/* Bottom Left Node */} {nodeLabels?.bottomLeft || "DB"} {/* Flow Path Masks */} {/* Gradient */} {/* Main Container */}
{/* Shadow */}
{/* Title Badge */}
{title || "Distributed Cloud Architecture"}
{/* Center Hub Circle */}
{centerText || "HUB"}
{/* Main Content Box */}
{/* Info Badges */}
{badges?.left || "Live Traffic"}
{badges?.right || "Sync Active"}
{/* Animated Concentric Circles */}
) } ===== EXAMPLE: cloud-flow-demo ===== Title: Cloud Flow Demo --- file: example/cloud-flow-demo.tsx --- import CloudFlow from "@/registry/gammaui/cloud-flow" export const CloudFlowDemo = () => { return (
) } ===== COMPONENT: cpu-architecture ===== Title: CPU Architecture Description: A fully animated CPU architecture SVG component with dynamic paths, gradients, and optional CPU connections. --- file: gammaui/cpu-architecture.tsx --- import React from "react" import { cn } from "@/lib/utils" export interface CpuArchitectureSvgProps { className?: string width?: string height?: string text?: string showCpuConnections?: boolean lineMarkerSize?: number animateText?: boolean animateLines?: boolean animateMarkers?: boolean } export default function CpuArchitecture({ className, width = "100%", height = "100%", text = "CPU", showCpuConnections = true, animateText = true, lineMarkerSize = 18, animateLines = true, animateMarkers = true, }: CpuArchitectureSvgProps) { return ( {/* Paths */} {/* 1st */} {/* 2nd */} {/* 3rd */} {/* 4th */} {/* 5th */} {/* 6th */} {/* 7th */} {/* 8th */} {/* Animation For Path Starting */} {animateLines && ( )} {/* 1. Blue Light */} {/* 2. Yellow Light */} {/* 3. Pinkish Light */} {/* 4. White Light */} {/* 5. Green Light */} {/* 6. Orange Light */} {/* 7. Cyan Light */} {/* 8. Rose Light */} {/* CPU Box */} {/* Cpu connections */} {showCpuConnections && ( )} {/* Main CPU Rectangle */} {/* CPU Text */} {text} {/* Masks */} {/* Gradients */} {animateMarkers && ( )} {/* Cpu connection gradient */} {/* Add CPU Text Gradient */} ) } ===== COMPONENT: data-feeding-in ===== Title: Data Feeding In Description: An animated data ingestion visualization with flowing SVG paths, pulsing gradient beams, and a staggered table reveal using Framer Motion. --- file: gammaui/data-feeding-in.tsx --- "use client" import React, { useEffect, useRef } from "react" import { motion, useAnimation } from "motion/react" const paths = [ "M0 100H55.022C61.8914 100 68.6451 101.769 74.6324 105.137L120.368 130.863C126.355 134.231 133.109 136 139.978 136H201.5", "M0 60H48.2171C59.2463 60 69.7861 64.5539 77.3451 72.5854L117.655 115.415C125.214 123.446 135.754 128 146.783 128H201.5", "M0 188H55.022C61.8914 188 68.6451 186.231 74.6324 182.863L120.368 157.137C126.355 153.769 133.109 152 139.978 152H201.5", "M0 228H48.2171C59.2463 228 69.7861 223.446 77.3451 215.415L117.655 172.585C125.214 164.554 135.754 160 146.783 160H201.5", "M0 287H41.7852C56.4929 287 70.0142 278.929 76.994 265.983L118.49 189.017C125.47 176.071 138.991 168 153.699 168H202", "M0 144L201 145", "M0 1H41.5946C56.3171 1 69.8495 9.08744 76.823 22.0537L118.177 98.9463C125.15 111.913 138.683 120 153.405 120H201.5", ] export default function DataFeedingIn() { const svgRef = useRef(null) const controls = useAnimation() useEffect(() => { if (!svgRef.current) return controls.start({ x1: ["-50%", "100%"], x2: ["50%", "150%"], transition: { duration: 1.5, ease: "linear", repeat: Infinity, delay: 0.25, }, }) }, [controls]) return (
{paths.map((d, index) => ( ))} {Array.from({ length: 7 }).map((_, index) => ( ))}
{Array.from({ length: 3 }).map((_, i) => (
))}
{Array.from({ length: 3 }, (_, index) => (
))}
{Array.from({ length: 6 }, (_, i) => ( {Array.from({ length: 3 }, (_, j) => (
))} ))}
) } ===== EXAMPLE: data-feeding-in-demo ===== Title: Data Feeding In Demo --- file: example/data-feeding-in-demo.tsx --- import DataFeedingIn from "@/registry/gammaui/data-feeding-in" export const DataFeedingInDemo = () => { return (
) } ===== COMPONENT: inverted-cursor ===== Title: Inverted Cursor Description: A smooth, animated custom cursor component with blend mode effects. --- file: gammaui/inverted-cursor.tsx --- "use client" import React, { useEffect, useRef, useState } from "react" interface CursorProps { size?: number } export const Cursor: React.FC = ({ size = 60 }) => { const cursorRef = useRef(null) const containerRef = useRef(null) // @ts-ignore const requestRef = useRef() const previousPos = useRef({ x: -size, y: -size }) const [visible, setVisible] = useState(false) const [position, setPosition] = useState({ x: -size, y: -size }) const animate = () => { if (!cursorRef.current) return const currentX = previousPos.current.x const currentY = previousPos.current.y const targetX = position.x - size / 2 const targetY = position.y - size / 2 const deltaX = (targetX - currentX) * 0.2 const deltaY = (targetY - currentY) * 0.2 const newX = currentX + deltaX const newY = currentY + deltaY previousPos.current = { x: newX, y: newY } cursorRef.current.style.transform = `translate(${newX}px, ${newY}px)` requestRef.current = requestAnimationFrame(animate) } useEffect(() => { const container = containerRef.current if (!container) return const handleMouseMove = (e: MouseEvent) => { const rect = container.getBoundingClientRect() setVisible(true) setPosition({ x: e.clientX - rect.left, y: e.clientY - rect.top, }) } const handleMouseEnter = () => { setVisible(true) } const handleMouseLeave = () => { setVisible(false) } container.addEventListener("mousemove", handleMouseMove) container.addEventListener("mouseenter", handleMouseEnter) container.addEventListener("mouseleave", handleMouseLeave) requestRef.current = requestAnimationFrame(animate) return () => { container.removeEventListener("mousemove", handleMouseMove) container.removeEventListener("mouseenter", handleMouseEnter) container.removeEventListener("mouseleave", handleMouseLeave) if (requestRef.current) cancelAnimationFrame(requestRef.current) } }, [position, size]) return (
) } export default Cursor ===== EXAMPLE: inverted-cursor-demo ===== Title: Inverted Cursor Demo --- file: example/inverted-cursor-demo.tsx --- "use client" import { Cursor } from "@/registry/gammaui/inverted-cursor" export default function CursorDemo() { return (
{/* Custom circular inverted color cursor */} {/* Main content centered vertically and horizontally */}

Move your mouse

) } ===== COMPONENT: live-waveform ===== Title: Live Waveform Description: A customizable live audio waveform visualizer using the Web Audio API. --- file: gammaui/live-waveform.tsx --- "use client" import { useEffect, useRef, type HTMLAttributes } from "react" export type LiveWaveformProps = HTMLAttributes & { active?: boolean processing?: boolean deviceId?: string barWidth?: number barGap?: number barRadius?: number barColor?: string fadeEdges?: boolean fadeWidth?: number height?: string | number sensitivity?: number smoothingTimeConstant?: number fftSize?: number historySize?: number updateRate?: number mode?: "scrolling" | "static" onError?: (error: Error) => void onStreamReady?: (stream: MediaStream) => void onStreamEnd?: () => void } export function LiveWaveform({ active = false, processing = false, deviceId, barWidth = 3, barGap = 1, barRadius = 1.5, barColor, fadeEdges = true, fadeWidth = 24, height = 64, sensitivity = 1, smoothingTimeConstant = 0.8, fftSize = 256, historySize = 60, updateRate = 30, mode = "static", onError, onStreamReady, onStreamEnd, className, ...props }: LiveWaveformProps) { const canvasRef = useRef(null) const containerRef = useRef(null) const historyRef = useRef([]) const analyserRef = useRef(null) const audioContextRef = useRef(null) const streamRef = useRef(null) const animationRef = useRef(0) const lastUpdateRef = useRef(0) const processingAnimationRef = useRef(null) const lastActiveDataRef = useRef([]) const transitionProgressRef = useRef(0) const staticBarsRef = useRef([]) const needsRedrawRef = useRef(true) const gradientCacheRef = useRef(null) const lastWidthRef = useRef(0) const currentWidthRef = useRef(0) // Track current width const heightStyle = typeof height === "number" ? `${height}px` : height // Handle canvas resizing useEffect(() => { const canvas = canvasRef.current const container = containerRef.current if (!canvas || !container) return const updateCanvasSize = () => { const rect = container.getBoundingClientRect() const dpr = window.devicePixelRatio || 1 // Store the current width for use in animation loop currentWidthRef.current = rect.width canvas.width = rect.width * dpr canvas.height = rect.height * dpr canvas.style.width = `${containerRef.current?.offsetWidth}px` canvas.style.height = `${rect.height}px` const ctx = canvas.getContext("2d") if (ctx) { ctx.scale(dpr, dpr) } gradientCacheRef.current = null lastWidthRef.current = rect.width needsRedrawRef.current = true } // Initial size updateCanvasSize() const resizeObserver = new ResizeObserver(() => { updateCanvasSize() }) resizeObserver.observe(container) return () => resizeObserver.disconnect() }, []) useEffect(() => { if (processing && !active) { let time = 0 transitionProgressRef.current = 0 const animateProcessing = () => { time += 0.03 transitionProgressRef.current = Math.min( 1, transitionProgressRef.current + 0.02 ) const processingData = [] // Use currentWidthRef instead of getBoundingClientRect const barCount = Math.floor( currentWidthRef.current / (barWidth + barGap) ) if (mode === "static") { const halfCount = Math.floor(barCount / 2) for (let i = 0; i < barCount; i++) { const normalizedPosition = (i - halfCount) / halfCount const centerWeight = 1 - Math.abs(normalizedPosition) * 0.4 const wave1 = Math.sin(time * 1.5 + normalizedPosition * 3) * 0.25 const wave2 = Math.sin(time * 0.8 - normalizedPosition * 2) * 0.2 const wave3 = Math.cos(time * 2 + normalizedPosition) * 0.15 const combinedWave = wave1 + wave2 + wave3 const processingValue = (0.2 + combinedWave) * centerWeight let finalValue = processingValue if ( lastActiveDataRef.current.length > 0 && transitionProgressRef.current < 1 ) { const lastDataIndex = Math.min( i, lastActiveDataRef.current.length - 1 ) const lastValue = lastActiveDataRef.current[lastDataIndex] || 0 finalValue = lastValue * (1 - transitionProgressRef.current) + processingValue * transitionProgressRef.current } processingData.push(Math.max(0.05, Math.min(1, finalValue))) } } else { for (let i = 0; i < barCount; i++) { const normalizedPosition = (i - barCount / 2) / (barCount / 2) const centerWeight = 1 - Math.abs(normalizedPosition) * 0.4 const wave1 = Math.sin(time * 1.5 + i * 0.15) * 0.25 const wave2 = Math.sin(time * 0.8 - i * 0.1) * 0.2 const wave3 = Math.cos(time * 2 + i * 0.05) * 0.15 const combinedWave = wave1 + wave2 + wave3 const processingValue = (0.2 + combinedWave) * centerWeight let finalValue = processingValue if ( lastActiveDataRef.current.length > 0 && transitionProgressRef.current < 1 ) { const lastDataIndex = Math.floor( (i / barCount) * lastActiveDataRef.current.length ) const lastValue = lastActiveDataRef.current[lastDataIndex] || 0 finalValue = lastValue * (1 - transitionProgressRef.current) + processingValue * transitionProgressRef.current } processingData.push(Math.max(0.05, Math.min(1, finalValue))) } } if (mode === "static") { staticBarsRef.current = processingData } else { historyRef.current = processingData } needsRedrawRef.current = true processingAnimationRef.current = requestAnimationFrame(animateProcessing) } animateProcessing() return () => { if (processingAnimationRef.current) { cancelAnimationFrame(processingAnimationRef.current) } } } else if (!active && !processing) { const hasData = mode === "static" ? staticBarsRef.current.length > 0 : historyRef.current.length > 0 if (hasData) { let fadeProgress = 0 const fadeToIdle = () => { fadeProgress += 0.03 if (fadeProgress < 1) { if (mode === "static") { staticBarsRef.current = staticBarsRef.current.map( (value) => value * (1 - fadeProgress) ) } else { historyRef.current = historyRef.current.map( (value) => value * (1 - fadeProgress) ) } needsRedrawRef.current = true requestAnimationFrame(fadeToIdle) } else { if (mode === "static") { staticBarsRef.current = [] } else { historyRef.current = [] } } } fadeToIdle() } } }, [processing, active, barWidth, barGap, mode]) // Handle microphone setup and teardown useEffect(() => { if (!active) { if (streamRef.current) { streamRef.current.getTracks().forEach((track) => track.stop()) streamRef.current = null onStreamEnd?.() } if ( audioContextRef.current && audioContextRef.current.state !== "closed" ) { audioContextRef.current.close() audioContextRef.current = null } if (animationRef.current) { cancelAnimationFrame(animationRef.current) animationRef.current = 0 } return } const setupMicrophone = async () => { try { const stream = await navigator.mediaDevices.getUserMedia({ audio: deviceId ? { deviceId: { exact: deviceId }, echoCancellation: true, noiseSuppression: true, autoGainControl: true, } : { echoCancellation: true, noiseSuppression: true, autoGainControl: true, }, }) streamRef.current = stream onStreamReady?.(stream) const AudioContextConstructor = window.AudioContext || (window as any).webkitAudioContext const audioContext = new AudioContextConstructor() const analyser = audioContext.createAnalyser() analyser.fftSize = fftSize analyser.smoothingTimeConstant = smoothingTimeConstant const source = audioContext.createMediaStreamSource(stream) source.connect(analyser) audioContextRef.current = audioContext analyserRef.current = analyser historyRef.current = [] } catch (error) { onError?.(error as Error) } } setupMicrophone() return () => { if (streamRef.current) { streamRef.current.getTracks().forEach((track) => track.stop()) streamRef.current = null onStreamEnd?.() } if ( audioContextRef.current && audioContextRef.current.state !== "closed" ) { audioContextRef.current.close() audioContextRef.current = null } if (animationRef.current) { cancelAnimationFrame(animationRef.current) animationRef.current = 0 } } }, [ active, deviceId, fftSize, smoothingTimeConstant, onError, onStreamReady, onStreamEnd, ]) // Animation loop useEffect(() => { const canvas = canvasRef.current if (!canvas) return const ctx = canvas.getContext("2d") if (!ctx) return let rafId: number const animate = (currentTime: number) => { // Use currentWidthRef for consistent width throughout render const width = currentWidthRef.current const rect = canvas.getBoundingClientRect() // Update audio data if active if (active && currentTime - lastUpdateRef.current > updateRate) { lastUpdateRef.current = currentTime if (analyserRef.current) { const dataArray = new Uint8Array( analyserRef.current.frequencyBinCount ) analyserRef.current.getByteFrequencyData(dataArray) if (mode === "static") { const startFreq = Math.floor(dataArray.length * 0.05) const endFreq = Math.floor(dataArray.length * 0.4) const relevantData = dataArray.slice(startFreq, endFreq) const barCount = Math.floor(width / (barWidth + barGap)) const halfCount = Math.floor(barCount / 2) const newBars: number[] = [] for (let i = halfCount - 1; i >= 0; i--) { const dataIndex = Math.floor( (i / halfCount) * relevantData.length ) const value = Math.min( 1, (relevantData[dataIndex] / 255) * sensitivity ) newBars.push(Math.max(0.05, value)) } for (let i = 0; i < halfCount; i++) { const dataIndex = Math.floor( (i / halfCount) * relevantData.length ) const value = Math.min( 1, (relevantData[dataIndex] / 255) * sensitivity ) newBars.push(Math.max(0.05, value)) } staticBarsRef.current = newBars lastActiveDataRef.current = newBars } else { let sum = 0 const startFreq = Math.floor(dataArray.length * 0.05) const endFreq = Math.floor(dataArray.length * 0.4) const relevantData = dataArray.slice(startFreq, endFreq) for (let i = 0; i < relevantData.length; i++) { sum += relevantData[i] } const average = (sum / relevantData.length / 255) * sensitivity historyRef.current.push(Math.min(1, Math.max(0.05, average))) lastActiveDataRef.current = [...historyRef.current] if (historyRef.current.length > historySize) { historyRef.current.shift() } } needsRedrawRef.current = true } } if (!needsRedrawRef.current && !active) { rafId = requestAnimationFrame(animate) return } needsRedrawRef.current = active ctx.clearRect(0, 0, width, rect.height) const computedBarColor = barColor || (() => { const style = getComputedStyle(canvas) const color = style.color return color || "#000" })() const step = barWidth + barGap const barCount = Math.floor(width / step) const centerY = rect.height / 2 if (mode === "static") { const dataToRender = processing ? staticBarsRef.current : active ? staticBarsRef.current : staticBarsRef.current.length > 0 ? staticBarsRef.current : [] for (let i = 0; i < barCount && i < dataToRender.length; i++) { const value = dataToRender[i] || 0.1 const x = i * step const barHeight = Math.max(4, value * rect.height * 0.8) const y = centerY - barHeight / 2 ctx.fillStyle = computedBarColor ctx.globalAlpha = 0.4 + value * 0.6 if (barRadius > 0) { ctx.beginPath() ctx.roundRect(x, y, barWidth, barHeight, barRadius) ctx.fill() } else { ctx.fillRect(x, y, barWidth, barHeight) } } } else { for (let i = 0; i < barCount && i < historyRef.current.length; i++) { const dataIndex = historyRef.current.length - 1 - i const value = historyRef.current[dataIndex] || 0.1 const x = width - (i + 1) * step const barHeight = Math.max(4, value * rect.height * 0.8) const y = centerY - barHeight / 2 ctx.fillStyle = computedBarColor ctx.globalAlpha = 0.4 + value * 0.6 if (barRadius > 0) { ctx.beginPath() ctx.roundRect(x, y, barWidth, barHeight, barRadius) ctx.fill() } else { ctx.fillRect(x, y, barWidth, barHeight) } } } if (fadeEdges && fadeWidth > 0 && width > 0) { if (!gradientCacheRef.current || lastWidthRef.current !== width) { const gradient = ctx.createLinearGradient(0, 0, width, 0) const fadePercent = Math.min(0.3, fadeWidth / width) gradient.addColorStop(0, "rgba(255,255,255,1)") gradient.addColorStop(fadePercent, "rgba(255,255,255,0)") gradient.addColorStop(1 - fadePercent, "rgba(255,255,255,0)") gradient.addColorStop(1, "rgba(255,255,255,1)") gradientCacheRef.current = gradient lastWidthRef.current = width } ctx.globalCompositeOperation = "destination-out" ctx.fillStyle = gradientCacheRef.current ctx.fillRect(0, 0, width, rect.height) ctx.globalCompositeOperation = "source-over" } ctx.globalAlpha = 1 rafId = requestAnimationFrame(animate) } rafId = requestAnimationFrame(animate) return () => { if (rafId) { cancelAnimationFrame(rafId) } } }, [ active, processing, sensitivity, updateRate, historySize, barWidth, barGap, barRadius, barColor, fadeEdges, fadeWidth, mode, ]) return (
{!active && !processing && (
)}
) } export default LiveWaveform ===== EXAMPLE: live-waveform-demo ===== Title: Live Waveform Demo --- file: example/live-waveform-demo.tsx --- "use client" import { useState } from "react" import { Button } from "@/components/ui/button" import { LiveWaveform } from "@/registry/gammaui/live-waveform" export default function LiveWaveformDemo() { const [active, setActive] = useState(false) const [processing, setProcessing] = useState(false) const [mode, setMode] = useState<"static" | "scrolling">("static") const handleToggleActive = () => { setActive(!active) if (!active) { setProcessing(false) } } const handleToggleProcessing = () => { setProcessing(!processing) if (!processing) { setActive(false) } } return (

Live Audio Waveform

Real-time microphone input visualization with audio reactivity

) } ===== COMPONENT: location-map ===== Title: Location Map Description: An interactive 3D-tilting location card with expandable animated map, live indicator, and motion-based hover effects. --- file: gammaui/location-map.tsx --- "use client" import type React from "react" import { useRef, useState } from "react" import { AnimatePresence, motion, useMotionValue, useSpring, useTransform, } from "motion/react" interface LocationMapProps { location?: string coordinates?: string className?: string } export function LocationMap({ location = "San Francisco, CA", coordinates = "37.7749° N, 122.4194° W", className, }: LocationMapProps) { const [isHovered, setIsHovered] = useState(false) const [isExpanded, setIsExpanded] = useState(false) const containerRef = useRef(null) const mouseX = useMotionValue(0) const mouseY = useMotionValue(0) const rotateX = useTransform(mouseY, [-50, 50], [8, -8]) const rotateY = useTransform(mouseX, [-50, 50], [-8, 8]) const springRotateX = useSpring(rotateX, { stiffness: 300, damping: 30 }) const springRotateY = useSpring(rotateY, { stiffness: 300, damping: 30 }) const handleMouseMove = (e: React.MouseEvent) => { if (!containerRef.current) return const rect = containerRef.current.getBoundingClientRect() const centerX = rect.left + rect.width / 2 const centerY = rect.top + rect.height / 2 mouseX.set(e.clientX - centerX) mouseY.set(e.clientY - centerY) } const handleMouseLeave = () => { mouseX.set(0) mouseY.set(0) setIsHovered(false) } const handleClick = () => { setIsExpanded(!isExpanded) } return ( setIsHovered(true)} onMouseLeave={handleMouseLeave} onClick={handleClick} > {/* Subtle gradient overlay */}
{isExpanded && (
{/* Main roads - using foreground with opacity */} {/* Vertical main roads */} {/* Secondary streets */} {[20, 50, 80].map((y, i) => ( ))} {[15, 45, 55, 85].map((x, i) => ( ))} {/* Buildings - using muted-foreground */}
)} {/* Grid pattern - only show when collapsed */} {/* Content */}
{/* Top section */}
{/* Map Icon SVG */}
{/* Status indicator */}
Live
{/* Bottom section */}
{location} {isExpanded && ( {coordinates} )} {/* Animated underline */}
{/* Click hint */} Click to expand ) } ===== EXAMPLE: location-map-demo ===== Title: Location Map Demo --- file: example/location-map-demo.tsx --- import { LocationMap } from "@/registry/gammaui/location-map" export default function LocationMapDemo() { return (
{/* Optional subtle label */}

Current Location

) } ===== COMPONENT: logo-cloud ===== Title: Logo Cloud Description: A responsive logo grid component supporting dark mode, decorations, and flexible layout. --- file: gammaui/logo-cloud.tsx --- import { IconPlus } from "@tabler/icons-react" import { cn } from "@/lib/utils" interface Logo { src: string alt: string width?: number height?: number } type LogoCloudProps = React.ComponentProps<"div"> export default function LogoCloud({ className, ...props }: LogoCloudProps) { return (
) } type LogoCardProps = React.ComponentProps<"div"> & { logo: Logo } function LogoCard({ logo, className, children, ...props }: LogoCardProps) { return (
{logo.alt} {children}
) } ===== EXAMPLE: logo-cloud-demo ===== Title: Logo Cloud Demo --- file: example/logo-cloud-demo.tsx --- import LogoCloud from "@/registry/gammaui/logo-cloud" export default function DemoOne() { return (

Companies we{" "} collaborate with.

) } ===== COMPONENT: logo-loop ===== Title: Logo Timeline Description: A looping logo marquee component with horizontal and vertical motion, hover effects, fading edges, and responsive behavior. --- file: gammaui/logo-loop.tsx --- "use client" import React, { useCallback, useEffect, useMemo, useRef, useState } from "react" export type LogoItem = | { node: React.ReactNode href?: string title?: string ariaLabel?: string } | { src: string alt?: string href?: string title?: string srcSet?: string sizes?: string width?: number height?: number } export interface LogoLoopProps { logos: LogoItem[] speed?: number direction?: "left" | "right" | "up" | "down" width?: number | string logoHeight?: number gap?: number pauseOnHover?: boolean hoverSpeed?: number fadeOut?: boolean fadeOutColor?: string scaleOnHover?: boolean renderItem?: (item: LogoItem, key: React.Key) => React.ReactNode ariaLabel?: string className?: string style?: React.CSSProperties } const ANIMATION_CONFIG = { SMOOTH_TAU: 0.25, MIN_COPIES: 2, COPY_HEADROOM: 2, } as const const toCssLength = (value?: number | string): string | undefined => typeof value === "number" ? `${value}px` : (value ?? undefined) const cx = (...parts: (string | false | null | undefined)[]) => parts.filter(Boolean).join(" ") const useResizeObserver = ( callback: () => void, elements: React.RefObject[], dependencies: React.DependencyList ) => { useEffect(() => { if (!window.ResizeObserver) { const handleResize = () => callback() window.addEventListener("resize", handleResize) callback() return () => window.removeEventListener("resize", handleResize) } const observers = elements.map((ref) => { if (!ref.current) return null const observer = new ResizeObserver(callback) observer.observe(ref.current) return observer }) callback() return () => { observers.forEach((observer) => observer?.disconnect()) } }, dependencies) } const useImageLoader = ( seqRef: React.RefObject, onLoad: () => void, dependencies: React.DependencyList ) => { useEffect(() => { const images = seqRef.current?.querySelectorAll("img") ?? [] if (images.length === 0) { onLoad() return } let remainingImages = images.length const handleImageLoad = () => { remainingImages -= 1 if (remainingImages === 0) { onLoad() } } images.forEach((img) => { const htmlImg = img as HTMLImageElement if (htmlImg.complete) { handleImageLoad() } else { htmlImg.addEventListener("load", handleImageLoad, { once: true }) htmlImg.addEventListener("error", handleImageLoad, { once: true }) } }) return () => { images.forEach((img) => { img.removeEventListener("load", handleImageLoad) img.removeEventListener("error", handleImageLoad) }) } }, dependencies) } const useAnimationLoop = ( trackRef: React.RefObject, targetVelocity: number, seqWidth: number, seqHeight: number, isHovered: boolean, hoverSpeed: number | undefined, isVertical: boolean ) => { const rafRef = useRef(null) const lastTimestampRef = useRef(null) const offsetRef = useRef(0) const velocityRef = useRef(0) useEffect(() => { const track = trackRef.current if (!track) return const prefersReduced = typeof window !== "undefined" && window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches const seqSize = isVertical ? seqHeight : seqWidth if (seqSize > 0) { offsetRef.current = ((offsetRef.current % seqSize) + seqSize) % seqSize const transformValue = isVertical ? `translate3d(0, ${-offsetRef.current}px, 0)` : `translate3d(${-offsetRef.current}px, 0, 0)` track.style.transform = transformValue } if (prefersReduced) { track.style.transform = isVertical ? "translate3d(0, 0, 0)" : "translate3d(0, 0, 0)" return () => { lastTimestampRef.current = null } } const animate = (timestamp: number) => { if (lastTimestampRef.current === null) { lastTimestampRef.current = timestamp } const deltaTime = Math.max(0, timestamp - lastTimestampRef.current) / 1000 lastTimestampRef.current = timestamp const target = isHovered && hoverSpeed !== undefined ? hoverSpeed : targetVelocity const easingFactor = 1 - Math.exp(-deltaTime / ANIMATION_CONFIG.SMOOTH_TAU) velocityRef.current += (target - velocityRef.current) * easingFactor if (seqSize > 0) { let nextOffset = offsetRef.current + velocityRef.current * deltaTime nextOffset = ((nextOffset % seqSize) + seqSize) % seqSize offsetRef.current = nextOffset const transformValue = isVertical ? `translate3d(0, ${-offsetRef.current}px, 0)` : `translate3d(${-offsetRef.current}px, 0, 0)` track.style.transform = transformValue } rafRef.current = requestAnimationFrame(animate) } rafRef.current = requestAnimationFrame(animate) return () => { if (rafRef.current !== null) { cancelAnimationFrame(rafRef.current) rafRef.current = null } lastTimestampRef.current = null } }, [targetVelocity, seqWidth, seqHeight, isHovered, hoverSpeed, isVertical]) } export const LogoLoop = React.memo( ({ logos, speed = 120, direction = "left", width = "100%", logoHeight = 28, gap = 32, pauseOnHover, hoverSpeed, fadeOut = false, fadeOutColor, scaleOnHover = false, renderItem, ariaLabel = "Partner logos", className, style, }) => { const containerRef = useRef(null) const trackRef = useRef(null) const seqRef = useRef(null) const [seqWidth, setSeqWidth] = useState(0) const [seqHeight, setSeqHeight] = useState(0) const [copyCount, setCopyCount] = useState( ANIMATION_CONFIG.MIN_COPIES ) const [isHovered, setIsHovered] = useState(false) const effectiveHoverSpeed = useMemo(() => { if (hoverSpeed !== undefined) return hoverSpeed if (pauseOnHover === true) return 0 if (pauseOnHover === false) return undefined return 0 }, [hoverSpeed, pauseOnHover]) const isVertical = direction === "up" || direction === "down" const targetVelocity = useMemo(() => { const magnitude = Math.abs(speed) let directionMultiplier: number if (isVertical) { directionMultiplier = direction === "up" ? 1 : -1 } else { directionMultiplier = direction === "left" ? 1 : -1 } const speedMultiplier = speed < 0 ? -1 : 1 return magnitude * directionMultiplier * speedMultiplier }, [speed, direction, isVertical]) const updateDimensions = useCallback(() => { const containerWidth = containerRef.current?.clientWidth ?? 0 const sequenceRect = seqRef.current?.getBoundingClientRect?.() const sequenceWidth = sequenceRect?.width ?? 0 const sequenceHeight = sequenceRect?.height ?? 0 if (isVertical) { const parentHeight = containerRef.current?.parentElement?.clientHeight ?? 0 if (containerRef.current && parentHeight > 0) { const targetHeight = Math.ceil(parentHeight) if (containerRef.current.style.height !== `${targetHeight}px`) containerRef.current.style.height = `${targetHeight}px` } if (sequenceHeight > 0) { setSeqHeight(Math.ceil(sequenceHeight)) const viewport = containerRef.current?.clientHeight ?? parentHeight ?? sequenceHeight const copiesNeeded = Math.ceil(viewport / sequenceHeight) + ANIMATION_CONFIG.COPY_HEADROOM setCopyCount(Math.max(ANIMATION_CONFIG.MIN_COPIES, copiesNeeded)) } } else if (sequenceWidth > 0) { setSeqWidth(Math.ceil(sequenceWidth)) const copiesNeeded = Math.ceil(containerWidth / sequenceWidth) + ANIMATION_CONFIG.COPY_HEADROOM setCopyCount(Math.max(ANIMATION_CONFIG.MIN_COPIES, copiesNeeded)) } }, [isVertical]) useResizeObserver( updateDimensions, [containerRef, seqRef], [logos, gap, logoHeight, isVertical] ) useImageLoader(seqRef, updateDimensions, [ logos, gap, logoHeight, isVertical, ]) useAnimationLoop( trackRef, targetVelocity, seqWidth, seqHeight, isHovered, effectiveHoverSpeed, isVertical ) const cssVariables = useMemo( () => ({ "--logoloop-gap": `${gap}px`, "--logoloop-logoHeight": `${logoHeight}px`, ...(fadeOutColor && { "--logoloop-fadeColor": fadeOutColor }), }) as React.CSSProperties, [gap, logoHeight, fadeOutColor] ) const rootClasses = useMemo( () => cx( "relative group", isVertical ? "overflow-hidden h-full inline-block" : "overflow-x-hidden", "[--logoloop-gap:32px]", "[--logoloop-logoHeight:28px]", "[--logoloop-fadeColorAuto:#ffffff]", "dark:[--logoloop-fadeColorAuto:#0b0b0b]", scaleOnHover && "py-[calc(var(--logoloop-logoHeight)*0.1)]", className ), [isVertical, scaleOnHover, className] ) const handleMouseEnter = useCallback(() => { if (effectiveHoverSpeed !== undefined) setIsHovered(true) }, [effectiveHoverSpeed]) const handleMouseLeave = useCallback(() => { if (effectiveHoverSpeed !== undefined) setIsHovered(false) }, [effectiveHoverSpeed]) const renderLogoItem = useCallback( (item: LogoItem, key: React.Key) => { if (renderItem) { return (
  • {renderItem(item, key)}
  • ) } const isNodeItem = "node" in item const content = isNodeItem ? ( {(item as any).node} ) : ( {(item ) const itemAriaLabel = isNodeItem ? ((item as any).ariaLabel ?? (item as any).title) : ((item as any).alt ?? (item as any).title) const inner = (item as any).href ? ( {content} ) : ( content ) return (
  • {inner}
  • ) }, [isVertical, scaleOnHover, renderItem] ) const logoLists = useMemo( () => Array.from({ length: copyCount }, (_, copyIndex) => (
      0} ref={copyIndex === 0 ? seqRef : undefined} > {logos.map((item, itemIndex) => renderLogoItem(item, `${copyIndex}-${itemIndex}`) )}
    )), [copyCount, logos, renderLogoItem, isVertical] ) const containerStyle = useMemo( (): React.CSSProperties => ({ width: isVertical ? toCssLength(width) === "100%" ? undefined : toCssLength(width) : (toCssLength(width) ?? "100%"), ...cssVariables, ...style, }), [width, cssVariables, style, isVertical] ) return (
    {fadeOut && ( <> {isVertical ? ( <>
    ) : ( <>
    )} )}
    {logoLists}
    ) } ) LogoLoop.displayName = "LogoLoop" export default LogoLoop ===== EXAMPLE: logo-loop-demo ===== Title: Logo Timeline Demo 2 --- file: example/logo-loop-demo.tsx --- import { IconBrandAndroid, IconBrandApple, IconBrandAws, IconBrandCss3, IconBrandDocker, IconBrandFigma, IconBrandFirebase, IconBrandGithub, IconBrandGitlab, IconBrandGoogle, IconBrandHtml5, IconBrandJavascript, IconBrandNextjs, IconBrandNodejs, IconBrandReact, IconBrandStripe, IconBrandSupabase, IconBrandTailwind, IconBrandTypescript, IconBrandVercel, } from "@tabler/icons-react" import { LogoLoop } from "@/registry/gammaui/logo-loop" const techLogos = [ { node: , title: "React", href: "https://react.dev", }, { node: , title: "Next.js", href: "https://nextjs.org", }, { node: , title: "TypeScript", href: "https://www.typescriptlang.org", }, { node: , title: "Tailwind CSS", href: "https://tailwindcss.com", }, // --- Added Logos --- { node: , title: "Vercel", href: "https://vercel.com", }, { node: , title: "Node.js", href: "https://nodejs.org", }, { node: , title: "GitHub", href: "https://github.com", }, { node: , title: "GitLab", href: "https://gitlab.com", }, { node: , title: "Docker", href: "https://www.docker.com", }, { node: , title: "JavaScript", href: "https://developer.mozilla.org/en-US/docs/Web/JavaScript", }, { node: , title: "HTML5", href: "https://developer.mozilla.org/en-US/docs/Web/HTML", }, { node: , title: "CSS3", href: "https://developer.mozilla.org/en-US/docs/Web/CSS", }, { node: , title: "Figma", href: "https://figma.com", }, { node: , title: "Amazon AWS", href: "https://aws.amazon.com", }, { node: , title: "Google", href: "https://google.com", }, { node: , title: "Stripe", href: "https://stripe.com", }, { node: , title: "Firebase", href: "https://firebase.google.com", }, { node: , title: "Supabase", href: "https://supabase.com", }, { node: , title: "Apple", href: "https://apple.com", }, { node: , title: "Android", href: "https://www.android.com", }, ] export function LogoLoopDemo() { return (
    ) } ===== COMPONENT: logo-timeline ===== Title: Logo Timeline Description: A multi-row infinitely scrolling logo timeline built with Motion One, supporting hover-activated animations and row grouping. --- file: gammaui/logo-timeline.tsx --- "use client" import type React from "react" import { useState } from "react" import { motion } from "motion/react" import { cn } from "@/lib/utils" export interface LogoItem { /** The label text displayed next to the icon */ label: string /** The icon name from the Icons object (e.g., "gitHub", "react", "tailwind") */ icon: React.ReactNode /** Animation delay in seconds (use negative values for staggered effect) */ animationDelay: number /** Animation duration in seconds */ animationDuration: number /** The row number where this logo should appear (1-based) */ row: number } export interface LogoTimelineProps { /** Array of logo items to display */ items: LogoItem[] /** Optional title text to display in the center */ title?: string /** Height of the timeline container */ height?: string /** Additional className for the container */ className?: string /** Icon size in pixels (default: 16) */ iconSize?: number /** Whether to show separator lines between rows (default: true) */ showRowSeparator?: boolean /** Whether to animate logos only on hover (default: false) */ animateOnHover?: boolean } export function LogoTimeline({ items, title, height = "h-[400px] sm:h-[800px]", className, iconSize = 16, showRowSeparator = true, animateOnHover = false, }: LogoTimelineProps) { const [isHovered, setIsHovered] = useState(false) // Group items by row const rowsMap = new Map() items.forEach((item) => { if (!rowsMap.has(item.row)) { rowsMap.set(item.row, []) } rowsMap.get(item.row)?.push(item) }) // Convert map to sorted array const rows = Array.from(rowsMap.entries()) .sort(([a], [b]) => a - b) .map(([, rowItems]) => rowItems) // Determine animation play state const animationPlayState = animateOnHover ? isHovered ? "running" : "paused" : "running" return (
    ) } ===== EXAMPLE: logo-timeline-demo ===== Title: Logo Timeline Demo --- file: example/logo-timeline-demo.tsx --- import { IconBrandApple, IconBrandDiscord, IconBrandGithub, IconBrandGoogleDrive, IconBrandMessenger, IconBrandNotion, IconBrandOpenai, IconBrandPaypal, IconBrandReact, IconBrandTailwind, IconBrandTypescript, IconBrandWhatsapp, IconBrandX, } from "@tabler/icons-react" import { LogoTimeline } from "@/registry/gammaui/logo-timeline" import type { LogoItem } from "@/registry/gammaui/logo-timeline" const logos: LogoItem[] = [ // Row 1 - Communication & Social (2 logos, 50s duration, spaced 25s apart) { label: "Discord", icon: , animationDelay: -50, animationDuration: 50, row: 1, }, { label: "X (Twitter)", icon: , animationDelay: -25, animationDuration: 50, row: 1, }, // Row 2 - Development Tools (2 logos, 45s duration, spaced 22.5s apart) { label: "GitHub", icon: , animationDelay: -45, animationDuration: 45, row: 2, }, { label: "React", icon: , animationDelay: -22.5, animationDuration: 45, row: 2, }, // Row 3 - Development Tools Continued (3 logos, 60s duration, spaced 20s apart) { label: "TypeScript", icon: , animationDelay: -60, animationDuration: 60, row: 3, }, { label: "Tailwind", icon: , animationDelay: -40, animationDuration: 60, row: 3, }, // Row 4 - Productivity & Cloud (2 logos, 55s duration, spaced 27.5s apart) { label: "Google Drive", icon: , animationDelay: -55, animationDuration: 55, row: 4, }, { label: "Notion", icon: , animationDelay: -27.5, animationDuration: 55, row: 4, }, // Row 5 - Messaging (2 logos, 50s duration, spaced 25s apart) { label: "WhatsApp", icon: , animationDelay: -50, animationDuration: 50, row: 5, }, { label: "Messenger", icon: , // Placeholder icon animationDelay: -25, animationDuration: 50, row: 5, }, // Row 6 - AI & Automation (3 logos, 65s duration, spaced ~21.5s apart) { label: "OpenAI", icon: , // Placeholder icon animationDelay: -65, animationDuration: 65, row: 6, }, // Row 7 - Payment & Services (2 logos, 50s duration, spaced 25s apart) { label: "PayPal", icon: , // Placeholder icon animationDelay: -50, animationDuration: 50, row: 7, }, { label: "Apple", icon: , // Placeholder icon animationDelay: -25, animationDuration: 50, row: 7, }, ] export function LogoTimelineDemo() { return (
    ) } ===== EXAMPLE: logo-timeline-demo-2 ===== Title: Logo Timeline Demo 2 --- file: example/logo-timeline-demo-2.tsx --- import { IconBrandApple, IconBrandDiscord, IconBrandGithub, IconBrandGoogleDrive, IconBrandMessenger, IconBrandNotion, IconBrandOpenai, IconBrandPaypal, IconBrandReact, IconBrandTailwind, IconBrandTypescript, IconBrandWhatsapp, IconBrandX, } from "@tabler/icons-react" import { LogoTimeline } from "@/registry/gammaui/logo-timeline" import type { LogoItem } from "@/registry/gammaui/logo-timeline" const logos: LogoItem[] = [ // Row 1 - Communication & Social (2 logos, 50s duration, spaced 25s apart) { label: "Discord", icon: , animationDelay: -50, animationDuration: 50, row: 1, }, { label: "X (Twitter)", icon: , animationDelay: -25, animationDuration: 50, row: 1, }, // Row 2 - Development Tools (2 logos, 45s duration, spaced 22.5s apart) { label: "GitHub", icon: , animationDelay: -45, animationDuration: 45, row: 2, }, { label: "React", icon: , animationDelay: -22.5, animationDuration: 45, row: 2, }, // Row 3 - Development Tools Continued (3 logos, 60s duration, spaced 20s apart) { label: "TypeScript", icon: , animationDelay: -60, animationDuration: 60, row: 3, }, { label: "Tailwind", icon: , animationDelay: -40, animationDuration: 60, row: 3, }, // Row 4 - Productivity & Cloud (2 logos, 55s duration, spaced 27.5s apart) { label: "Google Drive", icon: , animationDelay: -55, animationDuration: 55, row: 4, }, { label: "Notion", icon: , animationDelay: -27.5, animationDuration: 55, row: 4, }, // Row 5 - Messaging (2 logos, 50s duration, spaced 25s apart) { label: "WhatsApp", icon: , animationDelay: -50, animationDuration: 50, row: 5, }, { label: "Messenger", icon: , // Placeholder icon animationDelay: -25, animationDuration: 50, row: 5, }, // Row 6 - AI & Automation (3 logos, 65s duration, spaced ~21.5s apart) { label: "OpenAI", icon: , // Placeholder icon animationDelay: -65, animationDuration: 65, row: 6, }, // Row 7 - Payment & Services (2 logos, 50s duration, spaced 25s apart) { label: "PayPal", icon: , // Placeholder icon animationDelay: -50, animationDuration: 50, row: 7, }, { label: "Apple", icon: , // Placeholder icon animationDelay: -25, animationDuration: 50, row: 7, }, ] export default function LogoTimelineDemo() { return (
    ) } ===== COMPONENT: macbook-keyboard ===== Title: Macbook Keyboard Description: Interactive animated MacBook keyboard component. --- file: gammaui/macbook-keyboard.tsx --- "use client" import React, { memo, useEffect, useState } from "react" import { ArrowDownIcon, ArrowLeftIcon, ArrowRightIcon, ArrowUpIcon, ChevronUpIcon, CommandIcon, DotIcon, GlobeIcon, LayoutPanelLeft, MicIcon, MoonIcon, OptionIcon, PauseIcon, SearchIcon, SkipBackIcon, SkipForwardIcon, SunDimIcon, SunIcon, Volume1Icon, Volume2Icon, VolumeIcon, } from "lucide-react" import { motion } from "motion/react" import { cn } from "../lib/utils" const iconSize = 12 type CustomKeyType = "FingerPrintKey" | "ArrowKeys" interface IconKeyboardKeyProps { icon: string | React.ReactNode text?: string | React.ReactNode className?: string isSingleKey?: boolean custom?: CustomKeyType index?: number rowIndex?: number isLastRow?: boolean keySize?: number transparentKey?: boolean glowColor?: string pressedKey: string | null } interface MacBookKeyboardSvgProps { transparentKey?: boolean keySize?: number glowColor?: string } type KeyRowDataProps = Omit const keyMap: Record = { Escape: "esc", F1: "F1", F2: "F2", F3: "F3", F4: "F4", F5: "F5", F6: "F6", F7: "F7", F8: "F8", F9: "F9", F10: "F10", F11: "F11", F12: "F12", "`": "`", "1": "1", "2": "2", "3": "3", "4": "4", "5": "5", "6": "6", "7": "7", "8": "8", "9": "9", "0": "0", "-": "-", "=": "=", Backspace: "delete", Tab: "tab", q: "Q", w: "W", e: "E", r: "R", t: "T", y: "Y", u: "U", i: "I", o: "O", p: "P", "[": "[", "]": "]", "\\": "/", CapsLock: "caps lock", a: "A", s: "S", d: "D", f: "F", g: "G", h: "H", j: "J", k: "K", l: "L", ";": ";", "'": "'", Enter: "return", Shift: "shift", z: "Z", x: "X", c: "C", v: "V", b: "B", n: "N", m: "M", ",": ",", ".": ".", "/": "/", Control: "control", Alt: "option", Meta: "command", " ": "", ArrowUp: "ArrowUp", ArrowDown: "ArrowDown", ArrowLeft: "ArrowLeft", ArrowRight: "ArrowRight", } const MacBookKeyboard = memo( ({ transparentKey = true, keySize = 40, glowColor = "#f43f5d", }: MacBookKeyboardSvgProps) => { const [pressedKey, setPressedKey] = useState(null) useEffect(() => { const handleKeyDown = (event: KeyboardEvent) => { const mappedKey = keyMap[event.key] if (mappedKey !== undefined) { setPressedKey(mappedKey) } } const handleKeyUp = () => { setPressedKey(null) } window.addEventListener("keydown", handleKeyDown) window.addEventListener("keyup", handleKeyUp) return () => { window.removeEventListener("keydown", handleKeyDown) window.removeEventListener("keyup", handleKeyUp) } }, []) return (
    {keysData.map((row, rowIndex) => { return (
    {row.map((item, index) => { if (item.custom === "FingerPrintKey") { return ( ) } if (item.custom === "ArrowKeys") { return ( ) } return ( ) })}
    ) })}
    ) } ) MacBookKeyboard.displayName = "MacBookKeyboard" const IconKeyboardKey = memo( ({ icon, text, className, isSingleKey, index = 0, rowIndex = 0, isLastRow, keySize, transparentKey, glowColor, pressedKey, }: IconKeyboardKeyProps) => { const isText = typeof text === "string" const shouldGlow = isText && pressedKey === text return ( {!isSingleKey && (
    {icon}
    )}
    {text}
    ) } ) IconKeyboardKey.displayName = "IconKeyboardKey" const FingerPrintKey = memo( ({ index, rowIndex, keySize, transparentKey, }: { index: number rowIndex: number keySize: number transparentKey: boolean }) => { return (
    ) } ) FingerPrintKey.displayName = "FingerPrintKey" const ArrowKeys = memo( ({ index, rowIndex, pressedKey, glowColor, }: { index: number rowIndex: number pressedKey: string | null glowColor: string }) => { return (
    } keyName="ArrowLeft" pressedKey={pressedKey} glowColor={glowColor} className="h-1/2! translate-y-5 items-center!" />
    } keyName="ArrowUp" pressedKey={pressedKey} glowColor={glowColor} className="h-1/2! items-center! rounded-b-none!" /> } keyName="ArrowDown" pressedKey={pressedKey} glowColor={glowColor} className="h-1/2! items-center! rounded-t-none!" />
    } keyName="ArrowRight" pressedKey={pressedKey} glowColor={glowColor} className="h-1/2! translate-y-5 items-center!" />
    ) } ) ArrowKeys.displayName = "ArrowKeys" const ArrowKey = memo( ({ index, rowIndex, icon, keyName, pressedKey, glowColor, className, }: { index: number rowIndex: number icon: React.ReactNode keyName: string pressedKey: string | null glowColor: string className?: string }) => { const shouldGlow = pressedKey === keyName return (
    {icon}
    ) } ) ArrowKey.displayName = "ArrowKey" export { MacBookKeyboard } const firstRowData: KeyRowDataProps[] = [ { icon: null, text: "esc", className: "!basis-full", }, { icon: , text: "F1", }, { icon: , text: "F2", }, { icon: , text: "F3", }, { icon: , text: "F4", }, { icon: , text: "F5", }, { icon: , text: "F6", }, { icon: , text: "F7", }, { icon: , text: "F8", }, { icon: , text: "F9", }, { icon: , text: "F10", }, { icon: , text: "F11", }, { icon: , text: "F12", }, { custom: "FingerPrintKey", icon: null, }, ] const secondRowData: KeyRowDataProps[] = [ { icon: "~", text: "`", className: "first:!items-center", }, { icon: "!", text: "1", }, { icon: "@", text: "2", }, { icon: "#", text: "3", }, { icon: "$", text: "4", }, { icon: "%", text: "5", }, { icon: "^", text: "6", }, { icon: "&", text: "7", }, { icon: "*", text: "8", }, { icon: "(", text: "9", }, { icon: ")", text: "0", }, { icon: "_", text: "-", }, { icon: "+", text: "=", }, { icon: null, text: "delete", className: "!basis-full", }, ] const thirdRowData: KeyRowDataProps[] = [ { icon: null, text: "tab", className: "!basis-full", }, { icon: "q", text: "Q", isSingleKey: true, }, { icon: "w", text: "W", isSingleKey: true, }, { icon: "e", text: "E", isSingleKey: true, }, { icon: "r", text: "R", isSingleKey: true, }, { icon: "t", text: "T", isSingleKey: true, }, { icon: "y", text: "Y", isSingleKey: true, }, { icon: "u", text: "U", isSingleKey: true, }, { icon: "i", text: "I", isSingleKey: true, }, { icon: "o", text: "O", isSingleKey: true, }, { icon: "p", text: "P", isSingleKey: true, }, { icon: "{", text: "[", }, { icon: "}", text: "]", }, { icon: "|", text: "/", className: "last:!items-center", }, ] const fourthRowData: KeyRowDataProps[] = [ { icon: , text: "caps lock", className: "!basis-full", }, { icon: "a", text: "A", isSingleKey: true, }, { icon: "s", text: "S", isSingleKey: true, }, { icon: "d", text: "D", isSingleKey: true, }, { icon: "f", text: "F", isSingleKey: true, }, { icon: "g", text: "G", isSingleKey: true, }, { icon: "h", text: "H", isSingleKey: true, }, { icon: "j", text: "J", isSingleKey: true, }, { icon: "k", text: "K", isSingleKey: true, }, { icon: "l", text: "L", isSingleKey: true, }, { icon: ":", text: ";", }, { icon: '"', text: "'", }, { icon: null, text: "return", className: "!basis-full", }, ] const fifthRowData: KeyRowDataProps[] = [ { icon: null, text: "shift", className: "!basis-full", }, { icon: "z", text: "Z", isSingleKey: true, }, { icon: "x", text: "X", isSingleKey: true, }, { icon: "c", text: "C", isSingleKey: true, }, { icon: "v", text: "V", isSingleKey: true, }, { icon: "b", text: "B", isSingleKey: true, }, { icon: "n", text: "N", isSingleKey: true, }, { icon: "m", text: "M", isSingleKey: true, }, { icon: "<", text: ",", }, { icon: ">", text: ".", }, { icon: "?", text: "/", }, { icon: null, text: "shift", className: "!basis-full", }, ] const sixthRowData: KeyRowDataProps[] = [ { icon: "fn", text: , }, { icon: , text: "control", className: "!items-end", }, { icon: , text: "option", className: "!items-end", }, { icon: , text: "command", className: "!items-end !basis-20", }, { icon: null, text: "", className: "!basis-80", }, { icon: , text: "command", className: "!items-start !basis-20", }, { icon: , text: "option", className: "!items-start", }, { icon: null, custom: "ArrowKeys", }, ] const keysData = [ firstRowData, secondRowData, thirdRowData, fourthRowData, fifthRowData, sixthRowData, ] ===== EXAMPLE: macbook-keyboard-demo ===== Title: Macbook Keyboard Demo --- file: example/macbook-keyboard-demo.tsx --- "use client" import { useTheme } from "next-themes" import { MacBookKeyboard } from "@/registry/gammaui/macbook-keyboard" export const MacbookKeyboardDemo = () => { const { resolvedTheme } = useTheme() return (
    ) } ===== EXAMPLE: cpu-architecture-demo ===== Title: CPU Architecture Demo --- file: example/cpu-architecture-demo.tsx --- "use client" import CpuArchitecture from "@/registry/gammaui/cpu-architecture" const CpuArchitectureDemo = () => { return (
    ) } export default CpuArchitectureDemo ===== COMPONENT: magnetize-button ===== Title: Magnetize Button Description: A magnetic particle-attraction button built with Framer Motion. --- file: gammaui/magnetize-button.tsx --- "use client" import * as React from "react" import { useCallback, useEffect, useState } from "react" import { Magnet } from "lucide-react" import { motion, useAnimation } from "motion/react" import { cn } from "@/lib/utils" import { Button } from "@/components/ui/button" interface MagnetizeButtonProps extends React.ButtonHTMLAttributes { particleCount?: number attractRadius?: number } interface Particle { id: number x: number y: number } export default function MagnetizeButton({ className, particleCount = 12, attractRadius = 50, ...props }: MagnetizeButtonProps) { const [isAttracting, setIsAttracting] = useState(false) const [particles, setParticles] = useState([]) const particlesControl = useAnimation() useEffect(() => { const newParticles = Array.from({ length: particleCount }, (_, i) => ({ id: i, x: Math.random() * 360 - 180, y: Math.random() * 360 - 180, })) setParticles(newParticles) }, [particleCount]) const handleInteractionStart = useCallback(async () => { setIsAttracting(true) await particlesControl.start({ x: 0, y: 0, transition: { type: "spring", stiffness: 50, damping: 10, }, }) }, [particlesControl]) const handleInteractionEnd = useCallback(async () => { setIsAttracting(false) await particlesControl.start((i) => ({ x: particles[i].x, y: particles[i].y, transition: { type: "spring", stiffness: 100, damping: 15, }, })) }, [particlesControl, particles]) return ( ) } ===== EXAMPLE: magnetize-button-demo ===== Title: Magnetize Button Demo --- file: example/magnetize-button-demo.tsx --- import MagnetizeButton from "@/registry/gammaui/magnetize-button" export default function MagnetizeButtonDemo() { return ( ) } ===== COMPONENT: overlay-button ===== Title: Overlay Button Description: An animated gradient button using Framer Motion with looping motion and spring transitions. --- file: gammaui/overlay-button.tsx --- "use client" import { motion } from "motion/react" interface OverlayButtonProps { label: string } export default function OverlayButton({ label }: OverlayButtonProps) { return ( {label} ) } ===== EXAMPLE: overlay-button-demo ===== Title: Overlay Button Demo --- file: example/overlay-button-demo.tsx --- import OverlayButton from "../gammaui/overlay-button" export default function OverlayButtonDemo() { return } ===== COMPONENT: pixel-button ===== Title: Pixel Button Description: A pixelated button animation effect built with Tailwind CSS and React. --- file: gammaui/pixel-button.tsx --- "use client" import React, { useEffect, useRef, useState } from "react" interface PixelButtonProps { children: React.ReactNode color?: string } export const PixelButton: React.FC = ({ children, color = "#ff5722", }) => { const containerRef = useRef(null) const buttonRef = useRef(null) const [hovered, setHovered] = useState(false) useEffect(() => { const button = buttonRef.current const pixelContainer = containerRef.current if (!button || !pixelContainer) return const pixelSize = 10 const btnWidth = button.offsetWidth const btnHeight = button.offsetHeight const cols = Math.floor(btnWidth / pixelSize) const rows = Math.floor(btnHeight / pixelSize) // Clear old pixels pixelContainer.innerHTML = "" for (let row = 0; row < rows; row++) { for (let col = 0; col < cols; col++) { const pixel = document.createElement("div") pixel.className = "absolute w-[10px] h-[10px] border border-black/25 opacity-0 transition-opacity duration-500 ease-in-out" pixel.style.background = color pixel.style.left = `${col * pixelSize}px` pixel.style.top = `${row * pixelSize}px` pixel.style.transitionDelay = `${Math.random() * 0.8}s` pixelContainer.appendChild(pixel) } } }, [color]) useEffect(() => { // When hover state changes, toggle opacity const pixels = containerRef.current?.querySelectorAll("div") if (!pixels) return pixels.forEach((pixel) => { pixel.style.opacity = hovered ? "1" : "0" }) }, [hovered]) return ( ) } ===== EXAMPLE: pixel-button-demo ===== Title: Pixel Button Demo --- file: example/pixel-button-demo.tsx --- "use client" import { PixelButton } from "@/registry/gammaui/pixel-button" export default function PixelButtonDemo() { return (
    Pixel Button Hover Me
    ) } ===== COMPONENT: plasma ===== Title: Plasma Description: A mesmerizing plasma shader animation rendered with WebGL, perfect for dynamic backgrounds and previews. --- file: gammaui/plasma.tsx --- "use client" import React, { useEffect, useRef } from "react" export const Plasma: React.FC = () => { const canvasRef = useRef(null) // Vertex shader const vsSource = ` attribute vec4 aVertexPosition; void main() { gl_Position = aVertexPosition; } ` // Fragment shader (same as before) const fsSource = `precision highp float; uniform vec2 iResolution; uniform float iTime; const float overallSpeed = 0.2; const float gridSmoothWidth = 0.015; const float axisWidth = 0.05; const float majorLineWidth = 0.025; const float minorLineWidth = 0.0125; const float majorLineFrequency = 5.0; const float minorLineFrequency = 1.0; const vec4 gridColor = vec4(0.5); const float scale = 5.0; const vec4 lineColor = vec4(0.4, 0.2, 0.8, 1.0); const float minLineWidth = 0.01; const float maxLineWidth = 0.2; const float lineSpeed = 1.0 * overallSpeed; const float lineAmplitude = 1.0; const float lineFrequency = 0.2; const float warpSpeed = 0.2 * overallSpeed; const float warpFrequency = 0.5; const float warpAmplitude = 1.0; const float offsetFrequency = 0.5; const float offsetSpeed = 1.33 * overallSpeed; const float minOffsetSpread = 0.6; const float maxOffsetSpread = 2.0; const int linesPerGroup = 16; #define drawCircle(pos, radius, coord) smoothstep(radius + gridSmoothWidth, radius, length(coord - (pos))) #define drawSmoothLine(pos, halfWidth, t) smoothstep(halfWidth, 0.0, abs(pos - (t))) #define drawCrispLine(pos, halfWidth, t) smoothstep(halfWidth + gridSmoothWidth, halfWidth, abs(pos - (t))) #define drawPeriodicLine(freq, width, t) drawCrispLine(freq / 2.0, width, abs(mod(t, freq) - (freq) / 2.0)) float drawGridLines(float axis) { return drawCrispLine(0.0, axisWidth, axis) + drawPeriodicLine(majorLineFrequency, majorLineWidth, axis) + drawPeriodicLine(minorLineFrequency, minorLineWidth, axis); } float drawGrid(vec2 space) { return min(1.0, drawGridLines(space.x) + drawGridLines(space.y)); } float random(float t) { return (cos(t) + cos(t * 1.3 + 1.3) + cos(t * 1.4 + 1.4)) / 3.0; } float getPlasmaY(float x, float horizontalFade, float offset) { return random(x * lineFrequency + iTime * lineSpeed) * horizontalFade * lineAmplitude + offset; } void main() { vec2 fragCoord = gl_FragCoord.xy; vec4 fragColor; vec2 uv = fragCoord.xy / iResolution.xy; vec2 space = (fragCoord - iResolution.xy / 2.0) / iResolution.x * 2.0 * scale; float horizontalFade = 1.0 - (cos(uv.x * 6.28) * 0.5 + 0.5); float verticalFade = 1.0 - (cos(uv.y * 6.28) * 0.5 + 0.5); space.y += random(space.x * warpFrequency + iTime * warpSpeed) * warpAmplitude * (0.5 + horizontalFade); space.x += random(space.y * warpFrequency + iTime * warpSpeed + 2.0) * warpAmplitude * horizontalFade; vec4 lines = vec4(0.0); vec4 bgColor1 = vec4(0.1, 0.1, 0.3, 1.0); vec4 bgColor2 = vec4(0.3, 0.1, 0.5, 1.0); for(int l = 0; l < linesPerGroup; l++) { float normalizedLineIndex = float(l) / float(linesPerGroup); float offsetTime = iTime * offsetSpeed; float offsetPosition = float(l) + space.x * offsetFrequency; float rand = random(offsetPosition + offsetTime) * 0.5 + 0.5; float halfWidth = mix(minLineWidth, maxLineWidth, rand * horizontalFade) / 2.0; float offset = random(offsetPosition + offsetTime * (1.0 + normalizedLineIndex)) * mix(minOffsetSpread, maxOffsetSpread, horizontalFade); float linePosition = getPlasmaY(space.x, horizontalFade, offset); float line = drawSmoothLine(linePosition, halfWidth, space.y) / 2.0 + drawCrispLine(linePosition, halfWidth * 0.15, space.y); float circleX = mod(float(l) + iTime * lineSpeed, 25.0) - 12.0; vec2 circlePosition = vec2(circleX, getPlasmaY(circleX, horizontalFade, offset)); float circle = drawCircle(circlePosition, 0.01, space) * 4.0; line = line + circle; lines += line * lineColor * rand; } fragColor = mix(bgColor1, bgColor2, uv.x); fragColor *= verticalFade; fragColor.a = 1.0; fragColor += lines; gl_FragColor = fragColor; } ` const loadShader = ( gl: WebGLRenderingContext, type: number, source: string ): WebGLShader | null => { const shader = gl.createShader(type) if (!shader) return null gl.shaderSource(shader, source) gl.compileShader(shader) if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { console.error("Shader compile error:", gl.getShaderInfoLog(shader)) gl.deleteShader(shader) return null } return shader } const initShaderProgram = ( gl: WebGLRenderingContext, vsSource: string, fsSource: string ): WebGLProgram | null => { const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vsSource) const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fsSource) if (!vertexShader || !fragmentShader) return null const shaderProgram = gl.createProgram() if (!shaderProgram) return null gl.attachShader(shaderProgram, vertexShader) gl.attachShader(shaderProgram, fragmentShader) gl.linkProgram(shaderProgram) if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { console.error( "Shader program link error:", gl.getProgramInfoLog(shaderProgram) ) return null } return shaderProgram } useEffect(() => { const canvas = canvasRef.current if (!canvas) return const gl = canvas.getContext("webgl") if (!gl) { console.warn("WebGL not supported.") return } const shaderProgram = initShaderProgram(gl, vsSource, fsSource) if (!shaderProgram) return const positionBuffer = gl.createBuffer() gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer) const positions = new Float32Array([-1, -1, 1, -1, -1, 1, 1, 1]) gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW) const programInfo = { program: shaderProgram, attribLocations: { vertexPosition: gl.getAttribLocation(shaderProgram, "aVertexPosition"), }, uniformLocations: { resolution: gl.getUniformLocation(shaderProgram, "iResolution"), time: gl.getUniformLocation(shaderProgram, "iTime"), }, } const resizeCanvas = () => { const rect = canvas.getBoundingClientRect() canvas.width = rect.width * window.devicePixelRatio canvas.height = rect.height * window.devicePixelRatio gl.viewport(0, 0, canvas.width, canvas.height) } resizeCanvas() const observer = new ResizeObserver(resizeCanvas) observer.observe(canvas) const startTime = Date.now() const render = () => { const currentTime = (Date.now() - startTime) / 1000 gl.clearColor(0, 0, 0, 1) gl.clear(gl.COLOR_BUFFER_BIT) gl.useProgram(programInfo.program) if (programInfo.uniformLocations.resolution) gl.uniform2f( programInfo.uniformLocations.resolution, canvas.width, canvas.height ) if (programInfo.uniformLocations.time) gl.uniform1f(programInfo.uniformLocations.time, currentTime) gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer) gl.vertexAttribPointer( programInfo.attribLocations.vertexPosition, 2, gl.FLOAT, false, 0, 0 ) gl.enableVertexAttribArray(programInfo.attribLocations.vertexPosition) gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4) requestAnimationFrame(render) } requestAnimationFrame(render) return () => observer.disconnect() }, []) // ❗ Remove fixed/full-screen styling: return ( ) } export default Plasma ===== EXAMPLE: plasma-demo ===== Title: Plasma Demo --- file: example/plasma-demo.tsx --- import Plasma from "@/registry/gammaui/plasma" export default function PlasmaDemo() { return (
    ) } ===== COMPONENT: pricing-interaction ===== Title: Pricing Interaction Description: An interactive pricing switcher with animated numbers, selectable plans, and monthly/yearly billing modes. --- file: gammaui/pricing-interaction.tsx --- "use client" import React from "react" import NumberFlow from "@number-flow/react" export default function PricingInteraction() { const [active, setActive] = React.useState(0) const [period, setPeriod] = React.useState(0) const handleChangePlan = (index: number) => { setActive(index) } const handleChangePeriod = (index: number) => { setPeriod(index) if (index === 0) { setStarter(9.99) setPro(19.99) } else { setStarter(7.49) setPro(17.49) } } const [starter, setStarter] = React.useState(9.99) const [pro, setPro] = React.useState(19.99) return (
    handleChangePlan(0)} >

    Free

    $0.00 /month

    handleChangePlan(1)} >

    Starter{" "} Popular

    $ /month

    handleChangePlan(2)} >

    Pro

    $ /month

    ) } ===== EXAMPLE: pricing-interaction-demo ===== Title: Pricing Interaction Demo --- file: example/pricing-interaction-demo.tsx --- import PricingInteraction from "@/registry/gammaui/pricing-interaction" export default function PricingInteractionDemo() { return } ===== COMPONENT: radial-intro ===== Title: Radial Intro Description: A dynamic radial orbit animation built with Motion and React, featuring upright spinning image nodes. --- file: gammaui/radial-intro.tsx --- "use client" import * as React from "react" import { delay, LayoutGroup, motion, useAnimate, type AnimationSequence, type Transition, } from "motion/react" interface RadialIntroProps { orbitItems: OrbitItem[] stageSize?: number imageSize?: number } interface OrbitItem { id: number name: string src: string } const transition: Transition = { delay: 0, stiffness: 300, damping: 35, type: "spring", restSpeed: 0.01, restDelta: 0.01, } const spinConfig = { duration: 30, ease: "linear" as const, repeat: Infinity, } const qsa = (root: Element, sel: string) => Array.from(root.querySelectorAll(sel)) const angleOf = (el: Element) => Number((el as HTMLElement).dataset.angle || 0) const armOfImg = (img: Element) => (img as HTMLElement).closest("[data-arm]") as HTMLElement | null export default function RadialIntro({ orbitItems, stageSize = 320, imageSize = 60, }: RadialIntroProps) { const step = 360 / orbitItems.length const [scope, animate] = useAnimate() React.useEffect(() => { const root = scope.current if (!root) return // get arm and image elements const arms = qsa(root, "[data-arm]") const imgs = qsa(root, "[data-arm-image]") const stops: (() => void)[] = [] // image lift-in delay(() => animate(imgs, { top: 0 }, transition), 250) // build sequence for orbit placement const orbitPlacementSequence: AnimationSequence = [ ...arms.map((el): [Element, Record, any] => [ el, { rotate: angleOf(el) }, { ...transition, at: 0 }, ]), ...imgs.map((img): [Element, Record, any] => [ img, { rotate: -angleOf(armOfImg(img)!), opacity: 1 }, { ...transition, at: 0 }, ]), ] // play placement sequence delay(() => animate(orbitPlacementSequence), 700) // start continuous spin for arms and images delay(() => { // arms spin clockwise arms.forEach((el) => { const angle = angleOf(el) const ctrl = animate(el, { rotate: [angle, angle + 360] }, spinConfig) stops.push(() => ctrl.cancel()) }) // images counter-spin to stay upright imgs.forEach((img) => { const arm = armOfImg(img) const angle = arm ? angleOf(arm) : 0 const ctrl = animate( img, { rotate: [-angle, -angle - 360] }, spinConfig ) stops.push(() => ctrl.cancel()) }) }, 1300) return () => stops.forEach((stop) => stop()) }, []) return ( {orbitItems.map((item, i) => ( ))} ) } ===== EXAMPLE: radial-intro-demo ===== Title: Radial Intro Demo --- file: example/radial-intro-demo.tsx --- import RadialIntro from "@/registry/gammaui/radial-intro" const ITEMS = [ { id: 1, name: "Gamma UI", src: "/gamma-ui-dark.svg", }, { id: 2, name: "arhamkhnz", src: "https://pbs.twimg.com/profile_images/1897311929028255744/otxpL-ke_400x400.jpg", }, { id: 3, name: "Skyleen", src: "https://pbs.twimg.com/profile_images/1948770261848756224/oPwqXMD6_400x400.jpg", }, { id: 4, name: "Shadcn", src: "https://pbs.twimg.com/profile_images/1593304942210478080/TUYae5z7_400x400.jpg", }, { id: 5, name: "Adam Wathan", src: "https://pbs.twimg.com/profile_images/1677042510839857154/Kq4tpySA_400x400.jpg", }, { id: 6, name: "Guillermo Rauch", src: "https://pbs.twimg.com/profile_images/1783856060249595904/8TfcCN0r_400x400.jpg", }, { id: 7, name: "Jhey", src: "https://pbs.twimg.com/profile_images/1534700564810018816/anAuSfkp_400x400.jpg", }, { id: 8, name: "David Haz", src: "https://pbs.twimg.com/profile_images/1927474594102784000/Al0g-I6o_400x400.jpg", }, { id: 9, name: "Matt Perry", src: "https://pbs.twimg.com/profile_images/1690345911149375488/wfD0Ai9j_400x400.jpg", }, ] export default function RadialIntroDemo() { return (
    ) } ===== COMPONENT: shadcn-ui ===== Title: Shadcn UI Description: Animated ShadCN UI SVG with glowing masked paths, motion shapes, and dynamic shimmering text with customizable props. --- file: gammaui/shadcn-ui.tsx --- "use client" import { AnimationGeneratorType, motion } from "motion/react" import { useTheme } from "next-themes" import { cn } from "@/lib/utils" import { TextShimmer } from "@/components/motion-primitives/text-shimmer" const YourAppVariants = { hidden: { opacity: 0, scale: 0.5 }, visible: { opacity: 0.2, scale: 1, }, } const YourAppTransition: { duration: number type: AnimationGeneratorType damping: number bounce: number mass: number } = { duration: 0.2, type: "spring", damping: 20, bounce: 0.1, mass: 2, } interface ShadCnUIProps { className?: string circleText?: string triangleText?: string diamondText?: string animatedText?: string animatedBoxText?: string hintText?: string appName?: string } export default function ShadCnUI({ className, animatedBoxText = "Sidebar", animatedText = "npx shadcn add", appName = "Your App", circleText = "Registry", diamondText = "Registry", hintText = "(components.json)", triangleText = "AI", }: ShadCnUIProps) { const { resolvedTheme } = useTheme() return (
    {/* SVG */} {/* Paths */} {/* Left-Hand Shapes */} {/* Registry Circle */} {circleText} {/* AI Trinangle */} {triangleText} {/* Registry Polygon */} {diamondText} {/* Right-Hand Shapes */} {appName} {/* Animated Lights */} {/* Animated - Comp Box */} {""} {animatedBoxText} {/* Shadcn Box */}
    {/* Shadcn TextBox */}

    {hintText}

    {animatedText}
    ) } ===== EXAMPLE: shadcn-ui-demo ===== Title: Shadcn UI Demo --- file: example/shadcn-ui-demo.tsx --- "use client" import ShadCnUI from "@/registry/gammaui/shadcn-ui" const Page = () => { return (
    ) } export default Page ===== COMPONENT: support-box ===== Title: Support Box Description: A collapsible animated support widget for quick help actions using Framer Motion. --- file: gammaui/support-box.tsx --- "use client" import { useState } from "react" import { AnimatePresence, motion, Variants } from "motion/react" interface MenuItem { title: string description: string icon: React.ReactNode onPress: () => void } interface SupportBoxProps { title?: string items: MenuItem[] collapsedWidth?: number collapsedHeight?: number expandedWidth?: number expandedHeight?: number className?: string containerClassName?: string titleClassName?: string itemClassName?: string bgColor?: string borderColor?: string } export function SupportBox({ items, title = "Need Help?", collapsedWidth = 120, collapsedHeight = 40, expandedWidth = 300, expandedHeight = 320, className = "", containerClassName = "bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700", titleClassName = "text-gray-800 dark:text-gray-100", itemClassName = "text-gray-800 dark:text-gray-100", bgColor = "", borderColor = "", }: SupportBoxProps) { const [isExpanded, setIsExpanded] = useState(false) const boxVariants: Variants = { collapsed: { width: collapsedWidth, height: collapsedHeight, transition: { type: "spring" as const, stiffness: 200, damping: 25 }, }, expanded: { width: expandedWidth, height: expandedHeight, transition: { type: "spring" as const, stiffness: 180, damping: 22 }, }, } const contentVariants: Variants = { hidden: { opacity: 0, y: 20 }, visible: { opacity: 1, y: 0, transition: { delay: 0.1, duration: 0.25, ease: "easeOut" }, }, exit: { opacity: 0, y: 20, transition: { duration: 0.2 } }, } return (
    {isExpanded ? (

    {title}

    setIsExpanded(false)} className="text-gray-500 transition-all hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200" aria-label="Close menu" whileHover={{ scale: 1.2 }} whileTap={{ scale: 0.9 }} > ✕
    {items.map((option, index) => ( {option.icon}

    {option.title}

    {option.description}

    ))}
    ) : ( setIsExpanded(true)} className={`flex h-full w-full items-center justify-center font-medium ${titleClassName}`} aria-label={`Expand ${title}`} variants={contentVariants} initial="hidden" animate="visible" exit="exit" whileHover={{ scale: 1.05 }} whileTap={{ scale: 0.95 }} > {title} )}
    ) } export default SupportBox ===== EXAMPLE: support-box-demo ===== Title: Support Box Demo --- file: example/support-box-demo.tsx --- "use client" import { IconBook, IconBug, IconBulb, IconMessageCircle, } from "@tabler/icons-react" import { toast } from "sonner" import { SupportBox } from "@/registry/gammaui/support-box" export default function SupportBoxDemo() { const helpItems = [ { title: "Contact Support", description: "Chat with our support team for assistance.", icon: , onPress: () => toast("Opening support chat..."), }, { title: "Documentation", description: "Browse our guides and API documentation.", icon: , onPress: () => toast("Opening documentation..."), }, { title: "Report a Bug", description: "Found an issue? Let us know!", icon: , onPress: () => toast("Redirecting to bug report form..."), }, { title: "Request a Feature", description: "Suggest improvements or new features.", icon: , onPress: () => toast("Opening feature request page..."), }, ] return (
    ) } ===== COMPONENT: text-roll ===== Title: Text Roll Description: A rolling hover-animated text component built with Motion One. --- file: gammaui/text-roll.tsx --- "use client" import React from "react" import { motion } from "motion/react" import { cn } from "@/lib/utils" const STAGGER = 0.035 export default function TextRoll({ children, className, center = false, }: { children: string className?: string center?: boolean }) { return ( {/* Top Text (Slides up) */}
    {children.split("").map((l, i) => { const delay = center ? STAGGER * Math.abs(i - (children.length - 1) / 2) : STAGGER * i return ( {l} ) })}
    {/* Bottom Text (Slides in from bottom) */}
    {children.split("").map((l, i) => { const delay = center ? STAGGER * Math.abs(i - (children.length - 1) / 2) : STAGGER * i return ( {l} ) })}
    ) } ===== EXAMPLE: text-roll-demo ===== Title: Text Roll Demo --- file: example/text-roll-demo.tsx --- import TextRoll from "@/registry/gammaui/text-roll" const navigationItems = [ { name: "Home", href: "/", description: "[0]", }, { name: "Components", href: "/components", description: "[1]", }, { name: "Pricing", href: "/pricing", description: "[2]", }, { name: "How to use", href: "/docs/quick-start", description: "[3]", }, { name: "Account", href: "/user", description: "[4]", }, { name: "Login", href: "/login", description: "[7]", }, ] export default function TextRollDemo() { return (
      {navigationItems.map((item, index) => (
    • {item.name}
    • ))}
    ) } ===== COMPONENT: usage-card ===== Title: Usage Card Description: An animated system monitor card that displays a title, percentage, and neon-lit pill indicators driven by Motion. --- file: gammaui/usage-card.tsx --- "use client" import { useEffect, useState } from "react" import { animate, motion, useMotionValue, useTransform } from "motion/react" interface UsageCardProps { title: string percentage: number pillCount?: number accentColor?: string icon?: React.ReactNode } function useAnimatedCounter(target: number, duration = 1.2) { const count = useMotionValue(0) const rounded = useTransform(count, (v) => Math.round(v)) const [display, setDisplay] = useState(0) useEffect(() => { const controls = animate(count, target, { duration, ease: [0.16, 1, 0.3, 1], }) const unsub = rounded.on("change", setDisplay) return () => { controls.stop() unsub() } }, [target]) return display } function Pill({ active, index, accent, }: { active: boolean index: number accent: string }) { return ( {/* Track */}
    {/* Fill */} {/* Shimmer */} {active && ( )} ) } export default function UsageCard({ title, percentage, pillCount = 10, accentColor = "#00f5ff", icon, }: UsageCardProps) { const display = useAnimatedCounter(percentage) const activePills = Math.round((percentage / 100) * pillCount) const accent = percentage >= 75 ? "#ff2d55" : accentColor return (
    {/* Outer glow */}
    {/* Card */}
    {/* Top row */}
    {icon} {title}
    {display} %
    {/* Pills */}
    {Array.from({ length: pillCount }).map((_, i) => ( ))}
    ) } ===== EXAMPLE: usage-card-demo ===== Title: Usage Card Demo --- file: example/usage-card-demo.tsx --- "use client" import { useEffect, useState } from "react" import { IconCpu } from "@tabler/icons-react" import UsageCard from "@/registry/gammaui/usage-card" const UsageCardDemo = () => { const [cpuVal, setCpuVal] = useState(72) useEffect(() => { const id = setInterval(() => { setCpuVal((v) => Math.min(100, Math.max(0, v + (Math.random() - 0.5) * 10)) ) }, 2000) return () => clearInterval(id) }, []) return (
    } />
    ) } export default UsageCardDemo ===== COMPONENT: voxel-wall ===== Title: Voxel Wall Description: A dark voxel tunnel with volumetric light rays, drifting cubes, and mouse-driven camera parallax. --- file: gammaui/voxel-wall/camera-rig.tsx --- "use client" import { useEffect, useRef } from "react" import { useFrame, useThree } from "@react-three/fiber" import * as THREE from "three" import { HOLE_CENTER } from "./wall" const BASE_POSITION = new THREE.Vector3(0.15, -2.35, 4.2) const LOOK_TARGET = new THREE.Vector3( HOLE_CENTER.x, HOLE_CENTER.y - 0.1, HOLE_CENTER.z + 1.5 ) export default function CameraRig() { const mouse = useRef({ x: 0, y: 0 }) const { camera } = useThree() useEffect(() => { const onPointerMove = (event: PointerEvent) => { mouse.current.x = (event.clientX / window.innerWidth) * 2 - 1 mouse.current.y = -(event.clientY / window.innerHeight) * 2 + 1 } window.addEventListener("pointermove", onPointerMove, { passive: true }) return () => window.removeEventListener("pointermove", onPointerMove) }, []) useFrame(({ clock }) => { const t = clock.elapsedTime const swayX = Math.sin(t * 0.18) * 0.05 + Math.cos(t * 0.14) * 0.03 const swayY = Math.cos(t * 0.16) * 0.025 + Math.sin(t * 0.11) * 0.018 const parallaxX = mouse.current.x * 0.18 const parallaxY = mouse.current.y * 0.1 camera.position.x = THREE.MathUtils.lerp( camera.position.x, BASE_POSITION.x + swayX + parallaxX, 0.05 ) camera.position.y = THREE.MathUtils.lerp( camera.position.y, BASE_POSITION.y + swayY + parallaxY * 0.35, 0.05 ) camera.position.z = THREE.MathUtils.lerp( camera.position.z, BASE_POSITION.z + Math.sin(t * 0.12) * 0.06, 0.04 ) const lookAt = LOOK_TARGET.clone() lookAt.x += parallaxX * 0.12 lookAt.y += parallaxY * 0.08 camera.lookAt(lookAt) }) return null } --- file: gammaui/voxel-wall/light-rays.tsx --- "use client" import { forwardRef, useMemo, useRef } from "react" import { useFrame } from "@react-three/fiber" import { Bloom, EffectComposer, GodRays, Noise, Vignette, } from "@react-three/postprocessing" import { BlendFunction } from "postprocessing" import * as THREE from "three" import { HOLE_CENTER, HOLE_SIZE, LIGHT_ORIGIN, ROOM_DEPTH } from "./wall" interface LightSourceProps { position?: [number, number, number] } export const LightSource = forwardRef( function LightSource({ position }, ref) { useFrame(({ clock }) => { if (!ref || typeof ref === "function" || !ref.current) return const pulse = 0.96 + Math.sin(clock.elapsedTime * 1.2) * 0.04 const mat = ref.current.material as THREE.MeshBasicMaterial mat.opacity = pulse }) return ( ) } ) export function DustMotes() { const pointsRef = useRef(null) const positions = useMemo(() => { const count = 220 const arr = new Float32Array(count * 3) for (let i = 0; i < count; i++) { arr[i * 3] = HOLE_CENTER.x + (Math.random() - 0.5) * HOLE_SIZE.width * 1.6 arr[i * 3 + 1] = HOLE_CENTER.y + (Math.random() - 0.5) * HOLE_SIZE.height * 1.5 arr[i * 3 + 2] = -ROOM_DEPTH + Math.random() * (ROOM_DEPTH + 2) } return arr }, []) const geometry = useMemo(() => { const geo = new THREE.BufferGeometry() geo.setAttribute("position", new THREE.BufferAttribute(positions, 3)) return geo }, [positions]) useFrame(({ clock }) => { const points = pointsRef.current if (!points) return points.rotation.z = Math.sin(clock.elapsedTime * 0.06) * 0.015 }) return ( ) } interface LightEffectsProps { sun: THREE.Mesh } export function LightEffects({ sun }: LightEffectsProps) { return ( ) } export function SceneLights() { return ( <> ) } --- file: gammaui/voxel-wall/scene.tsx --- "use client" import { Suspense, useEffect, useLayoutEffect, useRef, useState } from "react" import { Canvas } from "@react-three/fiber" import * as THREE from "three" import CameraRig from "./camera-rig" import { DustMotes, LightEffects, LightSource, SceneLights } from "./light-rays" import VoxelWall from "./wall" interface VoxelWallSceneProps { className?: string height?: string | number } function SceneContents() { const sunRef = useRef(null!) const [sun, setSun] = useState(null) useLayoutEffect(() => { if (sunRef.current) setSun(sunRef.current) }, []) return ( <> {sun && } ) } export default function VoxelWallScene({ className, height = "100%", }: VoxelWallSceneProps) { const [mounted, setMounted] = useState(false) useEffect(() => { setMounted(true) }, []) return (
    {mounted ? ( ) : null}
    ) } --- file: gammaui/voxel-wall/wall.tsx --- "use client" import { useEffect, useMemo, useRef } from "react" import { useFrame } from "@react-three/fiber" import * as THREE from "three" const SPACING = 0.44 const CUBE = 0.38 const ROOM_W = 9.2 const ROOM_H = 6.4 const ROOM_D = 10.5 const HOLE_CX = 0.55 const HOLE_CY = 0.85 const HOLE_W = 3.4 const HOLE_H = 2.55 interface CubeInstance { x: number y: number z: number rotX: number rotY: number rotZ: number scale: number jitter: boolean phase: number driftSpeed: number maxDrift: number } const LIGHT_TARGET = { x: HOLE_CX, y: HOLE_CY, z: -ROOM_D - 0.35, } function mulberry32(seed: number) { return () => { seed |= 0 seed = (seed + 0x6d2b79f5) | 0 let t = Math.imul(seed ^ (seed >>> 15), 1 | seed) t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t return ((t ^ (t >>> 14)) >>> 0) / 4294967296 } } function isInHole( x: number, y: number, z: number, rand: () => number ): false | true | "debris" { const backZ = -ROOM_D if (Math.abs(z - backZ) > SPACING * 0.6) return false const dx = Math.abs(x - HOLE_CX) const dy = Math.abs(y - HOLE_CY) const inside = dx < HOLE_W / 2 && dy < HOLE_H / 2 if (!inside) return false const edgeDist = Math.min(HOLE_W / 2 - dx, HOLE_H / 2 - dy) if (edgeDist < SPACING * 0.85) { if (rand() < 0.42) return true if (rand() < 0.72) return "debris" return false } if (edgeDist < SPACING * 1.6 && rand() < 0.38) return "debris" return true } function pushCube( cubes: CubeInstance[], x: number, y: number, z: number, rand: () => number, edge = false, debris = false ) { const spread = edge || debris ? 0.22 : 0.06 const bx = x + (rand() - 0.5) * spread const by = y + (rand() - 0.5) * spread const bz = z + (rand() - 0.5) * (debris ? 0.5 : spread) const dx = LIGHT_TARGET.x - bx const dy = LIGHT_TARGET.y - by const dz = LIGHT_TARGET.z - bz const dist = Math.hypot(dx, dy, dz) cubes.push({ x: bx, y: by, z: bz, rotX: edge || debris ? (rand() - 0.5) * 0.7 : 0, rotY: edge || debris ? (rand() - 0.5) * 0.9 : 0, rotZ: edge || debris ? (rand() - 0.5) * 0.55 : 0, scale: CUBE * (debris ? 0.65 + rand() * 0.45 : edge ? 0.78 + rand() * 0.35 : 0.92 + rand() * 0.12), jitter: edge || debris, phase: rand() * Math.PI * 2, driftSpeed: debris ? 0.18 + rand() * 0.14 : edge ? 0.1 + rand() * 0.07 : 0.03 + rand() * 0.025, maxDrift: debris ? dist * 0.98 : edge ? dist * 0.55 : dist * 0.18, }) } function generateTunnel(): CubeInstance[] { const rand = mulberry32(481516) const cubes: CubeInstance[] = [] const halfW = ROOM_W / 2 const halfH = ROOM_H / 2 const backZ = -ROOM_D for (let z = 0; z >= backZ; z -= SPACING) { for (let x = -halfW; x <= halfW; x += SPACING) { const hole = isInHole(x, -halfH, z, rand) if (hole === true) continue if (hole === "debris") { pushCube(cubes, x, -halfH, z + rand() * 0.4, rand, true, true) continue } pushCube(cubes, x, -halfH, z, rand, z < backZ + SPACING * 2) } } for (let z = 0; z >= backZ; z -= SPACING) { for (let x = -halfW; x <= halfW; x += SPACING) { const hole = isInHole(x, halfH, z, rand) if (hole === true) continue if (hole === "debris") { pushCube(cubes, x, halfH, z + rand() * 0.3, rand, true, true) continue } pushCube(cubes, x, halfH, z, rand) } } for (let z = 0; z >= backZ; z -= SPACING) { for (let y = -halfH; y <= halfH; y += SPACING) { const hole = isInHole(-halfW, y, z, rand) if (hole === true) continue if (hole === "debris") { pushCube(cubes, -halfW, y, z + rand() * 0.35, rand, true, true) continue } pushCube(cubes, -halfW, y, z, rand, z < backZ + SPACING * 3) } } for (let z = 0; z >= backZ; z -= SPACING) { for (let y = -halfH; y <= halfH; y += SPACING) { if (Math.abs(y) < 0.2 && z > backZ + SPACING * 4 && rand() < 0.18) continue const hole = isInHole(halfW, y, z, rand) if (hole === true) continue if (hole === "debris") { pushCube(cubes, halfW, y, z + rand() * 0.35, rand, true, true) continue } pushCube(cubes, halfW, y, z, rand, z < backZ + SPACING * 2) } } for (let y = -halfH; y <= halfH; y += SPACING) { for (let x = -halfW; x <= halfW; x += SPACING) { const hole = isInHole(x, y, backZ, rand) if (hole === true) continue if (hole === "debris") { pushCube( cubes, x + (rand() - 0.5) * 0.35, y + (rand() - 0.5) * 0.35, backZ + rand() * 0.8, rand, true, true ) continue } const nearHole = Math.abs(x - HOLE_CX) < HOLE_W / 2 + SPACING * 1.5 && Math.abs(y - HOLE_CY) < HOLE_H / 2 + SPACING * 1.5 pushCube(cubes, x, y, backZ, rand, nearHole) } } for (let i = 0; i < 28; i++) { pushCube( cubes, HOLE_CX + (rand() - 0.5) * HOLE_W * 1.1, HOLE_CY + (rand() - 0.5) * HOLE_H * 1.1, backZ + 0.4 + rand() * 2.2, rand, true, true ) } return cubes } export default function VoxelWall() { const meshRef = useRef(null) const instances = useMemo(() => generateTunnel(), []) const dummy = useMemo(() => new THREE.Object3D(), []) useEffect(() => { const mesh = meshRef.current if (!mesh) return instances.forEach((cube, i) => { dummy.position.set(cube.x, cube.y, cube.z) dummy.rotation.set(cube.rotX, cube.rotY, cube.rotZ) dummy.scale.setScalar(cube.scale) dummy.updateMatrix() mesh.setMatrixAt(i, dummy.matrix) }) mesh.instanceMatrix.needsUpdate = true }, [instances, dummy]) useFrame(({ clock }) => { const mesh = meshRef.current if (!mesh) return const t = clock.elapsedTime instances.forEach((cube, i) => { const dx = LIGHT_TARGET.x - cube.x const dy = LIGHT_TARGET.y - cube.y const dz = LIGHT_TARGET.z - cube.z const dist = Math.hypot(dx, dy, dz) || 1 const cycle = (t * cube.driftSpeed + cube.phase) % 1 const eased = cycle * cycle * (3 - 2 * cycle) const suck = eased * eased const pull = suck * cube.maxDrift const px = cube.x + (dx / dist) * pull const py = cube.y + (dy / dist) * pull const pz = cube.z + (dz / dist) * pull const wobble = cube.jitter ? Math.sin(t * 1.4 + cube.phase) * 0.012 * (1 - suck * 0.6) : Math.sin(t * 0.6 + cube.phase) * 0.004 * (1 - suck * 0.4) dummy.position.set( px + Math.sin(t * 0.7 + cube.phase) * 0.003 * (1 - suck), py + wobble, pz ) dummy.rotation.set( cube.rotX + suck * 0.5 + Math.sin(t + cube.phase) * 0.01, cube.rotY + suck * 0.8, cube.rotZ + suck * 0.4 + Math.cos(t * 1.1 + cube.phase) * 0.01 ) dummy.scale.setScalar( cube.scale * (1 - suck * (cube.jitter ? 0.55 : 0.3)) ) dummy.updateMatrix() mesh.setMatrixAt(i, dummy.matrix) }) mesh.instanceMatrix.needsUpdate = true }) return ( ) } export const HOLE_CENTER = new THREE.Vector3(HOLE_CX, HOLE_CY, -ROOM_D - 0.35) export const HOLE_SIZE = { width: HOLE_W * 0.95, height: HOLE_H * 0.95, } export const LIGHT_ORIGIN = new THREE.Vector3( HOLE_CX + 0.9, HOLE_CY + 1.4, -ROOM_D - 2.2 ) export const ROOM_DEPTH = ROOM_D ===== EXAMPLE: voxel-wall-demo ===== Title: Voxel Wall Demo --- file: example/voxel-wall-demo.tsx --- import VoxelWallScene from "@/registry/gammaui/voxel-wall/scene" export default function VoxelWallDemo() { return (
    ) } ===== COMPONENT: wave-path ===== Title: Wave Path Description: A dynamic interactive SVG wave component that responds to pointer movement. --- file: gammaui/wave-path.tsx --- "use client" import React, { useEffect, useRef } from "react" import { cn } from "@/lib/utils" type WWavePathProps = React.ComponentProps<"div"> export default function WavePath({ className, ...props }: WWavePathProps) { const path = useRef(null) let progress = 0 let x = 0.2 let time = Math.PI / 2 let reqId: number | null = null useEffect(() => { setPath(progress) }, []) const setPath = (progress: number) => { const width = window.innerWidth * 0.7 if (path.current) { path.current.setAttributeNS( null, "d", `M0 100 Q${width * x} ${100 + progress * 0.6}, ${width} 100` ) } } const lerp = (x: number, y: number, a: number) => x * (1 - a) + y * a const manageMouseEnter = () => { if (reqId) { cancelAnimationFrame(reqId) resetAnimation() } } const manageMouseMove = (e: React.MouseEvent) => { const { movementY, clientX } = e if (path.current) { const pathBound = path.current.getBoundingClientRect() x = (clientX - pathBound.left) / pathBound.width progress += movementY setPath(progress) } } const manageMouseLeave = () => { animateOut() } const animateOut = () => { const newProgress = progress * Math.sin(time) progress = lerp(progress, 0, 0.025) time += 0.2 setPath(newProgress) if (Math.abs(progress) > 0.75) { reqId = requestAnimationFrame(animateOut) } else { resetAnimation() } } const resetAnimation = () => { time = Math.PI / 2 progress = 0 } return (
    ) } ===== EXAMPLE: wave-path-demo ===== Title: Wave Path Demo --- file: example/wave-path-demo.tsx --- "use client" import React from "react" import { cn } from "@/lib/utils" import WavePath from "@/registry/gammaui/wave-path" export default function Demo() { return (
    ) } ===== COMPONENT: wavy-text-block ===== Title: Wavy Text Block Description: A scroll-triggered wavy text animation component that creates flowing motion effects using Framer Motion and scroll context. --- file: gammaui/wavy-text-block.tsx --- "use client" import React from "react" import { HTMLMotionProps, motion, MotionValue, SpringOptions, useMotionValue, useReducedMotion, useScroll, useSpring, } from "motion/react" interface WavyTextsConfig { baseOffsetFactor: number baseExtra: number baseAmplitude: number lengthEffect: number frequency: number progressScale: number phaseShiftDeg: number spring: SpringOptions } interface WavyBlockItemProps extends HTMLMotionProps<"div"> { index: number config?: WavyTextsConfig } interface WavyBlockContextValue { scrollYProgress: MotionValue maxLen: number } const WavyBlockContext = React.createContext( undefined ) function useWavyBlockContext() { const context = React.useContext(WavyBlockContext) if (context === undefined) { throw new Error("useWavyBlockContext must be used within a WavyBlock") } return context } const toRadian = (deg: number) => (deg * Math.PI) / 180 export function WavyBlockItem({ index, config = { baseOffsetFactor: 0.1, baseExtra: 0, baseAmplitude: 160, lengthEffect: 0.6, frequency: 35, progressScale: 6, phaseShiftDeg: -180, spring: { damping: 22, stiffness: 300 }, }, style, ...props }: WavyBlockItemProps) { const { scrollYProgress, maxLen } = useWavyBlockContext() const reducedMotion = useReducedMotion() const lengthFactor = Math.min(1, Math.max(0, maxLen / (maxLen || 1))) const [isMounted, setIsMounted] = React.useState(false) const calculateX = React.useCallback( (p: number, windowWidth?: number) => { const phase = config.progressScale * p const width = windowWidth ?? (typeof window !== "undefined" ? window.innerWidth : 1200) const baseOffset = config.baseOffsetFactor * width + config.baseExtra const amplitudeScale = 1 - config.lengthEffect * lengthFactor const amplitude = config.baseAmplitude * amplitudeScale const angle = toRadian(config.frequency * index) + phase + toRadian(config.phaseShiftDeg) return baseOffset + amplitude * Math.cos(angle) }, [config, lengthFactor, index] ) const initialX = calculateX(0, 1200) const rawX = useMotionValue(initialX) const springX = useSpring(rawX, config.spring) const x = reducedMotion ? rawX : springX React.useLayoutEffect(() => { setIsMounted(true) }, []) React.useEffect(() => { if (!scrollYProgress || !isMounted) return const unsub = scrollYProgress.onChange((p) => { const windowWidth = typeof window !== "undefined" ? window.innerWidth : 1200 const newX = calculateX(p, windowWidth) rawX.set(newX) }) return () => { if (unsub) unsub() } }, [scrollYProgress, rawX, calculateX, isMounted]) return ( ) } export function WavyBlock({ offset = ["start end", "end start"], ...props }: React.ComponentPropsWithRef<"div"> & { offset?: any }) { const containerRef = React.useRef(null) const { current } = containerRef const maxLen = React.useMemo(() => { if (!current?.children || current.children.length === 0) return 1 const childrenArray = Array.from(current.children) return Math.max( ...childrenArray.map((child) => (child ? String(child).length : 0)) ) }, [current?.children]) const { scrollYProgress } = useScroll({ target: containerRef, offset: offset, }) return (
    ) } ===== EXAMPLE: wavy-text-block-demo ===== Title: Wavy Text Block Demo --- file: example/wavy-text-block-demo.tsx --- import { WavyBlock, WavyBlockItem } from "@/registry/gammaui/wavy-text-block" const titles = [ "Flexible", "Animated", "Customizable", "Optimized", "Lightweight", "Responsive", "UI Blocks", ] export default function DemoOne() { return (
    {titles.map((title, index) => (

    {title}

    ))}
    ) }