video-moves
Camera and editing grammar for any code-built video (HyperFrames) - ads, launch films, UI walkthroughs, illustrated scenes. 24 moves (pan, tilt, dolly, zoom, tracking, orbit, crane, dolly zoom, rack f
SKILL.mdskills/video-moves/SKILL.md
GitHubvideo-moves
The grammar of the camera and the cut for any HyperFrames film: an ad, a launch film for a
product or a side project, a walkthrough, an illustrated or abstract scene. Nothing here is
tied to one brand or one piece of material: moves are described in roles (subject, near
props, background, shot A / shot B), the code takes any DOM content, and the styles use
neutral --mv-* colour tokens that each film sets from its own material (the colours of
what is being edited - not a brand).
It came from the "24 camera and editing moves" guide (see THIRD_PARTY_NOTICES.md), ported from Three.js to DOM rigs and tested move by move on a 77 s test reel that staged all 24 on one recorded video call. Each new film stages the moves on its own material.
Input: the film's brief.md (../video-studio/references/brief.md). It owns copy, claims,
tone, material, moments and private data; this skill owns the implementation. If the brief
cannot be met, say so to video-studio - never change it silently.
video-studio orchestrates the film (format, laws, order of work); video-sound, video-build and video-check do sound, building and gates. This skill owns the moves: which one, how to stage it so it reads, the code, and its sound. video-studio's house laws apply to every film, and your brand laws to your brand's films. When a rule here and a law disagree, the law wins.
Setup. What video-build needs (HyperFrames CLI, Node 22+, FFmpeg). For the move sounds:
the sound library's Python venv, installed by ../video-sound/scripts/library.py install.
1. Pick the move by meaning
Every move needs a finished "the camera moves because...". No reason = no move.
| The film needs | Take |
|---|---|
| a hook in the first second | smash cut, crash zoom, dolly zoom |
| the subject close, the key element | dolly in (we come closer), zoom (a sharp accent) |
| scale: one of many, the whole picture | crane up, tilt, pull-out to a grid |
| speed, a process running | tracking, FPV, whip pan |
| the peak of the action, the promise | speed ramp into slow motion, freeze frame on the peak |
| time has passed | time-lapse, jump cut, dissolve |
| two scenes are one idea | match cut, J-cut, L-cut |
| a problem, confusion | dutch angle, handheld |
| the finale | orbit, crane, dissolve into the end card |
| cause here, effect there | whip pan across one world |
Rhythm: main cuts are hard. Effect transitions (whip, dissolve, wipe) go in 2-3 key places only. One move per shot. A move starts 0.1-0.3 s after its cut and an important frame holds at least 1 s after the move lands.
Full catalogue - what each move tells the viewer, how to stage it, its numbers, what must be
visible for it to read, its traps and its sound: references/moves.md. Read the entries for
the moves you pick, not the whole file.
2. Build it
references/rigs.md is the build manual. Its section 0 is a complete two-scene film (a pan and
a dolly in) that builds and renders with the files below as they are - start there. The shape:
- One pure render function. A single linear driver tween on the paused GSAP timeline
calls
render(t); each scene is a HyperFrames clip with anIMPL[id](root, scene)whoseframe(u)writes every transform from local timeualone. Nothing reads the clock,Math.randomor the layout per frame. - Rigs from
scripts/camera-kit.js(inline it into the composition):lens2d(pan, tilt, zoom, roll, shake, whip - no parallax) andpose3don a CSS 3D world under a static-perspective stage (dolly, orbit, crane, tracking, FPV; dolly zoom animates the perspective, which is the focal length). - Story time for slow motion, speed ramp and freeze: one rate curve per scene
(
piecewiseRate→makeStoryTime), written toscenes.jsonso the picture andscripts/move_audio.pyread the same curve. Words, the voice, a scope - everything in the world - live in story time; the camera and the cut live in real time. - Styles and scaffold to copy into a project:
assets/moves.css(rigs, label, readout pill, voice scope, edit strip, focus brackets, freeze border, scene dip; colours are--mv-*tokens - set them per film from the film's own palette) andassets/scaffold.js(scene loop,show(), label timing, 3+3-frame dip, driver tween; its exampleIMPLentries are placeholders you replace with the film's scenes). - The code for each rig - 2D lens, CSS 3D world, story time, A/B transitions, overlays -
is in
references/rigs.mdsections 2-6, with the numbers frommoves.md.
3. Give it sound
references/sound.md: the sound of each move (whoosh with a rising band for the whip,
tone 110 → 520 Hz for the dolly zoom, tape stop for the ramp, silence and a 1760 Hz ring
for the freeze, the hit on the cut frame for the smash), story-time varispeed of a real
recording, J/L-cuts measured from the recording's first word, and the loudness traps.
Synths and helpers: scripts/move_audio.py, run with the library venv:
"${VIDEO_LIBRARY:-$HOME/.local/share/marketing-agent-kit/video-library}/tools/.venv/bin/python" \
${CLAUDE_SKILL_DIR}/scripts/move_audio.py --demo /tmp/move-sounds # every synth, for listening
A film's own audio script imports it (import move_audio as ma); mix with video-sound's
scripts/mix.py.
4. Check that the move reads
Before the full render, stills at the start, middle and landing of every move, and
either side of every cut (video-check scripts/stills.py). For each move ask the
question from its entry in moves.md - "does the foreground pass by (dolly) or get
cropped (zoom)?", "does something visibly slow down?" - a move that needs explaining
does not work. Then video-check's gates and independent review as usual.
5. Generated shots (Atlas)
A model is told the move in words: references/prompts.md has the English camera terms
and one prompt line per move. One move per generated shot; cuts inside one generation are
unreliable - generate two shots and cut them in HyperFrames.
6. When something new is learned
A move used in a real film and approved by the user: note it in its moves.md entry
("Proven in: <film>") and, if it changes how films are made, in ../video-studio/references/laws.md. A move
rejected: write why in its entry and as a rule against it in laws.md. Code fixes go into
scripts/ and assets/; re-run the section 0 example in rigs.md so it keeps rendering.
Note what changed in the film's own notes; for changes to the skill itself, open a PR
(CONTRIBUTING.md).
Adapt it
- Set the
--mv-*tokens per film from that film's own material, aftermoves.css; the defaults are neutral placeholders. - Keep a project of your own that uses the moves on your material (start from
rigs.mdsection 0) as your house example. - Add "Proven in: <film>" or a rejection note to
moves.mdas your films use the moves. - Swap the Kenney sounds in
move_audio.py(KENNEYmap) for sounds from your own library. - Add prompt lines that worked on your video model to
references/prompts.md, with the date.