# Scroll Morph Mask Video

> A looping video that never stops playing, seen through a vector mask that scroll morphs from a mark into the letterforms of a word, then zooms through the X to full bleed. Flick and the mask breathes: it inflates, the edge splits cyan and pink, and the footage speeds up.

Canonical: https://gsapvault.com/effects/scroll-morph-mask-video
Live demo: https://gsapvault.com/demos/scroll-morph-mask-video/index.html

| Property | Value |
|----------|-------|
| Type | effect |
| Tier | paid |
| Price | £5 |
| Difficulty | advanced |
| Plugins | ScrollTrigger, MorphSVG |
| Techniques | video-mask, morph-svg, clip-path, pinning, scrub, velocity-reactive, elastic |
| Uses Lenis | Yes |

## Overview

A video hero where the film keeps playing and scroll changes only the shape you see it through. One real <video> element loops underneath a vector clip-path, and MorphSVG morphs that path along a sequence you author: a small mark, the letterforms of your word, then a zoom straight through one letter until its solid stroke fills the screen and hands off to your headline and call to action. Every frame of that exit is still the real letterform, just closer.

The mask is alive rather than mechanical. At rest it breathes gently. Flick the wheel and it inflates and tilts with your scroll velocity, the edge splits into a cyan and pink fringe, a displacement ripple runs along the cut, and the footage itself speeds up, before the whole thing settles back with an elastic overshoot.

Because the video plays rather than scrubs, it needs no special encoding: any muted, looping MP4 works, and the mask shapes are ordinary SVG paths you can export from Figma or Illustrator.

## Features

- Real <video> playing continuously under a morphing SVG clip-path (no canvas, no frame sequence, no scrubbed encode)
- MorphSVG morphs through any number of authored shapes in order, then zooms through a point you choose on the last shape to exact full bleed
- Velocity-reactive inflate and tilt with an elastic settle, plus a gentle idle breath so the mask is never static
- Chromatic edge split: cyan and pink echoes clipped by the same mask slide apart with scroll speed
- Footage playbackRate rises with velocity (capped via data attribute) and eases back to real time
- Desktop-only SVG displacement ripple along the mask edge, removed at rest so idle frames cost nothing
- Design-space fitting: shapes authored in one viewBox are centred and scaled to any stage without distortion
- Touch-friendly: the same morph runs on a shorter pin; reduced motion gets a designed still over the poster frame
- Replay button scrolls back to the start of the journey through Lenis when present

## Use Cases

- Brand and campaign heroes where a looping film should feel alive rather than scrubbed
- Studio and agency intros that reveal the name in the footage before the page opens up
- Product launches that want an Awwwards-grade scroll moment from one MP4 and one word
- Film, music and event sites with strong motion footage worth showing through type
- Portfolio landings where the mark morphs into the name and then into the work

## Vibe-Code Ready Setup

This effect includes `START-HERE-AI.md`, a product-specific copy-paste setup prompt for Cursor, Claude Code, ChatGPT, GitHub Copilot, Windsurf, and other coding assistants. It tells the assistant to inspect the existing stack, integrate the supplied files, preserve the design, scope selectors, retain accessibility and responsive behaviour, add framework-appropriate GSAP cleanup, and report what it tested.

