shadimage
unlumen-ui
uiaurora-blursource: ok · hit

Aurora Blur

Aurora blur shader layer used by Aurora Card.

npx shadcn add "https://ui.unlumen.com/r/aurora-blur.json"
free

Visual style

glass-blur

Tags auto-generated by rules on 9/24/2026 — empty means no evidence, not lack of support.

Dependencies

Bundle weight · heavythree
webgl@react-three/fiberthree

Files1 · source cached 10/9/2026, 8:13:25 AM

registry/components/ui/blob-card/aurora-blur.tsx
"use client";
/* eslint-disable react/no-unknown-property */

import * as React from "react";
import { Canvas, useFrame, useThree } from "@react-three/fiber";
import * as THREE from "three";

import { cn } from "@/lib/utils";

export interface AuroraLayer {
  color: string;
  speed: number;
  intensity: number;
}

export interface SkyLayer {
  color: string;
  blend: number;
}

export interface AuroraBlurProps extends React.ComponentProps<"div"> {
  width?: number | string;
  height?: number | string;
  speed?: number;
  layers?: AuroraLayer[];
  noiseScale?: number;
  movementX?: number;
  movementY?: number;
  verticalFade?: number;
  bloomIntensity?: number;
  skyLayers?: SkyLayer[];
  brightness?: number;
  saturation?: number;
  opacity?: number;
}

const DEFAULT_LAYERS: AuroraLayer[] = [
  { color: "#22d3ee", speed: 0.35, intensity: 0.7 },
  { color: "#3b82f6", speed: 0.18, intensity: 0.65 },
  { color: "#60a5fa", speed: 0.12, intensity: 0.4 },
  { color: "#1d4ed8", speed: 0.08, intensity: 0.22 },
];

const DEFAULT_SKY_LAYERS: SkyLayer[] = [
  { color: "#020617", blend: 0.52 },
  { color: "#0f172a", blend: 0.78 },
];

const vertexShader = `
varying vec2 vUv;
void main() {
  vUv = uv;
  gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`;

const fragmentShader = `
precision highp float;
varying vec2 vUv;
uniform float u_time;
uniform vec2 u_resolution;
uniform float u_speed;
uniform vec3 u_layer1Color;
uniform float u_layer1Speed;
uniform float u_layer1Intensity;
uniform vec3 u_layer2Color;
uniform float u_layer2Speed;
uniform float u_layer2Intensity;
uniform vec3 u_layer3Color;
uniform float u_layer3Speed;
uniform float u_layer3Intensity;
uniform vec3 u_layer4Color;
uniform float u_layer4Speed;
uniform float u_layer4Intensity;
uniform float u_noiseScale;
uniform float u_movementX;
uniform float u_movementY;
uniform float u_verticalFade;
uniform float u_bloomIntensity;
uniform vec3 u_skyColor1;
uniform vec3 u_skyColor2;
uniform float u_skyBlend1;
uniform float u_skyBlend2;
uniform float u_brightness;
uniform float u_saturation;
uniform float u_opacity;

float hashNoise(float n) { return fract(sin(n) * 43758.5453123); }

float noise2d(vec2 p) {
  vec2 i = floor(p);
  vec2 f = fract(p);
  vec2 u = f * f * (3.0 - 2.0 * f);

  return mix(
    mix(hashNoise(i.x + hashNoise(i.y)), hashNoise(i.x + 1.0 + hashNoise(i.y)), u.x),
    mix(hashNoise(i.x + hashNoise(i.y + 1.0)), hashNoise(i.x + 1.0 + hashNoise(i.y + 1.0)), u.x),
    u.y
  );
}

vec3 aurora(vec2 uv, float layerSpeed, float intensity, vec3 color, float aspect) {
  float time = u_time * u_speed * layerSpeed;
  vec2 scaled = vec2(uv.x * aspect, uv.y) * u_noiseScale;
  vec2 point = scaled + time * vec2(u_movementX, u_movementY);
  float n = noise2d(point + noise2d(color.xy + point + time));
  float alpha = n - uv.y * u_verticalFade;
  return color * alpha * intensity * u_bloomIntensity;
}

vec3 saturateColor(vec3 color, float saturation) {
  float gray = dot(color, vec3(0.299, 0.587, 0.114));
  return mix(vec3(gray), color, saturation);
}

void main() {
  vec2 uv = vUv;
  float aspect = u_resolution.x / u_resolution.y;

  vec3 color = vec3(0.0);
  color += aurora(uv, u_layer1Speed, u_layer1Intensity, u_layer1Color, aspect);
  color += aurora(uv, u_layer2Speed, u_layer2Intensity, u_layer2Color, aspect);
  color += aurora(uv, u_layer3Speed, u_layer3Intensity, u_layer3Color, aspect);
  color += aurora(uv, u_layer4Speed, u_layer4Intensity, u_layer4Color, aspect);

  color += u_skyColor2 * (1.0 - smoothstep(u_skyBlend1, 1.0, uv.y));
  color += u_skyColor1 * (1.0 - smoothstep(0.0, u_skyBlend2, uv.y));

  color = saturateColor(color, u_saturation) * u_brightness;

  gl_FragColor = vec4(color, u_opacity);
}
`;

