Cinematic Text
v1.0.0Cinematic typography that reveals a looping video through bold text, with cursor-driven SVG distortion shaders (liquid, prism, glitch, wave, melt) and optional 3D tilt. Dependency-free.
Installation
npx vectorvesper add cinematic-textRun npx vectorvesper init once first to set up your project.
Usage
Import
Usage
Client-only component — disable SSR
This component requires browser APIs and must be rendered client-side only.
prefers-reduced-motion and provides a graceful fallback.Props
An advanced video-masked typography component featuring real-time, cursor-driven SVG distortion shaders (liquid, prism, glitch, wave, melt) and interactive 3D card perspective tilt. It runs its animation loops on high-performance requestAnimationFrame directly mutating SVG attributes, keeping it lightweight and completely dependency-free.
<WarpedVideoText />
Cinematic video-masked typography with shader distortion and 3D tilt. The visible text is a video revealed through a text-shaped mask, so an accessible label is provided via role="img" + aria-label.
| Prop | Type | Default | Description |
|---|---|---|---|
text | string | "CREATIVE" | The masking text. |
video* | string | "" | Looping background video path — **required** to reveal anything. |
fx | "liquid" | "prism" | "glitch" |
"wave" | "melt" | "clean" | "liquid" | Cursor-driven distortion mode (`clean` = no distortion). |
font | string | "font-sans" | Font-family CSS class (e.g. `font-oswald`). The text is always `font-black uppercase`. |
poster | string | — | Still frame shown before the video loads / if autoplay is blocked. |
isPlaying | boolean | true | Play/pause the background video. |
width | number | 1200 | Outer clip rect — base width in px (responsive `max-width`). |
height | number | 300 | Base height in px. |
fontSize | number | 145 | Text size in SVG units at base width. |
innerWidth | number | width − 60 | Inner clip rect — the width the text is fit within. |
fitText | boolean | true | Auto-compress text horizontally to fit `innerWidth`. |
charWidthRatio | number | 0.55 | Assumed avg glyph width ÷ fontSize for fit math (condensed ≈ 0.45, wide ≈ 0.7). |
minTextScaleX | number | 0.4 | Floor for horizontal compression so text never squishes to nothing. |
bleed | number | 0 | Reveals a band of video around the text at the card edges — higher = thicker. Pair with `clipOverflow={false}` to spill past the box; the 3D tilt exaggerates it. |
filterMargin | number | 20 | How far (%) the distortion region extends beyond the box before clipping. |
clipOverflow | boolean | true | Clip effects to the outer rect; set `false` to let melt/liquid (and `bleed`) spill. |
videoFit | "cover" | "contain" | "fill" | "cover" | How the video fills the clip; `contain` shows the whole frame. |
videoScale | number | 1.04 | Zoom of the video inside the clip — lower reveals more of the frame; `1.0` = exact cover. |
videoPosition | string | "center" | `object-position` of the video, e.g. `"top"`, `"50% 30%"`. |
enableTilt | boolean | true | 3D perspective tilt on mouse move. |
tiltMaxX | number | 12 | Max horizontal tilt in degrees. |
tiltMaxY | number | 12 | Max vertical tilt in degrees. |
tiltPerspective | number | 1200 | Perspective distance for the 3D effect (px). |
tiltLerp | number | 0.08 | Cursor easing coefficient (0.01–0.2). |
freq | number | 0.015 | Noise base frequency (liquid/wave/melt shaders). |
damp | number | 8 | Strength multiplier for the speed-based distortion. |
className | string | "" | Extra classes on the outer container. |
style | React.CSSProperties | — | Inline style overrides for the container. |
aria-label | string | = text | Accessible name for the masked text. |
Examples
video. Abstract/high-contrast clips read best; avoid clips with their own baked-in text. Provide a poster so the mask isn't blank while the video loads.font-black uppercase tracking-tighter).'use client'). It uses refs/useId and renders on the client; the masked video won't animate during SSR.prefers-reduced-motion automatically — cursor distortion and 3D tilt are disabled; the masked video still plays.