[How AI-assisted setup works](https://gsapvault.com/vibe-coding)

## How It Works

The stage holds three full-size layers, two solid colour echoes and the film, all pointing at one SVG <clipPath> in userSpaceOnUse units. The shapes you author live in a hidden SVG with a design viewBox; on every ScrollTrigger refresh the script measures the stage, computes a contain-fit transform, and writes it onto the clip path together with the live inflate, breath and tilt values, so the word is never stretched and the animation needs no per-breakpoint paths.

A pinned, scrubbed timeline morphs the clip path with MorphSVG from shape to shape using fromTo tweens (so invalidateOnRefresh never re-records a mid-morph start), then zooms the clip path about a point on the last shape (data-mask-zoom) in log space, with the required scale computed on refresh from the farthest stage corner, so the solid stroke covers the stage exactly at the end of the tween. The same timeline pushes the video's scale out slowly, fades the grade layer, and reveals the content.

Scroll velocity from getVelocity() feeds one decaying smear value on the GSAP ticker. That value drives the echo offsets through quickSetter, the video's playbackRate, the feDisplacementMap scale (applied to the layer wrapper only while there is velocity), and a tilt on the clip transform; a hard flick also kicks an inflate value that an elastic tween brings home. Every live channel is multiplied by (1 - bleed) so the full-bleed frame never shows a gap.

gsap.matchMedia splits desktop, touch and reduced motion. Reduced motion sets the word shape on the poster frame with no pin and the content visible; cleanup removes the ticker, the refreshInit listener, the replay handler and the ScrollTrigger, and restores the video's playback rate.

## Integration Preview

How this effect integrates into a page. The full documentation (examples, events, programmatic API, customization guide) ships with the download.

### Quick Start

**1. Add to your HTML `<head>`:**

```html
<link rel="stylesheet" href="path/to/style.css">
```

**2. Add before closing `</body>` tag:**

```html
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/ScrollTrigger.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/MorphSVGPlugin.min.js"></script>
<!-- Optional: smooth scroll. The scrub reads softer with it. -->
<script src="https://unpkg.com/lenis@1.3.17/dist/lenis.min.js"></script>
<script src="path/to/script.js"></script>
```

**3. Add the section to your `<body>`:**

```html
<section class="mask-video" data-mask-video>
  <div class="mask-video__stage" data-mask-stage>
    <div class="mask-video__grade" data-mask-grade aria-hidden="true"></div>

    <div class="mask-video__layers" data-mask-layers>
      <div class="mask-video__echo mask-video__echo--a" data-mask-echo aria-hidden="true"></div>
      <div class="mask-video__echo mask-video__echo--b" data-mask-echo aria-hidden="true"></div>
      <div class="mask-video__film" data-mask-film>
        <video muted playsinline loop preload="auto" poster="poster.jpg" aria-hidden="true">
          <source src="loop.webm" type="video/webm">
          <source src="loop.mp4" type="video/mp4">
        </video>
      </div>
    </div>

    <div class="mask-video__scrim" data-mask-scrim aria-hidden="true"></div>

    <svg class="mask-video__defs" width="0" height="0" aria-hidden="true" focusable="false">
      <defs>
        <clipPath clipPathUnits="userSpaceOnUse"><path d=""></path></clipPath>
        <filter x="-10%" y="-10%" width="120%" height="120%" color-interpolation-filters="sRGB">
          <feTurbulence type="fractalNoise" baseFrequency="0.011 0.028" numOctaves="2" seed="7" result="noise"></feTurbulence>
          <feDisplacementMap in="SourceGraphic" in2="noise" scale="0" xChannelSelector="R" yChannelSelector="G"></feDisplacementMap>
        </filter>
      </defs>
    </svg>

    <!-- Mask states, in order. The script morphs through them, then zooms through the last one (data-mask-zoom="x y r"). -->
    <svg class="mask-video__shapes" viewBox="0 0 1200 675" hidden aria-hidden="true" focusable="false">
      <path data-mask-shape="mark" d="M600 265.5C639.8 265.5 672 297.7 672 337.5 ... Z"></path>
      <path data-mask-shape="word" data-mask-zoom="976 338.6 28" d="M329.1 265.9L329.1 309.1 ... Z"></path>
    </svg>

    <div class="mask-video__content">
      <h1 class="mask-video__title" data-mask-reveal>Footage that never<br>stops moving.</h1>
      <p class="mask-video__text" data-mask-reveal>Your standfirst.</p>
      <a class="mask-video__replay" href="/work" data-mask-reveal>See the work</a>
    </div>
  </div>
</section>
```

Copy the two `d` attributes from the demo's `index.html` to start with, then replace the word with your own (see below).

### Options

All attributes go on the `[data-mask-video]` section.

| Attribute | Values | Default | Description |
|-----------|--------|---------|-------------|
| `data-mask-length` | Number | `2.6` | Pin distance as a multiple of the viewport height. Lower is a quicker journey. |
| `data-mask-scrub` | Number (seconds) | `1` | ScrollTrigger scrub smoothing. The lag is part of the feel; `0` locks it 1:1. |
| `data-mask-velocity` | Number | `1` | Strength of every flick reaction (inflate, tilt, echo split, playback rate, ripple). `0` disables them all. |
| `data-mask-max-rate` | Number | `3` | Fastest `playbackRate` the footage reaches on a hard flick. |
| `data-mask-ripple` | `true`, `false` | `true` | Edge displacement ripple on flicks. Desktop only; it is removed at rest so idle frames cost nothing. |
| `data-mask-playback` | `play`, `scrub` | `play` | `play` loops the film on its own clock and scroll only moves the mask. `scrub` makes scroll position the playhead (`currentTime` follows progress), so the film is frozen when you stop; touch devices always play. Add `?film=play` or `?film=scrub` to the URL to compare without editing. |

### Accessibility

- **Reduced motion**: no pin, no ticker, no morph. The word shape is set once over the poster frame, positioned in the upper half so the content below it is fully readable, and the scrim and content are shown immediately. The video is never started.
- **No JavaScript**: the grade, echoes and hint are hidden, nothing is clipped, and the film fills the stage under the visible content.
- **Keyboard**: the only control is a real `<button>` (or `<a>` in your page) with `:focus-visible` styling matching `:hover`.
- **Screen readers**: the video, echoes and decorative layers are `aria-hidden`; the section carries an `aria-label`.

### Dependencies

**Required:**
- GSAP 3.13+ (MorphSVGPlugin is free as of 3.13)
- ScrollTrigger plugin
- MorphSVGPlugin

**Optional:**
- Lenis (smooth scroll; opt out with `data-smooth="off"` on `<html>` or `?smooth=off`)

## What You Get

- `index.html`: working demo page
- `assets/script.js`: commented, readable source
- `assets/style.css`: effect styles
- `README.md`: full documentation with examples and framework integration notes
- `START-HERE-AI.md`: product-specific copy-paste prompt for AI-assisted setup
- `LICENSE.txt`: standard license terms
- Lifetime updates: re-download anytime from your library

## Get the Code

This is a premium effect. The standard license costs £5 one-time and covers unlimited personal and commercial projects with no attribution required. The only restrictions: no redistribution of the code itself and no competing effect libraries.

- [Buy Scroll Morph Mask Video](https://gsapvault.com/effects/scroll-morph-mask-video)
- [Effects & Templates Vault (£39 one-time, best value): every current and future effect and template](https://gsapvault.com/effects)

## Judge the Code Quality First

These related effects are free with complete source published, written to the same production standard (cleanup functions, reduced-motion support, framework-agnostic):

- [Scroll Progress Indicator](https://gsapvault.com/effects/scroll-progress.md): A precise GSAP reading-progress instrument with bar, ring, side rail, percentage, and active chapter feedback.
- [CSS Scroll Reveal](https://gsapvault.com/effects/css-scroll-reveal.md): Native CSS scroll-driven reveals for crisp fade, slide, and scale entrances with accessible static fallbacks and no animation JavaScript.
- [Parallax Hero](https://gsapvault.com/effects/parallax-hero.md): A pinned hero that separates its background photograph, copy, and foreground card into distinct scroll depths from a single scrubbed ScrollTrigger.

---

From [GSAP Vault](https://gsapvault.com): production-ready GSAP animation effects. Full catalog for agents: https://gsapvault.com/llms-full.txt
