Renderbox

@jtdigital/renderbox-sdk · 0.17.0 · apache-2.0

The TypeScript SDK for Renderbox.

It builds Renderbox jobs in TypeScript, in two ways: a graph of typed streams, or a motion composition. It does not process video. Renderbox's workers run what it builds.

The composition API

A motion program is one value.

/motion writes a timed composition, such as a looping display video or an ad in three shapes, as one value. Leaves (Text, Rectangle, Still, Video) sit in containers that time them and lay them out, in a Project().

The program never reads a clock. Every time is on the clock of the value that carries it, and every value that changes is a key track.

launch.ts
import {
  DISPLAYS, EASE, ENTER, Project, Rectangle, Row, Safe, Series, Text,
  defineMarkers, defineTheme, dissolve, eased, keys,
} from '@jtdigital/renderbox-sdk/motion';

const t = defineTheme({
  name: 'launch',
  colors: { night: '#0A0C10', cyan: '#22D3EE', ink: '#F1F4F9' },
  type: {
    head: {
      face: 'anton', caps: true, capPx: 120,
      role: 'headline', color: 'ink',
    },
    tag: { face: 'inter', capPx: 40, role: 'support', color: 'ink' },
  },
});
const M = defineMarkers(['hook', 'proof'] as const);

const hook = Safe({},
  Text('Every frame, checked.', { style: t.type.head })
    .place({ x: 'center', y: 360 }).apply(ENTER.rise),
  Rectangle({ w: 1200, h: 8, fill: t.color.cyan })
    .place({ x: 'center', y: 560 })
    .move(keys(-1560, eased(0, { dur: 1.2 }, EASE.outCubic)), 0),
).duration(3).marker(M.hook, 1);

const proof = Safe({},
  Row({ gap: 64, stagger: { each: 0.2 } },
    ...['TYPED', 'SEALED', 'CHECKED'].map((s) =>
      Text(s, { style: t.type.tag }).apply(ENTER.rise)),
  ).place({ x: 'center', y: 500 }),
).duration(3).marker(M.proof, 1.5);

export default Project(Series(hook, dissolve(0.4), proof), {
  name: 'launch', display: DISPLAYS.desktop, fps: 30,
  ground: t.color.night, beats: [M.hook, M.proof],
});

Its parts

What a program is made of.

time
A Series plays its items one after another, a Parallel together, a Stagger a fixed step apart. Markers and cues anchor times, so no time is typed by hand.
transitions
dissolve(), wipe(), push(), dip(), overlap(), overlay(), and xfade() with any of FFmpeg's transitions by name, checked by tsc.
layout
Row, Column, Stack, Grid, and Safe, which keeps its items inside the display's safe area. .place() puts a value by a number or a word.
key tracks
keys(first, ...segments) animates a number, a colour or a point on the value's own clock. cubic() and spring() are refused at build until a later epoch draws them.
looks
defineTheme() holds a closed palette and type styles, each with a face, a cap height and a role. .effect() runs a grade, a blur, a shadow, a glow or a key.
templates
defineTemplate() makes a component from typed props, so one program makes a video for each row of data it is given.
refusals
A program with no meaning, such as a time set twice or a marker no value carries, throws Refused when it builds, with a fix that compiles.
pictures and sound
Still() and Video() take an asset by its S3 key and pixel size, and Drawn() draws path data a function writes each frame. The project's audio lists its sounds on the same clock, with gain, fades and ducking.

Two backends

One program, two ways to draw it.

FFmpeg is the default, and the backend the fleet runs today. Its build is a graph like any other.

The native backend writes the program as a composition document, which the renderbox-motion renderer draws. It is not the default yet, it draws no video yet, and the fleet runs it only once the renderer is on its hosts.

How the renderer draws a frame →

build.ts shortened
import { build } from '@jtdigital/renderbox-sdk/motion';
import { textShaper } from '@jtdigital/renderbox-sdk/node';
import launch from './launch.js';

// FFmpeg, the default: a graph like any other
const built = build(launch);
const picture = built.video.format({ pix_fmts: 'yuv420p' });
built.graph.writeVideo('renders/launch.mp4', picture);

// native: a composition document the renderer draws
const native = build(launch, {
  backend: 'native',
  shaper: textShaper(),
  assets: (asset) => sha256(`media/${asset.key}`),
});
native.document?.sha256;  // sealed with the render epoch

Access is by request.

There is no sign-up. To run work on Renderbox, or to ask how a part of it works, write to us.

Write to us →