Skip to content

FreeIn reviewv1.0.0

Prism

Cut glass: facets that bend the colour behind them, with a fringe at each edge.
npx shadcn@latest add @vibebox/prism

Controls

1
1
<Prism />

About

A cellular pattern gives the facets; each cell's id gives its tilt. The colour behind is a three-hue field; every facet samples it at the pixel displaced by its tilt, with the three channels displaced by 1.15, 1.0 and 0.85 of that amount, which is dispersion. A thin bright line where two cells meet is the edge, strongest near the light, and each facet is lit by how its tilt faces a fixed direction.

Use it when

  • A software, fintech or AI brand wants a crisp, premium surface.
  • A property or architecture studio wants glass.
  • A jewellery or beauty brand wants crystal.

Skip it when

  • The brand is warm and organic.
  • Text must sit directly over many bright edges.

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/prism
  3. 3Use it:
    import { Prism } from "@/components/vibebox/prism";
    
    <Prism preset="spectrum" className="min-h-[560px] rounded-xl">
      <div className="p-12 text-white">
        <h1 className="text-4xl font-semibold">Clear, sharp, finished.</h1>
      </div>
    </Prism>

Code

3 files
components/vibebox/prism/index.tsx
/**
 * VibeBox Motion · Prism
 * 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 { Prism, type PrismProps, type PrismColor, type PrismValue } from "./prism.client";
export { PRESET_NAMES as PRISM_PRESETS, presets as prismPresets, type PrismPreset } from "./prism.shader";
components/vibebox/prism/prism.client.tsx
/**
 * VibeBox Motion · Prism
 * 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 { prismPiece, presets, type PrismPreset } from "./prism.shader";

export type PrismColor = "baseP" | "hueA" | "hueB" | "hueC";
export type PrismValue = "facets" | "spread" | "shine";

export type PrismProps = {
  /** What sits on the scene: a heading, a call to action, a whole section. */
  children?: ReactNode;
  /** A named look: `spectrum`, `ice`, `steel`, `quartz`, `gold`. */
  preset?: PrismPreset;
  /** Overrides single palette colours with any CSS colour. */
  colors?: Partial<Record<PrismColor, 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<PrismValue, number>>;

const POSTER: PosterSpec = { type: "linear", from: "baseP", to: "hueB", angle: 135, blobs: [{ color: "hueA", at: [25, 30], size: 38 }, { color: "hueC", at: [75, 70], size: 38 }] };

export function Prism({ children, preset, colors, speed, intensity, interactive = true, label, still, className, facets, spread, shine }: PrismProps) {
  return (
    <BackdropCanvas
      slug="prism"
      piece={prismPiece}
      presets={presets}
      fallback="spectrum"
      preset={preset}
      colors={colors}
      values={{ facets, spread, shine }}
      speed={speed}
      intensity={intensity}
      poster={POSTER}
      scale={0.5}
      stillTime={3}
      still={still}
      interactive={interactive}
      label={label}
      className={className}
    >
      {children}
    </BackdropCanvas>
  );
}
components/vibebox/prism/prism.shader.ts
/**
 * VibeBox Motion · Prism
 * 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";

/*
 * Cut glass: the plane is divided into facets, each tilted a little differently, and each facet
 * shows the colour behind it displaced by its tilt, with red, green and blue displaced by slightly
 * different amounts (dispersion), so a facet's edge carries the fringe of a real prism. The facets
 * drift and the pointer is a light that glints along their edges.
 */

