| 1 | # Page Transitions & Per-Element Animations |
| 2 | |
| 3 | Execution contract for generated-PPTX **page transitions** and **per-element |
| 4 | object animations**, including deterministic Morph object pairing. This file |
| 5 | owns defaults, sidecar semantics, anchor selection, validation, and package |
| 6 | read-back. |
| 7 | |
| 8 | ## Capability Menu — Open Here |
| 9 | |
| 10 | Motion here is several separate capabilities, not one dial. Two of them are |
| 11 | decided **upstream, while pages are still being authored** — read this menu |
| 12 | before the page plan is frozen, not only when a deck is already exported. |
| 13 | |
| 14 | | What the deck needs | Reach for | Decided at | |
| 15 | |---|---|---| |
| 16 | | Reveal content in step with the narration | Per-element object animation — `-a auto` for generic entrance reveals, or an `animations.json` sidecar for explicit enter/emphasize/move/exit/static lifecycle choreography | Post-processing; §2, §4, [`customize-animations`](../workflows/stages/customize-animations.md) | |
| 17 | | A continuous action — slide-in, flip, camera push-in, progressive reveal, camera pan | **Morph: author the action as two static pages, then select Morph and add explicit pairs when identity must be deterministic.** There is no keyframe timeline anywhere in this pipeline; the difference between two ordinary editable slides *is* the animation | **Page authoring (Step 6), then motion post-processing** — §2.1, §3.1 | |
| 18 | | A static full-bleed page that should stop looking frozen | Consider slow `path_*` motion on a visually subordinate image or atmospheric layer; §4.1 gives one starting recipe | Post-processing; §4.1 | |
| 19 | | Carousel, counting numerals, parallax depth, click-to-reveal flip card | Four recurring recipes assembled from the mechanisms above | §4.2 — the carousel and odometer both need paired pages | |
| 20 | | Kiosk or unattended playback | `--auto-advance <seconds>`, optionally with `-t none` | Export; §3 | |
| 21 | | Nothing should move | `-t none`, and leave per-element animation at its default `none` | Export; §1 | |
| 22 | |
| 23 | **Hard rule — Morph geometry is an authoring decision; pairing is a later |
| 24 | execution decision**: export cannot invent the two visible endpoint states. |
| 25 | Author both consecutive pages while `svg_output/` is still being built. For |
| 26 | deterministic identity, expose each endpoint as a compatible direct-root group |
| 27 | and declare the pair in `animations.json` (§2.1); the source and destination ids |
| 28 | and geometry may differ. `-t morph` without explicit pairs leaves matching to |
| 29 | PowerPoint's heuristic and is not proof that the intended objects will tween. |
| 30 | |
| 31 | **Reference — not a constraint**: per-element animation stays off by default |
| 32 | (§1). Auto-firing element builds on every page are an unsolicited "AI deck" |
| 33 | tell; each capability above earns its place per page, not per deck. |
| 34 | |
| 35 | --- |
| 36 | |
| 37 | ## 1. Defaults |
| 38 | |
| 39 | | Layer | Default | Why | |
| 40 | |---|---|---| |
| 41 | | Page transition | CLI: `fade`, 0.4s | Calm baseline that suits most decks; the public Python builder retains its legacy 0.5s default | |
| 42 | | Per-element animation | **`none` (off)** | A page appears as a whole. Auto-firing element builds are an unsolicited "AI deck" tell, so object animation is opt-in. Turn on the content-aware canonical entrance policy with `-a auto`, or select one PowerPoint-native `entrance_*`, `emphasis_*`, `path_*`, or `exit_*` key explicitly | |
| 43 | |
| 44 | To regenerate a deck with different settings, rerun the final checker when its current matching report is absent or stale, then rerun `svg_to_pptx.py` against the same `svg_output/`; the content-generation LLM need not rerun unless authored SVG requires repair. `-s final` is reserved for diagnostic comparison and is not a supported release source. To turn per-element animation on for the whole deck, pass `-a auto`. |
| 45 | |
| 46 | --- |
| 47 | |
| 48 | ## 2. Custom Object-Level Animation |
| 49 | |
| 50 | Per-element animation is off by default. To enable generic entrance reveals |
| 51 | deck-wide, pass `-a auto` at export (no config needed). When a deck instead |
| 52 | needs a specific object lifecycle—for example enter, move, emphasize, then |
| 53 | exit—use the optional `animations.json` sidecar. The SVG remains the visual |
| 54 | source; the custom stage may rewrite its grouping hierarchy, ids, and bounds to |
| 55 | create better semantic anchors without changing visible output, while the |
| 56 | sidecar controls PPTX animation behavior. |
| 57 | |
| 58 | Run the [`customize-animations`](../workflows/stages/customize-animations.md) |
| 59 | post-processing stage when the project already carries `animations.json`, when |
| 60 | the user explicitly asks to tune animation order/effects/timing/object-level |
| 61 | reveals, or when the effective Custom Animations outcome in |
| 62 | `design_spec.md §I` is enabled. A §IX `Motion suggestion` remains Strategist |
| 63 | advice and informs an active pass, but never triggers the stage alone. |
| 64 | |
| 65 | **Hard rule — semantic anchors before object-targeted sidecar entries**: when |
| 66 | object animation is in scope, derive motion units and their lifecycle duties |
| 67 | from page meaning and narration, then regroup coarse/fragmented Slide-local |
| 68 | content without changing its appearance. Only post-regroup top-level ids are |
| 69 | valid object targets. |
| 70 | |
| 71 | ```bash |
| 72 | # Inspect the real anchors after the semantic regrouping pass |
| 73 | python3 skills/ppt-master/scripts/animation_config.py list-groups <project> |
| 74 | |
| 75 | # Build a neutral editable scaffold from the post-regroup anchors when useful |
| 76 | python3 skills/ppt-master/scripts/animation_config.py scaffold <project> |
| 77 | |
| 78 | # Validate references before export |
| 79 | python3 skills/ppt-master/scripts/animation_config.py validate <project> |
| 80 | |
| 81 | # Export reads <project>/animations.json automatically when present |
| 82 | python3 skills/ppt-master/scripts/svg_to_pptx.py <project> |
| 83 | ``` |
| 84 | |
| 85 | The scaffold keeps `defaults.animation.effect: none` and may list untouched |
| 86 | groups as empty `{}` placeholders; creating it does not opt the deck into |
| 87 | object motion. Populate only adopted motion units. |
| 88 | |
| 89 | Sparse sidecar excerpt (unlisted slides inherit resolved defaults): |
| 90 | |
| 91 | ```json |
| 92 | { |
| 93 | "version": 1, |
| 94 | "slides": { |
| 95 | "03_threshold": { |
| 96 | "groups": { |
| 97 | "risk-marker": { |
| 98 | "effects": [ |
| 99 | { "effect": "entrance_fade", "order": 1, "duration": 0.25 }, |
| 100 | { "effect": "path_right", "effect_options": { "relative": true }, "order": 2, "duration": 0.7 }, |
| 101 | { "effect": "emphasis_teeter", "order": 3, "duration": 0.45 }, |
| 102 | { "effect": "exit_fade", "order": 4, "duration": 0.3 } |
| 103 | ] |
| 104 | } |
| 105 | } |
| 106 | } |
| 107 | } |
| 108 | } |
| 109 | ``` |
| 110 | |
| 111 | Rules: |
| 112 | |
| 113 | - `slides` keys match SVG stems (`03_market.svg` → `03_market`). |
| 114 | - `groups` keys match top-level `<g id="...">` anchors. |
| 115 | - A populated group block chooses exactly one representation: the |
| 116 | backward-compatible single-effect object, or |
| 117 | `{ "effects": [row, ...] }`. `effects` is non-empty and mutually exclusive |
| 118 | with every legacy single-effect field; each row explicitly names `effect`. |
| 119 | An untouched scaffold `{}` remains a neutral placeholder. |
| 120 | - `effect: none` in the legacy form removes that group from the object-animation |
| 121 | sequence and is useful for overriding inherited generic animation. |
| 122 | - `effects[]` permits the same PowerPoint shape to carry several Animation Pane |
| 123 | rows. `order` sorts ordinary rows across the slide; ties retain SVG group |
| 124 | order and then array order. `trigger_shape` rows keep that relative ordering |
| 125 | in separate interactive sequences rather than interleaving with the main |
| 126 | sequence. Ordering never changes slide layering. |
| 127 | - `delay` is seconds added to that row's resolved Start. |
| 128 | - `trigger` may be set per legacy row or `effects[]` row; otherwise it inherits |
| 129 | the resolved slide Start mode. |
| 130 | - `trigger_shape` is a row-specific reference to another unique, triggerable |
| 131 | top-level group. It maps to PowerPoint **Trigger → On Click of**, makes only |
| 132 | that row interactive, and uses `delay` as `TriggerDelayTime`. It implies |
| 133 | `on-click`; an explicit row `trigger` may accompany it only when also |
| 134 | `on-click`. |
| 135 | - `duration` overrides the per-row schedule duration. `entrance_appear` |
| 136 | remains a 1ms visibility flip, and instantaneous native emphasis presets |
| 137 | retain their PowerPoint-authored duration; the configured value still spaces |
| 138 | the next `after-previous` row. |
| 139 | - `effect_options` requires an explicit canonical `effect` in the same legacy |
| 140 | block or `effects[]` row and accepts only parameters PowerPoint exposes for |
| 141 | that effect: |
| 142 | |
| 143 | | Option | Applies to | |
| 144 | |---|---| |
| 145 | | `direction` | Directional Fly/Crawl/Wipe/Peek/Strips/Split/Stretch/Zoom and related entrance/exit effects | |
| 146 | | `amount` | Wheel spokes (`1`, `2`, `3`, `4`, `8`), emphasis Spin degrees, or Transparency ratio | |
| 147 | | `color` | Color-capable emphasis effects; `#RRGGBB` or `theme:<scheme-color>` | |
| 148 | | `font_name` | Change Font; required for `emphasis_change_font`; one installed PowerPoint face, not a CSS list | |
| 149 | | `size` | Grow/Shrink | |
| 150 | | `relative` | Motion paths (`true` = shape-relative, `false` = fixed slide path) | |
| 151 | - Any animation block or effect row may set `repeat_count` or `repeat_duration` |
| 152 | (mutually exclusive), `auto_reverse`, `rewind`, `accelerate`, `decelerate`, |
| 153 | `bounce_end`, `restart`, `after_effect`, and `sound`. Ratios are `0..1`; |
| 154 | `bounce_end` requires an interpolated behavior and cannot combine with |
| 155 | `decelerate`; `restart` is `always`, `when-not-active`, or `never`; |
| 156 | `after_effect` is `none`, `dim` (with `color`), `hide`, or |
| 157 | `hide-on-next-click`; `sound` is a project-relative or absolute `.m4a`, |
| 158 | `.mp3`, or `.wav` path. |
| 159 | - `Speed` and smooth start/end are not duplicate sidecar fields: they are |
| 160 | derived from `duration` and `accelerate`/`decelerate`. |
| 161 | - This is the complete parameter surface for the generated top-level-group |
| 162 | target model, including multiple ordered effects on one group. PowerPoint |
| 163 | paragraph/text-range build fields are intentionally absent because grouped |
| 164 | SVG content is not emitted as paragraph builds; media play/pause/stop |
| 165 | commands remain in the audio/video workflows. |
| 166 | - Run `python3 skills/ppt-master/scripts/pptx_animations.py --describe |
| 167 | <canonical_effect>` for that effect's exact option values and full parameter |
| 168 | contract. |
| 169 | - `--animation none` overrides the sidecar and disables all per-element animation. |
| 170 | - An explicit sidecar group may override the legacy chrome-name heuristic, but it cannot override `data-pptx-layer` or an explicit static role/placeholder marker. |
| 171 | - Unknown effects, modes, or triggers and invalid numeric/order fields fail validation; no fallback effect is substituted. |
| 172 | |
| 173 | **Inheritance**: the sidecar and its `defaults` block are optional. Unlisted |
| 174 | slides and omitted slide fields inherit `defaults.transition` / |
| 175 | `defaults.animation`, then CLI/exporter resolution. Explicit CLI flags override |
| 176 | the corresponding sidecar default/slide fields; explicit group overrides remain |
| 177 | unless `-a none` hard-disables all object motion. Groups inherit the resolved |
| 178 | slide duration, Start mode, timing modifiers, after-effect, and sound into each |
| 179 | legacy or `effects[]` row. `effect_options` remains coupled to an explicit |
| 180 | effect; `trigger_shape` is never inherited; omitted `order`/`delay` use |
| 181 | exporter defaults. |
| 182 | |
| 183 | ### 2.1 Deterministic Morph Object Pairing |
| 184 | |
| 185 | When one semantic object continues across two adjacent slides, the destination |
| 186 | slide may declare explicit forced-Morph pairs. This is separate from `groups`: |
| 187 | Morph owns cross-slide identity, while `groups` owns Animation Pane rows. |
| 188 | The generated names follow Microsoft's |
| 189 | [forced object-matching convention](https://support.microsoft.com/en-us/powerpoint/morph-transition-tips-and-tricks). |
| 190 | |
| 191 | ```json |
| 192 | { |
| 193 | "version": 1, |
| 194 | "slides": { |
| 195 | "02_detail": { |
| 196 | "transition": { |
| 197 | "effect": "morph", |
| 198 | "effect_options": { "morph_by": "object" }, |
| 199 | "duration": 0.8 |
| 200 | }, |
| 201 | "morph": { |
| 202 | "from": "01_overview", |
| 203 | "pairs": { |
| 204 | "hero-image": { |
| 205 | "from": "hero-overview", |
| 206 | "to": "hero-detail" |
| 207 | } |
| 208 | } |
| 209 | } |
| 210 | } |
| 211 | } |
| 212 | } |
| 213 | ``` |
| 214 | |
| 215 | - `morph` belongs to the destination slide. `morph.from` must be the |
| 216 | immediately preceding SVG stem in export order. |
| 217 | - `animation_config.py scaffold` never guesses cross-slide identity. Add pairs |
| 218 | from the semantic motion plan after inspecting the final direct-root ids. |
| 219 | - Each `pairs` key is a stable identity; its `from` and `to` values are unique |
| 220 | direct-root `<g id>` values on the source and destination slides. Supply the |
| 221 | key without `!!`; export writes the PowerPoint Selection Pane name |
| 222 | `!!<key>` on both objects. |
| 223 | - A destination with explicit pairs must explicitly set `effect: morph`. |
| 224 | `morph_by` may be omitted for its `object` default or set to `object`; |
| 225 | `word`/`character` are rejected. A CLI transition override that changes the |
| 226 | resolved effect fails export. |
| 227 | - A middle slide may continue the same object into another Morph transition, |
| 228 | but the same group must retain the same key. One key cannot name two objects |
| 229 | on one slide, and one object cannot carry two keys. Every `!!` key shared by |
| 230 | two adjacent Morph pages must be declared in that destination's `pairs`; |
| 231 | undeclared forced matches are rejected. |
| 232 | - Explicit pairing can coexist with in-slide object animation and remains |
| 233 | active when `-a none` disables Animation Pane rows. `--no-animations` |
| 234 | disables the sidecar and all page/object motion. |
| 235 | - The exporter resolves both group ids to final Slide-local PowerPoint shapes, |
| 236 | writes names only after Master/Layout processing, then reopens the package |
| 237 | and verifies adjacency, Morph by object, one name per slide, and matching |
| 238 | OOXML object types. Missing, structural, moved, ambiguous, or mismatched |
| 239 | targets fail instead of falling back to automatic Morph matching. |
| 240 | |
| 241 | --- |
| 242 | |
| 243 | ## 3. Page Transitions |
| 244 | |
| 245 | **Reference — not a constraint**: choose a transition from the relationship |
| 246 | between adjacent pages, not from gallery coverage. Run this playbook before |
| 247 | selecting a canonical key: |
| 248 | |
| 249 | | Pass | Decision | |
| 250 | |---|---| |
| 251 | | Relate | Decide whether the destination continues the same object or space, advances in a meaningful direction, opens a new section, or intentionally breaks continuity. | |
| 252 | | Diagnose | Name the transition's job: neutral continuity, immediate cut, directional progress, object/state continuity, spatial movement, or a deliberate thematic beat. | |
| 253 | | Select | Use the smallest family that performs that job; keep `fade` when no stronger relationship exists. | |
| 254 | | Coordinate | Align direction, duration, and recurrence with reading order, narration, and the deck's established motion language. | |
| 255 | | Stop | Keep `fade` or `none` when another effect adds no meaning; never vary transitions for catalog coverage. | |
| 256 | |
| 257 | | Page relationship | Candidate family | |
| 258 | |---|---| |
| 259 | | Ordinary continuation within one section | `fade` | |
| 260 | | Immediate change with no continuity to preserve | `none` or `cut` | |
| 261 | | Directional steps, timeline, or layer progression | `push` / `wipe`; use `cover` / `uncover` when an overlay relationship is visible | |
| 262 | | The same semantic object or scene changes across adjacent pages | `morph`; use §2.1 pairs when identity must be deterministic | |
| 263 | | Section opening, key reveal, or marked state boundary | Selective `split` / `reveal` / `shape` / `flash` / `random_bars` | |
| 264 | | A repeated collection advances through one spatial frame | `pan` / `conveyor` / `ferris_wheel`; use the §4.2 Morph carousel when individual cards need deterministic identity | |
| 265 | | The viewpoint travels around or through a continuous space | `rotate` / `window` / `orbit` / `fly_through` | |
| 266 | | The narrative or theme supports a stage, paper, or physical-page metaphor | Selective `fall_over` / `drape` / `curtains` / `wind` / `prestige` / `peel_off` / `page_curl` / `airplane` / `origami` / `doors` | |
| 267 | | A disruptive beat represents breakage, collapse, or dispersal | Selective `fracture` / `crush` / `dissolve` / `vortex` / `shred` | |
| 268 | | A marked reveal benefits from a geometric, timed, or textured pattern | Selective `checkerboard` / `blinds` / `clock` / `ripple` / `honeycomb` / `glitter` / `comb` | |
| 269 | | A card, panel, gallery, or viewpoint visibly turns or changes face | Selective `switch` / `flip` / `gallery` / `cube` / `box` / `zoom` | |
| 270 | | Unpredictability is itself the requested behavior | `random`; never use it merely to create variety | |
| 271 | |
| 272 | ```bash |
| 273 | # Pick a different effect |
| 274 | python3 skills/ppt-master/scripts/svg_to_pptx.py <project> -t push --transition-duration 0.6 |
| 275 | |
| 276 | # Remove the visual transition |
| 277 | python3 skills/ppt-master/scripts/svg_to_pptx.py <project> -t none |
| 278 | |
| 279 | # Auto-advance every 5 seconds (kiosk-style playback) |
| 280 | python3 skills/ppt-master/scripts/svg_to_pptx.py <project> --auto-advance 5 |
| 281 | |
| 282 | # Auto-advance with no visual transition |
| 283 | python3 skills/ppt-master/scripts/svg_to_pptx.py <project> -t none --auto-advance 5 |
| 284 | ``` |
| 285 | |
| 286 | The native registry covers PowerPoint's complete Subtle, Exciting, and Dynamic |
| 287 | Content gallery: 48 canonical keys. New selection, sidecars, plans, conversion |
| 288 | traces, and writers use only those keys. Run `pptx_animations.py --list` for |
| 289 | the categorized identifiers. |
| 290 | |
| 291 | Eight old low-level names remain accepted only as compatibility inputs. They |
| 292 | desugar to a native key plus native `effect_options`: for example, `diamond` |
| 293 | becomes `shape` with `shape: diamond`, and `wedge` becomes `clock` with |
| 294 | `style: wedge`. They are never selected for new output. |
| 295 | |
| 296 | Effects expose their real PowerPoint Effect Options through |
| 297 | `transition.effect_options`. Common examples include Push/Wipe direction, |
| 298 | Morph by object/word/character, Reveal through black, Shape geometry, Page |
| 299 | Curl direction/pages, Glitter pattern/direction, and Fly Through bounce. Run |
| 300 | `pptx_animations.py --describe-transition <effect>` for the exact |
| 301 | effect-specific contract; unknown or inapplicable options fail validation. |
| 302 | `none` removes the visual effect. Effects that require newer Office namespaces |
| 303 | carry a real PowerPoint effect in `mc:Choice` and a `fade` fallback for older |
| 304 | consumers; validation requires the requested primary effect and never accepts |
| 305 | the fallback as a silent substitute. |
| 306 | |
| 307 | Flags: |
| 308 | |
| 309 | - `-t/--transition` — native effect name, compatibility input, or `none` for no visual transition. Default: `fade`. `none` does not remove an explicitly configured automatic advance. |
| 310 | - `--transition-duration` — seconds, default `0.4`. |
| 311 | - `--auto-advance` — seconds; click remains enabled, so the slide advances on click or when the timer expires. Omit for presenter-controlled advance. |
| 312 | |
| 313 | **Hard rule — no silent downgrade**: an unknown transition effect, unsupported Effect Option, or invalid/non-finite duration fails export. It is never replaced by `fade`. Recorded narration keeps the resolved visual transition; `-t none --recorded-narration ...` writes narration-driven advance timing without restoring a visual effect. |
| 314 | |
| 315 | ### 3.1 Morph — author an action as the difference between two pages |
| 316 | |
| 317 | Morph tweens objects it can match across consecutive slides. That makes it a general mechanism, not just a transition: **any continuous action can be authored as two static pages plus a Morph transition**, with no keyframe timeline anywhere. Duplicate the page, change one property on one object, and PowerPoint interpolates the rest. Use §2.1 explicit pairs when the match must be deterministic. |
| 318 | |
| 319 | | Change between the two pages | Reads as | |
| 320 | |---|---| |
| 321 | | Object sits off-canvas, then on-canvas | Slide-in, drawer pull, card extending | |
| 322 | | Object rotates | Flip, turn, hinge | |
| 323 | | Image container scales up | Camera push-in | |
| 324 | | Scrim opacity drops, or a cut contour grows | Progressive reveal | |
| 325 | | Same wide image at two `x` offsets | Camera pan (see image-layout-patterns `#C2-01`) | |
| 326 | |
| 327 | Chain three or more pages to build a sequence — extend, hold, retract — where each page is still an ordinary editable slide. |
| 328 | |
| 329 | **Hard rule — matching needs compatible object identity, not identical SVG |
| 330 | geometry**: for generated decks, prefer §2.1 deterministic pairs. The source |
| 331 | and destination direct-root group ids may differ, and position, size, crop, or |
| 332 | other visible state is expected to change; both endpoints must still resolve to |
| 333 | one compatible top-level PowerPoint object kind. Automatic Morph without pairs |
| 334 | is heuristic and may cross-fade instead of tweening. |
| 335 | |
| 336 | **Give text somewhere to come from.** Morph tweens objects present on both pages; text that only exists on the second page can only fade in. The standard fix is to place the *next* page's copy on the current page just outside the canvas (below), and the *previous* page's copy just outside the opposite edge (above). Each block then slides through the frame instead of blinking, and the deck reads as one continuous surface being scrolled. A wholly off-canvas endpoint must be one direct-root `<g id>` with valid `data-pptx-bounds` and `data-pptx-morph-staging="true"`; when Morph remains enabled, pair it explicitly under §2.1. The marker only declares an intentional invisible endpoint; it cannot excuse a partially clipped group or text carrier. |
| 337 | |
| 338 | **When Morph refuses to match**: PowerPoint pairs compatible object kinds; a |
| 339 | shape and a picture will cross-fade instead of tweening. For generated pages, |
| 340 | declare the identity through the destination slide's `morph` block (§2.1). |
| 341 | The exporter writes the shared `!!<key>` name after structure processing and |
| 342 | reads the package back. Do not author `data-pptx-shape-name` for this purpose; |
| 343 | that attribute remains importer metadata for mirror/preserve packages |
| 344 | ([`svg-effects.md`](./svg-effects.md) §6.6). |
| 345 | |
| 346 | **Not supported — Slide Zoom / Summary Zoom.** Click-to-jump navigation built on PowerPoint's Zoom objects (the "click a portrait, zoom into that section" pattern) has no exporter path. Build click-driven navigation with `trigger_shape` on ordinary object animations instead, or with plain hyperlinks. |
| 347 | |
| 348 | **No 3D**: perspective rotation, extrusion, and shear are outside the SVG contract — `skewX` / `skewY` and shear matrices fail closed ([`svg-effects.md`](./svg-effects.md) §6.8). Build the same impression with 2D means — offset, scale, overlap, and per-facet lightness — rather than attempting a 3D tilt. |
| 349 | |
| 350 | --- |
| 351 | |
| 352 | ## 4. Per-Element Animations |
| 353 | |
| 354 | Off by default — enable deck-wide with `-a auto` (or another effect). Once enabled, three Start modes are available — these mirror PowerPoint's animation-pane "Start" dropdown: |
| 355 | |
| 356 | - **`on-click`** — each click reveals the next group. Use only for a controlled semantic reveal; live delivery alone is insufficient. Forbidden with `--recorded-narration`. |
| 357 | - **`with-previous`** — groups start together as one coordinated beat. Stagger ignored. |
| 358 | - **`after-previous`** (default) — click-free cascade on slide entry with `--animation-stagger` spacing. Use when controlled reveals are unnecessary. |
| 359 | |
| 360 | **Default — coherent Start rhythm (may override when a semantic beat needs |
| 361 | different control)**: Keep one dominant deck rhythm and normally one mode per |
| 362 | slide. Mix only for a distinct simultaneous or presenter-controlled beat. |
| 363 | |
| 364 | Enable with `-a auto`, select a canonical effect with |
| 365 | `--animation entrance_fade`, and choose Start behavior with |
| 366 | `--animation-trigger on-click|with-previous|after-previous`. |
| 367 | |
| 368 | PowerPoint's separate **Trigger → On Click of** behavior uses row-specific |
| 369 | `trigger_shape`. It links that row to another top-level group while unlinked |
| 370 | rows keep the slide Start mode; it is not a fourth deck-wide Start mode. |
| 371 | |
| 372 | **Mandatory — lifecycle before effect selection**: start from `static`, then |
| 373 | classify semantic `initial → action → end` before choosing an effect. Generic |
| 374 | staged reveals normally use `enter`; narrower communication jobs select their |
| 375 | matching lifecycle instead. |
| 376 | |
| 377 | | Duty | State contract | Use when | Effect family | |
| 378 | |---|---|---|---| |
| 379 | | `static` | present → hold as reference → present | Motion adds no clarity or intended feeling | No row; legacy `effect: none` only suppresses inheritance | |
| 380 | | `enter` | absent → introduce → present | Information should be withheld, ordered, or revealed with narration | `entrance_*`; modes only for generic reveal | |
| 381 | | `emphasize` | present → redirect attention → present/altered | An already visible object must regain attention or show a local change; never substitute for its first reveal | Explicit `emphasis_*` | |
| 382 | | `move` | state/position A → progress → state/position B | The trajectory carries spatial or causal meaning, or §4.1 adopts subordinate ambient motion; use Morph for cross-page continuity | Explicit `path_*`, or endpoint pages + Morph | |
| 383 | | `exit` | present → retire → absent | The same slide must remove, replace, or make room for content; an ordinary page change needs no object exit | Explicit `exit_*` | |
| 384 | |
| 385 | **Default — restrained entrance-led choreography (may override for content, |
| 386 | tone, or the request)**: Use entrances for ordinary builds. Add emphasis or |
| 387 | exit sparingly, only for a real duty and fitting effect. Multiple `effects[]` |
| 388 | rows require multiple duties. |
| 389 | |
| 390 | The registry exposes two layers: |
| 391 | |
| 392 | - **203 PowerPoint-native object presets**: 53 `entrance_*` presets, 33 |
| 393 | `emphasis_*` effects, 64 `path_*` motion paths, and 53 `exit_*` effects. |
| 394 | Examples include `entrance_bounce`, `emphasis_spin`, `path_circle`, and |
| 395 | `exit_faded_zoom`. Each native key carries the complete PowerPoint-authored |
| 396 | behavior tree, not a generic filter approximation. |
| 397 | - **29 legacy compatibility inputs**, listed by `--list`; new output never |
| 398 | selects them. |
| 399 | |
| 400 | Run the registry command for the exact categorized key list: |
| 401 | |
| 402 | ```bash |
| 403 | python3 skills/ppt-master/scripts/pptx_animations.py --list |
| 404 | ``` |
| 405 | |
| 406 | Compatibility names normalize before selection and writing: for example, |
| 407 | `fade` resolves to `entrance_fade`; every old Fly direction name resolves to |
| 408 | `entrance_fly`; every old Wipe direction name resolves to `entrance_wipe`; and |
| 409 | `cut` resolves to `entrance_appear` because current PowerPoint has no separate |
| 410 | Cut object effect. Directional aliases preserve their old direction through |
| 411 | `effect_options`; legacy `wheel` maps to `entrance_wheel` with four spokes. |
| 412 | These names are accepted only as compatibility inputs. |
| 413 | Automatic selection, new sidecars, conversion traces, and writers use |
| 414 | canonical keys. |
| 415 | |
| 416 | The native keys mirror the object-capable `MsoAnimEffect` surface. The four |
| 417 | media commands—play, pause, stop, and play from bookmark—are not object effects |
| 418 | for SVG groups and remain owned by the audio/video workflows. |
| 419 | |
| 420 | - `auto` handles generic `enter` duties only and maps semantic ids to canonical entrances: charts/tables/timelines use |
| 421 | `entrance_wipe`; cards/steps use `entrance_fly`; titles/takeaways use |
| 422 | `entrance_fade`; image-like ids cycle a richer pool; unmatched ids cycle |
| 423 | fade/wipe/fly/zoom. |
| 424 | - `mixed` (legacy mode name) handles generic `enter` duties only and is |
| 425 | deterministic. The first animated group on each |
| 426 | slide uses `entrance_fade`; later groups cycle through a 16-effect canonical |
| 427 | PowerPoint entrance pool across the deck. The mode name remains compatible; |
| 428 | it no longer selects hand-authored compatibility rows. |
| 429 | - `random` handles generic `enter` duties only and samples from the same |
| 430 | canonical PowerPoint entrance pool. |
| 431 | Resolution is seeded from the effective deck input, so the same input |
| 432 | produces the same choices; `--conversion-trace` records every resolved effect |
| 433 | when diagnostics are enabled. |
| 434 | |
| 435 | `entrance_appear` is excluded from every variation pool because it has no |
| 436 | visible motion. `auto`, `mixed`, and `random` never satisfy an adopted |
| 437 | `emphasize`, `move`, or `exit` duty; those require explicit canonical effects. |
| 438 | |
| 439 | Flags: `-a/--animation` selects effect/mode; `--animation-trigger` selects Start; |
| 440 | `--animation-duration` and `--animation-stagger` control base timing; |
| 441 | `--animation-config` selects a sidecar; `--no-animations` disables page/object |
| 442 | motion but preserves narration audio and recorded advance timing. |
| 443 | |
| 444 | > Note: `--recorded-narration` rejects `on-click` and `trigger_shape`. When either animation sidecar exists, narrated export selects `narration_animations.json`; canonical `animations.json` without that derived file remains a synchronization error. Without sidecars, pass `--inherit-motion-from <base_postflight_report>` for the base deck motion. Pass `--animation-config animations.json` for canonical animation, or `--no-animations` to remove page and object motion. |
| 445 | |
| 446 | ### 4.1 Slow ambient motion — the page that breathes |
| 447 | |
| 448 | **Reference — not a constraint**: ambient motion can keep a static page from |
| 449 | feeling frozen when it remains visually subordinate to the message. A common |
| 450 | starting recipe is `path_left` or `path_right` on a background image, started |
| 451 | `with-previous` and paced much more slowly than a content reveal. The same |
| 452 | principle may suit another atmospheric or non-information-bearing layer. Choose |
| 453 | duration, distance, and moving-object count from the composition and delivery |
| 454 | context. |
| 455 | |
| 456 | Keep a full-bleed moving image covering the canvas at both endpoints; exposing |
| 457 | the slide beneath it is a visible failure. |
| 458 | |
| 459 | It pairs naturally with a fixed foreground: with image-layout-patterns `#M1-07`, the scrim and its cut contour stay locked while the world moves behind the cuts, which reads as looking through windows rather than as a sliding photo. The same logic applies to `#M1-10` and `#P1-09`. |
| 460 | |
| 461 | Motion remains subordinate: avoid competing ambient paths or movement that |
| 462 | reduces the readability of body copy or data. Multiple coordinated layers are |
| 463 | valid when they express one intentional depth or atmosphere relationship. |
| 464 | |
| 465 | ### 4.2 Recurring recipes |
| 466 | |
| 467 | Four combinations that recur constantly in authored decks. Each is built from |
| 468 | mechanisms already defined above — none needs a new capability. |
| 469 | |
| 470 | **Carousel** (Morph, §2.1 and §3.1) — hold a fixed row of card frames and rotate the *content* through them: on each page every image advances one position, so the card at centre changes while the frames stay put. Explicitly pair each moving content unit across adjacent pages; the fixed frames stay static and need no pair. Scales to any number of images with one page each. |
| 471 | |
| 472 | **Odometer / counting numerals** (morph or motion path) — build a vertical strip of digits 0–9 and show one through a fixed window formed by background-filled rectangles above and below ([`image-layout-patterns.md`](./image-layout-patterns.md) `#M1-08`). Shift the strip so the target digit lands in the window, then either morph between two pages or run a `path_up` motion on the strip. A small stagger, such as `0.1s`, can make digit columns settle in sequence; synchronized motion is also valid when it fits the intended rhythm. |
| 473 | |
| 474 | **Parallax depth** (morph) — move a background layer a *short* distance and a foreground layer a longer one between two pages. The differing travel is read as depth. Keep both layers' z-order identical on both pages; a layer that changes stacking between pages breaks the tween and the transition jumps. |
| 475 | |
| 476 | **Flip-card / click-to-reveal** (`trigger_shape`, §4) — pair a face group and a back group at the same position, give the face an exit and the back an entrance, and set the back's `trigger_shape` to the face's id. Clicking the face plays both. This is the supported route for click-driven interaction; PowerPoint's Zoom objects are not (§3.1). |
| 477 | |
| 478 | --- |
| 479 | |
| 480 | ## 5. Anchor Logic — Top-Level `<g id="...">` |
| 481 | |
| 482 | Per-element animations are anchored on **top-level `<g id="...">` content |
| 483 | groups** in the SVG (e.g. `<g id="cover-title">`, `<g id="card-1">`). IDs must |
| 484 | be unique within the page. A backward-compatible single-effect group produces |
| 485 | one Animation Pane row; `effects[]` may produce several ordered rows targeting |
| 486 | the same PowerPoint shape. Each row inherits the slide Start mode unless it |
| 487 | declares its own `trigger`. Nested implementation groups may remain anonymous |
| 488 | because the sidecar does not target them. |
| 489 | |
| 490 | **Hard rule — existing groups are not custom-animation intent**: the |
| 491 | pre-existing SVG hierarchy is implementation evidence, not an authoritative |
| 492 | motion plan. During the custom-animation stage, derive one group per logical |
| 493 | motion unit from claims, comparisons, sequence, causality, and narration beats; |
| 494 | split coarse wrappers and merge fragmented atoms when needed, then use |
| 495 | `list-groups` only after that rewrite. This is also the granularity PowerPoint |
| 496 | uses for group-select / group-move. Do not split or merge units to hit a target |
| 497 | count. |
| 498 | |
| 499 | **Chrome stays static.** `data-pptx-layer` and explicit static |
| 500 | role/placeholder markers are absolute. For marker-free legacy SVGs, chrome-like |
| 501 | ids (background, header/footer, decor, watermark, page number, nav, logo, rule) |
| 502 | are skipped; an explicit sidecar entry may override only this name heuristic. |
| 503 | Keep wrappers and use `effect: none` for static content. |
| 504 | |
| 505 | **Fallback for flat SVGs** (no top-level `<g>` wrappers, only raw `<rect>` / `<text>` / `<path>` at the root): |
| 506 | |
| 507 | - ≤ 8 visible top-level primitives → each becomes one anchor (capped to avoid 70+ atom cascades on dense pages). |
| 508 | - > 8 → animation is skipped on that slide. The slide still renders, just without object animation. |
| 509 | |
| 510 | Executors should wrap logical sections in `<g id>` regardless of whether you plan to animate. [`shared-standards-core.md`](./shared-standards-core.md) requires it. |
| 511 | |
| 512 | --- |
| 513 | |
| 514 | ## 6. Validation and Read-Back |
| 515 | |
| 516 | Animation configuration is strict. Export fails on an unknown effect, mode, or |
| 517 | trigger; invalid timing/order values; a missing slide/group/`trigger_shape` |
| 518 | reference; a self-trigger; or any attempt to animate or trigger from a |
| 519 | structural layer. These errors never downgrade or silently omit a target. |
| 520 | |
| 521 | Generated export reads each slide's timing tree back and checks row count/order, |
| 522 | including repeated rows on one shape, trigger, trigger shape, shape target, |
| 523 | preset class, resolved effect tuple, native behavior signature, duration, and |
| 524 | timeline offset. Package validation then checks root |
| 525 | timing placement, unique and valid `p:cTn` ids, and every `p:spTgt` reference. |
| 526 | Deterministic Morph additionally checks the final adjacent slide parts for the |
| 527 | requested `!!` names, one-to-one uniqueness, compatible object types, and a |
| 528 | real Morph-by-object transition on the destination. |
| 529 | The writer does not emit `p:bldP` for groups or pictures. Direct-PPTX preserve |
| 530 | mode tolerates unchanged legacy group/picture `p:bldP` rows from earlier PPT |
| 531 | Master exports; new generated packages remain strict. |
| 532 | |
| 533 | Narration injection preserves animation and updates both p14 Choice/Fallback |
| 534 | when bounce timing is present; unsupported nested timing fails safely. |
| 535 | Direct-PPTX routes fingerprint source |
| 536 | object-animation timing before and after their allowed edits, then run |
| 537 | structural package validation; they do not author or normalize animation |
| 538 | effects. |
| 539 | |
| 540 | --- |
| 541 | |
| 542 | ## 7. Video Adaptation Contract |
| 543 | |
| 544 | Video renderers consume the resolved conversion trace through |
| 545 | `video_motion_plan.py`, never a raw sidecar or delay-only inference. The plan |
| 546 | locks identity, order, effect, direction, and timing; video may refine only its |
| 547 | declared renderer parameters. Unsupported families fail visibly. See |
| 548 | [`video-motion-plan.md`](../scripts/docs/video-motion-plan.md). |
| 549 | |
| 550 | --- |
| 551 | |
| 552 | ## 8. Limitations |
| 553 | |
| 554 | - Generated animation belongs to the native PPTX built from `svg_output/`. |
| 555 | `svg_final/` is a static preview, and inserting it as one SVG picture does |
| 556 | not create object anchors. |
| 557 | - PowerPoint OOXML is the compatibility target; other presentation apps may |
| 558 | reinterpret individual native behavior trees. |
| 559 | - Direct-PPTX routes preserve unknown transition `AlternateContent`; timing |
| 560 | edits keep Choice and Fallback advance attributes synchronized. |
| 561 | |
| 562 | --- |
| 563 | |
| 564 | ## 9. Implementation References |
| 565 | |
| 566 | See [`svg-pipeline.md`](../scripts/docs/svg-pipeline.md), |
| 567 | [`pptx-transitions.md`](../scripts/docs/pptx-transitions.md), |
| 568 | [`pptx-animations.md`](../scripts/docs/pptx-animations.md), and |
| 569 | [`video-motion-plan.md`](../scripts/docs/video-motion-plan.md). |
| 570 |