reelkit-cli 0.5.0 → 0.8.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/README.md +3 -2
- package/package.json +7 -2
- package/skill/SKILL.md +22 -11
- package/skill/THIRD_PARTY.md +102 -0
- package/skill/commands/launch-film.md +7 -0
- package/skill/reference/asset-reuse.md +13 -2
- package/skill/reference/backgrounds.md +63 -0
- package/skill/reference/beat-sync.md +32 -17
- package/skill/reference/captions.md +11 -5
- package/skill/reference/component-authoring.md +12 -1
- package/skill/reference/continuity.md +21 -2
- package/skill/reference/kit.md +140 -11
- package/skill/reference/launch-film.md +190 -0
- package/skill/reference/remotion-composition.md +4 -3
- package/skill/reference/scene-treatments.md +20 -0
- package/skill/reference/scriptwriting.md +4 -1
- package/skill/reference/three-d.md +134 -0
- package/skill/reference/voice-sync.md +108 -0
- package/src/agents.ts +23 -12
- package/src/api/client.ts +4 -1
- package/src/cli.ts +25 -8
- package/src/commands/assets.ts +334 -36
- package/src/commands/build.ts +172 -36
- package/src/commands/components.ts +220 -0
- package/src/commands/init.ts +9 -3
- package/src/commands/install.ts +1 -1
- package/src/commands/plan.ts +8 -5
- package/src/commands/ref.ts +5 -2
- package/src/contract/index.ts +27 -5
- package/src/pipeline/beatsnap.ts +72 -0
- package/src/pipeline/review.ts +67 -7
- package/src/pipeline/schema.ts +51 -4
- package/src/pipeline/timing.ts +27 -1
- package/src/project/background.ts +33 -0
- package/src/project/layers.ts +60 -0
- package/src/project/loudness.ts +68 -0
- package/src/project/manifest.ts +68 -14
- package/src/project/music.ts +19 -5
- package/src/project/project.ts +7 -2
- package/src/project/refmeasure.ts +1 -1
- package/src/project/soundreport.ts +347 -0
- package/src/project/svgcheck.ts +21 -0
- package/src/remotion/kit/Assemble3D.tsx +92 -0
- package/src/remotion/kit/BrowserFrame.tsx +83 -0
- package/src/remotion/kit/Camera.tsx +6 -4
- package/src/remotion/kit/Captions.tsx +33 -17
- package/src/remotion/kit/Card3D.tsx +211 -0
- package/src/remotion/kit/ChapterFrame.tsx +68 -0
- package/src/remotion/kit/CounterRoll.tsx +75 -0
- package/src/remotion/kit/GlassPanel.tsx +43 -0
- package/src/remotion/kit/Grounds.tsx +177 -0
- package/src/remotion/kit/Headline.tsx +97 -0
- package/src/remotion/kit/Hero3D.tsx +197 -0
- package/src/remotion/kit/HudOverlay.tsx +52 -0
- package/src/remotion/kit/ImageLayers.tsx +48 -0
- package/src/remotion/kit/Music.tsx +4 -4
- package/src/remotion/kit/NamedCursor.tsx +54 -0
- package/src/remotion/kit/Orbit3D.tsx +49 -0
- package/src/remotion/kit/Particles3D.tsx +74 -0
- package/src/remotion/kit/Place.tsx +12 -0
- package/src/remotion/kit/PromptBox.tsx +84 -0
- package/src/remotion/kit/Scene3D.tsx +70 -0
- package/src/remotion/kit/SceneFrame.tsx +88 -11
- package/src/remotion/kit/Sfx.tsx +12 -6
- package/src/remotion/kit/SoundCues.tsx +22 -0
- package/src/remotion/kit/TerminalLog.tsx +98 -0
- package/src/remotion/kit/Text3D.tsx +78 -0
- package/src/remotion/kit/TextOnImage.tsx +41 -0
- package/src/remotion/kit/Warp3D.tsx +59 -0
- package/src/remotion/kit/bg-math.ts +179 -0
- package/src/remotion/kit/caption-groups.ts +7 -3
- package/src/remotion/kit/caption-style.ts +45 -0
- package/src/remotion/kit/docs.ts +133 -11
- package/src/remotion/kit/image-layers-math.ts +115 -0
- package/src/remotion/kit/index.ts +43 -1
- package/src/remotion/kit/inter-bold-typeface.ts +3 -0
- package/src/remotion/kit/media.ts +5 -3
- package/src/remotion/kit/motion-math.ts +36 -2
- package/src/remotion/kit/music-math.ts +27 -10
- package/src/remotion/kit/quiet-three.ts +11 -0
- package/src/remotion/kit/sample-text.ts +55 -0
- package/src/remotion/kit/scene3d-context.ts +5 -0
- package/src/remotion/kit/seeded.ts +13 -0
- package/src/remotion/kit/sound-cues.ts +89 -0
- package/src/remotion/kit/sound-kinds.ts +122 -0
- package/src/remotion/kit/theme.ts +2 -0
- package/src/remotion/kit/three-fx-math.ts +192 -0
- package/src/remotion/kit/three-math.ts +145 -0
- package/src/remotion/kit/transition-math.ts +116 -0
- package/src/remotion/kit/ui-math.ts +145 -0
- package/src/remotion/kit/ui-theme.ts +25 -0
- package/src/remotion/kit/word-anchor.ts +107 -0
- package/src/render/contact-sheet.ts +39 -0
- package/src/render/continuity.ts +14 -4
- package/src/render/deps.ts +15 -3
- package/src/render/master.ts +31 -0
- package/src/render/render.ts +15 -8
- package/src/render/sound-notes.ts +106 -0
- package/src/render/static-check.ts +156 -0
- package/src/render/validate.ts +3 -150
- package/src/render/word-check.ts +181 -0
- package/src/testing/conformance.ts +61 -1
- package/src/testing/fake-api.ts +11 -5
- package/src/testing/fixtures.ts +3 -0
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@ A CLI and a Claude skill for making short-form video. Claude plans the video and
|
|
|
14
14
|
npm install -g reelkit-cli
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
-
This puts the `reelkit` command on your path. Then put the skill, and
|
|
17
|
+
This puts the `reelkit` command on your path. Then put the skill, and the `/reelkit-video` and `/reelkit-launch-film` commands where the agent supports them, into your coding agents (Claude Code, Codex, Cursor, Gemini CLI and the shared `.agents` folder):
|
|
18
18
|
|
|
19
19
|
```bash
|
|
20
20
|
reelkit install
|
|
@@ -54,6 +54,7 @@ reelkit render
|
|
|
54
54
|
| `reelkit install` | Install the skill into your coding agents (`--agent <id>`, `--force`) |
|
|
55
55
|
| `reelkit init [name]` | Set up a video project, in a new folder named after it |
|
|
56
56
|
| `reelkit assets upload <file>` | Add one of your own files (private unless `--share`); `--green` or `--cutout` also makes a transparent copy of a video (`--cutout` sends it to the server) |
|
|
57
|
+
| `reelkit components share [name...]` | Send components written in this project to the library for review (source, description and an example only); a render does this by itself unless you use `reelkit render --no-share`, `reelkit init <name> --private` or `REELKIT_NO_SHARE=1` |
|
|
57
58
|
| `reelkit assets search "<query>"` | Search the shared library by meaning; each result shows a match percentage |
|
|
58
59
|
| `reelkit assets pull <id>` | Download a library item (a component lands in `src/`) |
|
|
59
60
|
| `reelkit assets voices` | List narration voices |
|
|
@@ -67,7 +68,7 @@ reelkit render
|
|
|
67
68
|
|
|
68
69
|
## What is shared
|
|
69
70
|
|
|
70
|
-
Your own files, your plan and your video stay on your machine. Illustrations that the plan marks as generic, and anything you add with `--share`, go to the shared library for review, where other people can reuse them.
|
|
71
|
+
Your own files, your plan and your video stay on your machine. Illustrations that the plan marks as generic, and anything you add with `--share`, go to the shared library for review, where other people can reuse them. New components written for your video are sent for review after a render too (their source, a description and an example, nothing else); turn that off with `reelkit init <name> --private` (which also keeps generated images, clips and graphics out of the library), `reelkit render --no-share` or `REELKIT_NO_SHARE=1`.
|
|
71
72
|
|
|
72
73
|
A component you pull from the library is code, and it runs on your machine when you preview or render. The library only serves components published by Reelkit.
|
|
73
74
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "reelkit-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "CLI and Claude skill for making short-form video with a shared asset library.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Daniel Livshin",
|
|
@@ -31,7 +31,8 @@
|
|
|
31
31
|
"./client": "./src/api/client.ts",
|
|
32
32
|
"./context": "./src/context.ts",
|
|
33
33
|
"./commands/*": "./src/commands/*.ts",
|
|
34
|
-
"./testing/conformance": "./src/testing/conformance.ts"
|
|
34
|
+
"./testing/conformance": "./src/testing/conformance.ts",
|
|
35
|
+
"./static-check": "./src/render/static-check.ts"
|
|
35
36
|
},
|
|
36
37
|
"files": [
|
|
37
38
|
"bin",
|
|
@@ -50,17 +51,21 @@
|
|
|
50
51
|
"typecheck": "tsc --noEmit"
|
|
51
52
|
},
|
|
52
53
|
"dependencies": {
|
|
54
|
+
"@react-three/fiber": "^9.8.1",
|
|
53
55
|
"@remotion/bundler": "^4.0.532",
|
|
54
56
|
"@remotion/google-fonts": "4.0.532",
|
|
55
57
|
"@remotion/renderer": "^4.0.532",
|
|
58
|
+
"@remotion/three": "4.0.532",
|
|
56
59
|
"@types/react": "^19.3.0",
|
|
57
60
|
"@types/react-dom": "^19.3.0",
|
|
61
|
+
"@types/three": "^0.186.0",
|
|
58
62
|
"commander": "^15.0.0",
|
|
59
63
|
"ink": "^8.0.0",
|
|
60
64
|
"ink-spinner": "^5.0.0",
|
|
61
65
|
"react": "^19.3.0",
|
|
62
66
|
"react-dom": "^19.3.0",
|
|
63
67
|
"remotion": "^4.0.532",
|
|
68
|
+
"three": "^0.186.1",
|
|
64
69
|
"tsx": "^4.23.15",
|
|
65
70
|
"typescript": "^5.9.3",
|
|
66
71
|
"zod": "^4.6.5"
|
package/skill/SKILL.md
CHANGED
|
@@ -30,12 +30,18 @@ If the user points at an existing video ("make one like this", a link or a file)
|
|
|
30
30
|
### 2. Look
|
|
31
31
|
Read `reference/styles.md` and agree the video's look with the user: one of its looks, or their own. If they already described what they want, match it and confirm in one sentence. Ask anything still open in one message, each question with a default. If any on-screen text will be Hebrew, read `reference/hebrew-rtl.md` now: it changes how words may enter and how lines are written.
|
|
32
32
|
|
|
33
|
-
Ask whether
|
|
33
|
+
Ask whether the video has a narrator. If it does not (a product, feature or brand launch carried by music and sound effects), follow `reference/launch-film.md` and write `"voice": "none"` in the plan; the voice step (4) and the captions question below are skipped.
|
|
34
|
+
|
|
35
|
+
Ask whether they want captions, and which kind: none, one word at a time (`word`), or a few words at a time (`phrase`, the default). "A few words at a time" is `phrase`, never `word`; see `reference/captions.md`. Write the answer into the plan as `captions`.
|
|
34
36
|
|
|
35
37
|
Then read `reference/continuity.md` and offer the user three concepts for the video, each one sentence about the picture (not the product) with a different central idea, and say what carries each scene change. They pick one; write it into the first scene's `notes` beside the look.
|
|
36
38
|
|
|
37
39
|
### 3. Plan
|
|
38
|
-
|
|
40
|
+
For a film with no narrator, write the plan in the launch-film shape (`"voice": "none"` and a `seconds` for every scene, see `reference/launch-film.md`) and skip the voice choice below.
|
|
41
|
+
|
|
42
|
+
Read `reference/scriptwriting.md` and `reference/scene-treatments.md` (and `reference/clips.md` if any scene might be a video clip). Write the chosen look into the first scene's `notes`. Run `reelkit assets voices` (women first), pick THREE voices that fit the idea, audience and language, the first a female voice (the default) and at least one male, and show the user the three with one short line each (name, gender, accent, character). Ask which they want, saying the first is used if they do not mind; on no answer or "you choose", use the first. For a film in Hebrew offer the voices marked `he` first, then say that any other voice can also read Hebrew with the current model.
|
|
43
|
+
|
|
44
|
+
Before writing it, ask the user for real material: screenshots of the product, the real numbers, the logo and the exact product name, and use what they give you (no third-party brand marks). Act each claim out in a small product surface instead of stating it (`reference/kit.md`, "Act the product out"), and choose which claims are highlighted and which are merely mentioned: at most three highlighted in 30 seconds. Prefer 15 to 40 seconds and state the length in the plan's first scene's notes, with the film's one accent colour and a plan of dark and light grounds scene by scene. A film with a narrator-less launch shape also follows `reference/launch-film.md`, section "Structure and briefing".
|
|
39
45
|
|
|
40
46
|
Write `plan.json`:
|
|
41
47
|
|
|
@@ -46,6 +52,7 @@ Write `plan.json`:
|
|
|
46
52
|
"mode": "motion",
|
|
47
53
|
"voiceId": "an id from reelkit assets voices",
|
|
48
54
|
"pace": "normal",
|
|
55
|
+
"gap": "normal",
|
|
49
56
|
"scenes": [
|
|
50
57
|
{
|
|
51
58
|
"id": "hook",
|
|
@@ -68,13 +75,14 @@ Write `plan.json`:
|
|
|
68
75
|
- `imagePrompt` is set only for `illustration` scenes, with 3 to 6 `imageTags`. Set `shareable` to true only when the prompt is fully generic: no brand, product, person or detail specific to this user.
|
|
69
76
|
- `clipPrompt` is set only for `clip` scenes (a generated or reused video clip is the scene's picture; the rest of the scene uses `imageTags` and `shareable` as an illustration does). Clips are scarce: most videos have none or one or two.
|
|
70
77
|
- `userAssetIds` lists the ids of the user's files shown in that scene.
|
|
71
|
-
- `pace` is `slow`, `normal` or `fast
|
|
72
|
-
- `
|
|
78
|
+
- `pace` is `slow`, `normal` or `fast`: how fast the voice speaks.
|
|
79
|
+
- `gap` is the silence between one sentence and the next: `tight` (0.2 s, for a promo), `normal` (0.3 s, the default) or `relaxed` (0.5 s, for a calm or emotional film). The film's length follows from it: `plan check` estimates it and `reelkit assets voiceover` prints the real one.
|
|
80
|
+
- `captions` is `none`, `word` or `phrase` (a few words, up to 3, never spanning two sentences), as the user chose; leave it out for `phrase`. The manifest carries it: `<Captions group={manifest.captions} />`.
|
|
73
81
|
- When the video follows a reference, add `"reference": { "id": "<id>", "take": ["fast cuts every ~1.2s"] }` (1 to 6 notes on what you took).
|
|
74
82
|
|
|
75
83
|
Run `reelkit plan check`; it prints the estimated length to tell the user. Fix everything under "Fix these". Act on "Worth improving" unless you have a good reason not to.
|
|
76
84
|
|
|
77
|
-
**Checkpoint.** Show the user the look, the title, the estimated length, and each scene's narration, the exact on-screen text and one line on the visual, in plain words about what they will see. Wait for a clear yes. A question or a comment is not approval: answer it and ask again.
|
|
85
|
+
**Checkpoint.** Tell the user, in one sentence, that new components you write are shared with the Reelkit library for review after the render (their code, a description and an example, nothing else), and that they can say no: then use `reelkit init <name> --private` (it also keeps the images, clips and graphics generated for the video out of the library, whatever `shareable` says) or `reelkit render --no-share`, or set REELKIT_NO_SHARE=1. Show the user the look, the title, the estimated length, and each scene's narration, the exact on-screen text and one line on the visual, in plain words about what they will see. Wait for a clear yes. A question or a comment is not approval: answer it and ask again.
|
|
78
86
|
|
|
79
87
|
### 4. Voice
|
|
80
88
|
`reelkit assets voiceover --all`. It records each scene and prints the real length of the video. If the user wants a different voice, change `voiceId` in `plan.json` and run it again with `--redo`.
|
|
@@ -82,23 +90,26 @@ Run `reelkit plan check`; it prints the estimated length to tell the user. Fix e
|
|
|
82
90
|
### 5. Images and clips
|
|
83
91
|
Read `reference/asset-reuse.md`. For each illustration scene, search first:
|
|
84
92
|
`reelkit assets search "<what the scene needs>" --kind image`
|
|
85
|
-
Each result starts with a match percentage: how likely it is good enough to reuse. Pull the best result (`reelkit assets pull <id> --scene <sceneId>`) when its match is 60% or more and, reading its description, it fits the scene. Otherwise generate: `reelkit assets gen image --scene <sceneId>`. Never give two scenes the same image.
|
|
93
|
+
Each result starts with a match percentage: how likely it is good enough to reuse. Pull the best result (`reelkit assets pull <id> --scene <sceneId>`) when its match is 60% or more and, reading its description, it fits the scene. Otherwise generate: `reelkit assets gen image --scene <sceneId>`. Never give two scenes the same image. For an icon, a mark or a diagram that must stay sharp, `reelkit assets gen svg "<prompt>"` makes a vector graphic instead (see `reference/asset-reuse.md`).
|
|
86
94
|
|
|
87
95
|
For each clip scene, read `reference/clips.md`, then search `reelkit assets search "<what the clip shows>" --kind clip` and pull a 60% match (`reelkit assets pull <id> --scene <sceneId>`), or generate: `reelkit assets gen clip --scene <sceneId>` (add `--green` for a green-screen subject). It waits for the clip, which takes minutes; check what is left with `reelkit whoami`.
|
|
88
96
|
|
|
89
97
|
### 6. Composition
|
|
90
|
-
Read `reference/kit.md`, `reference/remotion-composition.md`, `reference/motion-design.md`, `reference/continuity.md`, `reference/captions.md` and `reference/
|
|
98
|
+
Read `reference/kit.md`, `reference/remotion-composition.md`, `reference/motion-design.md`, `reference/continuity.md`, `reference/captions.md`, `reference/beat-sync.md` and, for a narrated film, `reference/voice-sync.md`.
|
|
91
99
|
|
|
100
|
+
- **Fit the picture to the voice** (narrated films; the whole method is in `reference/voice-sync.md`). The voice is the clock: write the narration first, record it (step 4), and only then place visuals from the real word times with `onWord(s, "word")` (the frame to start an entrance so it lands on its word; `wordFrame` is the word's own frame, `onWordBeat` lets it land on a beat only within 3 frames after the word). Never count words by hand. One visual event per stressed word and at most one every 0.5 s; the thing a word names is on screen within 3 frames of the word starting, never before the previous sentence ends; numbers count up to finish on their word; a list ticks on each item's word; scene changes fall in the gaps between sentences, never inside one; the last word of a sentence is held for the gap, not longer; sound effects for a word's visual sit under the voice at 0.3 or less. `reelkit check` fails on a word the scene never says, and `reelkit preview` renders a frame 4 frames after each word you timed: each of those should already show the thing that word names.
|
|
92
101
|
- Choose the music before you write the composition, and read `reference/beat-sync.md`: `reelkit assets search "<mood and tempo>" --kind music`, then `reelkit assets pull <id> --music`. Scene changes then land on its beat by themselves; bring each element in on a beat inside a scene as that file shows.
|
|
93
102
|
- Search the library before writing a component: `reelkit assets search "<what it shows>" --kind component`. To use one, `reelkit assets pull <id>`: it lands in `src/` and the command prints the import line and an example.
|
|
94
103
|
- Read `reference/sound-design.md`, then find the few sounds the video needs: `reelkit assets search "whoosh" --kind sfx`, `reelkit assets pull <id>`. The pull prints how to reference the file.
|
|
104
|
+
- For one hero moment with real depth (extruded type, a screen floating in space, a ring of cards), read `reference/three-d.md`; everything else stays 2D.
|
|
105
|
+
- For what sits behind the whole film (a video, a picture that changes, or an animated ground), read `reference/backgrounds.md`; pick one ground and keep it.
|
|
95
106
|
- Before writing a new component, read `reference/component-authoring.md`.
|
|
96
107
|
- Write `src/Video.tsx` and any component files beside it. `Video.tsx` may import only `react`, `remotion`, `reelkit/kit` and sibling components (`./Name`).
|
|
97
108
|
- `manifest.json` is the exact object passed to `Video` as the `manifest` prop. Media is referenced as `urls[path]`, where `path` is the file's path in the project, such as `urls[scene.voiceoverKey]` or `urls["assets/lib/<id>/clip.mp3"]`.
|
|
98
109
|
|
|
99
|
-
Run `reelkit check` and fix every error until it passes. Then `reelkit preview` (the first preview on a machine downloads a browser once and can take a minute) and look at every frame in `out/preview/`: each scene has two, `early` (30% into the scene) and `late` (90%), so look at both frames of each scene. Look for: text cut off or overflowing, text overlapping other text or the captions, text too small or too low-contrast for a phone, an empty or broken frame, content hidden behind another layer, and any number, price or quote on screen that is not in the plan. A frame is one moment: an element mid-animation is not a problem, but anything that should be fully on screen by the late frame and is not is. Fix real problems and preview again. Go round at least twice, and finish with the studio test in `reference/motion-design.md`. `reelkit preview` also reports continuity: how many scene changes carry something across. Fix every `cut` it names, or keep it as a deliberate choice (a burst of fast hits, the end card); see `reference/continuity.md`.
|
|
110
|
+
Self-review loop after the first `reelkit preview`, on `out/preview/sheet.jpg`: no text smaller than about 28 px at 1080 wide, every claim has a visible demonstration, the grounds alternate as the plan said, and the hero object is present in every scene it should be; fix and preview again, at most two rounds. Run `reelkit check` and fix every error until it passes. Read its "Worth improving" notes as well: they say when no library component was used, when every scene is type and shapes, and when the opening has no picture. Then `reelkit preview` (the first preview on a machine downloads a browser once and can take a minute) and look at every frame in `out/preview/`: each scene has two, `early` (30% into the scene) and `late` (90%), so look at both frames of each scene. Look for: text cut off or overflowing, text overlapping other text or the captions, text too small or too low-contrast for a phone, an empty or broken frame, content hidden behind another layer, and any number, price or quote on screen that is not in the plan. A frame is one moment: an element mid-animation is not a problem, but anything that should be fully on screen by the late frame and is not is. Fix real problems and preview again. Go round at least twice, and finish with the studio test in `reference/motion-design.md`. `reelkit preview` also reports continuity: how many scene changes carry something across. Fix every `cut` it names, or keep it as a deliberate choice (a burst of fast hits, the end card); see `reference/continuity.md`.
|
|
100
111
|
|
|
101
|
-
You only ever see frames, never the moving video, so the frames are your eyes: look at every
|
|
112
|
+
You only ever see frames, never the moving video, so the frames are your eyes. `reelkit preview` writes `out/preview/sheet.jpg`, one labelled picture of all of them: look at it first, then at every frame singly. And whoever built a video is the worst judge of it. If you can start a separate agent, give it only the user's original request and the preview frames (not your explanations) and ask for a score out of 100 per scene, a list of flaws, and a concrete fix for each, in numbers ("raise the title 60 px", "hold the label 0.4 s longer"). Fix, preview, and have the same reviewer look again until every scene passes 90. If two rounds leave a scene under 70, stop and show the user the gap instead of spending more rounds.
|
|
102
113
|
|
|
103
114
|
**Checkpoint.** Show the user the preview frames (both of each scene). Wait for a clear yes. For changes, edit the code, `reelkit check`, `reelkit preview`, and show them again.
|
|
104
115
|
|
|
@@ -111,9 +122,9 @@ If the render fails, read the error, fix the composition, and run `reelkit check
|
|
|
111
122
|
|
|
112
123
|
- Search before generating. Reuse beats regenerate.
|
|
113
124
|
- Never generate app UI. Use the user's real screenshots.
|
|
114
|
-
- The user's own files
|
|
125
|
+
- The user's own files (images, footage, sounds) stay private: pass `--share` only when they ask you to contribute a file. Components are the opposite, by default: new components you write are sent to the library for review after the render (source, description and example only), unless the user says no; then use `reelkit init <name> --private` or `reelkit render --no-share` (or REELKIT_NO_SHARE=1).
|
|
115
126
|
- Pass the user's facts through unchanged. Never invent a number, statistic, price or quote.
|
|
116
|
-
- Keep components driven by props, not hardcoded, so they can be reused.
|
|
127
|
+
- Keep components driven by props, not hardcoded, so they can be reused: nothing of this user's text in them, and every new component starts with a one or two sentence comment saying what it shows and when to use it (`reference/component-authoring.md`).
|
|
117
128
|
- One stage at a time. Do not write composition code before the user has approved the plan, and do not render before they have approved the preview.
|
|
118
129
|
- Never say a video is done without having looked at its frames and checked that the file exists.
|
|
119
130
|
- If a command reports that a quota is used up, tell the user what ran out and when it resets. Do not work around it. A message that says to wait a minute or an hour ("Too many searches", "Too many uploads started") is a throttle, not a used-up quota: wait and retry once instead of stopping.
|
package/skill/THIRD_PARTY.md
CHANGED
|
@@ -18,3 +18,105 @@ Some guidance in these skills, and some starter-kit components, are adapted from
|
|
|
18
18
|
It burns captions in with FFmpeg subtitle files, which this project does not do. Adapted into the kit's `Captions` component (the pop, highlight and karaoke modes) and the `captions` skill (words per screen, timing, safe zones, contrast, shake and flash limits). Its engagement statistics were left out because they are unsourced.
|
|
19
19
|
|
|
20
20
|
Used in: `motion-design`, `remotion-composition`, `scriptwriting`, `scene-treatments`, the starter kit, and the preview checklist in `SKILL.md`.
|
|
21
|
+
|
|
22
|
+
## The 3D typeface
|
|
23
|
+
|
|
24
|
+
- **Inter** (Bold weight), by the Inter Project Authors (https://github.com/rsms/inter), SIL Open Font License 1.1. It is the one typeface bundled for the kit's `Text3D`, converted to three.js's typeface JSON (the Latin letters, digits, punctuation and a few symbols) and kept as data in `src/remotion/kit/inter-bold-typeface.ts`. The three npm package ships no typeface files, so the glyph outlines were taken from the Inter font files in the `@fontsource/inter` package; the outlines are unchanged apart from scaling to 1000 units to the em. The licence text, as shipped with that package:
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
Copyright 2016 The Inter Project Authors (https://github.com/rsms/inter) Inter-Italic[opsz,wght].ttf: Copyright 2016 The Inter Project Authors (https://github.com/rsms/inter)
|
|
28
|
+
|
|
29
|
+
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
|
30
|
+
This license is copied below, and is also available with a FAQ at:
|
|
31
|
+
http://scripts.sil.org/OFL
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
-----------------------------------------------------------
|
|
35
|
+
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
|
36
|
+
-----------------------------------------------------------
|
|
37
|
+
|
|
38
|
+
PREAMBLE
|
|
39
|
+
The goals of the Open Font License (OFL) are to stimulate worldwide
|
|
40
|
+
development of collaborative font projects, to support the font creation
|
|
41
|
+
efforts of academic and linguistic communities, and to provide a free and
|
|
42
|
+
open framework in which fonts may be shared and improved in partnership
|
|
43
|
+
with others.
|
|
44
|
+
|
|
45
|
+
The OFL allows the licensed fonts to be used, studied, modified and
|
|
46
|
+
redistributed freely as long as they are not sold by themselves. The
|
|
47
|
+
fonts, including any derivative works, can be bundled, embedded,
|
|
48
|
+
redistributed and/or sold with any software provided that any reserved
|
|
49
|
+
names are not used by derivative works. The fonts and derivatives,
|
|
50
|
+
however, cannot be released under any other type of license. The
|
|
51
|
+
requirement for fonts to remain under this license does not apply
|
|
52
|
+
to any document created using the fonts or their derivatives.
|
|
53
|
+
|
|
54
|
+
DEFINITIONS
|
|
55
|
+
"Font Software" refers to the set of files released by the Copyright
|
|
56
|
+
Holder(s) under this license and clearly marked as such. This may
|
|
57
|
+
include source files, build scripts and documentation.
|
|
58
|
+
|
|
59
|
+
"Reserved Font Name" refers to any names specified as such after the
|
|
60
|
+
copyright statement(s).
|
|
61
|
+
|
|
62
|
+
"Original Version" refers to the collection of Font Software components as
|
|
63
|
+
distributed by the Copyright Holder(s).
|
|
64
|
+
|
|
65
|
+
"Modified Version" refers to any derivative made by adding to, deleting,
|
|
66
|
+
or substituting -- in part or in whole -- any of the components of the
|
|
67
|
+
Original Version, by changing formats or by porting the Font Software to a
|
|
68
|
+
new environment.
|
|
69
|
+
|
|
70
|
+
"Author" refers to any designer, engineer, programmer, technical
|
|
71
|
+
writer or other person who contributed to the Font Software.
|
|
72
|
+
|
|
73
|
+
PERMISSION & CONDITIONS
|
|
74
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
75
|
+
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
|
76
|
+
redistribute, and sell modified and unmodified copies of the Font
|
|
77
|
+
Software, subject to the following conditions:
|
|
78
|
+
|
|
79
|
+
1) Neither the Font Software nor any of its individual components,
|
|
80
|
+
in Original or Modified Versions, may be sold by itself.
|
|
81
|
+
|
|
82
|
+
2) Original or Modified Versions of the Font Software may be bundled,
|
|
83
|
+
redistributed and/or sold with any software, provided that each copy
|
|
84
|
+
contains the above copyright notice and this license. These can be
|
|
85
|
+
included either as stand-alone text files, human-readable headers or
|
|
86
|
+
in the appropriate machine-readable metadata fields within text or
|
|
87
|
+
binary files as long as those fields can be easily viewed by the user.
|
|
88
|
+
|
|
89
|
+
3) No Modified Version of the Font Software may use the Reserved Font
|
|
90
|
+
Name(s) unless explicit written permission is granted by the corresponding
|
|
91
|
+
Copyright Holder. This restriction only applies to the primary font name as
|
|
92
|
+
presented to the users.
|
|
93
|
+
|
|
94
|
+
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
|
95
|
+
Software shall not be used to promote, endorse or advertise any
|
|
96
|
+
Modified Version, except to acknowledge the contribution(s) of the
|
|
97
|
+
Copyright Holder(s) and the Author(s) or with their explicit written
|
|
98
|
+
permission.
|
|
99
|
+
|
|
100
|
+
5) The Font Software, modified or unmodified, in part or in whole,
|
|
101
|
+
must be distributed entirely under this license, and must not be
|
|
102
|
+
distributed under any other license. The requirement for fonts to
|
|
103
|
+
remain under this license does not apply to any document created
|
|
104
|
+
using the Font Software.
|
|
105
|
+
|
|
106
|
+
TERMINATION
|
|
107
|
+
This license becomes null and void if any of the above conditions are
|
|
108
|
+
not met.
|
|
109
|
+
|
|
110
|
+
DISCLAIMER
|
|
111
|
+
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
112
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
|
113
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
|
114
|
+
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
|
115
|
+
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
116
|
+
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
|
117
|
+
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
118
|
+
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
|
119
|
+
OTHER DEALINGS IN THE FONT SOFTWARE.
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
The 3D layer also depends on `three` (MIT), `@react-three/fiber` (MIT) and `@remotion/three` (Remotion's own licence, the same as `remotion`).
|
|
@@ -21,11 +21,22 @@ A slightly imperfect existing image is usually fine, and it is free, but below 6
|
|
|
21
21
|
|
|
22
22
|
## Generating
|
|
23
23
|
- Start from the scene's wanted prompt. Keep one consistent style across the video's images.
|
|
24
|
-
- Never ask for text, letters, logos or UI in the picture.
|
|
24
|
+
- Never ask for text, letters, logos or UI in the picture. `reelkit assets gen image` adds "No text, letters, numbers, logos or brand marks anywhere in the picture." to every prompt that does not itself ask for text or a logo.
|
|
25
25
|
- Generate one image per scene. Do not regenerate to chase small improvements.
|
|
26
26
|
|
|
27
27
|
## What may be shared
|
|
28
28
|
- shareable: true only for generic imagery with nothing specific to this user. Write those prompts in generic terms.
|
|
29
29
|
- shareable: false if the prompt mentions or depends on a brand, product, person, place or anything the user uploaded.
|
|
30
|
-
- If the user says a scene may not be shared, it stays private whatever you pass.
|
|
30
|
+
- If the user says a scene may not be shared, it stays private whatever you pass. A project made with `reelkit init <name> --private` shares no generated image, clip or graphic at all, whatever the plan's `shareable` says.
|
|
31
31
|
- Give 3 to 6 short lowercase tags (subject, style, mood); they are how later videos find the image.
|
|
32
|
+
|
|
33
|
+
## Vector graphics
|
|
34
|
+
|
|
35
|
+
Use `reelkit assets gen svg "<prompt>"` for icons, logo-like marks, simple illustrations and diagrams: anything that must stay sharp when zoomed or that takes one flat accent colour. Use `reelkit assets gen image` for photos and textured art. Say the colours and "transparent background" in the prompt ("a flat teal rocket icon, two colours, transparent background"). It takes about 45 seconds and counts as one image against the image quota. With `--scene <id>` on an illustration scene the graphic becomes that scene's image; without it the file is saved as `assets/svg-<id>.svg` and you place it yourself with `<Img src={urls["assets/svg-<id>.svg"]} />`. The file is checked before it is saved: a graphic with a script or an outside link is refused and nothing is saved. `--private` keeps it from the library whatever the plan says.
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
## Components you pull from the library
|
|
39
|
+
|
|
40
|
+
Library components differ in how they count frames, so read the pulled file in `src/` before timing one to a spoken word. For each prop that takes a frame (`delay`, `at`, `tickAt`, `every`, `from`), find out what it counts from: the component's own first frame, or the frame after some fixed lead-in. Some add a hidden offset (a checklist whose `tickAt` has 10 frames added after its `delay`; a cursor that clicks 3 frames after `at`), and some have one uniform `every` for all their items instead of one time per item. Account for the offset in the number you pass, or write the item times out one by one.
|
|
41
|
+
|
|
42
|
+
A component that has only a `delay` (or nothing) starts from its own frame 0: wrap it, so that its frame 0 is the word's: `<Sequence from={onWord(s, "tasks")}><Card /></Sequence>`. Inside the `Sequence` its `delay` counts from there. Then look at `reelkit preview`'s word frame for that word: the component should already show what the word names.
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: backgrounds
|
|
3
|
+
description: Use when choosing what sits behind the whole film - a flat or mesh ground, an animated ground drawn in code, a looping video, a picture, or pictures that change from scene to scene - and how to get and use one.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Grounds
|
|
7
|
+
|
|
8
|
+
The ground is what is under everything. Choose one for the whole film and keep it: a film with a new look in every scene reads as a collage.
|
|
9
|
+
|
|
10
|
+
## Which ground
|
|
11
|
+
|
|
12
|
+
- A flat colour or `BgMesh`: for interface and data films, where the product is the picture and the ground must stay out of the way.
|
|
13
|
+
- An animated ground drawn in code (`BgAurora`, `BgBeams`, and for a technical film `BgGrid`; `BgGrain` for a still, filmic ground): when the frame would otherwise be empty. No media is needed and they are cheap to render.
|
|
14
|
+
- A video ground (`BgVideo`): for mood and brand films. Always dim it (0.3 to 0.5) or blur it, and never put small text over it.
|
|
15
|
+
- Pictures that change (`BgSequence`): a set of related pictures that change on scene starts, never in the middle of a sentence and no more than once every few seconds. Keep one `dim` and one `tint` across all of them so the film stays one film. Use "fade" by default and "zoom" or "blur" for an energetic film.
|
|
16
|
+
|
|
17
|
+
## Grade and Vignette darken a ground
|
|
18
|
+
|
|
19
|
+
`Grade` and `Vignette` laid over the film darken everything under them, and a ground drawn in code loses the most: a ground that looked right alone can go muddy under them, and on a near-black base it can all but vanish. `BgAurora`, `BgBeams`, `BgGrid` and `BgGrain` take `intensity` (0 to 1, default 0.6, which reads on a phone on its own). Raise it to 0.8 when Grade and Vignette sit on top, or when the base is near-black; lower it to 0.3 to 0.4 when type sits right on the colour fields and has to stay readable. Look at a preview frame with the whole film's layers on, not the ground alone.
|
|
20
|
+
|
|
21
|
+
## Getting one
|
|
22
|
+
|
|
23
|
+
Search the library first: `reelkit assets search "<mood> abstract background" --kind clip`, and also `--kind overlay` and `--kind image`. Pull a good fit as the film's ground with `reelkit assets pull <id> --background`; for a picture that is one scene's ground, add `--scene <id>`. A video is only ever the ground of the whole film.
|
|
24
|
+
|
|
25
|
+
Generate only when nothing fits, because a clip uses the clip quota: `reelkit assets gen clip --background "<look>"` makes an abstract, slow, seamless clip with no people or text, and `reelkit assets gen image --background --scene <id>` a picture. For a set of pictures, give every prompt the same style sentence so that they look related. The user's own file: `reelkit assets upload <file> --background`.
|
|
26
|
+
|
|
27
|
+
All of these record the ground in `assets/background.json`. The composition reads it as `manifest.background` (`key` and, for a video, `durationSec`) and `manifest.scenes[i].background` (`key`).
|
|
28
|
+
|
|
29
|
+
## Keeping the accent
|
|
30
|
+
|
|
31
|
+
Tint the ground with the film's one accent colour (`tint={palette.hero}`), so that a clip you did not make still belongs to the film.
|
|
32
|
+
|
|
33
|
+
## Readable type
|
|
34
|
+
|
|
35
|
+
`reelkit check` notes a `BgVideo` with a `dim` under 0.2 when the plan has on-screen text, and a recorded ground the composition never draws. Preview the frames: if type is hard to read anywhere, raise `dim`, add `blur`, or move the type.
|
|
36
|
+
|
|
37
|
+
## Example
|
|
38
|
+
|
|
39
|
+
```tsx
|
|
40
|
+
import React from "react";
|
|
41
|
+
import { AbsoluteFill } from "remotion";
|
|
42
|
+
import { BgImage, BgSequence, BgVideo, SceneFrame, bgPerScene, palettes } from "reelkit/kit";
|
|
43
|
+
import type { VideoProps } from "reelkit/kit";
|
|
44
|
+
|
|
45
|
+
export const Video: React.FC<VideoProps> = ({ manifest, urls }) => {
|
|
46
|
+
const palette = palettes.darkTech;
|
|
47
|
+
const scenesWithPictures = manifest.scenes.some((s) => s.background);
|
|
48
|
+
return (
|
|
49
|
+
<AbsoluteFill>
|
|
50
|
+
{scenesWithPictures ? (
|
|
51
|
+
<BgSequence items={bgPerScene(manifest.scenes, manifest.scenes.map((s) => (s.background ? urls[s.background.key] : undefined)))} tint={palette.hero} dim={0.4} />
|
|
52
|
+
) : manifest.background ? (
|
|
53
|
+
<BgVideo src={urls[manifest.background.key]} durationSec={manifest.background.durationSec} dim={0.4} tint={palette.hero} />
|
|
54
|
+
) : (
|
|
55
|
+
<BgImage src={urls["assets/bg-film.png"]} dim={0.4} drift="left" />
|
|
56
|
+
)}
|
|
57
|
+
{manifest.scenes.map((s) => (
|
|
58
|
+
<SceneFrame key={s.id} from={s.startFrame} durationInFrames={s.durationFrames}>{null}</SceneFrame>
|
|
59
|
+
))}
|
|
60
|
+
</AbsoluteFill>
|
|
61
|
+
);
|
|
62
|
+
};
|
|
63
|
+
```
|
|
@@ -8,38 +8,53 @@ A video with music feels edited when things happen on the beat. The CLI puts the
|
|
|
8
8
|
|
|
9
9
|
Then `manifest.json` has `music: { key, bpm, beatFrames }`. `beatFrames` are the beats as composition frames. If a track has no clear tempo there is no `bpm`, `beatFrames` is empty, and nothing below applies: the scenes stay where the narration puts them.
|
|
10
10
|
|
|
11
|
-
## 2. Scene changes
|
|
11
|
+
## 2. Scene changes: the voice first, the beat when it is close
|
|
12
12
|
|
|
13
|
-
When the track has a tempo,
|
|
13
|
+
With a narrator, the narration sets where a scene ends: the next scene starts after the plan's `gap` of silence (`tight` 0.2 s, `normal` 0.3 s, `relaxed` 0.5 s; the last scene keeps 0.7 s after its last word). When the track has a tempo, a scene change moves to the nearest beat only if that is at most 4 frames away either way and leaves at least 2 frames after the last word; the next scene's voice starts with it. Every other change stays where the voice puts it. `reelkit assets voiceover` and `reelkit preview` say how many changes are on the beat ("3 of 5 scene changes land on the beat; the others follow the voice"): that is the truth, not a fault to fix. Do not shift `startFrame` yourself, and do not pad a scene to reach a beat: that is dead air. A film with no voice puts its cuts on the beat (`reference/launch-film.md`).
|
|
14
14
|
|
|
15
|
-
## 3. Inside a scene:
|
|
15
|
+
## 3. Inside a scene: the voice is the clock, the beat is for decoration
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
With a narrator, anything that shows or names a word goes on that word: a number, an icon, a list item, a headline, a banner. The voice is the clock for it, and a beat never moves it more than a few frames. The beat is the clock for what has no word: scene changes, a decorative pulse, a sound under a move, and every element of a film with no voice. Do not count words by hand and do not write your own index helper: the kit finds the word for you (`reference/voice-sync.md` has the whole method).
|
|
18
18
|
|
|
19
|
-
- the word starts
|
|
20
|
-
- `
|
|
21
|
-
- `
|
|
19
|
+
- `wordFrame(s, "tasks")` is the frame inside the scene where the word starts (a phrase such as "whole week" gives its first word; `{ nth: 2 }` the second time it is said; `{ edge: "end" }` where it ends; `{ absolute: true }` adds `s.startFrame`). A word the scene does not say throws and lists the scene's words, so a typo stops the render and `reelkit check` reports it before then.
|
|
20
|
+
- `onWord(s, "tasks")` is the frame to START an entrance so that it is seen landing on the word: `wordFrame` minus 3 (`lead`), never below 0.
|
|
21
|
+
- `onWordBeat(s, "tasks", manifest.music)` is the same, with one rule for beat and word together: the entrance lands on a beat only if a beat falls within 3 frames AFTER the word starts (`window`); otherwise the word wins. It is never earlier than `onWord`, so a thing is never on screen more than 3 frames before the word that names it. Use it for the hero element when a track is set; use plain `onWord` for everything that must be exact.
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
Nearest-beat snapping is wrong for an element that illustrates a word: the nearest beat is as often before the word as after it, and a banner that appears before "meeting" is said is a mistake. `nearestBeat`, `nextBeat` and `beatPulse` stay for the things the beat owns: `beatPulse(beatFrames, frame)` is 1 on a beat and falls to 0; use it sparingly, for a small scale or glow on the hero element only, never on everything. Put a decorative move on a strong beat (every fourth beat counts from the scene's first beat).
|
|
24
24
|
|
|
25
|
-
Sound effects
|
|
25
|
+
Sound effects: the `at` of an `Sfx` is a frame from the scene start. A sound for a word's visual goes at that visual's frame (`onWord(s, "tasks") + 2`), under the voice at 0.3 or less (see "Sound levels" below).
|
|
26
26
|
|
|
27
27
|
```tsx
|
|
28
|
-
import { Entrance, Music,
|
|
28
|
+
import { Entrance, Music, onWord, onWordBeat, SceneFrame, sceneById, Voiceover } from "reelkit/kit";
|
|
29
29
|
|
|
30
|
-
const
|
|
31
|
-
// Frames from the scene start to the beat nearest the word at index i.
|
|
32
|
-
const onBeat = (s: { startFrame: number; words: { startSec: number }[] }, i: number) => {
|
|
33
|
-
const word = s.startFrame + Math.round((s.words[i]?.startSec ?? 0) * manifest.fps);
|
|
34
|
-
return (nearestBeat(beats, word) ?? word) - s.startFrame;
|
|
35
|
-
};
|
|
30
|
+
const s = sceneById(manifest, "meet");
|
|
36
31
|
|
|
37
32
|
<SceneFrame from={s.startFrame} durationInFrames={s.durationFrames}>
|
|
38
|
-
|
|
33
|
+
{/* exact: the list item lands on its word */}
|
|
34
|
+
<Entrance delay={onWord(s, "tasks")}><Card /></Entrance>
|
|
35
|
+
{/* the hero: lands on a beat when one is within 3 frames after the word, otherwise on the word */}
|
|
36
|
+
<Entrance delay={onWordBeat(s, "builds", manifest.music)}><Card /></Entrance>
|
|
39
37
|
{s.voiceoverKey ? <Voiceover src={urls[s.voiceoverKey]} /> : null}
|
|
40
38
|
</SceneFrame>
|
|
41
39
|
// Once, outside the scenes:
|
|
42
40
|
{manifest.music ? <Music src={urls[manifest.music.key]} /> : null}
|
|
43
41
|
```
|
|
44
42
|
|
|
43
|
+
`onBeat(s, i)` with a hand-counted word index (the old way: `nearestBeat` of the word at index `i`) still works if you wrote it yourself, but do not start new work with it.
|
|
44
|
+
|
|
45
45
|
`Music` ducks under the voice by itself and fades out over the last second; do not set its volume per scene.
|
|
46
|
+
|
|
47
|
+
## Hits go where the eye sees the change
|
|
48
|
+
|
|
49
|
+
A `SceneFrame` transition (a zoom-through, blur, push or whip) is complete on the scene boundary, but the picture changes most half the transition's frames earlier: 4 frames before the boundary for the default 8, 3 for `transitionFrames={6}`. A hit on the boundary lands after the change and reads as late; the sound report measures it as a miss. Put the hit on the visual peak: `cuesOnChanges(scenes, { impact, whoosh })` does it for every change (the impact on the peak, the whoosh so that it ends there), and `changeFrame(scene)` gives the peak for one. The scene's `startFrame` still sits on the beat, so a hit 3 or 4 frames before it is within a tenth of a second of the beat too.
|
|
50
|
+
|
|
51
|
+
A camera zoom, a `zoomTo` and a punch are picture changes as well: put a hit on each with `cueOnCamera(frame)` (for a key: about 9 frames before the key's frame) or `cueOnCamera(frame, { punch: true })` (just after the punch's frame). A `Camera` inside a `SceneFrame` counts frames from the scene's start, so add the scene's `startFrame` for the absolute frame a cue needs.
|
|
52
|
+
|
|
53
|
+
## Sound levels
|
|
54
|
+
|
|
55
|
+
Every sound you pull (`sfx` and `music`) is measured when it is pulled and levelled: a sound effect shorter than 3 seconds to a peak of -3 dBFS, a longer sound and the music to about -18 LUFS, never more than 18 dB either way. The gain is in `assets/library.json` (`gainDb`) and in the manifest's `soundGain`, and `Sfx` and `Music` apply it by themselves, so the same `volume` number sounds equally loud for every file. Then:
|
|
56
|
+
|
|
57
|
+
- the voiceover is `1` (the default);
|
|
58
|
+
- a sound effect is `0.2` to `0.45`; the default `0.35` is right for most, and a small tick can go lower (a film with no voice uses the levels in `reference/launch-film.md` instead);
|
|
59
|
+
- the music is `0.5` when it plays alone and sits at `0.18` under the voice by itself (`volume` and `duckTo` on `Music`). It stays steady across the 0.2 to 0.5 s between sentences and comes up only in a real pause of 1.2 s or more (rising over 0.4 s, back down 0.25 s before the next word), so it does not pump; leave both alone unless `reelkit sound` says the music under the voice is outside 10 to 22 dB below it;
|
|
60
|
+
- the render is mastered at the end to -14 LUFS with peaks under -1 dBTP, so do not try to make the whole video louder: balance the parts against each other.
|
|
@@ -17,10 +17,16 @@ The plan's `captions` field says: `"none"`, `"word"` (one word at a time) or `"p
|
|
|
17
17
|
|
|
18
18
|
With `"none"` the component draws nothing; better still, leave `<Captions>` out of the composition altogether. `reelkit check` notes a composition that renders `<Captions>` when the plan says `"none"`, and one that renders none when the plan asks for words.
|
|
19
19
|
|
|
20
|
+
## What the user's answer means
|
|
21
|
+
|
|
22
|
+
- "No subtitles": `"none"`.
|
|
23
|
+
- "A few words at a time", "a phrase at a time", "a line", or no preference: `"phrase"` (the default). This is the group mode: a few words, never a whole sentence, never one word.
|
|
24
|
+
- "One word at a time", "word by word", "like the big pop captions": `"word"`. Only then is a single word shown. Do not pick `"word"` for "a few words": one word at a time is hard to read on a busy picture and the user did not ask for it.
|
|
25
|
+
|
|
20
26
|
## How words are grouped (`group`)
|
|
21
27
|
- `"word"`: exactly one word on screen at a time. Larger text, the most energy.
|
|
22
|
-
- `"phrase"`: words are grouped the way they are spoken
|
|
23
|
-
- Without `group
|
|
28
|
+
- `"phrase"`: words are grouped the way they are spoken. A group ends at the end of a sentence (`.`, `?` or `!`: a group never spans two sentences), at a comma or a dash once it has three words, and at a pause of 0.35 s or more between words. It holds at most 3 words by default (`maxWords={4}` for more, up to 6) and about 32 characters; when it must break it does so after a comma or before a short joining word ("and", "of", "to") where it can. Hebrew and Arabic sentence marks work the same way.
|
|
29
|
+
- Without `group` (and without `perLine`) the groups are the same as `"phrase"`: 3 words, never one. Only `group="word"` (the plan's `"captions": "word"`) shows one word. `perLine` alone counts the words as it always did; use `group` for every new video.
|
|
24
30
|
- `mode` (below) still decides how the words inside the group look.
|
|
25
31
|
|
|
26
32
|
## Pick one style for the whole video
|
|
@@ -35,10 +41,10 @@ With `"none"` the component draws nothing; better still, leave `<Captions>` out
|
|
|
35
41
|
<Captions words={s.words} mode="pop" highlight={palette.hero} uppercase />
|
|
36
42
|
```
|
|
37
43
|
|
|
38
|
-
Do not mix modes between scenes. Set `highlight` to the palette's hero colour (captions are the one exception to the one-hero-element rule)
|
|
44
|
+
Do not mix modes between scenes. Set `highlight` to the palette's hero colour (captions are the one exception to the one-hero-element rule): any accent works, because the word is on a chip with its own ink; leave it out for a yellow.
|
|
39
45
|
|
|
40
46
|
## Words per screen
|
|
41
|
-
- With `group="phrase"` the groups are at most
|
|
47
|
+
- With `group="phrase"` (or no `group`) the groups are at most 3 words, and about 32 characters, by themselves; `maxWords` raises it to at most 6. With `group="word"` it is one.
|
|
42
48
|
- With `perLine` instead: `pop` 1 to 3 words; `highlight` and `karaoke` 3 to 5 words for vertical video, up to 6 for landscape; in Hebrew at most 4.
|
|
43
49
|
- Never show a full sentence at once. Word-level timing reads better than sentence captions.
|
|
44
50
|
|
|
@@ -61,7 +67,7 @@ Do not mix modes between scenes. Set `highlight` to the palette's hero colour (c
|
|
|
61
67
|
|
|
62
68
|
## Legibility
|
|
63
69
|
- Heavy sans-serif weight, large: about 5.5 percent of the width for line captions, 7.5 percent for pop. The component sets these.
|
|
64
|
-
- Contrast of at least 4.5 to 1 against whatever is behind
|
|
70
|
+
- Contrast of at least 4.5 to 1 against whatever is behind, on any ground, with no colour chosen by you: the component puts the words on a dark rounded plate (about 58% opaque) in white, and the spoken word sits on a chip of `highlight` (the accent) with white or dark ink, whichever reads better on it (a `karaoke` fill has no chip: it uses the accent mixed toward white until it reads on the plate). Measured on the render: white ink on the plate is about 5:1 over a cream ground, 5:1 over a bright busy picture and 20:1 over a dark one. Do not set `color` to anything but white, and do not draw your own outline or box.
|
|
65
71
|
- `uppercase` suits `pop` and short hooks; avoid it for long lines.
|
|
66
72
|
|
|
67
73
|
## Emphasis effects, with limits
|
|
@@ -16,6 +16,7 @@ Run `reelkit assets search "<description>" --kind component`. If a library compo
|
|
|
16
16
|
- Size relative to `useVideoConfig()`, never fixed pixels.
|
|
17
17
|
- Animate from the local `useCurrentFrame()`, and accept an optional `delay` prop in frames.
|
|
18
18
|
- Declare the props type in the same file and export it.
|
|
19
|
+
- Start every new component with a comment of one or two sentences directly above `export const Name`, saying what it shows and when to use it. It becomes the library description when the component is shared, so it must make sense without this video: "A ring that fills to a percentage with a label in the middle. Use it for one big stat."
|
|
19
20
|
|
|
20
21
|
## Keep it reusable
|
|
21
22
|
Write each component so it would suit a video on an unrelated topic:
|
|
@@ -23,4 +24,14 @@ Write each component so it would suit a video on an unrelated topic:
|
|
|
23
24
|
- **Structural** - a badge, checklist, chart, progress bar, callout, quote card, icon animation, transition.
|
|
24
25
|
- **Passing** - `reelkit check` passes with it in use.
|
|
25
26
|
|
|
26
|
-
Avoid one-off layouts, anything with content baked in, or a near-duplicate of a library component.
|
|
27
|
+
Avoid one-off layouts, anything with content baked in, or a near-duplicate of a library component.
|
|
28
|
+
|
|
29
|
+
## Sharing
|
|
30
|
+
After a successful `reelkit render`, every new component written in the project is sent to the library for review (its source, the comment above it as the description, and an example taken from `Video.tsx`; nothing else), and only the owner publishes it. Make that work:
|
|
31
|
+
- Use the component in `Video.tsx` with plain values where you can (`<NumberBadge value={42} label="users" />`). The example is the first use there; if its props are variables or `urls[...]` or `s.words`, the component is skipped and `reelkit components share NumberBadge --example '<NumberBadge value={42} label="users" />'` sends it with an example you give.
|
|
32
|
+
- Keep the text out of the file: nothing from the user's script, no `assets/user/...` path, and not the video's title. A component that has any is refused.
|
|
33
|
+
- `reelkit components share [name...]` sends by hand (`--describe`, `--example`, `--tags` for one name); it does not wait for a render.
|
|
34
|
+
- The user may say no: `reelkit init <name> --private` for the project, `reelkit render --no-share` for one render, or `REELKIT_NO_SHARE=1`.
|
|
35
|
+
|
|
36
|
+
## What keeps a component from being shared
|
|
37
|
+
A component is shared only when nothing of this video is written into it. It is skipped when it uses one of the user's files, contains the plan's title, contains any run of eight or more characters that also appears in the narration or the on-screen text, or has a whole sentence written into its code. Pass every word in as a prop, with a short neutral default ("Label", "42"), and the component is both reusable and shareable.
|
|
@@ -32,7 +32,7 @@ Ways to do it with the kit:
|
|
|
32
32
|
- A container that grows into the next scene's background. The card of one scene becomes the full-frame ground of the next.
|
|
33
33
|
- A shared colour field that one scene's object expands into. The new scene is the colour the old object grew to.
|
|
34
34
|
|
|
35
|
-
A hard cut is a choice. Use it for a burst of fast hits, or for the end card, and know that you are doing it. It is not the default.
|
|
35
|
+
A hard cut is a choice. Use it for a burst of fast hits, or for the end card, and know that you are doing it. It is not the default. Say so in the plan: give the scene that begins on the cut `"cutIn": true`. `reelkit preview` then reports that change as `cut (intended)` and leaves it out of the score.
|
|
36
36
|
|
|
37
37
|
`SceneFrame` fades each scene in and out over its first and last few frames. Anything that must stay on screen through a change therefore lives outside it, in a `Carry` layer (or inside a `Camera` that wraps all the scenes). This example holds a price card through two scenes: it is large in the first, then shrinks into a badge that the second scene is built around.
|
|
38
38
|
|
|
@@ -90,10 +90,29 @@ For product pictures, the default is a small element on a big ground: the card,
|
|
|
90
90
|
|
|
91
91
|
## Reading the numbers
|
|
92
92
|
|
|
93
|
-
`reelkit preview` also saves the two frames around each scene change (`b01-end-<scene>.jpg`, then `b01-start-<scene>.jpg`, and so on) and prints a continuity report: a line such as "3 of 5 scene changes carry something across", and a line for each change where nothing does. The frames are the last and first ones at full strength, because `SceneFrame` fades the very last and first. The measure compares edges, the outlines of what is on screen: when at least a quarter of the earlier frame's outlines are still there, nearby, in the later frame, the change is `carried`; otherwise it is a `cut`. A change where the earlier frame is nearly empty is not counted.
|
|
93
|
+
`reelkit preview` also saves the two frames around each scene change (`b01-end-<scene>.jpg`, then `b01-start-<scene>.jpg`, and so on) and prints a continuity report: a line such as "3 of 5 scene changes carry something across", and a line for each change where nothing does. The frames are the last and first ones at full strength, because `SceneFrame` fades the very last and first. The measure compares edges, the outlines of what is on screen: when at least a quarter of the earlier frame's outlines are still there, nearby, in the later frame, the change is `carried`; otherwise it is a `cut`. A change where the earlier frame is nearly empty is not counted. The later scene is sampled twice, at its first fully visible frame and about six frames on, and the better of the two is taken, so a plate that settles into a card is not called a cut. A scene with `cutIn: true` is reported as `cut (intended)` and not counted.
|
|
94
94
|
|
|
95
95
|
Read it as a smoke alarm, not a grade. A `cut` is a prompt to fix the change or to say in the notes why it is a cut. A `carried` change can still be wrong (an element that stays but means nothing), so look at the pair of frames. The report is advice and never makes `preview` fail. Aim for most changes carried, with the cuts that remain on purpose.
|
|
96
96
|
|
|
97
97
|
`reelkit plan check` reads the rhythm from the narration: with four scenes or more, it adds a line under "Worth improving" when the scenes are about equally long, or when none is shorter than half the average. It quotes the shortest and the longest. Its data holds `sceneSeconds` for each scene and `lengthVariation`, the spread of the lengths over their mean; below 0.25 is too even. The cure is in the line itself: make one or two scenes much shorter, a hit of a few words, and let one run long.
|
|
98
98
|
|
|
99
99
|
When the video follows a reference, `reelkit ref analyze` gives targets to match. `pacing.shotLengthVariation` is the same spread for the reference's shots (left out when it has fewer than three), and `stillness` holds `share`, the part of the time in which the picture barely changes from frame to frame, and `longestSec`, the longest such hold. If the reference's shots vary by 0.8 and it is still a third of the time, plan scene lengths that vary about that much, and let the picture rest about that often. A video that moves every frame, against a reference that holds, will feel busier than the reference, whatever else you copy.
|
|
100
|
+
|
|
101
|
+
## Transitions
|
|
102
|
+
|
|
103
|
+
`SceneFrame` fades by default. Choose another transition with `enter` and `exit` (the same kind on both sides of a change), `transitionFrames` (8 by default, at most 20) and, for a zoom, `origin` (where it aims, as fractions of the frame). Each is complete exactly at the scene boundary, so the cut stays on the beat. A transition is a choice with a meaning:
|
|
104
|
+
|
|
105
|
+
- `zoom-through`: going INTO something. The old scene rushes toward a point and the new one arrives from there: into a screen, a detail, a product.
|
|
106
|
+
- `push-left`, `push-right`, `push-up`, `push-down`: moving along a sequence, such as the next step or the next card in a row.
|
|
107
|
+
- `whip-left`, `whip-right`: a quick list, one item after another, with a smear that carries the eye.
|
|
108
|
+
- `turn`: a before and after, or two sides of the same thing.
|
|
109
|
+
- `zoom-in`, `zoom-out`, `blur`: a soft change of subject or mood.
|
|
110
|
+
- `cut`: a burst of hits, where speed matters more than smoothness (mark it with `cutIn` in the plan).
|
|
111
|
+
- `fade`: the default, for a calm change.
|
|
112
|
+
|
|
113
|
+
Rules: use at most two kinds in one video. Never the same exit twice in a row for different meanings: a push that always means "next" is a rhythm, but a push for one thing and a push for another is noise. A carried element (`Carry`, `Camera`) beats any transition: if something stays on screen and moves into the next scene, the change needs no transition at all, so leave it `fade` or `cut`. `reelkit preview` takes its boundary frames clear of a literal `transitionFrames={N}` in the code.
|
|
114
|
+
|
|
115
|
+
```tsx
|
|
116
|
+
<SceneFrame from={a.startFrame} durationInFrames={a.durationFrames} exit="zoom-through" origin={{ x: 0.5, y: 0.45 }}>...</SceneFrame>
|
|
117
|
+
<SceneFrame from={b.startFrame} durationInFrames={b.durationFrames} enter="zoom-through" origin={{ x: 0.5, y: 0.45 }}>...</SceneFrame>
|
|
118
|
+
```
|