Skip to content

FreeIn reviewv1.0.0

Deep Sea

Underwater: light shafts slanting down, caustics near the top and flecks of marine snow.
npx shadcn@latest add @vibebox/deep-sea

Controls

Look
1
1
<DeepSea />

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

  1. 1Add the VibeBox registry to components.json:
    {
      "registries": {
        "@vibebox": "https://motion.vibeboxph.com/r/{name}.json"
      }
    }
  2. 2Add the component with the shadcn CLI:
    npx shadcn@latest add @vibebox/deep-sea
  3. 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 files
components/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;

float caustic(vec2 p) {
  vec2 q = p + 0.25 * vec2(sin(p.y * 1.7 + T * 0.5), cos(p.x * 1.5 - T * 0.4));
  vec3 v = voronoi(q + vec2(T * 0.08, 0.0));
  return pow(1.0 - smoothstep(0.0, 0.12, v.y - v.x), 3.0);
}

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 * 3.0 + 4.0) * 0.6 + caustic(uv * 5.2 - 2.0) * 0.4;
  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

PropTypeDefaultDescription
childrenReactNode—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).
speednumber1Multiplies the preset's speed; 0 holds the scene still.
intensitynumber1Multiplies the preset's brightness, 0.5 to 1.4.
raysnumber1How bright the shafts are (0 to 1.6; each look sets its own).
causticsnumber1How bright the caustic light is (0 to 1.6; each look sets its own).
flecksnumber1How much marine snow drifts through (0 to 1.8; each look sets its own).
interactivebooleantrueThe pointer parts the light shafts and stirs the flecks.
labelstring—Accessible name; leave it out when the scene is only a background.
stillbooleanfalseHold one composed frame until false (a gallery card that plays on hover).
classNamestring—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 label it 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 flecks under 1.
  • A sea at dusk under a low sun with a glitter path across the water

    Ocean

    Free

    A sea from a low viewpoint: a swell, a sun or moon on the horizon, and a glitter path the pointer ripples.

    Backgrounds · WebGL2

  • Green and violet aurora curtains over a mountain ridge and stars

    Aurora

    Free

    Curtains of northern light over a night sky and a mountain ridge, drawn toward the pointer.

    Backgrounds · WebGL2

  • Warm amber bokeh lights of different sizes on a dark background

    Bokeh

    Free

    Out-of-focus lights in three depths, brighter at the rim and drifting with the pointer.

    Backgrounds · WebGL2

↑ ↓ to move↵ to openesc to close