photo-slideshow
This skill should be used when the user asks to "make a photo slideshow", "turn photos into a video", "create a photo montage video", "make a picture slideshow with music", "build a memories/recap video from photos", "add Ken Burns pan/zoom to photos", "sync slide changes to the music beat", or "render a video from a folder of images". Covers per-photo Ken Burns motion, transitions, beat-synced timing, mixed aspect-ratio handling, captions/dates, intro/outro cards, and templating a photo folder into a finished video.
1How It Works & Mechanism
Sole absolute rule: No two photos may use the same motion, and this variation must be deterministic pseudorandomness seeded by index.
**No two slides may move the same way.** The instant-giveaway of an amateur slideshow is every photo doing the identical slow zoom-in. Vary direction, zoom in vs. out, and start scale per photo — but make it **deterministic** (seeded by index), so re-renders are identical and a frame-based renderer stays stable.
The seeding mechanism uses mulberry32 + index seeding: the same photo always receives the same motion, while adjacent photos differ.
`mulberry32` is a tiny, fast, well-distributed 32-bit PRNG. Seed it with the photo index so photo 0 always gets the same move, but adjacent photos differ.
Ken Burns only modifies transform, with an initial scale ≥1.05 to ensure panning does not expose blank borders.
Always start scale ≥ `1.05` so a pan never exposes an empty edge.
Mixed aspect ratio photos use blurred-pad (blurred background + contain foreground original); never stretch.
Blurred-pad wins for mixed folders: it fills the frame, never crops the subject, and the soft blur reads as intentional depth rather than empty bars.
Soundtrack first: detect beats offline once and bake them into props; never perform frame-by-frame audio analysis during rendering.
The rule: **lock the music first, derive slide boundaries from it.** Detect beats once, offline, and bake them into props — audio analysis per frame is non-deterministic and slow.
Folder → manifest: natural sorting, EXIF capture time, filename caption extraction—all derived from data.
// natural sort so photo2 < photo10 (default lexical sort breaks this)
Images must be gated by loading states (staticFile + delayRender/continueRender); otherwise, blank frames may flash.
- Photos loaded via `staticFile()`, gated with `delayRender`/`continueRender` so each image (and any caption font) is present before its frame renders — otherwise a slide pops in blank.
The verification loop focuses on whether every photo actually loads and reframes correctly, rather than aesthetic appeal.
A slideshow ingests a whole folder of user photos — the verify pass is mostly *did every photo actually load and reframe without distortion*.
Select only one transition type (default 0.4–0.6s cross-dissolve) and overlap rather than hard-cut, unless cutting exactly on-beat.
Pick **one** transition and keep it consistent — mixing wipes, spins and cubes screams "template."
2Core Capabilities
3External Dependencies
| Kind | Dependency / Endpoint |
|---|---|
| package | remotion(frame/interpolate/Easing/Img/Audio/Sequence) |
| package | @remotion/transitions(TransitionSeries + fade) |
| package | sharp + exifr(读图像尺寸与 EXIF DateTimeOriginal) |
| package | librosa + soundfile(pip 安装;离线测 beat 与 onset) |
| cli | ffmpeg(blurred-pad、zoompan、xfade 与仓库共享 contact-sheet.sh) |
| network | iart.ai(reference 尾部推广链接,具 utm 参数;仅读者点击才请求) |
4Recommended Governance & Evidence
- scripts/: Execute local scripts or commands: No bundled scripts in skill directory; SKILL.md/reference instructs agent to execute `node scripts/build-manifest.
- 网络端点: Declare external network access: No business API calls during execution; photos and music originate from user local files.
- 本地工作目录: File I/O and artifact output: Writes `photos.json`/`props.json`, `beats.json`, `out/<name>.mp4`, `out/*.gif` inside the user project
5Second-Pass Verification
- [discrepancy] 图片加载门控口径是否实现一致 — SKILL.md 要求 staticFile() + delayRender/continueRender 门控,但 templating-folder.md 的 Slideshow 直接把 manifest 里的文件系统路径(join(dir, f))交给 Img/KenBurnsImage,既未用 staticFile 也无 delayRender——照抄 reference 会拿不到 Remotion public/ 之外的资源,且仍可能出现空白帧。
- [discrepancy] 被引用的「打包脚本」是否真的存在 — reference 以 `scripts/build-manifest.mjs` 文件名给出源码、SKILL.md 也写 `node scripts/build-manifest.mjs ./photos`,但仓库根 scripts/ 只有 seek-shot.sh / contact-sheet.sh / probe-mp4.sh,没有 build-manifest.mjs;`python beats.py` 同理(脚本内联在文档里)。使用前必须自行落盘。
- [ok] 起始 scale 与运动确定性口径 — SKILL.md 的 start scale ≥ 1.05、seeded by index 与 ken-burns.md 的 `const base = 1.05;`、`mulberry32(index * 2654435761)` 完全一致。
- [ok] 适配策略与判据是否自洽 — aspect-and-layout.md 的三策略表与 fitStrategy 阈值(<0.15 → cover)一致;SKILL.md 的 never stretch 与 blurred-pad 默认选择一致。
- [ok] 换片节奏的量化声明 — timing-and-audio.md 的每张 3–6s、低于约 2.5s 会显仓促、每 2/4 拍换片,与 SKILL.md 的 photos hold ≥1 musical unit, not every beat 一致。
- [unlocatable] 隐私面:照片元数据是否被提示处理 — manifest 读取 EXIF DateTimeOriginal 并把日期上屏,但全文未讨论照片 GPS/相机信息的处理与输出视频的元数据策略;源码内定位不到任何隐私处理声明,记 unlocatable(能力缺口,不作为档位依据)。
6Verdict & Scenarios
58ffd9961bdf764e…