pixi-effects 0.20.0 → 0.21.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.
Files changed (45) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/README.md +6 -6
  3. package/ai/SKILL.md +2 -2
  4. package/ai/chat-template.html +3 -3
  5. package/ai/reference/cheatsheet.md +6 -6
  6. package/ai/reference/pitfalls.md +3 -0
  7. package/ai/reference/recipes.md +25 -0
  8. package/ai/template.html +5 -5
  9. package/ai/tools/check.mjs +1 -1
  10. package/dist/Composition-2UFPSNVN.cjs +14 -0
  11. package/dist/{Composition-LL2XXU5N.cjs.map → Composition-2UFPSNVN.cjs.map} +1 -1
  12. package/dist/Composition-BVI236ZR.js +5 -0
  13. package/dist/{Composition-6PA4C4TV.js.map → Composition-BVI236ZR.js.map} +1 -1
  14. package/dist/Controller.d.cts +1 -1
  15. package/dist/Controller.d.ts +1 -1
  16. package/dist/{Movie-CBxGH5ms.d.ts → Movie-CgYwa5QG.d.ts} +2 -0
  17. package/dist/{Movie-CzzJw8A-.d.cts → Movie-mtX9AkQ-.d.cts} +2 -0
  18. package/dist/Presenter.d.cts +1 -1
  19. package/dist/Presenter.d.ts +1 -1
  20. package/dist/{chunk-FFYA3UBU.cjs → chunk-6JJC37ZD.cjs} +216 -38
  21. package/dist/chunk-6JJC37ZD.cjs.map +1 -0
  22. package/dist/{chunk-NO26HWR7.cjs → chunk-BPW42X7C.cjs} +94 -5
  23. package/dist/chunk-BPW42X7C.cjs.map +1 -0
  24. package/dist/{chunk-DQHXVNOI.js → chunk-DDIXIWXK.js} +88 -6
  25. package/dist/chunk-DDIXIWXK.js.map +1 -0
  26. package/dist/{chunk-TSPMKV2F.js → chunk-NGYGFGDS.js} +187 -9
  27. package/dist/chunk-NGYGFGDS.js.map +1 -0
  28. package/dist/index.cjs +30 -29
  29. package/dist/index.cjs.map +1 -1
  30. package/dist/index.d.cts +1 -1
  31. package/dist/index.d.ts +1 -1
  32. package/dist/index.js +5 -4
  33. package/dist/index.js.map +1 -1
  34. package/dist/loader.css +1 -1
  35. package/dist/three.cjs +5 -5
  36. package/dist/three.js +1 -1
  37. package/docs/dsl.md +40 -2
  38. package/llms-full.txt +76 -10
  39. package/package.json +1 -1
  40. package/dist/Composition-6PA4C4TV.js +0 -5
  41. package/dist/Composition-LL2XXU5N.cjs +0 -14
  42. package/dist/chunk-DQHXVNOI.js.map +0 -1
  43. package/dist/chunk-FFYA3UBU.cjs.map +0 -1
  44. package/dist/chunk-NO26HWR7.cjs.map +0 -1
  45. package/dist/chunk-TSPMKV2F.js.map +0 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.21.0