function hexToVec3(hex: string): [number, number, number] {
  const parsed = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);

  if (!parsed) return [1, 1, 1];

  return [
    parseInt(parsed[1], 16) / 255,
    parseInt(parsed[2], 16) / 255,
    parseInt(parsed[3], 16) / 255,
  ];
}

function colorToVec3(color: string): [number, number, number] {
  if (color.startsWith("#")) return hexToVec3(color);
  if (typeof document === "undefined") return [1, 1, 1];

  const probe = document.createElement("span");
  probe.style.color = color;
  document.body.appendChild(probe);
  const resolved = getComputedStyle(probe).color;
  probe.remove();

  const parsed = resolved.match(/\d+(\.\d+)?/g);
  if (!parsed || parsed.length < 3) return [1, 1, 1];

  return [
    Number(parsed[0]) / 255,
    Number(parsed[1]) / 255,
    Number(parsed[2]) / 255,
  ];
}

interface SceneProps {
  speed: number;
  layers: AuroraLayer[];
  noiseScale: number;
  movementX: number;
  movementY: number;
  verticalFade: number;
  bloomIntensity: number;
  skyLayers: SkyLayer[];
  brightness: number;
  saturation: number;
  opacity: number;
}

function Scene({
  speed,
  layers,
  noiseScale,
  movementX,
  movementY,
  verticalFade,
  bloomIntensity,
  skyLayers,
  brightness,
  saturation,
  opacity,
}: SceneProps) {
  const meshRef = React.useRef<THREE.Mesh>(null);
  const { size } = useThree();

  const uniforms = React.useMemo(
    () => ({
      u_time: { value: 0 },
      u_resolution: { value: new THREE.Vector2(1, 1) },
      u_speed: { value: 1 },
      u_layer1Color: { value: new THREE.Vector3(0.13, 0.83, 0.93) },
      u_layer1Speed: { value: 0.35 },
      u_layer1Intensity: { value: 0.7 },
      u_layer2Color: { value: new THREE.Vector3(0.23, 0.51, 0.96) },
      u_layer2Speed: { value: 0.18 },
      u_layer2Intensity: { value: 0.65 },
      u_layer3Color: { value: new THREE.Vector3(0.38, 0.65, 0.98) },
      u_layer3Speed: { value: 0.12 },
      u_layer3Intensity: { value: 0.4 },
      u_layer4Color: { value: new THREE.Vector3(0.11, 0.31, 0.85) },
      u_layer4Speed: { value: 0.08 },
      u_layer4Intensity: { value: 0.22 },
      u_noiseScale: { value: 3.2 },
      u_movementX: { value: -1.4 },
      u_movementY: { value: -2.6 },
      u_verticalFade: { value: 0.5 },
      u_bloomIntensity: { value: 1.9 },
      u_skyColor1: { value: new THREE.Vector3(0.01, 0.02, 0.09) },
      u_skyColor2: { value: new THREE.Vector3(0.06, 0.09, 0.16) },
      u_skyBlend1: { value: 0.78 },
      u_skyBlend2: { value: 0.52 },
      u_brightness: { value: 0.92 },
      u_saturation: { value: 1.12 },
      u_opacity: { value: 1 },
    }),
    [],
  );

  useFrame((state) => {
    if (!meshRef.current) return;

    const material = meshRef.current.material as THREE.ShaderMaterial;
    const resolvedLayers = layers.length ? layers : DEFAULT_LAYERS;
    const resolvedSkyLayers = skyLayers.length ? skyLayers : DEFAULT_SKY_LAYERS;

    material.uniforms.u_time.value = state.clock.elapsedTime;
    material.uniforms.u_resolution.value.set(size.width, size.height);
    material.uniforms.u_speed.value = speed;

    material.uniforms.u_layer1Color.value.set(
      ...colorToVec3(resolvedLayers[0]?.color ?? DEFAULT_LAYERS[0].color),
    );
    material.uniforms.u_layer1Speed.value =
      resolvedLayers[0]?.speed ?? DEFAULT_LAYERS[0].speed;
    material.uniforms.u_layer1Intensity.value =
      resolvedLayers[0]?.intensity ?? DEFAULT_LAYERS[0].intensity;

    material.uniforms.u_layer2Color.value.set(
      ...colorToVec3(resolvedLayers[1]?.color ?? DEFAULT_LAYERS[1].color),
    );
    material.uniforms.u_layer2Speed.value =
      resolvedLayers[1]?.speed ?? DEFAULT_LAYERS[1].speed;
    material.uniforms.u_layer2Intensity.value =
      resolvedLayers[1]?.intensity ?? DEFAULT_LAYERS[1].intensity;

    material.uniforms.u_layer3Color.value.set(
      ...colorToVec3(resolvedLayers[2]?.color ?? DEFAULT_LAYERS[2].color),
    );
    material.uniforms.u_layer3Speed.value =
      resolvedLayers[2]?.speed ?? DEFAULT_LAYERS[2].speed;
    material.uniforms.u_layer3Intensity.value =
      resolvedLayers[2]?.intensity ?? DEFAULT_LAYERS[2].intensity;

    material.uniforms.u_layer4Color.value.set(
      ...colorToVec3(resolvedLayers[3]?.color ?? DEFAULT_LAYERS[3].color),
    );
    material.uniforms.u_layer4Speed.value =
      resolvedLayers[3]?.speed ?? DEFAULT_LAYERS[3].speed;
    material.uniforms.u_layer4Intensity.value =
      resolvedLayers[3]?.intensity ?? DEFAULT_LAYERS[3].intensity;

    material.uniforms.u_noiseScale.value = noiseScale;
    material.uniforms.u_movementX.value = movementX;
    material.uniforms.u_movementY.value = movementY;
    material.uniforms.u_verticalFade.value = verticalFade;
    material.uniforms.u_bloomIntensity.value = bloomIntensity;
    material.uniforms.u_skyColor1.value.set(
      ...colorToVec3(
        resolvedSkyLayers[0]?.color ?? DEFAULT_SKY_LAYERS[0].color,
      ),
    );
    material.uniforms.u_skyColor2.value.set(
      ...colorToVec3(
        resolvedSkyLayers[1]?.color ?? DEFAULT_SKY_LAYERS[1].color,
      ),
    );
    material.uniforms.u_skyBlend1.value =
      resolvedSkyLayers[1]?.blend ?? DEFAULT_SKY_LAYERS[1].blend;
    material.uniforms.u_skyBlend2.value =
      resolvedSkyLayers[0]?.blend ?? DEFAULT_SKY_LAYERS[0].blend;
    material.uniforms.u_brightness.value = brightness;
    material.uniforms.u_saturation.value = saturation;
    material.uniforms.u_opacity.value = opacity;
  });

  return (
    <mesh ref={meshRef}>
      <planeGeometry args={[2, 2]} />
      <shaderMaterial
        fragmentShader={fragmentShader}
        transparent
        uniforms={uniforms}
        vertexShader={vertexShader}
      />
    </mesh>
  );
}

