flomotion 0.2.0 → 0.4.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/CHANGELOG.md +58 -2
- package/LICENSE +1 -1
- package/README.md +29 -20
- package/dist/bin.mjs +6279 -2582
- package/dist/chunks/{chunk-QG2MOGKO.mjs → chunk-IGG2VZB6.mjs} +178 -14
- package/dist/chunks/{chunk-AVP5XX4G.mjs → chunk-PEC2UVQY.mjs} +138 -108
- package/dist/chunks/{chunk-IUSYVGP6.mjs → chunk-TTHAZGRG.mjs} +7 -7
- package/dist/chunks/{chunk-JCLRAREG.mjs → chunk-XWY6CUY6.mjs} +16 -16
- package/dist/chunks/{lock-MLQUU7X3.mjs → lock-WGPR7CJ2.mjs} +3 -3
- package/dist/components.mjs +19 -13
- package/dist/core.mjs +12 -12
- package/dist/hooks.mjs +8 -12
- package/dist/jsx-dev-runtime.mjs +1 -1
- package/dist/jsx-runtime.mjs +1 -1
- package/docs/components/Adjustment.md +2 -2
- package/docs/components/Audio.md +2 -2
- package/docs/components/B.md +3 -3
- package/docs/components/Captions.md +2 -2
- package/docs/components/Card.md +2 -2
- package/docs/components/Center.md +2 -2
- package/docs/components/Chart.md +2 -2
- package/docs/components/Chip.md +2 -2
- package/docs/components/Circle.md +2 -2
- package/docs/components/Clip.md +2 -2
- package/docs/components/CodeBlock.md +2 -2
- package/docs/components/Composition.md +2 -2
- package/docs/components/Counter.md +2 -2
- package/docs/components/Cursor.md +2 -2
- package/docs/components/Fonts.md +2 -2
- package/docs/components/GlassCard.md +2 -2
- package/docs/components/Grid.md +2 -2
- package/docs/components/Group.md +2 -2
- package/docs/components/Highlight.md +3 -3
- package/docs/components/I.md +3 -3
- package/docs/components/Image.md +2 -2
- package/docs/components/Line.md +2 -2
- package/docs/components/Marker.md +2 -2
- package/docs/components/Path.md +2 -2
- package/docs/components/Polygon.md +2 -2
- package/docs/components/Precomp.md +2 -2
- package/docs/components/ProgressRing.md +2 -2
- package/docs/components/Rect.md +2 -2
- package/docs/components/RoundRect.md +2 -2
- package/docs/components/SafeArea.md +2 -2
- package/docs/components/Scene.md +2 -2
- package/docs/components/Sequence.md +2 -2
- package/docs/components/Span.md +3 -3
- package/docs/components/SplitText.md +2 -2
- package/docs/components/Stack.md +2 -2
- package/docs/components/Stagger.md +2 -2
- package/docs/components/Strike.md +3 -3
- package/docs/components/Svg.md +2 -2
- package/docs/components/Text.md +2 -2
- package/docs/components/Title.md +2 -2
- package/docs/components/Track.md +2 -2
- package/docs/components/UIMock.md +2 -2
- package/docs/components/Underline.md +3 -3
- package/docs/components/Video.md +2 -2
- package/docs/gate2-manual-checklist.md +10 -9
- package/docs/hyperframes-notes.md +20 -20
- package/docs/later.md +23 -16
- package/docs/spec.md +83 -22
- package/package.json +1 -2
- package/skills/flomotion-studio/CHANGELOG.md +39 -0
- package/skills/flomotion-studio/README.md +81 -0
- package/skills/flomotion-studio/SKILL.md +90 -0
- package/skills/flomotion-studio/config/thresholds.md +191 -0
- package/skills/flomotion-studio/dataset/annotations/ansarada.md +53 -0
- package/skills/flomotion-studio/dataset/annotations/canva.md +49 -0
- package/skills/flomotion-studio/dataset/annotations/clickup.md +60 -0
- package/skills/flomotion-studio/dataset/annotations/gemini-stub.md +39 -0
- package/skills/flomotion-studio/dataset/annotations/monday.md +51 -0
- package/skills/flomotion-studio/dataset/annotations/phantom.md +53 -0
- package/skills/flomotion-studio/dataset/annotations/slack.md +54 -0
- package/skills/flomotion-studio/dataset/annotations/unknown-7.md +39 -0
- package/skills/flomotion-studio/dataset/annotations/vaylens.md +53 -0
- package/skills/flomotion-studio/dataset/brief-template.md +110 -0
- package/skills/flomotion-studio/dataset/rating-sheet.md +28 -0
- package/skills/flomotion-studio/dataset/schema.md +44 -0
- package/skills/flomotion-studio/evals/README.md +33 -0
- package/skills/flomotion-studio/evals/briefs/briefs.md +25 -0
- package/skills/{motion-studio/looks → flomotion-studio/examples/engine-looks}/kinetic.scene.tsx +1 -1
- package/skills/{motion-studio/looks → flomotion-studio/examples/engine-looks}/minimal.scene.tsx +1 -1
- package/skills/{motion-studio/looks → flomotion-studio/examples/engine-looks}/sketch.scene.tsx +1 -1
- package/skills/flomotion-studio/examples/flomotion-ad/LOG.md +39 -0
- package/skills/flomotion-studio/examples/flomotion-ad/brief.md +41 -0
- package/skills/flomotion-studio/examples/flomotion-ad/ideas.md +12 -0
- package/skills/flomotion-studio/examples/flomotion-ad/project.tsx +1694 -0
- package/skills/flomotion-studio/examples/flomotion-ad/script.md +28 -0
- package/skills/flomotion-studio/examples/flomotion-ad/storyboard.md +93 -0
- package/skills/{motion-studio → flomotion-studio}/examples/saas-promo/README.md +1 -1
- package/skills/{motion-studio → flomotion-studio}/examples/saas-promo/make-assets.sh +1 -1
- package/skills/{motion-studio → flomotion-studio}/examples/saas-promo/project.tsx +3 -3
- package/skills/flomotion-studio/looks/bold-kinetic.md +122 -0
- package/skills/flomotion-studio/looks/clean-light.md +122 -0
- package/skills/flomotion-studio/looks/dark-product.md +120 -0
- package/skills/flomotion-studio/references/anti-patterns.md +36 -0
- package/skills/flomotion-studio/references/assets.md +42 -0
- package/skills/flomotion-studio/references/block-recipes.md +249 -0
- package/skills/flomotion-studio/references/blocks.md +114 -0
- package/skills/flomotion-studio/references/catalogue.md +53 -0
- package/skills/flomotion-studio/references/checks.md +87 -0
- package/skills/flomotion-studio/references/clean-styles.md +56 -0
- package/skills/{motion-studio → flomotion-studio}/references/components.md +94 -94
- package/skills/flomotion-studio/references/copywriting.md +64 -0
- package/skills/flomotion-studio/references/decision-tables.md +80 -0
- package/skills/flomotion-studio/references/interview.md +145 -0
- package/skills/flomotion-studio/references/looks.md +45 -0
- package/skills/flomotion-studio/references/mcp-tools.md +70 -0
- package/skills/flomotion-studio/references/motion.md +68 -0
- package/skills/flomotion-studio/references/patterns.md +45 -0
- package/skills/flomotion-studio/references/review.md +56 -0
- package/skills/flomotion-studio/references/script-craft.md +59 -0
- package/skills/flomotion-studio/references/sound.md +57 -0
- package/skills/flomotion-studio/references/story-templates.md +52 -0
- package/skills/flomotion-studio/references/storyboard.md +62 -0
- package/skills/flomotion-studio/references/styles.md +70 -0
- package/skills/flomotion-studio/references/transitions.md +62 -0
- package/skills/flomotion-studio/references/typography.md +46 -0
- package/skills/flomotion-studio/references/workflow.md +96 -0
- package/skills/motion-studio/SKILL.md +0 -87
- package/skills/motion-studio/looks/kinetic.json +0 -89
- package/skills/motion-studio/looks/minimal.json +0 -91
- package/skills/motion-studio/looks/sketch.json +0 -83
- package/skills/motion-studio/references/motion.md +0 -73
- package/skills/motion-studio/references/review.md +0 -32
- package/skills/motion-studio/references/saas-promo.md +0 -29
- package/skills/motion-studio/references/sound.md +0 -47
- package/skills/motion-studio/references/typography.md +0 -50
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
# Block recipes (TSX you can write with `scene_write`)
|
|
2
|
+
|
|
3
|
+
`blocks.md` says what each block is for. This file is **how to build the 13 core blocks with the real components**. Each recipe is one `<Scene>` in the `clean-light` look at 1080 x 1920 inside `<SafeArea preset="reels">`. To use one:
|
|
4
|
+
|
|
5
|
+
1. Copy the recipe, change the `id`s to names that describe the beat (`hook-claim`, not `t1`), and replace the sample words and numbers with the person's own. Sample text is invented; never ship it.
|
|
6
|
+
2. Set `start` and `duration` from the storyboard.
|
|
7
|
+
3. `scene_write` it over the placeholder scene, then `lint`, then `frame_render` at the first and last moment and read the frames.
|
|
8
|
+
|
|
9
|
+
Switching look: the tokens (`$brand.*`, `$type.*`) come from the active look, so the same recipe re-skins when `theme` changes. Dark looks use `$brand.text` on `$brand.bg`; the flat-block looks (`bold-kinetic`) should set `fill` of the background `Rect` to one of `$brand.block1..5` per scene.
|
|
10
|
+
|
|
11
|
+
A recipe is a starting structure, not a finished scene. Tune timing to the voice, rotate entrance directions, and keep **one hero** (see `motion.md`). The 13 pattern blocks (`TypedQuery`, `WipeMatchCut`, ...) have no recipes yet: build them from the same components as a **custom scene** and record why in `LOG.md` (block rule 5).
|
|
12
|
+
|
|
13
|
+
Sounds are not placed here. After the scenes exist run `sound_auto` (dry run first) and place the rest with `sound_search` and `sound_get`.
|
|
14
|
+
|
|
15
|
+
## 1 Hook
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
<Scene id="hook" start="0s" duration="2.6s" scale={[[0, 1], [2.6, 1.05]]}>
|
|
19
|
+
<Rect id="hook-bg" width={1080} height={1920} fill="$brand.bg" />
|
|
20
|
+
<SafeArea id="hook-safe" preset="reels">
|
|
21
|
+
<SplitText id="hook-claim" text="Stop editing. Start shipping." at={[0, 520]} align="left" size={132}
|
|
22
|
+
fill="$brand.text" font="$type.display" preset="rise" stagger={0.08} start="0.05s" maxWidth="100%">
|
|
23
|
+
<Shake id="hook-shake" amp={8} freq={16} decay={9} start="0.55s" />
|
|
24
|
+
</SplitText>
|
|
25
|
+
<Rect id="hook-rule" at={[0, 1010]} width={220} height={12} radius={6} fill="$brand.accent" anchor={[0, 50]}
|
|
26
|
+
scale={[[0.5, [0, 1]], [1.1, [1, 1], "expo.out"]]} />
|
|
27
|
+
</SafeArea>
|
|
28
|
+
</Scene>
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## 2 Problem (list)
|
|
32
|
+
|
|
33
|
+
```tsx
|
|
34
|
+
<Scene id="problem" start="2.6s" duration="4.5s" scale={[[0, 1], [4.5, 1.04]]}>
|
|
35
|
+
<Rect id="problem-bg" width={1080} height={1920} fill="$brand.bg" />
|
|
36
|
+
<SafeArea id="problem-safe" preset="reels">
|
|
37
|
+
<Stagger id="problem-lines" each={0.9}>
|
|
38
|
+
<Text id="problem-1" at={[0, 420]} text="Updates in five places." size={72} weight={700} fill="$brand.text" align="left" maxWidth="100%" font="$type.display" enter={{ from: "w", distance: 90, ease: "expo.out", duration: 0.5 }} />
|
|
39
|
+
<Text id="problem-2" at={[0, 640]} text="Nobody reads them." size={72} weight={700} fill="$brand.text" align="left" maxWidth="100%" font="$type.display" enter={{ from: "e", distance: 90, ease: "expo.out", duration: 0.5 }} />
|
|
40
|
+
<Text id="problem-3" at={[0, 860]} text="Deadlines slip." size={72} weight={700} fill="$brand.negative" align="left" maxWidth="100%" font="$type.display" enter={{ from: "w", distance: 90, ease: "expo.out", duration: 0.5 }} />
|
|
41
|
+
</Stagger>
|
|
42
|
+
</SafeArea>
|
|
43
|
+
</Scene>
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Vary the entry direction line to line (block fail: every line the same animation). Dim earlier lines with an `opacity` keyframe to 0.4 when the next one lands.
|
|
47
|
+
|
|
48
|
+
## 3 ProductReveal
|
|
49
|
+
|
|
50
|
+
```tsx
|
|
51
|
+
<Scene id="reveal" start="7.1s" duration="3.2s" transition={{ type: "slide", direction: "s", duration: 0.45 }} scale={[[0, 1], [3.2, 1.04]]}>
|
|
52
|
+
<Rect id="reveal-bg" width={1080} height={1920} fill={{ type: "linear", angle: 180, stops: [[0, "$brand.bg"], [1, "$brand.surface"]] }} />
|
|
53
|
+
<SafeArea id="reveal-safe" preset="reels">
|
|
54
|
+
<Title id="reveal-title" text="Meet Pulse." at="top" size={110} fill="$brand.text" font="$type.display" enter={{ from: "n", distance: 80, ease: "expo.out", duration: 0.55 }} />
|
|
55
|
+
</SafeArea>
|
|
56
|
+
<UIMock id="reveal-phone" at={[190, 400]} device="phone" width={700} height={1080}
|
|
57
|
+
enter={{ from: "s", distance: 520, ease: "expo.out", duration: 0.9, delay: 0.2 }}>
|
|
58
|
+
<Rect id="reveal-screen" width={656} height={1036} fill="$brand.surface" at="top-left" />
|
|
59
|
+
<Text id="reveal-app" text="Pulse" at={[40, 90]} size={56} weight={700} fill="$brand.text" font="$type.display" maxWidth={500} align="left" />
|
|
60
|
+
<Chip id="reveal-live" at={[420, 96]} label="Live" dot="$brand.positive" fill="$brand.bg" textColor="$brand.text" size={30} />
|
|
61
|
+
</UIMock>
|
|
62
|
+
<PunchZoom id="reveal-punch" start="0.2s" amount={0.05} />
|
|
63
|
+
</Scene>
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Use a real screenshot or skeleton UI inside the screen (`Image` or a few `RoundRect` bars), never invented product claims.
|
|
67
|
+
|
|
68
|
+
## 4 UIZoom
|
|
69
|
+
|
|
70
|
+
```tsx
|
|
71
|
+
<Scene id="uizoom" start="10.3s" duration="3s">
|
|
72
|
+
<Rect id="uizoom-bg" width={1080} height={1920} fill="$brand.bg" />
|
|
73
|
+
<Card id="uizoom-ui" at={[60, 420]} layout="absolute" width={960} height={680} fill="$brand.surface" radius={28}
|
|
74
|
+
anchor={[8, 28]} scale={[[0.2, 1], [1.4, 1.7, "power3.inOut"]]}>
|
|
75
|
+
<Text id="uizoom-label" text="Weekly digest" at={[56, 56]} size={48} weight={700} fill="$brand.text" font="$type.display" maxWidth={600} align="left" />
|
|
76
|
+
<RoundRect id="uizoom-target" at={[56, 180]} width={460} height={120} fill="$brand.bg" radius={20}
|
|
77
|
+
stroke={{ width: 6, color: "$brand.accent" }} />
|
|
78
|
+
</Card>
|
|
79
|
+
<SafeArea id="uizoom-safe" preset="reels">
|
|
80
|
+
<Text id="uizoom-caption" at="bottom" text="One click, every Monday." size={56} weight={600} fill="$brand.text" font="$type.body" maxWidth="90%"
|
|
81
|
+
enter={{ from: "s", distance: 40, ease: "expo.out", duration: 0.5, delay: 1.1 }} />
|
|
82
|
+
</SafeArea>
|
|
83
|
+
</Scene>
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
The card zooms around `anchor` (a point in the card, in %), so put the target region there. After the zoom, check the target text is still above the minimum size (`config/thresholds.md`).
|
|
87
|
+
|
|
88
|
+
## 5 FeatureCallout
|
|
89
|
+
|
|
90
|
+
```tsx
|
|
91
|
+
<Scene id="feature-1" start="13.3s" duration="3s" transition={{ type: "slide", direction: "e", duration: 0.4 }}>
|
|
92
|
+
<Rect id="feature-1-bg" width={1080} height={1920} fill="$brand.bg" />
|
|
93
|
+
<SafeArea id="feature-1-safe" preset="reels">
|
|
94
|
+
<Card id="feature-1-card" at={[0, 520]} width={920} height={420} fill="$brand.surface" padding={48}
|
|
95
|
+
enter={{ from: "s", distance: 100, ease: "expo.out", duration: 0.55 }}>
|
|
96
|
+
<Text id="feature-1-title" text="Deadline alerts" size={68} weight={700} fill="$brand.text" align="left" maxWidth="100%" font="$type.display" />
|
|
97
|
+
<Text id="feature-1-detail" text="Warned early, not late." size={44} fill="$brand.textMuted" align="left" maxWidth="100%" font="$type.body" />
|
|
98
|
+
</Card>
|
|
99
|
+
<Cursor id="feature-1-cursor" at={[0, 0]} size={64} position={[[0.2, [820, 1300]], [1.2, [700, 760], "power3.inOut"]]} clicks={["1.3s"]} />
|
|
100
|
+
</SafeArea>
|
|
101
|
+
</Scene>
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
One feature per callout. The `click` sound belongs on the ripple frame (1.3 s here).
|
|
105
|
+
|
|
106
|
+
## 6 StatCounter
|
|
107
|
+
|
|
108
|
+
```tsx
|
|
109
|
+
<Scene id="stat" start="16.3s" duration="2.4s" scale={[[0, 1.04], [2.4, 1]]}>
|
|
110
|
+
<Rect id="stat-bg" width={1080} height={1920} fill="$brand.bg" />
|
|
111
|
+
<SafeArea id="stat-safe" preset="reels">
|
|
112
|
+
<Counter id="stat-number" at={[0, 560]} from={0} to={5} suffix=" h" over={1.2} delay="0.2s" size={300} weight={800}
|
|
113
|
+
align="left" maxWidth="100%" fill="$brand.accent" font="$type.numerals">
|
|
114
|
+
<Thud id="stat-thud" amp={20} start="1.4s" />
|
|
115
|
+
</Counter>
|
|
116
|
+
<Text id="stat-label" at={[0, 980]} text="saved per team, every week" size={52} fill="$brand.text" align="left" maxWidth="100%" font="$type.body"
|
|
117
|
+
enter={{ from: "s", distance: 40, ease: "expo.out", duration: 0.5, delay: 1.3 }} />
|
|
118
|
+
</SafeArea>
|
|
119
|
+
</Scene>
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
`value` must come with a **source** (and permission for a customer figure). Put the source in the storyboard and the license/credits notes; if the person has none, do not use this block.
|
|
123
|
+
|
|
124
|
+
## 7 ChartReveal
|
|
125
|
+
|
|
126
|
+
```tsx
|
|
127
|
+
<Scene id="chart" start="18.7s" duration="3.2s">
|
|
128
|
+
<Rect id="chart-bg" width={1080} height={1920} fill="$brand.bg" />
|
|
129
|
+
<SafeArea id="chart-safe" preset="reels">
|
|
130
|
+
<Text id="chart-title" at={[0, 300]} text="Projects on time" size={64} weight={700} fill="$brand.text" align="left" maxWidth="100%" font="$type.display" />
|
|
131
|
+
<Card id="chart-card" at={[0, 520]} width={920} height={760} fill="$brand.surface" padding={48}>
|
|
132
|
+
<Chart id="chart-bars" type="bar" width={824} height={620} progress={[[0.4, 0], [1.8, 1, "expo.out"]]}
|
|
133
|
+
data={[{ label: "Q1", value: 42, color: "$brand.textMuted" }, { label: "Q2", value: 55, color: "$brand.textMuted" }, { label: "Q3", value: 71, color: "$brand.accent" }, { label: "Q4", value: 64, color: "$brand.textMuted" }]}
|
|
134
|
+
labelColor="$brand.textMuted" />
|
|
135
|
+
</Card>
|
|
136
|
+
</SafeArea>
|
|
137
|
+
</Scene>
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Data must come from the person. At most 6 bars. Give every bar a `color` from the look (muted for the rest, the accent for the one you highlight), or the chart falls back to its own default colours. Pulse it once with a `Pulse` effect on a ring or label if needed.
|
|
141
|
+
|
|
142
|
+
## 8 Quote
|
|
143
|
+
|
|
144
|
+
```tsx
|
|
145
|
+
<Scene id="quote" start="21.9s" duration="3.6s">
|
|
146
|
+
<Rect id="quote-bg" width={1080} height={1920} fill="$brand.surface" />
|
|
147
|
+
<SafeArea id="quote-safe" preset="reels">
|
|
148
|
+
<Text id="quote-text" at={[0, 480]} text="It finally feels calm." size={96} weight={700} fill="$brand.text" align="left" maxWidth="100%" font="$type.display"
|
|
149
|
+
enter={{ from: "s", distance: 60, ease: "expo.out", duration: 0.6 }} />
|
|
150
|
+
<Stack id="quote-who" position={[0, 1080]} direction="column" gap={6}>
|
|
151
|
+
<Text id="quote-name" text="Name Surname" size={44} weight={600} fill="$brand.text" align="left" maxWidth="100%" font="$type.body" enter={{ from: "s", distance: 24, duration: 0.45, delay: 0.9 }} />
|
|
152
|
+
<Text id="quote-role" text="Role, Company" size={40} fill="$brand.textMuted" align="left" maxWidth="100%" font="$type.body" enter={{ from: "s", distance: 24, duration: 0.45, delay: 1.0 }} />
|
|
153
|
+
</Stack>
|
|
154
|
+
</SafeArea>
|
|
155
|
+
</Scene>
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Only with a real quote, name, role, company and permission from the person. The sample above is a placeholder: replace it or leave a visible `TBD`.
|
|
159
|
+
|
|
160
|
+
## 9 LogoStrip
|
|
161
|
+
|
|
162
|
+
```tsx
|
|
163
|
+
<Scene id="logos" start="25.5s" duration="2.4s">
|
|
164
|
+
<Rect id="logos-bg" width={1080} height={1920} fill="$brand.bg" />
|
|
165
|
+
<SafeArea id="logos-safe" preset="reels">
|
|
166
|
+
<Text id="logos-caption" at={[0, 520]} text="Trusted by teams at" size={48} fill="$brand.textMuted" align="left" maxWidth="100%" font="$type.body" />
|
|
167
|
+
<Stack id="logos-row" position={[0, 700]} direction="column" gap={48}>
|
|
168
|
+
<Image id="logo-1" src="./assets/logo-1.png" height={110} fit="contain" enter={{ from: "s", distance: 30, duration: 0.4 }} />
|
|
169
|
+
<Image id="logo-2" src="./assets/logo-2.png" height={110} fit="contain" enter={{ from: "s", distance: 30, duration: 0.4, delay: 0.12 }} />
|
|
170
|
+
<Image id="logo-3" src="./assets/logo-3.png" height={110} fit="contain" enter={{ from: "s", distance: 30, duration: 0.4, delay: 0.24 }} />
|
|
171
|
+
</Stack>
|
|
172
|
+
</SafeArea>
|
|
173
|
+
</Scene>
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Logos are the person's own or licensed files in `assets/`. Never draw another company's logo.
|
|
177
|
+
|
|
178
|
+
## 10 BeforeAfter
|
|
179
|
+
|
|
180
|
+
```tsx
|
|
181
|
+
<Scene id="before-after" start="27.9s" duration="3.4s">
|
|
182
|
+
<Rect id="ba-bg" width={1080} height={1920} fill="$brand.bg" />
|
|
183
|
+
<SafeArea id="ba-safe" preset="reels">
|
|
184
|
+
<Card id="ba-before" at={[0, 380]} width={760} height={190} fill="$brand.surface" opacity={[[1.4, 1], [2, 0.4]]} scale={[[1.4, 1], [2, 0.88, "power3.inOut"]]}>
|
|
185
|
+
<Text id="ba-before-t" text="Before: five tools" size={52} fill="$brand.textMuted" align="left" maxWidth="100%" font="$type.body" />
|
|
186
|
+
</Card>
|
|
187
|
+
<Card id="ba-after" at={[0, 700]} width={920} height={300} fill="$brand.surface" stroke={{ width: 6, color: "$brand.accent" }}
|
|
188
|
+
enter={{ from: "s", distance: 120, ease: "expo.out", duration: 0.6, delay: 1.5 }}>
|
|
189
|
+
<Text id="ba-after-t" text="After: one board" size={68} weight={700} fill="$brand.text" align="left" maxWidth="100%" font="$type.display" />
|
|
190
|
+
</Card>
|
|
191
|
+
</SafeArea>
|
|
192
|
+
</Scene>
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## 11 Offer
|
|
196
|
+
|
|
197
|
+
```tsx
|
|
198
|
+
<Scene id="offer" start="31.3s" duration="3.4s">
|
|
199
|
+
<Rect id="offer-bg" width={1080} height={1920} fill="$brand.bg" />
|
|
200
|
+
<SafeArea id="offer-safe" preset="reels">
|
|
201
|
+
<Stagger id="offer-plans" each={0.15}>
|
|
202
|
+
<Card id="plan-1" at={[0, 380]} width={920} height={230} fill="$brand.surface" enter={{ from: "s", distance: 90, ease: "expo.out", duration: 0.5 }}>
|
|
203
|
+
<Text id="plan-1-name" text="Starter" size={56} weight={700} fill="$brand.text" align="left" maxWidth="100%" font="$type.display" />
|
|
204
|
+
<Text id="plan-1-price" text="PRICE" size={56} fill="$brand.textMuted" align="left" maxWidth="100%" font="$type.body" />
|
|
205
|
+
</Card>
|
|
206
|
+
<Card id="plan-2" at={[0, 700]} width={920} height={300} fill="$brand.surface" stroke={{ width: 6, color: "$brand.accent" }} enter={{ from: "s", distance: 90, ease: "expo.out", duration: 0.5 }}>
|
|
207
|
+
<Text id="plan-2-name" text="Team" size={68} weight={700} fill="$brand.text" align="left" maxWidth="100%" font="$type.display" />
|
|
208
|
+
<Text id="plan-2-price" text="PRICE" size={56} fill="$brand.accent" align="left" maxWidth="100%" font="$type.body" />
|
|
209
|
+
</Card>
|
|
210
|
+
</Stagger>
|
|
211
|
+
</SafeArea>
|
|
212
|
+
</Scene>
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
Prices come only from the person. `PRICE` is a visible placeholder: replace it or leave it so the draft shows what is missing.
|
|
216
|
+
|
|
217
|
+
## 12 CtaCard
|
|
218
|
+
|
|
219
|
+
```tsx
|
|
220
|
+
<Scene id="cta" start="34.7s" duration="3s" transition={{ type: "dissolve", duration: 0.4 }} scale={[[0, 1], [3, 1.04]]}>
|
|
221
|
+
<Rect id="cta-bg" width={1080} height={1920} fill="$brand.bg" />
|
|
222
|
+
<SafeArea id="cta-safe" preset="reels">
|
|
223
|
+
<Title id="cta-line" text="Try Pulse free." at={[0, 620]} align="left" size={128} maxWidth="100%" fill="$brand.text" font="$type.display"
|
|
224
|
+
enter={{ from: "s", distance: 120, ease: "expo.out", duration: 0.55 }}>
|
|
225
|
+
<PunchZoom id="cta-punch" start="0.5s" amount={0.04} />
|
|
226
|
+
</Title>
|
|
227
|
+
<Chip id="cta-url" at={[0, 1060]} label="pulse.example" dot="$brand.accent" fill="$brand.surface" textColor="$brand.text" size={56}
|
|
228
|
+
enter={{ from: "s", distance: 40, ease: "expo.out", duration: 0.5, delay: 0.9 }}>
|
|
229
|
+
<Pulse id="cta-pulse" start="1.5s" count={3} amp={0.05} freq={2} sound="" />
|
|
230
|
+
</Chip>
|
|
231
|
+
</SafeArea>
|
|
232
|
+
</Scene>
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
The URL must be readable (56 px or more) and the last state must hold at least one second.
|
|
236
|
+
|
|
237
|
+
## 13 Bridge (the hero carried between scenes)
|
|
238
|
+
|
|
239
|
+
The hero lives on its own track above the scenes so it is never cut by a scene change. Add the track once, then move the hero with keyframes across the beats:
|
|
240
|
+
|
|
241
|
+
```tsx
|
|
242
|
+
<Track id="hero-track" kind="video">
|
|
243
|
+
<Circle id="hero" start="0s" duration="37.7s" width={84} fill="$brand.accent" at={[900, 300]}
|
|
244
|
+
position={[[0, [0, 0]], [2.6, [0, 24], "power3.inOut"], [7.1, [0, 0], "power3.inOut"], [13.3, [0, 24], "power3.inOut"], [34.7, [0, 0], "power3.inOut"]]}
|
|
245
|
+
scale={[[0, 1], [2.6, 0.8, "power3.inOut"], [7.1, 1.7, "back.out(1.2)"], [13.3, 1, "power3.inOut"], [34.7, 2.2, "expo.out"]]} />
|
|
246
|
+
</Track>
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
The hero sits in the top-right margin, above the content, so it never covers text; it grows at the turn and again on the CTA. Rules: the hero keeps its shape logic, transitions are 0.3 to 0.7 s, and the `Marker` named `turn` sits where the look changes. A hero that changes style mid-video is a defect.
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# Scene blocks
|
|
2
|
+
|
|
3
|
+
A block is a parameterized scene with look-driven defaults. Fill parameters; do not hand-write scene code unless no block fits. Durations are ranges (tune in `config/thresholds.md` and from ratings). Every block lists: purpose, params, default timing, motion, sound tags, failure modes (feed `checks.md`).
|
|
4
|
+
|
|
5
|
+
**Recipes:** `block-recipes.md` has real TSX for the 13 core blocks (A), built from the engine's components and tested. The pattern blocks (B) are descriptions: build them as custom scenes from the same components and record why (block rule 5).
|
|
6
|
+
|
|
7
|
+
Param notation: `text:string(<=6 words)`, `hero?` optional, `*` required.
|
|
8
|
+
|
|
9
|
+
## A. Core blocks (13)
|
|
10
|
+
|
|
11
|
+
### 1 Hook
|
|
12
|
+
- **Purpose**: stop the scroll. Kinetic headline or bold claim; motion from frame 0.
|
|
13
|
+
- **Params**: `text*:string(<=6w)`, `emphasis?:word`, `direction?:8-way`, `bg?:token`, `hit?:sound-tag`.
|
|
14
|
+
- **Timing**: 1.5-3.0 s. **Motion**: words enter staggered, one emphasis word in contrast color.
|
|
15
|
+
- **Sound**: `whoosh-short` + `thud-light` on the emphasis word peak.
|
|
16
|
+
- **Fails**: static first frame; text > 6 words; emphasis not readable.
|
|
17
|
+
|
|
18
|
+
### 2 Problem
|
|
19
|
+
- **Purpose**: 2-4 pain statements in rhythm, or a pain image.
|
|
20
|
+
- **Params**: `lines*:string[2..4](<=5w each)`, `style:'list'|'image'|'tangle'`, `image?:asset`.
|
|
21
|
+
- **Timing**: 2.5-6 s. **Motion**: lines appear on beat, previous lines dim to 40%.
|
|
22
|
+
- **Sound**: tension texture `low-drone`; tick per line.
|
|
23
|
+
- **Fails**: every line same animation (vary entry), more than 4 lines.
|
|
24
|
+
|
|
25
|
+
### 3 ProductReveal
|
|
26
|
+
- **Purpose**: device or object appears and UI settles.
|
|
27
|
+
- **Params**: `device:'laptop'|'phone'|'none'`, `ui*:asset|skeleton`, `tilt?:deg`, `caption?:string(<=6w)`.
|
|
28
|
+
- **Timing**: 2.5-4 s. **Motion**: slide + settle with overshoot <= 8%; perspective tilt.
|
|
29
|
+
- **Sound**: `air-whoosh` + soft `click` on settle.
|
|
30
|
+
- **Fails**: tiny UI text; UI cropped by safe area.
|
|
31
|
+
|
|
32
|
+
### 4 UIZoom
|
|
33
|
+
- **Purpose**: camera moves to a specific UI region while a caption names it.
|
|
34
|
+
- **Params**: `ui*:asset`, `region*:{x,y,w,h}`, `caption*:string(<=6w)`, `blurOthers?:boolean`.
|
|
35
|
+
- **Timing**: 2-4 s. **Motion**: ease-in-out zoom, highlight ring on target.
|
|
36
|
+
- **Fails**: region text below min size after zoom; zoom overshoot.
|
|
37
|
+
|
|
38
|
+
### 5 FeatureCallout
|
|
39
|
+
- **Purpose**: one feature with card/label, animated cursor, highlight.
|
|
40
|
+
- **Params**: `title*:string(<=4w)`, `detail?:string(<=8w)`, `target?:region`, `cursor?:boolean`, `ripple?:boolean`.
|
|
41
|
+
- **Timing**: 2-3.5 s. **Motion**: cursor travels with ease, click ripple, card slides in.
|
|
42
|
+
- **Sound**: `click` on the ripple peak.
|
|
43
|
+
- **Fails**: more than one feature per callout.
|
|
44
|
+
|
|
45
|
+
### 6 StatCounter
|
|
46
|
+
- **Purpose**: one big number counting up with a short label.
|
|
47
|
+
- **Params**: `value*:number`, `format?:'int'|'pct'|'currency'`, `label*:string(<=5w)`, `source*:string` (required when value is a claim).
|
|
48
|
+
- **Timing**: 1.5-3 s. **Motion**: count with ease-out, settle with a soft thud.
|
|
49
|
+
- **Fails**: value without source; label wraps to 3 lines.
|
|
50
|
+
|
|
51
|
+
### 7 ChartReveal
|
|
52
|
+
- **Purpose**: bar/line/donut building in with emphasis on one value.
|
|
53
|
+
- **Params**: `type*`, `data*`, `highlight*:index`, `title*:string(<=5w)`.
|
|
54
|
+
- **Timing**: 2.5-4 s. **Motion**: bars grow staggered, highlight pulses once.
|
|
55
|
+
- **Fails**: more than 6 bars; unlabeled axes; fake data (must come from the person).
|
|
56
|
+
|
|
57
|
+
### 8 Quote
|
|
58
|
+
- **Purpose**: customer or team quote.
|
|
59
|
+
- **Params**: `quote*:string(<=14w)`, `name*`, `role*`, `company*`, `avatar?:generated-shape`, `permission*:boolean`.
|
|
60
|
+
- **Timing**: 3-4.5 s. **Motion**: key words in contrast color; attribution lockup slides in small.
|
|
61
|
+
- **Fails**: no permission flag; avatar of a real person without license.
|
|
62
|
+
|
|
63
|
+
### 9 LogoStrip
|
|
64
|
+
- **Purpose**: social proof row.
|
|
65
|
+
- **Params**: `logos*:asset[3..6]` (licensed or the person's own), `caption?:string(<=5w)`.
|
|
66
|
+
- **Timing**: 2-3 s. **Fails**: unlicensed third-party logos; uneven optical sizes.
|
|
67
|
+
|
|
68
|
+
### 10 BeforeAfter
|
|
69
|
+
- **Purpose**: split or swap comparison.
|
|
70
|
+
- **Params**: `before*`, `after*`, `mode:'split'|'swap'|'wipe'`, `labels?`.
|
|
71
|
+
- **Timing**: 3-4 s. **Motion**: old way muted and small, new way saturated and large.
|
|
72
|
+
|
|
73
|
+
### 11 Offer
|
|
74
|
+
- **Purpose**: pricing/plan cards with one emphasized.
|
|
75
|
+
- **Params**: `plans*:{name,price,bullets<=3}[2..3]`, `highlight*:index`.
|
|
76
|
+
- **Timing**: 3-4 s. **Fails**: prices not supplied by the person.
|
|
77
|
+
|
|
78
|
+
### 12 CtaCard
|
|
79
|
+
- **Purpose**: close. Brand, one line, URL or button.
|
|
80
|
+
- **Params**: `logo*:asset`, `line*:string(<=6w)`, `url|button*`.
|
|
81
|
+
- **Timing**: 2-3.5 s. **Motion**: logo assembly or morph, then URL fade-up and hold 1 s.
|
|
82
|
+
- **Fails**: URL too small; hold shorter than 1 s.
|
|
83
|
+
|
|
84
|
+
### 13 Bridge
|
|
85
|
+
- **Purpose**: carries the hero element between blocks.
|
|
86
|
+
- **Params**: `hero*:elementId`, `from*`, `to*`, `kind:'morph'|'wipe'|'line'|'zoom'`.
|
|
87
|
+
- **Timing**: 0.3-0.7 s. **Fails**: hero changes shape arbitrarily; transition > 0.7 s.
|
|
88
|
+
|
|
89
|
+
## B. Pattern blocks (13, from the catalogue)
|
|
90
|
+
|
|
91
|
+
| Block | Pattern | Params (key) | Timing |
|
|
92
|
+
|---|---|---|---|
|
|
93
|
+
| `TypedQuery` | curiosity hook, typed search or prompt | `text*(<=10w)`, `caret:true`, `bg` | 2-3 s |
|
|
94
|
+
| `QuoteCard` | color-block quote with white key words | `quote`, `keyWords[]`, `attribution` | 3-4 s |
|
|
95
|
+
| `LogoSwap` | logo replaces a word inside a sentence | `before`, `logo`, `after`, `footage?` | 1.2-2 s each |
|
|
96
|
+
| `SpotlightWord` | magnifier/selection box reveals a word | `phrase`, `target`, `gesture:'magnifier'|'select'` | 2-3 s |
|
|
97
|
+
| `CardGrid` / `CardCollage` | modular product cards, tilt or flat | `cards[4..9]`, `tilt`, `blur` | 2-4 s |
|
|
98
|
+
| `OrbitCards` | cards orbit a central line of text | `cards[6..10]`, `text` | 2.5-3.5 s |
|
|
99
|
+
| `MotifLine` | continuous line character | `path seed`, `headStyle`, `streaks` | spans scenes |
|
|
100
|
+
| `StrikeText` | word destroyed by scanline glitch | `word`, `style:'scan'|'slash'` | 1-1.5 s |
|
|
101
|
+
| `TextSwap` | second line swapped for variants | `lead`, `variants[2..3]` | 2-3 s total |
|
|
102
|
+
| `KaraokeLine` | sentence with current word bright | `sentence`, `wordTimes?` | 3-4 s |
|
|
103
|
+
| `ShapePivot` / `ShapeMorph` | circles close in; blob morphs to logo | `shapes`, `to:'wordmark'` | 1.5-2.5 s |
|
|
104
|
+
| `GlassUI` | frosted UI over footage | `ui`, `footage`, `blur` | 3-5 s |
|
|
105
|
+
| `WipeMatchCut` | split-wipe on shared geometry | `left`, `right`, `anchor:'center'` | 0.6-1 s |
|
|
106
|
+
|
|
107
|
+
Also: `OldWay` (muted small windows), `ClaimWith3D`, `GestureCallout` (bubble + ripple), `Burst` (radial dashes), `WordBeat` (single big word), `UrlCard`, `OverwhelmDiagram`, `PainFlavor`, `QuestionBubbles`, `VersionBadge`, `CellFill`, `ChatDemo`, `FootageToUI`, `CursorTag`, `ProofMosaic`, `ProofCredential`.
|
|
108
|
+
|
|
109
|
+
## Block rules
|
|
110
|
+
1. One idea per block. If a block needs two ideas, split it.
|
|
111
|
+
2. Default durations are ranges; shorten to the voice, not the other way round.
|
|
112
|
+
3. Every block declares `hero?` so `hero-continuity` can track it.
|
|
113
|
+
4. Every block exposes `soundHints` (tags and peak frame) so impacts land on frames.
|
|
114
|
+
5. If a block is missing, create a **custom scene** and record: reason, parameters, and a proposal for a new block in `CHANGELOG.md`.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Reference catalogue (pattern source)
|
|
2
|
+
|
|
3
|
+
Source: ~60 still frames the user collected from one compilation video of "best SaaS promo videos". Frames only: **no timing, no sound, no performance data**. Rankings in the overlay are the compilation's, not a quality measure (the compilation has low view counts).
|
|
4
|
+
|
|
5
|
+
Use this file to learn **patterns** (structure, pacing logic, hierarchy). Never reproduce a brand's assets, footage, copy or exact layouts. Brand names appear here only as citations of where a pattern was observed.
|
|
6
|
+
|
|
7
|
+
Confidence: `H` clearly visible, `M` inferred from stills, `L` guess.
|
|
8
|
+
|
|
9
|
+
## Ad A: Ansarada (data rooms) — Metaphor contrast
|
|
10
|
+
Beats: (1) grainy B/W roulette wheel, red tint [pain as image] -> (2) vertical split-wipe match-cut: wheel centre to vault-door centre, dark to white [turn] -> (3) black text on white "Hello simple and secure data rooms" [promise] -> (4) white 3D vault, door opening onto a glowing lime disc [reveal] -> (5) tilted laptop with skeleton UI (grey bars + colored pills) [product] -> (6) same UI on phone [multi-device] -> (7) black logo on white [brand] -> (8) "First data room to be ISO 27001:2022 certified" [credential proof].
|
|
11
|
+
Design: dark gritty vs white clean; single lime accent; big left-aligned neo-grotesque; 3D render for the vault. Confidence H on structure, M on timing.
|
|
12
|
+
Lessons: pain can be an image, not words; a match-cut on shared geometry makes the turn feel designed; skeleton UI hides complexity; proof is a concrete credential.
|
|
13
|
+
|
|
14
|
+
## Ad B: Slack — Typographic color blocks
|
|
15
|
+
Beats: logo + parent lockup -> typed search query in a purple search field ("Is Slack changing the way businesses do business?") [curiosity hook] -> customer-name swap "Just ask [logo] Roku" over blurred footage -> customer quote cards on solid colors (pink, green) with key words in white, attribution lockup (logo, name, title) -> "And [logo] Canva", "And [logo] Signable" roll call over b-roll -> magnifier sweeping a word to reveal white letters on blue -> colorful UI-card collage tight crop -> logo assembled from large colored shapes.
|
|
16
|
+
Design: one flat color per scene, giant geometric sans, cropped letters at frame edges, palette taken from the logo colors; UI gestures (cursor, selection box, magnifier) applied to type.
|
|
17
|
+
Lessons: proof from other people's voices; one color world per scene; key word in white, lead-in in dark; logo-in-sentence swap; brand assembled from motion shapes.
|
|
18
|
+
|
|
19
|
+
## Ad C: ClickUp — Pain-to-calm line story
|
|
20
|
+
Beats: white screen "Work Sprawl is killing us." over unreadable diagram of boxes and arrows -> thick black line with round head sweeping with speed streaks -> "Too many apps…" icons tangled on the line with red badges -> dark wedge wipe to white -> dark starry "Too many AI tools…" line looping through logos -> "Context is lost." with question bubbles along the line -> thesis "Work sprawl kills human productivity" (second line swapped to "AI productivity"), line's dot belongs to a letter -> "It's time to kill sprawl." on pale gradient, line ends as the period -> "kill Sprawl" struck with scanline glitch -> saturated magenta/orange/purple gradient with grain, product cards flying with heavy motion blur -> grid of mini-app cards -> "A New Era of AI" (soft star blob) -> "A New Era of Craft & Quality" (cards orbiting text) -> logo + version badge, "AI Workspace" with motion blur -> tilted isometric card wall.
|
|
21
|
+
Design: pain half is monochrome and thin; solution half is saturated, grained, 3D tilt and blur.
|
|
22
|
+
Lessons: a **single continuous motif** (the line) ties scenes; pain takes about half the runtime in several distinct flavors; palette flip marks the turn; template reuse with word swap; text strike for "kill".
|
|
23
|
+
|
|
24
|
+
## Ad D: Vaylens (EV charging software) — Trend-to-product explainer
|
|
25
|
+
Beats: flat blue "As the need to use electric cars becomes the norm" (current word bright, others dim) -> pale screen, photo framed in the middle, text flanking left and right ("the way we [photo] recharge them") -> two blue circles close in, "we built" -> white blob pinches and morphs into the wordmark -> gradient "It's a charging management software" -> frosted-glass dashboard over blurred top-down footage of a team -> mosaic of industry footage with "helping thousands of companies".
|
|
26
|
+
Design: one electric blue + pale lavender; light-weight type, generous space; circles/blob as transitions; footage in clean framed rectangles.
|
|
27
|
+
Lessons: open from an agreeable market fact; split one sentence across scenes; shapes as punctuation; category line in plain words; glass UI placed over real-world context; breadth proof as a mosaic.
|
|
28
|
+
|
|
29
|
+
## Ad E: Monday.com — Real-UI demo
|
|
30
|
+
Beats: person talking to camera in office -> logo on a frosted glass pill -> real board UI with colored status/priority cells -> dashboard widgets (progress bar, revenue vs goal, trend line) -> AI sidekick chat ("Please collect all live Q4 projects into one board" / "Sure, I'm on it!") -> board builds itself: empty cells become filled colored cells via circle-to-cell morphs, avatars slide in.
|
|
31
|
+
Design: close crops of real UI; saturated status color blocks; pastel gradient; glass.
|
|
32
|
+
Lessons: demo-led; plain-language AI request/response; shape morph as progress; color-coded cells make the product recognizable.
|
|
33
|
+
|
|
34
|
+
## Ad F: Canva (video) — Human story to prompt
|
|
35
|
+
Beats: woman chopping vegetables with a phone in her mouth filming herself, script logotype overlaid [funny, relatable pain] -> wide shot with ring light -> overhead hands with purple selection frame, green cursor, name tag "Masha" [multiplayer cue] -> white prompt box "What's the topic of your video?" typing, four thumbnails of the footage just seen, option chips ("Playful", "Montage") -> teal-to-violet gradient, "[logo] Video" in a white pill.
|
|
36
|
+
Lessons: story first, product as a simple prompt; footage becomes the UI input; cursor name tag as character.
|
|
37
|
+
|
|
38
|
+
## Ad G: Phantom (wallet) — 3D object explainer (frames labelled "#8 Google Gemini" in the overlay; the label and brand disagree, treat as Phantom)
|
|
39
|
+
Beats: three plain grey desktop windows (connect, collections, gas fee) [the old, complicated way] -> violet screen "a home for your NFTs" over a black circle with a glassy 3D NFT frame and props -> phone UI long-press menu with "Pin it!" bubble and touch ripple -> black "one wallet" with 3D bits orbiting -> 3D purple wallet with app cards -> "drag" with floating coins and large blobs -> "a wallet you'll love" with radial black dashes -> black "phantom.app".
|
|
40
|
+
Design: electric violet alternating with pure black; big lowercase geometric sans in white; glossy 3D props; burst graphic for impact.
|
|
41
|
+
Lessons: old way small and grey, new way big and colored; 2 to 4 word lines; gesture callouts; URL end card.
|
|
42
|
+
|
|
43
|
+
## Ad H: Google Gemini (stub)
|
|
44
|
+
Only one frame: bold white "powerful tools" over a dark mascot silhouette on violet. Insufficient to annotate. Ask the user for more frames.
|
|
45
|
+
|
|
46
|
+
## Unidentified #7
|
|
47
|
+
Live-action boardroom, man with white hair and glasses mid-shout, blurred foreground head, cool grey grade. Brand unknown. Annotate when identified.
|
|
48
|
+
|
|
49
|
+
## Not an ad
|
|
50
|
+
A closing card of the compilation channel: glossy chrome/glass text on a blue grid. Type-style inspiration only.
|
|
51
|
+
|
|
52
|
+
## Cross-ad observations (how the 21 patterns were derived)
|
|
53
|
+
See `patterns.md`. A pattern is only **strong** when seen in 3 or more of A-G and later confirmed by user ratings.
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# Automatic checks (23 + 5 pattern checks)
|
|
2
|
+
|
|
3
|
+
## Who computes each check (read this first)
|
|
4
|
+
|
|
5
|
+
The `review` tool computes 17 checks of its own (numbered 1 to 17 in its output). The skill's check ids below map to them or to a procedure you run. **Say which ones you ran.** A check nobody ran is "not run", not "pass".
|
|
6
|
+
|
|
7
|
+
| Skill check | Computed by | Notes |
|
|
8
|
+
|---|---|---|
|
|
9
|
+
| `motion-frame0` | `review` #1 | Something changing within 0.1 s (stricter than `first_motion_max_s`) |
|
|
10
|
+
| `no-dead-frames` | `review` #4 | From the compiled animation; it does not know your `hold` ranges, so report deliberate holds in `LOG.md` |
|
|
11
|
+
| `novelty-rate` | `review` #3 | |
|
|
12
|
+
| `text-overlap`, `text-clipped` | `review` #7 | Measured in a rendered page at about nine times; skipped with `deep:false` (then read the frames) |
|
|
13
|
+
| `safe-area` | `review` #8 | Uses the `SafeArea` presets (reels, shorts, tiktok, feed). For other formats check `safe_area_px` yourself |
|
|
14
|
+
| `contrast` | `review` #9 | Floor 3:1 measured; verify 4.5:1 for body text from the look's palette yourself |
|
|
15
|
+
| `min-size` | **you** | Read sizes with `project_read` or the schema and compare with `text.min_px`; the engine only rejects text under about 28 px |
|
|
16
|
+
| `reading-speed`, `text-words` | **you** | Count words per beat from the props and the visible duration |
|
|
17
|
+
| `font-glyphs` | `lint` (rule `missing-glyph`), `font_check` | |
|
|
18
|
+
| `type-families` | **you** (`font_list`) | The engine warns above 3 families; the skill limit is 2 |
|
|
19
|
+
| `voice-sync` | `review` #5 (captions) + **you** | Report the `align_to_voice` output for word-tied elements |
|
|
20
|
+
| `sound-on-impact` | `review` #10 | |
|
|
21
|
+
| `mix-voice-on-top` | `review` #11 (heuristic) | Compares gains and the duck amount; it does not measure 12 dB. Say so |
|
|
22
|
+
| `loudness`, `clipping-audio` | `review` #12 | Integrated LUFS within 1.5 LU of -14 and true peak under -1 dBTP |
|
|
23
|
+
| `palette-adherence` | **not run** | No tool measures frame colours. Use only `$brand.*` tokens and say it was not measured |
|
|
24
|
+
| `look-consistency` | `review` #14 (partial) + **you** | Theme set and font count; you check easing and transition tokens |
|
|
25
|
+
| `hero-continuity` | `review` #2 | A layer spanning four scenes passes; otherwise it is "needs eyes" |
|
|
26
|
+
| `cta-present` | **you** | Last scene has a readable URL or button and holds at least 1 s |
|
|
27
|
+
| `assets-licensed`, `credits-present` | `review` #16, #17 | |
|
|
28
|
+
| `determinism` | **not run** by `review` | The server rejects impure TSX and lint rejects impure expressions. Byte-identical renders were verified on one machine only. Say so |
|
|
29
|
+
| `turn-present`, `product-after-turn`, `proof-sourced`, `logo-late` | **you** | Use the `turn` marker and the storyboard's `source` fields |
|
|
30
|
+
| `transition-count` | `review` #13 (partial) | |
|
|
31
|
+
|
|
32
|
+
Engine checks the skill did not list but you should report: `review` #6 (numbers to add up: you do the arithmetic) and #15 (caption line length and reading speed).
|
|
33
|
+
|
|
34
|
+
## The checks
|
|
35
|
+
|
|
36
|
+
Every check returns `{ id, severity: "hard"|"warn"|"info", pass, evidence, time?, bbox?, fix_hint }`. HARD failures block a draft from being shown as passing. All thresholds come from `config/thresholds.md`.
|
|
37
|
+
|
|
38
|
+
| Id | Check | How computed | Sev |
|
|
39
|
+
|---|---|---|---|
|
|
40
|
+
| `motion-frame0` | Something moves within `first_motion_max_s` | Frame difference between frame 0 and frame at the limit above threshold | HARD |
|
|
41
|
+
| `no-dead-frames` | No static stretch > `max_static_s` | Sliding-window frame diff; ignore ranges tagged `hold` in the project | HARD |
|
|
42
|
+
| `novelty-rate` | >= 1 new element/scene change per `novelty_window_s` | Timeline IR: element starts, scene changes, color/camera events | WARN |
|
|
43
|
+
| `text-overlap` | No overlapping text or key components | Bounding boxes of text and key layers per frame (headless Chrome) | HARD |
|
|
44
|
+
| `text-clipped` | No clipped text (unless `cropEdge`) | Text bounds vs container and frame | HARD |
|
|
45
|
+
| `safe-area` | Text inside format safe area | Text bounds vs `safe_area_px` | HARD |
|
|
46
|
+
| `contrast` | Text readable on its background | Sample pixels behind text, ratio >= 4.5 (3.0 large) | HARD |
|
|
47
|
+
| `min-size` | Text not below minimum | Computed font px vs `text.min_px` | WARN (headline: HARD) |
|
|
48
|
+
| `reading-speed` | Words/s within limits | On-screen text words / visible duration | WARN |
|
|
49
|
+
| `text-words` | Headline <= 6 words (8 hard) | Count words per beat | WARN/HARD at 9+ |
|
|
50
|
+
| `font-glyphs` | All characters exist in the font | Font parser coverage | HARD |
|
|
51
|
+
| `type-families` | <= 2 families | Distinct families used | WARN |
|
|
52
|
+
| `voice-sync` | Word-tied animations land on words | Compare key times to word timings, tolerance frames | HARD |
|
|
53
|
+
| `sound-on-impact` | Impact effects have a sound peak on their frame | Match effect times with placed sound peaks | WARN |
|
|
54
|
+
| `mix-voice-on-top` | Voice >= 12 dB over music during speech | Offline mix levels per window | HARD |
|
|
55
|
+
| `loudness` | Master near target LUFS | FFmpeg loudness measure | HARD |
|
|
56
|
+
| `clipping-audio` | No digital clipping; true peak <= -1 dBTP | Peak analysis | HARD |
|
|
57
|
+
| `palette-adherence` | Colors stay within the look palette | Dominant colors per frame vs palette (deltaE tolerance) | WARN |
|
|
58
|
+
| `look-consistency` | Fonts, easing, transitions match the look | Compare tokens used vs look | WARN |
|
|
59
|
+
| `hero-continuity` | A hero element carries across beats | Track designated hero element ids | WARN |
|
|
60
|
+
| `cta-present` | Last block is a CTA with readable URL/button, hold >= 1 s | Timeline IR | WARN |
|
|
61
|
+
| `assets-licensed` | All assets, sounds, fonts in the ledger | Ledger lookup | HARD |
|
|
62
|
+
| `credits-present` | Required attributions in license report | Ledger flags vs report | HARD |
|
|
63
|
+
| `determinism` | Same project renders the same frames | Render `sample_frames` twice, compare | HARD |
|
|
64
|
+
|
|
65
|
+
## Pattern checks (from `patterns.md`)
|
|
66
|
+
| Id | Check | Sev |
|
|
67
|
+
|---|---|---|
|
|
68
|
+
| `turn-present` | A declared `Turn` marker exists and the look/space changes there | WARN |
|
|
69
|
+
| `product-after-turn` | First product block comes after the turn (or story has no pain) | WARN |
|
|
70
|
+
| `proof-sourced` | Every number/credential/quote has a `source` and `permission` | HARD |
|
|
71
|
+
| `logo-late` | Brand logo appears after 30% of runtime (unless brand-first is intended) | INFO |
|
|
72
|
+
| `transition-count` | No more than 1 non-cut transition type per 3 scenes | INFO |
|
|
73
|
+
|
|
74
|
+
## Review output
|
|
75
|
+
|
|
76
|
+
`review` returns all results plus a contact sheet with failing frames outlined and a summary:
|
|
77
|
+
|
|
78
|
+
```
|
|
79
|
+
Checks: 27 run, 24 pass, 2 warn, 1 HARD fail
|
|
80
|
+
HARD: contrast @ 0:07.4 — text "Sources" on #8FA4FF = 2.9:1 (need 4.5). Fix: use `text` token on `bg`, or add scrim.
|
|
81
|
+
WARN: novelty-rate @ 0:12-0:15 — no new element for 3.1 s. Fix: add a card or camera move at 0:13.2.
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Reading the checks
|
|
85
|
+
- Fix HARD first, WARN second. If a WARN is intentional (a deliberate hold), mark it in the project with the reason; do not silence silently.
|
|
86
|
+
- If a check seems wrong, say so with the evidence; do not edit thresholds mid-run. Propose a change in `CHANGELOG.md`.
|
|
87
|
+
- A check passing never proves the video is good; it proves the floor is met.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Clean styles: what "good" looks like in short motion pieces
|
|
2
|
+
|
|
3
|
+
Notes from three short motion pieces the person collected (each a brand film made with Claude and shown on a laptop screen on a phone feed). They set the bar for flomotion's output. **What I could check:** frames sampled every 2 s, and loudness. **What I could not:** the spoken words and the taste of the music. Treat the sound notes as measurements, not opinions.
|
|
4
|
+
|
|
5
|
+
## The three pieces
|
|
6
|
+
|
|
7
|
+
### A. A money-tracking app: a story told by a character (43 s)
|
|
8
|
+
|
|
9
|
+
- One hero character: a glossy orange blob that is "your money". Each beat takes a bite out of it (fees, ads, refunds, shipping) with a different small creature or object; the number shrinks with every bite.
|
|
10
|
+
- Pale, warm background; the blob and a second accent are the only strong colours.
|
|
11
|
+
- Text is tiny and sparse: 1-4 words, set in negative space ("Fees ate", "Ads burned", "And shipping?"), with one serif-italic accent word ("today.", "Paused.", "More sleep.") against a clean sans.
|
|
12
|
+
- Problem as a visual joke (a clock, "14 TABS" flying in), solution as relief ("melted into one number"), then a calm end card ("Fewer tabs. More sleep.").
|
|
13
|
+
- Meta reveal at the end: "Oh, and this whole video?" over a code editor, then "Made with AI." The reveal that the film itself was built from code is the hook for the next watch.
|
|
14
|
+
|
|
15
|
+
### B. A language app: a glowing thread through the scenes (19 s)
|
|
16
|
+
|
|
17
|
+
- Near-black background, one brand colour (neon green), all light comes from it.
|
|
18
|
+
- A thin glowing line draws through the scenes and connects them (phone, streak flame, leaderboard, mascot call, language orbs); the line is the motion design.
|
|
19
|
+
- Product UI shown as clean cards with real-looking data, never a screen recording.
|
|
20
|
+
- Ends on one three-word idea ("One path.") and the logo; a soft fade, no loud sting.
|
|
21
|
+
|
|
22
|
+
### C. A website builder: the build is the story (48 s)
|
|
23
|
+
|
|
24
|
+
- A prompt box is typed into ("Describe your idea"), then the site builds itself on screen: wireframe, layout, product photos, headlines, publish, "Your store is live", a growth chart.
|
|
25
|
+
- Dark cobalt interface around a bright product page; the contrast tells you where to look.
|
|
26
|
+
- Every beat is a state change of the same screen, so it reads as one continuous take.
|
|
27
|
+
|
|
28
|
+
## What they share (the rules)
|
|
29
|
+
|
|
30
|
+
1. **One idea per shot, 1-4 words.** If a shot needs a sentence, split it.
|
|
31
|
+
2. **One hero object that carries the film** (blob, line, prompt box, dot). It changes shape and mood with the story and is never replaced. flomotion's version: the mascot dot with eyes (`flo`).
|
|
32
|
+
3. **Two or three colours, used flat.** One brand colour, one neutral, one accent. No gradients to make it feel rich.
|
|
33
|
+
4. **Type contrast:** a heavy sans for the message, one serif-italic or hand-drawn accent word for the feeling. Max two families (see `typography.md`).
|
|
34
|
+
5. **Negative space is the style.** Text sits in the empty part of the frame, never over busy detail.
|
|
35
|
+
6. **Show the work, not the screenshot.** The product appears as a built, animated UI or as the process of building (typing, assembling), not a static capture. When a real capture is the honest choice (flomotion's own live view), animate it: scroll it, zoom it, float a card out of it.
|
|
36
|
+
7. **A metaphor that moves, not a diagram.** Money eaten by bites; a thread connecting steps; a vault door opening onto the product. Pick one metaphor per film.
|
|
37
|
+
8. **A reveal at the end that reframes the film** ("this whole video is one file"). It pays off the hook and invites a second watch.
|
|
38
|
+
9. **A single, cheap call to action.** Comment a word, run one command. No form, no price table.
|
|
39
|
+
10. **Sound is dense and level.** All three sit at about -14 LUFS with very little loudness change (loudness range 2 LU, 2 LU and 8 LU); the music is a constant bed and the hits are small. Do not spike the mix for drama: use level, tempo and cuts.
|
|
40
|
+
|
|
41
|
+
## How to apply it in flomotion
|
|
42
|
+
|
|
43
|
+
- Look: `bold-kinetic` for light/colour-block pieces (A), `dark-product` for glow pieces (B, C). Keep to the look's tokens.
|
|
44
|
+
- Blocks: the hook is a claim or a typed prompt (`Hook`, `TypedQuery`); the middle is a character or a metaphor (`MetaphorImage`, `OldWay`, the transitions in `transitions.md`); the product is built on screen (`ProductReveal` + `UIZoom`).
|
|
45
|
+
- Build the hero as a `Card` of circles (body, eyes, pupils) with keyframes: squash on beats, blink every few seconds, look toward the thing that matters. See the mascot in `examples/flomotion-ad/project.tsx`.
|
|
46
|
+
- End with the reveal line and the one-command CTA.
|
|
47
|
+
|
|
48
|
+
## Checklist before a draft
|
|
49
|
+
|
|
50
|
+
- [ ] Each shot has 1-4 words (headline 6 max).
|
|
51
|
+
- [ ] One hero object appears in at least 4 beats and reacts to the story.
|
|
52
|
+
- [ ] Two or three flat colours per scene, from the look.
|
|
53
|
+
- [ ] One metaphor, used once, clearly.
|
|
54
|
+
- [ ] The last 3 seconds reframe or pay off the first 3.
|
|
55
|
+
- [ ] One CTA, one action.
|
|
56
|
+
- [ ] Mix near -14 LUFS, true peak under -1 dBTP, `mix_preview` run.
|