Skip to content

Generators

A generator is a picture the engine draws from a description, with no media to decode: a color or gradient, a shape layer, a text, or a layout. This page covers the first three; Layout covers the fourth. After reading it you can make backgrounds, motion graphics and titles from JSON alone.

A Generator appears in three places:

  • as the generator of a source with "kind": "generated", which clips show like any other source;
  • as a composition’s generator node;
  • through a composition’s media node that names a generated source.

A generated picture is the size of the canvas. Positions are fractions, (0, 0) top-left to (1, 1) bottom-right, and lengths are fractions of the canvas’s shorter side, so the picture is the same at any render resolution.

A fill never changes over time. Text changes only through its animators. Shapes and layouts may animate any value, keyed in picture time: the source time the clip selects (see Animation).

{ "type": "fill", "paint": ... } covers the picture with a Paint:

Paint Fields
solid color
linear_gradient start, end, stops, spread (pad, repeat or reflect)
radial_gradient center, radius (a fraction of the shorter side), stops, spread
conic_gradient center, angle_degrees (where offset 0 lies, clockwise from straight up), stops

A gradient has 2 to 8 stops, each an offset in 0..1 and a color. Gradients interpolate premultiplied stops in linear light, so a gradient between two colors passes through their physical mix, and a stop fading to transparent does not darken its neighbour. Wherever a paint is read, a bare color is read as a solid paint.

This project shows the four paints one after another, a second each:

[
{
"id": "solid",
"kind": "generated",
"generator": {
"type": "fill",
"paint": { "type": "solid", "color": { "red": 0.17, "green": 0.36, "blue": 0.99, "alpha": 1 } }
}
},
{
"id": "linear",
"kind": "generated",
"generator": {
"type": "fill",
"paint": {
"type": "linear_gradient",
"start": { "x": 0, "y": 0.5 },
"end": { "x": 1, "y": 0.5 },
"stops": [
{ "offset": 0, "color": { "red": 0.98, "green": 0.45, "blue": 0.1, "alpha": 1 } },
{ "offset": 1, "color": { "red": 0.17, "green": 0.36, "blue": 0.99, "alpha": 1 } }
]
}
}
},
{
"id": "radial",
"kind": "generated",
"generator": {
"type": "fill",
"paint": {
"type": "radial_gradient",
"center": { "x": 0.5, "y": 0.5 },
"radius": 0.6,
"stops": [
{ "offset": 0, "color": { "red": 0.98, "green": 0.45, "blue": 0.1, "alpha": 1 } },
{ "offset": 1, "color": { "red": 0.17, "green": 0.36, "blue": 0.99, "alpha": 1 } }
]
}
}
},
{
"id": "conic",
"kind": "generated",
"generator": {
"type": "fill",
"paint": {
"type": "conic_gradient",
"center": { "x": 0.5, "y": 0.5 },
"stops": [
{ "offset": 0, "color": { "red": 0.98, "green": 0.45, "blue": 0.1, "alpha": 1 } },
{ "offset": 0.5, "color": { "red": 0.17, "green": 0.36, "blue": 0.99, "alpha": 1 } },
{ "offset": 1, "color": { "red": 0.98, "green": 0.45, "blue": 0.1, "alpha": 1 } }
]
}
}
}
]

{ "type": "shapes", "items": [...] } draws an ordered content tree, as an After Effects or Lottie shape layer does. Each ShapeItem is one of:

  • Geometry: rectangle (with corner_radius), ellipse, polystar (a polygon or star), path (Bezier subpaths).
  • Paints: fill paints the inside of the geometry listed before it in its group; stroke outlines it, with width, cap, join, miter_limit and dash.
  • Modifiers: trim keeps part of the geometry before it, by length, from start to end, shifted by offset.
  • group: items drawn together through one transform and opacity.

The tree reads front first: a fill or stroke paints the geometry before it in the same group, nested groups included, and earlier paints draw on top of later ones.

