RepoStudio Docs

Documentation

RepoStudio turns any public GitHub repository into a polished 1080p product demo video — automatically. Paste a URL, and the pipeline ingests the code, writes a script grounded in real imports, synthesizes narration, and composites a cinematic MP4.

Input
GitHub URL
Output
1080p MP4
Time
~2 minutes

Stack

LayerTechnology
FrameworkNext.js 16 App Router
Video renderRemotion 4 — React → MP4 via headless Chrome
AuthNextAuth v5 — GitHub OAuth
DatabaseSupabase (video_jobs table + video-exports bucket)
AI scriptGemini 2.5 Pro → 2.5 Flash → 2.0 Flash → 1.5 Pro; Nemotron fallback
TTS + captionsElevenLabs /v1/text-to-speech/{id}/with-timestamps
ScreenshotsPlaywright headless Chromium — 6-frame interaction journey
Brand detectionCSS custom-property + Tailwind config parser
UIFramer Motion · Apple Liquid Glass design system

How It Works

Four stages run in sequence every time you click "Create Video".

Ingest

READMEFetched via GitHub API or Firecrawl (if key present). Used for demo URL extraction and LLM context.
File treeFull recursive tree via GitHub Tree API (?recursive=1). Top 8 largest source files fetched at 5,000 chars each.
Source files.ts / .tsx / .js / .jsx / .py only. Excludes configs, tests, generated files, node_modules, dist.
Brand colorsParses globals.css (--primary, --accent, --background) and tailwind.config.* for hex colors.
Screenshots6-frame Playwright journey: hero at rest → CTA hover → 650px scroll → 1,400px → 2,200px → return to top. Falls back to the GitHub repo page if no live demo URL is found.

Script

Model orderGemini 2.5 Pro → 2.5 Flash → 2.0 Flash → 1.5 Pro (all via Google AI). Falls back to NVIDIA Nemotron 49B if no Gemini key.
Prompt"World-class Product Marketer" system prompt. Reads import patterns and maps them to commercial claims — supabase → "Real-time Sync", framer-motion → "Fluid Animations". Nothing invented.
OutputStructured JSON: 4 scenes (hook / feature_1 / feature_2 / outro), each with text, badge, duration, start_time. Total duration 25–55 s, LLM-determined by repo complexity.
FallbackIf all AI providers fail, buildFallbackScenes() generates generic marketing copy so the pipeline never blocks.

Audio

ProviderElevenLabs /v1/text-to-speech/{voice_id}/with-timestamps
CaptionsCharacter-level word alignment timestamps — real karaoke sync, not evenly-distributed fakes.
FallbackIf no ElevenLabs key, audio is skipped and captions use generated timestamps spread across scene durations.

Render

EngineRemotion 4 — React components rendered frame-by-frame to PNG, encoded to H.264 MP4 via headless Chrome.
BackgroundFull-bleed 1920×1080 screenshot at 108% size. Ken Burns zoom varies direction per scene: hook zooms in, feature_1 zooms out, feature_2 zooms in from shifted origin.
Shot cycling2 screenshots per scene (SCENE_PAIR map). Midpoint crossfade (14-frame) between the two shots within each scene.
Transitions18-frame (0.6 s) cross-dissolve between all scenes. All scenes rendered simultaneously; opacity calculated per frame.
VignetteCinematic: heavy at top/bottom edges, light in the UI center where the product shows.
OverlaysFeature badge pill (lower-left), brand watermark pill (top-right), narration lower-third, optional karaoke captions.
Background musicOptional — ducks to 8% during speech with 0.35 s fade, rises to 28% in gaps. 1 s fade-in / 1.5 s fade-out envelope.
StorageMP4 uploaded to Supabase video-exports bucket; public URL stored in video_jobs.video_url.

Quick Start

1. Clone and install

git clone https://github.com/JayantDeveloper/repostudio
cd repostudio
npm install
npx playwright install chromium

2. Configure environment variables

Create a .env.local file in the project root. See the Environment Variables section for the full reference.

# Minimum viable setup (no audio, no persistent DB)
AUTH_SECRET=                  # openssl rand -base64 32
GITHUB_ID=                    # GitHub OAuth App client ID
GITHUB_SECRET=                # GitHub OAuth App client secret
GEMINI_API_KEY=               # Google AI Studio — free tier works

