Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Combines GIFs and videos into composite tutorials with vertical or grid layouts via ffmpeg. Use when assembling multi-part media into a single output.
.claude/skills/athola-media-composition/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-20 | ✗→✓ | ▲ Improved | 63% | 0% |
| case-05 | ✗→✓ | ▲ Improved | 130% | 0% |
| case-07 | ✗→✓ | ▲ Improved | 18% | 0% |
| case-08 | ✗→✓ | ▲ Improved | -1% | 0% |
| case-09 | ✗→✓ | ▲ Improved | 63% | 0% |
Combine multiple media assets (GIFs, videos, images) into composite outputs for detailed tutorials and documentation.
This skill orchestrates the combination of separately generated media assets into unified outputs. It reads manifest files that define components and their composition rules, validates all inputs exist, and executes FFmpeg commands to produce the final composite media.
The following tools must be available on PATH before using this skill:
ffmpeg: media composition and encodingyq: YAML manifest parsingRun ffmpeg -version and yq --version to verify availability.
- Parse composition manifest file
- Validate all component outputs exist
- Determine composition layout and parameters
- Execute FFmpeg composition command
- Verify combined output file created
- Report composition metrics (file size, dimensions)Manifests define the components to combine and how to arrange them:
yaml# Example manifest: tutorials/mcp.manifest.yaml name: mcp title: "MCP Server Integration" components: - type: tape source: mcp.tape output: assets/gifs/mcp-terminal.gif - type: playwright source: browser/mcp-browser.spec.ts output: assets/gifs/mcp-browser.gif requires: - "skrills serve" combine: output: assets/gifs/mcp-combined.gif layout: vertical options: padding: 10 background: "#1a1a2e"
| Field | Type | Required | Description | |-------|------|----------|-------------| | name | string | Yes | Identifier for the composition | | title | string | No | Human-readable title | | components | array | Yes | List of media components to combine | | components[].type | string | Yes | Source type: tape, playwright, static | | components[].source | string | Yes | Path to source file | | components[].output | string | Yes | Path to generated output | | components[].requires | array | No | Commands to run before generation | | combine.output | string | Yes | Path for combined output | | combine.layout | string | Yes | Layout mode (see table below) | | combine.options | object | No | Layout-specific options |
bash# Read and validate manifest structure yq eval '.' manifest.yaml # Extract component outputs yq eval '.components[].output' manifest.yaml
bash# Check all required files exist for output in $(yq eval '.components[].output' manifest.yaml); do if [[ ! -f "$output" ]]; then echo "ERROR: Missing component: $output" exit 1 fi done
Based on the layout specified in the manifest, execute the appropriate FFmpeg command.
bash# Verify output exists and has content if [[ -f "$output" && -s "$output" ]]; then echo "Composition successful: $output" ls -lh "$output" else echo "ERROR: Composition failed" exit 1 fi
Stack GIFs/videos top to bottom:
bashffmpeg -i top.gif -i bottom.gif \ -filter_complex "[0:v][1:v]vstack=inputs=2" \ -y output.gif
With padding between:
bashffmpeg -i top.gif -i bottom.gif \ -filter_complex "[0:v]pad=iw:ih+10:0:0:color=black[top];[top][1:v]vstack=inputs=2" \ -y output.gif
Stack GIFs/videos side by side:
bashffmpeg -i left.gif -i right.gif \ -filter_complex "[0:v][1:v]hstack=inputs=2" \ -y output.gif
Play GIFs/videos one after another:
bash# Create concat list file cat > concat_list.txt << EOF file 'first.gif' file 'second.gif' file 'third.gif' EOF # Concatenate ffmpeg -f concat -safe 0 -i concat_list.txt \ -y output.gif
bashffmpeg -i tl.gif -i tr.gif -i bl.gif -i br.gif \ -filter_complex "[0:v][1:v]hstack=inputs=2[top];[2:v][3:v]hstack=inputs=2[bottom];[top][bottom]vstack=inputs=2" \ -y output.gif
bashffmpeg -i top.gif -i bottom.gif \ -filter_complex "color=c=#1a1a2e:s=800x600[bg];[bg][0:v]overlay=0:0[tmp];[tmp][1:v]overlay=0:300" \ -y output.gif
| Layout | Description | Options | |--------|-------------|---------| | vertical | Stack top to bottom | padding, background, align | | horizontal | Stack left to right | padding, background, align | | sequential | Play in order | transition, duration | | grid | N x M grid arrangement | rows, cols, padding | | overlay | Layer on top of each other | position, opacity | | pip | Picture-in-picture | corner, scale, margin |
| Option | Type | Default | Description | |--------|------|---------|-------------| | padding | int | 0 | Pixels between components | | background | string | "black" | Background color (hex or name) | | align | string | "center" | Alignment: left, center, right | | transition | string | "none" | Transition type: fade, wipe, none | | scale | float | 0.25 | Scale factor for PiP | | corner | string | "br" | PiP corner: tl, tr, bl, br |
yamlname: plugin-demo components: - type: tape source: demo.tape output: terminal.gif - type: playwright source: browser.spec.ts output: browser.gif combine: output: demo-combined.gif layout: vertical options: padding: 5 background: "#0d1117"
yamlname: before-after components: - type: static source: before.gif output: before.gif - type: static source: after.gif output: after.gif combine: output: comparison.gif layout: horizontal options: padding: 10
yamlname: pip-demo components: - type: tape source: main.tape output: main.gif - type: playwright source: overlay.spec.ts output: overlay.gif combine: output: pip-demo.gif layout: pip options: corner: br scale: 0.3 margin: 20
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-20 | fail→pass | 9,358 | 2,359 | -75% | 1 | 1 | 0% | 1,479 | 2,405 | +63% | 0 | 0 | — |
case-01 | fail→fail | 3,107 | 6,479 | +109% | 1 | 1 | 0% | 356 | 2,340 | +557% | 0 | 0 | — |
case-02 | fail→fail | 7,114 | 9,395 | +32% | 1 | 1 | 0% | 311 | 2,767 | +790% | 0 | 0 | — |
case-03 | fail→fail | 6,261 | 6,622 | +6% | 1 | 1 | 0% | 231 | 2,320 | +904% | 0 | 0 | — |
case-04 | pass→pass | 8,560 | 3,271 | -62% | 1 | 1 | 0% | 1,001 | 2,605 | +160% | 0 | 0 | — |
case-05 | fail→pass | 9,042 | 8,879 | -2% | 1 | 1 | 0% | 1,564 | 3,603 | +130% | 0 | 0 | — |
case-06 | pass→pass | 12,613 | 7,605 | -40% | 1 | 1 | 0% | 1,853 | 3,337 | +80% | 0 | 0 | — |
case-07 | fail→pass | 23,463 | 4,934 | -79% | 1 | 1 | 0% | 2,330 | 2,753 | +18% | 0 | 0 | — |
case-08 | fail→pass | 13,849 | 3,081 | -78% | 1 | 1 | 0% | 2,479 | 2,450 | -1% | 0 | 0 | — |
case-09 | fail→pass | 11,094 | 5,252 | -53% | 1 | 1 | 0% | 1,780 | 2,906 | +63% | 0 | 0 | — |
case-10 | fail→pass | 13,309 | 8,112 | -39% | 1 | 1 | 0% | 2,502 | 3,486 | +39% | 0 | 0 | — |
case-11 | pass→pass | 6,432 | 3,937 | -39% | 1 | 1 | 0% | 1,024 | 2,603 | +154% | 0 | 0 | — |
case-12 | pass→pass | 11,050 | 6,345 | -43% | 1 | 1 | 0% | 1,784 | 3,138 | +76% | 0 | 0 | — |
case-13 | pass→pass | 8,980 | 2,193 | -76% | 1 | 1 | 0% | 1,616 | 2,422 | +50% | 0 | 0 | — |
case-14 | pass→pass | 11,097 | 5,768 | -48% | 1 | 1 | 0% | 1,983 | 3,111 | +57% | 0 | 0 | — |
case-15 | pass→pass | 13,741 | 6,208 | -55% | 1 | 1 | 0% | 1,803 | 3,182 | +76% | 0 | 0 | — |
case-16 | pass→pass | 15,918 | 6,229 | -61% | 1 | 1 | 0% | 2,941 | 3,226 | +10% | 0 | 0 | — |
case-17 | fail→pass | 11,682 | 2,221 | -81% | 1 | 1 | 0% | 1,807 | 2,362 | +31% | 0 | 0 | — |
case-18 | pass→pass | 12,940 | 5,567 | -57% | 1 | 1 | 0% | 2,120 | 3,113 | +47% | 0 | 0 | — |
case-19 | fail→pass | 9,637 | 3,866 | -60% | 1 | 1 | 0% | 1,759 | 2,612 | +48% | 0 | 0 | — |
case-21 | pass→pass | 6,655 | 3,758 | -44% | 1 | 1 | 0% | 1,066 | 2,573 | +141% | 0 | 0 | — |
case-22 | fail→pass | 14,004 | 5,059 | -64% | 1 | 1 | 0% | 2,110 | 2,933 | +39% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted, and 19 counted toward the lift figure. The other 3 produced results that are not comparable between the two arms, so they are excluded from the headline rather than averaged into it. The headline lift of +41 percentage points is the difference between those two pass rates over the 19 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.