Getting started
Units, paints and time
How positions, sizes, type, angles, time and colours are written, and why nothing is in pixels and nothing is random.
Every number you give Ymotion is in a unit chosen so that the picture survives being reframed. This page lists those units, how colours are written, how time is counted, and why a small preview is the same picture as the export.
An edit's check: notes compare an outlying requested value with what the document actually stored. A value can remain outside a slider or be normalised to a legal value; the note says which happened. Type below 1% is also flagged as a possible unit mix-up, without changing a legal size. Segment overrides and restyling several layers receive the same checks.
Why fractions rather than pixels
A number in pixels describes a different picture as soon as the frame changes size or shape, and reframing — say from landscape to vertical — is an ordinary thing to do to a film. So positions and sizes are fractions of the canvas, and one document draws the same picture at any size. The canvas size is the one thing in a document you write in pixels; beyond it, pixels appear only in answers that measure something.
Position
transform.x and transform.y move a layer, measured from the centre of the frame:
xis a fraction of the canvas width,ya fraction of the canvas height.0, 0is the centre, and positiveyis down, as in After Effects.- With the default anchor,
x: 0.5puts the layer's middle on the right edge,x: -0.5on the left edge andy: -0.5on the top edge.
A layer can sit outside the frame (the declared range is −2 to 2; a value past it is kept, up to ±8, with a check: line), which is how something enters from off screen. A layer with a parent is placed in its parent's space instead, so its position moves with the parent.
transform.anchorX and transform.anchorY are the point a layer turns and scales about, as fractions of its own box: 0.5, 0.5, the default, is its middle, and 0, 0 its top-left corner. The centerAnchor verb puts the anchor in the middle of what a layer actually draws without moving the picture.
Sizes and lengths
A shape's width and height are fractions of the canvas width and the canvas height respectively. Because those are two different axes, equal numbers draw a square only on a square frame. On a 1080x1920 canvas, width: 0.2, height: 0.2 is 216 x 384 pixels; a 216-pixel square is width: 0.2, height: 0.1125, since 0.2 × 1080 ÷ 1920 = 0.1125:
ymotion_edit {"ops":[{"verb":"setShape","args":{"layerId":"<layer id>","patch":{"width":0.2,"height":0.1125}}}]}
Thicknesses and softness — a shape's stroke width and dashes, a mask's feather and its expansion — are fractions of the canvas short edge, so they look the same in any aspect ratio. A stroke of 0.01 is about 11 pixels on a 1080-wide vertical film and the same 11 on a 1920x1080 one. A single-primitive line uses strokeWidth for its thickness in these same short-edge units; a line inside a tree takes the actual stroke item's width. Distances inside filters and effects follow the short-edge idea; ymotion_find {"catalogue":"filters","id":"lensBlur"} shows any one knob's range and default.
Corner rounding belongs to the shape rather than the frame. A rectangle's radius and a mask's roundness run from 0, square corners, to 1, the shorter side rounded all the way into a pill or a circle, whatever the shape's size. The round modifier inside a shape tree is different: its radius is a fraction of the short edge, like a stroke.
Scale is a multiplier: 1 is 100% and 0.5 is half size. transform.scaleX and transform.scaleY multiply on top of transform.scale to stretch one axis.
Type
Type size is a percentage of the canvas short edge — the one size in Ymotion written as a percentage rather than a fraction. The default is 5 and the range is 0.5 to 120; the top of that range is After Effects' largest type, 1296 pixels, on a 1080-pixel short edge. The size is the font size — the em, as in After Effects' Character panel — so capitals stand at about 0.7 of it, depending on the face: size 10 on a 1080 short edge is 108-pixel type with capitals about 78 pixels tall. Good starting points are around 6 to 8 for captions and 10 to 14 for a heading.
It's easy to write 0.09 meaning 9%, since nearly everything else is a fraction. That is kept as written and draws type about a pixel tall: it renders without an error and looks like an empty frame. When a value falls outside its declared range, the ymotion_edit reply adds a check: line naming the property and its range — for 0.09, that you probably meant 9 — so it's worth reading the whole reply.
Measurements tied to the type are multiples of the type size, so they scale with it:
tracking(letter-spacing):0.05adds 5% of the size between letters.lineHeight: the default1.2is the usual typographic spacing.baseline: a shift up (positive) or down, as for a superscript.- a text stroke's
width, which is how far it reaches out past each letter's edge, and a text shadow'sblur,xandy.
ymotion_edit {"ops":[{"verb":"setTextStyle","args":{"layerId":"<layer id>","patch":{"size":12,"tracking":0.05,"lineHeight":1.1}}}]}
A text layer's maxWidth, where paragraph text wraps, is a fraction of the canvas width: 1 is the whole width, which is where a new text layer starts.
Opacity and angles
opacityruns from 0 to 1. A paint can also carry its own alpha (below), and both apply.transform.rotationand the skews are in degrees; rotation is clockwise. Rotation's declared range is ±1440, four turns either way; a larger angle is kept, up to ±36000, with acheck:line.
Time
All times are seconds; nothing is counted in frames. There are three clocks, and most timing mistakes come from mixing them up.
Timeline seconds place things in the film: a layer's start and duration, t and times on ymotion_render, t on ymotion_read {"what":"scene","t":2}, and an export's from and to.
Layer seconds are measured from the layer's own start. Keyframe times are in layer seconds: a layer that starts at 2 seconds and fades in over its first half-second has keys at 0 and 0.5, not at 2 and 2.5, which is what lets you slide a layer along the timeline without its animation drifting. t on ymotion_read {"what":"value","layerId":"<layer id>","path":"opacity","t":0.25} and on ymotion_read {"what":"box","layerId":"<layer id>","t":0.25} is in layer seconds too.
File seconds belong to footage and sound. A footage layer's trimIn is the point in its file where the layer begins, and a transcript's word times are file seconds. A word spoken at file time F inside a layer placed at S with a trimIn of T, playing forwards at constant positive speed.rate R, is heard on the timeline at S + (F − T) ÷ R, which is S + F − T at normal speed. Leaving out the trim is silent — the captions render, just late by the trim — so take it into account whenever a time comes from a file. Ramps, numeric rate keys, reverse and holds need the source-clock mapping instead of that division; captions with source apply it for you. What an upload says has the details.
Paints
A paint is a string, and anything that takes a colour — a fill, a stroke, a solid, a background — takes any of these forms:
"#FF3B30"— an opaque colour. The three-digit"#F80"works too."#FFFFFF@50"— any paint at 50% opacity; the number after@is a percentage."#FFFFFF80"— CSS's eight digits, the last two being alpha. Four digits,"#FFF8", work the same way."rgb(255,59,48)"and"rgba(0,0,0,0.4)"— CSS's own forms, alpha and all."grad:#12203A,#3A1230"— a linear gradient, top to bottom by default. Give as many stops as you like and an angle in degrees after a bar,"grad:#FF6B5E,#FFD24A,#B69CFF|45", and add@40to the end for the whole gradient at 40%. The angle is the direction it travels, clockwise from left to right, so 90 is top to bottom."grad:#FF6B5E,#FFD24A 0.2,#B69CFF"— a stop can carry its position, a fraction along the gradient; stops without one are spread evenly between their neighbours, as in CSS."radial:#FFD24A,#12203A"— a radial gradient from the middle of the layer out to 0.75 of its longer side. After a bar, its centre and radius as fractions of the layer's box:"radial:#FFD24A,#12203A|0.3,0.4,0.9".
Keyed from one paint to another, colours and evenly spread gradients blend stop by stop, a colour counting as a gradient of one colour. A radial gradient, or one whose stops carry positions, blends only with one of its kind and number of stops; any other pair changes at the half-way point.
A colour that changes over time is keyed with keyPaint rather than keyOn. Numeric writers refuse a colour property, so use the verb that ymotion_read {"what":"properties","layerId":"<layer id>"} names for each row:
ymotion_edit {"ops":[{"verb":"keyPaint","args":{"layerId":"<layer id>","path":"content.text.style.paint","localT":0,"paint":"#FF3B30"}},{"verb":"keyPaint","args":{"layerId":"<layer id>","path":"content.text.style.paint","localT":1,"paint":"#FFFFFF"}}]}
Nothing is random
Two renders of one document at the same moment and size are the same bytes, whether for a preview or for the export. Where motion should look arbitrary — a wiggle, a jitter, a handheld drift — it's computed from a seed and the time, so it's identical on every render:
ymotion_edit {"ops":[{"verb":"wiggleProp","args":{"layerId":"<layer id>","path":"transform.rotation","wiggle":{"freq":2,"amp":3,"seed":1}}}]}
freq is wobbles a second and amp is how far it strays, in the property's own units — degrees here. The wobble runs on the layer's own clock, so two properties of one layer with the same seed and freq wobble in step; different seeds don't.
The document itself can be reproducible too: a document started with ymotion_doc {"action":"new","seed":42} gives the same ids to the same sequence of calls. See Documents and projects.
Why a preview is the same picture
ymotion_render draws at no more than 1080 pixels on the long edge unless you ask for {"full":true}; a document smaller than that is drawn at its own size. It asks the engine for a smaller canvas rather than shrinking a large picture, and because every value is a fraction, that's the same frame with fewer pixels: framing, type sizes and positions all match the export, which is made at the document's own size, or larger, up to 2160 on the short side, when the film draws footage or pictures sharper than its canvas.
Measurements come back in pixels
Questions that measure something answer in pixels, since a measurement is about one canvas at one moment. ymotion_read {"what":"box","layerId":"<layer id>"} — After Effects' sourceRectAtTime — gives a layer's letters, its ink (what its own stroke or shadow adds) and where it lands, in pixels and again as fractions. Set properties from the fractions, or let centerAnchor, alignLayers and distributeLayers do the arithmetic.
A link or a picture
A rendered frame can come back as a link instead of an image:
ymotion_render {"what":"frame","t":1.5,"link":true}
A link costs a line rather than a picture, so it's a cheap way to check your work as you go, and it's something a person can open.
{"link":true}alone is private and lasts about fifteen minutes.{"link":true,"public":true}is a stable link anybody can open, and it doesn't expire.
The same document at the same moment is the same picture, so asking again for a moment you've already linked, before anything has changed, gives back the same stored image under the same id. A public link is turned off by that id, which the reply gives:
ymotion_media {"action":"share","id":"<link id>","public":false}
Items in the library have the same two kinds of link: ymotion_media {"action":"preview","id":"<media id>"} for a private one and ymotion_media {"action":"share","id":"<media id>"} for a public one. Every link the server gives you is on its own host; pass it on exactly as it is rather than building one yourself. Links come from the hosted server; one running on a laptop has nowhere to keep them, so look at the image there.