Skip to main content

Masks

MaskGroup clips a set of nodes using a shape as a mask. The first child is always the mask shape; all subsequent children are the clipped content.

Basic mask

import { Scene, MaskGroup, Ellipse, Rect, wait } from '@motion-script/core';

export class MyScene extends Scene {
*build() {
this.add(
<MaskGroup mode="vector">
{/* First child = mask (circle) */}
<Ellipse width={300} height={300} fill="white" />

{/* Remaining children = clipped content */}
<Rect
width={400} height={400}
fill={{ type: 'image', src: './photo.jpg', mode: 'fill' }}
/>
</MaskGroup>
);

yield* wait(2);
}
}

Mask modes

ModeDescription
'vector'Clips based on the mask shape's outline — fill color is ignored
'alpha'Clips based on the mask shape's alpha channel (transparency)
'luminance'Clips based on the mask shape's brightness

'vector' is the most common: any filled shape works regardless of its color.

'alpha' is useful for gradient masks — areas with lower opacity reveal less content.

Alpha mask with gradient

<MaskGroup mode="alpha">
{/* Gradient mask: fully visible on left, fades out on right */}
<Rect
width={600} height={400}
fill={{
type: 'linear-gradient',
colors: ['black', 'transparent'],
stops: [0.5, 1],
}}
/>
<Rect
width={600} height={400}
fill={{ type: 'image', src: './photo.jpg', mode: 'fill' }}
/>
</MaskGroup>

Inverted mask

Set inverted: true to flip the mask — content shows outside the mask shape:

<MaskGroup mode="vector" inverted={true}>
<Ellipse width={200} height={200} fill="white" />
<Rect width="fill" height="fill" fill={{ type: 'image', src: './photo.jpg', mode: 'fill' }} />
</MaskGroup>

Animating the mask shape

The mask is a regular node. Animate it with .to() to create wipe or reveal effects:

import { Scene, MaskGroup, Ellipse, Rect, createRef, easeInOut } from '@motion-script/core';

export class MyScene extends Scene {
*build() {
const maskCircle = createRef<Ellipse>();

this.add(
<MaskGroup mode="vector">
<Ellipse ref={maskCircle} width={0} height={0} fill="white" />
<Rect
width={1280} height={720}
fill={{ type: 'image', src: './photo.jpg', mode: 'fill' }}
/>
</MaskGroup>
);

// Iris wipe reveal
yield* maskCircle().to({ width: 1500, height: 1500 }, 1.2, easeInOut);
}
}

Text as a mask

Any node type — including Text — works as the mask shape:

import { Scene, MaskGroup, Text, Rect } from '@motion-script/core';

export class MyScene extends Scene {
*build() {
this.add(
<MaskGroup mode="alpha">
<Text
text="MOTION"
fontSize={140}
fontWeight={900}
fill="white"
align={0}
/>
<Rect
width="fill" height="fill"
fill={{ type: 'linear-gradient', colors: ['#4f80ff', '#e84393'], stops: [0, 1] }}
/>
</MaskGroup>
);
}
}

MaskGroup props

PropTypeDefaultDescription
mode'vector' | 'alpha' | 'luminance'How the mask shape is interpreted
invertedbooleanfalseInvert the mask (show content outside the shape)