{"id":47259709,"url":"https://github.com/shreyaskarnik/argo","last_synced_at":"2026-05-06T01:03:38.054Z","repository":{"id":344089060,"uuid":"1180378531","full_name":"shreyaskarnik/argo","owner":"shreyaskarnik","description":"Turn Playwright scripts into polished product demo videos with AI voiceover","archived":false,"fork":false,"pushed_at":"2026-04-01T07:12:22.000Z","size":295343,"stargazers_count":7,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-02T02:45:47.729Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/shreyaskarnik.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-13T01:30:17.000Z","updated_at":"2026-04-01T07:12:27.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/shreyaskarnik/argo","commit_stats":null,"previous_names":["shreyaskarnik/argo"],"tags_count":47,"template":false,"template_full_name":null,"purl":"pkg:github/shreyaskarnik/argo","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shreyaskarnik%2Fargo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shreyaskarnik%2Fargo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shreyaskarnik%2Fargo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shreyaskarnik%2Fargo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/shreyaskarnik","download_url":"https://codeload.github.com/shreyaskarnik/argo/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shreyaskarnik%2Fargo/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32028575,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-20T00:18:06.643Z","status":"ssl_error","status_checked_at":"2026-04-20T00:17:31.068Z","response_time":55,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-03-14T22:42:47.242Z","updated_at":"2026-05-06T01:03:38.045Z","avatar_url":"https://github.com/shreyaskarnik.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"```\n                                     ___\n        _____                       /  /\n       /  _  |  _ __  __ _  ___    /  /\n      /  /_| | | '__|/ _` |/ _ \\  /  /\n     /  ___  | | |  | (_| | (_) |/  /\n    /__/   |_| |_|   \\__, |\\___//__/\n                      __/ |\n                     |___/\n```\n\n# @argo-video/cli\n\n[![npm version](https://img.shields.io/npm/v/@argo-video/cli)](https://www.npmjs.com/package/@argo-video/cli)\n\n**Turn Playwright demo scripts into polished product demo videos with AI voiceover.**\n\nWrite a demo script with Playwright. Add a scenes manifest. Run one command. Get an MP4 with overlays and narration.\n\n## Showcase\n\n[Watch the demo video](https://github.com/user-attachments/assets/7693c67c-8850-4f57-a15c-47cfbfd3d180)\n\n\u003e *This demo was recorded by Argo, using Argo. Yes, really.*\n\n## How it works\n\n```bash\n TTS          Record        Align         Export\n ───          ──────        ─────         ──────\n Kokoro       Playwright    Place clips   ffmpeg\n generates    captures      at scene      merges\n voice        browser +     timestamps    video +\n clips        scene marks                 audio\n                  │              │\n                  ▼              ▼\n           .scenes.json → narration-aligned.wav → final.mp4\n```\n\n## Quick start\n\n```bash\n# Install\nnpm i -D @argo-video/cli\n\n# Initialize project\nnpx argo init\n\n# Edit your demo script (or convert an existing Playwright test)\nvim demos/example.demo.ts\nnpx argo init --from tests/checkout.spec.ts  # auto-convert\n\n# Run the full pipeline\nnpx argo pipeline example\n\n# Or run steps individually\nnpx argo record example\nnpx argo tts generate demos/example.scenes.json\nnpx argo export example\n```\n\n## Writing a demo\n\nA demo is two files: a **script** and a **scenes manifest**.\n\n### Demo script (`demos/my-feature.demo.ts`)\n\n```ts\nimport { test } from '@argo-video/cli';\nimport { showOverlay, withOverlay } from '@argo-video/cli';\n\ntest('my-feature', async ({ page, narration }) =\u003e {\n  await page.goto('/');\n\n  // Begin screen capture once setup (login, theme, navigation) is done.\n  // The first narration.mark() below lands at t=0 in the recording.\n  await narration.startRecording(page);\n\n  narration.mark('intro');\n  await showOverlay(page, 'intro', narration.durationFor('intro'));\n\n  narration.mark('action');\n  await withOverlay(page, 'action', async () =\u003e {\n    await page.click('#get-started');\n    await page.waitForTimeout(narration.durationFor('action'));\n  });\n\n  narration.mark('done');\n  await showOverlay(page, 'done', narration.durationFor('done'));\n});\n```\n\n### Scenes manifest (`demos/my-feature.scenes.json`)\n\n```json\n[\n  {\n    \"scene\": \"intro\",\n    \"text\": \"Welcome to our product — let me show you around.\",\n    \"overlay\": { \"type\": \"lower-third\", \"text\": \"Welcome to our product\", \"placement\": \"bottom-center\", \"motion\": \"fade-in\", \"autoBackground\": true }\n  },\n  {\n    \"scene\": \"action\",\n    \"text\": \"Just click get started and you're off.\",\n    \"overlay\": { \"type\": \"headline-card\", \"title\": \"Watch this\", \"placement\": \"top-right\", \"motion\": \"slide-in\" }\n  },\n  {\n    \"scene\": \"done\",\n    \"text\": \"And that's all there is to it.\",\n    \"voice\": \"af_heart\",\n    \"overlay\": { \"type\": \"callout\", \"text\": \"That's it!\", \"placement\": \"top-left\", \"motion\": \"fade-in\" }\n  }\n]\n```\n\nEach `scene` in the manifest maps to a `narration.mark()` call in the script. The `text` field is spoken narration; the optional `overlay` sub-object defines what appears on screen. Argo records the timestamp of each mark, generates TTS clips, and aligns them to produce the final narrated video.\n\n## Configuration\n\n### `argo.config.mjs`\n\n```js\nimport { defineConfig } from '@argo-video/cli';\n\nexport default defineConfig({\n  baseURL: 'http://localhost:3000',\n  demosDir: 'demos',\n  outputDir: 'videos',\n  tts: { defaultVoice: 'af_heart', defaultSpeed: 1.0 },\n  video: {\n    width: 1920, height: 1080, fps: 30,\n    browser: 'chromium',         // chromium with jpeg-stitch is the highest-quality path (v0.35+)\n    captureMode: 'jpeg-stitch',  // CDP-direct paint-time capture; auto-downgrades on webkit/firefox\n    deviceScaleFactor: 2,        // 4K supersample → lanczos downscale; auto-clamps to 1 on non-chromium\n  },\n  export: {\n    preset: 'slow', crf: 16,\n    transition: { type: 'fade-through-black', durationMs: 2000 },   // scene transitions (2s+ recommended)\n    speedRamp: { gapSpeed: 2.0 },                                   // speed up gaps between scenes\n    formats: ['gif', '9:16'],                                       // additional export formats\n  },\n  overlays: {\n    autoBackground: true,\n    // defaultPlacement: 'top-right',\n  },\n});\n```\n\n\u003e **Tip:** Use `browser: 'webkit'` for sharper video on macOS. Chromium has a [known video capture quality issue](https://github.com/microsoft/playwright/issues/31424). Set `deviceScaleFactor: 2` for retina-quality recordings (captured at 2x, downscaled with lanczos in export).\n\n### Mobile Demos\n\nRecord mobile-viewport demos with touch support:\n\n```ts\n// In your demo script\ntest.use({\n  viewport: { width: 390, height: 664 },\n  isMobile: true,\n  hasTouch: true,\n});\n```\n\nOr set mobile options globally in config:\n\n```js\nvideo: {\n  width: 390,\n  height: 664,\n  browser: 'webkit',\n  isMobile: true,\n  hasTouch: true,\n},\n```\n\n\u003e **Important:** The recording size is derived from `video.width` × `video.height` and passed to `page.screencast.start()` automatically. Use `.tap()` instead of `.click()` for touch interactions.\n\nSee `demos/mobile.demo.ts` for a complete mobile demo example.\n\n### `playwright.config.ts`\n\nArgo scaffolds this for you via `argo init`. The key settings:\n\n```ts\nimport { defineConfig } from '@playwright/test';\nimport config from './argo.config.mjs';\n\nconst scale = Math.max(1, Math.round(config.video?.deviceScaleFactor ?? 1));\nconst width = config.video?.width ?? 1920;\nconst height = config.video?.height ?? 1080;\n\n// Recording is driven by narration.startRecording(page) inside the demo\n// (page.screencast.start under the hood). Keep video: 'off' so Playwright's\n// auto recordVideo doesn't fight the screencast.\nexport default defineConfig({\n  preserveOutput: 'always',\n  projects: [{\n    name: 'demos',\n    testDir: 'demos',\n    testMatch: '**/*.demo.ts',\n    use: {\n      browserName: config.video?.browser ?? 'chromium',\n      baseURL: process.env.BASE_URL || config.baseURL || 'http://localhost:3000',\n      viewport: { width, height },\n      deviceScaleFactor: scale,\n      video: 'off',\n    },\n  }],\n});\n```\n\n## CLI\n\n```\nargo init                          Scaffold demo files + config\nargo init --from \u003ctest\u003e            Convert Playwright test to Argo demo\nargo record \u003cdemo\u003e                 Record browser session\nargo tts generate \u003cmanifest\u003e       Generate TTS clips from manifest\nargo export \u003cdemo\u003e                 Merge video + audio to MP4\nargo pipeline \u003cdemo\u003e               Run all steps end-to-end\nargo pipeline --all                Run pipeline for every demo in demosDir\nargo validate \u003cdemo\u003e               Check scene name consistency (no TTS/recording)\nargo preview \u003cdemo\u003e                Browser-based editor for voiceover, overlays, timing\nargo preview                       Multi-demo dashboard (lists all demos with status)\nargo clip \u003cdemo\u003e \u003cscene\u003e            Extract a scene clip from exported video\nargo clip \u003cdemo\u003e \u003cscene\u003e --format gif  Extract as palette-optimized GIF\nargo import \u003cvideo-file\u003e           Import external video for post-production\nargo doctor                        Check environment (ffmpeg, Playwright, config)\nargo --config \u003cpath\u003e \u003ccommand\u003e     Use a custom config file\n\nOptions:\n  --browser \u003cengine\u003e               chromium | webkit | firefox (overrides config)\n  --base-url \u003curl\u003e                 Override baseURL from config\n  --headed                         Run browser in visible mode\n  --all                            Run pipeline for all demos\n  --port \u003cnumber\u003e                  Preview server port (default: auto)\n```\n\n### Overlay Blocks\n\nArgo ships 12 curated overlay blocks for demo narratives. Reference them from `.scenes.json`:\n\n```json\n{\n  \"scene\": \"social-proof\",\n  \"overlay\": {\n    \"type\": \"block\",\n    \"block\": \"x-post\",\n    \"props\": {\n      \"handle\": \"@jane\",\n      \"name\": \"Jane Doe\",\n      \"body\": \"this is exactly what I needed\",\n      \"timestamp\": \"2m\"\n    },\n    \"placement\": \"top-right\"\n  }\n}\n```\n\n**Static blocks** (use CSS-preset motion):\n\n| Block | Purpose |\n|-------|---------|\n| `x-post` | Social post card for social proof |\n| `macos-notification` | macOS-style notification banner |\n| `yt-lower-third` | YouTube-style lower third for speaker intros |\n| `data-chart` | Compact bar/line chart for metrics |\n| `spotify-card` | Now-playing card for decorative inserts |\n\n**Animated blocks** (ship with a GSAP `defaultMotion` — cue-level `motion` still overrides):\n\n| Block | Motion |\n|-------|--------|\n| `instagram-follow` | Back-easing scale-in entrance + pulsing Follow button |\n| `tiktok-follow` | Side-slide entrance + rotating gradient avatar ring |\n| `reddit-post` | Simple slide-up entrance + fade-out |\n| `logo-outro` | Scale-in end-card with back.out ease |\n| `flowchart` | Staggered reveal across nodes and arrows |\n| `app-showcase` | Back-eased hero entrance + slow floating icon loop |\n| `ui-3d-reveal` | Perspective tilt-to-flat reveal with a subtle wobble loop |\n\n### GSAP motion (advanced)\n\nEvery overlay supports two kinds of `motion`: a named CSS preset (`none`, `fade-in`, `slide-in`) or a declarative GSAP motion object with `in`, `out`, and `loop` phases. `showOverlay(page, scene, durationMs)` auto-times the exit so the visible window matches `durationMs`:\n\n```json\n{\n  \"scene\": \"hero\",\n  \"overlay\": {\n    \"type\": \"headline-card\",\n    \"title\": \"Argo\",\n    \"kicker\": \"Demo videos, locally\",\n    \"motion\": {\n      \"type\": \"gsap\",\n      \"in\":  { \"from\": { \"y\": 40, \"opacity\": 0 }, \"duration\": 0.5, \"ease\": \"back.out\" },\n      \"out\": { \"to\":   { \"opacity\": 0, \"y\": -20 }, \"duration\": 0.3, \"ease\": \"power2.in\" }\n    }\n  }\n}\n```\n\n`GsapTween` fields: `from` / `to` / `fromTo`, `duration`, `delay`, `ease`, `stagger`, `target` (CSS selector inside the overlay root), `repeat`, `yoyo`. Allowed eases and animation vars are whitelisted; `argo validate` rejects unknown values. Raw GSAP code via `motion.raw` is off by default — enable with `overlays.allowRawGsap: true` if you need the escape hatch. GSAP ships with Argo and is injected into the recording page on demand.\n\nBlocks live under `src/blocks/\u003cname\u003e/` — see [demos/blocks-showcase](demos/blocks-showcase.demo.ts) for a complete example.\n\n## API\n\nArgo exports Playwright fixtures and helpers for use in demo scripts:\n\n```ts\nimport { test, expect, demoType } from '@argo-video/cli';\nimport { showOverlay, hideOverlay, withOverlay } from '@argo-video/cli';\nimport { showConfetti } from '@argo-video/cli';\nimport { spotlight, focusRing, dimAround, zoomTo, resetCamera } from '@argo-video/cli';\nimport { cursorHighlight, resetCursor } from '@argo-video/cli';\nimport { showCaption, hideCaption, withCaption } from '@argo-video/cli';\nimport { defineConfig, demosProject, engines } from '@argo-video/cli';\n```\n\n| Export | Description |\n|--------|-------------|\n| `test` | Playwright `test` with `narration` fixture injected |\n| `expect` | Re-exported from Playwright |\n| `demoType(page, selectorOrLocator, text, delay?)` | Type character-by-character — accepts CSS selector or Playwright Locator |\n| `showOverlay(page, scene, durationMs)` | Show overlay from manifest for a fixed duration |\n| `showOverlay(page, scene, cue, durationMs)` | Show overlay with inline cue (backward compat) |\n| `withOverlay(page, scene, action)` | Show overlay from manifest during an async action |\n| `withOverlay(page, scene, cue, action)` | Show overlay with inline cue during action (backward compat) |\n| `hideOverlay(page, zone?)` | Remove overlay from a zone |\n| `showConfetti(page, opts?)` | Non-blocking confetti animation (`spread: 'burst' \\| 'rain'`, `emoji: '🎃'` or `emoji: ['🎄', '⭐']` for emoji mode, `wait: true` to block) |\n| `spotlight(page, selector, opts?)` | Dark overlay with hole around target element |\n| `focusRing(page, selector, opts?)` | Pulsing glow border on target |\n| `dimAround(page, selector, opts?)` | Fade sibling elements to highlight target |\n| `zoomTo(page, selector, opts?)` | Scale viewport centered on target. Pass `{ narration }` for overlay-safe ffmpeg post-export zoom (recommended). |\n| `resetCamera(page)` | Clear all active camera effects |\n| `cursorHighlight(page, opts?)` | Persistent cursor ring with pulse + click ripple. Options: `color`, `radius`, `pulse`, `clickRipple`, `opacity` |\n| `resetCursor(page)` | Remove cursor highlight |\n| `showCaption(page, scene, text, durationMs)` | Show a simple text caption |\n| `withCaption(page, scene, text, action)` | Show caption during an async action |\n| `hideCaption(page)` | Remove caption |\n| `narration.mark(scene)` | Record a scene timestamp |\n| `narration.durationFor(scene, opts?)` | Compute hold duration from TTS clip length (remaining time from now) |\n| `narration.sceneDuration(scene, opts?)` | Full scene duration — stable, non-decreasing (for overlay display) |\n| `defineConfig(userConfig)` | Create config with defaults |\n| `demosProject(options)` | Create Playwright project entry |\n\n## Requirements\n\n- **Node.js** \u003e= 18\n- **Playwright** \u003e= 1.40 (peer dependency)\n- **ffmpeg** — system install required for export\n\n```bash\n# Install ffmpeg\nbrew install ffmpeg        # macOS\napt install ffmpeg         # Linux\nchoco install ffmpeg       # Windows\n```\n\n## How the pipeline works\n\n1. **TTS** — Generates WAV clips from the scenes manifest. Kokoro is the default (local, free), but you can swap in OpenAI, ElevenLabs, Gemini, Sarvam, or mlx-audio via `engines.*` factories. Clips are cached by content hash in `.argo/\u003cdemo\u003e/clips/`.\n\n   ```js\n   import { defineConfig, engines } from '@argo-video/cli';\n   export default defineConfig({\n     tts: { engine: engines.openai({ model: 'tts-1-hd' }) },\n   });\n   ```\n\n   | Engine | Type | Install | API Key |\n   |--------|------|---------|---------|\n   | `engines.kokoro()` | local | built-in | none |\n   | `engines.mlxAudio()` | local | `pip install mlx-audio` | none |\n   | `engines.openai()` | cloud | `npm i openai` | `OPENAI_API_KEY` |\n   | `engines.elevenlabs()` | cloud | `npm i @elevenlabs/elevenlabs-js` | `ELEVENLABS_API_KEY` |\n   | `engines.gemini()` | cloud | `npm i @google/genai` | `GEMINI_API_KEY` |\n   | `engines.sarvam()` | cloud | `npm i sarvamai` | `SARVAM_API_KEY` |\n   | `engines.transformers()` | local | built-in | none |\n\n   **Transformers.js** — Use any HuggingFace `text-to-speech` model locally. Supertonic, or any future ONNX TTS model:\n\n   ```js\n   tts: {\n     engine: engines.transformers({\n       model: 'onnx-community/Supertonic-TTS-ONNX',\n       speakerEmbeddings: 'https://huggingface.co/.../voices/F1.bin',\n       numInferenceSteps: 10,\n     }),\n   }\n   ```\n\n   **Voice cloning** — Clone your own voice locally with mlx-audio. Record a 15-second clip, and every demo sounds like you — privately, no data leaves your machine:\n\n   ```bash\n   # Record a reference clip (macOS)\n   bash $(npm root)/@argo-video/cli/scripts/record-voice-ref.sh assets/ref-voice.wav\n\n   # Preview cloned voice against your manifest\n   bash $(npm root)/@argo-video/cli/scripts/voice-clone-preview.sh \\\n     --ref-audio assets/ref-voice.wav \\\n     --ref-text \"Transcript of what I said.\" \\\n     --voiceover demos/showcase.scenes.json --play\n   ```\n\n   ```js\n   tts: {\n     engine: engines.mlxAudio({\n       model: 'mlx-community/Qwen3-TTS-12Hz-0.6B-Base-bf16',\n       refAudio: './assets/ref-voice.wav',\n       refText: 'Transcript of what I said in the clip.',\n     }),\n   }\n   ```\n\n2. **Record** — Playwright runs the demo script in a real browser. The `narration` fixture records timestamps for each `mark()` call. Video is captured at native resolution.\n\n3. **Align** — Each TTS clip is placed at its scene's recorded timestamp. Overlapping clips are pushed forward with a 100ms gap. All clips are mixed into a single `narration-aligned.wav`.\n\n4. **Export** — ffmpeg combines the screen recording (WebM) with the aligned narration (WAV) into an H.264 MP4 with chapter markers. Subtitle files (`.srt` + `.vtt`) and a scene report are generated alongside the video. A progress bar shows encoding percentage during export.\n\n### Scene Transitions\n\nAdd smooth transitions between scenes:\n\n```js\nexport: {\n  transition: { type: 'fade-through-black', durationMs: 2000 },\n}\n```\n\n\u003e **Tip:** Use `durationMs: 2000` or higher for transitions that are clearly visible during narration. Short durations (500ms) look like glitches rather than intentional transitions.\n\nTransition types: `fade-through-black`, `dissolve` (quicker dip-to-black, not a true crossfade), `wipe-left`, `wipe-right`.\n\n\u003e **Tip:** Content changes (page navigation, slide switches) should happen **before** `narration.mark()` so the transition fades between the old and new content. If you change content after `mark()`, the transition just pulses the same visual.\n\n### Shader Transitions\n\nPre-rendered WebGL shader transitions between scenes, cached by content hash.\n\n```js\nexport: {\n  transition: {\n    type: 'shader',\n    shader: 'crosswarp',   // crosswarp | swirl | ripple | luma-mask | light-leak\n    durationMs: 800,\n  },\n}\n```\n\nShaders are adapted from [gl-transitions.com](https://gl-transitions.com) (MIT). First export launches headless Chromium to pre-render shader frames; cached at `.argo/\u003cdemo\u003e/shaders/\u003chash\u003e/` so subsequent exports skip the browser launch.\n\nSee `demos/shaders-showcase.demo.ts` for a complete example.\n\n### Speed Ramp\n\nCompress gaps between scenes (navigation, page loads) to keep demos tight:\n\n```js\nexport: {\n  speedRamp: { gapSpeed: 2.0, minGapMs: 500 },\n}\n```\n\n`gapSpeed: 2.0` means inter-scene gaps play at 2× speed. Only gaps longer than `minGapMs` (default 500ms) are affected. Both video and audio are sped up together.\n\n### Multi-Format Export\n\nExport additional formats alongside the main 16:9 MP4:\n\n```js\nexport: {\n  formats: ['1:1', '9:16', 'gif'],\n}\n```\n\n- `1:1` — Square with blur-fill background for Instagram/LinkedIn\n- `9:16` — Vertical with blur-fill background for TikTok/Reels\n- `gif` — Animated GIF with palette optimization for docs/READMEs\n\n### Audio Processing\n\n```js\nexport: {\n  audio: {\n    loudnorm: true,                   // EBU R128 normalization (-16 LUFS)\n    music: 'assets/bg-music.mp3',     // background music track\n    musicVolume: 0.15,                // music volume (0.0-1.0, default 0.15)\n  }\n}\n```\n\n- **Loudnorm** — EBU R128 loudness normalization. Makes voiceover volume consistent across TTS engines and scenes.\n- **Background music** — loops to fill the video, mixed at a constant low volume under narration, 2-second fade-out at the end. Works with silent demos too (music becomes the sole audio track).\n\n### Export Quality\n\nArgo's H.264 output is tagged BT.709 color space, uses x264 adaptive quantization (`aq-mode=3`) to reduce banding on gradients, and converts Chrome's full-range RGB to H.264 TV range. Tags ensure colors match across Safari, TVs, and mobile players.\n\n**GPU encoding.** Argo auto-detects GPU encoders and uses them when available:\n\n- macOS: `h264_videotoolbox`\n- NVIDIA: `h264_nvenc`\n- Linux/AMD: `h264_vaapi`\n- Intel: `h264_qsv`\n\nTypical speedup: 3-10x on macOS, 5-15x on NVIDIA. Falls back to libx264 when no GPU encoder is available.\n\nDisable GPU encoding with `ARGO_USE_GPU=0` (e.g., for deterministic CI builds):\n\n```bash\nARGO_USE_GPU=0 argo pipeline my-demo\n```\n\n### Studio Polish\n\n```js\nexport: {\n  sharpen: true,                        // contrast-adaptive sharpening (CAS) for crisp text\n  frame: {                              // \"Screen Studio\" look\n    padding: 48,\n    borderRadius: 16,\n    shadow: 0.5,\n    background: { type: 'auto' },       // auto-derives gradient from video colors\n    // background: { type: 'gradient', value: 'linear-gradient(135deg, #1a1a2e, #16213e)' },\n    // background: { type: 'solid', value: '#000000' },\n    // background: { type: 'image', value: 'assets/bg.jpg' },\n  },\n  motionBlur: { intensity: 0.5 },       // blur during camera move transitions\n}\n```\n\n- **Frame** — Wraps the recording with padding, rounded corners, drop shadow, and a background. Background `auto` probes the video for dominant edge colors and generates a matching gradient.\n- **Sharpening** — ffmpeg CAS filter restores text crispness lost during screen recording encode.\n- **Motion blur** — Time-gated `tblend` that only activates during zoom-in/zoom-out camera move windows. Static frames stay sharp.\n\n### Per-Scene Playback Speed\n\nControl video playback rate per scene in the manifest (separate from TTS speech `speed`):\n\n```json\n{ \"scene\": \"hero\", \"text\": \"...\", \"playbackSpeed\": 0.5 }\n```\n\nUseful for slow-mo hero moments or fast-forwarding setup steps.\n\n### Post-Export Camera Moves\n\nZoom into specific elements with frame-exact ffmpeg `zoompan` — overlays stay unaffected:\n\n```ts\nimport { zoomTo } from '@argo-video/cli';\n\nnarration.mark('details');\nzoomTo(page, '#revenue-chart', {\n  narration,\n  scale: 1.35,\n  duration: 5000,\n  fadeIn: 1000,\n  holdMs: 3000,\n});\nawait showOverlay(page, 'details', narration.durationFor('details'));\n```\n\nWhen `narration` is passed, `zoomTo` records the target's bounding box as a camera move mark instead of manipulating the DOM. During export, the pipeline applies animated `zoompan` filters via ffmpeg. This is overlay-safe (overlays are already burned into the video before the zoom is applied) and frame-exact.\n\nWithout `narration`, `zoomTo` falls back to browser-side CSS transforms (for VS Code preview / standalone Playwright runs).\n\n### Viewport-Native Variants\n\nRe-record at different viewports for pixel-perfect multi-format output. CSS handles layout — much better than blur-fill for responsive content:\n\n```js\nexport: {\n  variants: [\n    { name: 'vertical', video: { width: 1080, height: 1920 } },\n    { name: 'square',   video: { width: 1080, height: 1080 } },\n  ]\n}\n```\n\nTTS runs once, then the pipeline records and exports each variant separately. Output: `videos/\u003cdemo\u003e-vertical.mp4`, `videos/\u003cdemo\u003e-square.mp4`.\n\n### Batch Pipeline\n\nBuild all demos in one command:\n\n```bash\nnpx argo pipeline --all\n```\n\nDiscovers all `.scenes.json` files in `demosDir` and runs the full pipeline for each.\n\n### Dashboard\n\nView all demos at a glance:\n\n```bash\nnpx argo preview\n```\n\nOpens a dashboard listing every discovered demo with build status, video size, resolution, and quick-action links. Run `argo preview \u003cdemo\u003e` for the single-demo editor — scene list, overlay editor, frame \u0026 background panel, and a **waveform strip** painted from the aligned narration WAV (click-to-seek, mirrored playhead).\n\n### Video Import\n\nBring any existing video into Argo for post-production — add voiceover, overlays, and scene boundaries:\n\n```bash\nnpx argo import recording.mp4              # import with auto-detected name\nnpx argo import recording.mp4 --demo myapp # custom demo name\nnpx argo preview myapp                     # open in editor\n```\n\nImport scaffolds a `.scenes.json` manifest and `.timing.json` from the video. In the preview editor you can scrub the timeline, add scene boundaries at any point, write voiceover text, drag-to-snap overlays into position, and export — all without re-recording. Overlays are composited as PNGs with adaptive theme detection (light/dark auto-detected from video frames).\n\n## Example\n\nA self-contained example is in [`example/`](example/) — it records a demo of Argo's own showcase page:\n\n```bash\ncd example \u0026\u0026 npm install \u0026\u0026 npx playwright install webkit\nnpm run serve      # in one terminal\nnpm run demo       # in another\n```\n\n## LLM Skill\n\nArgo ships as a **Claude Code skill** so LLMs can create demo videos autonomously. Install it as a plugin:\n\n```bash\n# In Claude Code\n/plugin marketplace add shreyaskarnik/argo\n```\n\nThe skill teaches Claude how to write demo scripts, scenes manifests, overlay cues, and run the pipeline — no manual guidance needed.\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fshreyaskarnik%2Fargo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fshreyaskarnik%2Fargo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fshreyaskarnik%2Fargo/lists"}