$npx -y skills add gooseworks-ai/goose-skills --skill render-model-comparison-gridRender a 'model comparison grid' video from a config — a fal-style "same prompt, N contenders" showcase — a dark real-DOM stage where per beat a monospace prompt fades in centered, docks to a small top strip, then a labeled 2-4 panel grid (static images OR muted video clips, mixa
| 1 | # render-model-comparison-grid |
| 2 | |
| 3 | Render the 'model comparison grid' format from a config. The signature of this format is a |
| 4 | **"Same prompt. N models."** gauntlet: a dark stage where, per beat, a `PROMPT` eyebrow + |
| 5 | the (condensed) prompt **fades in** centered in monospace and holds readable ~0.8s, then |
| 6 | docks to a small top strip while a **grid of 2-4 labeled panels** staggers in (0.15s apart) |
| 7 | and holds for side-by-side comparison. A persistent model/variant label sits under each |
| 8 | panel; column order is identical on every beat. Ends on a minimal end card (headline + |
| 9 | column names only — **no meta-stats line**). |
| 10 | |
| 11 | The grid is **media-agnostic per cell**: any cell is a static image or a **muted video |
| 12 | clip** (i2v outputs, screen recordings), mixable within one beat. Video cells loop during |
| 13 | the hold and are **frame-seeked deterministically** (the renderer awaits each seek), so the |
| 14 | render never depends on wall-clock playback timing. |
| 15 | |
| 16 | The renderer itself is FREE/deterministic (Playwright frame-step + FFmpeg). The paid inputs |
| 17 | are separate capabilities: the cell **images** come from `create-image-fal`, the cell |
| 18 | **clips** from `create-video-fal`, and the **music bed** from `create-music-elevenlabs`. |
| 19 | Prompt text and labels are real DOM — never AI-rendered. |
| 20 | |
| 21 | Default shape: 5 beats × 4.5s + 2.5s end card = 25.0s @ 1280×720/30fps, all configurable |
| 22 | from one `config.json`. |
| 23 | |
| 24 | ## Run |
| 25 | build_composition.py --config config.json --output hyperframe.html ; render_seekable_hyperframe.py hyperframe.html master-silent.mp4 <duration> --fps 30 --width 1280 --height 720 — dark stage, staggered grid, deterministic, $0. The config schema is documented at the top of `scripts/build_composition.py`; `scripts/config.example.json` IS the shipped worked example (re-point the cell paths at your own media). |
| 26 | |
| 27 | `build_composition.py` validates every cell path and the column count (2-4), infers each |
| 28 | cell's media type from its extension (`.png/.jpg/.jpeg/.webp` → image; `.mp4/.mov/.webm/.m4v` |
| 29 | → muted video), and emits a self-contained HTML that exposes `window.mediaReady()` + |
| 30 | `window.renderAt(t)`. `render_seekable_hyperframe.py` awaits both, so `<video>` cells seek |
| 31 | to the right frame before each screenshot — never a frozen first frame. |
| 32 | |
| 33 | ## Contract |
| 34 | - Deterministic + FREE (Playwright frame-step + FFmpeg); no paid calls in this capability. |
| 35 | - Columns = panels-per-beat (2-4); every beat supplies exactly that many cells, same order. |
| 36 | - The template recipe (DB) supplies the config; cell images/clips + music are separate |
| 37 | capabilities. |
| 38 | - State is computed entirely in `renderAt(t)` — never CSS `animation-delay`/transitions |
| 39 | (Playwright scrubbing traps delayed animations in pre-state). |
| 40 | - Video cells must decode in the render Chromium (H.264 yes, ProRes no — transcode `.mov` |
| 41 | ProRes to H.264 first). An images-only grid has no decode dependency. |