eng-remotion-explainer-video-generator

Category: Design Risk: High risk ★ 3.9 · Rating 3.9/5 (8) sboghossian/mini-claude-for-legal MIT

Rating is derived from the repo's GitHub stars and shown for reference.

shell_executionnetwork_access

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 renderer
  • REMOTION_AWS_REGION
  • VIDEO_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: embed per text block, not per scene.

Rendering pipeline

  1. LLM generates VideoScript JSON (streamed as structured output).
  2. Validate JSON against the VideoScript schema.
  3. Call renderMedia() (local) or renderMediaOnLambda() with the script as inputProps.
  4. Upload rendered MP4 to R2 bucket.
  5. Return signed URL (24-hour TTL) to client.
  6. Store { userId, docId, videoUrl, script, createdAt } in explainer_videos table.

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
  • [[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