Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
220 changes: 220 additions & 0 deletions jobs/compose.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,225 @@ A `text` asset renders a styled title. Set the `font`, `size`, `weight`, `color`
<video src="https://cdn.rendobar.com/assets/showcase/fx/title-lowerthird.mp4" autoPlay loop muted playsInline />
</Frame>

## Shapes

A `shape` asset draws a rect, ellipse, circle, triangle, diamond, or any SVG
path you paste in. No image to make, no file to host, and it stays sharp at any
output resolution.

```json
"asset": {
"type": "shape",
"color": "#0A0A0ACC",
"position": { "x": "50%", "y": "86%" },
"size": { "width": "100%", "height": "22%" }
}
```

That is a bar across the lower third, dark enough to read a title on and thin
enough to see the footage through. Put it on a track below the text.

<Columns cols={3}>
<Frame caption="a bar, plus a text track">
<video src="https://cdn.rendobar.com/assets/showcase/shapes/lower-third.mp4" autoPlay loop muted playsInline controls />
</Frame>
<Frame caption="animateTo on the width">
<video src="https://cdn.rendobar.com/assets/showcase/shapes/progress-bar.mp4" autoPlay loop muted playsInline controls />
</Frame>
<Frame caption="radius with a shadow">
<video src="https://cdn.rendobar.com/assets/showcase/shapes/rounded-card.mp4" autoPlay loop muted playsInline controls />
</Frame>
<Frame caption="a circle, two colours">
<video src="https://cdn.rendobar.com/assets/showcase/shapes/gradient-badge.mp4" autoPlay loop muted playsInline controls />
</Frame>
<Frame caption="a stroke with no fill">
<video src="https://cdn.rendobar.com/assets/showcase/shapes/outline-box.mp4" autoPlay loop muted playsInline controls />
</Frame>
<Frame caption="pasted path data">
<video src="https://cdn.rendobar.com/assets/showcase/shapes/svg-path.mp4" autoPlay loop muted playsInline controls />
</Frame>
</Columns>

Only `type` and `size` are required. Everything else on this page is optional,
and the defaults are chosen so the common cases need almost none of it.

Two things to know up front. `position` is the **centre** of the shape unless
you set `anchor`, so a full-width bar sits at `x: "50%"` whatever its height.
And there is no `length`, because a shape has no duration of its own: like text
and images it runs to the end of its container unless you give it one.

`width` is a percent of the frame's width, `height` a percent of its height.

### Leave the height out to get a circle

Omit `size.height` and the shape is made **square in pixels**. That is the only
way to get a true circle or square on a frame that is not square, and it is the
one thing about shapes worth memorising.

A `10%` by `10%` box on a 16:9 frame is 192 by 108 pixels. Rounding it fully
gives a pill, not a circle. Omit the height and the engine derives 17.8% for
you.

```json
{ "type": "shape", "shape": "circle", "color": "#059669",
"position": { "x": "88%", "y": "18%" }, "size": { "width": "12%" } }
```

`shape: "circle"` is `ellipse` with that rule applied, so it rejects an explicit
height rather than quietly ignoring one. The same omission works on every kind:
a `rect` with no height is a square, a `diamond` with no height is a regular one.

### Anchor

`anchor: "topLeft"` points `position` at the corner instead of the centre. It is
what makes letterbox bars arithmetic-free.

```json
{ "type": "shape", "color": "#000000", "anchor": "topLeft",
"position": { "x": "0%", "y": "90%" }, "size": { "width": "100%", "height": "10%" } }
```

### Fills, flat and gradient

`color` takes `#RRGGBB`, or the 8-digit `#RRGGBBAA` for a constant translucency
the way `#0A0A0ACC` does above.

Do not reach for the clip's `opacity` instead. `opacity` dims the fill as well
as fading it, so a half-opacity white box renders grey rather than white you can
see through. Keep `opacity` for animated fades.

Give `color` **two** colours and it is a gradient. `gradient` names the
direction: `vertical` (the default), `horizontal`, `diagonal` or `radial`.

```json
{ "type": "shape", "color": ["#E11D48", "#3B82F6"], "gradient": "diagonal",
"position": { "x": "50%", "y": "50%" }, "size": { "width": "40%", "height": "30%" } }
```

The gradient maps to the **shape**, not the frame, so a small badge shows the
whole ramp instead of a sliver of one. Stops take the 8-digit form too, which is
how you get a scrim that fades to nothing: `["#0A0A0A00", "#0A0A0AFF"]`.

### Outlines, with a real hole

`stroke` draws a border just inside the shape's edge, its `width` in pixels of
the output frame. Give the shape a `color` too and you get a filled shape with a
border; `radius` rounds both together.

Leave `color` out and only the outline draws. What it surrounds is genuinely
transparent, not filled with black, so the footage shows through the middle.
That works on every kind except a path.

```json
{ "type": "shape", "shape": "ellipse",
"position": { "x": "62%", "y": "40%" },
"size": { "width": "26%", "height": "40%" },
"stroke": { "color": "#F59E0B", "width": 5 } }
```

### Rounded corners

`radius` runs from `0` (square) to `1` (fully round) on a `rect`. At `1` a wide
box becomes a pill and a square one becomes a circle.

### Drop shadows

`shadow` follows the shape, including its outline, its rotation and its
animation. Every field has a default, so `"shadow": {}` is a sensible shadow.

