Overlays Reference

Every overlay type Renderly supports — fields, units, gotchas, and minimal examples. The companion to the OpenAPI spec.

This page is the human-readable companion to the OpenAPI reference. It documents every overlay type a composition can contain, calls out the units each field uses, and flags the surprises (fields that look optional but aren't, fields the renderer silently ignores).

For the literal schema and copy-pasteable examples, see POST /renders.

Fields every overlay has

These come from BaseOverlay and exist on text, video, image, sound, shape, sticker, caption, and effect alike.

FieldUnitPurpose
idintegerUnique numeric id within the composition.
typestringDiscriminator: "text", "video", "image", "sound", "shape", "sticker", "caption", "effect".
fromframesFirst frame the overlay is visible on the timeline.
durationInFramesframesHow long the overlay stays visible.
rowintegerTimeline track index. Higher rows render above lower rows.
top, leftpixelsPosition of the layer's top-left corner relative to the canvas.
width, heightpixelsLayer box size.
rotationdegreesRotation around the layer's center. Defaults to 0.
isDynamicbooleanMarks the overlay as a template variable.
namestringVariable key used to match the replacements object.

Positioning cheatsheet

top and left are pixels from the top-left corner of the canvas — (0, 0) is the top-left. To pin a 300×300 element to the bottom-right of a 1080×1920 canvas with a 40 px margin:

top  = canvasHeight - height - margin   →  1920 - 300 - 40 = 1580
left = canvasWidth  - width  - margin   →  1080 - 300 - 40 =  740

text

{
  "id": 1, "type": "text",
  "content": "Hello world",
  "from": 0, "durationInFrames": 90,
  "row": 1, "top": 800, "left": 60, "width": 960, "height": 200,
  "styles": {
    "fontSize": "4rem", "fontWeight": "900",
    "color": "#FFFFFF", "textAlign": "center",
    "animation": { "enter": "fade", "exit": "fade", "enterDuration": 0.4 }
  }
}
  • content is the rendered string. When isDynamic is true, this is what replacements swaps.
  • styles.fontSize accepts any CSS size string ("3rem", "48px").
  • styles.animation — preset enter/exit. enterDuration / exitDuration are in seconds.
  • styles.animatedText, styles.typeWriter, styles.counterMode, styles.codeBlock — alternate text modes, each with enabled: true to activate. See the editor doc for what each does.

video

{
  "id": 0, "type": "video",
  "src": "https://example.com/clip.mp4",
  "from": 0, "durationInFrames": 180,
  "row": 0, "top": 0, "left": 0, "width": 1080, "height": 1920,
  "videoStartTime": 2,
  "speed": 1,
  "styles": {
    "objectFit": "cover", "opacity": 1
  }
}
  • src — public URL of the source media. Replaced by replacements if isDynamic is true.
  • videoStartTime — in-point of the source clip in seconds. Skips the first N seconds of the source.
  • speed — playback speed multiplier. 2 plays twice as fast; 0.5 plays at half speed.
  • mediaSrcDuration — total length of the source media in seconds. Helps the editor compute trim boundaries.
  • segments — ordered list of { startFrame, endFrame, speed? } slices to chain together. When set, videoStartTime is ignored. Frames are absolute source frames.
  • greenscreen — chroma-key removal. { enabled: true, sensitivity, threshold: { red, green, blue }, smoothing, spill }.
  • styles.volume — 0–1. Defaults to the source's natural volume; set to 0 to mute.
  • styles.cropEnabled / cropX / cropY / cropWidth / cropHeight — percentages (0–100) of the source frame to display. Set cropEnabled: true to apply.

image

{
  "id": 2, "type": "image",
  "src": "https://example.com/photo.jpg",
  "from": 0, "durationInFrames": 90,
  "row": 1, "top": 200, "left": 100, "width": 400, "height": 400,
  "styles": {
    "objectFit": "cover", "borderRadius": "16px", "opacity": 0.85
  }
}
  • styles.opacity — 0–1. Combined with the composition's backgroundColor, this is how you tint or darken an image.
  • greenscreen — same shape as video.
  • styles.layout3D.layout — apply a 3D layout transform preset.
  • styles.animation — enter/exit preset with optional duration in seconds.
  • Crop fields are the same as video.

sound

{
  "id": 3, "type": "sound",
  "src": "https://example.com/music.mp3",
  "from": 0, "durationInFrames": 180,
  "row": 2, "top": 0, "left": 0, "width": 0, "height": 0,
  "startFromSound": 5,
  "speed": 1,
  "mediaSrcDuration": 145,
  "styles": {
    "volume": 0.8, "fadeIn": 1, "fadeOut": 1.5
  }
}

Sound overlays still need top / left / width / height (they come from the shared BaseOverlay), but they're ignored at render time. Set them to 0.

  • startFromSound — in-point of the source audio in seconds. Use this to skip an intro.
  • speed — playback speed multiplier.
  • styles.fadeIn, styles.fadeOut — fade durations in seconds.
  • styles.volume — 0–1.

shape

{
  "id": 4, "type": "shape",
  "content": "rectangle",
  "from": 0, "durationInFrames": 90,
  "row": 1, "top": 100, "left": 100, "width": 200, "height": 200,
  "styles": {
    "fill": "#FF0080", "borderRadius": "16px",
    "animation": { "enter": "scale", "exit": "fade" }
  }
}
  • content — shape identifier ("rectangle", "circle", "triangle").
  • styles.gradient — single CSS gradient string.
  • styles.gradientTransition — animated transition between multiple gradient strings.

sticker

{
  "id": 5, "type": "sticker",
  "content": "discount-50",
  "category": "Discounts",
  "from": 30, "durationInFrames": 90,
  "row": 2, "top": 500, "left": 740, "width": 300, "height": 300,
  "styles": { "scale": 1, "animation": { "enter": "bounce" } }
}
  • content — sticker identifier from the sticker library.
  • category — one of "Shapes", "Discounts", "Emojis", "Reviews", "Default".
  • styles.scale — uniform scale multiplier.

caption

{
  "id": 6, "type": "caption",
  "captions": [
    {
      "text": "Hello world",
      "startMs": 0, "endMs": 1200,
      "timestampMs": null, "confidence": 0.97,
      "words": [
        { "word": "Hello", "startMs": 0,   "endMs": 500,  "confidence": 0.98 },
        { "word": "world", "startMs": 600, "endMs": 1200, "confidence": 0.96 }
      ]
    }
  ],
  "from": 0, "durationInFrames": 90,
  "row": 3, "top": 1500, "left": 60, "width": 960, "height": 200,
  "styles": {
    "fontSize": "3rem", "color": "#FFFFFF",
    "textAlign": "center",
    "highlightStyle": { "color": "#FFD700", "scale": 1.1 }
  }
}

Caption timing is in milliseconds and is interpreted at 30 FPS regardless of the composition's fps. If you build captions at 60 FPS the words will fall out of sync.

  • captions[] carries one entry per phrase, each with word-level timing.
  • styles.highlightStyle is what the currently-spoken word looks like — karaoke-style emphasis.
  • template — an optional named style template (set by the editor).

effect

{
  "id": 7, "type": "effect",
  "content": "confetti",
  "from": 0, "durationInFrames": 180,
  "row": 4, "top": 0, "left": 0, "width": 1080, "height": 1920,
  "styles": { "particleCount": 200, "speed": 1, "size": 8 }
}
  • content is the effect preset: one of "snow", "confetti", "rain", "fireflies", "sparkles", "bubbles", "matrix", "grid-stagger", "fracture", "scrolling-columns".
  • Each preset takes its own subset of styles. Matrix effects use matrixFontSize / matrixColor / matrixDensity / matrixCharset; grid effects use gridCols / gridRows / cellColors / cellImages / staggerDelay; scrolling columns use columns[] / columnGap / imageHeight.

Composition-level fields

These sit at the top of inputProps, not inside any overlay:

FieldUnitPurpose
width, heightpixelsCanvas size.
fpsframes per secondComposition frame rate.
durationInFramesframesTotal composition length.
backgroundColorCSS colorSolid fill behind every overlay. Defaults to "white".
overlays[]arrayThe overlays themselves.

Where to next

  • Templates — turn any overlay into a per-render variable
  • Rendering — kick off a render job and poll for the result
  • API reference — every field with its OpenAPI schema