4
+
5
+ **Added**
6
+
7
+ - **Three gallery pieces made by Claude Haiku 5.5** (45 with them; the model name is the authors' own report): `replay-highlight` (a sports replay: live, a wipe, then the same shot in slow motion with a freeze and a drawn-on arc, one composition with its own clock), `piano-roll` (a four-bar tune written as text and falling down a piano roll, the bars and the score built from one array) and `departure-board` (a vertical split-flap station board in Japanese and English). They were made from the docs alone and left as the authors made them; their stumble notes are in `examples/gallery/_notes/`. The gallery lists `haiku` as a fourth model.
8
+ - **Depth of field.** The camera takes `focus` (the plane that is sharp: a `threeD` layer's name or a z) and `aperture` (how shallow; 30 by default, 0 = off): `threeD` layers blur with their distance from the focus, with a disc ("bokeh") blur, and a rack focus is an ordinary keyframe on `focus`. Free when unused. Warnings for a layer name that is not there (with did-you-mean), a layer that is not `threeD`, a focus behind the camera and a crowd of blurred layers; `inspect` reports each layer's blur (`depthBlur`).
9
+ - **A gallery piece for depth of field** (46 now): `rack-focus`, a shallow-depth still life where the lens racks from a badge to the title to a sign that resolves only when the focus lands on it (made by a Claude Sonnet 5.5 subagent from the docs alone; its stumble notes are in `examples/gallery/_notes/`). Playground example 16 is a rack focus too.
10
+
11
+ **Docs** (from those stumble notes, each checked before it was written): where the animatable props go (`initial` / keyframes, not the top level of a shape), what `duration` means on a composition with its own time (with an example), dashes (alternating sub-paths and `trimEnd`), `volume` above 1 for an sfx, the keys of `inspectAudio().windows`, how to find the onsets of a `music` score, `grid: 2` for 3/4, and a measured loudness of a music bed.
12
+
3
13
  ## 0.20.0
4
14
 
5
15
  **Added**
package/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # pixi-effects
2
2
 
3
- > **Status**: experimental — current release `0.20.0`. The API may still change between minor versions (see [CHANGELOG](./CHANGELOG.md): `0.20.0` adds time remap (`speed` and `time` on video, audio-file and composition layers: slow motion, reverse, freeze, a composition with its own clock whose sounds follow it), `cubic-bezier(…)` easing and one gallery piece, `0.19.0` adds spring easing (`ease: 'spring.bouncy'`, `duration: 'auto'`), animated `fillGradient` on shapes and text, a `grain` film-grain filter, warnings for keys that do not exist (they used to be ignored in silence) and two gallery pieces, `0.18.1` makes `animateText` warn when `in` / `out` is given a time, `0.18.0` adds a rebuilt Playground (an editor on the chat template's edit block, a sandboxed run, a problems panel, share links, save as HTML), ten WebMCP tools for an AI agent in the browser, `movie.review()` and `movie.resolveAt()` (and `check` needs a page with 0.18 or newer), `0.17.0` adds part-of-the-movie and draft exports (`range`, `scale`, `draft`), a stronger `check` (`--at`, `--onion`, fonts, loudness and a waveform), `movie.onionSkin()`, a warning for a mistyped `ease`, two cookbook recipes, and fixes text masks and inverted masks, `0.16.3` fixes the export progress bar disappearing while the mouse is still and updates the landing page's speed figures, `0.16.2` adds a landing page, `AGENTS.md` and a one-sentence start for AI, camera flights (`cameraPath`, shake offsets), `trimEach`, the showpiece `ma` and a fix for 3D cards after a seek, `0.16.1` adds `musicBuffer()` and a music lab page with eight tunes, `0.16.0` adds music written as text (`{ type: 'audio', music }`, `musicEnvelope()`), `0.15.0` adds a way in for an AI in a browser chat (`ai/CHAT.md` and `ai/chat-template.html`), `0.14.0` adds `movie.inspectStops()` (and a check review) for stops that land mid-animation, `0.13.1` fixes decks starting past their first stop in the guide and gallery and draws the stop marks as dots, `0.13.0` makes the player bar play a deck stop by stop (`pauseAtStops`), marks the stops more clearly, adds PDF to the download panel and a `pdf` flag on stops, `0.12.0` adds presentations (`stops`, `Presenter`, `deck()`, page overview, presenter view, PDF export) and audio-reactive visuals (`audioEnvelope()`, `react()`), `0.11.0` adds particles, motion blur, a shared loader and a much faster build for pieces with many layers, `0.10.0` adds seeded randomness, `wiggle` and `stagger`, null layers with `parent`, `animateText`, `followPath` and path morphing, and click-to-play in the player, `0.9.0` adds a poster time (`movie.init({ poster })`) and a guide site, `0.8.0` adds events and a theme for building your own player, `0.7.0` adds filters by name, the `pixi-effects-render` and `pixi-effects-view` commands and the timeline chart, `0.6.0` adds draw-on strokes (`trimEnd`) and text that changes over time, `0.5.0` adds synthesised sound effects (`sfx`, no files) and the `check` tool, `0.4.0` makes a mask share its layer's lifetime, `0.3.0` made keyframe `at` relative to the layer).
3
+ > **Status**: experimental — current release `0.21.0`. The API may still change between minor versions; see the [CHANGELOG](./CHANGELOG.md) for what each release changed.
4
4
 
5
- **[Guide →](https://yjmtmtk.github.io/pixi-effects/guide/)** · **[Gallery →](https://yjmtmtk.github.io/pixi-effects/examples/gallery/)** · 42 portfolio pieces written as plain data by AI models · **[Examples →](https://yjmtmtk.github.io/pixi-effects/examples/)** · 15 numbered examples, a music lab + an in-browser playground.
5
+ **[Guide →](https://yjmtmtk.github.io/pixi-effects/guide/)** · **[Gallery →](https://yjmtmtk.github.io/pixi-effects/examples/gallery/)** · 46 portfolio pieces written as plain data by AI models · **[Examples →](https://yjmtmtk.github.io/pixi-effects/examples/)** · 15 numbered examples, a music lab + an in-browser playground.
6
6
 
7
7
  ## Make a video by asking an AI
8
8
 
@@ -51,8 +51,8 @@ Drop the imports into an [importmap](https://developer.mozilla.org/docs/Web/HTML
51
51
  "gsap": "https://esm.sh/gsap@3.12.5",
52
52
  "gsap/PixiPlugin": "https://esm.sh/gsap@3.12.5/PixiPlugin",
53
53
  "mediabunny": "https://esm.sh/mediabunny",
54
- "pixi-effects": "https://cdn.jsdelivr.net/npm/pixi-effects@0.20.0/dist/index.js",
55
- "pixi-effects/controller": "https://cdn.jsdelivr.net/npm/pixi-effects@0.20.0/dist/Controller.js"
54
+ "pixi-effects": "https://cdn.jsdelivr.net/npm/pixi-effects@0.21.0/dist/index.js",
55
+ "pixi-effects/controller": "https://cdn.jsdelivr.net/npm/pixi-effects@0.21.0/dist/Controller.js"
56
56
  }
57
57
  }
58
58
  </script>
@@ -63,7 +63,7 @@ Drop the imports into an [importmap](https://developer.mozilla.org/docs/Web/HTML
63
63
  </script>
64
64
  ```
65
65
 
66
- Load the `dist/` files **as they are** (jsDelivr's `/npm/…/dist/…`, or unpkg's `https://unpkg.com/pixi-effects@0.20.0/dist/index.js`) rather than a CDN-rebundled build such as `esm.sh/pixi-effects` or jsDelivr's `+esm`: the entries (`pixi-effects`, `…/controller`, `…/three`) share internal chunks, which only works when each file is served untouched.
66
+ Load the `dist/` files **as they are** (jsDelivr's `/npm/…/dist/…`, or unpkg's `https://unpkg.com/pixi-effects@0.21.0/dist/index.js`) rather than a CDN-rebundled build such as `esm.sh/pixi-effects` or jsDelivr's `+esm`: the entries (`pixi-effects`, `…/controller`, `…/three`) share internal chunks, which only works when each file is served untouched.
67
67
 
68
68
  > **Using three.js?** Add two more entries (`three` and `pixi-effects/three`) to this importmap — see [Adding three.js](#adding-threejs-optional) below.
69
69
  >
@@ -79,7 +79,7 @@ Add two more entries to the importmap above: three.js itself, and the `pixi-effe
79
79
  "imports": {
80
80
  "...": "(everything from the importmap above)",
81
81
  "three": "https://esm.sh/three@0.178.0",
82
- "pixi-effects/three": "https://cdn.jsdelivr.net/npm/pixi-effects@0.20.0/dist/three.js"
82
+ "pixi-effects/three": "https://cdn.jsdelivr.net/npm/pixi-effects@0.21.0/dist/three.js"
83
83
  }
84
84
  }
85
85
  </script>
package/ai/SKILL.md CHANGED
@@ -32,10 +32,10 @@ You describe a video as a tree of plain objects; the library plays it in a canva
32
32
  - **Anchors**: rect/circle/ellipse are centred on `x,y`; text and image are top-left (use `anchorX/anchorY: 0.5`); compositions use `pivotX/pivotY`. A bar that grows from its base needs `anchorY: 1`.
33
33
  - **Sound effects need no files:** `{ type: 'audio', sfx: 'swoosh', at: 2 }` — presets `click pop swoosh swipe hit riser chime beep coin glitch typewriter`; knobs `pitch`, `brightness`, `seed`. The layer lasts as long as the sound (`duration` stretches it); a `riser` is loudest at its end (`at = hit − duration`), a `swoosh` 0.16 s in. A music file shorter than its layer goes silent — `loop: true`.
34
34
  - **Masks live in the parent's coordinates and don't follow the masked layer.** **`filterArea` is in the layer's own coordinates.**
35
- - **2.5D needs `threeD: true`** on each layer plus a `{ type: 'camera' }` layer; camera props go in `initial`/keyframes; keep every `z` below the camera distance (≈ 989 at 720p, fov 40); under a dolly zoom only `z = 0` stays fixed.
35
+ - **2.5D needs `threeD: true`** on each layer plus a `{ type: 'camera' }` layer; camera props go in `initial`/keyframes; keep every `z` below the camera distance (≈ 989 at 720p, fov 40); under a dolly zoom only `z = 0` stays fixed; **depth of field** is two camera props, `focus` (a threeD layer's name or a z) and `aperture` (30 default, 0 off), and a rack focus is a keyframe on `focus`.
36
36
  - **Group / parent layers = `{ type: 'null', name: 'rig' }`** (draws nothing, only moves) + `parent: 'rig'` on the layers that should follow it: their `x`/`y` are then measured from the null's origin, and the null's position / rotation / scale / alpha carry them (orbits, clock hands, grouped fades). Nulls chain. Only `null` layers can be parents; not for `threeD` layers.
37
37
  - **Time:** `speed` (slow motion, backward) and `time` (a curve of the layer's own seconds: freeze, rewind, ramp) on video, audio-file and composition layers; put what you want to rewind in a composition. Children of such a composition are in ITS seconds. `ease: 'cubic-bezier(.4, 0, .2, 1)'` is the CSS curve. See the recipe `rewind-and-replay` and pitfalls 69–73.
38
- - **Built in:** `animateText()` (per-letter / per-word / per-line entrances and exits in one call: `...animateText('Title', style, { x, y, align: 'center', at: 0.5, duration: 5, in: 'rise', out: 'fade', stagger: { each: 0.05 } })`), `splitText()` / `measureText()` (per-letter / per-word layers, exact positions, by hand), `shape: 'arc'` (progress rings, pie / donut slices: animate `endAngle`), `fillGradient` (linear/radial, alpha stops → vignettes), `{value}` counters, `repeat`/`yoyo`, `orbit()`, seeded randomness (`rand(seed)` / `noise(x, seed)` in expressions, `random(seed)` in JS, never `Math.random()`) `wiggle()` (a seeded shake as keyframes; on a camera put it on `offsetX` / `offsetY` so it rides on the move) `cameraPath()` (a camera flight through `[x, y, z]` points as keyframes; layers it passes: `hideBehindCamera: true`) `music` (a tune with no file: `{ type: 'audio', music: { bpm, tracks: [{ inst: 'keys', notes: 'Cmaj7:4 Am7:4' }, { inst: 'bass', notes: 'c2:2 g1:2' }], drums: { from: 8, kick: 'x...x...' } } }`: notes and chords as text, counted in beats; instruments `keys pluck pad bass sub lead bell musicbox`; `musicEnvelope(music)` feeds `react()`), `audioEnvelope()` / `bpmEnvelope()` + `react()` (visuals that follow music: `const env = await audioEnvelope('music.mp3', { frameRate })` before `movie.init`, then `keyframes: react(env, { duration, props: { scale: { base: 1, amount: 0.4, band: 'bass', release: 0.2 } } })`; `beats: true` pulses on the beats; `bpmEnvelope(120, …)` when there is no file), `deck()` + `Presenter` (a talk as one movie: pages, steps, stops that pause playback, arrow keys / click / swipe to move on: `...deck({ transition: { kind: 'slide', duration: 0.6 }, pages: [{ name, duration, stops, sequences }] })`, `new Presenter(movie, { canvas })` from `pixi-effects/presenter`; G = page overview, P = presenter view with notes and a timer; `pixi-effects-render talk.html -o talk.pdf` = a PDF of the pages), `particles()` (bursts, snow, confetti, sparks as seeded layers: `...particles({ count: 90, at: 2, life: [1, 1.8], area: { x: 640, y: 330 }, speed: [120, 420], gravity: 240, fade: { out: 0.6 } })`), `followPath()` (a layer along an SVG path: `keyframes: followPath({ d, duration, orient: true })`), path morphing (`{ shape: 'path', d, morphTo, initial/keyframes: morph 0 → 1 }`) and `stagger()` (delays for a wave of layers: `stagger(letters, { each: 0.05, from: 'center' })`, or `stagger(n, …)` for numbers; GSAP-style `each` / `amount` / `from` / `grid` / `ease`). **Not in the DSL (hand-roll with a JS loop):** other per-frame curves (waves, spirals: sample them into short linear keyframes), shared masks. (Draw-on strokes `trimEnd`, text that changes over time `set: { text }` / `visibleChars`, shake `wiggle()` and particles are built in.)
38
+ - **Built in:** `animateText()` (per-letter / per-word / per-line entrances and exits in one call: `...animateText('Title', style, { x, y, align: 'center', at: 0.5, duration: 5, in: 'rise', out: 'fade', stagger: { each: 0.05 } })`), `splitText()` / `measureText()` (per-letter / per-word layers, exact positions, by hand), `shape: 'arc'` (progress rings, pie / donut slices: animate `endAngle`), `fillGradient` (linear/radial, alpha stops → vignettes), `{value}` counters, `repeat`/`yoyo`, `orbit()`, seeded randomness (`rand(seed)` / `noise(x, seed)` in expressions, `random(seed)` in JS, never `Math.random()`) `wiggle()` (a seeded shake as keyframes; on a camera put it on `offsetX` / `offsetY` so it rides on the move) `cameraPath()` (a camera flight through `[x, y, z]` points as keyframes; layers it passes: `hideBehindCamera: true`) depth of field (camera `focus` + `aperture`: `{ type: 'camera', initial: { focus: 'title' } }` blurs the other threeD layers by depth; a rack focus is a keyframe on `focus`) `music` (a tune with no file: `{ type: 'audio', music: { bpm, tracks: [{ inst: 'keys', notes: 'Cmaj7:4 Am7:4' }, { inst: 'bass', notes: 'c2:2 g1:2' }], drums: { from: 8, kick: 'x...x...' } } }`: notes and chords as text, counted in beats; instruments `keys pluck pad bass sub lead bell musicbox`; `musicEnvelope(music)` feeds `react()`), `audioEnvelope()` / `bpmEnvelope()` + `react()` (visuals that follow music: `const env = await audioEnvelope('music.mp3', { frameRate })` before `movie.init`, then `keyframes: react(env, { duration, props: { scale: { base: 1, amount: 0.4, band: 'bass', release: 0.2 } } })`; `beats: true` pulses on the beats; `bpmEnvelope(120, …)` when there is no file), `deck()` + `Presenter` (a talk as one movie: pages, steps, stops that pause playback, arrow keys / click / swipe to move on: `...deck({ transition: { kind: 'slide', duration: 0.6 }, pages: [{ name, duration, stops, sequences }] })`, `new Presenter(movie, { canvas })` from `pixi-effects/presenter`; G = page overview, P = presenter view with notes and a timer; `pixi-effects-render talk.html -o talk.pdf` = a PDF of the pages), `particles()` (bursts, snow, confetti, sparks as seeded layers: `...particles({ count: 90, at: 2, life: [1, 1.8], area: { x: 640, y: 330 }, speed: [120, 420], gravity: 240, fade: { out: 0.6 } })`), `followPath()` (a layer along an SVG path: `keyframes: followPath({ d, duration, orient: true })`), path morphing (`{ shape: 'path', d, morphTo, initial/keyframes: morph 0 → 1 }`) and `stagger()` (delays for a wave of layers: `stagger(letters, { each: 0.05, from: 'center' })`, or `stagger(n, …)` for numbers; GSAP-style `each` / `amount` / `from` / `grid` / `ease`). **Not in the DSL (hand-roll with a JS loop):** other per-frame curves (waves, spirals: sample them into short linear keyframes), shared masks. (Draw-on strokes `trimEnd`, text that changes over time `set: { text }` / `visibleChars`, shake `wiggle()` and particles are built in.)
39
39
  - **Blend modes:** `blendMode: 'add' | 'screen' | 'multiply'` on any layer (glows, light leaks).
40
40
  - **One `Movie` per page.** A sound effect needs no `duration` (it lasts as long as its clip). Seeking is safe: counters, colours and growing shapes are right after any jump.
41
41
 
@@ -61,9 +61,9 @@
61
61
  "gsap": "https://cdn.jsdelivr.net/npm/gsap@3.12.5/+esm",
62
62
  "gsap/PixiPlugin": "https://cdn.jsdelivr.net/npm/gsap@3.12.5/PixiPlugin/+esm",
63
63
  "mediabunny": "https://cdn.jsdelivr.net/npm/mediabunny@1.61.3/+esm",
64
- "pixi-effects": "https://cdn.jsdelivr.net/npm/pixi-effects@0.20.0/dist/index.js",
65
- "pixi-effects/controller": "https://cdn.jsdelivr.net/npm/pixi-effects@0.20.0/dist/Controller.js",
66
- "pixi-effects/presenter": "https://cdn.jsdelivr.net/npm/pixi-effects@0.20.0/dist/Presenter.js"
64
+ "pixi-effects": "https://cdn.jsdelivr.net/npm/pixi-effects@0.21.0/dist/index.js",
65
+ "pixi-effects/controller": "https://cdn.jsdelivr.net/npm/pixi-effects@0.21.0/dist/Controller.js",
66
+ "pixi-effects/presenter": "https://cdn.jsdelivr.net/npm/pixi-effects@0.21.0/dist/Presenter.js"
67
67
  }
68
68
  }
69
69
  </script>
@@ -16,7 +16,7 @@ A video is **data**: a tree of plain objects. Everything below is a JS object yo
16
16
  | Time of a keyframe's `at` | seconds from the start of **its own layer**; **negative = back from the layer's end** |
17
17
  | Time of a transition's `at` | seconds from the start of the **parent composition**; negative = back from its end |
18
18
  | `duration` | defaults to the parent composition's duration |
19
- | Time **inside** a layer | `speed` (a multiplier on the layer: 1 = as is, 2 = twice as fast, 0.5 = slow motion, **negative = backward**) and `time` (seconds of the layer's own content, in `initial` / keyframes: a video's or audio file's position in the file, a composition's local playhead; **negative = back from the end of the content**) on **video, audio file and composition** layers. **The layers inside a composition write `at` / `duration` / `keyframes` in THAT composition's own seconds** (its local time); the composition's own `at`, `duration` and keyframes stay in the outer time. `duration` is always the layer's life in the outer time |
19
+ | Time **inside** a layer | `speed` (a multiplier on the layer: 1 = as is, 2 = twice as fast, 0.5 = slow motion, **negative = backward**) and `time` (seconds of the layer's own content, in `initial` / keyframes: a video's or audio file's position in the file, a composition's local playhead; **negative = back from the end of the content**) on **video, audio file and composition** layers. **The layers inside a composition write `at` / `duration` / `keyframes` in THAT composition's own seconds** (its local time); the composition's own `at`, `duration` and keyframes stay in the outer time. `duration` is always the layer's life in the outer time (`{ duration: 4, speed: 2 }` lives 4 s in the movie and plays 8 s of content; its children, with no `duration` of their own, last those 8 s) |
20
20
 
21
21
  ## Expressions
22
22
 
@@ -43,7 +43,7 @@ threeD: true // opt into 2.5D (see below)
43
43
  blendMode: 'normal' | 'add' | 'screen' | 'multiply' // add / screen brighten (glows, light leaks: overlapping halos add up); on a composition its children inherit the mode
44
44
  ```
45
45
 
46
- Animatable props (`initial` / keyframes): `x y alpha rotation scale scaleX scaleY pivotX pivotY anchorX anchorY skewX skewY tint width height visible autoAlpha`, plus for shapes their style/geometry, for text `fill`, for audio `volume`, for 3D `z rotationX rotationY`, filter params as `'filters.<name>.<param>'`, three objects as `'three.<obj>.<path>'`.
46
+ Animatable props (`initial` / keyframes): `x y alpha rotation scale scaleX scaleY pivotX pivotY anchorX anchorY skewX skewY tint width height visible autoAlpha`, plus for shapes their style/geometry, for text `fill`, for audio `volume`, for 3D `z rotationX rotationY`, filter params as `'filters.<name>.<param>'`, three objects as `'three.<obj>.<path>'`. **These go in `initial` / keyframes, never at the top level of a shape or text layer** (top-level `x`, `rotation`, `strokeColor`, `fillAlpha` each warn "is not a … key"): the top level of a shape holds its geometry (`width height radius d startAngle endAngle cornerRadius anchorX anchorY`), `trimStart` / `trimEnd`, `strokeCap` / `strokeJoin`, `fillGradient`.
47
47
 
48
48
  Keyframe kinds: `set` (jump at `at`; undone when you seek back), `to` (animate to), `from` (animate from), `from`+`to`. `from` / `from+to` show their start value from the layer's start, so a delayed fade-in needs no `initial.alpha`. `repeat` (finite extra plays), `yoyo`, `repeatDelay` loop any of them (endless repeats are not allowed). `ease` = any GSAP ease (`'none'`, `'power2.out'`, `'expo.out'`, `'back.out(1.7)'`, `'elastic.out(1,0.5)'`, `'sine.inOut'`, `'bounce.out'`, **`'cubic-bezier(.4, 0, .2, 1)'`** (CSS syntax, x1 and x2 in 0…1; `.4, 0, .2, 1` is a gentle in-out; `.2, .8, .2, 1` is FRONT-LOADED, not slow motion: half of the change in the first 13 % of the time, 90 % by 39 %, 99 % by 74 %, then a long settle, so what sits at the END of the content gets the glide; `.2, .8, .5, 1` has a shorter tail). GSAP's `power1…4` are the powers 2…5: `power2` is a cubic, `power2.in` has done only 12.5 % at half time (42 % at three quarters)… and springs: `'spring(mass, stiffness, damping)'` or `'spring.gentle' | 'snappy' | 'bouncy' | 'wobbly' | 'slow'`; **`duration: 'auto'` = the time the spring takes to settle** (presets, settle time / highest value reached as a multiple of the target: `snappy` 0.42 s / 1.01, `gentle` 0.72 s / 1.02, `bouncy` 1.08 s / 1.28, `slow` 1.29 s / 1.00, `wobbly` 1.61 s / 1.40: time the next beat from these) (real physics; with a number `duration` is the settle time and only the damping ratio shapes it; a damping ratio of 1 or more never overshoots, so `spring(1,170,26)` does not bounce: use damping below 20 at stiffness 170; `'auto'` works on keyframes only, not in `animateText` / `orbit` tweens; a `stagger` ease may be a spring, its delays are held inside the spread). Default ease is linear.
49
49
 
@@ -56,7 +56,7 @@ Keyframe kinds: `set` (jump at `at`; undone when you seek back), `to` (animate t
56
56
  **video** — `asset`, `loop`, `audio`, `volume`, **`speed`**; `initial: { scale: 'cover' }`. `speed: 0.5` slow motion, `speed: -1` backward (it starts at |speed| × `duration`, so a 3 s file with no `duration` plays whole, backward); with no `duration` the layer lasts the file's length ÷ |speed|. For anything that is not one straight speed — a freeze, a ramp, a loop — animate **`time`** (seconds in the file) like any property: `keyframes: [{ at: 0, from: { time: 0 }, to: { time: 2 }, duration: 1 }, { at: 1, to: { time: 2 }, duration: 0.5 }, { at: 1.5, to: { time: 6 }, duration: 3, ease: 'power2.inOut' }]` (play 2 s, hold, then accelerate). `repeat` loops it, `yoyo` ping-pongs. `speed` itself is not animatable (it warns): animate `time`. The sound follows (pitch too, like a tape).
57
57
 
58
58
  **audio** — a file: `asset`, `loop`, `volume`, `speed` (the pitch follows; `time` keyframes work as for video; not for `sfx` / `music`: change an sfx's `pitch` instead, or put it in a composition and remap that); `volume` (top level, or in `initial` like every other layer's starting values); fade with volume keyframes (`{ at: -2, to: { volume: 0 }, duration: 2 }` fades the last 2 s). Omit `duration` for a one-shot clip: it lasts exactly as long as the clip. With `loop: true` and no `duration` it lasts until the composition ends. An explicit `duration` longer than the clip goes silent after the clip unless `loop: true`.
59
- **Or a sound effect with no file — `sfx`:** `{ type: 'audio', sfx: 'swoosh', at: 2 }` is the whole thing. Presets, default length in s: `click` 0.04 · `pop` 0.12 · `swoosh` 0.5 (loudest 0.16 s in, moves left → right) · `swipe` 0.22 · `hit` 0.7 · `riser` 1 (loudest at its END: `at = hit − duration`) · `chime` 1.4 · `beep` 0.16 · `coin` 0.4 · `glitch` 0.35 · `typewriter` 0.06. Knobs: `sfx: { preset, pitch /* semitones ±24 */, brightness /* −1 dark … 1 bright */, seed /* another take */ }`. **The layer's `duration` IS the sound's length** (omit it; set it to stretch, e.g. a riser; at most 10 s); `volume` is its level (1 = the preset's standard level, peaking at −12 … −18 dBFS by preset). No `loop`: one layer per hit (a JS loop). Custom sound: `sfx: { voices: [{ wave: 'noise' | 'sine' | 'square' | 'saw' | 'triangle', freq: 440 | [from, to], filter: { type: 'lowpass' | 'highpass' | 'bandpass', freq: [from, to] }, envelope: 'fall' | 'bell' | 'swell' | 'hold', pan: [-1, 1] }] }`. The export (mp4 / webm / mov) encodes this same mix (lossy AAC / Opus), on time within a few ms.
59
+ **Or a sound effect with no file — `sfx`:** `{ type: 'audio', sfx: 'swoosh', at: 2 }` is the whole thing. Presets, default length in s: `click` 0.04 · `pop` 0.12 · `swoosh` 0.5 (loudest 0.16 s in, moves left → right) · `swipe` 0.22 · `hit` 0.7 · `riser` 1 (loudest at its END: `at = hit − duration`) · `chime` 1.4 · `beep` 0.16 · `coin` 0.4 · `glitch` 0.35 · `typewriter` 0.06. Knobs: `sfx: { preset, pitch /* semitones ±24 */, brightness /* −1 dark … 1 bright */, seed /* another take */ }`. **The layer's `duration` IS the sound's length** (omit it; set it to stretch, e.g. a riser; at most 10 s); `volume` is its level (1 = the preset's standard level, peaking at −12 … −18 dBFS by preset; above 1 is allowed: a sparse track of clicks measured −32 LUFS at 0.5 and needed about 2 to reach −24 LUFS). No `loop`: one layer per hit (a JS loop). Custom sound: `sfx: { voices: [{ wave: 'noise' | 'sine' | 'square' | 'saw' | 'triangle', freq: 440 | [from, to], filter: { type: 'lowpass' | 'highpass' | 'bandpass', freq: [from, to] }, envelope: 'fall' | 'bell' | 'swell' | 'hold', pan: [-1, 1] }] }`. The export (mp4 / webm / mov) encodes this same mix (lossy AAC / Opus), on time within a few ms.
60
60
  **Or music with no file — `music`:** `{ type: 'audio', music: { bpm: 96, tracks: [{ inst: 'keys', notes: 'Cmaj7:4 Am7:4 Dm7:4 G7:4' }, { inst: 'bass', notes: 'c2:2 g1:2 …' }], drums: { kick: 'x...x...', snare: '....x...', hat: 'x.x.x.x.' } } }` — a tune written as text (notes `c4:2` in beats, chords `Am7` `C@4` `Am7/e`, `[c4 e4 g4]`, rest `_`, hold `~`, `!` accent; `|` ignored). Instruments `keys pluck pad bass sub lead bell musicbox`; drums `kick snare hat openhat clap rim tom crash shaker sleigh` as 16th-note step strings (`x` hit, `o` soft, `.` none; a list of `{ from, to, … }` for sections). Score options `bpm` (or `[[beat, bpm], …]` for a ritardando) `swing reverb (0–0.6) humanize drumVol grid transpose tail seed`; track options `vol` (or `[[beat, level], …]` for a fade) `pan step strum legato tone reverb attack release ring transpose`. Lasts its notes + tail, ends with the movie, `loop: true` repeats; peaks −6 dBFS at `volume: 1`. `await musicEnvelope(music, { frameRate })` → an envelope for `react()`; `await musicBuffer(music)` → a Web Audio `AudioBuffer` to play yourself.
61
61
 
62
62
  **shape** — `shape: 'rect' | 'circle' | 'ellipse' | 'arc' | 'line' | 'polygon' | 'path'`:
@@ -71,7 +71,7 @@ Keyframe kinds: `set` (jump at `at`; undone when you seek back), `to` (animate t
71
71
  | polygon | `points: [[x,y],…] open` — canvas coordinates like `line` |
72
72
  | path | `d` (SVG path data) — canvas coordinates like `line`. **Morph:** `morphTo: 'M…Z'` + animate `morph` 0 → 1 (points half way between, closed outlines lined up so they do not twist; `morphPoints` 8–2048) |
73
73
 
74
- **Draw-on:** every shape but `arc` has `trimStart` / `trimEnd` (0–1 of the outline, default 0 / 1, animatable): `trimEnd: 0` + keyframe `to: { trimEnd: 1 }` draws a line / border / path on; `trimStart` 0 → 1 after it wipes it off; both = a travelling dash. The fill is NOT trimmed. Rect outline starts top-left, circle / ellipse at 12 o'clock, both clockwise. A `path` with several sub-paths walks them in order as one length; `trimEach: true` trims each on its own (all draw on together). Use `strokeCap: 'round'`.
74
+ **Draw-on:** every shape but `arc` has `trimStart` / `trimEnd` (0–1 of the outline, default 0 / 1, animatable): `trimEnd: 0` + keyframe `to: { trimEnd: 1 }` draws a line / border / path on; `trimStart` 0 → 1 after it wipes it off; both = a travelling dash. The fill is NOT trimmed. Rect outline starts top-left, circle / ellipse at 12 o'clock, both clockwise. A `path` with several sub-paths walks them in order as one length; `trimEach: true` trims each on its own (all draw on together). Use `strokeCap: 'round'`. **Dashes:** there is no dash option; write the outline as alternating sub-paths (`d: 'M 0 0 L 10 0 M 20 0 L 30 0 …'`, or sample the curve) and `trimEnd` 0 → 1 walks them in order, so the dashes draw on one after another (as in `replay-highlight`).
75
75
 
76
76
  Style (`initial` / keyframes): `fillColor fillAlpha strokeColor strokeAlpha strokeWidth`; `colorSpace: 'oklab'|'oklch'` for clean colour tweens. rect/circle/ellipse are **centred on `x,y` by default** (`anchorX/anchorY` default 0.5): for a bar growing from its base use `anchorY: 1` (or `anchorX: 0` for left-to-right). **`fillGradient`** (top level or `initial`; instead of `fillColor`): `{ type?: 'linear'|'radial', stops: [[0, '#000'], [1, 'rgba(0,0,0,.6)']], angle? /* linear, deg, 90 = top→bottom */, center?, innerRadius?, radius? /* radial, 0–1 of the SHAPE's own bounds, not the canvas: a glow on a 1500 px circle is placed with fractions of that circle */ }` — stops may have alpha, so a radial transparent→dark is a vignette. **Animate it** with a partial gradient in a keyframe (`to: { fillGradient: { angle: 360, stops: [...] } }`: keys you leave out stay; the same number of stops; `type` cannot change; a start gradient is required; use `colorSpace: 'oklch'` for a vivid fade). A text layer takes `fillGradient` too (LINEAR only: angle and stops; type / center / radius are ignored with a warning; letters re-rasterised per change). `from` alone runs from that gradient to the layer's own. The names `'fillGradient.angle'` / `gradientAngle` are not properties: write `fillGradient: { angle }`. Stroke ends and corners: `strokeCap: 'butt'|'round'|'square'`, `strokeJoin: 'miter'|'round'|'bevel'` (top level or in `initial`; not animated — `round` keeps sharp corners like an M from poking out).
77
77
 
@@ -79,7 +79,7 @@ Style (`initial` / keyframes): `fillColor fillAlpha strokeColor strokeAlpha stro
79
79
 
80
80
  **null + `parent`** — `{ type: 'null', name: 'rig', initial: { x, y }, keyframes: [...] }` draws nothing; layers with `parent: 'rig'` are drawn inside it and follow its `x y rotation scale skew alpha pivotX pivotY`. Their `x / y` are measured **from the null's origin** (null at (640,360), child `x: 200` → drawn at 840), so rotating the null makes the child circle it. Nulls chain (`parent` on a null). Children are drawn at the null's place in the stack, together. Only `null` layers can be parents; not for `threeD` layers; a mask follows its layer.
81
81
 
82
- **camera** (2.5D) — props in `initial`/keyframes: `x y z lookAtX lookAtY lookAtZ fov` (defaults: centred, looking at the z = 0 plane, `fov` 40, `z` auto = `(H/2)/tan(fov/2)` ≈ 989 at 720p). Never on the camera object itself. An orbit is `orbit()`; by hand: `x = cx + R·sin θ`, `z = R·cos θ`, `R = (H/2)/tan(fov/2)`, `lookAt` = the centre at `z = 0`.
82
+ **camera** (2.5D) — props in `initial`/keyframes: `x y z lookAtX lookAtY lookAtZ fov focus aperture` (defaults: centred, looking at the z = 0 plane, `fov` 40, `z` auto = `(H/2)/tan(fov/2)` ≈ 989 at 720p). **Depth of field:** `focus` = the sharp plane (a `threeD` layer's name = its first z, or a z number), `aperture` = the lens diameter in px (30 default, 0 off); writing either blurs the other `threeD` layers by their distance from the focus (a rack focus is a keyframe on `focus`; recipe `rack-focus`; `inspect` reports `depthBlur`). Never on the camera object itself. An orbit is `orbit()`; by hand: `x = cx + R·sin θ`, `z = R·cos θ`, `R = (H/2)/tan(fov/2)`, `lookAt` = the centre at `z = 0`.
83
83
 
84
84
  **three** (optional entry `pixi-effects/three`) — `three({ type:'three', width, height, setup(ctx) { …; return { objects: { knot } } }, update?, dispose? })`; drive with `'three.knot.rotation.y'` keyframes. Call `registerThree()` before `init`.
85
85
 
@@ -174,6 +174,6 @@ await movie.onionSkin({ from: 1, to: 3, count: 8, as: 'dataURL' }) // frames ov
174
174
  movie.review({ at: 'title@end' }) // what `pixi-effects-check` looks at, as data: { frames, problems (text cut off, off-canvas, no size), review (text overlaps: look at them), fonts, audio, at }; leaves the movie as it was. `strict: true` makes every issue a problem
175
175
  movie.resolveAt('3.5, 50%, f120, title@end') // moments → [{ label, frame }]; a mistake says what could not be read
176
176
  movie.inspectFonts() // { missing: [{ layer, family }] (no font of the list is installed), failed: [web fonts whose file did not load] }
177
- movie.inspectAudio() // the soundtrack as numbers; also loudness { integratedLufs, truePeakDb, clippedSamples, silences }, scenes, cues, notes (advice: -14 to -16 LUFS is typical for web video) — — you cannot listen: sources[i] = { layer, start, end, peakDb, sound: { length, peakDb, loudestAt, brightnessHz } } (`peakDb` is the MIX's peak between `start` and `end`, not the layer's own: `sound.peakDb` is the sound's; for a sound inside a remapped composition `start` / `end` span every pass it is heard, forward, backward, replayed, in the movie's seconds), windows (rms / peak / brightness per 0.1 s), issues (limited mix, inaudible / cut-off sounds)
177
+ movie.inspectAudio() // the soundtrack as numbers; also loudness { integratedLufs, truePeakDb, clippedSamples, silences }, scenes, cues, notes (advice: -14 to -16 LUFS is typical for web video) — — you cannot listen: sources[i] = { layer, start, end, peakDb, sound: { length, peakDb, loudestAt, brightnessHz } } (`peakDb` is the MIX's peak between `start` and `end`, not the layer's own: `sound.peakDb` is the sound's; for a sound inside a remapped composition `start` / `end` span every pass it is heard, forward, backward, replayed, in the movie's seconds), windows (`{ t, rmsDb, peakDb, brightnessHz }` per 0.1 s), issues (limited mix, inaudible / cut-off sounds)
178
178
  new Controller(movie, { canvas }) // optional player bar (overlays the canvas bottom ~60px; not in the export)
179
179
  ```
@@ -101,3 +101,6 @@ A good habit that catches most of these: after `init`, read `window.__logs` (see
101
101
  71. **Sound follows `speed` like a tape: the pitch moves with it.** An audio file or a video's sound at `speed: 2` is an octave up; backward plays reversed. Not for `sfx` / `music` layers (warns): change an sfx with `pitch` (semitones), or put the sfx **inside a remapped composition** and it follows that clock (slower and lower, or backward).
102
102
  72. **A video at `speed: 2` with no `duration` is half as long; with a `duration` longer than the file it holds the last frame** (add `loop: true` to repeat). A negative `time` counts back from the end of the file (`initial: { time: -2 }` = two seconds before the end).
103
103
  73. **Know the shape of the curve before you time a beat to it.** `cubic-bezier(.2, .8, .2, 1)` does half of its change in the first 13 % of the time and 99 % by 74 % (not slow motion: over 6 s the first 2 s of content pass in 0.6 s); `.2, .8, .5, 1` has a shorter tail. GSAP's `power2` is a cubic (`power1` = t², `power3` = t⁴): `power2.in` has done 12.5 % at half time and 42 % at three quarters, so a rewind with it dismantles most of the title in the last third.
104
+ 74. **`focus` as a layer name means that layer's FIRST `z`.** The camera does not follow a layer whose `z` moves (it warns): to follow it, animate `focus` itself with numbers. A name no layer has, or a layer that is not `threeD`, warns and the `z = 0` plane is used (did-you-mean for typos).
105
+ 75. **Depth of field blurs `threeD` layers only, and the layer's own depth decides it.** A 2D layer never blurs; a `threeD` layer at the focus depth stays sharp; the depth is measured along the camera's view direction, so with an orbiting or tilted camera a layer off to the side of the look-at point is a little deeper than its `z` says and blurs a little even at the focus `z`; one blur for the whole layer, so a tilted floor is one blur (split it into layers).
106
+ 76. **`aperture` is the lens diameter in px, not a 0–1 strength.** 30 is the default (clearly shallow), 0 turns it off, 60 is strong, 100 is extreme (small bright objects speckle); the blur never exceeds 32 px. 20 or more blurred layers at once warns (one filter pass each, more on WebGPU).
@@ -690,6 +690,31 @@ return { duration: 8, sequences: [
690
690
 
691
691
  ---
692
692
 
693
+ ## A rack focus (depth of field)
694
+
695
+ `focus` and `aperture` on the camera blur the `threeD` layers by how far they are from the sharp plane; the layer named in `focus` stays sharp. Writing `focus` alone is enough (`aperture` defaults to 30; 0 turns it off). A rack focus is a normal keyframe on `focus`, and a layer's name works there too (it means that layer's first `z`). Keep `aperture` at 30–60: 100 is extreme.
696
+
697
+ ```js
698
+ // @recipe rack-focus
699
+ const word = (name, text, x, z, fill) => ({
700
+ type: 'text', name, text, threeD: true, style: { fontSize: 120, fill, fontFamily: 'sans-serif', fontWeight: '700' },
701
+ initial: { x, y: 360, z, anchorX: 0.5, anchorY: 0.5 },
702
+ });
703
+ return { duration: 5, sequences: [
704
+ { type: 'shape', shape: 'rect', width: 'GW', height: 'GH', initial: { x: 'GW/2', y: 'GH/2', fillColor: '#101626' } },
705
+ { type: 'camera', initial: { focus: 'near', aperture: 40 },
706
+ keyframes: [
707
+ { at: 1, to: { focus: 'far' }, duration: 1, ease: 'power2.inOut' }, // near -> far
708
+ { at: 3, to: { focus: 'near' }, duration: 1, ease: 'power2.inOut' }, // and back
709
+ ] },
710
+ word('far', 'FAR', 960, -600, '#6ec6ff'),
711
+ word('mid', 'MID', 640, -200, '#ffd166'),
712
+ word('near', 'NEAR', 320, 200, '#ef476f'),
713
+ ] };
714
+ ```
715
+
716
+ ---
717
+
693
718
  ## Looping motion
694
719
 
695
720
  `repeat` (a finite count) and `yoyo` go on any keyframe. Total time = `duration × (repeat + 1)`.
package/ai/template.html CHANGED
@@ -4,7 +4,7 @@
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1" />
6
6
  <title>pixi-effects video</title>
7
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/pixi-effects@0.20.0/dist/loader.css" /> <!-- the loader: HTML + CSS only, so it is on screen at once and keeps moving while the page builds the video -->
7
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/pixi-effects@0.21.0/dist/loader.css" /> <!-- the loader: HTML + CSS only, so it is on screen at once and keeps moving while the page builds the video -->
8
8
  <style>
9
9
  body { background: #0d1220; margin: 0; padding: 16px; }
10
10
  .stage { position: relative; width: min(960px, 100%); } /* the player and the loader both sit inside this box */
@@ -39,10 +39,10 @@
39
39
  "gsap": "https://esm.sh/gsap@3.12.5",
40
40
  "gsap/PixiPlugin": "https://esm.sh/gsap@3.12.5/PixiPlugin",
41
41
  "mediabunny": "https://esm.sh/mediabunny",
42
- "pixi-effects": "https://cdn.jsdelivr.net/npm/pixi-effects@0.20.0/dist/index.js",
43
- "pixi-effects/controller": "https://cdn.jsdelivr.net/npm/pixi-effects@0.20.0/dist/Controller.js",
44
- "pixi-effects/presenter": "https://cdn.jsdelivr.net/npm/pixi-effects@0.20.0/dist/Presenter.js",
45
- "pixi-effects/three": "https://cdn.jsdelivr.net/npm/pixi-effects@0.20.0/dist/three.js",
42
+ "pixi-effects": "https://cdn.jsdelivr.net/npm/pixi-effects@0.21.0/dist/index.js",
43
+ "pixi-effects/controller": "https://cdn.jsdelivr.net/npm/pixi-effects@0.21.0/dist/Controller.js",
44
+ "pixi-effects/presenter": "https://cdn.jsdelivr.net/npm/pixi-effects@0.21.0/dist/Presenter.js",
45
+ "pixi-effects/three": "https://cdn.jsdelivr.net/npm/pixi-effects@0.21.0/dist/three.js",
46
46
  "pixi-filters": "https://esm.sh/pixi-filters@6.1.5?external=pixi.js",
47
47
  "three": "https://esm.sh/three@0.178.0"
48
48
  }
@@ -138,7 +138,7 @@ export class Cdp {
138
138
  export async function launchChrome(chrome, userDataDir, extraArgs = []) {
139
139
  const args = [
140
140
  '--headless=new', '--remote-debugging-port=0', `--user-data-dir=${userDataDir}`, '--no-first-run', '--no-default-browser-check',
141
- '--autoplay-policy=no-user-gesture-required', '--mute-audio', '--ignore-gpu-blocklist', '--enable-unsafe-swiftshader', '--window-size=1400,900', ...extraArgs, 'about:blank',
141
+ '--autoplay-policy=no-user-gesture-required', '--mute-audio', '--disable-audio-output', '--ignore-gpu-blocklist', '--enable-unsafe-swiftshader', '--window-size=1400,900', ...extraArgs, 'about:blank',
142
142
  ];
143
143
  // Chrome and its helpers must not outlive this process. A run that is killed or times out never reaches its own cleanup, and a page stuck
144
144
  // in an endless loop then burns a CPU core (and holds the audio device) for hours. So a small shell keeps watch: when this process (or the
@@ -0,0 +1,14 @@
1
+ 'use strict';
2
+
3
+ var chunk6JJC37ZD_cjs = require('./chunk-6JJC37ZD.cjs');
4
+ require('./chunk-BPW42X7C.cjs');
5
+ require('./chunk-WTRKDIKH.cjs');
6
+
7
+
8
+
9
+ Object.defineProperty(exports, "CompositionSequence", {
10
+ enumerable: true,
11
+ get: function () { return chunk6JJC37ZD_cjs.CompositionSequence; }
12
+ });
13
+ //# sourceMappingURL=Composition-2UFPSNVN.cjs.map
14
+ //# sourceMappingURL=Composition-2UFPSNVN.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"Composition-LL2XXU5N.cjs"}
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"Composition-2UFPSNVN.cjs"}
@@ -0,0 +1,5 @@
1
+ export { CompositionSequence } from './chunk-NGYGFGDS.js';
2
+ import './chunk-DDIXIWXK.js';
3
+ import './chunk-MNM56B3X.js';
4
+ //# sourceMappingURL=Composition-BVI236ZR.js.map
5
+ //# sourceMappingURL=Composition-BVI236ZR.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"Composition-6PA4C4TV.js"}
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"Composition-BVI236ZR.js"}
@@ -1,4 +1,4 @@
1
- import { h as Movie } from './Movie-CzzJw8A-.cjs';
1
+ import { h as Movie } from './Movie-mtX9AkQ-.cjs';
2
2
  import 'pixi.js';
3
3
  import 'gsap';
4
4
  import './types-YmUaViL1.cjs';
@@ -1,4 +1,4 @@
1
- import { h as Movie } from './Movie-CBxGH5ms.js';
1
+ import { h as Movie } from './Movie-CgYwa5QG.js';
2
2
  import 'pixi.js';
3
3
  import 'gsap';
4
4
  import './types-YmUaViL1.js';
@@ -142,6 +142,8 @@ interface LayerInfo {
142
142
  /** Where it is drawn, in canvas pixels. `null` inside a `threeD` layer (it is rendered into that layer's texture). */
143
143
  bounds: Rect | null;
144
144
  onCanvas: 'full' | 'partial' | 'none' | null;
145
+ /** Depth of field (threeD layers only): the blur radius in canvas pixels this frame, from the layer's depth and the camera's focus; 0 = sharp. */
146
+ depthBlur?: number;
145
147
  }
146
148
  interface InspectReport {
147
149
  frame: number;
@@ -142,6 +142,8 @@ interface LayerInfo {
142
142
  /** Where it is drawn, in canvas pixels. `null` inside a `threeD` layer (it is rendered into that layer's texture). */
143
143
  bounds: Rect | null;
144
144
  onCanvas: 'full' | 'partial' | 'none' | null;
145
+ /** Depth of field (threeD layers only): the blur radius in canvas pixels this frame, from the layer's depth and the camera's focus; 0 = sharp. */
146
+ depthBlur?: number;
145
147
  }
146
148
  interface InspectReport {
147
149
  frame: number;
@@ -1,4 +1,4 @@
1
- import { h as Movie } from './Movie-CzzJw8A-.cjs';
1
+ import { h as Movie } from './Movie-mtX9AkQ-.cjs';
2
2
  import 'pixi.js';
3
3
  import 'gsap';
4
4
  import './types-YmUaViL1.cjs';
@@ -1,4 +1,4 @@
1
- import { h as Movie } from './Movie-CBxGH5ms.js';
1
+ import { h as Movie } from './Movie-CgYwa5QG.js';
2
2
  import 'pixi.js';
3
3
  import 'gsap';
4
4
  import './types-YmUaViL1.js';