Gooey Morph
Bars fly in Tetris-style, land as a row, and melt into a word through a gooey blur-and-threshold morph
Installation
$ pnpm dlx shadcn@latest add @remocn/gooey-morphUsage
// src/Root.tsx
import { Composition } from "remotion";
import { GooeyMorph } from "@/components/remocn/gooey-morph";
const GooeyMorphScene = () => (
<GooeyMorph word="HELLO" fontFamily="'Bodoni Moda', Georgia, serif" />
);
export const RemotionRoot = () => (
<Composition
id="GooeyMorph"
component={GooeyMorphScene}
durationInFrames={120}
fps={30}
width={1920}
height={1080}
/>
);The melt works by blurring the alpha channel and pushing it through a near-vertical threshold: where two shapes sit close, their blurred edges add up, cross the threshold, and paint a solid neck between them. Everything that should fuse lives inside the component's single filtered group — nearby elements outside it never join the goo.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
word | string | "HELLO" | The word the bars melt into |
fill | string | "#ffffff" | Fill color of the bars and the word |
fontFamily | string | "sans-serif" | Font family of the word — heavy display faces melt best |
fontSize | number | 237 | Font size in comp pixels |
tracking | number | 0 | Letter spacing of the word |
wordCenterX / wordBaselineY | number | 961.4 / 601.3 | Horizontal center and baseline of the word in comp pixels |
width / height | number | 1920 / 1080 | Coordinate space of the composition |
barWidth / barHeight | number | 213 / 181 | Size of each bar in comp pixels |
barStartPositions | [number, number][] | 4 scattered points | Where each bar starts, as `[x, y]` centers |
barRestPositions | [number, number][] | 4 points in a row | Where each bar lands before the melt |
barEntryFrames | number[] | [8, 0, 4, 13] | Per-bar entry frames — deliberately non-monotonic so the build reads as Tetris, not a wave |
barTravelFrames | number | 30 | Length of one bar's L-shaped move: horizontal into the column, then vertical into the row |
morphStartFrame / morphPeakFrame / morphEndFrame | number | 50 / 63 / 76 | The melt window. Blur and displacement are zero outside it |
blurRadius | number | 6 | Peak goo blur radius |
blurIterations | number | 6 | Box blur passes folded into the equivalent Gaussian sigma |
alphaInputBlack / alphaInputWhite | number | 0.4588 / 0.5098 | Alpha threshold range — the narrower, the harder the goo edge |
displaceAmount | number | 20 | Peak turbulent displacement of the goo edge, in pixels |
displaceSize | number | 19 | Feature size of the displacement noise |
displaceComplexity | number | 1 | Noise octaves |
displaceEvolutionPerSecond | number | 50 | How fast the noise field pans, per second |
className | string | — | Optional className passed to the wrapper |
Inspired by 5 Clean Text Animations by Mapal.