Ymotion
Help
Dashboard
All pages

Guides

Shapes, icons, masks and mattes

Shape layers and their trees, icons, solids and gradients, masks and track mattes, with recipes for bars, highlights and vignettes.

This page covers the vector side of Ymotion: shape layers you draw from numbers, the icon catalogue, solids, and the two ways of cutting one layer to a shape — masks, which are shapes you describe, and track mattes, which borrow another layer's picture. It ends with a few recipes built from them.

Shape layers

A shape layer holds one shape, drawn from the centre of the frame. Give its kind, size and paint when you add it:

ymotion_edit {"ops":[{"verb":"addLayer","args":{"trackId":"<track id>","content":{"kind":"shape","shape":{"kind":"rect","width":0.415,"height":0.08,"radius":0.25,"fill":"#FFD24A"}},"start":0,"duration":4,"name":"Plate"}}]}

The kinds are rect, ellipse, line, triangle, polygon, star, arrow and path, a drawn outline. The fields you'll use most:

  • width and height, as fractions of the canvas width and height.
  • radius rounds a rectangle's corners, from 0 (square) to 1 (a pill). A new simple shape starts at 0.3, so pass 0 for sharp corners.
  • points and inset for a polygon or a star.
  • fill and stroke, both paints, and strokeWidth as a fraction of the short edge. A newly requested simple shape has a white stroke at width 0, so raising the width reveals it. An explicit transparent stroke stays transparent, and old saved shapes retain their old missing-paint fallback. The stroke also takes join, cap, miterLimit, dash and dashOffset.
  • trim (start, end, offset, each a fraction of the outline's length) and repeat (copies plus a step of x, y, rotation and scale, applied once more per copy).

A line is a little different: width is its length, it's painted with fill, and its thickness is strokeWidth measured against the canvas short edge. Its trim shortens it from the ends, which makes it a good progress bar.

Move a shape with the layer's transform, and change its own fields with setShape and a patch. Every number keys at a path such as content.shape.width or content.shape.trim.end.

Width and height are two axes

Because width follows the canvas width and height the canvas height, equal numbers draw a square only on a square frame. On a 1080x1920 canvas a square 0.2 wide is 0.1125 tall, and on a 1920x1080 canvas a circle 0.2 wide is 0.3556 tall: multiply the width by the canvas width over its height. Leaving one out gives you the default for that axis, so give both.

A gradient fill on a shape layer is laid across a frame-sized box that travels with the layer, not across the shape itself, so a thin plate shows only a sliver from the middle of the gradient and looks nearly flat. For a gradient that runs end to end across a shape, draw the shape at full frame (width and height 1, radius 0) and size it with the layer's transform.scaleX and scaleY, which scale the gradient with it; or use a full-frame gradient solid cut by a mask or a track matte.

Shape trees

For anything with more than one part, a shape layer can hold a tree: items, a list of paths, paints and modifiers, with groups inside it. Two rules decide the picture:

  1. The list runs from the top. A path adds itself to what has gathered so far, a fill or stroke paints everything gathered so far, and a modifier changes what has gathered. So a trim above a stroke trims it, and the same trim below the stroke does nothing to it.
  2. A group keeps its paths to itself, and draws with its own transform. A fill placed after a group paints nothing of it, so put the paint inside the group.

The modifiers follow After Effects' list: trim, repeater, round (corner radius), offset, zigzag, pucker, twist, wiggle and merge, whose mode is add, subtract, intersect or exclude. A few do less than After Effects' do: pucker scales each outline rather than bending its edges, zigzag and wiggle follow the straight line between points, so an ellipse turns into a diamond, offset moves the points but not their handles, and intersect fills wherever two or more outlines overlap. ymotion_help {"topic":"types","name":"ShapeItem"} gives every field.

Here's a progress ring: a circle, a trim and a stroke, in that order, inside a group. The circle is sized for a 1080x1920 frame, where 0.5 of the width is 0.2813 of the height.

ymotion_edit {"label":"ring","ops":[{"verb":"addLayer","args":{"trackId":"<track id>","content":{"kind":"shape","shape":{"items":[{"kind":"group","name":"Ring","items":[{"kind":"path","shape":{"kind":"ellipse","width":0.5,"height":0.2813}},{"kind":"trim","start":0,"end":0,"offset":-0.25},{"kind":"stroke","paint":"#FFD24A","width":0.03,"cap":"round"}]}]}},"start":0,"duration":4,"name":"Progress ring"}}]}

An ellipse's outline starts at three o'clock and runs clockwise, so the offset of −0.25 starts the ring at twelve. Items are addressed by index, so the trim's end is content.shape.items.0.items.1.end:

ymotion_edit {"ops":[{"verb":"keyRamp","args":{"layerId":"<layer id>","path":"content.shape.items.0.items.1.end","from":0,"to":1,"opts":{"start":0,"end":3,"ease":"easyEase"}}}]}

To edit a tree, addShapeItem, setShapeItem, moveShapeItem and removeShapeItem each take a trail: the indices of the groups to walk into, [] for the top level and [0] for inside the first group. Keys follow their item when items move or are removed. Removing the last top-level item leaves an empty tree; setShape with items: null explicitly returns it to its stored single primitive. A tree's line is an open centreline painted with a stroke, whose cap, dashes and trim work along its length.

A repeater copies a group to one side of where it starts, so a row of five dots sits off-centre. alignLayers with {"layerIds":["<layer id>"],"opts":{"horizontal":"centre","vertical":"middle"}} centres what the layer actually draws.

Icons

There are 226 icons, drawn for Ymotion, that arrive as ordinary shape layers. Search with ymotion_find {"catalogue":"icons","text":"heart"}, or narrow by group, by style (filled or stroked), or with "marks":false to leave out other companies' trademarks. The catalogue carries no colours; you choose the paint.

ymotion_edit {"ops":[{"verb":"placeIcon","args":{"trackId":"<track id>","icon":"heart","paint":"#FF3B30","start":0,"duration":3,"place":{"size":0.2,"y":-0.1}}}]}

place sets size (a fraction of the short edge, 0.12 by default), x and y (also fractions of the short edge, from the centre, so they differ from a layer's position, which follows the canvas width and height), rotation, opacity, weight for line art (0 keeps the drawn weight), parts (1 gives each part its own group, so parts can be coloured apart) and copies. The reply's rig names the paths for the icon's transform, outline and paint, ready to key.

setIcon swaps the drawing and keeps the layer's keyframes, but it rebuilds the drawing from the place you give it, so pass the size and position again. Positioning icons with the layer's own transform (setTransform) avoids that, since a swap leaves the transform alone.

Solids and gradients

A solid fills the whole frame with one paint: a background, a colour card, or a scrim.

ymotion_edit {"ops":[{"verb":"addLayer","args":{"trackId":"<track id>","content":{"kind":"solid","fill":"grad:#12203A,#3A1230"},"start":0,"duration":4,"name":"Ground"}}]}

A gradient runs top to bottom unless you give an angle: |0 runs left to right. Units, paints and time lists every paint form.

Masks

A mask decides which part of a layer shows. Give addMask a kind and the parts you care about; the rest take their defaults (centred, half the frame each way, no feather, not inverted), and a mask with no kind is none, which cuts nothing:

ymotion_edit {"ops":[{"verb":"addMask","args":{"layerId":"<layer id>","mask":{"kind":"ellipse","width":1.1,"height":1.1,"feather":0.35,"invert":true}}}]}
  • kind: rect (with roundness), circle, ellipse, star, heart, path (a drawn outline), linear (everything above a line; turn it with rotation) and mirror (a band).
  • x and y place the mask's centre as fractions of the canvas from its centre; width and height are fractions of the canvas width and height. A circle uses the shorter of the two, so it stays round on any frame.
  • feather is the width of the soft edge and expand grows or shrinks the shape first, both as fractions of the short edge. invert keeps everything outside.
  • mode combines masks in order: add, subtract, intersect or difference. A first mask set to subtract starts from the whole layer, so it cuts a hole.
  • space is frame by default, fixed to the canvas, which suits vignettes and reveals. With layer the mask is measured on the layer's own box and travels with its transform.

Change a mask with editMask, which takes an index and a patch; moveMask reorders and removeMask removes. Its numbers key at masks.0.x, masks.0.feather and so on. A drawn outline is keyed with keyOutline, whose vertices are fractions of the mask's box from its centre. The mask has to be of kind path for its outline keys to draw: on a rect or an ellipse the keys are stored and the shape stays as it was. Here mask 0 is a path mask with width and height set:

ymotion_edit {"ops":[{"verb":"keyOutline","args":{"layerId":"<layer id>","path":"masks.0.path","localT":0,"outline":{"closed":true,"vertices":[{"x":0,"y":-0.5},{"x":0.5,"y":0.5},{"x":-0.5,"y":0.5}]}}}]}

Track mattes

A track matte cuts a layer to another layer's picture: its alpha, or with "kind":"luma" its brightness. Use it when the shape already exists as a layer — type filled with footage or a gradient, a logo-shaped reveal. The matte layer isn't drawn as itself while the layer it cuts is drawn; outside that span it shows as usual, unless its track is hidden.

ymotion_edit {"ops":[{"verb":"setMatte","args":{"layerId":"<footage layer id>","matteLayerId":"<text layer id>","kind":"alpha"}}]}

"invert":true keeps what the matte doesn't cover, and clearMatte draws the layer whole again. A real matte source has to span the moment being drawn: outside its span, the layer it cuts isn't drawn, so give the two the same span. Hiding or disabling the source does not stop it cutting. An id that is not a layer of the same timeline is refused. Deleting the source or its track clears the matte, so the cut layer draws whole; an old document with a missing source is repaired the same way when opened.

Recipes

A progress bar. Two line layers on separate tracks, a dim one for the track and a bright one whose trim end is keyed from 0 to 1 over the film:

ymotion_edit {"ops":[{"verb":"addLayer","args":{"trackId":"<track id>","content":{"kind":"shape","shape":{"kind":"line","width":0.8,"strokeWidth":0.006,"fill":"#FFD24A","trim":{"start":0,"end":0,"offset":0}}},"start":0,"duration":5,"name":"Bar fill"}}]}

Then keyRamp on content.shape.trim.end from 0 to 1 with the linear ease.

A highlight box behind a word. Read the type's box with ymotion_read {"what":"box","layerId":"<layer id>"}, then add a rectangle on a track below it, its width and height the box's onCanvas fractions plus some padding, centred where the box is centred (its transform.x is the box's left plus half its width, minus 0.5, and transform.y likewise). Parent it to the text with setParent and {"keep":true} so they move together, and key content.shape.width from 0 for a box that grows out from the middle. A width of 0 has no geometry or stroke, so the box can grow from nothing.

A vignette. A black solid on the top track with an inverted, feathered ellipse mask, as in the addMask example above, at about 60% opacity. The vignette filter does a similar job inside one layer.

Type that rises out of a line. A linear mask in frame space keeps everything above its line, and the line sits a little under where the letters come to rest, while the layer's transform.y is keyed up into place. The mask stays put as the type moves, so the letters appear from nothing. Here the type rests at the centre and the line is 0.08 of the frame's height below it; read the type's box with ymotion_read {"what":"box","layerId":"<layer id>"} to put the line just under its bottom edge.

ymotion_edit {"label":"rise from a line","ops":[{"verb":"addMask","args":{"layerId":"<layer id>","mask":{"kind":"linear","y":0.08}}},{"verb":"keyRamp","args":{"layerId":"<layer id>","path":"transform.y","from":0.2,"to":0,"opts":{"start":0,"end":0.6,"ease":"expoOut"}}}]}

Good to know

  • A stroke's width is measured against the frame, not the shape, so widening a shape keeps its outline the same thickness. Scale the layer to grow both.
  • alignLayers and the other verbs that change nothing are refused with the reason, rather than silently succeeding. If a layer is already centred, there's nothing to do.
  • Render a frame after building anything with more than a couple of parts. ymotion_render {"what":"frame","t":1} shows the order of a tree at a glance.

See also