greenlight-motion 0.3.4 → 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.
@@ -4,7 +4,7 @@
4
4
  > here. `frontend/src/…` paths are files of the engine's public source repository. MotionFast templates (`mediaType: "motion"`)
5
5
  > stay in GreenLight Dash.
6
6
 
7
- # GreenLight Dash — Text animation presets: typing reveals, text on shapes, highlights, loops, decoders, voice captions
7
+ # GreenLight Dash — Text animation presets: typing reveals, text on shapes, highlights, loops, decoders, voice captions, classic reveals
8
8
 
9
9
  > Builds on [video-text.md](video-text.md#advanced-animations--effects-over-a-moving-range-of-characters-words-or-lines) (the `textAnimators` model). Read that for the fields; this file is the app's own preset catalogue as JSON to paste. Energetic in-and-out titles are in [video-text-kinetic.md](video-text-kinetic.md).
10
10
 
@@ -14,6 +14,7 @@
14
14
  - [Writing a recipe onto a layer](#writing-a-recipe-onto-a-layer)
15
15
  - [The catalogue](#the-catalogue)
16
16
  - Recipes: [Sweep Reveal](#sweep-reveal) · [Type on Shape](#type-on-shape) · [Emphasis](#emphasis) · [Loop](#loop) · [Characters](#characters) · [Voice](#voice)
17
+ - [Classic](#classic): 29 calm word and letter reveals, highlights, caption bars and randomizer looks
17
18
 
18
19
  ## Pick a category
19
20
 
@@ -29,6 +30,7 @@ animator built from scratch rarely looks as good.
29
30
  | keep moving for the whole layer with no keyframes (neon, rainbow, gradient, float, jitter, flicker) | [Loop](#loop) |
30
31
  | resolve out of random glyphs, binary or a rolling alphabet, or keep glitching | [Characters](#characters) |
31
32
  | follow speech in a caption (karaoke box, word pops, read-along) | [Voice](#voice) |
33
+ | come in calmly word by word or letter by letter, optionally with a highlight, a caption bar or a randomizer look (captions, lower thirds, a typewriter, a ransom note) | [Classic](#classic) |
32
34
 
33
35
  Recipes combine: a layer holds up to 8 animators, so an entrance plus an Emphasis or Loop recipe on the same text
34
36
  works. Append the second recipe with fresh ids (next section).
@@ -45,6 +47,7 @@ first:
45
47
  - Voice: [Spoken Box](#spoken-box), [Spoken Pop](#spoken-pop), [Spoken Glow](#spoken-glow),
46
48
  [Karaoke Wipe](#karaoke-wipe), [Word Pop](#word-pop), [Rise on Voice](#rise-on-voice),
47
49
  [Typed as Spoken](#typed-as-spoken).
50
+ - Classic: [Classic Fade Up](#classic-fade-up), [Classic Rise (Mask)](#classic-rise-mask) (calm lines and captions).
48
51
  - Kinetic: see [video-text-kinetic.md](video-text-kinetic.md#when-to-use-them).
49
52
 
50
53
  Type on Shape has no preferred preset: pick by the shape the text needs.
@@ -53,12 +56,15 @@ Type on Shape has no preferred preset: pick by the shape the text needs.
53
56
 
54
57
  - **Copy** the recipe's `textAnimators` into the text item, plus `textAnimatorPivotX` / `textAnimatorPivotY` when it
55
58
  has them, and merge its `propertyKeyframes` into the item's own (the item's other tracks stay).
56
- - To **replace** the layer's animation, delete the old `textAnimators` and every `txa:` track first, so no orphan
57
- keys are left behind.
59
+ - To **replace** the layer's animation, delete the old `textAnimators` and every `txa:` track first, and set both
60
+ pivots to the recipe's values (0 when it has none): recipes reuse ids like `a1`/`p1`, so a leftover track or pivot
61
+ would drive the new animator.
58
62
  - To **add** on top, give the new animators and effects `aid` / `pid` values the layer does not use yet, and rename
59
63
  the recipe's `txa:<aid>:…` / `<pid>.<field>` track keys to match.
60
- - **Lengths are for `textFontSize` 100.** Multiply every length field listed under [The catalogue](#the-catalogue)
61
- by `textFontSize / 100`. Angles, scales, opacities, percentages and timings stay as written.
64
+ - **Lengths are for a 100 px font.** Multiply every length field listed under [The catalogue](#the-catalogue)
65
+ by the layer's font size in px / 100: `textFontSize`, or in pct mode `textFontSizePct × videoSize.h / 100`. Angles, scales, opacities, percentages and timings stay as written.
66
+ [Classic](#classic) recipes are the exception: their lengths are px and stay as written, apart from the few each
67
+ recipe lists.
62
68
  - **Keyframe `position` is a % of the layer's length.** Every recipe is written for a **5 s layer**, and each key has
63
69
  its time in a comment. For a layer `L` seconds long:
64
70
  - `t s` → `position = t / L × 100`
@@ -67,7 +73,9 @@ Type on Shape has no preferred preset: pick by the shape the text needs.
67
73
  - If the recipe's longest key time is more than `L`, first divide every key time by `longest / L`, so the motion
68
74
  fits the layer. The app does the same.
69
75
 
70
- Recipes without `propertyKeyframes` (Loop, Voice and the Time-driven parts) fit any layer length as written.
76
+ Recipes without `propertyKeyframes` (Voice, the Time-driven parts, and every Loop except Gradient Flow, whose keys
77
+ are layer %) fit any layer length as written.
78
+ - **Strip the `//` time comments** from a recipe's blocks before you send it: they are notes, not JSON.
71
79
  - **Faster or slower** by a factor `k`: divide every key time and every `stagger`, `duration`, `delay`, `hold` and
72
80
  `fadeOut` by `k`; multiply `flow`, wiggle `rate` and scramble `rate` by `k`.
73
81
  - **Colours** in the effects are the recipe's look. The text keeps its own colour, font and size. Some recipes are
@@ -75,13 +83,13 @@ Type on Shape has no preferred preset: pick by the shape the text needs.
75
83
  paints the text itself, so it reads on its shape whatever the layer's colour is.
76
84
  - **Voice recipes need `textWordTimings`** on the item (captions made from a transcript have them). Without timings
77
85
  they run as Time animators, one word after another.
78
- - **To check it**, render a still in the middle of the motion and one after it settles (video-rendering.md).
86
+ - **To check it**, render a still in the middle of the motion and one after it settles (rendering.md).
79
87
 
80
88
  ## The catalogue
81
89
 
82
- <!-- PRESETS:BEGIN (generated by tests/text-animators/skill_presets.mjs from presets.js — edit there) -->
90
+ <!-- PRESETS:BEGIN (generated from the app's presets; do not edit by hand) -->
83
91
 
84
- 49 recipes in 6 categories. **Lengths** to multiply by `textFontSize / 100`: `blur` `amount`; `box` `padX`, `padY`, `radius`; `glow` `size`; `position` `x`, `y`; `rotate3d` `perspective`; `stroke` `width`.
92
+ 54 recipes in 7 categories; the app's 29 Classic recipes follow in [Classic](#classic). **Lengths** to multiply by the font size in px / 100: `blur` `amount`; `box` `padX`, `padY`, `radius`; `glow` `size`; `position` `x`, `y`, `z`; `rotate3d` `perspective`; `stroke` `width`; and the layer's `textAnimatorPerspective`.
85
93
 
86
94
  **A fade-out exit for an `in` recipe**, ending with the layer: append this animator with unused ids. Set its `basedOn` to the recipe's first animator's, and `stagger` to 0.015 for letters, 0.04 for words or 0.08 for lines.
87
95
 
@@ -91,6 +99,8 @@ Type on Shape has no preferred preset: pick by the shape the text needs.
91
99
  "effects": [{"pid": "p19", "type": "opacity", "opacity": 0}] }
92
100
  ```
93
101
 
102
+ It is quick by design (0.3 s per unit); raise `duration` when the exit has to be read.
103
+
94
104
  | Recipe | Category | What it does | Unit | Plays |
95
105
  |---|---|---|---|---|
96
106
  | [Glide Text](#glide-text) | Sweep Reveal | Letters type in as the word glides left under a chasing blue band and settles, then it slides off to the left | letters | in, the first 1.3 s, then out at the end |
@@ -107,6 +117,11 @@ Type on Shape has no preferred preset: pick by the shape the text needs.
107
117
  | [Counter Sweep](#counter-sweep) | Sweep Reveal | Letters type in left to right while a pink band runs back against them | letters | in, the first 2.7 s |
108
118
  | [Sunset Shrink](#sunset-shrink) | Sweep Reveal | Big letters shrink down into place; a band paints each one top to bottom in sunset colours | letters | in, the first 2.8 s |
109
119
  | [Boomerang](#boomerang) | Sweep Reveal | Letters glide down at a slant; a violet band runs out with the typing and swings back | letters | in, the first 3 s |
120
+ | [Rack Focus](#rack-focus) | Depth | Words come forward out of a soft background and snap into focus; at the end they pass the focus toward you | words | in, the first 1.78 s on a 5-word line, then out at the end |
121
+ | [Stand Up](#stand-up) | Depth | Letters slide forward across the floor and stand up one after another, hinged at their feet; at the end they fall forward toward you | letters | in, the first 1.5 s on a 16-letter line, then out at the end |
122
+ | [Strata](#strata) | Depth | Words float in and rest at their own depths while a slow camera drift slides them past each other; at the end they fall back | words | in, the first 1.8 s on a 6-word line (Drift across the whole layer), then out at the end |
123
+ | [Spotlight](#spotlight) | Depth | Words wait set back and dim; each in turn steps forward into the light, and the last one keeps it | words | once, the first 2.85 s on a 5-word line |
124
+ | [Ripple](#ripple) | Depth | Rings of depth spread from the centre of the text to its edges, over and over, like ripples on still water | letters | loops for the whole layer |
110
125
  | [Pill Type](#pill-type) | Type on Shape | Letters rise in a soft wave onto a pill that stretches along each line | letters | in, the first 1.6 s |
111
126
  | [Card Type](#card-type) | Type on Shape | Letters drop in while a white card widens with them, then grows a line taller | letters | in, the first 2 s |
112
127
  | [Word Bars](#word-bars) | Type on Shape | Words pop up one at a time and the red bar stretches to take each one | words | in, the first 1.6 s |
@@ -341,7 +356,7 @@ Letters land glowing hot at the typing edge and cool down behind it. Plays: in,
341
356
 
342
357
  ### Focus Type
343
358
 
344
- A long soft wave of letters falls out of a blur under a cool gradient. Plays: in, the first 2.9 s. Designed for left-aligned. Written for text like “Build a premium, modern sports-tech / landing page for a running performance / app with a full-screen cinematic hero / and a bold white headline about speed”.
359
+ A long soft wave of letters falls out of a blur under a cool gradient. Plays: in, the first 2.9 s. Designed for left-aligned text. Written for text like “Build a premium, modern sports-tech / landing page for a running performance / app with a full-screen cinematic hero / and a bold white headline about speed”.
345
360
 
346
361
  ```jsonc
347
362
  {
@@ -368,7 +383,7 @@ A long soft wave of letters falls out of a blur under a cool gradient. Plays: in
368
383
 
369
384
  ### Tumble Type
370
385
 
371
- Letters tumble down into place, then a lime band runs over the finished text. Plays: in, the first 2.7 s. Designed for left-aligned. Written for text like “Build a premium, modern sports-tech / landing page for a running performance / app with a full-screen cinematic hero / and a bold white headline about speed”.
386
+ Letters tumble down into place, then a lime band runs over the finished text. Plays: in, the first 2.7 s. Designed for left-aligned text. Written for text like “Build a premium, modern sports-tech / landing page for a running performance / app with a full-screen cinematic hero / and a bold white headline about speed”.
372
387
 
373
388
  ```jsonc
374
389
  {
@@ -395,7 +410,7 @@ Letters tumble down into place, then a lime band runs over the finished text. Pl
395
410
 
396
411
  ### Flap Down
397
412
 
398
- Letters swing down from a hinge at their top edge under an amber band. Plays: in, the first 2.75 s. Designed for left-aligned. Written for text like “Build a premium, modern sports-tech / landing page for a running performance / app with a full-screen cinematic hero / and a bold white headline about speed”.
413
+ Letters swing down from a hinge at their top edge under an amber band. Plays: in, the first 2.75 s. Designed for left-aligned text. Written for text like “Build a premium, modern sports-tech / landing page for a running performance / app with a full-screen cinematic hero / and a bold white headline about speed”.
399
414
 
400
415
  ```jsonc
401
416
  {
@@ -423,7 +438,7 @@ Letters swing down from a hinge at their top edge under an amber band. Plays: in
423
438
 
424
439
  ### Drop Swell
425
440
 
426
- Letters drop hard into place; a coral band lifts and swells them as it passes. Plays: in, the first 2.7 s. Designed for left-aligned. Written for text like “Build a premium, modern sports-tech / landing page for a running performance / app with a full-screen cinematic hero / and a bold white headline about speed”.
441
+ Letters drop hard into place; a coral band lifts and swells them as it passes. Plays: in, the first 2.7 s. Designed for left-aligned text. Written for text like “Build a premium, modern sports-tech / landing page for a running performance / app with a full-screen cinematic hero / and a bold white headline about speed”.
427
442
 
428
443
  ```jsonc
429
444
  {
@@ -450,7 +465,7 @@ Letters drop hard into place; a coral band lifts and swells them as it passes. P
450
465
 
451
466
  ### Rain Type
452
467
 
453
- Letters fall in at random like rain while a blue band runs left to right. Plays: in, the first 2.8 s. Designed for left-aligned. Written for text like “Build a premium, modern sports-tech / landing page for a running performance / app with a full-screen cinematic hero / and a bold white headline about speed”.
468
+ Letters fall in at random like rain while a blue band runs left to right. Plays: in, the first 2.8 s. Designed for left-aligned text. Written for text like “Build a premium, modern sports-tech / landing page for a running performance / app with a full-screen cinematic hero / and a bold white headline about speed”.
454
469
 
455
470
  ```jsonc
456
471
  {
@@ -477,7 +492,7 @@ Letters fall in at random like rain while a blue band runs left to right. Plays:
477
492
 
478
493
  ### Counter Sweep
479
494
 
480
- Letters type in left to right while a pink band runs back against them. Plays: in, the first 2.7 s. Designed for left-aligned. Written for text like “Build a premium, modern sports-tech / landing page for a running performance / app with a full-screen cinematic hero / and a bold white headline about speed”.
495
+ Letters type in left to right while a pink band runs back against them. Plays: in, the first 2.7 s. Designed for left-aligned text. Written for text like “Build a premium, modern sports-tech / landing page for a running performance / app with a full-screen cinematic hero / and a bold white headline about speed”.
481
496
 
482
497
  ```jsonc
483
498
  {
@@ -504,7 +519,7 @@ Letters type in left to right while a pink band runs back against them. Plays: i
504
519
 
505
520
  ### Sunset Shrink
506
521
 
507
- Big letters shrink down into place; a band paints each one top to bottom in sunset colours. Plays: in, the first 2.8 s. Designed for left-aligned. Written for text like “Build a premium, modern sports-tech / landing page for a running performance / app with a full-screen cinematic hero / and a bold white headline about speed”.
522
+ Big letters shrink down into place; a band paints each one top to bottom in sunset colours. Plays: in, the first 2.8 s. Designed for left-aligned text. Written for text like “Build a premium, modern sports-tech / landing page for a running performance / app with a full-screen cinematic hero / and a bold white headline about speed”.
508
523
 
509
524
  ```jsonc
510
525
  {
@@ -531,7 +546,7 @@ Big letters shrink down into place; a band paints each one top to bottom in suns
531
546
 
532
547
  ### Boomerang
533
548
 
534
- Letters glide down at a slant; a violet band runs out with the typing and swings back. Plays: in, the first 3 s. Designed for left-aligned. Written for text like “Build a premium, modern sports-tech / landing page for a running performance / app with a full-screen cinematic hero / and a bold white headline about speed”.
549
+ Letters glide down at a slant; a violet band runs out with the typing and swings back. Plays: in, the first 3 s. Designed for left-aligned text. Written for text like “Build a premium, modern sports-tech / landing page for a running performance / app with a full-screen cinematic hero / and a bold white headline about speed”.
535
550
 
536
551
  ```jsonc
537
552
  {
@@ -557,6 +572,114 @@ Letters glide down at a slant; a violet band runs out with the typing and swings
557
572
  }
558
573
  ```
559
574
 
575
+ ## Depth
576
+
577
+ ### Rack Focus
578
+
579
+ Words come forward out of a soft background and snap into focus; at the end they pass the focus toward you. Plays: in, the first 1.78 s on a 5-word line, then out at the end. Written for text like “Pull focus to every word”.
580
+
581
+ ```json
582
+ {
583
+ "textAnimators": [
584
+ { "aid": "a1", "name": "Focus in",
585
+ "range": {"basedOn": "words", "drive": "time", "animate": "in", "around": "word", "stagger": 0.12, "duration": 1.3, "curve": "Expo Out"},
586
+ "effects": [{"pid": "p1", "type": "position", "x": 0, "y": 0, "z": -900}, {"pid": "p2", "type": "blur", "amount": 12}, {"pid": "p3", "type": "opacity", "opacity": 0}] },
587
+ { "aid": "a2", "name": "Focus out",
588
+ "range": {"basedOn": "words", "drive": "time", "animate": "out", "around": "word", "stagger": 0.05, "duration": 0.6, "curve": "Ease In"},
589
+ "effects": [{"pid": "p4", "type": "position", "x": 0, "y": 0, "z": 500}, {"pid": "p5", "type": "blur", "amount": 12}, {"pid": "p6", "type": "opacity", "opacity": 0}] }
590
+ ],
591
+ "textAnimatorCamera": "layer",
592
+ "textAnimatorPerspective": 1400
593
+ }
594
+ ```
595
+
596
+ ### Stand Up
597
+
598
+ Letters slide forward across the floor and stand up one after another, hinged at their feet; at the end they fall forward toward you. Plays: in, the first 1.5 s on a 16-letter line, then out at the end. Written for text like “Stand up and be seen”.
599
+
600
+ ```json
601
+ {
602
+ "textAnimators": [
603
+ { "aid": "a1", "name": "Stand",
604
+ "range": {"basedOn": "charsNoSpace", "drive": "time", "animate": "in", "around": "char", "stagger": 0.04, "duration": 0.9, "curve": "Cubic Out"},
605
+ "effects": [{"pid": "p1", "type": "position", "x": 0, "y": 0, "z": -300}, {"pid": "p2", "type": "rotate3d", "x": 90, "y": 0, "perspective": 600}, {"pid": "p3", "type": "opacity", "opacity": 0}] },
606
+ { "aid": "a2", "name": "Fall",
607
+ "range": {"basedOn": "charsNoSpace", "drive": "time", "animate": "out", "around": "char", "stagger": 0.025, "duration": 0.6, "curve": "Ease In"},
608
+ "effects": [{"pid": "p4", "type": "position", "x": 0, "y": 0, "z": 150}, {"pid": "p5", "type": "rotate3d", "x": -90, "y": 0, "perspective": 600}, {"pid": "p6", "type": "opacity", "opacity": 0}] }
609
+ ],
610
+ "textAnimatorPivotY": 100,
611
+ "textAnimatorCamera": "layer",
612
+ "textAnimatorPerspective": 600
613
+ }
614
+ ```
615
+
616
+ ### Strata
617
+
618
+ Words float in and rest at their own depths while a slow camera drift slides them past each other; at the end they fall back. Plays: in, the first 1.8 s on a 6-word line (Drift across the whole layer), then out at the end. Written for text like “Each word at its own depth”.
619
+
620
+ ```jsonc
621
+ {
622
+ "textAnimators": [
623
+ { "aid": "a1", "name": "Layers",
624
+ "range": {"basedOn": "words", "around": "word", "shape": "smooth"},
625
+ "wiggle": {"enabled": true, "mode": "add", "min": 0, "max": 40, "rate": 0, "corr": 0, "seed": 8},
626
+ "effects": [{"pid": "p1", "type": "position", "x": 0, "y": 0, "z": -700}] },
627
+ { "aid": "a2", "name": "Drift",
628
+ "range": {"basedOn": "words", "around": "word"},
629
+ "effects": [{"pid": "p2", "type": "position", "x": 70, "y": 0, "z": -100}] },
630
+ { "aid": "a3", "name": "Float in",
631
+ "range": {"basedOn": "words", "drive": "time", "animate": "in", "around": "word", "stagger": 0.12, "duration": 1.2, "curve": "Cubic Out"},
632
+ "effects": [{"pid": "p3", "type": "position", "x": 0, "y": 0, "z": -1200}, {"pid": "p4", "type": "opacity", "opacity": 0}] },
633
+ { "aid": "a4", "name": "Fall back",
634
+ "range": {"basedOn": "words", "drive": "time", "animate": "out", "around": "word", "order": "backward", "stagger": 0.05, "duration": 0.6, "curve": "Ease In"},
635
+ "effects": [{"pid": "p5", "type": "position", "x": 0, "y": 0, "z": -1200}, {"pid": "p6", "type": "opacity", "opacity": 0}] }
636
+ ],
637
+ "textAnimatorCamera": "layer",
638
+ "textAnimatorPerspective": 1000,
639
+ "propertyKeyframes": {
640
+ "txa:a2:amount": [
641
+ { "position": 0, "value": "100", "easing": "Sine Smooth" }, // layer start
642
+ { "position": 100, "value": "-100", "easing": "Smooth" } // layer end
643
+ ]
644
+ }
645
+ }
646
+ ```
647
+
648
+ ### Spotlight
649
+
650
+ Words wait set back and dim; each in turn steps forward into the light, and the last one keeps it. Plays: once, the first 2.85 s on a 5-word line. Written for text like “Every word takes the stage”.
651
+
652
+ ```json
653
+ {
654
+ "textAnimators": [
655
+ { "aid": "a1", "name": "Sit back",
656
+ "range": {"basedOn": "words", "around": "word"},
657
+ "effects": [{"pid": "p1", "type": "position", "x": 0, "y": 0, "z": -160}, {"pid": "p2", "type": "fill", "color": "#5d636b"}] },
658
+ { "aid": "a2", "name": "Spotlight",
659
+ "range": {"basedOn": "words", "drive": "time", "animate": "active", "around": "word", "delay": 0.3, "stagger": 0.55, "duration": 0.35, "hold": 0, "fadeOut": 0.3, "keepLast": true, "curve": "Cubic Out"},
660
+ "effects": [{"pid": "p3", "type": "position", "x": 0, "y": 0, "z": 160}, {"pid": "p4", "type": "fill", "color": "#ffffff"}] }
661
+ ],
662
+ "textAnimatorCamera": "layer",
663
+ "textAnimatorPerspective": 1200
664
+ }
665
+ ```
666
+
667
+ ### Ripple
668
+
669
+ Rings of depth spread from the centre of the text to its edges, over and over, like ripples on still water. Plays: loops for the whole layer. Written for text like “Ripples from the centre”.
670
+
671
+ ```json
672
+ {
673
+ "textAnimators": [
674
+ { "aid": "a1", "name": "Ripple",
675
+ "range": {"basedOn": "charsNoSpace", "drive": "time", "animate": "active", "around": "char", "order": "center", "delay": 0.2, "stagger": 0.1, "duration": 0.14, "hold": 0, "fadeOut": 0.3, "loop": true, "curve": "Sine Out"},
676
+ "effects": [{"pid": "p1", "type": "position", "x": 0, "y": 0, "z": 110}, {"pid": "p2", "type": "fill", "color": "#8fe3ff", "mix": 65}] }
677
+ ],
678
+ "textAnimatorCamera": "layer",
679
+ "textAnimatorPerspective": 1000
680
+ }
681
+ ```
682
+
560
683
  ## Type on Shape
561
684
 
562
685
  ### Pill Type
@@ -1172,3 +1295,601 @@ Letters type in at speaking pace behind a caret. Plays: as each letter is spoken
1172
1295
  ```
1173
1296
 
1174
1297
  <!-- PRESETS:END -->
1298
+
1299
+ ## Classic
1300
+
1301
+ <!-- CLASSIC:BEGIN (generated from the app's Classic presets; do not edit by hand) -->
1302
+
1303
+ The editor's 29 Classic presets: calm Time-driven reveals by word or letter, highlights on their own clock, caption
1304
+ bars and randomizer looks. They are in the same preset picker as the recipes above. Each JSON below is what the app
1305
+ writes onto the text item when it applies the preset to a layer with a 100 px font.
1306
+
1307
+ They differ from the recipes above in four ways:
1308
+
1309
+ - **Lengths are composition px** and stay as written at any font size. The few that follow the font size are listed
1310
+ under the recipe with their formula; the JSON shows them for a 100 px font, so recompute them for the layer's font
1311
+ size in px (`textFontSize`, or in pct mode `textFontSizePct × videoSize.h / 100`).
1312
+ - **No keyframes.** Every Classic recipe is Time-driven: the in side starts with the layer and the out side ends with
1313
+ it, so a recipe fits any layer length as written.
1314
+ - **Layer fields.** Caption Bar and Randomizer recipes also set `textBg*` / `textBorder*` or `textRandom*` fields on
1315
+ the item (see [video-text.md](video-text.md#caption-bar-look) and [Text Randomizer](video-text.md#text-randomizer-fields)). They
1316
+ stay on the layer when you later switch recipe, so set `textBgEnabled`, `textBorderEnabled` or
1317
+ `textRandomEnabled` to `false` yourself when the new look should not have them.
1318
+ - **Voice.** To time a Classic recipe to speech (an item with `textWordTimings`), set `"drive": "voice"` and
1319
+ `"spread": true` on every animator whose `animate` is not `out` or `inOut`. That is what the app's caption presets
1320
+ do when voice sync is on.
1321
+
1322
+ | Recipe | Category | What it does | Unit | Plays |
1323
+ |---|---|---|---|---|
1324
+ | [Classic Fade Up](#classic-fade-up) | Classic · Reveal | Words fade in, rising 50px | words | in and out |
1325
+ | [Classic Fade Down](#classic-fade-down) | Classic · Reveal | Words drop in from above | words | in and out |
1326
+ | [Classic Rise (Mask)](#classic-rise-mask) | Classic · Reveal | Words rise from behind a mask | words | in and out |
1327
+ | [Classic Blur In](#classic-blur-in) | Classic · Reveal | Soft blur-focus reveal | words | in and out |
1328
+ | [Classic Slide In](#classic-slide-in) | Classic · Reveal | Words slide in from the left | words | in and out |
1329
+ | [Classic Side Mask](#classic-side-mask) | Classic · Reveal | Each word slides sideways out of its own box and leaves through the other side | words | in and out |
1330
+ | [Classic Tilt Drop](#classic-tilt-drop) | Classic · Reveal | Words fall in tilted and straighten with a bounce | words | in and out |
1331
+ | [Classic Flip Up](#classic-flip-up) | Classic · Character | Characters flip up on the X axis | letters | in and out |
1332
+ | [Classic Typewriter](#classic-typewriter) | Classic · Character | Characters reveal one by one behind a blinking caret | letters | in |
1333
+ | [Classic Wave Rise](#classic-wave-rise) | Classic · Character | Letters surge up in a rolling wave and settle with one soft overshoot | letters | in and out |
1334
+ | [Classic Bounce Letters](#classic-bounce-letters) | Classic · Character | Letters drop from above and bounce to rest | letters | in and out |
1335
+ | [Classic Materialize](#classic-materialize) | Classic · Character | Letters condense out of a cold glow, from blurred and large to sharp | letters | in and out, with a highlight |
1336
+ | [Classic Neon Runner](#classic-neon-runner) | Classic · Highlight | A glow sweeps across each word | words | in, with a highlight |
1337
+ | [Classic Ink Highlight](#classic-ink-highlight) | Classic · Highlight | Each word flashes a colour as it lands | words | in, with a highlight |
1338
+ | [Classic Flash Type](#classic-flash-type) | Classic · Highlight | Characters reveal with a running colour flash that trails off | letters | in, with a highlight |
1339
+ | [Classic Karaoke Sweep](#classic-karaoke-sweep) | Classic · Highlight | Fast type-in, then a bar walks word by word on its own clock | letters, then words | in, with a highlight |
1340
+ | [Classic Chase Loop](#classic-chase-loop) | Classic · Highlight | An accent keeps chasing through the words for as long as the clip runs | words | in and out, then a looping accent |
1341
+ | [Classic Sung Words](#classic-sung-words) | Classic · Highlight | Karaoke fill: each word turns gold on its beat and stays gold | words | in and out, with a highlight |
1342
+ | [Classic Marker Words](#classic-marker-words) | Classic · Highlight | Words rise from a mask, then a marker stroke lands under each word and stays | words | in, with a highlight |
1343
+ | [Classic Pill Grow](#classic-pill-grow) | Classic · Caption Bar | Words rise, then a dot pops at their left and stretches into a pill behind them | words | in and out |
1344
+ | [Classic Lower Third](#classic-lower-third) | Classic · Caption Bar | The bar slides in behind a mask while each word slides out of its own box; exits the same way | words | in and out |
1345
+ | [Classic Drop Bar](#classic-drop-bar) | Classic · Caption Bar | Bar and words drop in from above behind masks, and fall away below | words | in and out |
1346
+ | [Classic Outline Tag](#classic-outline-tag) | Classic · Caption Bar | An outline draws itself open to the right while the letters slide in | letters | in and out |
1347
+ | [Classic Prompt Field](#classic-prompt-field) | Classic · Caption Bar | An outlined pill fades in and the text types inside it behind a caret, like a search box | letters | in and out |
1348
+ | [Classic Random Pop](#classic-random-pop) | Classic · Randomizer | Words pop in, each with a random size and accent colour | words | in and out |
1349
+ | [Classic Chaos Letters](#classic-chaos-letters) | Classic · Randomizer | Characters flip in with scattered sizes and weights | letters | in and out |
1350
+ | [Classic Random Rise](#classic-random-rise) | Classic · Randomizer | Masked rise; a wave of size and two colours rolls across the words | words | in and out |
1351
+ | [Classic Ransom Note](#classic-ransom-note) | Classic · Randomizer | Cut-out letters: wild sizes, three colours, mixed weights and italics | letters | in |
1352
+ | [Classic Editorial Typer](#classic-editorial-typer) | Classic · Randomizer | Word typing behind a caret, set in a wave of weights, sizes and italics | words | in and out |
1353
+
1354
+ ### Classic Fade Up
1355
+
1356
+ Words fade in, rising 50px. App id `classic-fade-up`. Plays: in, words 0.06 s apart, 0.6 s each; out at the end, 0.5 s each. Lengths are composition px and stay as written at any font size.
1357
+
1358
+ ```json
1359
+ {
1360
+ "textAnimators": [
1361
+ { "aid": "a1", "name": "In",
1362
+ "range": {"basedOn": "words", "drive": "time", "animate": "in", "stagger": 0.06, "duration": 0.6, "curve": "Smooth", "spread": false},
1363
+ "effects": [{"pid": "p1", "type": "position", "x": 0, "y": 50}, {"pid": "p2", "type": "opacity", "opacity": 0}] },
1364
+ { "aid": "a2", "name": "Out",
1365
+ "range": {"basedOn": "words", "drive": "time", "animate": "out", "stagger": 0.06, "duration": 0.5, "curve": "Smooth", "spread": false},
1366
+ "effects": [{"pid": "p3", "type": "position", "x": 0, "y": -50}, {"pid": "p4", "type": "opacity", "opacity": 0}] }
1367
+ ]
1368
+ }
1369
+ ```
1370
+
1371
+ ### Classic Fade Down
1372
+
1373
+ Words drop in from above. App id `classic-fade-down`. Plays: in, words 0.06 s apart, 0.6 s each; out at the end, 0.5 s each. Lengths are composition px and stay as written at any font size.
1374
+
1375
+ ```json
1376
+ {
1377
+ "textAnimators": [
1378
+ { "aid": "a1", "name": "In",
1379
+ "range": {"basedOn": "words", "drive": "time", "animate": "in", "stagger": 0.06, "duration": 0.6, "curve": "Smooth", "spread": false},
1380
+ "effects": [{"pid": "p1", "type": "position", "x": 0, "y": -50}, {"pid": "p2", "type": "opacity", "opacity": 0}] },
1381
+ { "aid": "a2", "name": "Out",
1382
+ "range": {"basedOn": "words", "drive": "time", "animate": "out", "stagger": 0.06, "duration": 0.5, "curve": "Smooth", "spread": false},
1383
+ "effects": [{"pid": "p3", "type": "position", "x": 0, "y": 50}, {"pid": "p4", "type": "opacity", "opacity": 0}] }
1384
+ ]
1385
+ }
1386
+ ```
1387
+
1388
+ ### Classic Rise (Mask)
1389
+
1390
+ Words rise from behind a mask. App id `classic-rise-mask`. Plays: in, words 0.08 s apart, 0.7 s each; out at the end, 0.5 s each. Lengths are composition px and stay as written at any font size, except: In position `y` is the larger of 90 px and 1.15 × font px (115 as written, for a 100 px font); Out position `y` is −(the larger of 90 px and 1.15 × font px) (−115 as written, for a 100 px font).
1391
+
1392
+ ```json
1393
+ {
1394
+ "textAnimators": [
1395
+ { "aid": "a1", "name": "In",
1396
+ "range": {"basedOn": "words", "drive": "time", "animate": "in", "mask": true, "stagger": 0.08, "duration": 0.7, "curve": "Smooth", "spread": false},
1397
+ "effects": [{"pid": "p1", "type": "position", "x": 0, "y": 115}] },
1398
+ { "aid": "a2", "name": "Out",
1399
+ "range": {"basedOn": "words", "drive": "time", "animate": "out", "mask": true, "stagger": 0.08, "duration": 0.5, "curve": "Smooth", "spread": false},
1400
+ "effects": [{"pid": "p2", "type": "position", "x": 0, "y": -115}] }
1401
+ ]
1402
+ }
1403
+ ```
1404
+
1405
+ ### Classic Blur In
1406
+
1407
+ Soft blur-focus reveal. App id `classic-blur-in`. Plays: in, words 0.07 s apart, 0.6 s each; out at the end, 0.45 s each. Lengths are composition px and stay as written at any font size.
1408
+
1409
+ ```json
1410
+ {
1411
+ "textAnimators": [
1412
+ { "aid": "a1", "name": "In",
1413
+ "range": {"basedOn": "words", "drive": "time", "animate": "in", "stagger": 0.07, "duration": 0.6, "curve": "Smooth", "spread": false},
1414
+ "effects": [{"pid": "p1", "type": "position", "x": 0, "y": 12}, {"pid": "p2", "type": "blur", "amount": 14}, {"pid": "p3", "type": "opacity", "opacity": 0}] },
1415
+ { "aid": "a2", "name": "Out",
1416
+ "range": {"basedOn": "words", "drive": "time", "animate": "out", "stagger": 0.07, "duration": 0.45, "curve": "Smooth", "spread": false},
1417
+ "effects": [{"pid": "p4", "type": "blur", "amount": 14}, {"pid": "p5", "type": "opacity", "opacity": 0}] }
1418
+ ]
1419
+ }
1420
+ ```
1421
+
1422
+ ### Classic Slide In
1423
+
1424
+ Words slide in from the left. App id `classic-slide-in`. Plays: in, words 0.06 s apart, 0.6 s each; out at the end, 0.5 s each. Lengths are composition px and stay as written at any font size.
1425
+
1426
+ ```json
1427
+ {
1428
+ "textAnimators": [
1429
+ { "aid": "a1", "name": "In",
1430
+ "range": {"basedOn": "words", "drive": "time", "animate": "in", "stagger": 0.06, "duration": 0.6, "curve": "Smooth", "spread": false},
1431
+ "effects": [{"pid": "p1", "type": "position", "x": -60, "y": 0}, {"pid": "p2", "type": "opacity", "opacity": 0}] },
1432
+ { "aid": "a2", "name": "Out",
1433
+ "range": {"basedOn": "words", "drive": "time", "animate": "out", "stagger": 0.06, "duration": 0.5, "curve": "Smooth", "spread": false},
1434
+ "effects": [{"pid": "p3", "type": "position", "x": 60, "y": 0}, {"pid": "p4", "type": "opacity", "opacity": 0}] }
1435
+ ]
1436
+ }
1437
+ ```
1438
+
1439
+ ### Classic Side Mask
1440
+
1441
+ Each word slides sideways out of its own box and leaves through the other side. App id `classic-side-mask`. Plays: in, words 0.08 s apart, 0.7 s each; out at the end, 0.45 s each. Lengths are composition px and stay as written at any font size, except: In position `y` is 1.15 × font px (115 as written, for a 100 px font); Out position `y` is −1.15 × font px (−115 as written, for a 100 px font).
1442
+
1443
+ ```json
1444
+ {
1445
+ "textAnimators": [
1446
+ { "aid": "a1", "name": "In",
1447
+ "range": {"basedOn": "words", "drive": "time", "animate": "in", "mask": true, "stagger": 0.08, "duration": 0.7, "curve": "Expo Out", "spread": false},
1448
+ "effects": [{"pid": "p1", "type": "position", "x": -300, "y": 115}] },
1449
+ { "aid": "a2", "name": "Out",
1450
+ "range": {"basedOn": "words", "drive": "time", "animate": "out", "mask": true, "stagger": 0.08, "duration": 0.45, "curve": "Expo In", "spread": false},
1451
+ "effects": [{"pid": "p2", "type": "position", "x": 300, "y": -115}] }
1452
+ ]
1453
+ }
1454
+ ```
1455
+
1456
+ ### Classic Tilt Drop
1457
+
1458
+ Words fall in tilted and straighten with a bounce. App id `classic-tilt-drop`. Plays: in, words 0.09 s apart, 0.75 s each; out at the end, 0.4 s each. Lengths are composition px and stay as written at any font size.
1459
+
1460
+ ```json
1461
+ {
1462
+ "textAnimators": [
1463
+ { "aid": "a1", "name": "In",
1464
+ "range": {"basedOn": "words", "drive": "time", "animate": "in", "stagger": 0.09, "duration": 0.75, "curve": "Back Out", "spread": false},
1465
+ "effects": [{"pid": "p1", "type": "position", "x": 0, "y": -90}, {"pid": "p2", "type": "rotation", "angle": -14}, {"pid": "p3", "type": "opacity", "opacity": 0}] },
1466
+ { "aid": "a2", "name": "Out",
1467
+ "range": {"basedOn": "words", "drive": "time", "animate": "out", "stagger": 0.09, "duration": 0.4, "curve": "Ease In", "spread": false},
1468
+ "effects": [{"pid": "p4", "type": "position", "x": 0, "y": 60}, {"pid": "p5", "type": "rotation", "angle": 10}, {"pid": "p6", "type": "opacity", "opacity": 0}] }
1469
+ ]
1470
+ }
1471
+ ```
1472
+
1473
+ ### Classic Flip Up
1474
+
1475
+ Characters flip up on the X axis. App id `classic-flip-up`. Plays: in, letters 0.03 s apart, 0.5 s each; out at the end, 0.4 s each. Lengths are composition px and stay as written at any font size.
1476
+
1477
+ ```json
1478
+ {
1479
+ "textAnimators": [
1480
+ { "aid": "a1", "name": "In",
1481
+ "range": {"basedOn": "chars", "drive": "time", "animate": "in", "stagger": 0.03, "duration": 0.5, "curve": "Smooth", "spread": false},
1482
+ "effects": [{"pid": "p1", "type": "position", "x": 0, "y": 10}, {"pid": "p2", "type": "rotate3d", "x": 90, "y": 0}, {"pid": "p3", "type": "opacity", "opacity": 0}] },
1483
+ { "aid": "a2", "name": "Out",
1484
+ "range": {"basedOn": "chars", "drive": "time", "animate": "out", "stagger": 0.03, "duration": 0.4, "curve": "Smooth", "spread": false},
1485
+ "effects": [{"pid": "p4", "type": "rotate3d", "x": -90, "y": 0}, {"pid": "p5", "type": "opacity", "opacity": 0}] }
1486
+ ]
1487
+ }
1488
+ ```
1489
+
1490
+ ### Classic Typewriter
1491
+
1492
+ Characters reveal one by one behind a blinking caret. App id `classic-typewriter`. Plays: in, letters 0.025 s apart, 0.12 s each. Lengths are composition px and stay as written at any font size.
1493
+
1494
+ ```json
1495
+ {
1496
+ "textAnimators": [
1497
+ { "aid": "a1", "name": "In",
1498
+ "range": {"basedOn": "chars", "drive": "time", "animate": "in", "stagger": 0.025, "duration": 0.12, "curve": "Smooth", "spread": false},
1499
+ "effects": [{"pid": "p1", "type": "opacity", "opacity": 0}, {"pid": "p2", "type": "caret"}] }
1500
+ ]
1501
+ }
1502
+ ```
1503
+
1504
+ ### Classic Wave Rise
1505
+
1506
+ Letters surge up in a rolling wave and settle with one soft overshoot. App id `classic-wave-rise`. Plays: in, letters 0.03 s apart, 0.9 s each; out at the end, 0.4 s each. Lengths are composition px and stay as written at any font size.
1507
+
1508
+ ```json
1509
+ {
1510
+ "textAnimators": [
1511
+ { "aid": "a1", "name": "In",
1512
+ "range": {"basedOn": "chars", "drive": "time", "animate": "in", "stagger": 0.03, "duration": 0.9, "curve": "Smooth Overshoot", "spread": false},
1513
+ "effects": [{"pid": "p1", "type": "position", "x": 0, "y": 60}, {"pid": "p2", "type": "opacity", "opacity": 0}] },
1514
+ { "aid": "a2", "name": "Out",
1515
+ "range": {"basedOn": "chars", "drive": "time", "animate": "out", "stagger": 0.03, "duration": 0.4, "curve": "Ease In", "spread": false},
1516
+ "effects": [{"pid": "p3", "type": "position", "x": 0, "y": -40}, {"pid": "p4", "type": "opacity", "opacity": 0}] }
1517
+ ]
1518
+ }
1519
+ ```
1520
+
1521
+ ### Classic Bounce Letters
1522
+
1523
+ Letters drop from above and bounce to rest. App id `classic-bounce-letters`. Plays: in, letters 0.04 s apart, 0.9 s each; out at the end, 0.3 s each. Lengths are composition px and stay as written at any font size.
1524
+
1525
+ ```json
1526
+ {
1527
+ "textAnimators": [
1528
+ { "aid": "a1", "name": "In",
1529
+ "range": {"basedOn": "chars", "drive": "time", "animate": "in", "stagger": 0.04, "duration": 0.9, "curve": "Bounce Out", "spread": false},
1530
+ "effects": [{"pid": "p1", "type": "position", "x": 0, "y": -140}, {"pid": "p2", "type": "opacity", "opacity": 0}] },
1531
+ { "aid": "a2", "name": "Out",
1532
+ "range": {"basedOn": "chars", "drive": "time", "animate": "out", "stagger": 0.04, "duration": 0.3, "curve": "Ease In", "spread": false},
1533
+ "effects": [{"pid": "p3", "type": "position", "x": 0, "y": 40}, {"pid": "p4", "type": "opacity", "opacity": 0}] }
1534
+ ]
1535
+ }
1536
+ ```
1537
+
1538
+ ### Classic Materialize
1539
+
1540
+ Letters condense out of a cold glow, from blurred and large to sharp. App id `classic-materialize`. Plays: in, letters 0.035 s apart, 0.7 s each; out at the end, 0.4 s each; a highlight walks the letters 0.035 s apart. Lengths are composition px and stay as written at any font size.
1541
+
1542
+ ```json
1543
+ {
1544
+ "textAnimators": [
1545
+ { "aid": "a1", "name": "In",
1546
+ "range": {"basedOn": "chars", "drive": "time", "animate": "in", "stagger": 0.035, "duration": 0.7, "curve": "Cubic Out", "spread": false},
1547
+ "effects": [{"pid": "p1", "type": "scale", "scale": 135}, {"pid": "p2", "type": "blur", "amount": 18}, {"pid": "p3", "type": "opacity", "opacity": 0}] },
1548
+ { "aid": "a2", "name": "Out",
1549
+ "range": {"basedOn": "chars", "drive": "time", "animate": "out", "stagger": 0.035, "duration": 0.4, "curve": "Ease In", "spread": false},
1550
+ "effects": [{"pid": "p4", "type": "blur", "amount": 12}, {"pid": "p5", "type": "opacity", "opacity": 0}] },
1551
+ { "aid": "a3", "name": "Highlight",
1552
+ "range": {"basedOn": "chars", "drive": "time", "animate": "active", "stagger": 0.035, "duration": 0, "curve": "Linear", "fadeOut": 0.6},
1553
+ "effects": [{"pid": "p6", "type": "glow", "color": "#7ad7ff", "size": 26, "strength": 100}] }
1554
+ ]
1555
+ }
1556
+ ```
1557
+
1558
+ ### Classic Neon Runner
1559
+
1560
+ A glow sweeps across each word. App id `classic-neon-runner`. Plays: in, words 0.16 s apart, 0.35 s each; a highlight walks the words 0.16 s apart. Lengths are composition px and stay as written at any font size.
1561
+
1562
+ ```json
1563
+ {
1564
+ "textAnimators": [
1565
+ { "aid": "a1", "name": "In",
1566
+ "range": {"basedOn": "words", "drive": "time", "animate": "in", "stagger": 0.16, "duration": 0.35, "curve": "Smooth", "spread": false},
1567
+ "effects": [{"pid": "p1", "type": "opacity", "opacity": 0}] },
1568
+ { "aid": "a2", "name": "Highlight",
1569
+ "range": {"basedOn": "words", "drive": "time", "animate": "active", "stagger": 0.16, "duration": 0, "curve": "Linear"},
1570
+ "effects": [{"pid": "p2", "type": "fill", "color": "#7c5cff"}, {"pid": "p3", "type": "glow", "color": "#7c5cff", "size": 28, "strength": 100}] }
1571
+ ]
1572
+ }
1573
+ ```
1574
+
1575
+ ### Classic Ink Highlight
1576
+
1577
+ Each word flashes a colour as it lands. App id `classic-ink-highlight`. Plays: in, words 0.16 s apart, 0.35 s each; a highlight walks the words 0.16 s apart. Lengths are composition px and stay as written at any font size.
1578
+
1579
+ ```json
1580
+ {
1581
+ "textAnimators": [
1582
+ { "aid": "a1", "name": "In",
1583
+ "range": {"basedOn": "words", "drive": "time", "animate": "in", "stagger": 0.16, "duration": 0.35, "curve": "Smooth", "spread": false},
1584
+ "effects": [{"pid": "p1", "type": "position", "x": 0, "y": 14}, {"pid": "p2", "type": "opacity", "opacity": 0}] },
1585
+ { "aid": "a2", "name": "Highlight",
1586
+ "range": {"basedOn": "words", "drive": "time", "animate": "active", "stagger": 0.16, "duration": 0, "curve": "Linear"},
1587
+ "effects": [{"pid": "p3", "type": "fill", "color": "#ff5c8a"}] }
1588
+ ]
1589
+ }
1590
+ ```
1591
+
1592
+ ### Classic Flash Type
1593
+
1594
+ Characters reveal with a running colour flash that trails off. App id `classic-flash-type`. Plays: in, letters 0.05 s apart, 0.15 s each; a highlight walks the letters 0.05 s apart from 0.05 s. Lengths are composition px and stay as written at any font size.
1595
+
1596
+ ```json
1597
+ {
1598
+ "textAnimators": [
1599
+ { "aid": "a1", "name": "In",
1600
+ "range": {"basedOn": "chars", "drive": "time", "animate": "in", "stagger": 0.05, "duration": 0.15, "curve": "Smooth", "spread": false},
1601
+ "effects": [{"pid": "p1", "type": "opacity", "opacity": 0}] },
1602
+ { "aid": "a2", "name": "Highlight",
1603
+ "range": {"basedOn": "chars", "drive": "time", "animate": "active", "delay": 0.05, "stagger": 0.05, "duration": 0, "curve": "Linear", "fadeOut": 0.15},
1604
+ "effects": [{"pid": "p2", "type": "fill", "color": "#ff044b"}] }
1605
+ ]
1606
+ }
1607
+ ```
1608
+
1609
+ ### Classic Karaoke Sweep
1610
+
1611
+ Fast type-in, then a bar walks word by word on its own clock. App id `classic-karaoke-sweep`. Plays: in, letters 0.02 s apart, 0.25 s each; a highlight walks the words 0.45 s apart from 0.4 s, the last one stays lit. Lengths are composition px and stay as written at any font size, except: Highlight box `radius` is 10 px + 0.14 × font px (24 as written, for a 100 px font).
1612
+
1613
+ ```json
1614
+ {
1615
+ "textAnimators": [
1616
+ { "aid": "a1", "name": "In",
1617
+ "range": {"basedOn": "chars", "drive": "time", "animate": "in", "stagger": 0.02, "duration": 0.25, "curve": "Smooth", "spread": false},
1618
+ "effects": [{"pid": "p1", "type": "opacity", "opacity": 0}] },
1619
+ { "aid": "a2", "name": "Highlight",
1620
+ "range": {"basedOn": "words", "drive": "time", "animate": "active", "delay": 0.4, "stagger": 0.45, "duration": 0, "curve": "Linear", "fadeOut": 0.2, "keepLast": true},
1621
+ "effects": [{"pid": "p2", "type": "box", "color": "#ffe74c", "shape": "unit", "padX": 10, "padY": 10, "radius": 24, "grow": false, "withText": true}] }
1622
+ ]
1623
+ }
1624
+ ```
1625
+
1626
+ ### Classic Chase Loop
1627
+
1628
+ An accent keeps chasing through the words for as long as the clip runs. App id `classic-chase-loop`. Plays: in, words 0.03 s apart, 0.3 s each; out at the end, 0.3 s each; a highlight walks the words 0.3 s apart from 0.4 s, on a loop. Lengths are composition px and stay as written at any font size.
1629
+
1630
+ ```json
1631
+ {
1632
+ "textAnimators": [
1633
+ { "aid": "a1", "name": "In",
1634
+ "range": {"basedOn": "words", "drive": "time", "animate": "in", "stagger": 0.03, "duration": 0.3, "curve": "Smooth", "spread": false},
1635
+ "effects": [{"pid": "p1", "type": "opacity", "opacity": 0}] },
1636
+ { "aid": "a2", "name": "Out",
1637
+ "range": {"basedOn": "words", "drive": "time", "animate": "out", "stagger": 0.03, "duration": 0.3, "curve": "Smooth", "spread": false},
1638
+ "effects": [{"pid": "p2", "type": "opacity", "opacity": 0}] },
1639
+ { "aid": "a3", "name": "Highlight",
1640
+ "range": {"basedOn": "words", "drive": "time", "animate": "active", "delay": 0.4, "stagger": 0.3, "duration": 0.12, "curve": "Linear", "fadeOut": 0.35, "loop": true},
1641
+ "effects": [{"pid": "p3", "type": "fill", "color": "#33efab"}] }
1642
+ ]
1643
+ }
1644
+ ```
1645
+
1646
+ ### Classic Sung Words
1647
+
1648
+ Karaoke fill: each word turns gold on its beat and stays gold. App id `classic-sung-words`. Plays: in, words 0.04 s apart, 0.35 s each; out at the end, 0.3 s each; a highlight walks the words 0.32 s apart from 0.5 s, each stays lit. Lengths are composition px and stay as written at any font size.
1649
+
1650
+ ```json
1651
+ {
1652
+ "textAnimators": [
1653
+ { "aid": "a1", "name": "In",
1654
+ "range": {"basedOn": "words", "drive": "time", "animate": "in", "stagger": 0.04, "duration": 0.35, "curve": "Smooth", "spread": false},
1655
+ "effects": [{"pid": "p1", "type": "position", "x": 0, "y": 10}, {"pid": "p2", "type": "opacity", "opacity": 0}] },
1656
+ { "aid": "a2", "name": "Out",
1657
+ "range": {"basedOn": "words", "drive": "time", "animate": "out", "stagger": 0.04, "duration": 0.3, "curve": "Smooth", "spread": false},
1658
+ "effects": [{"pid": "p3", "type": "opacity", "opacity": 0}] },
1659
+ { "aid": "a3", "name": "Highlight",
1660
+ "range": {"basedOn": "words", "drive": "time", "animate": "active", "delay": 0.5, "stagger": 0.32, "duration": 0.15, "curve": "Linear", "hold": 10},
1661
+ "effects": [{"pid": "p4", "type": "fill", "color": "#ffd23f"}] }
1662
+ ]
1663
+ }
1664
+ ```
1665
+
1666
+ ### Classic Marker Words
1667
+
1668
+ Words rise from a mask, then a marker stroke lands under each word and stays. App id `classic-marker-lines`. Plays: in, words 0.06 s apart, 0.7 s each; a highlight walks the words 0.12 s apart from 0.9 s, each stays lit. Lengths are composition px and stay as written at any font size, except: In position `y` is the larger of 90 px and 1.15 × font px (115 as written, for a 100 px font); Highlight box `radius` is 6 px + 0.14 × font px (20 as written, for a 100 px font).
1669
+
1670
+ ```json
1671
+ {
1672
+ "textAnimators": [
1673
+ { "aid": "a1", "name": "In",
1674
+ "range": {"basedOn": "words", "drive": "time", "animate": "in", "mask": true, "stagger": 0.06, "duration": 0.7, "curve": "Expo Out", "spread": false},
1675
+ "effects": [{"pid": "p1", "type": "position", "x": 0, "y": 115}] },
1676
+ { "aid": "a2", "name": "Highlight",
1677
+ "range": {"basedOn": "words", "drive": "time", "animate": "active", "delay": 0.9, "stagger": 0.12, "duration": 0.25, "curve": "Linear", "hold": 10},
1678
+ "effects": [{"pid": "p2", "type": "box", "color": "#ff3d6e", "shape": "unit", "padX": 6, "padY": 6, "radius": 20, "grow": false, "withText": true}] }
1679
+ ]
1680
+ }
1681
+ ```
1682
+
1683
+ ### Classic Pill Grow
1684
+
1685
+ Words rise, then a dot pops at their left and stretches into a pill behind them. App id `classic-pill-grow`. Plays: in, words 0.07 s apart, 0.5 s each; out at the end, 0.25 s each. Lengths are composition px and stay as written at any font size. Sets the caption bar and its reveal (`textBg*` / `textBorder*`), as the app does when it applies the preset.
1686
+
1687
+ ```json
1688
+ {
1689
+ "textAnimators": [
1690
+ { "aid": "a1", "name": "In",
1691
+ "range": {"basedOn": "words", "drive": "time", "animate": "in", "stagger": 0.07, "duration": 0.5, "curve": "Cubic Out", "spread": false},
1692
+ "effects": [{"pid": "p1", "type": "position", "x": 0, "y": 24}, {"pid": "p2", "type": "opacity", "opacity": 0}] },
1693
+ { "aid": "a2", "name": "Out",
1694
+ "range": {"basedOn": "words", "drive": "time", "animate": "out", "stagger": 0.07, "duration": 0.25, "curve": "Ease In", "spread": false},
1695
+ "effects": [{"pid": "p3", "type": "position", "x": 0, "y": -16}, {"pid": "p4", "type": "opacity", "opacity": 0}] }
1696
+ ],
1697
+ "textBgEnabled": true, "textBgColor": "#2f6bff", "textBgPaddingX": 40, "textBgPaddingY": 20,
1698
+ "textBgRadius": 200, "textBorderEnabled": false, "textBgInType": "mask-right", "textBgInStyle": "grow",
1699
+ "textBgInDuration": 0.55, "textBgInEasing": "Expo Out", "textBgInDelay": 0.15, "textBgInGrowStart": 0,
1700
+ "textBgInGrowAppear": "scale-in", "textBgInGrowAppearDuration": 0.25, "textBgInGrowAppearEasing": "Back Out", "textBgOutType": "mask-right",
1701
+ "textBgOutStyle": "grow", "textBgOutDuration": 0.3, "textBgOutEasing": "Ease In", "textBgOutGrowStart": 0,
1702
+ "textBgOutGrowAppear": "scale-in", "textBgOutGrowAppearDuration": 0.15, "textBgOutGrowAppearEasing": "Ease In"
1703
+ }
1704
+ ```
1705
+
1706
+ ### Classic Lower Third
1707
+
1708
+ The bar slides in behind a mask while each word slides out of its own box; exits the same way. App id `classic-lower-third`. Plays: in, words 0.06 s apart, 0.7 s each; out at the end, 0.45 s each. Lengths are composition px and stay as written at any font size, except: In position `y` is 1.15 × font px (115 as written, for a 100 px font); Out position `y` is −1.15 × font px (−115 as written, for a 100 px font). Sets the caption bar and its reveal (`textBg*` / `textBorder*`), as the app does when it applies the preset.
1709
+
1710
+ ```json
1711
+ {
1712
+ "textAnimators": [
1713
+ { "aid": "a1", "name": "In",
1714
+ "range": {"basedOn": "words", "drive": "time", "animate": "in", "mask": true, "stagger": 0.06, "duration": 0.7, "curve": "Expo Out", "spread": false},
1715
+ "effects": [{"pid": "p1", "type": "position", "x": -260, "y": 115}] },
1716
+ { "aid": "a2", "name": "Out",
1717
+ "range": {"basedOn": "words", "drive": "time", "animate": "out", "mask": true, "stagger": 0.06, "duration": 0.45, "curve": "Expo In", "spread": false},
1718
+ "effects": [{"pid": "p2", "type": "position", "x": 260, "y": -115}] }
1719
+ ],
1720
+ "textBgEnabled": true, "textBgColor": "rgba(10,10,14,0.88)", "textBgPaddingX": 34, "textBgPaddingY": 18,
1721
+ "textBgRadius": 6, "textBorderEnabled": false, "textBgInType": "mask-right", "textBgInStyle": "slide",
1722
+ "textBgInDuration": 0.55, "textBgInEasing": "Expo Out", "textBgInDelay": 0, "textBgOutType": "mask-right",
1723
+ "textBgOutStyle": "slide", "textBgOutDuration": 0.45, "textBgOutEasing": "Expo In"
1724
+ }
1725
+ ```
1726
+
1727
+ ### Classic Drop Bar
1728
+
1729
+ Bar and words drop in from above behind masks, and fall away below. App id `classic-drop-bar`. Plays: in, words 0.05 s apart, 0.6 s each; out at the end, 0.4 s each. Lengths are composition px and stay as written at any font size, except: In position `y` is −(the larger of 90 px and 1.15 × font px) (−115 as written, for a 100 px font); Out position `y` is the larger of 90 px and 1.15 × font px (115 as written, for a 100 px font). Sets the caption bar and its reveal (`textBg*` / `textBorder*`), as the app does when it applies the preset.
1730
+
1731
+ ```json
1732
+ {
1733
+ "textAnimators": [
1734
+ { "aid": "a1", "name": "In",
1735
+ "range": {"basedOn": "words", "drive": "time", "animate": "in", "mask": true, "stagger": 0.05, "duration": 0.6, "curve": "Expo Out", "spread": false},
1736
+ "effects": [{"pid": "p1", "type": "position", "x": 0, "y": -115}] },
1737
+ { "aid": "a2", "name": "Out",
1738
+ "range": {"basedOn": "words", "drive": "time", "animate": "out", "mask": true, "stagger": 0.05, "duration": 0.4, "curve": "Expo In", "spread": false},
1739
+ "effects": [{"pid": "p2", "type": "position", "x": 0, "y": 115}] }
1740
+ ],
1741
+ "textBgEnabled": true, "textBgColor": "#7c5cff", "textBgPaddingX": 34, "textBgPaddingY": 16,
1742
+ "textBgRadius": 10, "textBorderEnabled": false, "textBgInType": "mask-down", "textBgInStyle": "slide",
1743
+ "textBgInDuration": 0.5, "textBgInEasing": "Expo Out", "textBgInDelay": 0, "textBgOutType": "mask-down",
1744
+ "textBgOutStyle": "slide", "textBgOutDuration": 0.4, "textBgOutEasing": "Expo In"
1745
+ }
1746
+ ```
1747
+
1748
+ ### Classic Outline Tag
1749
+
1750
+ An outline draws itself open to the right while the letters slide in. App id `classic-outline-tag`. Plays: in, letters 0.022 s apart, 0.3 s each; out at the end, 0.2 s each. Lengths are composition px and stay as written at any font size. Sets the caption bar and its reveal (`textBg*` / `textBorder*`), as the app does when it applies the preset.
1751
+
1752
+ ```json
1753
+ {
1754
+ "textAnimators": [
1755
+ { "aid": "a1", "name": "In",
1756
+ "range": {"basedOn": "chars", "drive": "time", "animate": "in", "stagger": 0.022, "duration": 0.3, "curve": "Ease Out", "spread": false},
1757
+ "effects": [{"pid": "p1", "type": "position", "x": -12, "y": 0}, {"pid": "p2", "type": "opacity", "opacity": 0}] },
1758
+ { "aid": "a2", "name": "Out",
1759
+ "range": {"basedOn": "chars", "drive": "time", "animate": "out", "stagger": 0.022, "duration": 0.2, "curve": "Ease In", "spread": false},
1760
+ "effects": [{"pid": "p3", "type": "opacity", "opacity": 0}] }
1761
+ ],
1762
+ "textBgEnabled": false, "textBgColor": "rgba(0,0,0,0.6)", "textBgPaddingX": 36, "textBgPaddingY": 18,
1763
+ "textBgRadius": 14, "textBorderEnabled": true, "textBorderColor": "#33efab", "textBorderWidth": 3,
1764
+ "textBorderStyle": "solid", "textBgInType": "mask-right", "textBgInStyle": "grow", "textBgInDuration": 0.6,
1765
+ "textBgInEasing": "Cubic Out", "textBgInDelay": 0, "textBgInGrowStart": 0, "textBgInGrowAppear": "fade",
1766
+ "textBgInGrowAppearDuration": 0.2, "textBgInGrowAppearEasing": "Ease Out", "textBgOutType": "mask-right", "textBgOutStyle": "grow",
1767
+ "textBgOutDuration": 0.4, "textBgOutEasing": "Ease In", "textBgOutGrowStart": 0, "textBgOutGrowAppear": "none",
1768
+ "textBgOutGrowAppearDuration": 0.4, "textBgOutGrowAppearEasing": "Ease In"
1769
+ }
1770
+ ```
1771
+
1772
+ ### Classic Prompt Field
1773
+
1774
+ An outlined pill fades in and the text types inside it behind a caret, like a search box. App id `classic-prompt-field`. Plays: in, letters 0.04 s apart, 0.05 s each; out at the end, 0.2 s each. Lengths are composition px and stay as written at any font size. Sets the caption bar and its reveal (`textBg*` / `textBorder*`), as the app does when it applies the preset.
1775
+
1776
+ ```json
1777
+ {
1778
+ "textAnimators": [
1779
+ { "aid": "a1", "name": "In",
1780
+ "range": {"basedOn": "chars", "drive": "time", "animate": "in", "stagger": 0.04, "duration": 0.05, "curve": "Linear", "spread": false},
1781
+ "effects": [{"pid": "p1", "type": "opacity", "opacity": 0}, {"pid": "p2", "type": "caret"}] },
1782
+ { "aid": "a2", "name": "Out",
1783
+ "range": {"basedOn": "chars", "drive": "time", "animate": "out", "stagger": 0.04, "duration": 0.2, "curve": "Ease In", "spread": false},
1784
+ "effects": [{"pid": "p3", "type": "opacity", "opacity": 0}] }
1785
+ ],
1786
+ "textBgEnabled": false, "textBgColor": "rgba(0,0,0,0.6)", "textBgPaddingX": 46, "textBgPaddingY": 28,
1787
+ "textBgRadius": 200, "textBorderEnabled": true, "textBorderColor": "rgba(255,255,255,0.5)", "textBorderWidth": 3,
1788
+ "textBorderStyle": "solid", "textBgInType": "fade", "textBgInStyle": "wipe", "textBgInDuration": 0.35,
1789
+ "textBgInEasing": "Ease Out", "textBgInDelay": 0, "textBgOutType": "fade", "textBgOutStyle": "wipe",
1790
+ "textBgOutDuration": 0.3, "textBgOutEasing": "Ease In"
1791
+ }
1792
+ ```
1793
+
1794
+ ### Classic Random Pop
1795
+
1796
+ Words pop in, each with a random size and accent colour. App id `classic-random-pop`. Plays: in, words 0.05 s apart, 0.45 s each; out at the end, 0.4 s each. Lengths are composition px and stay as written at any font size. Sets the Text Randomizer (`textRandom*`), as the app does when it applies the preset.
1797
+
1798
+ ```json
1799
+ {
1800
+ "textAnimators": [
1801
+ { "aid": "a1", "name": "In",
1802
+ "range": {"basedOn": "words", "drive": "time", "animate": "in", "stagger": 0.05, "duration": 0.45, "curve": "Back Out", "spread": false},
1803
+ "effects": [{"pid": "p1", "type": "scale", "scale": 40}, {"pid": "p2", "type": "opacity", "opacity": 0}] },
1804
+ { "aid": "a2", "name": "Out",
1805
+ "range": {"basedOn": "words", "drive": "time", "animate": "out", "stagger": 0.05, "duration": 0.4, "curve": "Smooth", "spread": false},
1806
+ "effects": [{"pid": "p3", "type": "scale", "scale": 50}, {"pid": "p4", "type": "opacity", "opacity": 0}] }
1807
+ ],
1808
+ "textRandomEnabled": true, "textRandomScope": "words", "textRandomDist": "uniform", "textRandomNth": 3,
1809
+ "textRandomSeed": 7, "textRandomSizeEnabled": true, "textRandomSizeMin": 85, "textRandomSizeMax": 150,
1810
+ "textRandomColorEnabled": true, "textRandomColors": ["#33efab"], "textRandomCoverage": 40, "textRandomWeightEnabled": true,
1811
+ "textRandomWeights": [400, 800], "textRandomItalicEnabled": false, "textRandomItalicMode": "mix", "textRandomItalicShare": 30
1812
+ }
1813
+ ```
1814
+
1815
+ ### Classic Chaos Letters
1816
+
1817
+ Characters flip in with scattered sizes and weights. App id `classic-chaos-letters`. Plays: in, letters 0.03 s apart, 0.5 s each; out at the end, 0.4 s each. Lengths are composition px and stay as written at any font size. Sets the Text Randomizer (`textRandom*`), as the app does when it applies the preset.
1818
+
1819
+ ```json
1820
+ {
1821
+ "textAnimators": [
1822
+ { "aid": "a1", "name": "In",
1823
+ "range": {"basedOn": "chars", "drive": "time", "animate": "in", "stagger": 0.03, "duration": 0.5, "curve": "Smooth", "spread": false},
1824
+ "effects": [{"pid": "p1", "type": "position", "x": 0, "y": 10}, {"pid": "p2", "type": "rotate3d", "x": 90, "y": 0}, {"pid": "p3", "type": "opacity", "opacity": 0}] },
1825
+ { "aid": "a2", "name": "Out",
1826
+ "range": {"basedOn": "chars", "drive": "time", "animate": "out", "stagger": 0.03, "duration": 0.4, "curve": "Smooth", "spread": false},
1827
+ "effects": [{"pid": "p4", "type": "rotate3d", "x": -90, "y": 0}, {"pid": "p5", "type": "opacity", "opacity": 0}] }
1828
+ ],
1829
+ "textRandomEnabled": true, "textRandomScope": "symbols", "textRandomDist": "gaussian", "textRandomNth": 3,
1830
+ "textRandomSeed": 21, "textRandomSizeEnabled": true, "textRandomSizeMin": 70, "textRandomSizeMax": 160,
1831
+ "textRandomColorEnabled": false, "textRandomColors": [], "textRandomCoverage": 35, "textRandomWeightEnabled": true,
1832
+ "textRandomWeights": [300, 700], "textRandomItalicEnabled": true, "textRandomItalicMode": "mix", "textRandomItalicShare": 25
1833
+ }
1834
+ ```
1835
+
1836
+ ### Classic Random Rise
1837
+
1838
+ Masked rise; a wave of size and two colours rolls across the words. App id `classic-random-rise`. Plays: in, words 0.08 s apart, 0.7 s each; out at the end, 0.5 s each. Lengths are composition px and stay as written at any font size, except: In position `y` is the larger of 90 px and 1.15 × font px (115 as written, for a 100 px font); Out position `y` is −(the larger of 90 px and 1.15 × font px) (−115 as written, for a 100 px font). Sets the Text Randomizer (`textRandom*`), as the app does when it applies the preset.
1839
+
1840
+ ```json
1841
+ {
1842
+ "textAnimators": [
1843
+ { "aid": "a1", "name": "In",
1844
+ "range": {"basedOn": "words", "drive": "time", "animate": "in", "mask": true, "stagger": 0.08, "duration": 0.7, "curve": "Smooth", "spread": false},
1845
+ "effects": [{"pid": "p1", "type": "position", "x": 0, "y": 115}] },
1846
+ { "aid": "a2", "name": "Out",
1847
+ "range": {"basedOn": "words", "drive": "time", "animate": "out", "mask": true, "stagger": 0.08, "duration": 0.5, "curve": "Smooth", "spread": false},
1848
+ "effects": [{"pid": "p2", "type": "position", "x": 0, "y": -115}] }
1849
+ ],
1850
+ "textRandomEnabled": true, "textRandomScope": "words", "textRandomDist": "wave", "textRandomNth": 3,
1851
+ "textRandomSeed": 12, "textRandomSizeEnabled": true, "textRandomSizeMin": 90, "textRandomSizeMax": 140,
1852
+ "textRandomColorEnabled": true, "textRandomColors": ["#7aa2ff", "#ff6b81"], "textRandomCoverage": 45, "textRandomWeightEnabled": true,
1853
+ "textRandomWeights": [500, 900], "textRandomItalicEnabled": false, "textRandomItalicMode": "mix", "textRandomItalicShare": 30
1854
+ }
1855
+ ```
1856
+
1857
+ ### Classic Ransom Note
1858
+
1859
+ Cut-out letters: wild sizes, three colours, mixed weights and italics. App id `classic-ransom-note`. Plays: in, letters 0.025 s apart, 0.4 s each. Lengths are composition px and stay as written at any font size. Sets the Text Randomizer (`textRandom*`), as the app does when it applies the preset.
1860
+
1861
+ ```json
1862
+ {
1863
+ "textAnimators": [
1864
+ { "aid": "a1", "name": "In",
1865
+ "range": {"basedOn": "chars", "drive": "time", "animate": "in", "stagger": 0.025, "duration": 0.4, "curve": "Smooth", "spread": false},
1866
+ "effects": [{"pid": "p1", "type": "scale", "scale": 30}, {"pid": "p2", "type": "rotation", "angle": 8}, {"pid": "p3", "type": "opacity", "opacity": 0}] }
1867
+ ],
1868
+ "textRandomEnabled": true, "textRandomScope": "symbols", "textRandomDist": "uniform", "textRandomNth": 3,
1869
+ "textRandomSeed": 33, "textRandomSizeEnabled": true, "textRandomSizeMin": 75, "textRandomSizeMax": 165,
1870
+ "textRandomColorEnabled": true, "textRandomColors": ["#ffd166", "#ff6b81", "#7aa2ff"], "textRandomCoverage": 60, "textRandomWeightEnabled": true,
1871
+ "textRandomWeights": [400, 700, 900], "textRandomItalicEnabled": true, "textRandomItalicMode": "mix", "textRandomItalicShare": 30
1872
+ }
1873
+ ```
1874
+
1875
+ ### Classic Editorial Typer
1876
+
1877
+ Word typing behind a caret, set in a wave of weights, sizes and italics. App id `classic-editorial-typer`. Plays: in, words 0.18 s apart, 0.14 s each; out at the end, 0.25 s each. Lengths are composition px and stay as written at any font size. Sets the Text Randomizer (`textRandom*`), as the app does when it applies the preset.
1878
+
1879
+ ```json
1880
+ {
1881
+ "textAnimators": [
1882
+ { "aid": "a1", "name": "In",
1883
+ "range": {"basedOn": "words", "drive": "time", "animate": "in", "stagger": 0.18, "duration": 0.14, "curve": "Ease Out", "spread": false},
1884
+ "effects": [{"pid": "p1", "type": "position", "x": 0, "y": 12}, {"pid": "p2", "type": "opacity", "opacity": 0}, {"pid": "p3", "type": "caret", "hide": true, "hideAfter": 1}] },
1885
+ { "aid": "a2", "name": "Out",
1886
+ "range": {"basedOn": "words", "drive": "time", "animate": "out", "stagger": 0.18, "duration": 0.25, "curve": "Ease In", "spread": false},
1887
+ "effects": [{"pid": "p4", "type": "opacity", "opacity": 0}] }
1888
+ ],
1889
+ "textRandomEnabled": true, "textRandomScope": "words", "textRandomDist": "wave", "textRandomNth": 3,
1890
+ "textRandomSeed": 5, "textRandomSizeEnabled": true, "textRandomSizeMin": 88, "textRandomSizeMax": 128,
1891
+ "textRandomColorEnabled": false, "textRandomColors": [], "textRandomCoverage": 35, "textRandomWeightEnabled": true,
1892
+ "textRandomWeights": [300, 800], "textRandomItalicEnabled": true, "textRandomItalicMode": "mix", "textRandomItalicShare": 35
1893
+ }
1894
+ ```
1895
+ <!-- CLASSIC:END -->