Docs
Cosmic Scene

Cosmic Scene

A dynamic, interactive background component with animated noise patterns and gradient effects.

Transform Your Ideas
Into Reality

We craft digital experiences that inspire, innovate, and elevate your brand to new heights. Lets create something extraordinary together.

Design Services

From concept to creation, we bring your vision to life with cutting-edge design solutions.

Explore Services →

Development

Powerful, scalable solutions built with modern technologies and best practices.

View Projects →

Installation

Copy and paste the following code into your project.

components/ui/cosmic-scene.tsx

import React, { useEffect, useRef, useState } from "react";
import { cn } from "@/lib/utils";
 
interface CosmicSceneProps extends React.HTMLAttributes<HTMLDivElement> {
  size?: string;
  colorScheme?:
    | "default"
    | "neon"
    | "sunset"
    | "ocean"
    | "aurora"
    | "cosmic"
    | "forest"
    | "desert"
    | "twilight"
    | "volcano"
    | "arctic"
    | "nebula"
    | "rainbow"
    | "midnight";
  interactive?: boolean;
  children?: React.ReactNode;
  overlayOpacity?: number;
}
 
export function CosmicScene({
  className,
  size = "20px",
  colorScheme = "default",
  interactive = true,
  children,
  overlayOpacity = 0,
  ...props
}: CosmicSceneProps) {
  const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 });
  const containerRef = useRef<HTMLDivElement>(null);
 
  const colorSchemes = {
    default:
      "from 180deg at 50% 70%, hsla(0,0%,98%,1) 0deg, #eec32d 72deg, #ec4b4b 144deg, #709ab9 216deg, #4dffbf 288deg, hsla(0,0%,98%,1) 1turn",
    neon: "from 180deg at 50% 70%, #ff00ff 0deg, #00ffff 120deg, #ffff00 240deg, #ff00ff 360deg",
    sunset:
      "from 180deg at 50% 70%, #ff7b00 0deg, #ff0055 120deg, #8900ff 240deg, #ff7b00 360deg",
    ocean:
      "from 180deg at 50% 70%, #00fff2 0deg, #0066ff 120deg, #002bff 240deg, #00fff2 360deg",
    aurora:
      "from 180deg at 50% 70%, #00ff87 0deg, #60efff 90deg, #0061ff 180deg, #60efff 270deg, #00ff87 360deg",
    cosmic:
      "from 180deg at 50% 70%, #6600ff 0deg, #ff00cc 90deg, #ff0066 180deg, #ff00cc 270deg, #6600ff 360deg",
    forest:
      "from 180deg at 50% 70%, #2ecc71 0deg, #27ae60 90deg, #145a32 180deg, #27ae60 270deg, #2ecc71 360deg",
    desert:
      "from 180deg at 50% 70%, #ff9500 0deg, #ff5e3a 90deg, #ff2d55 180deg, #ff5e3a 270deg, #ff9500 360deg",
    twilight:
      "from 180deg at 50% 70%, #141e30 0deg, #243b55 90deg, #2c5364 180deg, #243b55 270deg, #141e30 360deg",
    volcano:
      "from 180deg at 50% 70%, #dd1818 0deg, #ff4e00 90deg, #ff8700 180deg, #ff4e00 270deg, #dd1818 360deg",
    arctic:
      "from 180deg at 50% 70%, #74ebd5 0deg, #acb6e5 90deg, #9face6 180deg, #acb6e5 270deg, #74ebd5 360deg",
    nebula:
      "from 180deg at 50% 70%, #5f2c82 0deg, #49a09d 90deg, #24243e 180deg, #49a09d 270deg, #5f2c82 360deg",
    rainbow:
      "from 180deg at 50% 70%, #ff0000 0deg, #ff8000 60deg, #ffff00 120deg, #00ff00 180deg, #0000ff 240deg, #8000ff 300deg, #ff0000 360deg",
    midnight:
      "from 180deg at 50% 70%, #020024 0deg, #090979 90deg, #00d4ff 180deg, #090979 270deg, #020024 360deg",
  };
 
  useEffect(() => {
    if (!interactive || !containerRef.current) return;
 
    const handleMouseMove = (e: MouseEvent) => {
      const rect = containerRef.current?.getBoundingClientRect();
      if (!rect) return;
 
      const x = ((e.clientX - rect.left) / rect.width) * 100;
      const y = ((e.clientY - rect.top) / rect.height) * 100;
      setMousePosition({ x, y });
    };
 
    const container = containerRef.current;
    container.addEventListener("mousemove", handleMouseMove);
    return () => container.removeEventListener("mousemove", handleMouseMove);
  }, [interactive]);
 
  return (
    <div
      ref={containerRef}
      className={cn(
        "relative h-full min-h-[400px] w-full overflow-hidden bg-[hsl(0_0%_6%)]",
        className,
      )}
      {...props}
    >
      <div
        className="absolute inset-0 h-full w-full animate-flicker transition-transform duration-300 ease-out"
        style={{
          background: `conic-gradient(${colorSchemes[colorScheme]})`,
          transform: interactive
            ? `perspective(1000px) rotateX(${
                (mousePosition.y - 50) * 0.02
              }deg) rotateY(${(mousePosition.x - 50) * 0.02}deg)`
            : undefined,
          WebkitMaskImage: `radial-gradient(circle at ${
            interactive ? `${mousePosition.x}% ${mousePosition.y}%` : "50% 50%"
          }, white 2px, transparent 2.5px), url("https://assets.codepen.io/605876/noise-mask.png")`,
          WebkitMaskSize: `${size} ${size}, 256px 256px`,
          WebkitMaskPosition: "50% 50%, 4px 50%",
          WebkitMaskComposite: "intersect",
          maskImage: `radial-gradient(circle at ${
            interactive ? `${mousePosition.x}% ${mousePosition.y}%` : "50% 50%"
          }, white 2px, transparent 2.5px), url("https://assets.codepen.io/605876/noise-mask.png")`,
          maskSize: `${size} ${size}, 256px 256px`,
          maskPosition: "50% 50%, 4px 50%",
          maskComposite: "intersect",
        }}
      />
      {overlayOpacity > 0 && (
        <div
          className="absolute inset-0 bg-black transition-opacity duration-300"
          style={{ opacity: overlayOpacity }}
        />
      )}
      {children}
    </div>
  );
}

