Cut machine 23.84 s 1920 × 1080

The edit is a program.

Eight shots in one TypeScript file. Seven of eight joins sit on a beat the SDK found in the song. The match cut is placed by code.

// --- 05 match cut: the ring round the island reaches 400 px, and the cut lands there --
const EYE: readonly [number, number] = [530, 390];
const ringT0 = J[4]! - 0.72;
const dia = tween(180, 520, { at: ringT0, dur: 1.2, ease: EASE.inOutSine });
const cutT = passes(dia, 400, [ringT0, ringT0 + 1.2]) ;
const mcut = Math.ceil(cutT * FPS - 1e-9) / FPS;

ts-sdk/examples/showcase/cut-machine/cut.ts:218

Contact sheet: the frame before, on and after each of the eight joins
contact-sheet.png Stock footage

Read it like a shot list.

A composition is a screen, a clock and a table of scenes. The whole composition is built before a frame renders.

board.ts two scenes
// The ball, and the crossing it lays.
const ballX = key([{ t: BALL[0], v: -60 }, { t: BALL[1], v: 1980, ease: EASE.inOutSine }]);
const cross = crossing(ballX, BALL);
const board = composition({
display: { w: 1920, h: 1080, diagonalIn: 24, viewingM: 0.6, legible: { headline: 19, support: 12 } },
fps: FPS, duration: DURATION, loop: true, ground: C.night,
scenes: {
race: { from: 0, enter: dissolve(0.4) },
close: { from: BALL[0], enter: cross.wipe , ground: C.flame },
},
});

ts-sdk/examples/showcase/launch-board/board.ts:217

display
The screen and the viewing distance.
fps, duration, loop
The clock.
scenes
One line per scene.
cross.wipe
The ball's path draws the wipe.

board.mp4 · sample data 1920 × 1080 · 12 s

Worked examples

A render, its code and one measured number.

Title storm 8 s 1080 × 1920 Stock dancer

Each letter lands on a beat.

0 landing times typed by hand

Twenty-four letters, each on its own arc. lands[] holds the beats the SDK found in the song.

storm.ts each letter, flown to its beat
// An early letter flies for what time there is before it lands.
const at = Math.max(0, lands[e.n]! - FLY);
const dur = lands[e.n]! - at;
const spin = side * (360 + 180 * (e.n % 3));
return e.letter.show({
from: at,
pose: [along(d, { at, dur, ease: EASE.outCubic }), { dx: sx - ex, dy: sy - ey, rotate: tween(spin, 0, { at, dur, ease: EASE.outCubic }) }],
...(e.n % 3 === 2 ? { shutter: SHUTTER.open } : {}),
});
});
return row({ gap: 6 * u, x: shift, align: 'baseline', baseline }, shown).show({ shutter: SHUTTER.film });

ts-sdk/examples/showcase/title-storm/storm.ts:182

Seven frames of title-storm, the letters arriving one by one
hero-strip.png 7 frames of the render

Drop, measured 25.8 s 1080 × 1920 Song by the SDK

drop-measured-9x16.mp4 16:9 cut

The cut waits for the drop it heard.

12.1 ms early, under a third of a frame

The SDK wrote this song, so its true drop is known. It sits at 5.625 s, and the analysis found it at 5.613 s. 38 of 38 measured cuts land on their planned frames.

cut.ts the drop, from the analysis
const songA = A.songs['128'];
const bpm = songA.bpm;
if (bpm === null) throw new Error('cut: the song has no measured tempo');
const b = 60 / bpm;
const bar = 4 * b;
const D = songA.drops[0]?.t ;
if (D === undefined) throw new Error('cut: the song has no measured drop');
/** The bar the drop opens, counted from 1 on the measured grid. */
const dropBar = Math.round(D / bar) + 1;
/** The main cut: the drop, then eight measured bars, on a whole frame. */
const T = onFrame(D + 8 * bar);

ts-sdk/examples/showcase/drop-measured/cut.ts:62

Stock dancer and inserts. Master at −13.4 LUFS.

Lens plate 20 s 1920 × 1080 Stock plate

A film lens that exists only in code.

8 optical parts, each a switch

Dolly, rack focus, halation, two light leaks, grain, vignette and fringing. Switch one off and the spot renders without it.