export function AuroraBlur({
  width = "100%",
  height = "100%",
  className,
  children,
  speed = 1.1,
  layers = DEFAULT_LAYERS,
  noiseScale = 3.2,
  movementX = -1.4,
  movementY = -2.6,
  verticalFade = 0.5,
  bloomIntensity = 1.9,
  skyLayers = DEFAULT_SKY_LAYERS,
  brightness = 0.92,
  saturation = 1.12,
  opacity = 1,
  style,
  ...props
}: AuroraBlurProps) {
  const resolvedWidth = typeof width === "number" ? `${width}px` : width;
  const resolvedHeight = typeof height === "number" ? `${height}px` : height;

  return (
    <div
      className={cn("relative overflow-hidden", className)}
      style={{ ...style, width: resolvedWidth, height: resolvedHeight }}
      {...props}
    >
      <Canvas
        camera={{
          bottom: -1,
          left: -1,
          position: [0, 0, 1],
          right: 1,
          top: 1,
          zoom: 1,
        }}
        className="pointer-events-none absolute inset-0 h-full w-full"
        dpr={[1, 1.5]}
        gl={{
          alpha: true,
          antialias: true,
          powerPreference: "high-performance",
        }}
        orthographic
      >
        <Scene
          bloomIntensity={bloomIntensity}
          brightness={brightness}
          layers={layers}
          movementX={movementX}
          movementY={movementY}
          noiseScale={noiseScale}
          opacity={opacity}
          saturation={saturation}
          skyLayers={skyLayers}
          speed={speed}
          verticalFade={verticalFade}
        />
      </Canvas>

      {children ? (
        <div className="relative z-10 h-full w-full">{children}</div>
      ) : null}
    </div>
  );
}

More from unlumen-ui

Browse all components in this library →