```json
{ "type": "shape", "color": ["#FFFFFF", "#E5E7EB"], "radius": 0.3,
"stroke": { "color": "#111827", "width": 3 },
"shadow": { "color": "#00000099", "blur": 14, "x": 8, "y": 10 },
"position": { "x": "30%", "y": "50%" }, "size": { "width": "34%", "height": "40%" } }
```

### Rotation turns about the shape, not the frame

`rotate` is degrees clockwise about the shape's **own** centre, so a shape spins
where it sits rather than orbiting the middle of the frame. A diagonal rule is a
thin rect and one field:

```json
{ "type": "shape", "color": "#FFFFFF", "rotate": 30,
"position": { "x": "50%", "y": "72%" },
"size": { "width": "40%", "height": "0.8%" } }
```

There is no `line` type for the same reason there is no `square` type: a line is
a rect with a small height, and `rotate` is what makes it diagonal.

### Softness

`softness` runs 0 to 1 and feathers the edge. At 0.2 to 0.4 an ellipse reads as
a spotlight or a vignette rather than a hard-edged disc. A full-frame ellipse
with `softness: 0.5` and a radial `["#00000000", "#000000CC"]` fill is a
vignette with no image asset at all.

### Paths

`shape: "path"` takes SVG path data straight out of a design tool. `viewBox`
gives the coordinate space it was drawn in, defaulting to `"0 0 100 100"`, and
the path is scaled into `size` so the same data works at any size on any frame.

```json
{ "type": "shape", "shape": "path", "viewBox": "0 0 24 24",
"path": "M12 21C12 21 3 14.5 3 8.5C3 5.5 5.5 3 8.5 3C10.4 3 12 4.5 12 4.5C12 4.5 13.6 3 15.5 3C18.5 3 21 5.5 21 8.5C21 14.5 12 21 12 21Z",
"color": "#E11D48", "position": { "x": "50%", "y": "50%" }, "size": { "width": "20%" } }
```

`M L H V C S Q T Z` are supported, absolute and relative. Elliptical arcs (`A`)
are refused rather than approximated, so re-export with arcs converted to
curves. A path takes a fill, a gradient, a shadow, rotation and softness, but
not a `stroke`: outlining arbitrary path data needs true path offsetting.

### Animation

`animateTo` moves `position`, `size`, `rotate`, `color` and `radius` over the
clip. `easing` is `linear` by default, which is what a progress bar or a timer
wants, or `smooth` for an entrance.

```json
{ "type": "shape", "color": "#FFFFFF", "anchor": "topLeft",
"position": { "x": "2%", "y": "86%" },
"size": { "width": "0%", "height": "1.5%" },
"animateTo": { "size": { "width": "96%" }, "easing": "smooth" } }
```

A dimension you leave out of `animateTo` keeps its starting value, so the bar
above grows sideways without changing height. A shape that is square in pixels
stays square as it grows, so an animated circle does not turn into an ellipse
halfway through. A solid colour can animate into a gradient, and a square can
round itself off with `animateTo.radius`.

An animated `rotate` has no limit: rotation is baked into the shape's own
outline, so animating from 0 to 720 is two full turns.

### Combinations the API refuses

Two engine primitives draw shapes and they have different abilities, so a few
combinations are rejected at submit time rather than rendered wrong. Each error
names the fix.

| Not combinable | Why | What to do instead |
| --- | --- | --- |
| `radius` on anything but a `rect` | only rectangles have corners | drop it |
| `radius` with no `color` | an outline-only shape is drawn by the primitive with no corner rounding | give it a fill |
| `radius` with `rotate` or `softness` | the primitive that rounds corners cannot turn or feather | drop one of them |
| `stroke` on a `path` | outlining arbitrary path data needs true path offsetting | draw the outline into the path itself |
| `transform` on any shape | it moves the whole frame, not the shape in it, so the result lands somewhere neither value asked for | use `position`, `size` and `rotate` |

### What else a shape takes

`blendMode` mixes it with the layer below, `blur` softens the whole thing,
colour grading tints it, and opacity keyframes fade it in and out. A track
holding only shapes composites as a base layer, so a solid colour background is
a timeline that references no files at all.

There is no arbitrary path stroke and no per-corner radius. Anything past that
is still an `image` asset.


## Fonts

Name any [Google Fonts](https://fonts.google.com) family in `style.font` and it
Expand Down Expand Up @@ -566,6 +785,7 @@ A clip's `asset` is one of:
| `image` | `src` |
| `audio` | `src`, `trim`, `volume`, `fadeIn`, `fadeOut` (seconds) |
| `text` | `text`, `style` |
| `shape` | `shape` (`rect`, `ellipse`, `circle`, `triangle`, `diamond`, `path`), `color` (one colour, or two for a gradient; omit for outline-only), `gradient`, `stroke` `{ color, width }`, `shadow` `{ color, blur, x, y }`, `position` (the **centre** by default), `anchor`, `size` `{ width, height? }` (omit the height for a shape square in pixels), `radius`, `rotate`, `softness`, `path` + `viewBox`, `animateTo` `{ position, size, rotate, color, radius, easing }`. See [Shapes](#shapes) |
| `composition` | `timeline`, a nested `{ tracks }` rendered and composited as a single clip. Every clip field below applies to a composition clip too, except `speed` (set speed on the clips inside the composition instead) |

### Clip fields
Expand Down
Loading