About
Depth is the distance from the top of the frame; the water mixes from the surface colour to the abyss colour along it. Shafts are three sine bands over a slanted coordinate, sharpened with a power and broken by noise, fading with depth, and cut near the pointer. Caustics are the cellular edge pattern, strongest in the top third. Flecks are three layers of cell-based points drifting upward past.
Use it when
- A dive, aquarium, pool, fisheries or marine brand wants the water.
- A spa or resort page wants calm blue.
- A science or education page wants the ocean.
Skip it when
- The brand is dry, warm or urban.
- A lot of body text sits in the top third where the caustics run.
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/deep-sea - 3Use it:
import { DeepSea } from "@/components/vibebox/deep-sea"; <DeepSea preset="aquarium" className="min-h-[560px] rounded-xl"> <div className="p-12 text-white"> <h1 className="text-4xl font-semibold">Twenty metres down, nothing but quiet.</h1> </div> </DeepSea>
Code
3 filescomponents/vibebox/deep-sea/deep-sea.client.tsx
/**
* VibeBox Motion · Deep Sea
* 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 { deepSeaPiece, presets, type DeepSeaPreset } from "./deep-sea.shader";
export type DeepSeaColor = "surface" | "abyss" | "ray" | "fleck";
export type DeepSeaValue = "rays" | "caustics" | "flecks";
export type DeepSeaProps = {
/** What sits on the scene: a heading, a call to action, a whole section. */
children?: ReactNode;
/** A named look: `aquarium`, `abyss`, `lagoon`, `arctic`. */
preset?: DeepSeaPreset;
/** Overrides single palette colours with any CSS colour. */
colors?: Partial<Record<DeepSeaColor, 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<DeepSeaValue, number>>;
const POSTER: PosterSpec = { type: "linear", from: "surface", to: "abyss", angle: 180 };
export function DeepSea({ children, preset, colors, speed, intensity, interactive = true, label, still, className, rays, caustics, flecks }: DeepSeaProps) {
return (
<BackdropCanvas
slug="deep-sea"
piece={deepSeaPiece}
presets={presets}
fallback="aquarium"
preset={preset}
colors={colors}
values={{ rays, caustics, flecks }}
speed={speed}
intensity={intensity}
poster={POSTER}
scale={0.5}
stillTime={3}
still={still}
interactive={interactive}
label={label}
className={className}
>
{children}
</BackdropCanvas>
);
}components/vibebox/deep-sea/deep-sea.shader.ts
/**
* VibeBox Motion · Deep Sea
* 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";
/*
* Under the surface: water that darkens with depth, shafts of light slanting down from above that
* sway and break up, caustics dancing near the top, and flecks of marine snow drifting through.
* The pointer parts the shafts and stirs the flecks.
*/
const SCENE = /* glsl */ `
uniform vec3 uSurface;
uniform vec3 uAbyss;
uniform vec3 uRay;
uniform vec3 uFleck;
uniform float uRays;
uniform float uCaustics;
uniform float uFlecks;
uniform float uSpeed;
uniform float uIntensity;
float T;
/* Light focused by the surface: four plane waves, each bent by one at right angles, summed and
sharpened. Where their crests meet the sum peaks, which gives the soft, moving net of bright
lines that sunlit water throws on the floor. */
float caustic(vec2 p) {
float c = 0.0;
for (int i = 0; i < 4; i++) {
float fi = float(i);
float a = fi * 1.9 + 0.7;
vec2 d = vec2(cos(a), sin(a));
float ph = dot(p, d) * (2.0 + 0.8 * fi) + T * (0.3 + 0.09 * fi);
ph += 1.5 * sin(dot(p, vec2(-d.y, d.x)) * 1.5 + T * 0.27 + a);
c += 0.5 + 0.5 * cos(ph);
}
c *= 0.25;
return pow(c, 4.5) * 3.2;
}
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);
float depth = clamp(0.5 - uv.y, 0.0, 1.0);
vec3 col = mix(lin(uSurface), lin(uAbyss), pow(depth, 0.75));
// Shafts: soft slanted bands, brighter near the surface, drifting and shimmering.
float slant = uv.x + uv.y * 0.42;
float parted = uHover * exp(-pow(slant - (pu.x + pu.y * 0.42), 2.0) * 18.0) * 0.5;
float shafts = 0.0;
for (int i = 0; i < 3; i++) {
float fi = float(i);
float s = slant * (3.0 + fi * 2.4) + T * (0.05 + 0.02 * fi) + fi * 4.0;
float band = 0.5 + 0.5 * sin(s + 1.6 * sin(s * 0.37 + T * 0.1));
band = pow(band, 3.0 + fi);
shafts += band * (0.55 - 0.12 * fi);
}
shafts *= (0.4 + 0.6 * smoothstep(0.0, 0.7, fbm3(vec2(slant * 4.0, T * 0.06 + uv.y))));
shafts *= smoothstep(0.95, 0.0, depth) * (1.0 - parted);
col += lin(uRay) * shafts * uRays * 0.5;
// Caustics, strongest in the top third.
float c = caustic(uv * 2.6 + 4.0) * 0.65 + caustic(uv * 4.4 - 2.0) * 0.35;
col += lin(uRay) * c * uCaustics * smoothstep(0.65, 0.0, depth) * 0.5;
// Marine snow: three depths of drifting specks.
for (int l = 0; l < 3; l++) {
float fl = float(l);
float scale = 18.0 + fl * 22.0;
vec2 p = uv * scale;
p.y += T * (0.6 + fl * 0.4);
p.x += sin(T * 0.1 + fl) * 2.0 + (pu.x - uv.x) * uHover * 1.2;
ivec2 id = ivec2(floor(p)) + ivec2(int(fl) * 37, 11);
if (h2(id) < uFlecks * 0.18) {
vec2 o = h22(id + ivec2(3, 3)) * 0.6 + 0.2;
float d = length(fract(p) - o);
col += lin(uFleck) * smoothstep(0.14, 0.0, d) * (0.4 + 0.3 * (1.0 - fl * 0.3));
}
}
col *= uIntensity;
col *= 1.0 - 0.34 * smoothstep(0.5, 1.2, length(uv * vec2(0.85, 1.1)));
fragColor = vec4(finish(col, gl_FragCoord.xy), 1.0);
}
`;
export const fragment = compose(SCENE);
export const PRESET_NAMES = ["aquarium", "abyss", "lagoon", "arctic"] as const;
export type DeepSeaPreset = (typeof PRESET_NAMES)[number];
export const presets: Record<DeepSeaPreset, ScenePreset> = {
aquarium: {
colors: { surface: "#0f7fb0", abyss: "#03203a", ray: "#bff3ff", fleck: "#d8f6ff" },
values: { rays: 1, caustics: 1, flecks: 1, speed: 0.8, intensity: 1.05 },
},
abyss: {
colors: { surface: "#0a2e5a", abyss: "#010510", ray: "#6fb7ff", fleck: "#9fd0ff" },
values: { rays: 0.7, caustics: 0.5, flecks: 1.2, speed: 0.6, intensity: 1.05 },
},
lagoon: {
colors: { surface: "#20c9c0", abyss: "#046a7a", ray: "#e8fffb", fleck: "#ffffff" },
values: { rays: 1.1, caustics: 1.2, flecks: 0.7, speed: 0.8, intensity: 1.05 },
},
arctic: {
colors: { surface: "#6fc4ff", abyss: "#0a2a5a", ray: "#ffffff", fleck: "#e8f6ff" },
values: { rays: 1, caustics: 0.8, flecks: 0.9, speed: 0.6, intensity: 1.05 },
},
};
export const deepSeaPiece = createScenePiece("deep-sea", fragment);components/vibebox/deep-sea/index.tsx
/**
* VibeBox Motion · Deep Sea
* 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 { DeepSea, type DeepSeaProps, type DeepSeaColor, type DeepSeaValue } from "./deep-sea.client";
export { PRESET_NAMES as DEEP_SEA_PRESETS, presets as deepSeaPresets, type DeepSeaPreset } from "./deep-sea.shader";Props
| Prop | Type | Default | Description |
|---|---|---|---|
| children | ReactNode | — | What sits on the scene: a heading, a call to action, a whole section. |
| preset | "aquarium" | "abyss" | "lagoon" | "arctic" | "aquarium" | The look: aquarium, abyss, lagoon, arctic. |
| colors | { surface?: string; abyss?: string; ray?: string; fleck?: string } | — | Overrides single palette colours with any CSS colour: surface (the water near the surface), abyss (the water at depth), ray (the light shafts and caustics), fleck (the marine snow). |
| 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. |
| rays | number | 1 | How bright the shafts are (0 to 1.6; each look sets its own). |
| caustics | number | 1 | How bright the caustic light is (0 to 1.6; each look sets its own). |
| flecks | number | 1 | How much marine snow drifts through (0 to 1.8; each look sets its own). |
| interactive | boolean | true | The pointer parts the light shafts and stirs the flecks. |
| 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 water; the light does not move.
Edge cases and limits
- Caustics are busy in the top third; keep the heading lower.
- Arctic is light: use dark text.
- Many flecks on a phone look like noise; keep
flecksunder 1.
Related

Ocean
FreeA sea from a low viewpoint: a swell, a sun or moon on the horizon, and a glitter path the pointer ripples.
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