const SCENE = /* glsl */ `
uniform vec3 uBaseP;
uniform vec3 uHueA;
uniform vec3 uHueB;
uniform vec3 uHueC;
uniform float uFacets;
uniform float uSpread;
uniform float uShine;
uniform float uSpeed;
uniform float uIntensity;

float T;

vec3 field(vec2 q) {
  float k = 0.5 + 0.5 * sin(q.x * 1.3 + q.y * 0.7 + T * 0.1);
  float m = 0.5 + 0.5 * sin(q.y * 1.6 - q.x * 0.5 - T * 0.08 + 2.0);
  vec3 c = mix(lin(uHueA), lin(uHueB), k);
  c = mix(c, lin(uHueC), m * 0.7);
  return mix(lin(uBaseP), c, 0.62);
}

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);

  vec2 p = uv * uFacets + vec2(T * 0.03, T * 0.015);
  vec3 v = voronoi(p);
  // Each facet's tilt, from its id.
  float id = v.z;
  vec2 tilt = vec2(h1(int(id * 9731.0)) - 0.5, h1(int(id * 5113.0) + 17) - 0.5) * 2.0;

  // Dispersion: the three channels look through the facet at slightly different angles.
  vec2 off = tilt * uSpread;
  vec3 col;
  col.r = field(uv * 2.0 + off * 1.15).r;
  col.g = field(uv * 2.0 + off).g;
  col.b = field(uv * 2.0 + off * 0.85).b;

  // Facet edges: thin bright lines where one facet meets the next, strongest toward the light.
  float edge = 1.0 - smoothstep(0.0, 0.07, v.y - v.x);
  float toLight = exp(-length(uv - mix(vec2(0.3, 0.2), pu, uHover)) * 1.6);
  col += lin(uHueA) * edge * (0.12 + 0.55 * toLight) * uShine;
  // Each facet catches the light a little differently.
  col *= 0.4 + 0.9 * (0.5 + 0.5 * dot(normalize(tilt + 0.001), vec2(0.6, 0.8))) * (0.45 + toLight);

  col *= uIntensity;
  col *= 1.0 - 0.3 * smoothstep(0.55, 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 = ["spectrum", "ice", "steel", "quartz", "gold"] as const;
export type PrismPreset = (typeof PRESET_NAMES)[number];

export const presets: Record<PrismPreset, ScenePreset> = {
  spectrum: {
    colors: { baseP: "#0a0c1c", hueA: "#36d6ff", hueB: "#7a4cff", hueC: "#ff4fb6" },
    values: { facets: 3.2, spread: 0.6, shine: 1, speed: 0.7, intensity: 1.05 },
  },
  ice: {
    colors: { baseP: "#06182a", hueA: "#bfe9ff", hueB: "#4aa8ff", hueC: "#e9fbff" },
    values: { facets: 3.6, spread: 0.5, shine: 1.1, speed: 0.6, intensity: 1.05 },
  },
  steel: {
    colors: { baseP: "#0c0e12", hueA: "#cbd5e1", hueB: "#6b7686", hueC: "#f2f5f9" },
    values: { facets: 3, spread: 0.45, shine: 1, speed: 0.5, intensity: 1.05 },
  },
  quartz: {
    colors: { baseP: "#160a14", hueA: "#ffc2da", hueB: "#c8a2ff", hueC: "#ffe3ec" },
    values: { facets: 3.4, spread: 0.55, shine: 1, speed: 0.55, intensity: 1.05 },
  },
  gold: {
    colors: { baseP: "#140d05", hueA: "#ffd27a", hueB: "#c98a2b", hueC: "#fff0c9" },
    values: { facets: 3.2, spread: 0.5, shine: 1.1, speed: 0.55, intensity: 1.05 },
  },
};

export const prismPiece = createScenePiece("prism", fragment);

Props

PropTypeDefaultDescription
childrenReactNode—What sits on the scene: a heading, a call to action, a whole section.
preset"spectrum" | "ice" | "steel" | "quartz" | "gold""spectrum"The look: spectrum, ice, steel, quartz, gold.
colors{ baseP?: string; hueA?: string; hueB?: string; hueC?: string }—Overrides single palette colours with any CSS colour: baseP (the dark behind the glass), hueA (the first colour in the glass), hueB (the second colour in the glass), hueC (the third colour in the glass).
speednumber1Multiplies the preset's speed; 0 holds the scene still.
intensitynumber1Multiplies the preset's brightness, 0.5 to 1.4.
facetsnumber3.2How many facets cross the frame (1.5 to 7; each look sets its own).
spreadnumber0.6How far each facet bends the colour (0.1 to 1.4; each look sets its own).
shinenumber1How bright the facet edges glint (0 to 2; each look sets its own).
interactivebooleantrueThe pointer is a light that glints along the facet edges.
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 glass; the facets do not drift.

Edge cases and limits

  • Edges are bright: keep small text off them.
  • Steel and quartz are pale; use dark text.
  • Few facets (under 2) look like a gradient; use 3 to 4.
  • 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

  • A dusk skyline of lit towers under an orange and violet sky

    A skyline of lit windows against a glowing sky, with a road of light trails in front.

    Backgrounds · WebGL2

↑ ↓ to move↵ to openesc to close