3. Create a GitHub OAuth App

Go to GitHub → Settings → Developer settings → OAuth Apps → New OAuth App.

FieldValue
Homepage URLhttp://localhost:3000 (or your Vercel URL)
Callback URLhttp://localhost:3000/api/auth/callback/github

Copy the Client ID and generate a Client Secret — paste them into GITHUB_ID and GITHUB_SECRET.

4. Apply the Supabase migration (optional but recommended)

Without Supabase, video jobs are stored in server memory and lost on restart. To persist them, run this SQL in your Supabase Dashboard → SQL Editor:

create extension if not exists pgcrypto;

create table if not exists public.video_jobs (
  id         uuid primary key default gen_random_uuid(),
  user_id    text not null,
  repo_url   text not null,
  status     text not null default 'ready',
  scenes     jsonb not null default '[]'::jsonb,
  video_url  text,
  created_at timestamptz not null default now(),
  updated_at timestamptz not null default now(),
  constraint video_jobs_status_check check (
    status in ('ingesting','scripting','audio','face',
               'ready','rendering','done','error')
  )
);

create index if not exists video_jobs_user_updated
  on public.video_jobs (user_id, updated_at desc);

alter table public.video_jobs enable row level security;

create policy if not exists "users manage own jobs"
  on public.video_jobs
  using  (auth.uid()::text = user_id)
  with check (auth.uid()::text = user_id);

insert into storage.buckets (id, name, public)
values ('video-exports', 'video-exports', true)
on conflict (id) do nothing;

5. Run

npm run dev
# → http://localhost:3000
The app works end-to-end with just GEMINI_API_KEY + GitHub OAuth. ElevenLabs and Supabase are optional — the pipeline degrades gracefully without them.

Environment Variables

Auth (required)

VariableDescription
AUTH_SECRETRandom secret for NextAuth session encryption. Generate: openssl rand -base64 32
GITHUB_IDGitHub OAuth App client ID
GITHUB_SECRETGitHub OAuth App client secret

Supabase (optional — enables persistent storage)

VariableDescription
NEXT_PUBLIC_SUPABASE_URLYour project URL: https://xxx.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEYPublic anon key — safe to expose in browser
SUPABASE_SERVICE_ROLE_KEYService role key — server-only, bypasses RLS for Storage uploads

AI — script generation (at least one required)

VariableDescriptionPriority
GEMINI_API_KEYGoogle AI Studio key. Leads the model chain: 2.5 Pro → 2.5 Flash → 2.0 Flash → 1.5 Pro. Free tier available.1st
NVIDIA_NIM_API_KEYNVIDIA NIM key for Llama-3.3-Nemotron-Super-49B. Used as fallback if Gemini is unavailable.2nd
NVIDIA_NIM_BASE_URLOverride the NIM endpoint. Defaults to https://integrate.api.nvidia.com/v1optional

TTS + captions (optional)

VariableDescription
ELEVENLABS_API_KEYEnables real narration audio and character-level word timestamps for karaoke captions. Without this, the video renders silently with generated timestamps.

Utilities (optional)

VariableDescription
GITHUB_TOKENPersonal access token. Raises GitHub API rate limit from 60 → 5,000 requests/hour. Useful in production.
FIRECRAWL_API_KEYIf present, fetches README via Firecrawl instead of the GitHub API — better Markdown extraction for complex READMEs.

Background music (optional)

VariableDescription
NEXT_PUBLIC_MUSIC_CINEMATIC_URLHTTPS URL to a royalty-free instrumental MP3 for the Cinematic mood preset (🎬)
NEXT_PUBLIC_MUSIC_UPBEAT_URLHTTPS URL for the Upbeat mood preset (⚡)
NEXT_PUBLIC_MUSIC_MINIMAL_URLHTTPS URL for the Minimal mood preset (🌊)
NEXT_PUBLIC_MUSIC_HYPE_URLHTTPS URL for the Hype mood preset (🔥)

Background Music

RepoStudio supports instrumental background music that automatically ducks under the narrator's voice. Music is opt-in — if no mood is selected in the editor, the video renders without music.

Mood presets

EmojiMoodCharacter
🎬CinematicEpic, orchestral — builds tension and drama
UpbeatElectronic, energetic — forward-moving pulse
🌊MinimalAmbient, calm — focused and unobtrusive
🔥HypeHigh-energy, punchy — big drops

