About
An aurora drawn as ribbons instead of a volume. Each ribbon's centre line meanders across the frame on two slow waves and some noise; brightness is a sharp lower edge with a long fade upward, multiplied by vertical striations of gradient noise (the rays) and a slower curl. The colour runs from the base colour at the lower edge through the body to the crown. Three stars layers sit behind, a ridge in front lit faintly by the sky's own light.
Use it when
- A hotel, lodge or tour operator sells the north or the stars.
- A software, AI or analytics product wants a calm, atmospheric night hero.
- An event or festival page wants colour without a photo.
Skip it when
- The brand is bright and light; use Cloudscape or Satin.
- Long body copy sits directly on the curtains.
Install
- 1Add the VibeBox registry to
components.json:{ "registries": { "@vibebox": "https://motion.vibeboxph.com/r/{name}.json" } } - 2Add the component with the shadcn CLI:
npx shadcn@latest add @vibebox/aurora - 3Use it:
import { Aurora } from "@/components/vibebox/aurora"; <Aurora preset="arctic" className="min-h-[560px] rounded-xl"> <div className="p-12 text-white"> <h1 className="text-4xl font-semibold">Dark skies, long nights, one fire.</h1> </div> </Aurora>
Code
3 filescomponents/vibebox/aurora/aurora.client.tsx
/**
* VibeBox Motion · Aurora
* Copyright (c) 2026 VibeBox. Licensed under the VibeBox Motion Free License:
* https://motion.vibeboxph.com/license
* Use it in unlimited personal, commercial and client projects.
* Do not sell or redistribute it as a component, kit, template or builder.
*/
"use client";
import type { ReactNode } from "react";
import { BackdropCanvas, type PosterSpec } from "@/lib/vibebox/backdrop-canvas";
import { auroraPiece, presets, type AuroraPreset } from "./aurora.shader";
export type AuroraColor = "sky" | "base" | "mid" | "crown" | "ridge";
export type AuroraValue = "bands" | "height" | "rays" | "stars" | "hill";
export type AuroraProps = {
/** What sits on the scene: a heading, a call to action, a whole section. */
children?: ReactNode;
/** A named look: `arctic`, `violet`, `rose`, `ember`, `pastel`. */
preset?: AuroraPreset;
/** Overrides single palette colours with any CSS colour. */
colors?: Partial<Record<AuroraColor, string>>;
/** Multiplies the preset's speed (1 keeps it, 0 holds the scene still). */
speed?: number;
/** Multiplies the preset's brightness. */
intensity?: number;
/** The pointer and touch move the scene. */
interactive?: boolean;
/** Accessible name; leave it out when the scene is only a background. */
label?: string;
/** Hold one still frame until this is false (for a gallery card). */
still?: boolean;
className?: string;
} & Partial<Record<AuroraValue, number>>;
const POSTER: PosterSpec = { type: "linear", from: "sky", to: "ridge", mid: "sky", angle: 180, blobs: [{ color: "base", at: [30, 38], size: 55 }, { color: "mid", at: [72, 28], size: 50 }, { color: "crown", at: [50, 8], size: 45 }] };
export function Aurora({ children, preset, colors, speed, intensity, interactive = true, label, still, className, bands, height, rays, stars, hill }: AuroraProps) {
return (
<BackdropCanvas
slug="aurora"
piece={auroraPiece}
presets={presets}
fallback="arctic"
preset={preset}
colors={colors}
values={{ bands, height, rays, stars, hill }}
speed={speed}
intensity={intensity}
poster={POSTER}
scale={0.5}
stillTime={3}
still={still}
interactive={interactive}
label={label}
className={className}
>
{children}
</BackdropCanvas>
);
}components/vibebox/aurora/aurora.shader.ts
/**
* VibeBox Motion · Aurora
* Copyright (c) 2026 VibeBox. Licensed under the VibeBox Motion Free License:
* https://motion.vibeboxph.com/license
* Use it in unlimited personal, commercial and client projects.
* Do not sell or redistribute it as a component, kit, template or builder.
*/
import { compose, createScenePiece, type ScenePreset } from "@/lib/vibebox/engine/backdrops";
/*
* Curtains of light over a night sky. Three ribbons wander across the frame; each is a band whose
* brightness streaks vertically (the rays), fades upward and takes its colour from a ramp, from
* the base colour at the bottom edge to the crown colour at the top. Stars sit behind, a ridge
* silhouette in front, and the pointer draws the curtains toward it.
*/
const SCENE = /* glsl */ `
uniform vec3 uSky;
uniform vec3 uBase;
uniform vec3 uMid;
uniform vec3 uCrown;
uniform vec3 uRidge;
uniform float uBands;
uniform float uHeight;
uniform float uRays;
uniform float uStars;
uniform float uHill;
uniform float uSpeed;
uniform float uIntensity;
float T;
float starLayer(vec2 uv, float scale, float seed) {
vec2 g = uv * scale;
ivec2 c = ivec2(floor(g)) + ivec2(int(seed) * 37, int(seed) * 11);
vec2 f = fract(g);
vec2 o = h22(c) * 0.7 + 0.15;
float r = h2(c + ivec2(3, 8));
float d = length(f - o);
float tw = 0.6 + 0.4 * sin(T * (0.7 + r * 2.5) + r * 31.0);
return smoothstep(0.06 + r * 0.03, 0.0, d) * step(0.82, r) * tw;
}
/* One curtain: a ribbon whose centre line meanders across the frame. */
vec3 curtain(vec2 uv, float k, vec2 pull) {
float kk = float(k);
float x = uv.x * (1.1 + 0.2 * kk) + pull.x * 0.25 + kk * 3.7;
// The centre line of the curtain, wandering with two slow waves and a little noise.
float baseline = -0.02 + 0.1 * sin(x * 1.7 + T * 0.12 + kk * 1.3) + 0.05 * sin(x * 3.9 - T * 0.2 + kk) + (fbm3(vec2(x * 1.3, T * 0.04 + kk * 9.0)) - 0.5) * 0.18;
float dy = uv.y - baseline - 0.06 * kk - pull.y * 0.12;
// Brightness along the height: a sharp lower edge and a long fade upward.
float up = clamp(dy / (0.36 * uHeight), 0.0, 1.0);
float body = smoothstep(-0.02, 0.025, dy) * pow(1.0 - up, 1.6);
// Rays: vertical striations that shimmer.
float rays = 0.5 + 0.5 * gnoise(vec2(x * 22.0 * uRays, T * 0.25 + kk * 4.0));
rays = mix(1.0, pow(rays, 1.5) * 1.6, 0.8);
float curl = 0.65 + 0.35 * gnoise(vec2(x * 3.2 + T * 0.1, kk * 5.0 + dy * 2.0));
float a = body * rays * curl;
// Colour: base at the lower edge, mid through the body, crown at the top.
vec3 c = mix(lin(uBase), lin(uMid), smoothstep(0.0, 0.35, up));
c = mix(c, lin(uCrown), smoothstep(0.35, 1.0, up));
return c * a;
}
void main() {
vec2 uv = centred(gl_FragCoord.xy);
T = uTime * uSpeed;
float aspect = uResolution.x / uResolution.y;
vec2 pu = vec2((uPointer.x - 0.5) * aspect, uPointer.y - 0.5);
vec2 pull = (pu - uv) * 0.2 * uHover * exp(-length(pu - uv) * 1.6);
vec3 sky = lin(uSky);
vec3 col = mix(sky * 1.6, sky * 0.5, smoothstep(-0.45, 0.5, uv.y));
// Stars, three depths.
float stars = starLayer(uv + vec2(T * 0.002, 0.0), 46.0, 1.0) * 1.0 + starLayer(uv * 1.0, 80.0, 2.0) * 0.7 + starLayer(uv, 130.0, 3.0) * 0.5;
col += vec3(0.85, 0.92, 1.0) * stars * uStars * smoothstep(-0.3, 0.2, uv.y);
// The curtains, additive.
vec3 glow = vec3(0.0);
for (int i = 0; i < 3; i++) {
if (float(i) >= uBands) break;
glow += curtain(uv, float(i), pull) * (1.0 - 0.18 * float(i));
}
col += glow * 1.35;
// A faint wash of the mid colour across the whole sky.
col += lin(uMid) * 0.05 * smoothstep(0.3, -0.2, uv.y) * uBands;
// The ridge in front.
float hx = uv.x * 1.3 + 1.7;
float ridge = -0.34 + uHill * (0.1 * (fbm3(vec2(hx * 1.4, 1.0)) - 0.5) + 0.14 * (ridged(vec2(hx * 0.9, 5.0)) - 0.5) * 2.0);
float edge = smoothstep(ridge + 0.004, ridge - 0.004, uv.y);
// The sky's light falls faintly on the nearest slope.
vec3 ridgeCol = lin(uRidge) + glow * 0.12;
col = mix(col, ridgeCol, edge);
col *= uIntensity;
col *= 1.0 - 0.3 * smoothstep(0.55, 1.2, length(uv * vec2(0.9, 1.05)));
fragColor = vec4(finish(col, gl_FragCoord.xy), 1.0);
}
`;
export const fragment = compose(SCENE);
export const PRESET_NAMES = ["arctic", "violet", "rose", "ember", "pastel"] as const;
export type AuroraPreset = (typeof PRESET_NAMES)[number];
export const presets: Record<AuroraPreset, ScenePreset> = {
arctic: {
colors: { sky: "#050b1a", base: "#4dffa8", mid: "#19c9a6", crown: "#7a6bff", ridge: "#02060d" },
values: { bands: 3, height: 1, rays: 1, stars: 1, hill: 1, speed: 0.8, intensity: 1 },
},
violet: {
colors: { sky: "#08061a", base: "#7cf0ff", mid: "#7b5cff", crown: "#ff5fd2", ridge: "#030212" },
values: { bands: 3, height: 1.1, rays: 1.1, stars: 1, hill: 1, speed: 0.8, intensity: 1 },
},
rose: {
colors: { sky: "#120816", base: "#ff9ec2", mid: "#ff5f9f", crown: "#8f6bff", ridge: "#08030c" },
values: { bands: 2, height: 1, rays: 0.9, stars: 0.8, hill: 1, speed: 0.7, intensity: 1 },
},
ember: {
colors: { sky: "#0d0707", base: "#ffd36a", mid: "#ff7a3a", crown: "#d6336c", ridge: "#050202" },
values: { bands: 3, height: 0.95, rays: 1.2, stars: 0.7, hill: 1, speed: 0.75, intensity: 1 },
},
pastel: {
colors: { sky: "#1c2142", base: "#bff3e0", mid: "#a8c8ff", crown: "#f2b8ff", ridge: "#10152b" },
values: { bands: 3, height: 1.15, rays: 0.7, stars: 0.9, hill: 0.8, speed: 0.55, intensity: 0.72 },
},
};
export const auroraPiece = createScenePiece("aurora", fragment);components/vibebox/aurora/index.tsx
/**
* VibeBox Motion · Aurora
* Copyright (c) 2026 VibeBox. Licensed under the VibeBox Motion Free License:
* https://motion.vibeboxph.com/license
* Use it in unlimited personal, commercial and client projects.
* Do not sell or redistribute it as a component, kit, template or builder.
*/
export { Aurora, type AuroraProps, type AuroraColor, type AuroraValue } from "./aurora.client";
export { PRESET_NAMES as AURORA_PRESETS, presets as auroraPresets, type AuroraPreset } from "./aurora.shader";Props
| Prop | Type | Default | Description |
|---|---|---|---|
| children | ReactNode | — | What sits on the scene: a heading, a call to action, a whole section. |
| preset | "arctic" | "violet" | "rose" | "ember" | "pastel" | "arctic" | The look: arctic, violet, rose, ember, pastel. |
| colors | { sky?: string; base?: string; mid?: string; crown?: string; ridge?: string } | — | Overrides single palette colours with any CSS colour: sky (the night sky), base (the lower edge of the curtain), mid (the body of the curtain), crown (the crown, high in the curtain), ridge (the mountain silhouette). |
| speed | number | 1 | Multiplies the preset's speed; 0 holds the scene still. |
| intensity | number | 1 | Multiplies the preset's brightness, 0.5 to 1.4. |
| bands | number | 3 | How many ribbons of light (1 to 3) (1 to 3; each look sets its own). |
| height | number | 1 | How tall the curtains rise (0.5 to 1.6; each look sets its own). |
| rays | number | 1 | How fine the vertical striations are (0.4 to 2; each look sets its own). |
| stars | number | 1 | How many stars show (0 to 1.5; each look sets its own). |
| hill | number | 1 | How tall and rugged the ridge is (0 to 1.5; each look sets its own). |
| interactive | boolean | true | The pointer draws the curtains toward it. |
| label | string | — | Accessible name; leave it out when the scene is only a background. |
| still | boolean | false | Hold one composed frame until false (a gallery card that plays on hover). |
| className | string | — | Merged with cn(); give it a size and a radius. |
Accessibility and motion
- Keyboard
- Not focusable; content on it keeps its own focus order.
- Screen readers
- Decorative by default; with
labelit is an image with that name. - Touch
- A finger moves the scene like the cursor does; scrolling is never blocked.
- Reduced motion
- One still frame of the aurora; the curtains do not move.
Edge cases and limits
- The curtains are the brightest thing in the frame: put copy in the dark sky or on the ridge.
- The pastel look is gentler; check white text on it.
- More than two ribbons on a phone fills the small sky; set
bandsto 2.
Related

City Night
FreeA skyline of lit windows against a glowing sky, with a road of light trails in front.
Backgrounds · WebGL2

Cloudscape
FreeA sea of cloud seen from above, or a ceiling of it seen from below, lit by a low sun.
Backgrounds · WebGL2

Bokeh
FreeOut-of-focus lights in three depths, brighter at the rim and drifting with the pointer.
Backgrounds · WebGL2