Image

Renders an image file. Layout, padding, and child positioning are inherited from Rect: an Image lays out its children exactly like a Rect does, with a decoded image painted in place of the fill.

0.0s / 4.0s

Usage

import { createScene, Image } from 'motion-script';

export default createScene(function* (stage) {
  stage.add(
    <Image
      src="./assets/photo.jpg"
      fit="fill"
      width={800}
      height={450}
      cornerRadius={16}
    />
  );
});

Props

PropTypeDefaultDescription
srcstringPath to the image file
fit'fill' | 'fit' | 'tile' | 'stretch''fill'How the image fills the node's bounds
cropInsets0Window onto the source, in fractions of its own size, applied before fit
zoomnumber1Magnification on top of the fitted scale. 2 is twice the fitted size
anchorAnchor'center'The point held fixed as zoom scales, and the alignment when the image doesn't cover
matrixImageMatrixRaw image→shape matrix; bypasses crop/fit/zoom/anchor and the bounds
filtersMediaFilter[] | FilterChain[]Image filters (blur, color adjustments, etc.)
cornerRadiusnumber | CornerRadiusProps0Corner radius (clips the image)
cornerStyle'rounded' | 'angled' | CornerStyleProps'rounded'Corner shape

Fit modes

ValueBehaviour
'fill'Scales the image to cover the bounds, preserving aspect ratio (cropped). The default, Figma-style.
'fit'Scales the image to fit within the bounds, preserving aspect ratio (letterboxed)
'tile'Tiles the image at its natural size (× zoom)
'stretch'Stretches each axis independently to exactly fill the bounds, distorting aspect ratio

Crop, zoom and anchor

The four placement props form one pipeline: cropfitzoomanchor.

crop takes a window of the source first, and everything after it treats that window as if it were the whole image — so a crop composes with the fit modes rather than fighting them. It is expressed in fractions (0–1) of the source's own size, not pixels, so the same value reads identically at 1× and 4K and survives swapping src for a differently-sized asset. It accepts the same shorthands as padding: 0.1, { horizontal: 0.2 }, { left: 0.1, top: 0.05 }.

zoom multiplies whatever scale fit resolved, and anchor decides which point of the source lands on the same point of the box — which makes it both the focal point a zoom grows away from and the alignment when the image doesn't cover the bounds. It lives in the same [-1, 1] y-up space as pivot and align, so 'topLeft', 'centerRight' and { x, y } all work.

// Punch into the left third of a portrait, keeping that point fixed.
<Image src="./portrait.jpg" width={1200} height={675} zoom={1.6} anchor={{ x: -0.35, y: 0 }} />

// Trim 30% off each side to make a vertical hero from a wide shot.
<Image src="./landscape.jpg" width={540} height={960} crop={{ horizontal: 0.3 }} />

All four animate. A Ken Burns push is zoom plus anchor:

yield* photo().to({ zoom: 1.6 }, 3, easeInOut);
yield* photo().to({ anchor: 'centerRight', zoom: 1.9 }, 2.5, easeInOut);

Two things 'tile' does not honour: crop (the shader repeats the whole texture, so there is no way to tile a sub-rect) and anchor (a repeating texture has no corner to pin). Under 'tile' the fitted scale is native size, so zoom there is the tile-size multiplier.

Animating

Animate standard node props like opacity, scale, cornerRadius, or x/y. For image filters, animate inside a tween:

import { createScene, Image, ImageFilters, createRef, tween, lerpNumber, easeOut } from 'motion-script';

export default createScene(function* (stage) {
  const photo = createRef<Image>();

  stage.add(
    <Image
      ref={photo}
      src="./assets/photo.jpg"
      fit="fill"
      width={640}
      height={360}
      cornerRadius={0}
      opacity={0}
    />
  );

  // Fade in
  yield* photo().to({ opacity: 1 }, 0.8, easeOut);

  // Round corners
  yield* photo().to({ cornerRadius: 24 }, 0.6, easeOut);

  // Blur out with a filter
  yield* tween(0.8, (t) => {
    photo().set({ filters: ImageFilters.blur(lerpNumber(0, 20, easeOut(t))) });
  });
});

Notes

  • Image extends Rect, so it also supports flow, gap, align, and padding for laying out children on top of the image.
  • Placing children inside an Image is useful for overlaying text or icons on top of a photo.
  • Supported formats depend on the renderer; JPEG, PNG, and WebP are universally supported.
  • See Image ImageFilters for the full list of available filters.