Use when the user wants to explain or document something visually — annotated screenshots, feature demos, flow GIFs, terminal recordings, before/after comparisons — or says "take a screenshot", "record this flow", "make a gif of", "show how this works", "annotate this page",
Installs just this skill. Get the whole plugin for auto-invocation.
⚡ How it fires
How this skill gets triggered: by you, by Claude, or both.
Fires itselfClaude auto-loads it when your prompt matches the work.
You can call itInvoke it directly when you want it.
Slash command/showreel
👁️ Context preview
The summary Claude sees to decide when to auto-load this skill.
Use when the user wants to explain or document something visually — annotated screenshots, feature demos, flow GIFs, terminal recordings, before/after comparisons — or says "take a screenshot", "record this flow", "make a gif of", "show how this works", "annotate this page",
📊 Stats
Stars5
Forks0
LanguageJavaScript
LicenseMIT
📦 Ships with showreel
</> SKILL.md
showreel.SKILL.md
---name: showreel
description: Use when the user wants to explain or document something visually — annotated screenshots, feature demos, flow GIFs, terminal recordings, before/after comparisons — or says "take a screenshot", "record this flow", "make a gif of", "show how this works", "annotate this page", "demo this feature", "grava um gif", "tira um print anotado". Covers web pages (self-contained Chromium) and terminal sessions (vhs).
---# Showreel
## Overview
Turn "explain this" into professional visual artifacts in one command each: annotated screenshots, isolated feature demos, animated flow recordings (gif or mp4), terminal session GIFs, and before/after composites. **You speak in CSS selector + text. Never in pixels, never in seconds.** The motor (self-contained Chromium in `scripts/.deps/`, auto-installed on first use) measures the DOM, places annotations deterministically (`lib/autoplace.mjs`), draws, and gates the result — the verdict covers dominance, collision, contrast, and target-text-present. Any agent with Bash can run it, no browser MCP needed.
Scripts live in this plugin's `scripts/` dir. Run `scripts/preflight.sh` once on a new machine (node required; ffmpeg + vhs optional).
Default output dir: `./showreel-out/` in the current project (create it; respect the user's choice if they name another).
## One command per artifact
| Need | Command | Output |
|---|---|---|
| Annotated screenshot (marker + callout, self-validated) | `node scripts/prove.mjs <url> "<selector>" out.png --label "menu opens"` | `PASS out.png kb=<n>` |
| Quick auto-annotate — URL only, no selectors (discovers salient elements: heading, primary action, nav, hero, cards; role-based notes; same gate) | `node scripts/auto.mjs <url> [--max N] [--out-dir DIR]` | `PASS <out> kb=<n>` per element, then `AUTO <k>/<n> PASS`; writes `index.json` |
| Side-by-side of two PNGs or two GIFs | `node scripts/compose.mjs a.png b.png pair.png --labels "Before,After"` (gifs: `a.gif b.gif pair.gif [--height N]`) | `OK pair.png` |
| Side-by-side VIDEO | `node scripts/compose-video.mjs a.webm b.webm out.mp4 [--labels "Before,After"] [--sync-trim]` — `--sync-trim` reads the `.timeline.json` sidecars from `rec --keep-webm` and aligns both takes | `OK out.mp4` |
| Shrink a gif/png without visible quality loss | `node scripts/shrink.mjs in.gif [out.gif] [--target-kb N]` | `OK out (X KB -> Y KB, -N%)`; may print `RECOMMEND-MP4 <reason>` |
| Wrap a PNG in a share-ready frame (browser window / card, shadow, social aspect) | `node scripts/beautify.mjs in.png [out.png] [--frame window\|card\|minimal] [--ratio 16:9\|9:16\|1:1] [--url "..."] [--bg "c1,c2"]` | `OK out.png (WxH) kb=<n>` |
| N captures, ONE browser launch | `node scripts/demo.mjs <url> --batch jobs.json` — jobs: `[{selector,out,kind,text}]` | `OK` per job |
| Lighthouse before/after across branches | `bash scripts/lh-ba.sh <url> showreel-out [audit] [base-branch]` then `shot.mjs` each audit card + `compose.mjs` | real reports, never hand-built |
## Picking the right tool
- **Don't know the selectors / want a fast overview of a page** → `auto` (URL only; it picks the salient elements). For precise control of WHICH element + WHAT note → `prove`.
- **Motion / multi-step flow in a browser** → `rec`.
- **CLI / terminal behavior** → `tape` (vhs). Browser things never go through vhs; terminal things never through rec.
- **Comparison** → capture both states, then `compose` (stills) or `compose-video` (takes).
- **Doc page needing one concept per image** → `demo --batch`.
- **Make a capture share-ready (slides, social, marketing)** → `beautify` — frames any PNG with window chrome, shadow and social aspect presets.
## Fidelity — match the artifact to the subject
**Effort — the user's dial (default `rich`).** The user can ask for a level in any language ("quick", "rich", "capricha", "cinematic") — honor it. Absent a request, default to **`rich`**: someone trying showreel for the first time should get a result that makes them go *wow*, with zero configuration. Effort scales the CRAFT poured into the RIGHT artifact — it never forces a bigger artifact than the subject needs (one element → a gorgeous annotated still, not a movie).
| Effort | Pour in | Use when |
|---|---|---|
| `quick` | The lightest artifact that still EXPLAINS — minimal annotation, no camera. | user asks for fast / light |
| `standard` | The right artifact, well annotated; light camera on flows. | user asks to keep it lean |
| **`rich` — default** | Full capability on the right artifact: camera motion, spotlight, marks/glossary, multi-beat flows, mp4 for a hero, polish. | **default** |
| `cinematic` | Showcase-grade — everything above at maximum craft, finished with `beautify`. | the hero, or "make it impressive" |
Then match the ARTIFACT to the subject (the effort lives *inside* the right artifact):
| Subject | Artifact | How |
|---|---|---|
| One element / one fact | **annotated still** | `prove.mjs` — rect/badge + a note, zoom inset on the key detail. |
| A set of related elements | **still with marks** | `demo.mjs` primitives, or `prove` with badges on the group. |
| A user flow (click → result) | **flow gif** | `rec.mjs` with cursor glide, per-step notes (arrow on each), stamp counter. |
| A rich walkthrough / the hero | **rich mp4** | `rec.mjs` realtime `--fps 30`. **`references/rec-cookbook.md` is THE CONTRACT — read it first; its GRAMMAR TABLE + FORBIDDEN FORMS + RENDER-MODE/ANCHORING gates + pre-flight make valid, complete output correct-by-construction.** Then read `references/cinematic-grammar.md` (shot/camera/pacing for legible motion) and `references/motion-design.md` (easing/stagger numbers, compositing-by-splitting) for craft. A rich walkthrough shows the real end-to-end flow in the order it actually happens (or the order that best explains it), one focal point per beat, with smooth viewport motion (camera/follow/loupe) keeping the longer flow legible — never a flat numbered feature list, never a story arc. |
| Before vs after | **compose** | `compose.mjs` two stills or two gifs side by side; `lh-ba.sh` for real Lighthouse. |
Whatever you pick: every label visibly connects to its target, and the final frame still shows the effect (persist the note/rect/bar through the last step) so the artifact reads even when paused.
## Flow recordings (`rec.mjs`)
**Storyboard first.** Before recording a flow, show the user a plain-language beat list of what the reel will do — one line per beat, e.g. *"enter the page → zoom into #pipeline → click #deploy → pick a region → zoom out → scroll to #services"*. It's the human-readable mirror of the `--dry` step order. Default: show it, then record (keep the momentum). At `cinematic` effort — or if the user asks to review the plan first — wait for their nod before recording. A single still needs no storyboard.
**Discipline:** `--dry` is MANDATORY before any recording — resolves every selector ([ok]/[MISS]), no recording. A [MISS] on a glide/camera/click/fill/select target is a frozen dead beat — fix to zero MISS, then record ONCE.
**Fresh app state before every (re-)record.** A roster is authored for the app in state A; re-running it later hits state B, where the action already happened — a submitted form's button is now `disabled`, a one-time CTA is spent, a control changed and an overlay/next-page element sits where the target was. The recorder then clicks nothing (disabled) or the wrong thing (reads as random clicks). `--dry` will NOT catch this (a disabled button still resolves). The live safeguard now **refuses** (exit 2, `[not-actionable]`) when a click/fill/select target is `disabled` / `aria-disabled` / `pointer-events:none` / covered by another element. The recorder CANNOT reset server-side state (a fresh browser context won't un-submit a form) — so on a `not-actionable` refusal, **reset the demo/app to a fresh state and re-run**; never `--no-safeguards` past it (that records the broken flow).
**Render mode (decides at the command line, not in the frames):** a MOVING reel — any `glide`/`follow`/`camera`/`fill`/`select` — records realtime `--fps 30`, NO `--offline` (15fps stills → slideshow), NO `--pace fast` (collapses easing), NO `speed` (a no-op in realtime). `--pace fast` and `--offline` are ONLY for quick static/dwell-only proofs.
**Presets:** don't write step JSON from scratch. Copy a starting roster from `scripts/presets/` — `form-flow.json`, `nav-flow.json`, `dashboard.json` — and swap the selectors for the page under test.
EVERY explanatory label must visibly connect to its target (arrow on the note, leader on the anchored modal, or centered modal narration) — never floating.
**Before writing any rec steps beyond a straight preset swap, read `references/rec-cookbook.md` — THE CONTRACT:** the full step-key GRAMMAR TABLE (shape, required siblings, forbidden forms, element-vs-viewport anchor), the RENDER-MODE + ANCHORING gates, the authoring procedure, and the one binary pre-flight. Write every step against the table, never from memory. Takes open 1:1; establishing auto-fit is opt-in via `--fit`. Need to reveal something clipped inside an `overflow` div (log/list/chat)? Use `scrollIn` — see cookbook.
## Terminal recordings (`tape.mjs`)
Wraps [charmbracelet/vhs](https://github.com/charmbracelet/vhs). Steps JSON → generated `.tape` → `vhs` renders a terminal gif. Step keys: `type` (string typed with realistic delay), `enter`, `sleep`/`wait` (ms), `hide`/`show` (omit setup from the recording), `ctrl` (e.g. `"c"`). Options: `--width`, `--height`, `--font-size`, `--theme`, `--shell`. Raw passthrough: `node scripts/tape.mjs --tape file.tape out.gif`.
Requires the `vhs` binary (`brew install vhs`) — preflight reports it; if missing, say so and offer the install command instead of failing silently.
## Artifact optimization + video policy (hard rules)
- **ALWAYS optimize before delivering** — every png/gif goes through `node scripts/shrink.mjs <file>` first, no exceptions. Optimization is quality-preserving by default: no visible degradation, ever. When only a lossy step would hit a size target, don't take it — switch format instead (gif → mp4).
- gif > 2 MB (GIF_MAX_KB=2048) or > 8 s (GIF_MAX_SEC=8) → deliver mp4 instead (`rec --mp4`). When shrink can't reach the target it prints `RECOMMEND-MP4 <reason>` on stdout — obey it, re-export as mp4. In docs, a gif poster may link to the mp4.
## Rules
- **Richness that explains is not filler.** At `rich` (the default) and up, assume camera motion, spotlight, marks/glossary and multi-beat flows are warranted — hold back only for genuine noise, never out of caution.
- One focal point per beat. A still shows one concept; a flow is ONE coherent narrative — chaining related steps (click → result → next) is right, not "two concepts". Don't cram UNrelated concepts into a single frame.
- **Per-file `prove` for 2+ shots is forbidden.** Use `prove --batch` — one browser launch, N shots.
- **No eyeballing on PASS.** The motor verifies placement: each shot ends in `PASS <out> kb=<n>` or `FAIL <out> reason=<short>`; a batch ends `PROVE <k>/<n> PASS`. Open a PNG with Read ONLY when the verdict is FAIL (to diagnose the wrong selector/state). Never re-read passing shots.
- `prove` exits 0 only on internal vcheck PASS. `FAIL`/`NO_SPACE` are actionable errors (wrong selector, element too small) — fix the input, never the coordinates.
- Annotation text in the page's language unless the user asks otherwise; keep labels short (3-6 words).
- Never hand-write pixel coords or ann.json when `prove.mjs` works — selector + text in, verdict line out.
- Don't rect/circle an element that fills (or nearly fills) the screen — markers outline DETAIL; mark an inner element, or use camera/notes for whole-screen context. The motor skips such markers and warns.
## Portability rules (apply to everything you generate with this plugin)
- No machine-specific commands, no absolute personal paths (`/Users/...`), no personal hosts, no assumptions about local tool builds (ffmpeg drawtext/palettegen vary by build).
- Feature-detect, or let `scripts/preflight.sh` surface missing dependencies with install hints — never assume.
- Anything machine-local (local URLs, tokens, scratch config) stays out of generated docs and committed files.
## MCP fallback
Only when the page needs an authenticated/live session the motor can't reach (requires a browser MCP in the user's Claude Code, e.g. chrome-devtools or `claude mcp add playwright -- npx -y @playwright/mcp@latest`): `annotate.mjs freeze` → MCP `take_screenshot` → `annotate.mjs dims` → `grid` calibration → hand-write `target.json` + `ann.json` → `annotate.mjs check` → `build-inject.mjs` + evaluate → `dataurl-to-png.mjs` → `annotate.mjs vcheck` must PASS. Coords from real PNG pixels (not the downscaled Read preview); only the target rect is green `#16a34a`, everything else neutral dark `rgba(15,23,42,.95)`. Tight crops via `capture.mjs prep/scroll` + `resize_page`. Cursor gifs via `cursor-inject.mjs` + `end-card-inject.mjs` + `webm-to-gif.sh` (recipe: `scripts/drive-recipe.md`).
| Re-reading a PNG whose verdict was PASS | Open with Read ONLY on `FAIL <out> reason=...` |
| Bare screenshot that shows but doesn't explain | zoom + note + badges — make it EXPLAIN |
| Recording without `--dry` first | `--dry`, fix every [MISS] to zero, record ONCE |
| Recording a MOVING reel with `--offline`, `--pace fast`, or `speed` | Realtime `--fps 30`, no `speed` (those are stills-only / no-ops in motion) |
| Releasing follow with `follow:false` | Bare `{"camera":"out"}` — `follow:false` is INVALID |
| Scrolling/camera-moving while a prior step's blur/redact/highlight/rect is still up | Frame the element in the SAME step as the overlay, or clear it first |