View raw

group#

A positioned container whose children inherit its transform, opacity, and time window. The fundamental nesting primitive — think SVG <g>, Figma group, or AE pre-comp. Inherits common fields.

interface GroupElement extends BaseElement {
  type: "tok-str">'group';
  elements: Element[];          "tok-cmt">// child subtree (≥ 1)
  clip?: boolean;              "tok-cmt">// clip children to the group box (default false)
  border_radius?: number;      "tok-cmt">// rounds the clipped box (default 0)
  mask?: {
    mode: "tok-str">'alpha' | "tok-str">'alpha-inverted' | "tok-str">'luma' | "tok-str">'luma-inverted';
    elements: Element[];       "tok-cmt">// elements that compose the mask layer
  };
  time_remap?: Keyframe[];     "tok-cmt">// keyframes that warp the subtree clock (values are warped seconds)
}

Fields#

FieldTypeDefaultDescription
elementsElement[]requiredChild elements. Same shape as the Source's top-level elements array.
clipbooleanfalseRender children into a layer the size of the group's box and clip anything outside it (CSS overflow: hidden). Requires explicit width/height.
border_radiusnumber0Corner radius (px) of the clipped group box — rounds the clip box so children are masked to a rounded rectangle (a rounded card clipping its content). Only meaningful with clip: true; clamped to half the smaller box dimension.
mask{ mode, elements }Mask the group with another set of elements. mode is 'alpha' | 'alpha-inverted' | 'luma' | 'luma-inverted' — whether the mask layer drives content opacity by its alpha or luminance, optionally inverted. elements (≥ 1) compose the mask layer; the group's own subtree is shown only where the mask is opaque/bright.
time_remapKeyframe[]Keyframes that warp the group subtree's clock — each keyframe's value is a warped time (in seconds) so the whole subtree can speed up, slow down, hold, or reverse together. Replaces per-child time math for the subtree as a unit.

Semantics#

The whole concept in four rules:

  1. Coordinate system. A child's x/y are in the group's local space. The group's anchor sets the local origin — by default the group's top-left corner, so child x: 0, y: 0 sits at the group's top-left.
  2. Transforms stack. Rotation, scale, and opacity multiply down the tree — a group with opacity: 0.5 containing a child with opacity: 0.8 produces an effective 0.4 on screen. Same idea for rotation and scale.
  3. Time is relative. A child's time is offset by the group's time. A child whose time + duration exceeds the group's window is clipped (not visible after the group ends).
  4. Layering is local. A child's layer (integer 1–1000, unique within the group) sets paint order among its siblings — lower number draws in front, so layer: 1 is on top. The group's own layer decides where the whole subtree sits relative to its siblings. Layer is resolved per container, so child layers never collide with layers outside the group.

Example: animated card#

{
  "type": "group",
  "id": "card",
  "x": 960, "y": 540,
  "x_anchor": "50%", "y_anchor": "50%",
  "width": 800, "height": 400,
  "time": 0.5,
  "duration": 5,
  "animations": [
    { "type": "scale-in",  "duration": 0.5, "easing": "ease-out-back" },
    { "type": "fade-out",  "duration": 0.3, "time": "end" }
  ],
  "elements": [
    {
      "type": "shape",
      "shape": "rectangle",
      "x": 0, "y": 0,
      "width": 800, "height": 400,
      "fill_color": "#1e293b",
      "border_radius": 24
    },
    {
      "type": "text",
      "text": "Inside the card.",
      "x": 400, "y": 200,
      "x_anchor": "50%", "y_anchor": "50%",
      "font_size": 64,
      "font_weight": 700,
      "fill_color": "#ffffff"
    }
  ]
}

The scale-in animates the whole card (shape + text) together, because the animation is on the group.

Example: lower third#

Reusable "lower third" subtree — title + accent bar — that you can drop anywhere on the timeline by changing the group's time:

{
  "type": "group",
  "x": 540, "y": 950,
  "width": 800, "height": 120,
  "time": 2,
  "duration": 4,
  "animations": [
    { "type": "slide-right-in", "duration": 0.4 },
    { "type": "slide-left-out", "duration": 0.3, "time": "end" }
  ],
  "elements": [
    {
      "type": "shape",
      "shape": "rectangle",
      "x": 0, "y": 0,
      "width": 8, "height": 120,
      "fill_color": "#facc15",
      "x_anchor": "0%"
    },
    {
      "type": "text",
      "text": "Ian Scott · Founder",
      "x": 24, "y": 60,
      "x_anchor": "0%",
      "font_family": "Inter",
      "font_size": 36,
      "font_weight": 600,
      "fill_color": "#ffffff",
      "text_align": "left"
    }
  ]
}

Notes#

  • Nesting depth — groups can contain groups. The runtime imposes no nesting limit, but render cost scales with element count, not depth.
  • Empty groupelements: [] is rejected at the schema level; at minimum one child is required.
  • Why "group" and not "composition"? Both terms exist in motion graphics tools. We picked the lighter one. A future composition element could carry stronger semantics (nested timeline, separate frame rate, render caching) without colliding with this.