Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 

Repository files navigation

name remotion-video
description Create/edit branded videos with Remotion — Liquid Glass overlay, subtitles, key phrases, zooms, CTA. Reusable composition architecture.

Remotion Video Editing

Target: macOS with local Chrome. Project structure supports multiple videos via config-per-video pattern.

Quick start (new video)

  1. Drop .mp4 into public/videos/, CTA image into public/images/
  2. Create src/compositions/<id>.ts following the VideoCompositionConfig type
  3. Import and push to compositions array in src/Root.tsx
  4. Render: PUPPETEER_EXECUTABLE_PATH="/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" npx remotion render <id> out/<id>.mp4 --browser-executable="/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" --concurrency=1 --crf=16

Critical pitfalls

  • concurrency=1 required — parallel rendering opens Chrome tabs on sequential ports (3000, 3001, …) that fail. Always use --concurrency=1.
  • <Video> is visual-only during render — audio must be a separate <Audio> component using the same source file. Without it, output is silent.
  • staticFile() for all public assetsstaticFile("/videos/x.mp4"), staticFile("/images/x.png"). No raw string paths.
  • Google Fonts@import url() won't work in headless Chrome. Use @remotion/google-fonts (loadFont) at module init. Expose the loaded fontFamily string in theme tokens.
  • TypeScript ~5.5 only — TS 7+ breaks the Remotion bundler (typescript.sys.readFile undefined). Install typescript@~5.5.0.
  • Browser executable — Always pass --browser-executable="/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" or set PUPPETEER_EXECUTABLE_PATH. The auto-downloaded headless shell is 93MB and slow.
  • OffthreadVideo — Avoid. Causes delayRender timeouts on macOS 14. Use <Video> + <Audio> instead.
  • Source video graphics — Overlays sit on top of whatever the source MP4 contains. If the original video already has text boxes, icons, or lower-thirds, they WILL compete visually with subtitles and key phrases. Prefer clean-background talking-head footage. Test with a frame extraction before committing to a full render.
  • CTA image per video — Each composition needs its own CTA image in public/images/. Reusing another video's CTA image causes branding mismatch. Create a custom screenshot or graphic for every video's call to action.
  • npm run build is a full render — It renders LegalTech3 from scratch (~3 min). For verification after code changes, use npx tsc --noEmit (typecheck) plus a short render: remotion render <id> out/verify.mp4 --frames=0-5 --concurrency=1. Only run npm run build when you want the final LegalTech3 output.

Composition architecture

All components are generic — they receive data via props:

Component Props Role
VideoPlayer config: VideoCompositionConfig Wires everything together
LiquidGlassOverlay children Dark bg #040811 + corner blobs (no frame)
ZoomController moments: ZoomMoment[], children Subtle scale transforms (1.03–1.05)
Subtitles segments: SubtitleSegment[] Bottom-aligned Inter 300, glass pill bg
KeyPhrase phrases: KeyPhraseData[] Playfair italic 700, 55% from top, spring anim
CTAOverlay cta: CTASegment Image + bigText + subtext, spring entrance

Transcript timestamps

For accurate subtitle sync, transcribe with mlx-whisper (Apple Silicon native):

