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: 'group';
  elements: Element[];
}

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) for a clipped group — 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.

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: 50 containing a child with opacity: 80 produces an effective 40 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. Tracks are local. Child track values establish z-order within the group; the group's own track decides where the whole subtree sits relative to its siblings.

Example: animated card#

{
  "type": "group",
  "id": "card",
  "x": 960, "y": 540,
  "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": 400, "y": 200,
      "width": 800, "height": 400,
      "fill_color": "#1e293b",
      "border_radius": 24
    },
    {
      "type": "text",
      "text": "Inside the card.",
      "x": 400, "y": 200,
      "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.