Units follow the picture: a center is a position, a size is per axis (x a fraction of the width, y of the height, so { "x": 0.5, "y": 0.5 } is round only on a square canvas), and radii and stroke widths are fractions of the shorter side.

This star draws itself on with a keyframed trim while it turns:

{
"type": "shapes",
"items": [
{
"type": "group",
"items": [
{
"type": "polystar",
"kind": "star",
"center": { "mode": "constant", "value": { "x": 0.5, "y": 0.5 } },
"points": { "mode": "constant", "value": 5 },
"outer_radius": { "mode": "constant", "value": 0.35 },
"inner_radius": { "mode": "constant", "value": 0.15 },
"rotation_degrees": {
"mode": "keyframes",
"keyframes": [
{ "time": 0, "value": 0, "interpolation": { "type": "linear" } },
{ "time": 360000, "value": 36, "interpolation": { "type": "hold" } }
]
}
},
{
"type": "trim",
"start": { "mode": "constant", "value": 0 },
"end": {
"mode": "keyframes",
"keyframes": [
{
"time": 0,
"value": 0,
"interpolation": { "type": "cubic_bezier", "x1": 0.65, "y1": 0, "x2": 0.35, "y2": 1 }
},
{ "time": 240000, "value": 1, "interpolation": { "type": "hold" } }
]
}
},
{
"type": "stroke",
"paint": {
"mode": "constant",
"value": { "type": "solid", "color": { "red": 1, "green": 0.75, "blue": 0.2, "alpha": 1 } }
},
"width": { "mode": "constant", "value": 0.012 },
"join": "round",
"cap": "round"
}
]
}
]
}

The shapes preset moves an ellipse along a keyframed centre:

{ "type": "text", "text": ..., "style": ... } draws text on a transparent picture, laid out and placed by its style as caption cues are. The TextStyle sets:

  • Font: font_family, font_size (a fraction of the canvas’s shorter side, 0.05 by default), font_weight (1 to 1000, matched as CSS matches font-weight), letter_spacing, line_height.
  • Placement: position and anchor (the point of the text placed at position; (0.5, 1), its bottom centre, by default), max_width (the wrapping width, a fraction of the canvas width), align.
  • Look: fill (a paint, white by default), strokes, shadows, background (a box behind each line).
  • Motion: animators.

The text is a string, where \n starts a line, or a list of runs that animators can select by class or time.

An animator changes characters without laying the text out again. Its selector gives each character a selection between 0 and 1, and its properties are applied in proportion: opacity, offset (in font sizes, +y down), scale, rotation_degrees, tracking, fill and stroke colors. There are three selectors:

  • range: a range over characters, words or lines, in percent or by index, with a shape (square, ramp_up, ramp_down, triangle, round, smooth), like After Effects’ range selector.
  • timing: characters of timed runs, by whether their time is upcoming, active or spoken (see karaoke captions).
  • class: characters of runs that carry a class.

Here a range selector’s start moves from 0% to 100% over two seconds. Characters inside the range are fully selected, so they are transparent and lowered by half a font size, and each one rises into place as the range passes it:

{
"type": "text",
"text": "Characters arrive one by one",
"style": {
"font_family": "Hanken Grotesk",
"font_size": 0.09,
"position": { "x": 0.5, "y": 0.5 },
"anchor": { "x": 0.5, "y": 0.5 },
"animators": [
{
"selector": {
"type": "range",
"start": {
"mode": "keyframes",
"keyframes": [
{ "time": 0, "value": 0, "interpolation": { "type": "linear" } },
{ "time": 240000, "value": 100, "interpolation": { "type": "hold" } }
]
}
},
"properties": {
"opacity": { "mode": "constant", "value": 0 },
"offset": { "mode": "constant", "value": { "x": 0, "y": 0.5 } }
}
}
]
}
}

Animator keyframes are in the holder’s time: a text generator’s picture time, or the time since a caption cue started.