scroll-cinema
Skill0 starsChanged 20 days agoMITView on GitHub
Build scroll-driven cinematic sections — pinned full-viewport video scrollytelling where the scrollbar scrubs a continuous film, with graceful mobile/reduced-motion tiers; plus the production pipeline for making the film itself (frame-chained AI transitions between real photos via first/last-frame conditioning with MiniMax H3 Max on fal.ai or Venice, ffmpeg assembly, all-intra encoding). Use when the user wants "scroll-locked" video sections, Apple-style scroll scrubbing, a process film for a landing page, seamless transitions between scenes/photos, or asks to animate real product/process photography into a scrolling experience.
- Reads credentials
3 other projects use a version of this.
Adopted by
Most recently taken up by
- Dhyey2907/claude-skill-scroll-cinematheir own version, first seen Oct 13, 2026
- arturmuller25/claude-skillstheir own version, first seen Sep 29, 2026
- tdimino/claude-code-minoantheir own version, first seen Apr 16, 2026
Other versions3 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 3
- tdimino/claude-code-minoan★ 41same name
- Dhyey2907/claude-skill-scroll-cinema★ 1same name
- arturmuller25/claude-skills★ 0same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repositoryOne other file it gives its agents.
- venice-studioSkill
The file
We split this skill into the rules it gives an agent
2 rules. 0 are common, 2 are distinctive, and 0 are very specific to this project.
0
Common
Most of the 3 edited versions we read keep these.
2
Distinctive
This author's own choices. Worth a look.
0
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 73
Write each keyframe prompt as a complete description of ONE frame: subject, framing, camera position, setting, light, palette, texture.
- Distinctive: line 86
Read the brand: palette, register, existing assets.
“Common” is measured: kept by at least 6 in 10 of the 3 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Scroll Cinema
Two crafts in one: (A) the frontend pattern for scroll-driven film sections, and (B) the film production pipeline that makes a continuous, seamless film out of stills, real photos, and AI clips. Built and battle-tested on a production storefront; the pitfalls below were all hit for real.
A. The frontend pattern (capability-tiered, non-negotiable)
One section, three renderings, chosen by capability — this is the difference between "trend site" and "janky site":
| Tier | Who | What |
| Scrub | fine pointer + viewport ≥900px | ONE continuous film; scroll progress drives currentTime via a rAF loop with easing. The scrollbar IS the playhead; scrolling up plays the film backward. |
| Chapters | touch / small screens | The film's acts as separate small looping mp4s, crossfaded by scroll-driven opacity only. NEVER scrub on touch — currentTime-while-native-scroll is the documented jank path on Android. |
| Static | prefers-reduced-motion OR navigator.connection.saveData/2g | Poster images + text, normal document flow. |
Mechanics (see reference-component.jsx for a complete React/framer-motion implementation):
- Track & stage: a tall track (
height: ~25-35vh per film-second) with aposition: sticky; top: 0; height: 100vhstage inside.useScroll({ target, offset: ['start start','end end'] })→ progress 0..1. - Scrub loop: scroll handler writes progress to a ref (never state); a single rAF loop eases the playhead:
smooth += (target*duration - smooth) * 0.14; if (|video.currentTime - smooth| > 0.02) video.currentTime = smooth. The easing converts wheel steps into cinematic inertia. - Text overlays are
opacity/transform-only (GPU-cheap), timed to the film's act boundaries in seconds, fading OUT during camera-travel segments so moves play clean. - Perf gating (mandatory): video
srcattaches only when the section is within ~1.5 viewports (IntersectionObserver +rootMargin: '150% 0px'),preload="none"until then, posters meanwhile. A film withpreload="auto"at mount WILL download megabytes on initial page load — measured mistake. In chapter mode, only the active chapter's video plays; pause the rest. - Video element:
muted playsInlinealways; never call.play()in scrub mode (seek only).
B. Producing the film
The goal is ONE continuous take — no dissolves. Dissolves between unrelated clips read as a slideshow; the user will notice and say so.
Scene strategy (learned the hard way)
- Real photos beat generated scenes. Ground every act in the user's real assets when they exist (product shots, process photos, phone footage frames).
- AI-generated wide shots of technical apparatus WILL have wrong anatomy (tubes to nowhere, impossible plumbing). Two working fixes: (a) stay close — macro framing where each frame only needs local truth; (b) relight, don't invent — use an image-EDIT model with the real photo as reference: "keep this exact [object] and its geometry perfectly identical; re-photograph it in [lighting/scene]". Geometry stays real; only light is invented. This preserved a printed "50/42" joint marking and a product label perfectly.
- Never image-to-video a still that carries text/labels — i2v drifts text (a Jupiter glyph became "24"). For label-bearing stills use Ken Burns instead: forward zoompan + reversed copy concatenated = seamless "breathing" palindrome loop, zero drift, zero cost.
- Grade real daylight photos into the film's palette with ImageMagick/ffmpeg (this is color work, not AI). Pitfall: ImageMagick
-vignettedefaults to WHITE — always-background black -vignette ....
Seamless transitions: first/last-frame conditioning (the core trick)
Default: MiniMax H3 Max on fal.ai. It takes a start frame (image_url) and an end frame (end_image_url) and makes one camera move that starts EXACTLY on frame A and lands EXACTLY on frame B. It's cheap per clip and fast (a 5s move renders in under a minute). Use film.py in this skill's folder. It defaults to fal + H3 Max and also runs on Venice with the same flags:
python3 film.py quote # price of one move, spends nothing
python3 film.py flf A.png B.png "One continuous slow camera move, no cuts: the camera [travel from A to B]." a-b.mp4
python3 film.py check a-b.mp4 A.png B.png # SSIM join check (see below)
python3 film.py still "<full shot description>" B.png --ref A.png --ref product.jpg # keyframes--model | fal endpoint | Venice id | Use |
h3 (default) | minimax/h3-max/image-to-video | minimax-h3-max-image-to-video | Final moves. |
h3-turbo | minimax/h3-max-turbo/image-to-video | minimax-h3-max-turbo-image-to-video | Half price. Use it for drafts and for testing a move's prompt. |
kling | fal-ai/kling-video/v2.5-turbo/pro/image-to-video | kling-2.5-turbo-pro-image-to-video | Fallback when H3 refuses a subject or drifts. |
Provider: --provider fal|venice or $FILM_PROVIDER. Otherwise the script uses fal when it finds a FAL_KEY (env, .env in the cwd or a parent, or ~/.config/fal/env), then VENICE_API_KEY. Keyframes use Nano Banana Pro (nano-banana-pro, or the /edit variant when you pass --ref) on either provider.
H3 Max specifics (verified against the live schemas):
- No aspect parameter. The canvas follows
image_url, so every keyframe must share the film's aspect (generate all at 16:9). Otherwise the end frame gets reframed and the join breaks. durationis 5-15 s (5 is the sweet spot per move).resolutionis480P/768P/1080P. 1080P is an upscale of the 768P latent, so 768P is the default and the scrub encode is 1152-1280 wide anyway.prompt_expansion_modeis required on fal.film.pysendsdisabledso your camera-move sentence is used literally (balanced/qualitylet MiniMax rewrite it).- H3 always renders an audio track, and it can't be switched off. Drop it at encode (
-an). - Submit all moves at once. Each needs only its two finished keyframes, so the film takes about as long as its slowest move. On fal, poll the
status_url/response_urlthe submit call returns: they drop the endpoint's sub-path.
Prompt shape that works: "One continuous slow camera move, no cuts: " + the physical travel from A to B (dolly, crane, orbit, push-in), or a transformation when the style allows it (the scene morphs, the camera passes through a surface). One sentence, with no text or logos in either frame.
Chain rule: every segment must start on the previous segment's literal last frame. With FLF the shared keyframe IS the chain point: segment N ends on keyframe K and segment N+1 starts from K. When assembling, drop the first frame of every segment after the first (trim=start_frame=1) so the shared keyframe doesn't sit on screen for two frames. For a non-FLF segment, extract its exact last frame: ffmpeg -sseof -0.05 -i seg.mp4 -frames:v 1 -q:v 2 chain.png (-sseof BEFORE -i).
This also lets generated camera travels land on real photographs: the transition's end frame IS the real photo. That's how you fuse generated motion with authentic material.
Join check, every clip, before assembly: film.py check scores the clip's first and last frames against the two keyframes (SSIM at 160x90). A clip that lands scores ~0.8-0.9, slightly lower at 480P or when the model lifts the exposure. An unrelated frame scores ~0.1. Below 0.75 means redo or re-prompt the move. Look at the extracted .first.png/.last.png either way.
Keyframes
- Make keyframe A first. Then make every later keyframe as an EDIT that passes A (or the previous shot) as a reference, so place, light and palette carry over. Add the user's real product or subject photo as a reference whenever it must appear, and open the prompt with "Keep the subject from reference N exactly as it is (shape, colours, details)."
- Write each keyframe prompt as a complete description of ONE frame: subject, framing, camera position, setting, light, palette, texture. Don't put readable text or logos in any shot.
- Show the stills to the user, and only animate the ones they approve.
Assembly & encoding (see ffmpeg-recipes.md for full commands)
- Normalize every segment: same WxH (e.g. 1152x648 or 1280x720),
fps=24,setsar=1, crop to fill, never pad (padding bakes black bands into the film). Then use theconcatfilter, with no xfade anywhere. - Scrub master must be all-intra:
-an -c:v libx264 -crf 28 -x264-params keyint=1:scenecut=0 -movflags +faststart. With every frame a keyframe, every scroll position seeks instantly. The cost is ~0.3MB/s at 1152x648, the price of random access, so budget ~8-12MB for 30s. Grainy or B&W film compresses badly: addhqdn3d=2:2:4:4to the normalizer and go to crf 30. - Mobile chapter loops: normal encode (crf 26-27), looped as a palindrome (
split[f][r0];[r0]reverse[r];[f][r]concat) or with a tail-crossfade:[0]trim=end=D-1[v0];[0]trim=start=D-1[v1];[v1][v0]xfade=fade:1:0. - VERIFY EVERY JOIN in the assembled film too: extract frames at boundary±0.1s, montage them side by side, and look.
for t in 4.9 5.1 ...; do ffmpeg -ss $t -i film.mp4 -frames:v 1 j$t.png; done; montage .... Joins that aren't pixel-matched read as cuts.
Budget discipline
Run film.py quote before any batch. On fal it reads the live per-second rate from fal's pricing API. That is the base rate, and higher resolutions cost more (check the model page). On Venice it uses the free /video/quote. Typical cost per 5s move: H3 Max Turbo ~$0.08, H3 Max ~$0.16-0.40 depending on resolution and current fal rates, and a Nano Banana Pro keyframe ~$0.15. A four-act film (5 keyframes + 4 moves) comes to about $1.50-2.50. State the total before generating, and draft moves with h3-turbo when a prompt is untested. Shoot stills, show them to the user, then animate only the winners. A blind API probe with junk input still queues a real paid job, so don't send one. Use throwaway, spend-capped keys and delete them when the film is done.
Order of work (whole feature)
- Read the brand: palette, register, existing assets. Collect the user's REAL photos/footage first.
- Storyboard acts + transitions; get the user's eyes on keyframes BEFORE animating (stills are cheap, motion isn't — and taste vetoes happen at every step).
- Produce keyframes (real > relit-real > generated-closeup, all at the film's aspect), then H3 Max FLF moves on fal (
film.py flf, all queued at once),film.py checkeach, then assemble + verify joins. - Build the component (copy
reference-component.jsxas the starting point), retimeFILM_TEXT_SEC/FILM_BOUNDSto the actual cut. - Perf pass: deferred src, data-saver tier, measure payloads per device class before declaring done.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