pip3 install mlx-whisper
python3 -c "
import mlx_whisper
r = mlx_whisper.transcribe('audio.wav', language='es')
for s in r['segments']:
    print(f\"[{s['start']:.1f}-{s['end']:.1f}] {s['text']}\")
"

Then correct the text manually and pair with the accurate timestamps. Break long segments into 2-line max subtitle chunks.

Quality

--crf=16 for h264 gives ~2000kbps at 360x640 — good balance of quality/size. --image-format=jpeg for JPEG frame encoding (faster than PNG, same visual quality for video).

Adding a new video — config template

See references/config-template.ts for the VideoCompositionConfig shape.


End-to-End Reproducible Workflow (LLM-Agnostic)

This section is designed to be given verbatim to ANY LLM (ChatGPT, Claude, Gemini, DeepSeek, etc.) as a self-contained prompt chain. Each step builds on the previous one.

The system is a Remotion (React 19 + TypeScript 5.5) pipeline that produces branded 360×640 vertical videos with: Liquid Glass background, synced subtitles, animated key phrases, subtle zooms, and a final CTA overlay. Components are generic — all data lives in a per-video config.


PHASE 1: Transcription

Goal: Get a timestamped transcript of the source video.

Prompt to the LLM:

Transcribe this video's audio using mlx-whisper or whisper. Language: Spanish (or detect). Command:

pip3 install mlx-whisper
python3 -c "
import mlx_whisper
r = mlx_whisper.transcribe('audio.wav', language='es')
for s in r['segments']:
    print(f\"[{s['start']:.1f}-{s['end']:.1f}] {s['text']}\")
"

Output format: [start-end] raw transcribed text

Deliverable: Raw transcript file (e.g., assets/transcript_raw.txt).

Reality check: Whisper makes errors, especially with technical terms and non-English languages. ALWAYS do Phase 2 correction.


PHASE 2: Transcript Correction

Goal: Fix Whisper errors while preserving timestamps. Split into subtitle-friendly chunks (max ~2 lines per segment, ~2-4 seconds each).

Prompt to the LLM:

Here is the raw Whisper transcript with timestamps. Do two things:

  1. CORRECT the text: fix transcription errors, restore technical terms, fix grammar. DO NOT modify timestamps.

  2. SPLIT long segments into subtitle-friendly chunks of 2-4 seconds each. Each chunk should be 1-2 spoken lines max. If you split a segment, interpolate the timestamps proportionally.

Context about the video topic: [insert 1-2 sentence description of what the video is about]

Output format:

[start-end] corrected subtitle text

Raw transcript: [paste raw transcript here]

Deliverable: Cleaned transcript with 20-40 subtitle segments, ready for the config.

Pacing rule of thumb:

  • Spanish: ~10-13 chars/sec spoken
  • English: ~12-15 chars/sec spoken
  • Each subtitle: 2-4 seconds, 20-50 characters

PHASE 3: Identify Key Phrases

Goal: Find 3-5 conceptual anchors in the video that deserve visual emphasis. These appear as large Playfair Display italic overlays at 55% screen height.

Prompt to the LLM:

Analyze this corrected transcript from a [duration]s video about [topic].

Identify 3-5 KEY CONCEPTS that are the main takeaways. For each one:

  • text: THE CONCEPT IN UPPERCASE (1-4 words, punchy)
  • subtext: a brief explanation (4-8 words, Spanish or video language)
  • start: when the concept is first introduced (seconds)
  • end: when the speaker moves to the next concept (seconds)

Rules:

  • Concepts should be spread across the video, not clustered
  • start should be ~0.5-1s AFTER the speaker begins introducing it
  • end should align with subtitle segment boundaries
  • Don't overlap key phrases (end of one < start of next)
  • The last key phrase should end before the CTA starts (last ~6-8s)

Transcript: [paste corrected transcript]

Deliverable: 3-5 KeyPhraseData entries with text, subtext, start, end.


PHASE 4: Define Zooms

Goal: Add subtle scale animations (1.03-1.05x) that match the key phrases to create visual dynamism without being distracting.

Prompt to the LLM:

Using these key phrase segments and the video duration of [N]s, define zoom moments.

Rules:

  • Each zoom should cover the same range as its corresponding key phrase
  • Scale values: 1.03 for short segments, 1.04 for longer/more important ones
  • Between key phrases, zoom returns to 1.0 (handled automatically by ZoomController)
  • Last segment (CTA area, last ~10s) should be scale 1.0 (no zoom)
  • Add a short intro zoom (1.03, first 5-10s) if the video has a hook

Key phrases: [paste key phrases with timestamps]

Output format:

{ start: X, end: Y, scale: Z.Z }

Deliverable: 4-6 ZoomMoment entries covering the timeline.


PHASE 5: Define CTA (Call to Action)

Goal: Final overlay with image + text in the last ~6-8 seconds.

Prompt to the LLM:

Define the CTA for the last [N] seconds of the video (start at [CTA_START], end at [VIDEO_END]).

Provide:

  • image: filename in public/images/ (e.g., "cta-topic.png")
  • bigText: 1-2 words in UPPERCASE, the core action/topic
  • subtext: 1 short sentence telling the viewer what to do

The speaker's final message is: [paste the last subtitle segment text]

Deliverable: One CTASegment.


PHASE 6: Create the Composition Config

Goal: Assemble everything into a VideoCompositionConfig file.

Prompt to the LLM:

Create the file src/compositions/[video-id].ts exporting a VideoCompositionConfig:

import type { VideoCompositionConfig } from "../types";

export const [videoId]: VideoCompositionConfig = {
  id: "[VideoId]",
  videoSrc: "[filename].mp4",      // in public/videos/
  durationInSeconds: [exact duration],
  subtitles: [ /* all segments from Phase 2 */ ],
  keyPhrases: [ /* all entries from Phase 3 */ ],
  zooms: [ /* all entries from Phase 4 */ ],
  cta: { /* from Phase 5 */ },
};

Use these exact data: [paste all data from previous phases]

Deliverable: Complete config file.


PHASE 7: Register and Render

Prompt to the LLM:

  1. Import the new config in src/Root.tsx:

    import { [videoId] } from "./compositions/[video-id]";

    Add it to the compositions array.

  2. Place the CTA image in public/images/.

  3. Render:

    PUPPETEER_EXECUTABLE_PATH="/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" \
    npx remotion render [VideoId] out/[video-id]-branded.mp4 \
      --browser-executable="/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" \
      --codec=h264 --image-format=jpeg --crf=16 --concurrency=1

Master Prompt (All-in-One)

If you want to hand the ENTIRE workflow to an LLM in one shot, use this:

I have a vertical video (360×640, 9:16) at public/videos/[name].mp4. Its duration is [N] seconds. The topic is: [1-2 sentence description]. The video language is [Spanish/English].

Using the Remotion overlay system (Liquid Glass design: #040811 background, Inter 300 subtitles, Playfair Display 700 italic key phrases, subtle 1.03-1.04x zooms, CTA overlay at the end), I need you to produce a complete branded version.

Follow this exact pipeline:

  1. Transcribe the audio with mlx-whisper and save raw transcript
  2. Correct transcription errors + split into 2-4s subtitle chunks (keep timestamps)
  3. Identify 3-5 key concepts → key phrases with text + subtext + timestamps
  4. Define zoom moments matching key phrases (1.03-1.04x)
  5. Define CTA for last ~6s (image + bigText + subtext)
  6. Create src/compositions/[name].ts with the full VideoCompositionConfig
  7. Register in Root.tsx and render with --concurrency=1 --crf=16

Project details:

  • TypeScript ~5.5.0 (NOT 7+)
  • React 19 + Remotion 4
  • Dimensions: 360×640, 30fps
  • Chrome executable: /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
  • All public/ assets must use staticFile()
  • Video and Audio are separate components
  • @remotion/google-fonts for fonts (Inter + Playfair Display), NOT CSS @import
  • The video source is [path to mp4]

Start now and work through all 7 phases.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages