Backgrounds
Backgrounds
Spotlight
Soft pool of accent light that drifts slowly or holds a position, with a vignette that darkens the edges.
Install
Usage
<AbsoluteFill>
<Spotlight />
<Center><TextReveal text="Hello" /></Center>
</AbsoluteFill>Props
| Name | Type | Default | Description |
|---|---|---|---|
delay? | number | — | Frames to wait before entering. |
duration? | number | — | Enter duration in frames. Defaults to 0.6s. |
exit? | number | boolean | — | Exit at the end of the parent `<Sequence>`. `false` keeps it on screen, a number sets the exit length in frames. |
motion? | MotionPreset | — | Override the theme's motion personality. |
background? | string | — | Base fill. Defaults to the theme background. |
color? | string | — | Light color. Defaults to the theme accent. |
intensity? | number | 0.35 | Opacity at the center of the light, 0–1. |
position? | { x: number; y: number; } | — | Pin the light at fractions of the canvas (0–1). Omit to let it drift on a slow figure-eight. |
size? | number | 820 | Light radius in design units. |
vignette? | number | 0.4 | How dark the corners get, 0–1. |
speed? | number | 1 | Drift speed multiplier. 0 holds the light still. |
style? | CSSProperties | — | |
className? | string | — |
Use
- Product hero shots, single-idea statements and dramatic reveals
- Drawing the eye to one area of the frame without a hard shape
Avoid
- Several directional light shafts — use
beams
Dependencies
remotion
Source
registry/items/spotlight.tsx
/**
* @title Spotlight
* @category backgrounds
* @description Soft pool of accent light that drifts slowly or holds a position, with a vignette that darkens the edges.
* @duration sustained
* @use Product hero shots, single-idea statements and dramatic reveals
* @use Drawing the eye to one area of the frame without a hard shape
* @avoid Several directional light shafts — use `beams`
* @tags spotlight, light, vignette, stage, focus
* @example
* <AbsoluteFill>
* <Spotlight />
* <Center><TextReveal text="Hello" /></Center>
* </AbsoluteFill>
*/
import type React from "react";
import { AbsoluteFill } from "remotion";
import { alpha, type MotionProps, useMotion, useTheme, useViewport } from "./core";
export type SpotlightProps = MotionProps & {
/** Base fill. Defaults to the theme background. */
background?: string;
/** Light color. Defaults to the theme accent. */
color?: string;
/** Opacity at the center of the light, 0–1. */
intensity?: number;
/** Pin the light at fractions of the canvas (0–1). Omit to let it drift on a slow figure-eight. */
position?: { x: number; y: number };
/** Light radius in design units. */
size?: number;
/** How dark the corners get, 0–1. */
vignette?: number;
/** Drift speed multiplier. 0 holds the light still. */
speed?: number;
style?: React.CSSProperties;
className?: string;
};
const clamp01 = (value: number) => Math.min(Math.max(value, 0), 1);
export function Spotlight({
background,
color,
intensity = 0.35,
position,
size = 820,
vignette = 0.4,
speed = 1,
style,
className,
...motion
}: SpotlightProps) {
const theme = useTheme();
const { width, height, u } = useViewport();
const m = useMotion(motion);
const light = color ?? theme.colors.accent;
const seconds = (m.frame / m.fps) * speed;
// A 1:2 Lissajous curve: a lazy figure-eight around the upper middle of the frame.
const x = position ? position.x : 0.5 + 0.2 * Math.sin(seconds * 0.5);
const y = position ? position.y : 0.42 + 0.09 * Math.sin(seconds + 0.6);
const radius = u(size) * (1 + 0.04 * Math.sin((m.frame / m.fps) * 0.9));
const strength = intensity * m.presence;
const stop = (share: number) => alpha(light, clamp01(strength * share));
const pool = `radial-gradient(circle ${radius}px at ${x * width}px ${y * height}px, ${stop(1)} 0%, ${stop(0.62)} 22%, ${stop(0.28)} 48%, ${stop(0.08)} 75%, ${stop(0)} 100%)`;
const edges = `radial-gradient(ellipse farthest-corner at 50% 50%, ${alpha("#000000", 0)} 45%, ${alpha("#000000", clamp01(vignette))} 100%)`;
return (
<AbsoluteFill
className={className}
style={{
backgroundColor: background ?? theme.colors.background,
backgroundImage: `${edges}, ${pool}`,
...style,
}}
/>
);
}