godlights
v1.0.0
Published
Animated god ray/light beam effects for React — zero dependencies beyond React itself.
Maintainers
Readme
godlights
Animated god-ray / light-beam effects for React. Render stunning volumetric light scenes on a <canvas>, fully configurable and animatable. Zero runtime dependencies beyond React.
Installation
npm install godlightsOr install a ready-made component directly into your project using the shadcn CLI:
# Hero section with animated rays
npx shadcn@latest add "https://www.godlights.io/r/god-lights-hero.json"
# Minimal background wrapper
npx shadcn@latest add "https://www.godlights.io/r/god-lights-background.json"
# Auto-cycling presets with cross-fade
npx shadcn@latest add "https://www.godlights.io/r/god-lights-cycling.json"The CLI installs the .tsx component and adds godlights as a dependency automatically. Works with v0, Bolt, Lovable, and Cursor.
Quick start
import { GodLights } from "godlights";
import type { SceneConfig } from "godlights";
// "Corner haze" preset — rays from both top corners
const scene: SceneConfig = {
width: 1920,
height: 1080,
noise: 8,
grainSize: 1,
layers: [
// ⚠️ layers[0] MUST be a BackgroundLayer — it clears the canvas each frame
{
type: "background",
bgType: "solid",
bgColor: "#000000",
bgColor2: "#000000",
bgGradientAngle: 180,
},
{
type: "rays",
direction: 158,
spread: 70,
originX: 12,
originY: -25,
rayCount: 28,
rayWidth: 90,
divergence: 1.5,
rayLength: 0.6,
colorStart: "#ffffff",
colorEnd: "#ffffff",
opacity: 0.24,
blendMode: "screen",
fadeToTransparent: true,
blur: 17.5,
randomnessWidth: 100,
randomnessLength: 24,
randomnessAngle: 0,
seed: 554433,
},
{
type: "halo",
originX: 16,
originY: 2,
color: "#ffffff",
intensity: 0.16,
size: 0.47,
blendMode: "lighter",
},
],
};
export default function App() {
return (
<div style={{ position: "relative", width: "100%", height: "100vh" }}>
<GodLights
scene={scene}
animParams={{ speed: 3, angleAmp: 40, lengthAmp: 30, widthAmp: 20, haloAmp: 50 }}
style={{ position: "absolute", inset: 0, width: "100%", height: "100%" }}
/>
</div>
);
}Tip: Use the visual editor to design your scene, then export it as a ready-to-paste JSX component.
<GodLights> props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| scene | SceneConfig | required | Full scene configuration |
| animParams | AnimParams | — | Pass to enable the animation loop; omit for a static render |
| showFps | boolean | false | Show FPS counter overlay |
| className | string | — | CSS class on the wrapper <div> |
| style | CSSProperties | — | Inline style on the wrapper <div> |
Positioning note: The wrapper defaults to
position: relative. For full-bleed use, passstyle={{ position: "absolute", inset: 0, width: "100%", height: "100%" }}as an inline style — this takes precedence over anyclassNameyou add.
SceneConfig
interface SceneConfig {
width: number; // Canvas width in px — used for ray/halo proportion math
height: number; // Canvas height in px
noise: number; // Film grain intensity (0–100). 0 = no grain.
grainSize: number; // Grain pixel size (1–4). 1 = per-pixel noise.
layers: Layer[]; // Ordered back-to-front. layers[0] MUST be a BackgroundLayer.
}Layer types
BackgroundLayer
The first layer in every scene. Responsible for clearing the canvas each frame — without it you'll see smearing artifacts in animated mode.
{
type: "background";
bgType: "solid" | "gradient" | "transparent";
bgColor: string; // Primary color (hex, e.g. "#0b1024")
bgColor2: string; // Gradient end color
bgGradientAngle: number; // Gradient angle in degrees (0–360)
}RayLayer
A fan of volumetric light beams emanating from a single origin point.
{
type: "rays";
direction: number; // Direction rays point (degrees, 0 = right, 180 = down)
spread: number; // Angular spread of the fan (degrees, 0–360)
originX: number; // Origin X as % of canvas width (can be < 0 or > 100)
originY: number; // Origin Y as % of canvas height (can be < 0 or > 100)
rayCount: number; // Number of rays (1–200)
rayWidth: number; // Ray width at origin (1–200)
divergence: number; // How much rays splay outward (0.1–5)
rayLength: number; // Length as fraction of canvas diagonal (0.1–3)
colorStart: string; // Hex color at the origin
colorEnd: string; // Hex color at the tip
opacity: number; // Overall opacity (0–1)
blendMode: BlendMode; // CSS composite operation (see blend mode guide below)
fadeToTransparent: boolean; // Fade ray tips to alpha 0
blur: number; // Gaussian blur in px (0–100)
randomnessWidth: number; // Per-ray width variance (0–100)
randomnessLength: number; // Per-ray length variance (0–100)
randomnessAngle: number; // Per-ray angle jitter (0–100)
seed: number; // RNG seed — same seed = same ray layout
}HaloLayer
A soft radial glow, typically placed at the ray origin to simulate a light source.
{
type: "halo";
originX: number; // Center X as % of canvas width
originY: number; // Center Y as % of canvas height
color: string; // Hex color
intensity: number; // Peak brightness (0–1)
size: number; // Radius as fraction of canvas diagonal (0.01–2)
blendMode: BlendMode;
}Blend mode guide
| Mode | Use on | Effect |
|------|--------|--------|
| "screen" | Dark backgrounds | Additive brightening — most natural for light on black |
| "lighter" | Dark backgrounds | Stronger additive — good for intense halos |
| "multiply" | Light / white backgrounds | Darkening blend — keeps rays visible without blowout |
| "overlay" | Any | Contrast-boosting blend |
| "source-over" | Any | Plain alpha compositing |
Use
"multiply"when placing rays on a white or light background."screen"and"lighter"produce invisible results on white.
AnimParams
Controls the oscillation of ray properties over time.
interface AnimParams {
speed: number; // Global time multiplier (0–10). 1 = real-time. 0 = frozen.
angleAmp: number; // Ray angle oscillation amplitude (0–100)
lengthAmp: number; // Ray length oscillation amplitude (0–100)
widthAmp: number; // Ray width oscillation amplitude (0–100)
haloAmp: number; // Halo intensity oscillation amplitude (0–100)
}⚠️
opacityAmpdoes not exist. Onlyspeed,angleAmp,lengthAmp,widthAmp, andhaloAmpare valid keys.
Default values
All defaults are exported so you can spread them into your layers:
import {
DEFAULT_SCENE, // SceneConfig
DEFAULT_RAY_LAYER, // RayLayer
DEFAULT_HALO_LAYER, // HaloLayer
DEFAULT_BACKGROUND_LAYER, // BackgroundLayer
DEFAULT_ANIM_PARAMS, // AnimParams
} from "godlights";
// Example: spread defaults then override only what you need
const myRay: RayLayer = {
...DEFAULT_RAY_LAYER,
direction: 270,
colorStart: "#a855f7",
colorEnd: "#a855f7",
};Utility exports
import {
drawScene, // (canvas: HTMLCanvasElement, scene: SceneConfig, time?: number) => void
exportScene, // (scene: SceneConfig, type: "image/png" | "image/jpeg", quality?: number) => Promise<Blob>
buildSceneCssSnippet, // (scene: SceneConfig) => Promise<string> — returns CSS background-image lines
BLEND_MODES, // { value: BlendMode; label: string }[]
} from "godlights";Static render (no React)
import { drawScene } from "godlights";
const canvas = document.createElement("canvas");
canvas.width = 1920;
canvas.height = 1080;
drawScene(canvas, scene);
document.body.appendChild(canvas);Export to PNG
import { exportScene } from "godlights";
const blob = await exportScene(scene, "image/png");
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = "light-effect.png";
a.click();
URL.revokeObjectURL(url);Export as CSS background-image
import { buildSceneCssSnippet } from "godlights";
const css = await buildSceneCssSnippet(scene);
// css contains:
// background-image: url("data:image/png;base64,...");
// background-size: cover;
// background-position: center;
// background-repeat: no-repeat;
document.body.style.cssText = css;Recipes
Hero section background
Full-bleed animated light effect behind page content. Note the style prop — a Tailwind absolute class won't override the wrapper's default position: relative.
import { GodLights } from "godlights";
import type { SceneConfig } from "godlights";
const scene: SceneConfig = {
width: 1920,
height: 1080,
noise: 6,
grainSize: 1,
layers: [
{ type: "background", bgType: "solid", bgColor: "#06060f", bgColor2: "#06060f", bgGradientAngle: 180 },
{ type: "halo", originX: 50, originY: -5, color: "#a78bfa", intensity: 0.3, size: 0.6, blendMode: "lighter" },
{
type: "rays",
direction: 180, spread: 90,
originX: 50, originY: -5,
rayCount: 30, rayWidth: 80, divergence: 2, rayLength: 1.2,
colorStart: "#a78bfa", colorEnd: "#a78bfa",
opacity: 0.18, blendMode: "screen", fadeToTransparent: true, blur: 14,
randomnessWidth: 80, randomnessLength: 30, randomnessAngle: 20, seed: 42,
},
],
};
export default function HeroSection() {
return (
<section style={{ position: "relative", minHeight: "100vh" }}>
<GodLights
scene={scene}
animParams={{ speed: 0.8, angleAmp: 40, lengthAmp: 25, widthAmp: 15, haloAmp: 40 }}
style={{ position: "absolute", inset: 0, width: "100%", height: "100%" }}
/>
<div style={{ position: "relative", zIndex: 1 }}>
<h1>Your content here</h1>
</div>
</section>
);
}Next.js App Router
Add "use client" — the component uses useRef, useEffect, and requestAnimationFrame, which are client-only.
"use client";
import { GodLights } from "godlights";
import type { SceneConfig } from "godlights";
const scene: SceneConfig = {
width: 1920,
height: 1080,
noise: 8,
grainSize: 1,
layers: [
{ type: "background", bgType: "gradient", bgColor: "#0b1024", bgColor2: "#1a1340", bgGradientAngle: 180 },
{ type: "halo", originX: 20, originY: 10, color: "#ffd28a", intensity: 0.25, size: 0.4, blendMode: "lighter" },
{
type: "rays",
direction: 160, spread: 70,
originX: 20, originY: 10,
rayCount: 24, rayWidth: 70, divergence: 1.8, rayLength: 1.0,
colorStart: "#ffd28a", colorEnd: "#ffd28a",
opacity: 0.2, blendMode: "screen", fadeToTransparent: true, blur: 10,
randomnessWidth: 60, randomnessLength: 20, randomnessAngle: 15, seed: 99,
},
],
};
export default function Page() {
return (
<main style={{ position: "relative", minHeight: "100svh" }}>
<GodLights
scene={scene}
animParams={{ speed: 1, angleAmp: 40, lengthAmp: 25, widthAmp: 15, haloAmp: 40 }}
style={{ position: "absolute", inset: 0, width: "100%", height: "100%" }}
/>
<div style={{ position: "relative", zIndex: 1 }}>
{/* page content */}
</div>
</main>
);
}Light / white background
Use "multiply" blend mode — "screen" and "lighter" are invisible on light backgrounds.
const scene: SceneConfig = {
width: 1920,
height: 1080,
noise: 4,
grainSize: 1,
layers: [
{ type: "background", bgType: "solid", bgColor: "#ffffff", bgColor2: "#ffffff", bgGradientAngle: 180 },
{
type: "rays",
direction: 200, spread: 80,
originX: 70, originY: -10,
rayCount: 20, rayWidth: 100, divergence: 2, rayLength: 1.1,
colorStart: "#c4b5fd", colorEnd: "#c4b5fd",
opacity: 0.35,
blendMode: "multiply", // ← required on light backgrounds
fadeToTransparent: true, blur: 20,
randomnessWidth: 70, randomnessLength: 25, randomnessAngle: 10, seed: 7,
},
],
};Multi-layer scene (warm + cool)
Stack multiple ray and halo layers for richer lighting. Layers render back-to-front.
const scene: SceneConfig = {
width: 1920,
height: 1080,
noise: 10,
grainSize: 1,
layers: [
{ type: "background", bgType: "solid", bgColor: "#050510", bgColor2: "#050510", bgGradientAngle: 180 },
// warm glow — top-left
{ type: "halo", originX: 15, originY: 5, color: "#ff9a3c", intensity: 0.2, size: 0.5, blendMode: "lighter" },
{
type: "rays",
direction: 155, spread: 60,
originX: 15, originY: 5,
rayCount: 22, rayWidth: 60, divergence: 1.6, rayLength: 1.0,
colorStart: "#ff9a3c", colorEnd: "#ff9a3c",
opacity: 0.15, blendMode: "screen", fadeToTransparent: true, blur: 12,
randomnessWidth: 70, randomnessLength: 20, randomnessAngle: 15, seed: 11,
},
// cool accent — top-right
{ type: "halo", originX: 85, originY: 0, color: "#60a5fa", intensity: 0.18, size: 0.4, blendMode: "lighter" },
{
type: "rays",
direction: 205, spread: 55,
originX: 85, originY: 0,
rayCount: 18, rayWidth: 50, divergence: 1.5, rayLength: 0.9,
colorStart: "#60a5fa", colorEnd: "#60a5fa",
opacity: 0.12, blendMode: "screen", fadeToTransparent: true, blur: 10,
randomnessWidth: 60, randomnessLength: 15, randomnessAngle: 10, seed: 22,
},
],
};Overlay on existing content (transparent background)
Use bgType: "transparent" to render only the light effect without covering the page background. Add pointerEvents: "none" so clicks pass through.
const scene: SceneConfig = {
width: 1920,
height: 1080,
noise: 0,
grainSize: 1,
layers: [
// transparent — does not clear the canvas, just composites the lights on top
{ type: "background", bgType: "transparent", bgColor: "#000000", bgColor2: "#000000", bgGradientAngle: 180 },
{ type: "halo", originX: 50, originY: 0, color: "#ffffff", intensity: 0.15, size: 0.5, blendMode: "lighter" },
{
type: "rays",
direction: 180, spread: 100,
originX: 50, originY: 0,
rayCount: 20, rayWidth: 60, divergence: 2, rayLength: 1.2,
colorStart: "#ffffff", colorEnd: "#ffffff",
opacity: 0.1, blendMode: "screen", fadeToTransparent: true, blur: 16,
randomnessWidth: 80, randomnessLength: 30, randomnessAngle: 20, seed: 55,
},
],
};
export default function Overlay() {
return (
<GodLights
scene={scene}
animParams={{ speed: 1, angleAmp: 40, lengthAmp: 25, widthAmp: 15, haloAmp: 40 }}
style={{
position: "fixed",
inset: 0,
width: "100%",
height: "100%",
pointerEvents: "none",
zIndex: 0,
}}
/>
);
}Reusable wrapper component
A drop-in background component that accepts high-level props instead of a full SceneConfig. Useful when you want the same lighting style across multiple sections with different colors or intensities.
import { useMemo } from "react";
import { GodLights, DEFAULT_BACKGROUND_LAYER, DEFAULT_HALO_LAYER, DEFAULT_RAY_LAYER } from "godlights";
import type { SceneConfig, AnimParams } from "godlights";
interface GodLightsBackgroundProps {
color?: string;
originX?: number;
originY?: number;
direction?: number;
spread?: number;
opacity?: number;
speed?: number;
animated?: boolean;
className?: string;
style?: React.CSSProperties;
}
export function GodLightsBackground({
color = "#a78bfa",
originX = 50,
originY = 0,
direction = 180,
spread = 80,
opacity = 0.18,
speed = 1,
animated = true,
className,
style,
}: GodLightsBackgroundProps) {
const scene: SceneConfig = useMemo(() => ({
width: 1920,
height: 1080,
noise: 6,
grainSize: 1,
layers: [
{ ...DEFAULT_BACKGROUND_LAYER },
{ ...DEFAULT_HALO_LAYER, originX, originY, color, intensity: opacity * 1.5, size: 0.45 },
{ ...DEFAULT_RAY_LAYER, originX, originY, direction, spread, colorStart: color, colorEnd: color, opacity },
],
}), [color, originX, originY, direction, spread, opacity]);
const animParams: AnimParams = useMemo(() => ({
speed,
angleAmp: 40,
lengthAmp: 25,
widthAmp: 15,
haloAmp: 40,
}), [speed]);
return (
<GodLights
scene={scene}
animParams={animated ? animParams : undefined}
className={className}
style={style}
/>
);
}Usage across multiple sections:
// purple hero
<GodLightsBackground color="#a78bfa" originX={20} style={{ position: "absolute", inset: 0, width: "100%", height: "100%" }} />
// warm amber, slower
<GodLightsBackground color="#ffd28a" originX={80} direction={200} speed={0.5} style={{ position: "absolute", inset: 0, width: "100%", height: "100%" }} />
// teal, static
<GodLightsBackground color="#34d399" animated={false} style={{ position: "absolute", inset: 0, width: "100%", height: "100%" }} />Reactive scene (follow mouse)
Swap the scene prop and the component re-renders automatically. Wrap the config in useMemo so it only recomputes when the mouse position changes.
"use client";
import { useState, useCallback, useMemo } from "react";
import { GodLights, DEFAULT_BACKGROUND_LAYER, DEFAULT_HALO_LAYER, DEFAULT_RAY_LAYER } from "godlights";
import type { SceneConfig } from "godlights";
export function MouseTrackingBackground() {
const [mouse, setMouse] = useState({ x: 50, y: 10 });
const handleMouseMove = useCallback((e: React.MouseEvent<HTMLDivElement>) => {
const rect = e.currentTarget.getBoundingClientRect();
setMouse({
x: ((e.clientX - rect.left) / rect.width) * 100,
y: ((e.clientY - rect.top) / rect.height) * 100,
});
}, []);
const scene: SceneConfig = useMemo(() => ({
width: 1920,
height: 1080,
noise: 6,
grainSize: 1,
layers: [
{ ...DEFAULT_BACKGROUND_LAYER, bgColor: "#06060f" },
{ ...DEFAULT_HALO_LAYER, originX: mouse.x, originY: mouse.y, color: "#a78bfa", intensity: 0.28, size: 0.45 },
{ ...DEFAULT_RAY_LAYER, originX: mouse.x, originY: mouse.y, direction: 180, spread: 80, colorStart: "#a78bfa", colorEnd: "#a78bfa", opacity: 0.18 },
],
}), [mouse]);
return (
<div
style={{ position: "relative", width: "100%", height: "100vh" }}
onMouseMove={handleMouseMove}
>
<GodLights
scene={scene}
style={{ position: "absolute", inset: 0, width: "100%", height: "100%" }}
/>
<div style={{ position: "relative", zIndex: 1 }}>{/* content */}</div>
</div>
);
}Cycling presets with smooth transitions
Swap the scene prop between presets. The running animation loop covers the cut naturally. For a cross-fade, layer two <GodLights> instances and animate opacity between them.
"use client";
import { useState, useEffect, useRef } from "react";
import { GodLights } from "godlights";
import type { SceneConfig } from "godlights";
const presets: SceneConfig[] = [
{
width: 1920, height: 1080, noise: 8, grainSize: 1,
layers: [
{ type: "background", bgType: "solid", bgColor: "#06060f", bgColor2: "#06060f", bgGradientAngle: 180 },
{ type: "halo", originX: 20, originY: 5, color: "#a78bfa", intensity: 0.3, size: 0.5, blendMode: "lighter" },
{ type: "rays", direction: 160, spread: 70, originX: 20, originY: 5, rayCount: 24, rayWidth: 70, divergence: 1.8, rayLength: 1.0, colorStart: "#a78bfa", colorEnd: "#a78bfa", opacity: 0.18, blendMode: "screen", fadeToTransparent: true, blur: 12, randomnessWidth: 60, randomnessLength: 20, randomnessAngle: 15, seed: 1 },
],
},
{
width: 1920, height: 1080, noise: 8, grainSize: 1,
layers: [
{ type: "background", bgType: "solid", bgColor: "#060f08", bgColor2: "#060f08", bgGradientAngle: 180 },
{ type: "halo", originX: 80, originY: 5, color: "#34d399", intensity: 0.3, size: 0.5, blendMode: "lighter" },
{ type: "rays", direction: 200, spread: 70, originX: 80, originY: 5, rayCount: 24, rayWidth: 70, divergence: 1.8, rayLength: 1.0, colorStart: "#34d399", colorEnd: "#34d399", opacity: 0.18, blendMode: "screen", fadeToTransparent: true, blur: 12, randomnessWidth: 60, randomnessLength: 20, randomnessAngle: 15, seed: 2 },
],
},
];
export function CyclingPresets() {
const [current, setCurrent] = useState(0);
const [next, setNext] = useState(1);
const [fading, setFading] = useState(false);
useEffect(() => {
const id = setInterval(() => {
setNext((current + 1) % presets.length);
setFading(true);
setTimeout(() => {
setCurrent((c) => (c + 1) % presets.length);
setFading(false);
}, 800);
}, 4000);
return () => clearInterval(id);
}, [current]);
return (
<div style={{ position: "relative", width: "100%", height: "100vh" }}>
<GodLights scene={presets[current]} animParams={{ speed: 1, angleAmp: 40, lengthAmp: 25, widthAmp: 15, haloAmp: 40 }} style={{ position: "absolute", inset: 0, width: "100%", height: "100%" }} />
<GodLights
scene={presets[next]}
animParams={{ speed: 1, angleAmp: 40, lengthAmp: 25, widthAmp: 15, haloAmp: 40 }}
style={{
position: "absolute", inset: 0, width: "100%", height: "100%",
opacity: fading ? 1 : 0,
transition: "opacity 0.8s ease",
}}
/>
</div>
);
}Performance optimization
Each animated <GodLights animParams={...}> runs its own requestAnimationFrame loop. For multiple instances on the same page:
"use client";
import { useEffect, useRef, useState } from "react";
import { GodLights } from "godlights";
import type { SceneConfig } from "godlights";
// 1. Reduce rayCount and blur for background/decorative instances
const lightweightScene: SceneConfig = {
width: 1920,
height: 1080,
noise: 4,
grainSize: 1,
layers: [
{ type: "background", bgType: "solid", bgColor: "#06060f", bgColor2: "#06060f", bgGradientAngle: 180 },
{ type: "halo", originX: 50, originY: 0, color: "#a78bfa", intensity: 0.2, size: 0.4, blendMode: "lighter" },
{
type: "rays",
direction: 180, spread: 80,
originX: 50, originY: 0,
rayCount: 12, // ← keep low for secondary instances
rayWidth: 60, divergence: 2, rayLength: 1.0,
colorStart: "#a78bfa", colorEnd: "#a78bfa",
opacity: 0.15, blendMode: "screen", fadeToTransparent: true,
blur: 6, // ← blur is expensive; reduce or set to 0
randomnessWidth: 60, randomnessLength: 20, randomnessAngle: 15, seed: 1,
},
],
};
// 2. Pause animation when the element is off-screen
function LazyAnimatedBackground({ scene }: { scene: SceneConfig }) {
const ref = useRef<HTMLDivElement>(null);
const [visible, setVisible] = useState(false);
useEffect(() => {
const el = ref.current;
if (!el) return;
const observer = new IntersectionObserver(
([entry]) => setVisible(entry.isIntersecting),
{ threshold: 0.1 }
);
observer.observe(el);
return () => observer.disconnect();
}, []);
return (
<div ref={ref} style={{ position: "relative", height: 400 }}>
<GodLights
scene={scene}
animParams={visible ? { speed: 1, angleAmp: 40, lengthAmp: 25, widthAmp: 15, haloAmp: 40 } : undefined}
style={{ position: "absolute", inset: 0, width: "100%", height: "100%" }}
/>
</div>
);
}
export function MultiSectionPage() {
return (
<>
<LazyAnimatedBackground scene={lightweightScene} />
<LazyAnimatedBackground scene={lightweightScene} />
<LazyAnimatedBackground scene={lightweightScene} />
</>
);
}Quick checklist for multiple instances:
- Keep
rayCount≤ 16 for non-hero sections - Set
blur: 0or keep it under 8 — Gaussian blur viaOffscreenCanvasis the heaviest operation - Omit
animParamsfor purely decorative static instances - Pause off-screen instances with
IntersectionObserveras shown above
Common mistakes
Missing BackgroundLayer
layers[0] must be type: "background". Without it the canvas is never cleared and animated rays smear across frames.
Wrong blend mode on light backgrounds
"screen" and "lighter" are additive — they're invisible on white. Use "multiply" for light backgrounds with dark rays.
opacityAmp in AnimParams
This field doesn't exist. The valid keys are speed, angleAmp, lengthAmp, widthAmp, haloAmp. TypeScript will catch this at compile time.
className="absolute" not working
The wrapper div uses position: relative as an inline style default. A Tailwind class won't override it. Pass style={{ position: "absolute", inset: 0, width: "100%", height: "100%" }} instead.
Scene looks wrong at different sizes
originX / originY are percentages of scene.width / scene.height. If you change width/height, origins scale automatically — but blur is in absolute pixels and may need adjustment.
AI / LLM usage
If you're using an AI coding assistant (Cursor, Copilot, Claude, etc.) to generate scenes, point it at the machine-readable docs:
- context7.com/gustavoquinalha/godlights — use with the Context7 MCP so Claude, Cursor, and Copilot can pull up-to-date docs automatically
- godlights.io/llms.txt — quick start, common mistakes, key constraints
- godlights.io/llms-full.txt — complete type reference, all field ranges, full Next.js example
- Context7 llms.txt — always up-to-date, dynamically generated — paste into any AI chat
- godlights.io/registry.json — shadcn registry with ready-made components for v0, Bolt, Lovable, and Cursor
- godlights.io/godlights.mdc — Cursor rules file (
.cursor/rules/): teaches Cursor to use Godlights for light effects - godlights.io/godlights.cursorrules — legacy
.cursorrulesformat for older Cursor versions
These files follow the llms.txt standard and are designed to fit in a context window alongside your code. Providing them to the model avoids the most common generation errors.
With Context7 MCP installed, you can just ask your assistant: "use context7 — how do I add a godlights hero background in Next.js?" and it will fetch the right docs automatically.
License
MIT