Update your globals.css

Add the following animations to your globals.css:

@theme {
  --animate-flicker: flicker 30s cubic-bezier(0.45, 0, 0.55, 1) infinite;
 
  @keyframes flicker {
    0% {
      mask-position:
        50% 50%,
        4px 50%;
      -webkit-mask-position:
        50% 50%,
        4px 50%;
    }
    25% {
      mask-position:
        50% 50%,
        2.75px 50%;
      -webkit-mask-position:
        50% 50%,
        2.75px 50%;
    }
    50% {
      mask-position:
        50% 50%,
        1.5px 50%;
      -webkit-mask-position:
        50% 50%,
        1.5px 50%;
    }
    75% {
      mask-position:
        50% 50%,
        0.75px 50%;
      -webkit-mask-position:
        50% 50%,
        0.75px 50%;
    }
    100% {
      mask-position:
        50% 50%,
        0 50%;
      -webkit-mask-position:
        50% 50%,
        0 50%;
    }
  }
}

Update the import paths to match your project setup.

import CosmicScene from "@/components/ui/cosmic-scene";

Usage

Basic Usage

import { CosmicScene } from "@/components/ui/cosmic-scene";
 
export default function HeroSection() {
  return (
    <CosmicScene>
      <div className="relative z-10 text-white">
        <h1>Your Content Here</h1>
      </div>
    </CosmicScene>
  );
}

As Hero Section

<CosmicScene colorScheme="sunset" overlayOpacity={0.2}>
  <div className="relative z-10 flex h-full flex-col items-center justify-center px-4 text-center text-white">
    <h1 className="text-5xl font-bold">Transform Your Ideas</h1>
    <p className="mt-4 max-w-2xl text-lg">Your hero content here</p>
  </div>
</CosmicScene>

As Card Background

<CosmicScene colorScheme="ocean" size="15px">
  <div className="relative z-10 flex h-full flex-col justify-end p-6 text-white">
    <h3 className="text-2xl font-semibold">Card Title</h3>
    <p className="mt-2">Card content here</p>
  </div>
</CosmicScene>

Props

PropTypeDefaultDescription
sizestring"20px"Size of the noise pattern dots
colorScheme"default" | "neon" | "sunset" | "ocean" | "aurora" | "cosmic" | "forest" | "desert" | "twilight" | "volcano" | "arctic" | "nebula" | "rainbow" | "midnight""default"Predefined color schemes for the gradient background
interactivebooleantrueEnable/disable mouse interaction effects
overlayOpacitynumber0Opacity of the dark overlay (0-1)
childrenReact.ReactNode-Content to render inside the scene
classNamestring-Additional CSS classes

Examples

Different Color Schemes

<CosmicScene colorScheme="neon" />
<CosmicScene colorScheme="sunset" />
<CosmicScene colorScheme="ocean" />

Custom Size

<CosmicScene size="30px" />
<CosmicScene size="10px" />

With Overlay

<CosmicScene overlayOpacity={0.4}>
  <div className="relative z-10 text-white">Content with better contrast</div>
</CosmicScene>

Non-interactive

<CosmicScene interactive={false}>
  Static background without mouse effects
</CosmicScene>