About
Polar coordinates around a point that leans toward the pointer. The pleat function is four sines of the angle at whole-number multiples of the fold count (a fractional count would leave a seam where the angle wraps), each with its own drift and a small dependence on the radius for the twist. The result is mapped through a smooth step to a lit amount, with a second faint pass stretched along the radius for the streaks; colour goes from the shadow tone to the body to the highlight. The middle is darkened and a two-scale hash grain is multiplied in.
Use it when
- A fashion, beauty, jewellery or event page wants a rich, tactile hero.
- A launch or campaign page wants a bold radial focus for the headline.
- A brand with a single strong colour wants it shown as cloth.
Skip it when
- The page is dense with copy: use Dot Waves or Studio.
- The brand is cool and technical: use Strands or Plasma.
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/burst - 3Use it:
import { Burst } from "@/components/vibebox/burst"; <Burst preset="violet" className="min-h-[560px] rounded-xl"> <div className="p-12 text-white"> <h1 className="text-4xl font-semibold">Drawn back, to show the light.</h1> </div> </Burst>
Code
3 filescomponents/vibebox/burst/burst.client.tsx
/**
* VibeBox Motion · Burst
* 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 { burstPiece, presets, type BurstPreset } from "./burst.shader";
export type BurstColor = "shadow" | "body" | "high" | "hole";
export type BurstValue = "folds" | "twist" | "depth" | "holeSize" | "grain";
export type BurstProps = {
/** What sits on the scene: a heading, a call to action, a whole section. */
children?: ReactNode;
/** A named look: `violet`, `dusk`, `mono`, `garnet`, `jade`. */
preset?: BurstPreset;
/** Overrides single palette colours with any CSS colour. */
colors?: Partial<Record<BurstColor, 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<BurstValue, number>>;
const POSTER: PosterSpec = { type: "radial", base: "shadow", glow: "body", edge: "shadow", at: [50, 50] };
export function Burst({ children, preset, colors, speed, intensity, interactive = true, label, still, className, folds, twist, depth, holeSize, grain }: BurstProps) {
return (
<BackdropCanvas
slug="burst"
piece={burstPiece}
presets={presets}
fallback="violet"
preset={preset}
colors={colors}
values={{ folds, twist, depth, holeSize, grain }}
speed={speed}
intensity={intensity}
poster={POSTER}
scale={0.5}
stillTime={3}
still={still}
interactive={interactive}
label={label}
className={className}
>
{children}
</BackdropCanvas>
);
}components/vibebox/burst/burst.shader.ts
/**
* VibeBox Motion · Burst
* 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";
/*
* Pleated silk seen from its centre: folds run out of a dark middle in every direction, like a
* drawn-back curtain or the inside of a flower. The folds are the angle around the centre fed
* through a few drifting sines, so the brightness of each pleat is how square-on it faces a soft
* light; colour runs from the shadow tone up through the body to a highlight. A long streak runs
* along each pleat (stretched by the radius), a fine grain sits over everything, as on cloth,
* and the pointer leans the whole thing toward it. `twist` turns the pleats into a spiral.
*/
const SCENE = /* glsl */ `
uniform vec3 uShadow;
uniform vec3 uBody;
uniform vec3 uHigh;
uniform vec3 uHole;
uniform float uFolds;
uniform float uTwist;
uniform float uDepth;
uniform float uHoleSize;
uniform float uGrain;
uniform float uSpeed;
uniform float uIntensity;
float T;
float pleat(float a, float r) {
// Whole numbers of folds, so the pattern closes on itself where the angle wraps: a fractional
// count would leave a visible seam along the negative x axis.
float k = floor(uFolds + 0.5);
float tw = uTwist;
float s = sin(a * k + T * 0.12 + r * tw * 3.0);
s += 0.6 * sin(a * k * 2.0 - T * 0.09 + 1.7 + r * tw * 4.0);
s += 0.4 * sin(a * (floor(k * 0.5) + 1.0) + T * 0.05 + 0.6);
s += 0.25 * sin(a * k * 3.0 + T * 0.2 + r * 5.0);
return s / 2.25;
}
void main() {
vec2 uv = centred(gl_FragCoord.xy);
T = uTime * uSpeed;
float aspect = uResolution.x / uResolution.y;
// The centre leans toward the pointer.
vec2 lean = (uPointer - 0.5) * vec2(aspect, 1.0) * (0.12 + 0.2 * uHover);
vec2 p = uv - lean * 0.6;
float r = length(p);
float a = atan(p.y, p.x);
float f = pleat(a, r);
// How square-on a pleat faces the light, and a second, softer pass offset along the radius.
float lit = smoothstep(-0.75, 0.85, f);
float streak = 0.5 + 0.5 * sin(a * floor(uFolds + 0.5) * 3.0 + r * 9.0 - T * 0.2 + f * 2.0);
float shade = lit * (0.7 + 0.3 * streak);
vec3 shadow = lin(uShadow);
vec3 body = lin(uBody);
vec3 high = lin(uHigh);
vec3 col = mix(shadow, body, smoothstep(0.0, 0.6, shade));
col = mix(col, high, smoothstep(0.62, 1.0, shade) * 0.85);
// The pleats deepen toward the middle, where they meet, and open at the rim.
col *= 0.35 + 0.95 * smoothstep(0.02, 0.5 + 0.3 * uDepth, r);
// The hole: a dark middle with a soft shoulder.
float hole = smoothstep(uHoleSize + 0.14, uHoleSize * 0.5, r);
col = mix(col, lin(uHole), hole * 0.92);
// Cloth grain, in two sizes, multiplied in.
vec2 g = gl_FragCoord.xy;
float grain = h2(ivec2(g * 0.9)) * 0.6 + h2(ivec2(g * 0.45) + ivec2(17, 3)) * 0.4;
col *= 1.0 + (grain - 0.5) * 0.55 * uGrain;
col *= uIntensity;
col *= 1.0 - 0.28 * smoothstep(0.55, 1.15, length(uv * vec2(0.75, 1.0)));
fragColor = vec4(finish(col, gl_FragCoord.xy), 1.0);
}
`;
export const fragment = compose(SCENE);
export const PRESET_NAMES = ["violet", "dusk", "mono", "garnet", "jade"] as const;
export type BurstPreset = (typeof PRESET_NAMES)[number];
export const presets: Record<BurstPreset, ScenePreset> = {
violet: {
colors: { shadow: "#120d1f", body: "#5a3fc8", high: "#d8a8ff", hole: "#0b0912" },
values: { folds: 11, twist: 0.25, depth: 1, holeSize: 0.1, grain: 1, speed: 0.8, intensity: 1.05 },
},
dusk: {
colors: { shadow: "#1a0d1c", body: "#a2417f", high: "#ffc4a0", hole: "#0f0912" },
values: { folds: 9, twist: 0.4, depth: 1, holeSize: 0.09, grain: 1, speed: 0.7, intensity: 1.05 },
},
mono: {
colors: { shadow: "#0c0d10", body: "#59606e", high: "#f2f5fa", hole: "#07080a" },
values: { folds: 12, twist: 0.15, depth: 1.1, holeSize: 0.1, grain: 1.1, speed: 0.7, intensity: 1.05 },
},
garnet: {
colors: { shadow: "#14070a", body: "#a3122b", high: "#ff9a8a", hole: "#0a0405" },
values: { folds: 13, twist: 0.5, depth: 0.9, holeSize: 0.08, grain: 1, speed: 0.8, intensity: 1.05 },
},
jade: {
colors: { shadow: "#071410", body: "#12865f", high: "#b5ffd9", hole: "#040a08" },
values: { folds: 10, twist: 0.2, depth: 1, holeSize: 0.1, grain: 1, speed: 0.7, intensity: 1.05 },
},
};
export const burstPiece = createScenePiece("burst", fragment);components/vibebox/burst/index.tsx
/**
* VibeBox Motion · Burst
* 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 { Burst, type BurstProps, type BurstColor, type BurstValue } from "./burst.client";
export { PRESET_NAMES as BURST_PRESETS, presets as burstPresets, type BurstPreset } from "./burst.shader";Props
| Prop | Type | Default | Description |
|---|---|---|---|
| children | ReactNode | — | What sits on the scene: a heading, a call to action, a whole section. |
| preset | "violet" | "dusk" | "mono" | "garnet" | "jade" | "violet" | The look: violet, dusk, mono, garnet, jade. |
| colors | { shadow?: string; body?: string; high?: string; hole?: string } | — | Overrides single palette colours with any CSS colour: shadow (the tone in the deep folds), body (the colour of the cloth facing the light), high (the highlight along the brightest pleats), hole (the dark in the middle). |
| 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. |
| folds | number | 11 | How many pleats run round the centre (a whole number) (4 to 16; each look sets its own). |
| twist | number | 0.25 | How far the pleats turn into a spiral (0 to 1.2; each look sets its own). |
| depth | number | 1 | How far the shadow reaches out from the middle (0.4 to 1.6; each look sets its own). |
| holeSize | number | 0.1 | How big the dark middle is (0.02 to 0.3; each look sets its own). |
| grain | number | 1 | How visible the cloth's grain is (0 to 2; each look sets its own). |
| interactive | boolean | true | The centre leans toward the pointer. |
| 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 leans the centre toward it like the cursor does; scrolling is never blocked.
- Reduced motion
- One still frame of the cloth; the pleats do not shift.
Edge cases and limits
- The pleats are strongly contrasted: keep long text on a card.
foldsis a whole number; fractional values are rounded.- The grain is deliberate: setting it to 0 makes the cloth look like plastic.
Related

Flow Lines
FreeDozens of thin lines of light riding one slow wave, bunching where they cross, with a glow that follows the pointer.
Backgrounds · WebGL2

Aurora
FreeCurtains of northern light over a night sky and a mountain ridge, drawn toward the pointer.
Backgrounds · WebGL2

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