eng-remotion-explainer-video-generator
Rating is derived from the repo's GitHub stars and shown for reference.
name: eng-remotion-explainer-video-generator
description: Use when building or configuring the Remotion-based pipeline that converts legal explanations, clause summaries, or onboarding content into short animated explainer videos. Covers component structure, data-driven animation patterns, rendering pipeline, and output format considerations for a legal AI product.
license: MIT
metadata:
id: eng.remotion-explainer-video-generator
category: eng
jurisdictions: [multi]
priority: P2
intent: [eng, remotion, video, explainer, animation]
related: [eng-supabase-edge-functions-patterns, eng-streaming-response-rules-mobile, eng-supabase-prerender-blog-pipeline]
source: Louis — HAQQ Legal AI (github.com/sboghossian/mini-claude-for-legal)
version: "1.0"
Remotion Explainer Video Generator
What it does
The Remotion explainer video generator turns AI-produced legal summaries and clause explanations into short (30–90 second) animated videos. The primary use case is converting complex legal concepts — "what does this indemnity clause actually mean?" — into accessible visual content for non-lawyer clients. Secondary use cases include onboarding walkthroughs and legal education modules.
Remotion renders React components to MP4/WebM server-side, allowing the output to be driven by structured JSON data produced by the LLM (text, bullet points, jurisdiction flags, highlight phrases).
Setup / auth
npm install remotion @remotion/renderer @remotion/bundler
For server-side rendering in a Supabase Edge Function environment, use @remotion/lambda or run the renderer in a Fly.io/Railway container (Deno/Edge does not support FFmpeg natively).
Environment variables:
REMOTION_LAMBDA_FUNCTION_NAME— if using Lambda rendererREMOTION_AWS_REGIONVIDEO_OUTPUT_BUCKET— R2 or S3 bucket for rendered MP4 files
Capabilities
Data schema — what the LLM should produce
The LLM generates a VideoScript JSON object; the Remotion composition consumes it:
interface VideoScript {
title: string; // max 60 chars
jurisdiction?: string; // "UAE", "KSA", "Lebanon", "DIFC", etc.
durationSeconds: number; // 30 | 60 | 90
scenes: Scene[];
}
interface Scene {
id: string;
type: "title" | "bullet_list" | "callout" | "comparison" | "quote";
durationFrames: number; // at 30fps: 30s = 900 frames
content: {
heading?: string;
body?: string;
bullets?: string[];
leftLabel?: string; // for comparison scenes
rightLabel?: string;
highlightPhrase?: string; // phrase to animate with emphasis
quoteText?: string;
quoteSource?: string; // "Article 7, DIFC Contract Law"
};
language: "ar" | "en" | "fr";
rtl: boolean; // true for Arabic
}
Remotion composition structure
src/remotion/
Root.tsx — composition registry
LegalExplainer.tsx — main composition, maps scenes to components
scenes/
TitleScene.tsx
BulletListScene.tsx
CalloutScene.tsx
ComparisonScene.tsx
QuoteScene.tsx
shared/
AnimatedText.tsx — spring-animated word-by-word reveal
JurisdictionBadge.tsx
RTLWrapper.tsx — wraps scenes in dir="rtl" for Arabic
Arabic / RTL considerations
- Wrap every Arabic scene in a
dir="rtl"container; Remotion respects CSS direction. - Use a licensed Arabic font (e.g., IBM Plex Arabic, Noto Sans Arabic) embedded in the composition. System fonts are not available in the Lambda renderer.
- Arabic text should use line-height: 1.8 minimum; Arabic legal text with diacritics needs more vertical space than English.
- For mixed AR/EN scenes, use
unicode-bidi: embedper text block, not per scene.
Rendering pipeline
- LLM generates
VideoScriptJSON (streamed as structured output). - Validate JSON against the
VideoScriptschema. - Call
renderMedia()(local) orrenderMediaOnLambda()with the script asinputProps. - Upload rendered MP4 to R2 bucket.
- Return signed URL (24-hour TTL) to client.
- Store
{ userId, docId, videoUrl, script, createdAt }inexplainer_videostable.
Usage patterns
Triggering from the chat interface
User asks: "Can you explain this indemnity clause as a short video?"
// In the LLM response handler
const script = await generateVideoScript(clauseText, { jurisdiction: "DIFC", lang: "en" });
const videoUrl = await renderAndStore(script, userId);
return { type: "video", url: videoUrl, duration: script.durationSeconds };
Batch generation for onboarding
const onboardingTopics = ["what-is-an-nda", "termination-rights-uae", "difc-vs-adgm"];
for (const topic of onboardingTopics) {
const script = await generateOnboardingScript(topic);
await renderAndStore(script, "system");
}
Permissions & safety
- Never include PII (party names, amounts, account numbers) in a video script. The video may be shared; the summary should be generic.
- Do not render real client contract text as a quote scene — paraphrase instead.
- Videos are stored per user; R2 object key must include
tenantId/userId/prefix to prevent enumeration. - Legal disclaimers: the last scene of every explainer should include a static disclaimer: "This video is for informational purposes only and does not constitute legal advice."
- WCAG: all text must meet 4.5:1 contrast ratio on the background color. Subtitles/captions are required for accessibility.
Failure modes
| Failure | Impact | Mitigation |
|---|---|---|
| LLM produces invalid VideoScript JSON | Render fails | Validate with Zod schema; fallback to text-only response |
| Arabic font not bundled | Garbled text in rendered video | Bundle fonts at build time; test Arabic scene in CI |
| Render timeout (Lambda 15 min limit) | 90s video not rendered | Cap durationSeconds at 90; use async render + polling |
| R2 upload failure | URL never returned | Retry with exponential backoff; return graceful error to user |
| Over-long scene text | Text overflows frame | Truncate at 120 chars per line; wrap at word boundaries |
Related skills
- [[eng-supabase-edge-functions-patterns]] — deployment pattern for the render-trigger function
- [[eng-streaming-response-rules-mobile]] — mobile display considerations for the returned video URL
- [[eng-supabase-prerender-blog-pipeline]] — similar static asset generation pattern for blog content