givemotion 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +37 -0
- package/LICENSE +21 -0
- package/README.md +492 -2
- package/SECURITY.md +40 -0
- package/THIRD_PARTY_NOTICES.md +48 -0
- package/blocks/bar-chart.html +63 -0
- package/blocks/browser-window.html +52 -0
- package/blocks/callout.html +57 -0
- package/blocks/captions.html +120 -0
- package/blocks/checklist.html +32 -0
- package/blocks/code-window.html +126 -0
- package/blocks/compare.html +33 -0
- package/blocks/cursor.html +157 -0
- package/blocks/end-card.html +28 -0
- package/blocks/gradient-bg.html +48 -0
- package/blocks/grid-bg.html +18 -0
- package/blocks/highlight.html +13 -0
- package/blocks/line-chart.html +36 -0
- package/blocks/logo-reveal.html +33 -0
- package/blocks/lottie.html +34 -0
- package/blocks/lower-third.html +24 -0
- package/blocks/phone.html +18 -0
- package/blocks/quote.html +19 -0
- package/blocks/stat.html +24 -0
- package/blocks/steps.html +40 -0
- package/blocks/title-card.html +22 -0
- package/blocks/word-pop.html +40 -0
- package/examples/bench/index.html +24 -0
- package/examples/data-story/data.json +9 -0
- package/examples/data-story/index.html +361 -0
- package/examples/data-story/other.json +46 -0
- package/examples/generative/index.html +60 -0
- package/examples/made-by-agents/README.md +5 -0
- package/examples/made-by-agents/photosynthesis/blocks/checklist.html +32 -0
- package/examples/made-by-agents/photosynthesis/blocks/end-card.html +25 -0
- package/examples/made-by-agents/photosynthesis/blocks/gradient-bg.html +48 -0
- package/examples/made-by-agents/photosynthesis/blocks/steps.html +40 -0
- package/examples/made-by-agents/photosynthesis/blocks/title-card.html +22 -0
- package/examples/made-by-agents/photosynthesis/index.html +217 -0
- package/examples/personalized/index.html +28 -0
- package/llms.txt +34 -0
- package/package.json +80 -4
- package/skills/givemotion/SKILL.md +152 -0
- package/skills/givemotion/references/api.md +40 -0
- package/skills/givemotion/references/camera.md +72 -0
- package/skills/givemotion/references/charts.md +138 -0
- package/skills/givemotion/references/check-codes.md +94 -0
- package/skills/givemotion/references/data-templates.md +61 -0
- package/skills/givemotion/references/diagrams.md +75 -0
- package/skills/givemotion/references/genres/code-explainer.md +15 -0
- package/skills/givemotion/references/genres/data-story.md +21 -0
- package/skills/givemotion/references/genres/explainer.md +20 -0
- package/skills/givemotion/references/genres/hand-drawn.md +18 -0
- package/skills/givemotion/references/genres/logo-sting.md +20 -0
- package/skills/givemotion/references/genres/promo.md +18 -0
- package/skills/givemotion/references/genres/ui-demo.md +21 -0
- package/skills/givemotion/references/genres/vertical.md +19 -0
- package/skills/givemotion/references/genres/year-recap.md +18 -0
- package/skills/givemotion/references/javascript.md +72 -0
- package/skills/givemotion/references/media-audio.md +57 -0
- package/skills/givemotion/references/motion-design.md +183 -0
- package/skills/givemotion/references/output.md +64 -0
- package/skills/givemotion/references/review.md +60 -0
- package/skills/givemotion/references/styles.md +133 -0
- package/skills/givemotion/references/techniques/backgrounds-and-ambient.md +85 -0
- package/skills/givemotion/references/techniques/camera-moves.md +88 -0
- package/skills/givemotion/references/techniques/captions-vertical.md +89 -0
- package/skills/givemotion/references/techniques/code-on-screen.md +125 -0
- package/skills/givemotion/references/techniques/hand-drawn.md +87 -0
- package/skills/givemotion/references/techniques/impacts-and-energy.md +106 -0
- package/skills/givemotion/references/techniques/kinetic-type.md +92 -0
- package/skills/givemotion/references/techniques/light-and-depth.md +91 -0
- package/skills/givemotion/references/techniques/logo-reveal.md +94 -0
- package/skills/givemotion/references/techniques/numbers-and-charts.md +92 -0
- package/skills/givemotion/references/techniques/paths-and-maps.md +99 -0
- package/skills/givemotion/references/techniques/scene-transitions.md +85 -0
- package/skills/givemotion/references/techniques/shapes-and-motion-graphics.md +92 -0
- package/skills/givemotion/references/techniques/text-reveals.md +94 -0
- package/skills/givemotion/references/techniques/ui-demo.md +110 -0
- package/skills/givemotion/references/timeline.md +85 -0
- package/src/analyze.js +1485 -0
- package/src/api.js +36 -0
- package/src/args.js +33 -0
- package/src/audio-cli.js +58 -0
- package/src/bench.js +153 -0
- package/src/browser.js +381 -0
- package/src/bundle.js +79 -0
- package/src/cdp.js +94 -0
- package/src/check.js +1818 -0
- package/src/cli.js +611 -0
- package/src/dev.js +26 -0
- package/src/ffmpeg.js +330 -0
- package/src/parts.js +380 -0
- package/src/player/gm-player.js +175 -0
- package/src/render.js +772 -0
- package/src/runtime/audio.js +306 -0
- package/src/runtime/chart.js +665 -0
- package/src/runtime/fonts/BebasNeue-OFL.txt +93 -0
- package/src/runtime/fonts/Caveat-OFL.txt +93 -0
- package/src/runtime/fonts/Inter-OFL.txt +93 -0
- package/src/runtime/fonts/JetBrainsMono-OFL.txt +93 -0
- package/src/runtime/fonts/Nunito-OFL.txt +93 -0
- package/src/runtime/fonts/PlayfairDisplay-OFL.txt +93 -0
- package/src/runtime/fonts/SpaceGrotesk-OFL.txt +93 -0
- package/src/runtime/fonts/bebas-neue-latin-400-normal.woff2 +0 -0
- package/src/runtime/fonts/bebas-neue-latin-ext-400-normal.woff2 +0 -0
- package/src/runtime/fonts/caveat-latin-ext-wght-normal.woff2 +0 -0
- package/src/runtime/fonts/caveat-latin-wght-normal.woff2 +0 -0
- package/src/runtime/fonts/inter-latin-ext-wght-italic.woff2 +0 -0
- package/src/runtime/fonts/inter-latin-ext-wght-normal.woff2 +0 -0
- package/src/runtime/fonts/inter-latin-wght-italic.woff2 +0 -0
- package/src/runtime/fonts/inter-latin-wght-normal.woff2 +0 -0
- package/src/runtime/fonts/jetbrains-mono-latin-ext-wght-italic.woff2 +0 -0
- package/src/runtime/fonts/jetbrains-mono-latin-ext-wght-normal.woff2 +0 -0
- package/src/runtime/fonts/jetbrains-mono-latin-wght-italic.woff2 +0 -0
- package/src/runtime/fonts/jetbrains-mono-latin-wght-normal.woff2 +0 -0
- package/src/runtime/fonts/nunito-latin-ext-wght-normal.woff2 +0 -0
- package/src/runtime/fonts/nunito-latin-wght-normal.woff2 +0 -0
- package/src/runtime/fonts/playfair-display-latin-ext-wght-italic.woff2 +0 -0
- package/src/runtime/fonts/playfair-display-latin-ext-wght-normal.woff2 +0 -0
- package/src/runtime/fonts/playfair-display-latin-wght-italic.woff2 +0 -0
- package/src/runtime/fonts/playfair-display-latin-wght-normal.woff2 +0 -0
- package/src/runtime/fonts/space-grotesk-latin-ext-wght-normal.woff2 +0 -0
- package/src/runtime/fonts/space-grotesk-latin-wght-normal.woff2 +0 -0
- package/src/runtime/motion.js +2674 -0
- package/src/runtime/preset.css +97 -0
- package/src/runtime/runtime.js +2141 -0
- package/src/runtime/sketch.js +1249 -0
- package/src/server.js +402 -0
- package/src/studio/index.html +238 -0
- package/src/types/api.d.ts +354 -0
- package/templates/starter/index.html +70 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Genre: product promo / launch video (15–30 s)
|
|
2
|
+
|
|
3
|
+
Purpose: structure, pacing and the moves that scored well for premium launch videos.
|
|
4
|
+
|
|
5
|
+
| Time (18 s video) | Beat |
|
|
6
|
+
|---|---|
|
|
7
|
+
| 0–3 s | Problem in kinetic type, on the beat ("Copy. Paste. Repeat."), with a visual of the pain (a swarm of task chips, a pile of tabs) |
|
|
8
|
+
| 3–6 s | Reveal: logo or product name, a hit (burst, flash, shake), the product screenshot arriving in 3D |
|
|
9
|
+
| 6–14 s | Three benefits, ~2.5 s each, joined by one camera move or one connecting line, each with its own small visual (not three identical cards) |
|
|
10
|
+
| 14–18 s | End card: logo, call to action and URL, a slow push or shine so it is never still; ≤ 2 s of hold |
|
|
11
|
+
|
|
12
|
+
Moves that scored well:
|
|
13
|
+
- The screenshot tilted in 3D (`rotateY` 12–20°, `transformPerspective`), with a soft shadow, gliding next to a big headline, then the camera pushing into the part of the UI that matters.
|
|
14
|
+
- A live counter or micro-interaction (a "Deploy" button pressed with the cursor, a toggle going live) instead of describing a feature.
|
|
15
|
+
- A glowing path the camera travels along from benefit to benefit (one world), or `morph` carrying the logo or a card between scenes.
|
|
16
|
+
- Cuts on the beat: `npx givemotion audio music.m4a`, then `data-cuts="beats: …"` with `data-cut-align="middle"`.
|
|
17
|
+
|
|
18
|
+
What judges marked down: a flat, small, centered screenshot that just scrolls ("reads like a website demo"), benefits as centered one-per-scene cards, a black strip at the edge of a moving screenshot (the image did not cover its box during the move), text cropped mid-pan, a near-empty frame mid-video, ghost echoes of text, still end cards. Keep the screenshot big (≥ 60% of the frame width) and in motion.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Genre: UI / app demo (15–30 s)
|
|
2
|
+
|
|
3
|
+
Purpose: structure, pacing and the moves that scored well for feature demos with a built UI, a cursor and step captions.
|
|
4
|
+
|
|
5
|
+
Build the UI in HTML/CSS at full size in one camera world, then film it like a zoomed screen recording: the camera stays close on the part that is changing, so it reads on a phone.
|
|
6
|
+
|
|
7
|
+
| Time (18 s video) | Beat |
|
|
8
|
+
|---|---|
|
|
9
|
+
| 0–2 s | Intro: a kinetic title ("Smart scheduling") or a quick wide establishing shot of the UI (`overview: true`, ≤ 1.5 s) |
|
|
10
|
+
| 2–14 s | Steps, 2.5–4 s each: camera pushes to the control, cursor glides and clicks (press ripple), the result appears (panel slides in, slots fill live, skeleton loading), the camera follows the result |
|
|
11
|
+
| 14–16 s | The result lands (the event flies into its slot with `gm.fly`, confetti), camera pulls back to show it in context |
|
|
12
|
+
| 16–18 s | End card or logo, moving |
|
|
13
|
+
|
|
14
|
+
Tools:
|
|
15
|
+
- `cursor` block with selectors and `!` clicks, `show="auto"`, inside the world (`references/data-templates.md`); style `[data-gm-hover]` / `[data-gm-pressed]`.
|
|
16
|
+
- `gm.fly('#event', { from: '#slot2', at: … })` for "the thing moves from the panel into the calendar"; `morph` for state changes across scenes.
|
|
17
|
+
- Camera `fit` on the control plus its context, zoomed so UI text is ≥ 32 px on screen; a `fit` on a tiny button over-zooms, so include its neighbours.
|
|
18
|
+
|
|
19
|
+
Captions: short step captions (3–6 words, ≥ 48 px) in a band the UI never uses (a bar at the bottom with the camera framing the UI above it), or as a label anchored next to the control. Judges repeatedly marked down captions sitting on top of the panel title, a row, or the first slot.
|
|
20
|
+
|
|
21
|
+
What judges marked down: captions covering UI, the booked/finished state frozen for 1.5–2 s, a suggestion panel too small to read on a phone, a messy crossfade where a half-faded panel collides with the button and caption. Keep the last state alive (a gentle push, a highlight pulse) and end within 2 s.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Genre: vertical short (TikTok / Reels / Shorts, 10–30 s)
|
|
2
|
+
|
|
3
|
+
Purpose: structure, pacing, safe zones and the moves that scored well for 1080×1920 social clips.
|
|
4
|
+
|
|
5
|
+
Setup: `<html data-width="1080" data-height="1920" data-safe-zone="shorts">` (or `reels`, `tiktok`). The same pixel sizes as 1080p work (the short side is 1080). Keep the top 220 px and bottom 380 px free of key text and 60 px at the sides; `check` warns (`unsafe-zone`) and `snapshot --grid` draws the zone.
|
|
6
|
+
|
|
7
|
+
| Time (13 s video) | Beat |
|
|
8
|
+
|---|---|
|
|
9
|
+
| 0–1 s | Hook: the first words already big on screen at frame 0, moving |
|
|
10
|
+
| 1–11 s | One phrase per beat: big kinetic type (120–200 px, ≤ 6 words per line) plus a simple graphic for each idea (a code card tilted in 3D, a film strip, an arrow diagram HTML → video); a change every 0.4–0.8 s |
|
|
11
|
+
| 11–13 s | Payoff lockup (logo, the line, a CTA), still pulsing on the beat |
|
|
12
|
+
|
|
13
|
+
Rules:
|
|
14
|
+
- Captions from the voice-over file with the `captions` block (`offset` defaults to 420 px on vertical, above the app buttons; `words` 2–4 per page; preview with `npx givemotion captions captions.json --words 3`). Keep them on for the whole video if the brief asks for captions (judges noticed captions vanishing after 7 s) and keep every required word of the script.
|
|
15
|
+
- Big type must not collide with the captions: give each its own band (type in the upper-middle, captions low).
|
|
16
|
+
- Use the whole height: judges marked down top-heavy layouts with the lower half empty. Stack type, graphic and captions to fill the frame.
|
|
17
|
+
- Cut and pulse on the beat: `npx givemotion audio`, `data-cuts="beats: …"`, `data-pulse="beat 0.06"`. A pulse plus a camera push can push big type off screen: leave margin.
|
|
18
|
+
- Transitions that move the old text out (`push-up`, `cover-up`, `whip-*`), not crossfades: ghosted double words ("IN" over "IN") were the most common error. Check with `snapshot --transitions`.
|
|
19
|
+
- Pick a high-contrast look (Bold Pop or Neon Night); colored type on a colored background failed contrast.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Genre: year recap / stats recap ("wrapped", 15–30 s)
|
|
2
|
+
|
|
3
|
+
Purpose: structure, pacing and the moves that scored well for celebratory stat recaps.
|
|
4
|
+
|
|
5
|
+
| Time (18 s video) | Beat |
|
|
6
|
+
|---|---|
|
|
7
|
+
| 0–2 s | Opener: the year in huge type, rolling or assembling, shapes already drifting |
|
|
8
|
+
| 2–15 s | One stat per moment, ~2.5 s each, cut on the beat: the number counts up big (160–260 px), with its own simple illustration that moves (medals dropping in, a route line drawing, a dot grid filling from 96 to 214, a pin dropping on a city) |
|
|
9
|
+
| 15–18 s | Finale: the year rolls over (2026 → 2027), confetti, the closing line landing late so the end keeps moving |
|
|
10
|
+
|
|
11
|
+
Rules:
|
|
12
|
+
- **Vary the layout per stat.** The weakest recap used "number top-left, graphic right" in every scene and read as animated slides. Alternate: number centered with the graphic around it, a full-frame illustration with the number inside it, a camera move along a route, a split screen.
|
|
13
|
+
- Tell a small story inside a stat: "214 members, up from 96" counts from 96 to 214 while the grid fills; do not sit at 96 for a second before counting.
|
|
14
|
+
- Flowing transitions with direction (`push-*`, `cover-*`, `iris` from the next stat's shape, `morph` of a shape into the next illustration), cut on the beat (`data-cuts="beats: …"`).
|
|
15
|
+
- Ambient shapes drifting behind every scene keep frames alive; keep them, and confetti, off the numbers (judges marked route lines and confetti crossing key numbers).
|
|
16
|
+
- Fill the frame: big empty areas next to a small number were marked down; scale the illustration up.
|
|
17
|
+
- End on motion: the final line ≤ 2 s of hold; a 3.5 s still ending was the worst-rated part of one recap.
|
|
18
|
+
- Bold Pop look (`references/styles.md`); colored type on colored blocks needs contrast (cream on orange failed).
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# JavaScript: onFrame, helpers, particles, canvas
|
|
2
|
+
|
|
3
|
+
Purpose: the `gm.*` JavaScript helpers (frame callbacks, interpolation, springs, noise, seeded random, particles, logging) and the rules that keep scripts seekable.
|
|
4
|
+
|
|
5
|
+
Contents
|
|
6
|
+
- The gm API
|
|
7
|
+
- Particle bursts
|
|
8
|
+
- Rules for seekable JS
|
|
9
|
+
- Canvas, WebGL, libraries, Lottie, GIFs
|
|
10
|
+
|
|
11
|
+
## The gm API
|
|
12
|
+
|
|
13
|
+
```js
|
|
14
|
+
gm.onFrame((t, info) => { box.style.rotate = `${t * 90}deg`; }); // t = VIDEO seconds; info.frame, info.progress
|
|
15
|
+
gm.onFrame('#scene2', (local, info, el) => { /* local time while that clip is on screen; info.time = video time; el = #scene2 */ });
|
|
16
|
+
gm.interpolate(t, [0, 1, 2], [0, 300, 0], { easing: 'out-cubic' }); // numbers or colors; clamps by default
|
|
17
|
+
gm.spring(t, { from: 0, to: 1, stiffness: 170, damping: 26, delay: 0.2 });
|
|
18
|
+
gm.noise(t) /* smooth, 0..1 */, gm.lerp(a, b, p), gm.clamp(v), gm.ease.outExpo(p)
|
|
19
|
+
gm.data // JSON passed with --data / --props, or <script type="application/json" id="gm-data">
|
|
20
|
+
gm.beat(8), gm.bar(2) // video time of beat 8 / bar 2 from <html data-bpm data-beat-offset data-bar-offset> (printed by `givemotion audio`); ready at once, so you can build timelines on the beat: tl.to(..., gm.beat(8)) in a { global: true } timeline
|
|
21
|
+
gm.width, gm.height, gm.fps, gm.duration, gm.px, gm.time
|
|
22
|
+
gm.timing('#scene2') // { start, end, duration } of that clip in video time
|
|
23
|
+
gm.wait(promise) // hold rendering until it resolves (fetch JSON, load a model…)
|
|
24
|
+
gm.onLayout('#scene2', (local, info, el) => { /* runs after all animations are placed: measure with getBoundingClientRect, follow or aim at elements */ });
|
|
25
|
+
gm.color.mix('#ff0', 'oklch(60% .2 260)', 0.5), gm.mapRange(v, 0, 1, 10, 50), gm.wrap(v, 0, 360), gm.snap(v, 10)
|
|
26
|
+
gm.random('dot-7') // the same number in [0,1) for that seed, every frame and every render; gm.rng(seed) → generator; gm.pick(array, seed)
|
|
27
|
+
gm.log('pkg at', x) // like console.log, but `check` and `inspect --at` print it with the video time (repeats are grouped)
|
|
28
|
+
gm.motion // 'natural' (default) or 'classic' (<html data-motion="classic">): which motion defaults the engine uses
|
|
29
|
+
```
|
|
30
|
+
**Which time a callback gets.** With an element (or selector), `onFrame`/`onLayout` call `fn(local, info, el)`: `local` counts from that element's clip start (like `gm.timeline('#scene2')`), `info` also has `time` (video time), `local` and `clipDuration`, and `el` is the element. Without one, `fn(t, info)` gets the **video's** time, wherever the script is. So in a script inside a scene that starts at 12 s, `gm.onFrame((t) => …)` sees 12 at the scene's first frame: write `gm.onFrame('#scene', (local) => …)` instead (`check` warns `onframe-global-time` with that fix; a selector that matches nothing warns `onframe-no-target`). To swap a word or a label at a time, you need no callback: `tl.set('#label', { textContent: 'Done' }, 2)` (`references/timeline.md`).
|
|
31
|
+
|
|
32
|
+
Shapes, paths and sketch path data (`gm.shape`, `gm.path`, `gm.sketch`): `references/diagrams.md`. Timeline and tweens: `references/timeline.md`. Audio analysis (`gm.audio`): `references/media-audio.md`. Charts (`gm.chart`): `references/charts.md`.
|
|
33
|
+
|
|
34
|
+
## Particle bursts
|
|
35
|
+
|
|
36
|
+
```js
|
|
37
|
+
gm.burst('#scene3', { at: 2.1, kind: 'confetti', x: '50%', y: '60%' }) // also 'sparks', 'dust', 'bubbles'
|
|
38
|
+
```
|
|
39
|
+
- `at`: seconds into `#scene3` (add `global: true` for a video time, e.g. a beat or a voice word).
|
|
40
|
+
- `x`, `y`: `'50%'` of `#scene3`'s box, or px at 1080p (`960` or `'960px'`); default the center.
|
|
41
|
+
- `from: '#icon'` throws from that element's center instead of x/y (and follows it; inside a camera world the particles pan and zoom with the camera); `behind: '#icon'` draws under that element (in its layer, so it works in camera worlds).
|
|
42
|
+
- `count`, `colors`, `size` (px), `speed` (px/s), `gravity` (px/s², positive = down), `drag` (how fast pieces slow down), `duration` (s), `spread` (deg), `angle` (deg: -90 up, 90 down, 0 right, 180 left), `stagger` (s over which pieces leave, default 0.06), `seed`. Sizes and speeds are at 1080p.
|
|
43
|
+
- Defaults (count / speed / gravity / drag / size / duration / spread): confetti 160 / 1000 / 1100 / 1.8 / 17 / 2.8 s / 360°; sparks 80 / 1600 / 500 / 3.2 / 6 / 1 s / 360°; dust 60 / 260 / -40 / 1.2 / 6 / 2.2 s / 360°; bubbles 40 / 380 / -120 / 0.8 / 16 / 2.4 s / 120°. All start pointing up (`angle: -90`).
|
|
44
|
+
- The physics: each piece leaves at a random share (12-107%) of `speed` and slows down with `drag`, so it flies at most about `speed / drag` px (confetti: ~550 px) before it only falls; then it falls at about `gravity / drag` px/s (confetti: ~600 px/s). For a wider cloud raise `speed`; for confetti that hangs in the air lower `gravity` (300-500) and raise `duration`.
|
|
45
|
+
- The end: each piece lives 70-100% of `duration` (after its stagger delay) and fades out over the last quarter of its life; then it is gone. Nothing is left on screen after `duration` (+ stagger). For confetti still in the air at the last frame, make `duration` reach past the end of the video.
|
|
46
|
+
- `edge: 'top'` (or `'bottom'`, `'left'`, `'right'`): the pieces start spread along that edge of the target (just outside it) and drift in over the first half of `duration` (up to 1.6 s): confetti raining from above. Its defaults: `angle` pointing inward, `spread: 50`, `speed: 260`; `x`/`y` are ignored.
|
|
47
|
+
- `behind:` works by layer order, so a title that is still fading in (opacity below 1) shows the pieces through it. Fire the burst once the title is opaque, or bring the title in without opacity (a mask, a scale).
|
|
48
|
+
|
|
49
|
+
Recipes:
|
|
50
|
+
```js
|
|
51
|
+
gm.burst('#end', { at: 0.3, kind: 'confetti', edge: 'top', count: 220, duration: 4 }) // confetti falling from above
|
|
52
|
+
gm.burst('#end', { at: 0.2, kind: 'confetti', x: '0%', y: '100%', angle: -60, spread: 25, speed: 2200, count: 90 }) // cannon from the bottom-left corner
|
|
53
|
+
gm.burst('#end', { at: 0.2, kind: 'confetti', x: '100%', y: '100%', angle: -120, spread: 25, speed: 2200, count: 90 }) // and its mirror
|
|
54
|
+
gm.burst('#end', { at: 0.2, kind: 'confetti', speed: 1600, gravity: 450, duration: 3.5, behind: '#title' }) // a round burst behind the title
|
|
55
|
+
```
|
|
56
|
+
- For a big hit, layer it: a burst of sparks + a ring (`tl.fromTo` scale/opacity) + `camera.shake` (or `data-camera="shake 14 2.1-2.5"`) at the same time. Keep particles off key text (judges marked down confetti and rays crossing numbers and headlines).
|
|
57
|
+
|
|
58
|
+
## Rules for seekable JS
|
|
59
|
+
|
|
60
|
+
- In `onFrame`, compute everything from `t`. Don't accumulate state between calls.
|
|
61
|
+
- A callback that writes styles writes its whole state every frame: every property it ever sets, set back (`''` or `none`) when not in use. If one look sets a `box-shadow` and a later look on the same element does not clear it, the shadow stays in the render (frames in order) but not in a `still` (one seek), so stills look right and the video is wrong.
|
|
62
|
+
- Don't toggle classes inside `onFrame` to start CSS transitions; they can't be seeked (`check`: `non-seekable-animation`). To do something "at 2s", use `setTimeout(fn, 2000)` (virtual and exact) or give the element `data-start="2"`.
|
|
63
|
+
- Measure elements in `gm.onLayout`, not in `onFrame` (in `onFrame` boxes are from before this frame's animations).
|
|
64
|
+
- What the engine fixes for every computer, so a render is the same everywhere: the time zone is UTC and the language en-US (`new Date()`, `toLocaleString()`, `Intl` read the same on every machine; pass a time or a format in your data to show another), `prefers-reduced-motion` is `no-preference` and `prefers-color-scheme` is `light` (switch a look with a class or a `data-*` attribute, not a media query), scrolling is instant (`scroll-behavior: smooth` and `behavior: 'smooth'` are ignored), `loading="lazy"` images load at once, and SVG animations (`<animate>`, `<animateTransform>`, `<animateMotion>`, `<set>`) follow the video clock from the start of their clip.
|
|
65
|
+
- Script errors are reported with file and line (`script-error`, `console-error`); fix them first, they can cause misleading pacing warnings.
|
|
66
|
+
|
|
67
|
+
## Canvas, WebGL, libraries, Lottie, GIFs
|
|
68
|
+
|
|
69
|
+
- Canvas size: at `--scale 2` the page has `devicePixelRatio` 2 (the layout stays 1920×1080 but the frame has twice the pixels). A canvas drawn at its CSS size is stretched and looks soft: size it by the ratio, `canvas.width = cssWidth * devicePixelRatio` and `ctx.scale(devicePixelRatio, devicePixelRatio)`.
|
|
70
|
+
- Canvas / WebGL / three.js: draw inside `onFrame` (three.js: `preserveDrawingBuffer: true`). Libraries that animate with `requestAnimationFrame` (anime.js, Motion, GSAP) also work as they are, because the clock is virtual; `gm.timeline` is faster and frame-exact in parallel.
|
|
71
|
+
- Lottie (After Effects JSON): `<gm-include src="blocks/lottie.html" file="anim.json" width="500" height="500">` plays it on the video clock (`lib="path/to/lottie.min.js"` for offline renders).
|
|
72
|
+
- Animated GIF / WebP / APNG in `<img>` play frame-exact on clip time (`data-loop="false"`, `data-rate`).
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Media: video, audio, beats, captions, fonts and images
|
|
2
|
+
|
|
3
|
+
Purpose: `<video>` and `<audio>` elements, mixing, cutting and pulsing on the beat, reacting to sound, captions, fonts and image files.
|
|
4
|
+
|
|
5
|
+
Contents
|
|
6
|
+
- Video and audio elements
|
|
7
|
+
- Plan cuts on the beat
|
|
8
|
+
- Pulse on the beat without code
|
|
9
|
+
- React to sound (gm.audio)
|
|
10
|
+
- Captions
|
|
11
|
+
- Fonts, images, paths
|
|
12
|
+
|
|
13
|
+
## Video and audio elements
|
|
14
|
+
|
|
15
|
+
```html
|
|
16
|
+
<video src="clip.mp4" data-start="2" data-duration="4" data-from="1.5" data-rate="1" data-volume="0.8" muted loop></video>
|
|
17
|
+
<audio id="music" src="music.mp3" data-volume="0.4" data-fade-in="1" data-fade-out="2" data-duck="0.25" loop></audio>
|
|
18
|
+
<audio src="voice.mp3" data-start="1.5"></audio>
|
|
19
|
+
```
|
|
20
|
+
- Video is decoded frame-exact by ffmpeg (any codec); style the `<video>` element with CSS as usual. `data-from` = where to start in the source. `muted` drops its audio.
|
|
21
|
+
- Audio is mixed by ffmpeg: `data-start`, `data-duration`, `data-from`, `data-rate`, `data-volume`, `data-fade-in`, `data-fade-out`, `loop`. Volume curve: `data-volume="0:0 1:0.8 9:0.8 10:0"` (seconds in the clip : volume). `data-duck="0.25"` lowers music to 25% while any other sound plays (voice-over, video with sound).
|
|
22
|
+
- Where the fades sit: `data-fade-in` starts when the audio's clip starts, and `data-fade-out` **ends when the audio's clip ends**. An `<audio>` with no `data-start`/`data-duration` of its own (and not inside a timed scene) lasts the whole video, so `data-fade-out="2"` fades the last 2 s of the video, whatever the file's length (a 32 s song in an 18 s video fades over 16-18 s). Inside a scene, or with `data-duration`, it ends at that clip's end. The file's own length is not used: if the file is shorter than its clip, add `loop` or set `data-duration` to the file's length, or the fade happens after the sound has already stopped.
|
|
23
|
+
- `render v.html -o sound.mp3` (or .wav/.m4a) exports only the soundtrack; `--muted` renders without audio.
|
|
24
|
+
- Sounds that add up past full scale are limited to just under it (a peak limiter on the mix), so voice plus music never crackles; anything quieter is left as it is. Still mix with ears: music around 0.2-0.4 under a voice, ducked while it speaks, so the voice is clear.
|
|
25
|
+
- **Time things to the words.** Give the page the voice-over's word-timed transcript once, `<html data-words="voice.json">` (`[{"word":"Hi","start":0.2,"end":0.5}, …]`, the captions format; Whisper-style `{segments:[{words}]}` works too), then any time can name a word: `data-cuts="0 @spring_pricing @three_plans"` on a sequence (each scene starts when its words are said), `data-start="@pricing"`, `@pricing#2` (the second time), `@pricing.end`, `@pricing+0.3`, and `gm.word('pricing')` in scripts (video time: use it with `gm.timeline(scope, { global: true })`). A word is a moment of the whole video wherever you write it, also on an element inside a scene (`data-start="@waters"`) and in an entrance: `data-enter="rise 0.5 @thirsty"` comes in when "thirsty" is said (`data-exit="fade 0.3 @bye"` leaves on it). Matching ignores case and punctuation (`@needs` finds "needs."). Re-record the voice and the cuts follow. A word that is not in the transcript is the error `word-not-found`, with near spellings.
|
|
26
|
+
- A voice-over sets the length: make scenes follow its sentences (place clips at the times the words are spoken), and fade the music under it with `data-duck`.
|
|
27
|
+
|
|
28
|
+
## Plan cuts on the beat
|
|
29
|
+
|
|
30
|
+
`npx givemotion audio music.mp3` prints the tempo, every beat, bar starts (downbeats), the biggest hits (best moments for a logo or a big cut) and quiet gaps. Beats sit on the kick drum. Copy the printed `data-bpm data-beat-offset data-bar-offset` onto `<html>`; then:
|
|
31
|
+
- `data-cuts="beats: 0 4 8 14"` / `"bars: 0 1 2 4"` on a `data-sequence` cuts on the grid, `data-cut-align="middle"` puts the middle of each transition on the cut (see `references/timeline.md` for where a scene's time 0 then sits).
|
|
32
|
+
- `gm.beat(8)`, `gm.bar(2)` give the video time of beat 8 / bar 2 from `<html data-bpm data-beat-offset data-bar-offset>` (also `data-beats-per-bar`); ready at once, so you can build timelines on the beat: `tl.to(..., gm.beat(8))` in a `{ global: true }` timeline.
|
|
33
|
+
- A camera shot that should land on a beat starts at `beat - duration`. The cursor block takes `times="beats: 16 18"`.
|
|
34
|
+
|
|
35
|
+
## Pulse on the beat without code
|
|
36
|
+
|
|
37
|
+
`data-pulse` on any element scales it up on every beat of the `<html data-bpm data-beat-offset>` grid and lets it fall back: `data-pulse="beat 0.06 0.25"` (amount, decay in s; these are the defaults), `"bar 0.1"`, `"half 0.04"`, `"2 0.08 0.3"` (every 2 beats). It also sets `--beat` (1 on the beat → 0) on the element for your own CSS: `filter: brightness(calc(1 + var(--beat) * .4))`, or `data-pulse="beat 0"` for only the variable. It uses the CSS `scale` property, so it adds to timeline transforms; put it on a wrapper if the element has a scale `data-enter`. A pulse plus a camera push can push big type off screen: leave margin.
|
|
38
|
+
|
|
39
|
+
## React to sound (gm.audio)
|
|
40
|
+
|
|
41
|
+
`const music = gm.audio('#music')` (or a file path like `'music.mp3'`; call it once at the top of your script, or `check` warns `audio-late`). In `onFrame(t => …)`:
|
|
42
|
+
- `music.level(t)` (0..1 loudness), `music.bass(t)` `music.mid(t)` `music.treble(t)`, `music.bands(t, 16)` (spectrum), `music.wave(t, 128)` (waveform), `music.beat(t, 0.25)` (1 on each beat, fading to 0: great for pulses).
|
|
43
|
+
- All `music.*` take **video time**: in `gm.onFrame('#scene', (t, info) => …)` pass `info.time` (`t` there is the scene's local time).
|
|
44
|
+
- `music.beats` (`gm.audio(...).beats`) / `music.downbeats` / `music.onsets` (times in the video, to cut on the beat), `music.bpm`. To build a timeline from beats at load time: `gm.wait(music.ready.then(() => { const b = music.beats; gm.timeline().to('#logo', { scale: 1.2, duration: 0.2 }, b[4]); }))`. Beat lists only cover the part of the song that plays in the video.
|
|
45
|
+
- `check` codes: `audio-not-found` (no file/element), `audio-no-beats` (no steady beat), `audio-late` (called after the video started).
|
|
46
|
+
|
|
47
|
+
## Captions
|
|
48
|
+
|
|
49
|
+
- The `captions` block shows word-by-word captions (TikTok/Reels style) from a word-timed JSON file (`[{"word":"Hello","start":0.2,"end":0.5}, …]`, times relative to the block's start) or an `.srt`: `<gm-include src="blocks/captions.html" file="captions.json"></gm-include>`. Params: `words` (max words per line, default 5), `accent`, `position` (bottom|center|top), `offset` (420 px on vertical video by default, above the app buttons), `size` (default 64), `weight`, `upper`, `hold` (seconds the last line stays; 0 = gone with the word, so it does not run into the next scene).
|
|
50
|
+
- Preview how captions will page: `npx givemotion captions captions.json --words 3`.
|
|
51
|
+
- Captions must not cover the subject: judges marked down captions over the UI being shown. Put them in a band the layout leaves free, or move the content up. Keep them on for the whole video if the brief asks for captions.
|
|
52
|
+
|
|
53
|
+
## Fonts, images, paths
|
|
54
|
+
|
|
55
|
+
- Paths are relative to the HTML file (`image-broken`, `http-404`, `video-frame` mean a wrong path).
|
|
56
|
+
- Use local fonts (`@font-face` with a file next to the HTML) for identical results on every machine; built in (no file needed): `Inter`, `JetBrains Mono`, `Playfair Display`, `Space Grotesk`, `Bebas Neue`, `Caveat`. Web fonts (a Google Fonts `<link>`) also load and the render waits for them, but they need the network on every render.
|
|
57
|
+
- Animated GIF / WebP / APNG in `<img>` play frame-exact on clip time (`data-loop="false"`, `data-rate`).
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
# Motion design: principles with numbers
|
|
2
|
+
|
|
3
|
+
Purpose: how to make a givemotion video feel like real motion design instead of slides; concrete timings, easing, camera grammar, continuity, and the mistakes blind judges punished in our test rounds.
|
|
4
|
+
|
|
5
|
+
Contents
|
|
6
|
+
1. What judges punished and rewarded
|
|
7
|
+
2. Timing and pacing numbers
|
|
8
|
+
3. Easing and movement principles (with recipes)
|
|
9
|
+
4. Camera grammar
|
|
10
|
+
5. Scene-to-scene continuity (and what every transition looks like)
|
|
11
|
+
6. Layout, type and hierarchy (a line that builds word by word, step captions)
|
|
12
|
+
7. Anti-patterns checklist
|
|
13
|
+
|
|
14
|
+
## 1. What judges punished and rewarded
|
|
15
|
+
|
|
16
|
+
Across nine rounds, fresh agents made videos from these docs and blind judges (senior motion designer brief, 2 fps contact sheets) scored them. Counted over the round-8 notes:
|
|
17
|
+
|
|
18
|
+
| Punished (share of notes) | What it looked like |
|
|
19
|
+
|---|---|
|
|
20
|
+
| Near-still holds (2 of 3 notes) | End card or a "booked" state frozen 1.5–3.5 s; a counter sitting at 96 for a second before it counts |
|
|
21
|
+
| Small secondary text (2 of 3) | Taglines, card subtitles, axis labels, code in an editor, labels in the final wide shot |
|
|
22
|
+
| Text cut by the frame (1 of 2) | During or after camera pans; a neighbour's label half in frame while the shot is about something else |
|
|
23
|
+
| Empty space / unbalanced (2 of 5) | Small UI in a big empty frame; layouts crowding the top-left or the top half (vertical) |
|
|
24
|
+
| Ghosted text (1 of 5) | Crossfades where two scenes' text sit half-visible on top of each other |
|
|
25
|
+
| Slide/website feel | A flat centered screenshot that just scrolls; one centered card per benefit; every scene the same "big number top-left, graphic right" template |
|
|
26
|
+
| Weak first frame | Nearly empty opening, 0.5–1.5 s where nothing changes |
|
|
27
|
+
| Whiteboard-specific | Thin pale ink (3 px), tiny labels in the overview, the camera crossing empty paper, lines "too clean" |
|
|
28
|
+
|
|
29
|
+
Rewarded: a camera that pushes into the subject (zoom into the code with numbered callouts, dim the rest); one world with one camera route; a camera that follows a moving thing (the line head of a chart, a package on a map); tight zooms that stay readable on a phone; varied kinetic type; one strong hit (burst + shake + flash) at the key moment; things that move from frame one; an element carried from one scene into the next; live counters tied to the visual; a clear payoff at the end. Videos the judges preferred changed 26–40% of the picture per half second; slide-like ones 12–22%.
|
|
30
|
+
|
|
31
|
+
## 2. Timing and pacing numbers
|
|
32
|
+
|
|
33
|
+
| What | Number |
|
|
34
|
+
|---|---|
|
|
35
|
+
| First frame | Something on screen at 0.0 s and moving by 0.3 s; first message readable by ~1 s |
|
|
36
|
+
| Rhythm | A new change (entrance, move, cut, emphasis) every 0.5–1.5 s |
|
|
37
|
+
| Scene length | One idea per scene, 2.5–5 s (shorter for social, up to 6–8 s for one camera world step) |
|
|
38
|
+
| Reading hold | After text lands: ~1 s + 0.25 s per word. A 4-word headline holds ~2 s; never under 1 s |
|
|
39
|
+
| Element life | Plan it per element in the shot list: **in** (its entrance), **hold** (≥ 1 s + 0.25 s per word, ≥ 1 s for an icon or a picture, counted from fully in until it starts to leave or is covered), **out** (an exit, the scene's transition, or a hand-over: morph, `gm.fly`, the next element born from it). A card that lands on a word and is covered or cut a second later was never seen; something that blinks off with no exit cannot be transitioned out of (`check`: `short-hold`, `cut-exit`) |
|
|
40
|
+
| Holding is not freezing | During a hold keep a slow motion alive: camera `push 1.04–1.08`, `float`, a counter, a drifting background |
|
|
41
|
+
| End card | 1.5–2 s, with motion (slow push, a shine, the last word landing late); `still` fires at 1.9 s at the very end |
|
|
42
|
+
| Small element entrance (icon, chip, label) | 0.25–0.45 s |
|
|
43
|
+
| Text entrance | 0.5–0.8 s (per piece with stagger) |
|
|
44
|
+
| Big panel or full-frame move | 0.7–1.2 s |
|
|
45
|
+
| Camera move between shots | 0.8–1.6 s (fast push on a hit: 0.4–0.6 s) |
|
|
46
|
+
| Transition | 0.4–0.8 s; whip 0.3–0.5 s; morph 0.7–1.0 s |
|
|
47
|
+
| Exit | ~60–70% of the entrance duration, ease-in; or let the camera/transition carry it out |
|
|
48
|
+
| Stagger | 40–90 ms per item; keep the whole group under ~0.6 s (`stagger: { amount: 0.5 }`) |
|
|
49
|
+
| Overlap | Start the next move 0.1–0.3 s before the previous one ends (`'-=0.2'`, `'<0.1'`) |
|
|
50
|
+
| Social / vertical | Faster: a change every 0.4–0.8 s, scenes 1.5–3 s |
|
|
51
|
+
|
|
52
|
+
## 3. Easing and movement principles (with recipes)
|
|
53
|
+
|
|
54
|
+
- **Ease-out on entrances, ease-in on exits.** Things arriving decelerate into place (the default springs do this; by hand: `out-expo`, `out-quart`, `out-back`, `spring`); things leaving accelerate away (`in-quad`, `in-cubic`, `in-expo`). Moves from A to B on screen use in-out (`power3.inOut`, `in-out-quart`). Linear only for constant drifts, tickers and rotations.
|
|
55
|
+
- **Weight.** Big things move slower and overshoot less; small things are quick and can bounce. A full-frame panel: 0.9 s `out-quart`; a chip: 0.35 s `back.out(1.6)`.
|
|
56
|
+
- **Anticipation.** Before a big, fast move, a small move the other way (5–10% of the distance, 0.12–0.2 s, ease-in):
|
|
57
|
+
```js
|
|
58
|
+
gm.timeline('#s2')
|
|
59
|
+
.to('#card', { x: -30, scale: 0.97, duration: 0.15, ease: 'power2.in' }, 1.0)
|
|
60
|
+
.to('#card', { x: 640, scale: 1, duration: 0.6, ease: 'expo.out' });
|
|
61
|
+
```
|
|
62
|
+
- **Overshoot and settle (follow-through).** Landings go slightly past and settle within ~0.3 s: `back.out(1.4–1.8)`, `bouncy`, `spring`, or presets `pop`, `drop`, `elastic`. Use it on the hero, not on everything (a whole screen of bouncing items looks cheap).
|
|
63
|
+
- **Overlapping action and secondary motion.** Parts of one thing do not stop together: the card lands, its icon arrives 60–120 ms later, its label 60–120 ms after that; a slight rotation (3–8°) that settles to 0; a shadow that grows as it lifts.
|
|
64
|
+
- **Arcs.** Natural movement curves. Move x and y with different eases at the same time, or travel along a path:
|
|
65
|
+
```js
|
|
66
|
+
gm.timeline('#s3')
|
|
67
|
+
.to('#ball', { x: 900, duration: 0.9, ease: 'power2.inOut' }, 2)
|
|
68
|
+
.to('#ball', { keyframes: [{ y: -160, duration: 0.45, ease: 'sine.out' }, { y: 0, duration: 0.45, ease: 'sine.in' }] }, 2);
|
|
69
|
+
```
|
|
70
|
+
For longer routes use `gm.path.travel` with `rotate: true` (`references/camera.md`).
|
|
71
|
+
- **Start states hide completely.** A `from`/`fromTo` start state is on screen from the clip's first frame until the entrance: `opacity: 0`, `scale: 0` or `clipPath: 'inset(50%)'` (`inset(50% 0)` / `inset(0 50%)` to open from the middle line). `inset(45% 0)` leaves a 10% sliver sitting there, which reads as a glitch (`check`: `sliver-start`).
|
|
72
|
+
- **Stepped motion only on small things.** `steps(n)` is a fine brand language for a counter, a blinking caret or text typing, but on a window, a card or the camera (more than ~15% of the frame) at 30 fps viewers read it as dropped frames (`check`: `stepped-move`). Give big moves a smooth ease.
|
|
73
|
+
- **Squash and stretch, lightly.** A landing can flatten for 0.08–0.12 s (`scaleX: 1.08, scaleY: 0.92`) and recover; skip it for text and UI.
|
|
74
|
+
- **Contrast of speed.** Fast in, slow drift while it is read, fast out. Constant medium speed everywhere reads as software.
|
|
75
|
+
- **Emphasis.** To make a word or number matter: scale it 1.1–1.3, change its color to the accent, add a mark (`data-sketch-mark`), a flash or a small shake. One emphasis per beat.
|
|
76
|
+
- **Marking a thing on screen** (a highlight, a glow, an underline, a ring) to say "look here":
|
|
77
|
+
- It arrives after its target has landed, and holds ≥ 1 s (as text: ≥ 1 s + 0.25 s per word it covers).
|
|
78
|
+
- It stays on its target. If the target moves, scrolls or the camera pushes, re-measure it every frame in `gm.onLayout` (`references/javascript.md`); a box measured once at load floats off. Measure only after fonts load (`gm.onLayout` runs after they have), never in the script's top level.
|
|
79
|
+
- It belongs to what it marks: put it inside the target's card or window, so it is clipped by that card and goes under whatever covers the card. A mark painted on top of everything runs over the window that hides half the card.
|
|
80
|
+
- One mark per thing. Two marks stacked on one line read as one darker mark with a seam.
|
|
81
|
+
- To mark the next thing, let this mark leave and the next one appear on its target. A mark that slides across the screen to the next target reads as a glitch (except along a list, row to row).
|
|
82
|
+
- Pick one look for the whole video from the style or brand (a highlighter wash for text, a glow on a card's own edge, an underline, a drawn ring for hand-drawn styles) and keep it. The look is taste; the rules above are not.
|
|
83
|
+
- **A big hit** (logo lock, peak number): at the same time, a burst of sparks, a ring that scales 0.6→1.6 while fading, `camera.shake` 10–16 px for 0.3–0.4 s, a 0.1 s brightness flash. Put it on a music hit if there is music.
|
|
84
|
+
- **Motion blur** is on by default (natural motion): whatever moves fast in a frame gets a camera's smear along its move, worked out per frame, for almost no render time. `--motion-blur off` (or `<html data-motion="classic">`) keeps every frame sharp; `--motion-blur 8` renders 8 sub-frames per frame instead (true blur, 8x slower). Whip transitions and `rise`/`blur` entrances already carry their own blur.
|
|
85
|
+
|
|
86
|
+
### What the engine already does for you (natural motion, the default)
|
|
87
|
+
|
|
88
|
+
Leave out `ease` and `duration` unless you need exact timing, and you get these for free. Setting an ease on everything throws them away (`check` warns `same-ease` when one curve is used for almost everything).
|
|
89
|
+
- **Entrances have mass.** `data-enter` presets and timeline entrances spring in: fades, slides, masks and text land crisp (no overshoot); `pop`, `grow`, `scale`, `spin-in`, `flip-up`, `stretch`, `tilt` overshoot a hair and settle. Explicit names: `natural(0.7)` (a spring released from rest; the number is the damping, 1 = no overshoot) and `natural-move` (a move that gathers speed and settles).
|
|
90
|
+
- **Exits leave under their own power.** A `to()` that ends at opacity 0 or scale 0 eases in (slow start, leaves at speed) instead of settling.
|
|
91
|
+
- **Bigger moves take a little longer.** Without a duration, timeline moves last about 0.4–0.65 s and `gm.fly` 0.65–1.05 s depending on the distance; presets that travel by the element's size scale the same way.
|
|
92
|
+
- **Moves flow.** Back-to-back tweens on the same property (one ends where the next starts) carry their speed through the joint instead of stopping.
|
|
93
|
+
- **Arcs.** Diagonal x+y moves and `gm.fly` bend slightly upward like a thrown object. `arc: 0` for a dead-straight line, `arc: 0.2` for more.
|
|
94
|
+
- **Staggers breathe.** Staggered rows vary a few percent in timing (seeded, the same every render); the first and last item keep their times.
|
|
95
|
+
- **Moving text glides.** Chrome puts text on whole pixels when it only moves or scales, so slow moves and camera drifts made it step and wobble against its box. The engine gives every element moved by an inline transform (timeline tweens, the camera) or by your own CSS `@keyframes` animation a tiny `rotate` (about 0.04°, invisible) so its text lands at quarter pixels. An element with a `rotate` of its own is left alone.
|
|
96
|
+
- **The camera moves like an operator** (`references/camera.md`): back-to-back shots become one continuous move, `follow` trails its target and settles, held shots breathe by a pixel or two, fast zooms overshoot a touch, and backgrounds next to the world drift for depth.
|
|
97
|
+
- `gm.motion` is `'natural'`; `<html data-motion="classic">` brings back the older fixed curves and cameras that stop between shots (only to match an earlier render).
|
|
98
|
+
|
|
99
|
+
To see how natural a render is: `npx givemotion analyze out/video.mp4 --motion` gives a 0–10 naturalness score with the camera stops, stop-and-go moments, sudden velocity changes and strobing frames it found; `--strips` writes 10 fps strips of the busiest moments (`references/review.md`).
|
|
100
|
+
|
|
101
|
+
## 4. Camera grammar
|
|
102
|
+
|
|
103
|
+
- **Every scene has a camera.** At least `data-camera="push 1.04–1.1"` or `"float"`; a locked-off frame with one element fading in is the slide look.
|
|
104
|
+
- **Shot order:** establish (wide, short, ≤ 1.5 s) → push in to the detail (zoom until text reads ≥ 32 px) → move to the next detail (short distance, through content, never across empty space) → wide recap at the very end, brief, with key labels scaled up.
|
|
105
|
+
- **Motivated moves.** The camera moves because something moves or matters: it follows the line head, the package, the cursor, or pushes in when a callout appears. Start a shot when the thing appears, not before.
|
|
106
|
+
- **Lead room.** A subject moving right sits in the left third with space ahead (`offset: [260, 0]` on a follow shot). A subject looking or pointing somewhere gets space on that side.
|
|
107
|
+
- **Frame cleanly.** Every label fully in or fully out of a held shot. Fit the subject together with its label.
|
|
108
|
+
- **Land on time.** A shot's time is when the move starts; to land on a beat or on a scene's key moment, start it `duration` earlier. Finish before the scene's exit transition (`camera-late`).
|
|
109
|
+
- **Depth.** Backgrounds next to a camera world (grid-bg, gradient-bg, full-frame layers behind it) drift with the camera by themselves; `data-parallax` 0.2–0.5 sets a layer's depth yourself, `data-parallax="0"` pins it; a soft blur on far layers helps. 3D tilt (`rotateX`/`rotateY` with `transformPerspective`) for screenshots and cards beats a flat rectangle.
|
|
110
|
+
- **Impacts** use `shake`; a handheld feel (`data-camera="handheld"`) suits documentary or energetic styles, not clean corporate ones.
|
|
111
|
+
- **A camera budget.** One deliberate move per shot (a push, a pan to the next detail, or a slow drift), then hold while it is read. Do not push in and fly straight back out (in and out within ~2 s), and do not chain big zooms (a scale change of 1.5× or more) one after another: the camera reads as hunting, and viewers tire. To show a new framing after a push, cut or dissolve to it instead of flying back. The judges' contact sheets reward motion; people watching a real 90 s product demo found a zoom in every scene too much. The longer the video, the calmer the camera: in videos over ~45 s and in product demos, most shots are one move or a drift.
|
|
112
|
+
|
|
113
|
+
## 5. Scene-to-scene continuity
|
|
114
|
+
|
|
115
|
+
- **Carry something across.** `data-morph` a logo, card, number or shape into the next scene (`references/timeline.md`), or end one scene's element where the next scene's element starts (match cut: same position, shape or color).
|
|
116
|
+
- **Direction means something.** Keep a consistent direction for "next" (e.g. always `push-left`/`whip-left`); `zoom-through` for going into detail; `iris` or a wipe from the hero element for a reveal; `dip` for a chapter break.
|
|
117
|
+
- **Prefer one world.** Many of the best-scored videos were one big world (map, whiteboard, dashboard, chart) with one camera route instead of separate scenes.
|
|
118
|
+
- **Exit text before a crossfade**, or use a transition that moves the old scene out (push, whip, cover, wipe, iris). Plain `fade` between two text scenes ghosts.
|
|
119
|
+
- **Mid-transition frames count.** Look at `snapshot --transitions`: the middle frame must not be nearly empty or a muddy mix.
|
|
120
|
+
|
|
121
|
+
What each `data-transition` looks like (direction = where the motion goes; `wipe-right` reveals from left to right):
|
|
122
|
+
- `fade` (= `crossfade`): the new scene fades in over the old. Ghosts text; exit the old text first.
|
|
123
|
+
- `push-left/right/up/down`: the new scene pushes the old one out, both moving together (`slide-*` is the same with a softer curve).
|
|
124
|
+
- `cover-left/right/up/down`: the new scene slides in over the old one, which stays put.
|
|
125
|
+
- `whip-left/right/up/down`: a fast push with motion blur (0.3–0.5 s), for energy and beat cuts.
|
|
126
|
+
- `wipe-left/right/up/down`: a hard straight edge crosses the frame and reveals the new scene behind it. No color.
|
|
127
|
+
- `diagonal`: like `wipe-right`, but the edge is slanted (its top runs ahead of its bottom). No color, no band.
|
|
128
|
+
- `band-right/left/up/down` (`band` = `band-right`): a solid color band sweeps across and the new scene is revealed behind it. `data-transition="band-right 0.4 #ff5a36 30% 20deg"`: duration, color, band width (share of the frame side it travels along, default 35%; `100%` covers the whole frame at the middle, a full color wipe), edge slant in degrees (default 0). Without a color it uses `--transition-color` on the incoming scene (so each scene can bring its own color), else `--accent`, else pink. The band's center crosses the frame's center exactly halfway, so with `data-cut-align="middle"` that moment sits on the cut (keep its default symmetric easing). It also works as an entrance on any HTML element: `data-enter="band-right 0.6 #fff"`.
|
|
129
|
+
- `clock`: a radial sweep like a clock hand. `iris`: a circle grows from the center.
|
|
130
|
+
- `zoom`: the new scene settles from 125% while fading in. `zoom-through`: the old scene flies past the camera (grows, blurs, fades) and reveals the new one.
|
|
131
|
+
- `flip`: a 3D card flip around the vertical axis. `dip`: down to black and back up. `blur`: the new scene comes in from a blur. `fade-up`: both scenes drift up while crossfading.
|
|
132
|
+
- `morph`: elements with the same `data-morph` fly from the old scene to the new; the rest crossfades.
|
|
133
|
+
- **Keep one look.** Same palette, fonts, corner radius, stroke width and motion personality across scenes (`references/styles.md`); change layout and camera, not the style.
|
|
134
|
+
|
|
135
|
+
## 6. Layout, type and hierarchy
|
|
136
|
+
|
|
137
|
+
- Sizes on screen (after camera zoom), 1920×1080: titles 96–160 px, kinetic lines 120–260 px, body ≥ 36 px, captions ≥ 56 px, secondary ≥ 32 px; ≥ 96 px from the edges; ~10 words on screen at most.
|
|
138
|
+
- **Fill the frame.** The subject (or the group being shown) should cover roughly 40–70% of the frame. If a UI or code window looks small in empty space, zoom the camera in or scale it up, do not shrink the text.
|
|
139
|
+
- **Balance, not centering everything.** Use the thirds (`snapshot --grid`): a hero on one third with its supporting text on the other; a big number with a graphic that fills the opposite side. Vary the layout per scene. In vertical video use the whole height, not just the top half.
|
|
140
|
+
- **One hero per moment.** Dim the rest (opacity 0.3–0.4 or blur) when zooming into a detail; judges liked code with the other lines dimmed.
|
|
141
|
+
- **Contrast.** 4.5:1 for normal text, 3:1 for large; colored type on a colored background is the usual failure; add a backing, a shadow or change the color.
|
|
142
|
+
- **Shapes, lines and icons** drawn in SVG make it look designed; drop shadows, glows and grain give depth; avoid a page of rounded cards.
|
|
143
|
+
|
|
144
|
+
**A centred line that builds word by word.** With `data-split="words"` + `data-enter` + `data-stagger` the whole line is laid out from the start and each word appears where it will end up, so the first word sits left of center until the rest arrive. That is right for most kinetic type. If the words shown so far must stay centred as the line grows, let each word grow from zero width (the line re-centres smoothly as it widens; the space goes inside the word):
|
|
145
|
+
```html
|
|
146
|
+
<style>
|
|
147
|
+
.build { text-align: center; white-space: nowrap; }
|
|
148
|
+
.build .w { display: inline-grid; grid-template-columns: 0fr; animation: word-grow .5s var(--ease-out-expo) both; animation-delay: calc(.3s + var(--i) * .4s); }
|
|
149
|
+
.build .w > span { min-width: 0; overflow-x: clip; overflow-clip-margin: .2em; animation: fade-up .5s var(--ease-out-expo) both; animation-delay: inherit; }
|
|
150
|
+
@keyframes word-grow { to { grid-template-columns: 1fr; } }
|
|
151
|
+
</style>
|
|
152
|
+
<h1 class="build"><span class="w" style="--i:0"><span>Ship</span></span><span class="w" style="--i:1"><span> it</span></span><span class="w" style="--i:2"><span> faster.</span></span></h1>
|
|
153
|
+
```
|
|
154
|
+
(CSS animations are seekable; their delays count from the start of the element's clip.)
|
|
155
|
+
|
|
156
|
+
**Step captions** (one short caption at a time, e.g. the steps of a demo; for word-timed subtitles of a voice use the `captions` block): put them in a `data-sequence` with `data-cuts` at the moments each step starts, an entrance and a short exit on each, and **no** `data-transition`. Each caption leaves (0.15 s) before the next comes in, so two captions are never on screen together:
|
|
157
|
+
```html
|
|
158
|
+
<div class="captions" data-sequence data-cuts="0.4 1.8 3.2 4.6" style="position:absolute;left:0;right:0;bottom:120px;text-align:center">
|
|
159
|
+
<p data-enter="mask-up 0.35" data-exit="fade-up 0.15">Connect your repo</p>
|
|
160
|
+
<p data-enter="mask-up 0.35" data-exit="fade-up 0.15">Pick a template</p>
|
|
161
|
+
<p data-enter="mask-up 0.35" data-exit="fade-up 0.15">Press deploy</p>
|
|
162
|
+
<p data-enter="mask-up 0.35">Live in 30 seconds</p>
|
|
163
|
+
</div>
|
|
164
|
+
```
|
|
165
|
+
A crossfade between captions (`data-transition="fade"`) shows both half-visible on top of each other (ghosting) and, since sequence children sit in normal flow, stacks them for a moment. If you want them to overlap, keep it to 0.1 s and make the captions `position: absolute` in the same spot.
|
|
166
|
+
|
|
167
|
+
## 7. Anti-patterns checklist
|
|
168
|
+
|
|
169
|
+
Before rendering, none of these should be true:
|
|
170
|
+
- [ ] A frame where nothing moves for > 1 s (look at the motion strip and neighbouring sheet cells).
|
|
171
|
+
- [ ] The first frame is empty or only background.
|
|
172
|
+
- [ ] Any text under 32 px on screen that matters, or any text cut by the frame.
|
|
173
|
+
- [ ] A scene that is a centered title fading in and out.
|
|
174
|
+
- [ ] The same entrance (`fade-up` everywhere) or the same layout in every scene.
|
|
175
|
+
- [ ] A plain `fade` between every scene.
|
|
176
|
+
- [ ] The camera crosses empty space, or a wide shot holds for long with tiny labels.
|
|
177
|
+
- [ ] The camera pushes in and straight back out, or big zooms come one after another.
|
|
178
|
+
- [ ] A mark (highlight, glow, ring) that floats off its target, runs over a window covering its card, or sits on top of another mark.
|
|
179
|
+
- [ ] Particles, arrows or shapes crossing key text.
|
|
180
|
+
- [ ] The end card sits still for 2 s or more.
|
|
181
|
+
- [ ] Something that lands and is gone, covered or cut a second later; an element with no planned way out.
|
|
182
|
+
- [ ] A start state that leaves a sliver showing before the entrance; `steps()` on a big panel or the camera.
|
|
183
|
+
- [ ] The video drifts from the brief (a required part missing, a claim on screen that does not match what is shown, a dropped word from a required line).
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Output: render options, re-rendering, overlays, embed, React
|
|
2
|
+
|
|
3
|
+
Purpose: everything about producing files: render flags and formats, the parts cache, render speed, transparent overlays, standalone pages and the `<gm-player>` embed, and React/JSX compositions.
|
|
4
|
+
|
|
5
|
+
Contents
|
|
6
|
+
- Render options
|
|
7
|
+
- Re-rendering is fast (the parts cache)
|
|
8
|
+
- Render cost and speed
|
|
9
|
+
- Overlays with transparency
|
|
10
|
+
- Share and embed
|
|
11
|
+
|
|
12
|
+
## Render options
|
|
13
|
+
|
|
14
|
+
`npx givemotion render video.html -o out/video.mp4 [options]`
|
|
15
|
+
|
|
16
|
+
`-o out.mp4|.webm|.mov|.gif|out.mp3|folder/` (format from the extension; a folder gives PNG frames; `--format mp4|webm|mov|gif|png`) · `--quality draft|standard|high|max` · `--crf <n>` · `--fps 60` · `--width` / `--height` · `--scale 2` (4K from a 1080p layout; `4/3` → 1440p) · `--frames 0-90` or `2s-5s` (only that part, as a new file) · `--redo 4s-7s` · `--no-cache` · `--cache-dir <dir>` · `--transparent` (with `.mov` ProRes 4444 or `.webm`) · `--muted` · `--motion-blur off|auto|8` (default `off`; `--shutter 0.5` = 180°) · `--codec h265|av1` · `--hardware` (GPU encoder; on a Mac on by default once it checks out, `--no-hardware` to turn off) · `--workers N` · `--data f.json` · `--props '{…}'` · `--seed 7` (seed for `Math.random`, default 1) · `--force` (render even with errors) · `--json` · `--progress json`.
|
|
17
|
+
|
|
18
|
+
- `still video.html -t 3.2 -o f.png` (or `--frame <n>`) renders one exact frame; `--transparent` for a PNG with alpha.
|
|
19
|
+
- `render v.html -o sound.mp3` (or .wav/.m4a) exports only the soundtrack.
|
|
20
|
+
- Motion blur is on by default (`--motion-blur auto`): what moves fast in a frame gets a smear along its move (0-3% render time on the examples; the render log says how many frames). `--motion-blur off` keeps frames sharp (also `data-motion="classic"`). A number forces that many sub-frames on every frame (smooth, but each frame renders n times).
|
|
21
|
+
- **`--scale`** takes a number or a fraction. A whole scale (2, 3) always works. A fractional one must turn the layout into whole pixels, even ones for a video file: `--scale 4/3` makes 1920x1080 into 2560x1440 (also `1.5` → 2880x1620, `1.25` → 2400x1350). `1.333` would give 2559.36x1439.64, which the browser rounds to an odd, slightly stretched 2559x1440, so the render stops before it starts and names the scale that works (`--scale 4/3`). Layout, `var(--px)` and `gm.px` stay those of the composition's own size; text and SVG are drawn sharp at the bigger size, images at their own resolution. 1 worker == N workers holds at any scale.
|
|
22
|
+
- `npx givemotion doctor` checks Chrome, ffmpeg and which encoder is used; `npx givemotion install` downloads the Chrome build used for rendering; `npx givemotion clean` deletes cached frames.
|
|
23
|
+
|
|
24
|
+
## Re-rendering is fast (the parts cache)
|
|
25
|
+
|
|
26
|
+
A render keeps the video in 1-2 s parts (in `.gm-parts/` next to the output) with a fingerprint of what each part shows. Render again after a fix and only the parts that changed are redone:
|
|
27
|
+
- Edit one scene and only that scene's parts (plus the transitions into it) are rendered.
|
|
28
|
+
- Inside one long scene (a whiteboard, a camera world), editing something that appears later keeps the parts before it, when it is a nested clip (`data-start`), or is held hidden by a delayed `data-enter` and is `position:absolute` or an SVG shape, and no script, camera shot or arrow names it (the log says what changed).
|
|
29
|
+
- Change its length and the scenes after it move, so they are redone too; change shared CSS, a script, a file or the data and everything is redone.
|
|
30
|
+
- The joined video is byte-identical to a full render. `--redo 4s-7s` forces a part to be redone (e.g. when a script reads something the fingerprint cannot see); `--no-cache` keeps nothing.
|
|
31
|
+
- `--cache-dir <dir>` (API: `cacheDir`) keeps the parts in `<dir>/<output file name>/` instead of `.gm-parts/` next to the output: for an app that writes the video to a temporary path and moves it, or keeps caches in one place. By default the parts are keyed by the output's file name, so render to the same name to reuse them. An app that names each version's output differently (`render-v1.mov`, `render-v2.mov`) would redo everything and leave a full set of parts per name: pass `--cache-key <name>` (API: `cacheKey`), one stable name per job, so every version shares one folder. A render that keeps more than 0.5 GB of parts says so in the log; `parts.bytes` in the summary gives the size.
|
|
32
|
+
|
|
33
|
+
## For apps: progress and the API
|
|
34
|
+
|
|
35
|
+
`--progress json` turns stderr into a stream of JSON lines (one object per line, nothing else on stderr; stdout still gets the `--json` summary if you ask for it):
|
|
36
|
+
```
|
|
37
|
+
{"event":"progress","phase":"open"}
|
|
38
|
+
{"event":"progress","phase":"frames","done":0,"total":450,"fps":0,"eta":null,"frames":450,"reusedParts":3}
|
|
39
|
+
{"event":"progress","phase":"frames","done":120,"total":450,"fps":38.2,"eta":8.6,"frames":450,"reusedParts":3}
|
|
40
|
+
{"event":"progress","phase":"encode"}
|
|
41
|
+
{"event":"progress","phase":"done","output":"/abs/out/video.mp4","seconds":14.2,"frames":450}
|
|
42
|
+
{"event":"warning","code":"text-clipped","message":"…","selector":"#title","fix":"…"}
|
|
43
|
+
```
|
|
44
|
+
Phases: `open` (Chrome and the page load), `frames` (about once a second: `done`/`total` are the frames this render draws, after reused parts; `frames` is the whole video; `fps` is the capture speed; `eta` seconds), `encode` (joining the parts and mixing audio), `done`. Other lines are `{"event":"log","message"}`, `{"event":"warning",…}` (after the render) and `{"event":"error","message","issues"}` (exit code 1).
|
|
45
|
+
|
|
46
|
+
From Node: `render(file, { output, scale: '4/3', cacheDir, onProgress: (p) => … })`: `onProgress` gets the same objects without `event` (`{ phase, done, total, fps, eta, … }`), and `render` resolves to the summary (`width`, `height`, `frames`, `seconds`, `parts`, `warnings` …).
|
|
47
|
+
|
|
48
|
+
## Render cost and speed
|
|
49
|
+
|
|
50
|
+
Without a GPU, every big element that moves on its own, every `<canvas>`, and everything drawn on top of them is redrawn on each frame, and a `filter` on a big element doubles that. A big blurred glow that stays put is almost free. `npx givemotion bench video.html` prints how much of the frame these layers cover and the biggest ones. Use `--quality draft` while iterating if you must render, and `snapshot`/`still` instead of full renders to review.
|
|
51
|
+
|
|
52
|
+
Memory: Chrome draws each layer in tiles, and a 4K frame with many big layers (crossfading full-frame sections, opacity or filter animations, large `overflow` boxes) needs a lot of tile memory. The engine gives each render browser up to 4 GB for tiles (more above `--scale 2`), so no tile of a frame is left out; a page that needs less uses less. If you override Chrome's flags with `GIVEMOTION_CHROME_ARGS`, do not lower `--force-gpu-mem-available-mb`: past that limit frames come out with blank rectangles or pieces of an earlier frame.
|
|
53
|
+
|
|
54
|
+
## Overlays with transparency
|
|
55
|
+
|
|
56
|
+
Leave `<html>` and `<body>` without a background, then `npx givemotion render overlay.html -o out/overlay.mov --transparent` (ProRes 4444) or `-o out/overlay.webm --transparent` (VP9 with alpha). `check` and `snapshot` detect a transparent page automatically: contrast is not judged against nothing, and the sheet shows a checkerboard. Pass `--transparent` to `still` for a PNG with alpha. Good for lower thirds, captions and logo bugs placed over footage in an editor.
|
|
57
|
+
|
|
58
|
+
## Share and embed
|
|
59
|
+
|
|
60
|
+
`npx givemotion bundle video.html -o dist/` writes a standalone page (runtime inlined, blocks expanded, assets copied; plays in any browser, served by any static web server, not opened as `file://`), `gm-player.js` and a demo `player.html`. Embed in a site or app: `<script src="gm-player.js"></script><gm-player src="video.html" controls autoplay loop muted></gm-player>` (methods `play()`, `pause()`, `seekTo(s)`; events `ready`, `timeupdate`, `ended`; `data='{"name":"Ana"}'` feeds `gm.data`).
|
|
61
|
+
|
|
62
|
+
## Colour
|
|
63
|
+
|
|
64
|
+
Videos are tagged with what Chrome draws: BT.709 matrix and primaries, limited range, and the sRGB transfer curve (`iec61966-2-1`), the same in mp4, webm and mov. A player that reads the tags shows the colours of your page; one that ignores them shows the raw values, which is also right for sRGB.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Review: looking at your video, and learning from a reference
|
|
2
|
+
|
|
3
|
+
Purpose: recipes for `snapshot`, `still`, `inspect` and `check` that show you the right frames with few images, how to read a sheet, and `analyze` for reference videos and your own render.
|
|
4
|
+
|
|
5
|
+
Contents
|
|
6
|
+
- Snapshot recipes
|
|
7
|
+
- Inspect: where things are, frame by frame
|
|
8
|
+
- Check: fast re-checks
|
|
9
|
+
- Reading a sheet: what to look for
|
|
10
|
+
- Learning from a reference video (and reviewing your render)
|
|
11
|
+
|
|
12
|
+
## Snapshot recipes
|
|
13
|
+
|
|
14
|
+
- `npx givemotion snapshot video.html` writes `snapshots/sheet.png` next to the HTML file: the key moment of every scene (one per camera shot in long camera scenes, plus frame 0). **Open it and look.** `-o review2` writes to another folder so you can compare before and after.
|
|
15
|
+
- **After a one-scene fix, look only at that scene:** `snapshot --times 12,13.5` (seconds, or frames like `45f`; e.g. `snapshot --times 2,5.5,9`) shows exactly the moments you choose. Use `inspect` to find the scene's start. This costs one small image instead of a whole sheet.
|
|
16
|
+
- `snapshot --transitions`: the start, middle and end of every scene change (ghosted text, empty middle frames). It knows only the engine's own transitions (`data-transition`); a scene change you built yourself (overlapping clips, a band or wipe in `gm.onFrame`) is not in the automatic sheet either, so look at it with `snapshot --times 2.05,2.13,2.2` around its cut.
|
|
17
|
+
- `snapshot --from 3.2 --to 3.8 --every 1f`: a frame-by-frame strip of one move (easing, overshoot, a flicker). `--every 0.5` for one frame every half second, like the judges' sheets.
|
|
18
|
+
- `snapshot --grid`: thirds, the center and the safe zone (`data-safe-zone` / `--safe-zone`) drawn over the pictures, for balance and margins.
|
|
19
|
+
- `--max 12` (automatic moments, default 12), `--cols 2` (fewer columns = bigger pictures, to read small text), `--sheet-only` (only `sheet.png`), `--transparent` (checkerboard behind; auto-detected), `--data f.json` / `--props '{…}'`.
|
|
20
|
+
- `npx givemotion still video.html -t 3.2 -o f.png` renders one exact moment at full size (to read small text or check a detail).
|
|
21
|
+
|
|
22
|
+
## Inspect: where things are, frame by frame
|
|
23
|
+
|
|
24
|
+
- `npx givemotion inspect video.html` prints JSON: size, fps, duration, frames, every clip's real start/end (selector, start, end), transitions, media, camera shots with what each shows (`shows`: world area x, y, w, h and zoom), issues.
|
|
25
|
+
- `inspect video.html --at 4.5 --el "#logo"` prints where an element is on screen at that moment (screen box, opacity, transform, visible), and which clips are on screen with their local time (the clock that `data-enter` delays and `at` options count in), plus any `gm.log` output at that time.
|
|
26
|
+
- `inspect --at 2 --to 3 --el "#logo"` (`--every 1f`) prints its position, size and opacity frame by frame: check easing and speed without watching.
|
|
27
|
+
|
|
28
|
+
## Check: fast re-checks
|
|
29
|
+
|
|
30
|
+
- `check --at 7.5` re-checks one moment fast after a fix; `--from 6 --to 9` part of the video; `--fast` uses fewer samples (quicker, may miss short problems); `--samples 10`; `--safe-zone shorts`.
|
|
31
|
+
- Problems list every time range they happen in; times also say how far into the scene the problem is. How check decides and what each code means: `references/check-codes.md`.
|
|
32
|
+
|
|
33
|
+
## Reading a sheet: what to look for
|
|
34
|
+
|
|
35
|
+
Go through this list on every sheet (these are the things blind judges named most):
|
|
36
|
+
1. Frame 0: is something there, and does it hook?
|
|
37
|
+
2. Any cell where text is cut by the frame edge, or a neighbour's label half in frame?
|
|
38
|
+
3. Any cell that is nearly empty, or where everything sits in one corner or the top half?
|
|
39
|
+
4. Secondary text (subtitles, axis labels, code, labels in wide shots): would it read on a phone?
|
|
40
|
+
5. Two neighbouring cells that look the same (a hold): is something still moving?
|
|
41
|
+
6. Transition cells: ghosted double text, or a muddy half-and-half frame?
|
|
42
|
+
7. The end: does it land with a payoff and stay alive, or sit still for 2 s?
|
|
43
|
+
8. Variety: do several scenes use the same layout?
|
|
44
|
+
9. Is the look consistent (palette, fonts, motion) from start to end?
|
|
45
|
+
Remember the motion strip at the top of `check`: long flat runs (`▁▁▁▁`) are stalls.
|
|
46
|
+
|
|
47
|
+
## Learning from a reference video (and reviewing your render)
|
|
48
|
+
|
|
49
|
+
`npx givemotion analyze reference.mp4` turns any video into a report you can read: every shot with its length and camera move, the scene changes and their type (`cut` `fade` `dip` `wipe-*` `push-*` `zoom-in/out` `iris` `clock`, best guesses; a whip reads as `push-*`), a pacing strip, the palette (background, accents), where the detail sits, and how many cuts land on the beat. It ends with a style recipe in givemotion terms, and writes `analysis/<name>/sheet.png` (the whole video at even steps) and `shots.png` (start, middle, end of every shot): **open both**.
|
|
50
|
+
|
|
51
|
+
- Options: `-o <dir>`, `--every 0.5` (seconds between pictures on sheet.png), `--no-sheets` (only the report, faster), `--no-audio` (skip the music analysis), `--json`.
|
|
52
|
+
- To match a reference, analyze it first, build, render, then `npx givemotion analyze out/video.mp4 --compare reference.mp4`, which lists the differences ("yours: median shot 2.4 s, reference 0.9 s").
|
|
53
|
+
- Transition types are guesses from the pixels; trust the shot times and the sheets more.
|
|
54
|
+
- On your own render, `analyze` is a good last pass: the share of the picture that changes per half second (judges' favourites changed 26-40%, slide-like videos 12-22%) and the median shot length.
|
|
55
|
+
|
|
56
|
+
## Is the motion natural?
|
|
57
|
+
|
|
58
|
+
- `npx givemotion analyze out/video.mp4 --motion` adds a smoothness report: camera dead stops between moves, stop-and-go, sudden velocity changes (full-speed starts/stops, sharp turns), jerk, the share of fast frames that have motion blur (strobing), and a 0–10 naturalness score. Works on any MP4, also a reference video.
|
|
59
|
+
- `--strips [n]` also writes `strip-1.png` … (default 4): the busiest 1 s moments at 10 frames per second, each frame labelled with its time. Open them to see the spacing of a move: even gaps = constant speed, gaps shrinking into the landing = a good ease-out, a jump between two frames = strobing.
|
|
60
|
+
- `check` already warns about the usual causes: `camera-stop`, `fast-no-blur`, `straight-move`, `same-ease` (`references/check-codes.md`).
|