---
name: Render Photo Grid Card
slug: render-photo-grid-card
category: Automation
description: Render Photo Grid Card builds a deterministic promo-card video from config with a fixed header and footer, a continuously scrolling 2-row mixed-media grid, and real DOM text. Use it for the photo-grid-promo-card format when you need frame-stepped assembly with FFmpeg and Playwright.
github: "https://github.com/gooseworks-ai/goose-skills/tree/main/skills/ads/capabilities/render-photo-grid-card"
language: Python
stars: 1146
forks: 200
install: "npx degit https://github.com/gooseworks-ai/goose-skills/tree/main/skills/ads/capabilities/render-photo-grid-card ~/.claude/skills/render-photo-grid-card"
installs_to: ~/.claude/skills/render-photo-grid-card
source_path: skills/ads/capabilities/render-photo-grid-card/SKILL.md
collection_size: 51
category_size: 1523
collection_url: "https://dirskills.com/collections/gooseworks-ai/goose-skills"
added: 2026-08-12T04:43:27.780Z
last_synced: 2026-08-12T04:43:27.780Z
canonical_url: "https://dirskills.com/skills/render-photo-grid-card"
---

# Render Photo Grid Card

Render Photo Grid Card builds a deterministic promo-card video from config with a fixed header and footer, a continuously scrolling 2-row mixed-media grid, and real DOM text. Use it for the photo-grid-promo-card format when you need frame-stepped assembly with FFmpeg and Playwright.

**Install:**

```bash
npx degit https://github.com/gooseworks-ai/goose-skills/tree/main/skills/ads/capabilities/render-photo-grid-card ~/.claude/skills/render-photo-grid-card
```

## README

# render-photo-grid-card

Render the 'photo-grid promo card' format from a config. The signature of this format is a
**continuously scrolling 2-row grid** (NOT a static card) under a fixed header (brand
wordmark + big headline + sub) and fixed feature chips. The grid-viewport scrolls left
across the whole ~10s, edge-faded with a CSS mask, and its tiles are **mixed media**:
`video` clips playing inside tiles, `band-product`/`photo` stills, big-serif `pct`/`off`
type tiles, and a dark `code` (promo code) or `cta` tile.

The renderer itself is FREE/deterministic (Playwright frame-step + FFmpeg). The paid inputs
are separate capabilities: the tile **clips** come from `create-video-fal` (i2v of the
product heroes, or clips extracted from the brand's own ad corpus) and the **music** from
`create-music-elevenlabs`. Text (wordmark, %, code) is real DOM — never AI-rendered.

## Run
build_card.py --config config.json --out hyperframe.html ; render.py --config config.json --html hyperframe.html --out master-silent.mp4 — 1080x1920, scrolling grid, deterministic, $0.

Clip tiles are driven by FRAME-SWAP, not `<video>`: `render.py` pre-extracts each clip to
PNG frames with ffmpeg, preloads them, and swaps each `<img class="vidframe">`'s src per
output frame. This is because Playwright's bundled Chromium can't decode `<video>` H.264
over file:// (open-source build, no proprietary codecs) — it hangs on `canplay`. Letting
ffmpeg decode makes it codec-independent AND deterministic. Provide clips longer than the
master (or the swap just loops them); a subtle `setpts`-slow reads as cinematic.

## Contract
- Deterministic + FREE (Playwright frame-step + FFmpeg); no paid calls in this capability.
- Tiles-per-row = grid columns (`cols`), NOT total tiles; the grid fills row-major.
- The template recipe (DB) supplies the config; clips + music are separate capabilities.
