Skill v1.0.0
currentLLM-judged scan90/100version: "1.0.0" name: Compositor nodeType: Compositor summary: > Composes media inputs (Text, Image, SVG, Audio, Caption, Video, GIF, and Lottie) into a single image or video file with an HTML-like auto-layout engine. The composition document is a single recursive layout code tree (flex/block/box/text/media) with per-node keyframe animation — deterministic: identical pixels for the same document and frame, in preview AND final render. triggers:
- compositor
- composite
- layer
- merge media
- layout
- flex
- overlay
- picture in picture
- video layout
- title card
Compositor
What It Does
Composes media inputs into a single image or video using a layout code tree — the ONE source of truth for the composition. Agents author config.layout: a recursive tree of layout nodes (flex / block / box / text / media) with HTML-like auto-layout (dir, gap, padding, justify, align, wrap) and per-node keyframe animations. Rendering is deterministic: (document, frame) → pixels, identical in preview and final render.
When to Use
- Title cards / hero layouts: Flex stacks with title + subtitle text and box chips (see example below).
- Overlays / Watermarks: Absolute-positioned text or media over video/image backgrounds.
- Picture-in-Picture: Multiple videos/images arranged by a flex/block tree or absolute placement.
- Timeline composition: Per-node
startFrame/durationFrames+ keyframe tracks. - Visual styling: Box fills + radius, text styling, shadows, keyframe motion.
Inputs
This node uses Variable Inputs. You can add dynamically named input handles of the following types:
TextImageVideoAudioCaptionSVGGIFLottieSignal
Config
| Field | Type | Range | Default | Description | |
|---|---|---|---|---|---|
| width | number | 1–4096 | 1080 | Canvas width in pixels. | |
| height | number | 1–4096 | 1080 | Canvas height in pixels. | |
| backgroundColor | string | Hex/RGB CSS Color | undefined | Background color of the compositor canvas. | |
| volume | number | 0–1 | 1 | Overall master audio volume scaling. | |
| fps | number | 1–120 | 24 | Frames per second for video output. | |
| mode | string | "Video" or "Image" | "Video" | Explicitly configures compositor rendering/output mode. | |
| layout | array | Array of Layout Nodes | [] | The composition document: a recursive tree of layout nodes. |
`type` vs `kind`:typeis an INPUT DataType (Text,Image,Video,Audio,Caption,SVG,GIF,Lottie…) and never appears on layout nodes. The layout type ofa node is its `kind`:flex|block|box|text|media|shape|chart.
Layout Node Schema
Common fields (every node):
- `id` (string, required): Unique node id — also keys the timeline.
- `kind` (string, required):
"flex"|"block"|"box"|"text"|"media"|"shape". - `inputHandleId` (string, optional): Graph binding — which connected input this node renders (for text/media nodes).
> [!WARNING] > Do not place a handle label in inputHandleId when updating an existing live Compositor. The renderer may treat the media as unbound and produce a blank layer. Use the actual dynamic input handle ID returned from the live Compositor node.
- `position` (string, optional):
"relative"(default, in-flow) or"absolute"(out-of-flow; placed byx/y). - `x` / `y` (number, optional): Offset from the parent's content box (absolute placement / transform base).
- `width` / `height` (SizeSpec, optional):
number(pixels),"auto"(content),"fit"(fit content), or"fill"(fill the parent).blockdefaults to"fill"width. - `grow` (number, optional): Flex-grow weight — extra main-axis space is split proportionally.
- `flexShrink` (number, optional): Flex shrink factor (Yoga).
- `flexBasis` (number, optional): Flex basis in pixels.
- `alignSelf` (string, optional): Per-child cross-axis override (
"auto"|"start"|"center"|"end"|"stretch"|"baseline"). - `aspectRatio` (number, optional): Intrinsic aspect ratio (
width / height). - `zIndex` (number, optional, default 0): Stack order within the same parent level. Higher renders on top.
- `hidden` (boolean, optional): Skips drawing the node.
- `opacity` (number, optional, 0–1, default 1).
- `blendMode` (string, optional, default
"normal"): Blend mode used when compositing the node ("normal","multiply","screen","overlay","darken","lighten","color-dodge","color-burn","hard-light","soft-light","difference","exclusion","hue","saturation","color","luminosity","source-over","source-in","source-out","source-atop","destination-over","destination-in","destination-out","destination-atop","lighter","copy","mask-in","mask-out","xor"). - `rotation` (degrees) / `scale` (multiplier) / `anchorX`, `anchorY` (0–1): Node transform.
- `rotateX` / `rotateY` / `rotateZ` (degrees, default 0): 3D out-of-plane rotation (pitch, yaw, roll).
- `perspective` (number, px distance, default 0 = disabled): Virtual camera distance for 3D perspective foreshortening.
- `translateZ` (number, px depth, default 0): Translation along depth Z axis (moves closer/further under perspective).
- `perspectiveOriginX`, `perspectiveOriginY` (0–1, default 0.5): 3D perspective vanishing point anchor.
- `backfaceVisibility` (
"visible"|"hidden", default"visible"): When"hidden", culls the layer when rotated > 90° away from the camera. - `transformStyle` (
"flat"|"preserve-3d", default"flat"): 3D transform rendering style. - `startFrame` / `durationFrames` (integer, optional): Node visibility window on the master timeline (frames).
- `deflicker` (object, optional): Temporal de-flickering and optical flow motion warping options (
blendWeight,disocclusionThreshold,searchRadius). - `relighting` / `relight` (object, optional): Screen-space 3D normal relighting options (
lightType,intensity,lightPosX,lightPosY,lightPosZ,lightRadius,specularStrength,roughness,metallic,ambientIntensity,depthScale,normalTexture). - `effects` (array, optional): Post-processing effect pipeline attached to the node (e.g.
Effect.deflicker(),Effect.relight3d()). - `animation` (object, optional): Track-based keyframes (§ Animation Schema).
Container styles (flex/block/box with children):
- `dir` (flex only):
"row"(default) or"column". - `gap` (number): Space between children along the main axis.
- `padding` (number): Inset of the content box.
- `justify` (flex only):
start(default) |center|end|space-between|space-around. - `align` (flex/block):
start(default) |center|end|stretch. - `wrap` (flex only, boolean): Allow main-axis wrapping.
- `overflow` (
"hidden"(default) |"visible"): Clip children to container bounds (hidden) or allow drawing outside (visible). A hidden container with aborderRadiusclips to its rounded corners, animated radius and size included (an iris mask is a box closing itswidth,heightandborderRadius). - `staggerFrames` (number, optional, default 0): Automatic frame offset sequentially applied to child nodes.
- `staggerDirection` (
"forward"|"reverse"|"center-out", default"forward"): Traversal order when computing child stagger delays.
Per-kind fields:
- `box`:
background(CSS color, also accepts gradients),borderRadius(number),padding. Aboxwith children behaves like a column container. - `text`:
text(string),fontSize,fontFamily,fontWeight,fontStyle,fill(text color),align(start/endfollow the text direction),direction(autodefault |ltr|rtl: paragraph base direction;autotakes it from the first strong character, so Arabic/Hebrew/Persian read right to left, with mixed Latin and numbers kept in order),verticalAlign(topdefault |middle|bottom: places the text block in aheighttaller than it; ignored for path text),lineHeight,letterSpacing,textShadow,shadows,background(rounded text box fill),borderRadius,padding. - Kinetic Typography Animators (`animators`): Array of text animators applied sequentially at the Slug GPU glyph instancing stage:
id(string): Unique animator identifier.unit("character"|"word"|"line", default"character"): Granularity of text breakdown.rangeStart(number, 0–1, default 0): Normalized selector start bound.rangeEnd(number, 0–1, default 1): Normalized selector end bound.offset(number, -1 to 1, default 0): Normalized selection window phase offset (keyframeable viaoffset,animatorOffset,rangeStart,rangeEnd).ease(string, default"smoothstep"): GSAP-style easing curve ("power1","power2","power3","power4","sine","expo","back","smoothstep","linear").transform: Property offsets applied to selected glyph units:deltaX/deltaY(number): Spatial displacement in pixels.deltaRotation(number, degrees): 2D in-plane rotation angle.deltaScale(number, relative multiplier, e.g. -1 for full collapse to 0).scaleX/scaleY(number, directional scale multiplier).rotationX(number, degrees): 3D perspective flip squashing around the local glyph baseline.opacity(number, absolute or relative alpha override).blur(number): Gaussian blur radius.- `shape`: Parametric vector shape and path primitive for motion graphics.
shapeType("rect"|"circle"|"ellipse"|"polygon"|"star"|"arrow"|"path", default"rect").- Geometry:
borderRadius,radiusTL,radiusTR,radiusBR,radiusBL,polygonSides(3..64),starPoints(3..64),starInnerRadiusRatio(0.01..0.99),arrowHeadWidth,arrowHeadLength,arrowShaftWidth,d(SVG path data:M L H V C S Q T A Z, absolute and relative; arcs included). - Fill:
fillType("solid"|"linear"|"radial"|"none"),fillColor,gradientEndColor,gradientAngle. - Stroke:
strokeColor,strokeWidth,strokeDashArray,strokeDashOffset,strokeLineCap("butt"|"round"|"square"),strokeLineJoin("miter"|"round"|"bevel"),strokeAlign("inside"|"center"|"outside"). - Trim Paths:
trimStart(0..1),trimEnd(0..1),trimOffset(rotation/offset). - Charts: there is no chart node kind.
Layer.chartin theframefieldsSDK expands a chart intobox,shape("path"/"circle") andtextnodes, so a program document describes a chart with those kinds. - `media`:
inputHandleId(string, required — must match a connected input handle). - Standard Media (`Video`, `Image`, `GIF`, `SVG`, `Lottie`):
fit("cover"|"contain"|"fill"|"none", default"contain"),volume(0–1),muted,borderRadius,borderColor,borderWidth. - Captions & Subtitles (`Caption` DataType): When bound to a
Captioninput (SRT source),kind: "media"renders time-synchronized subtitle cues: fontSize(number, default48): Font size in pixels.fontFamily(string, default"Inter"): Font family.fontWeight(string | number, default700): Weight ("normal","bold",400,700,900).fontStyle("normal"|"italic").fill(string, default"#ffffff"): Text color.align("start"|"center"|"end", default"center"): Horizontal text alignment.direction("auto"|"ltr"|"rtl", default"auto"): Caption text direction;start/endfollow it.verticalAlign("top"|"middle"|"bottom", default"bottom"): Vertical alignment. When"bottom", the bottom edge is anchored, and multi-line subtitle cues expand upwards.lineHeight(number, default1.2orfontSize * 1.2).letterSpacing(number, default0).background(string, optional): Background box fill color behind the active subtitle text.padding(number, optional): Inset padding around text.borderRadius/strokeRadius(number, default8): Corner radius for background rectangle.stroke(string) /strokeWidth(number): Text outline stroke.textShadow/shadows: Drop shadows.- Sizing & Placement: An explicit
width(e.g.800or"80%") controls word-wrapping width. Whenheightis omitted or"auto", the engine measures the maximum height needed across all cues in the SRT file to keep layout and positioning stable throughout playback.
Layout semantics (HTML-like):
- A flex node with
dir: "column"stacks children vertically;dir: "row"lays them horizontally. - block behaves as a column container whose width fills the parent.
- box without children is a styled rectangle (fill + radius); with children it wraps them in a column.
"fill"/"fit"sizes resolve against the containing block;growsplits leftover space.- absolute nodes are removed from flow and placed at
x/yof their parent's content box. - Text wraps: an explicit numeric
widthwraps at that width. The node box always matches the drawn (wrapped) text. - Captions: Render synchronized cues from connected SRT sources. Default to bottom alignment (
verticalAlign: "bottom"), expanding earlier lines upwards when wrapping across multiple lines. - Canvas bounds: the output is exactly
width×height. Nodes may extend beyond it (large sizes, negativex/y) — anything outside the canvas is clipped in the output. Usefit/contain/filland canvas-sized boxes for fully-visible media.
Animation Schema (per node)
- `tracks` (array): Up to 24 animation tracks.
Each track represents animatable property modifications:
- `id` (string, required): Unique identifier for the track.
- `prop` (string, enum, required):
x,y,scale,rotation,opacity,fill,color,letterSpacing,width,height,volume,hidden,muted,fontSize,text,trimStart,trimEnd,trimOffset,strokeWidth,strokeDashOffset,cornerRadius,starInnerRadiusRatio,fillColor,strokeColor,rangeStart,rangeEnd,offset,animatorRangeStart,animatorRangeEnd,animatorOffset,rotateX,rotateY,rotateZ,perspective,translateZ,perspectiveOriginX,perspectiveOriginY,progress,drawProgress,chartProgress. - `source` (object, optional, default
{ type: "keyframe" }): Driver evaluating this track. { type: "keyframe" }: Keyframe sequence (requireskeyframes).{ type: "signal", inputHandleId: string, multiplier?: number, offset?: number, smoothingWindowFrames?: number, signalMode?: "continuous" | "accumulate", threshold?: number, debounceFrames?: number, colorMode?: "interpolate" | "hueRotate" | "threshold", colorA?: string, colorB?: string, colorThreshold?: number }: Samples a connectedSignalvariable input handle (e.g. fromAudioSignalExtractororSignalMath). In"continuous"mode, maps amplitude or continuous values directly. In"accumulate"mode, acts as a discrete event integrator that detects rising-edge acoustic transients/peaks exceedingthreshold(withdebounceFramesguard) and increments an integer step count on each peak — ideal for audio-reactive typography where each keystroke/drum hit reveals letters sequentially. Also supports dynamic color properties (fill,color) via linear interpolation, hue rotation, or binary threshold switching. AliaseshandleId(forinputHandleId),amplitude(formultiplier), andsmoothing(forsmoothingWindowFrames) are also accepted. For procedural drivers likesignal,keyframes: []can be empty.{ type: "wiggle", frequency: number, octaves?: number, amplitude: number, offset?: number, seed?: number }: Multi-octave continuous coherent gradient noise.{ type: "springOvershoot", mass?: number, stiffness?: number, damping?: number, initialVelocity?: number, targetValue?: number }: Analytical closed-form damped harmonic oscillator ODE.- `keyframes` (array, optional when procedural
sourceis used): Chronologically sorted keyframe points. - `repeat` (number, optional): GSAP loop count (e.g., -1 for infinite loops).
- `yoyo` (boolean, optional): If true, animates back and forth.
Keyframe Schema:
- `id` (string, required): Unique keyframe identifier.
- `frame` (number, required): Clip-relative frame number where this keyframe is reached (
0= node start). - `value` (number or boolean, required): Target value at this keyframe.
- `ease` (object, optional): Segment easing parameters.
- `name`:
none,power1,power2,power3,sine,circ,expo,back,elastic,bounce,spring,cubic,hold. - `dir`:
in,out,inOut. - `params` (array of numbers): Optional easing parameter overrides (e.g.
back.out(1.7)). - `cubicParams` (array of 4 numbers, optional): Control points
[x1, y1, x2, y2]whenname: "cubic". - `spatialTangentIn` (array of 2 numbers
[dx, dy], optional): Ingoing spatial tangent handle for 2D position spline trajectories. - `spatialTangentOut` (array of 2 numbers
[dx, dy], optional): Outgoing spatial tangent handle for 2D position spline trajectories.
Ordering / Z-Index
- Within each parent level, nodes draw in ascending
zIndex(default0). - The tree order (children array order) is the layout order;
zIndexonly breaks ties within a level.
Output
| Handle | Type | Description | |
|---|---|---|---|
| Result | Image, Video | The final rendered composite media file. |
Common Patterns
- Title card: one
flexcolumn (align: "center",pad,fill) containing titletext, subtitletext, and aflexrow ofboxchips withmediaavatars. Animate the column'sopacity, the row'sy, and a media node'sscalewith keyframe tracks. - Video Subtitles / Captions: Add a dynamic
Captioninput handle (e.g."subtitles"). In the layout tree, place amedianode bound to"subtitles"withwidth: 900,fill: "#ffffff",fontSize: 44,align: "center",verticalAlign: "bottom", and place it near the bottom of the canvas (e.g.x: 90,y: 880or in a bottom-aligned flex container). - Watermarking a Video: a
flexrow (align: "end",justify: "end", full canvas) containing amedianode bound to the PNG input; lowopacity. - Picture-in-Picture: a
flexrow with twomedianodes (fit: "cover", eachgrow: 1). - Audio-Reactive 3D Models: a
kind: "model3d"node loading an OBJ/FBX/GLTF/STL/PLY mesh withaudioDeform: { audioTrackId, mode: "normal_extrusion" | "radial_pulse" | "harmonic_wave" | "twist" | "ripple", amplitudeMultiplier, damping }, deformed directly in VRAM via WebGPU compute shaders driven by audio frequency spectra and transient energies.twistturns each slice about Y by its height (harder on the bass);rippleruns concentric waves across XY, struck by the drums.audioTrackIdmay be an audio layer's id or an audio file path. - glTF scenes: a glTF/GLB
model3dkeeps its node hierarchy, every skin and its animation clips (translation, rotation, scale; linear, step and cubic-spline). Skinned meshes are posed per frame from the clip (animationName,animationTime/animationProgress,loop); base-colour textures (with mipmaps), alphaMASK/BLENDand VRM MToon materials (material: "toon", picked automatically whenmaterialis unset) render as authored.center/normalizeSizeapply above the scene graph, so skinning stays intact.
Don't Forget
- If text rendering required, you must include a font in spec (CLI TOOL only). No emoji font ships with framefields: to draw emoji, provide a color emoji font yourself with
FontManager.registerEmojiFont(path)(a CBDT/CBLC bitmap font such as Noto Color Emoji). - The
layouttree IS the composition — there is no other layer model. Every media node
needs a valid inputHandleId matching a connected input, and every node needs a kind.
typeis an input DataType — never put it on layout nodes; usekind.- Connected inputs do NOT render automatically — build the tree explicitly.
- Static Image Compositions: When outputting static ad banners, posters, or graphics, set
"mode": "Image"and ensure all connected filter nodes (such asFilmGrain) are configured statically (animated: false). Do NOT insertExtractFramenodes for static image compositions — connectCompositordirectly toExport.
Live Canvas Authoring vs. Offline CLI/Spec Compilation
Understanding how inputHandleId resolves is critical depending on the authoring environment:
- Live Canvas Authoring:
- You must use the actual dynamic input handle ID returned after creating/inspecting the Compositor node (e.g.
input_abc123). - Never use handle labels here. Live runtime lookups query inputs directly by their exact handle IDs.
- Offline CLI / Spec Compilation:
- A human-readable handle label (e.g.
"bg_canvas_handle") may be used ininputHandleId. - The Artifex runner automatically resolves and maps these human-readable labels to internal generated IDs (
temp-xxxx) during graph compilation. - This mapping is recursively applied to matching dynamic input labels in root configuration properties and Compositor
layouttree elements (inputHandleId).
[!WARNING]Live Canvas Warning: Do not place a handle label ininputHandleIdwhen updating an existing live Compositor. The renderer may treat the media as unbound and produce a blank layer.
Verification Checklist
When authoring or modifying a Compositor configuration, verify:
- Confirm the graph edge exists: The upstream source node is connected to the corresponding dynamic input handle on the Compositor.
- Confirm the layout contains a media node: A
kind: "media"(orkind: "text") node is explicitly declared inconfig.layout. - Confirm its binding matches the live Compositor input: The
inputHandleIdmatches the live dynamic input handle ID (or human-readable handle label if building an offline CLI spec). - Preview a frame where opacity is greater than zero: Check an active playback frame where
opacity > 0,hiddenis nottrue, and the frame falls withinstartFrameanddurationFrames.
Troubleshooting: Blank Output or Missing Layers
If a layer or the final composite renders blank, check the following common failure modes:
- Missing media node: The upstream node is connected in the graph canvas, but no
kind: "media"element exists inconfig.layout. Inputs do not render automatically without a layout node. - Stale or incorrect input binding:
inputHandleIdreferences a non-existent handle, uses a human-readable label in live canvas mode instead of the real handle ID, or refers to a deleted/renamed handle. The renderer treats unbound media as empty. - Zero opacity: The node's
opacityis0, a parent container'sopacityis0, or an animation track interpolatesopacityto0at the inspected frame. - Timeline outside `startFrame`/`durationFrames`: The current frame is outside the active clip range (
frame < startFrameorframe >= startFrame + durationFrames). - Layer outside canvas bounds: Absolute coordinates (
x,y), padding/offsets, or transforms place the layer completely outside the canvaswidth×height, orscaleis0. - Hidden node or invalid dimensions:
hidden: trueis set, orwidth/heightevaluates to0with no intrinsic media dimensions available.
Example JSON Configuration
{"width": 1920,"height": 1080,"backgroundColor": "#16130d","fps": 24,"mode": "Video","layout": [{"id": "hero","kind": "flex","dir": "column","gap": 24,"padding": 80,"align": "center","width": "fill","height": "fill","animation": {"tracks": [{"id": "hero-fade","prop": "opacity","keyframes": [{ "id": "kf0", "frame": 0, "value": 0 },{ "id": "kf1", "frame": 15, "value": 1, "ease": { "name": "power2", "dir": "out" } }]}]},"children": [{"id": "title","kind": "text","text": "Big Title","fontSize": 96,"fontWeight": 900,"fill": "#f4ead8"},{"id": "subtitle","kind": "text","text": "Rendered by the compositor layout engine","fontSize": 40,"fill": "#b8a88a"},{"id": "badges","kind": "flex","dir": "row","gap": 16,"animation": {"tracks": [{"id": "badges-rise","prop": "y","keyframes": [{ "id": "kf0", "frame": 0, "value": 40 },{ "id": "kf1", "frame": 20, "value": 0, "ease": { "name": "back", "dir": "out" } }]}]},"children": [{ "id": "chip-avatar", "kind": "box", "width": 160, "height": 48, "borderRadius": 24, "background": "#3a2f1e" },{ "id": "chip-hero", "kind": "box", "width": 160, "height": 48, "borderRadius": 24, "background": "#3a2f1e" }]}]},{"id": "avatar-img","kind": "media","inputHandleId": "avatar","fit": "cover","width": 200,"height": 200,"borderRadius": 100,"startFrame": 0,"durationFrames": 72,"animation": {"tracks": [{"id": "avatar-pop","prop": "scale","keyframes": [{ "id": "kf0", "frame": 0, "value": 1.15 },{ "id": "kf1", "frame": 30, "value": 1 }]}]}},{"id": "subtitles","kind": "media","inputHandleId": "subtitles_handle","position": "absolute","x": 160,"y": 860,"width": 1600,"fontSize": 48,"fontWeight": 700,"fill": "#ffffff","align": "center","verticalAlign": "bottom","stroke": "#000000","strokeWidth": 4,"background": "#00000088","padding": 16,"borderRadius": 12}]}