lens.ts the last layer
export function spot(cut: Shape, lens: Lens = LENS): Spot {
const o = optics(cut, lens);
const [w, h] = [o.comp.display.w, o.comp.display.h];
const comp = composition({ display: o.comp.display, fps: FPS, duration: SECONDS, ground: night, scenes: { a: { from: 0 } } });
const effects: Effect[] = [];
if (lens.ca) effects.push(effect.filter('rgbashift', { rh: -3, bh: 3, edge: 'smear', enable: `between(t,${T.caFrom},${T.caUntil})` }));
if (lens.vignette) effects.push(effect.filter('vignette', { angle: 0.42 }));
if (lens.grain) effects.push(effect.filter('noise', { alls: 13, allf: 't+u', all_seed: 7 }));
const timing: Timing = {
...(lens.dolly ? { pose: { scale: tween(1, 1.18 , { at: 0, dur: SECONDS, ease: EASE.inOutSine }) } } : {}),
...(effects.length > 0 ? { effects } : {}),
};
comp.scene.a.add(precomp(o.comp, { rect: rect(0, 0, w, h), start: 0 }).show(timing));
return { comp, optics: o.comp, plate: o.plate };

ts-sdk/examples/showcase/lens-plate/lens.ts:286

Dot stings 50 fps 1920 × 1080 JT Digital's own mark

A sting library from one program.

45 files, one program

Eight stings at three lengths, on paper and on night. The ignite sting also ships as ProRes 4444 with real alpha.

stings.ts one call adds a sting to any scene
export function addSting (v: Variant, into: Target, o: StingOpts): Tick[] {
const { at, L, ground, g } = o;
const t = tones(ground);
const { lead, k } = pace(L);
const a = at + lead;
const end = at + (o.hold ?? L);
const blur: Shutter = { angle: 180, samples: o.samples ?? 64 };
switch (v) {
case 'ignite': {
into.add(
word(g, t.word).show({ from: a + 0.12, until: end, enter: RISE }),
dot(g, t.dot).show({ from: a, until: end, pose: { scale: ignite(a) } }),
);
return [{ t: a + IGNITE.half, f: 1318.5 }];

ts-sdk/examples/showcase/dot-stings/stings.ts:85

One program, many videos.

A new size, language or version is a new row of data.

Launch board Sample data

One board, three ads.

16:9 · YouTube

1:1 · Instagram

9:16 · Shorts

The lint is clean on all three. Edit the JSON and the video follows.

board.json the only input
{
"title": "Pre-orders by region",
"eyebrow": "Launch board",
"rounds": ["Week 1", "Week 2", "Week 3", "Week 4"],
"goal": 60000,
"entries": [
{ "name": "Americas", "values": [4200, 7900, 10400, 12100 ] },
{ "name": "Europe", "values": [3100, 7400, 11900, 15800] },
{ "name": "Nordics", "values": [2600, 4100, 5200, 6100] },

ts-sdk/examples/showcase/launch-board/data/board.json:1

Nine-up 19 s

Three languages, three shapes, one brief.

0 lint issues across nine ads

Ads for JT Cut in English, Slovenian and German.

brief.ts one language of three
de: {
lang: 'de',
locale: 'de-DE' ,
brand: 'JT Cut',
headline: 'Aus Ihren Fotos und Clips wird in wenigen Minuten ein Video',
how: 'So funktioniert es',
proof: ['Keine Schnittkenntnisse nötig', 'Text, Musik und Stimme hinzufügen', 'Die meisten Videos sind in 2 bis 5 Minuten fertig'],
offer: price('de-DE'),
per: 'pro Monat für Creator, 15 Videos in 1080p',
cta: 'Kostenlos starten auf jtcut.com',
},

ts-sdk/examples/showcase/nine-up/brief.ts:71

It says no before it renders.

Wrong wiring is a type error. Type too small for the screen is a lint issue, with the reason.

squiggle.mp4 1920 × 1080 · 60 s

Six mistakes in real SDK code. Every message is the compiler's or the lint's own.

diagnostics.json TypeScript 5.9.3, strict
  • scene · TS2551 Property 'intor' does not exist on type '{ readonly intro: Scene<"intro">; }'. Did you mean 'intro'?
  • filter · TS2345 Argument of type '"scale"' is not assignable to parameter of type 'EffectFilter'.
  • face · TS2820 Type '"inter-tite-bold"' is not assignable to type 'FaceKey | FaceMetrics'. Did you mean '"inter-tight-bold"'?
  • sorts · TS2345 Argument of type 'DetectionStream' is not assignable to parameter of type 'VideoStream'.
  • redact · TS2322 Type 'number' is not assignable to type 'undefined'.
  • floor · lint capHeight scene main: "Read on" is a support with 18 px caps, under the 26 px this display asks for

build/showcase/squiggle/diagnostics.json

A song with no drop

motion: dropEdit: the song has no drop between 0 s and 20 s

The planner will not invent a drop.

A typo in a scene name

film.scene.intor stops at the compiler. It never reaches a render.

The legibility floor

On a phone, headlines need 42 px caps and support lines 26 px.

A headline that picks its size

// The headline takes the largest of three cap heights whose block fits two lines.
const headline = text(brief.headline, {
type: ty.anton, capPx: [3, 2.4, 2].map((k) => cap(k, 'headline')), role: 'headline', color: c.ink,
wrap: { width: col.w, pitch: 3.75 * u, maxLines: 2 }, x: col.x, capTop: body,
});
comp.scene.hook.add(headline.show({ from: 0.3, enter }));

ts-sdk/examples/templates/ad.ts:97

The review sheet

reviewSheet() · cut-machine

Review sheet of cut-machine: the frame before, on and after each join with its number, and the verdict no lint

The match cut lands on frame 300. Top right, the verdict reads "no lint".

Sound runs on the same clock.

Captions, the music bed and the master share the picture's timeline.

Voice reel 15 s 1080 × 1920 Machine voice

A voice-over reel, captioned word by word.

40 of 43 words within 40 ms of the voice

The bed ducks under a machine voice from macOS say . Master at −14.0 LUFS.

stage.ts the bed ducks
const key = g.openAudio('voice-reel/vo.wav');
const vo = fanout ? key : g.source('vo.wav')[1];
const bed = g.openAudio('voice-reel/bed.wav').volume({ volume: 0.55 });
const ducked = ducking(bed, key, DUCK) ;
const mix = audioMix([ducked, vo], { normalize: false, duration: 'first' }).atrim({ end: SECONDS });
return profile === 'none' ? mix : masterAudio(mix, profile);

ts-sdk/examples/showcase/voice-reel/stage.ts:90

The drop edit's song is code too.

The tempo, 128, is the only literal time that places an event. The analysis hears 128.1 BPM.

song.ts a song from one tempo
export const BPM = 128 ;
export const BEAT = 60 / BPM;
export const BAR = 4 * BEAT;
/** The bar the kick returns on, counted from 1. */
export const DROP_BAR = 4;
export const DROP = (DROP_BAR - 1) * BAR;
/** Where the breakdown starts: one bar before the drop. */
export const BREAK = DROP - BAR;
/** Bars in the song. */
export const BARS = 12;
export const LENGTH = BARS * BAR;

ts-sdk/examples/showcase/drop-measured/song.ts:17

Same program, same pictures.

Broadcast credits, 3,200 names, built on two machines and rendered three times.

Receipt · credits-farm · 1,000 frames synthesized names
Whole, one render
100.30 s
8 stretches, 32 cores
38.62 s
joined vs whole
1,000 of 1,000 frame checksums match
Mac vs Linux build
the same job file, byte for byte
three fresh renders
identical pictures, identical sound
container
a few bytes differ between files

What you get

The package
@jtdigital/renderbox-sdk on npm, Apache-2.0, version 0.15.1. Free to write against.
The motion layer
Scenes, clips, effects, masks, transitions and text that sizes itself.
Fonts
43 families, pinned, the same on every render host.

The limits, plainly

  • The SDK writes a description of the job. The Everyframe engine renders it.
  • Rendering needs access. It is not self-serve.
  • A managed HTTP client is not published.
  • None of these videos came from a generative model.
Our wordmark
const efWord = (): readonly (string | Run)[] => ['Everyfr', run('A', { turn: 180 , color: c.tally }), 'me'];

ts-sdk/examples/stand-a/stand-a.ts:59

Start with the code.

  1. Install

    Types and lint run locally.

    npm install @jtdigital/renderbox-sdk
  2. Get render access

    Jobs run on the Everyframe platform.

  3. Talk it through

    Nothing to prepare, nothing to sign.

    Book a 15-minute call

Let's get you set up

The SDK is free to build with. Running jobs happens on the Everyframe platform: email us and we'll get you access, quotas and onboarding.

Email us

toni.k.turk@jtdigital.si jakob.raisp@jtdigital.si