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-morph

Usage

tsx
// 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.

Props21

PropTypeDefault
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
number237

Font size in comp pixels

tracking
number0

Letter spacing of the word

wordCenterX / wordBaselineY
number961.4 / 601.3

Horizontal center and baseline of the word in comp pixels

width / height
number1920 / 1080

Coordinate space of the composition

barWidth / barHeight
number213 / 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
number30

Length of one bar's L-shaped move: horizontal into the column, then vertical into the row

morphStartFrame / morphPeakFrame / morphEndFrame
number50 / 63 / 76

The melt window. Blur and displacement are zero outside it

blurRadius
number6

Peak goo blur radius

blurIterations
number6

Box blur passes folded into the equivalent Gaussian sigma

alphaInputBlack / alphaInputWhite
number0.4588 / 0.5098

Alpha threshold range — the narrower, the harder the goo edge

displaceAmount
number20

Peak turbulent displacement of the goo edge, in pixels

displaceSize
number19

Feature size of the displacement noise

displaceComplexity
number1

Noise octaves

displaceEvolutionPerSecond
number50

How fast the noise field pans, per second

className
string—

Optional className passed to the wrapper

  • wordstring

    The word the bars melt into

    Default"HELLO"

  • fillstring

    Fill color of the bars and the word

    Default"#ffffff"

  • fontFamilystring

    Font family of the word — heavy display faces melt best

    Default"sans-serif"

  • fontSizenumber

    Font size in comp pixels

    Default237

  • trackingnumber

    Letter spacing of the word

    Default0

  • wordCenterX / wordBaselineYnumber

    Horizontal center and baseline of the word in comp pixels

    Default961.4 / 601.3

  • width / heightnumber

    Coordinate space of the composition

    Default1920 / 1080

  • barWidth / barHeightnumber

    Size of each bar in comp pixels

    Default213 / 181

  • barStartPositions[number, number][]

    Where each bar starts, as `[x, y]` centers

    Default4 scattered points

  • barRestPositions[number, number][]

    Where each bar lands before the melt

    Default4 points in a row

  • barEntryFramesnumber[]

    Per-bar entry frames — deliberately non-monotonic so the build reads as Tetris, not a wave

    Default[8, 0, 4, 13]

  • barTravelFramesnumber

    Length of one bar's L-shaped move: horizontal into the column, then vertical into the row

    Default30

  • morphStartFrame / morphPeakFrame / morphEndFramenumber

    The melt window. Blur and displacement are zero outside it

    Default50 / 63 / 76

  • blurRadiusnumber

    Peak goo blur radius

    Default6

  • blurIterationsnumber

    Box blur passes folded into the equivalent Gaussian sigma

    Default6

  • alphaInputBlack / alphaInputWhitenumber

    Alpha threshold range — the narrower, the harder the goo edge

    Default0.4588 / 0.5098

  • displaceAmountnumber

    Peak turbulent displacement of the goo edge, in pixels

    Default20

  • displaceSizenumber

    Feature size of the displacement noise

    Default19

  • displaceComplexitynumber

    Noise octaves

    Default1

  • displaceEvolutionPerSecondnumber

    How fast the noise field pans, per second

    Default50

  • classNamestring

    Optional className passed to the wrapper

Inspired by 5 Clean Text Animations by Mapal.