Product & promo videos, written as React code and rendered locally to mp4.
A reusable Remotion engine — one parametrized base, many brands, RU/EN, 9:16 / 1:1 / 16:9.
One toolkit, wildly different looks — a floating-phone edtech promo, a crypto bridge, an AI-cinematic creative. Each is a theme preset + a data-driven scene map on the shared primitives.
Write the video in React, get a deterministic mp4. The whole storyline is data (a zod-validated scene map you can override with a JSON file), the look is design tokens, and the motion is a library of premium primitives — so a new brand, language, or aspect ratio is a preset, not a rewrite. Rendering is 100% local (Remotion drives headless Chromium; no API key needed to render).
Two things live here. The engine (
src/lib,src/theme,src/device,src/v2) is the reusable product — that's what you build on. The example builds (src/lumo,src/jumper,src/shotik,src/bybit,src/creative) are finished videos that show what the engine produces and how to build your own.src/lumois the reference example the "Author a promo" section walks through.
Not just phones: 16:9 laptop frames and real 3D (@remotion/three + a Blender→GLB pipeline). An AI-asset layer (top right) generates footage and composites a Remotion UI on top.
- Author without touching JSX — a promo is a scene map (typography beats, in-device
navigation, floats, brand copy) validated by a zod schema. Edit the map, tweak props live in
Studio, or pass
--props=client.jsonto re-skin a whole video. - Multi-brand by construction — a floating-phone promo = theme preset + product screens +
scene map + a thin wrapper. The
src/lumoexample proves the pattern;jumper/shotik/bybit/creativeshow the engine's range (crypto, 16:9 desktop, 3D, AI-hybrid). - A real test pyramid (L0–L6) for a medium that's notoriously hard to test — geometry/orbit
invariants reject a clipping 3D orbit arithmetically before rendering; a determinism guard
greps for
Math.random; browser-mode layout tests catch "child escaped its container"; a pixel self-check catches "stills pass, the video is broken". Seedocs/TESTING.md. - Hard-won craft, encoded — spring entrances, real motion blur on camera moves, film grain, a continuous camera rig, and a running log of headless-render footguns (collapsible below).
- Self-contained — fonts, sound (a DSP-synthesized SFX set), and assets ship in the repo; the core render needs no external service.
npm install
npm run dev # Remotion Studio — live preview, edit props in the UI
npm run lint # eslint + tsc (strict)
npm test # vitest — geometry/orbit invariants, anim math, determinism guard, scene-graphRender (run from the project dir so public/ assets resolve):
npx remotion render LumoPromo out/promo.mp4 # the reference floating-phone promo
npx remotion render LumoPromo out/client.mp4 --props=client.json # re-skin WITHOUT code
npm run stills LumoPromo 90,300,540 out/check # batch stills: bundle once, ~1s/frameThe storyline is data: src/lumo/lumo.map.ts (LUMO_DEFAULTS), validated by
src/v2/promoSchema.ts (zod).
beats: // big typography beats: { title, sub, accentWord, from, to, y, size }
zoomBeat // the beat shown over the zoomed-in display (drives the top scrim)
nav: // in-device navigation: { at, kind: "push" | "tab" | "flip", screen }
floats: // certificate / provider-chip windows
brand: // wordmark, CTA label, end-title linesEdit the map, tweak props in Studio, or pass a JSON override (--props=file.json — top-level keys
are replaced wholesale). Camera art (zoom/pull-back keyframes, blur windows) lives in the engine.
Example builds — finished videos; each demonstrates a different engine capability.
| id | what | format |
|---|---|---|
LumoPromo |
reference example — dark stage, floating phone, data-driven (schema + --props) |
9:16 · 1320f / 44s |
JumperPromo |
cross-chain bridge promo, authored from a URL, TransitionSeries sequencing |
9:16 · 1080f / 36s |
ShotikPromo |
16:9 preset, LaptopFrame, MagicMove chain as the transition language |
16:9 · 1140f / 38s |
LevelUpCreative · …V2 · …Morph |
AI-generated footage (ai-gen layer) composited under a Remotion UI |
9:16 · 240 / 310 / 420f |
BybitCardGif |
seamless 10s loop — 3D card + orbiting service tiles | 1:1 · 300f / 10s |
HeroManifest |
showreel hero / author signature | 1:1 · 300f / 10s |
Building blocks & sandboxes
| id | what |
|---|---|
Bybit* · ShotikDesktopStill |
card faces / service tile / desktop still — rendered standalone |
LibSandbox · FxSandbox · InteractionSandbox · DataSandbox · SoundSandbox |
dev benches for the src/lib primitives (grain/glow/blur · magic-move · cursor/tap · counters · sound) |
ThreeSandbox · GltfSandbox · Showcase3D |
@remotion/three pipeline test stands (16:9) |
<MotionBlur shutterAngle samples>— camera-style blur; wrap only moving content during motion windows (cost ×samples)<Grain opacity frequency blend>— living film grain;overlayfor dark bases,multiplyfor light<Glow>·<Parallax>— silhouette glow + organic simplex-noise depth drift<MagicMove from to a b spin>— Keynote-style morph: A flies/spins/cross-fades into B<Counter>·<SplitCompare>·<BarStat>— data-stat primitives (roll-up numbers, before/after wipe, growing bars)<Cursor>·<TapPulse>·<Spotlight>— UI-demo interaction layer (a moving cursor + synced tap ripple)- sound:
<Music>·<Sfx>+ aduck()sidechain, over a self-contained synthesized SFX set (npm run gen-sfx) - anim helpers:
SPRINGpresets,springWindow,window01,kf,stagger01,EASE* - tokens:
themefromsrc/theme— all visual constants live insrc/theme/tokens.ts
npm test runs pure-logic units (no render): camera/orbit geometry invariants, anim-window math,
a determinism guard, and a @react-three/test-renderer scene-graph smoke test. On top of that:
npm run check-render <Comp>— renders sample frames and runs pixel heuristics (content / motion / loop-seam / state-race) to catch "stills pass, the video is broken".npm run test:layout— real-Chromium layout tests (getBoundingClientRect) that catch a child overflowing its container — a class jsdom can't see.npm run check-assets— glTF validation (Khronos) + metadata regression onpublic/models/*.glb.npm run check-audio— integrated-LUFS + true-peak gate for voiced comps.
The full L0–L6 strategy, the footgun→layer map, and CI tiering live in docs/TESTING.md.
- Engine (the reusable product) —
theme/(design tokens, single source) ·lib/(premium primitives + dev sandboxes) ·v2/(floating-phone engine: camera rig,ScreenFlow,TypoBeat, floats,promoSchema.ts) ·device/(PhoneFrame/LaptopFrame) ·components/(BottomNav) - Example builds (worked examples on the engine) —
lumo/(reference),jumper/,shotik/,bybit/,creative/(Level-Up) Root.tsx— the composition registry ·public/— assets viastaticFile()·docs/TESTING.md— the test-pyramid reference
Remotion 4.0.484 · React 19 · TypeScript (strict) · zod 4.3.6 · @remotion/{transitions, media, motion-blur, noise, paths, three, google-fonts} · three / @react-three · vitest (node + browser). Master format 9:16 · 1080×1920 · 30fps.
Gotchas — headless-render footguns, discovered empirically
- Run
remotion render|stillfrom the project dir, elsepublic/assets 404. - No
Math.random()— breaks render determinism; parametrize byframe/index, use seedednoise2Dfor organic motion. mix-blend-modeonly works on fully opaque content — any pixel alpha < 1 silently disables blending (encode strength as color contrast around the blend's identity value).overlayblend is ≈ invisible on near-white backgrounds — usemultiplyon light bases.- Integer
feTurbulencebaseFrequency(e.g. 1.0) samples the noise lattice at its zero nodes → flat output; use 0.8. overflow:hidden+border-radiuson a parent does not clip atransform-ed child (3Dperspective/rotateY) — any transform-animated layer inside a clip container needs its ownborder-radius.- Flex child without
minWidth:0won't shrink below content width → a sibling escapes the container (caught by the browser-mode layout test). - A multi-scene video is a
TransitionSeries, not one component with hand-tuned windows (which silently overlap at the seam). - 3D orbits: check the whole cycle by sequential render, not lucky stills; a satellite orbit radius must clear the object's swept radius, or it clips the object edge-on.
- The first painted frame of a 3D render is a GL warm-up frame — pad +2 frames and trim in ffmpeg for seamless loops.
- zod must be exactly 4.3.6 with Remotion 4.0.484 (version-mismatch warning otherwise).
- Refactors are validated Δ=0: stills at fixed frames before/after must be MD5-identical.
Brand-kit injection (per-client theme JSON) · 15s / 6s cutdowns · Remotion Lambda (cloud render) · @remotion/captions subtitles · live music bed (drop-in over the synthesized SFX set).
Built by Egor Pestov — AI / code-driven motion design. Example builds use fictional brands and original copy; third-party logos (OpenAI, Google, etc.) belong to their owners. Licensed under MIT.