Adding tracks

Drop royalty-free MP3 files (no vocals) into public/music/:

public/music/
  cinematic.mp3   # 🎬 Cinematic
  upbeat.mp3      # ⚡ Upbeat
  minimal.mp3     # 🌊 Minimal
  hype.mp3        # 🔥 Hype

Or set the NEXT_PUBLIC_MUSIC_*_URL environment variables to host tracks externally (Supabase Storage, CDN, etc.).

i
Good sources for royalty-free instrumentals: Pixabay Audio, Freepd.com, Free Music Archive (CC0 / CC-BY). Avoid anything with vocals — the ducking algorithm uses word timestamps and will still lower the music, but vocals on both tracks sound bad.

How ducking works

StateMusic volume
During narration (±0.15 s buffer)8%
Fading out of speech (0–0.35 s gap)8% → 28% linear
Between words (silence)28%
First 30 frames (1 s)0% → full (fade-in envelope)
Last 45 frames (1.5 s)full → 0% (fade-out envelope)

Video Output Spec

PropertyValue
Resolution1920 × 1080 (1080p)
Frame rate30 fps
Duration25–55 s — LLM-determined by repo complexity
Max duration60 s hard cap (validated before render)
CodecH.264 via headless Chrome (Remotion)
ContainerMP4
Scene transitions18-frame (0.6 s) cross-dissolve — all scenes render simultaneously
BackgroundFull-bleed screenshot at 108% scale with Ken Burns zoom
Ken Burns — hookSlow zoom in (1.00 → 1.07) from 55% 40%
Ken Burns — feature_1Slow zoom out (1.06 → 1.00) from 45% 55%
Ken Burns — feature_2Zoom in (1.00 → 1.08) from 60% 45%
Ken Burns — outroHeld (1.03) from 50% 50%
Shot cycling2 screenshots per scene — 14-frame midpoint crossfade
VignetteHeavy top/bottom, light center (where product UI lives)
Lower-third text52px, weight 740, 190px from bottom
Feature badgeGlass pill, lower-left, brand accent color
WatermarkRepo name pill, top-right
CaptionsReal karaoke word-level timestamps (ElevenLabs)
MusicOptional instrumental, ducked to 8% during narration
StorageSupabase Storage (video-exports bucket) or local download

FAQ

What repos work best?

Public repos with a live deployed app and a populated README. The pipeline captures the live app with Playwright — repos without a deployment still work (it falls back to the GitHub repo page), but the visual quality is higher when there's a real product to screenshot.

Can I use this on private repos?

Not yet. The ingest stage uses the GitHub API which requires repos to be public. Private repo support via personal access token is on the roadmap.

Why does the video have no audio?

No ElevenLabs key is configured. Add ELEVENLABS_API_KEY to your .env.local to enable narration. Without it the video renders silently with subtitles generated from estimated timestamps.

The Supabase banner shows on my dashboard. What do I do?

The video_jobs table hasn't been created in your Supabase project yet. Copy the SQL from the banner in your dashboard and paste it into Supabase Dashboard → SQL Editor → Run. The banner is dismissible and the app works fine without it (in-memory fallback).

The AI script mentions things not in my repo.

This shouldn't happen — the system prompt hard-requires every claim to be traceable to an import in the source files. If you see hallucination, set GEMINI_API_KEY to ensure the most capable model runs first. The fallback models (especially if all fail and buildFallbackScenes is used) produce generic copy that isn't repo-specific.

How do I change the video duration?

The LLM sets durations automatically based on how much the repo has to say. You can override them manually in the editor (UI Editor or Raw JSON mode) after generation. The render uses whatever durations are in the scenes JSON at render time.

Playwright screenshots are blank or show errors.

Run npx playwright install chromium to ensure the Chromium binary is present. On Vercel, Playwright doesn't work inside serverless functions — you'll need to add the @sparticuz/chromium package and configure it for the serverless environment, or use an external screenshot service.

Can I deploy this myself?

Yes. Deploy to Vercel with vercel --prod. Set all environment variables in the Vercel dashboard. Note that Playwright screenshot capture requires the serverless function to have enough memory (1 GB+ recommended) and execution time (60 s+).