Surface study no. 04
A studio for identities that move. We cast brands in liquid metal, then let them settle into something you can hold.
A live WebGL gradient of colored smoke: soft plumes rise from the bottom edge, widen as they climb, curl through a fractal flow and thin out near the top, over a low haze along the floor. The smoke is colored by a gradient ramp from the base of each plume to its tip, blended in Oklab, under a fine film grain. Colors, base, density, plume count, height, rise speed, curl, softness, scale, saturation, grain and seed are all props; a new palette fades through over a set time, and the smoke parts gently around the cursor. It grows softly out of its base on load, adapts its resolution to the device, pauses offscreen, holds a still frame for reduced motion and falls back to soft CSS plumes without WebGL.
The import and the props worth knowing about, in one place.
import { SmokeGradient } from "@/components/beste/component/smoke-gradient";
// As a layer behind content
<section className="relative min-h-[40rem]">
<SmokeGradient className="absolute inset-0" />
<div className="relative">...</div>
</section>
// Pale incense on a light page
<SmokeGradient
className="min-h-[32rem]"
baseColor="#f4f1ec"
colors={["#d9d2c8", "#b7a6c9", "#8f7fb0"]}
plumes={1}
curl={0.8}
softness={0.8}
/>Read from the component's own type, so this cannot drift from what it accepts.
| Prop | Type | Default | Description |
|---|---|---|---|
colors | string[] | — | Smoke colors from the base of a plume to its tip, 1 to 6. Any CSS color, tokens included. |
baseColor | string | — | Color behind the smoke. |
density | number | — | How thick the smoke is, 0 (a trace) to 1 (heavy). |
plumes | number | — | Number of plumes rising from the bottom edge, 1 to 5. |
height | number | — | How far the plumes reach up the frame, 0.2 to 1. |
rise | number | — | How fast the smoke rises, 0 (still) to 3. |
curl | number | — | How much the smoke curls and folds, 0 (straight) to 1. |
softness | number | — | Edge of the smoke, 0 (crisp wisps) to 1 (soft haze). |
scale | number | — | Size of the smoke's detail, 0.5 (fine) to 2 (broad). |
saturation | number | — | Color intensity, 0 (grey) to 2 (vivid). |
grain | number | — | Film grain over the frame, 0 to 1. |
grainSize | number | — | Size of one grain in CSS pixels, 1 to 4. |
grainMotion | boolean | — | Let the grain flicker like film instead of holding still. |
transition | number | — | Seconds a change of `colors` or `baseColor` takes to fade through. 0 swaps at once. |
seed | number | — | Picks a different arrangement of plumes. |
interactive | boolean | — | The smoke parts gently around the cursor. |
paused | boolean | — | Hold the smoke still. |
className | string | — | |
children | ReactNode | — |