pixi-effects 0.44.0 → 0.46.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 (70) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +5 -5
  3. package/ai/chat-template.html +3 -3
  4. package/ai/tools/check.mjs +117 -2
  5. package/ai/tools/lookup.mjs +1 -0
  6. package/dist/Composition-NSY7A32Y.js +7 -0
  7. package/dist/{Composition-YGBQX3HZ.js.map → Composition-NSY7A32Y.js.map} +1 -1
  8. package/dist/Composition-PME3P5LP.cjs +16 -0
  9. package/dist/{Composition-N6BOROJ2.cjs.map → Composition-PME3P5LP.cjs.map} +1 -1
  10. package/dist/Controller.d.cts +2 -2
  11. package/dist/Controller.d.ts +2 -2
  12. package/dist/{Movie-B3VOAgwc.d.cts → Movie-BqrAWN0g.d.cts} +13 -1
  13. package/dist/{Movie-CQCVUqRs.d.ts → Movie-sV8nJr_f.d.ts} +13 -1
  14. package/dist/{Spans-XTIWYSNO.cjs → Spans-2TZCNREQ.cjs} +9 -9
  15. package/dist/{Spans-XTIWYSNO.cjs.map → Spans-2TZCNREQ.cjs.map} +1 -1
  16. package/dist/{Spans-6L2ZVECX.js → Spans-TESJPQKX.js} +4 -4
  17. package/dist/{Spans-6L2ZVECX.js.map → Spans-TESJPQKX.js.map} +1 -1
  18. package/dist/{chunk-4MBTYREC.js → chunk-2E74SP63.js} +72 -17
  19. package/dist/chunk-2E74SP63.js.map +1 -0
  20. package/dist/{chunk-MAAM45ZX.cjs → chunk-7M63Y6MO.cjs} +114 -59
  21. package/dist/chunk-7M63Y6MO.cjs.map +1 -0
  22. package/dist/{chunk-3HZV3UHU.cjs → chunk-DKCMJILW.cjs} +67 -65
  23. package/dist/chunk-DKCMJILW.cjs.map +1 -0
  24. package/dist/{chunk-4QVQ5UJJ.js → chunk-DM2OLQOJ.js} +5 -6
  25. package/dist/chunk-DM2OLQOJ.js.map +1 -0
  26. package/dist/{chunk-ICI6ACHY.js → chunk-FPV3NPYC.js} +8 -6
  27. package/dist/chunk-FPV3NPYC.js.map +1 -0
  28. package/dist/{chunk-UHNSL2KJ.js → chunk-K24FF4BT.js} +10 -5
  29. package/dist/chunk-K24FF4BT.js.map +1 -0
  30. package/dist/{chunk-X62H3RBG.cjs → chunk-LALEBV63.cjs} +5 -6
  31. package/dist/chunk-LALEBV63.cjs.map +1 -0
  32. package/dist/{chunk-LQDDDIC4.cjs → chunk-O66JGPVV.cjs} +10 -4
  33. package/dist/chunk-O66JGPVV.cjs.map +1 -0
  34. package/dist/index.cjs +35 -35
  35. package/dist/index.d.cts +3 -3
  36. package/dist/index.d.ts +3 -3
  37. package/dist/index.js +4 -4
  38. package/dist/loader.css +1 -1
  39. package/dist/present.cjs +4 -4
  40. package/dist/present.d.cts +2 -2
  41. package/dist/present.d.ts +2 -2
  42. package/dist/present.js +4 -4
  43. package/dist/sound.cjs +2 -2
  44. package/dist/sound.js +1 -1
  45. package/dist/three.cjs +194 -15
  46. package/dist/three.cjs.map +1 -1
  47. package/dist/three.d.cts +75 -4
  48. package/dist/three.d.ts +75 -4
  49. package/dist/three.js +191 -13
  50. package/dist/three.js.map +1 -1
  51. package/dist/{types-C5xo0fzb.d.cts → types-CTSrPvY3.d.cts} +64 -58
  52. package/dist/{types-C5xo0fzb.d.ts → types-CTSrPvY3.d.ts} +64 -58
  53. package/docs/api.md +2 -0
  54. package/docs/dsl.md +26 -0
  55. package/llms-full.txt +66 -4
  56. package/package.json +1 -1
  57. package/skills/pixi-effects/SKILL.md +2 -2
  58. package/skills/pixi-effects/reference/cheatsheet.md +2 -2
  59. package/skills/pixi-effects/reference/recipes.md +34 -0
  60. package/skills/pixi-effects/template.html +6 -6
  61. package/dist/Composition-N6BOROJ2.cjs +0 -16
  62. package/dist/Composition-YGBQX3HZ.js +0 -7
  63. package/dist/chunk-3HZV3UHU.cjs.map +0 -1
  64. package/dist/chunk-4MBTYREC.js.map +0 -1
  65. package/dist/chunk-4QVQ5UJJ.js.map +0 -1
  66. package/dist/chunk-ICI6ACHY.js.map +0 -1
  67. package/dist/chunk-LQDDDIC4.cjs.map +0 -1
  68. package/dist/chunk-MAAM45ZX.cjs.map +0 -1
  69. package/dist/chunk-UHNSL2KJ.js.map +0 -1
  70. package/dist/chunk-X62H3RBG.cjs.map +0 -1
@@ -1,8 +1,8 @@
1
1
  'use strict';
2
2
 
3
- var chunk3HZV3UHU_cjs = require('./chunk-3HZV3UHU.cjs');
4
- var chunkX62H3RBG_cjs = require('./chunk-X62H3RBG.cjs');
5
- var chunkLQDDDIC4_cjs = require('./chunk-LQDDDIC4.cjs');
3
+ var chunkDKCMJILW_cjs = require('./chunk-DKCMJILW.cjs');
4
+ var chunkLALEBV63_cjs = require('./chunk-LALEBV63.cjs');
5
+ var chunkO66JGPVV_cjs = require('./chunk-O66JGPVV.cjs');
6
6
  var chunkJN7JHNZR_cjs = require('./chunk-JN7JHNZR.cjs');
7
7
  var chunk3SAS3ZE5_cjs = require('./chunk-3SAS3ZE5.cjs');
8
8
  var chunkTWZNOUM7_cjs = require('./chunk-TWZNOUM7.cjs');
@@ -109,7 +109,7 @@ async function mixdown(audios, totalDuration, sampleRate = 44100, pace) {
109
109
  if (pace) await pace(made / all);
110
110
  if (!buffer) continue;
111
111
  if (a.warp || a.sourceMap) {
112
- const r = chunkLQDDDIC4_cjs.resampleThrough(a, buffer.getChannelData(0), buffer.numberOfChannels > 1 ? buffer.getChannelData(1) : buffer.getChannelData(0), buffer.sampleRate, sampleRate, totalDuration);
112
+ const r = chunkO66JGPVV_cjs.resampleThrough(a, buffer.getChannelData(0), buffer.numberOfChannels > 1 ? buffer.getChannelData(1) : buffer.getChannelData(0), buffer.sampleRate, sampleRate, totalDuration);
113
113
  if (!r) continue;
114
114
  const out = ctx.createBuffer(2, r.L.length, sampleRate);
115
115
  out.getChannelData(0).set(r.L);
@@ -142,6 +142,20 @@ async function mixdown(audios, totalDuration, sampleRate = 44100, pace) {
142
142
  return await ctx.startRendering();
143
143
  }
144
144
  var KNEE = 0.9;
145
+ var LOUDNESS_CEILING_DB = -1;
146
+ function normalizeMix(buffer, target) {
147
+ const chans = [];
148
+ for (let ch = 0; ch < buffer.numberOfChannels; ch++) chans.push(buffer.getChannelData(ch));
149
+ const m = chunkO66JGPVV_cjs.measureLoudness(chans, buffer.sampleRate, { truePeak: false });
150
+ if (m.integratedLufs === null) return { target, gainDb: 0, reachedLufs: null, limited: false };
151
+ let gainDb = target - m.integratedLufs;
152
+ const room = LOUDNESS_CEILING_DB - m.samplePeakDb;
153
+ const limited = gainDb > room;
154
+ if (limited) gainDb = room;
155
+ const gain = 10 ** (gainDb / 20);
156
+ for (const d of chans) for (let i = 0; i < d.length; i++) d[i] = d[i] * gain;
157
+ return { target, gainDb: Math.round(gainDb * 10) / 10, reachedLufs: Math.round((m.integratedLufs + gainDb) * 10) / 10, limited };
158
+ }
145
159
  function limitMix(buffer, audios) {
146
160
  let peak = 0;
147
161
  let peakIndex = 0;
@@ -158,7 +172,7 @@ function limitMix(buffer, audios) {
158
172
  }
159
173
  const peakAt = peakIndex / buffer.sampleRate;
160
174
  if (peak > 1) {
161
- const playing = audios.map((a) => ({ a, ...chunkLQDDDIC4_cjs.playSpan(a, buffer.duration) })).filter((s) => s.start <= peakAt && peakAt < s.end).map((s) => s.a.layer ?? "an audio layer");
175
+ const playing = audios.map((a) => ({ a, ...chunkO66JGPVV_cjs.playSpan(a, buffer.duration) })).filter((s) => s.start <= peakAt && peakAt < s.end).map((s) => s.a.layer ?? "an audio layer");
162
176
  console.warn(
163
177
  `pixi-effects: the audio mix peaks at ${peak.toFixed(2)} (${(20 * Math.log10(peak)).toFixed(1)} dBFS) at ${peakAt.toFixed(2)}s, so it was limited (it would distort). Playing there: ${[...new Set(playing)].join(", ")}. Lower their volume.`
164
178
  );
@@ -1120,8 +1134,8 @@ function expandTransitions(spec) {
1120
1134
  }
1121
1135
  const transitions = out.transitions;
1122
1136
  const sequences = out.sequences ?? [];
1123
- const remap = out.duration !== void 0 ? chunkX62H3RBG_cjs.timeRemapOf(out) : null;
1124
- const parentDuration = remap ? chunkX62H3RBG_cjs.contentLength(remap, out.duration) : out.duration ?? Infinity;
1137
+ const remap = out.duration !== void 0 ? chunkLALEBV63_cjs.timeRemapOf(out) : null;
1138
+ const parentDuration = remap ? chunkLALEBV63_cjs.contentLength(remap, out.duration) : out.duration ?? Infinity;
1125
1139
  const compW = out.width;
1126
1140
  const compH = out.height;
1127
1141
  const threeDByName = new Set(sequences.filter((s) => s.name && s.threeD).map((s) => s.name));
@@ -1189,16 +1203,16 @@ function expandTransitions(spec) {
1189
1203
  if (toUses > 1) {
1190
1204
  throw new Error(`pixi-effects: ${tag} sequence "${t.to}" is used as \`to\` in more than one transition`);
1191
1205
  }
1192
- const tStart = chunkX62H3RBG_cjs.resolveAt(t.at, parentDuration);
1206
+ const tStart = chunkLALEBV63_cjs.resolveAt(t.at, parentDuration);
1193
1207
  const tEnd = tStart + t.duration;
1194
1208
  const fromSeq = fromEntry.seq;
1195
- const fromStart = chunkX62H3RBG_cjs.resolveAt(fromSeq.at, parentDuration);
1209
+ const fromStart = chunkLALEBV63_cjs.resolveAt(fromSeq.at, parentDuration);
1196
1210
  const fromEnd = fromStart + (fromSeq.duration ?? parentDuration);
1197
1211
  if (tStart < fromStart - EPS || tEnd > fromEnd + EPS) {
1198
1212
  throw new Error(`pixi-effects: ${tag} window [${tStart}, ${tEnd}] is not covered by \`from\` "${t.from}" (lives [${fromStart}, ${fromEnd}], so it ends at ${fromEnd})`);
1199
1213
  }
1200
1214
  const toSeq = toEntry.seq;
1201
- const toStart = chunkX62H3RBG_cjs.resolveAt(toSeq.at, parentDuration);
1215
+ const toStart = chunkLALEBV63_cjs.resolveAt(toSeq.at, parentDuration);
1202
1216
  const toEnd = toStart + (toSeq.duration ?? parentDuration);
1203
1217
  if (tStart < toStart - EPS || tEnd > toEnd + EPS) {
1204
1218
  throw new Error(`pixi-effects: ${tag} window [${tStart}, ${tEnd}] is not covered by \`to\` "${t.to}" (lives [${toStart}, ${toEnd}], so it starts at ${toStart})`);
@@ -1258,7 +1272,7 @@ function wrapAsFullComposition(seq, compW, compH) {
1258
1272
  delete inner.at;
1259
1273
  delete inner.duration;
1260
1274
  const m = inner.mask;
1261
- const carriesMatte = m !== void 0 && chunk3HZV3UHU_cjs.maskSourceOf(m).kind === "refs";
1275
+ const carriesMatte = m !== void 0 && chunkDKCMJILW_cjs.maskSourceOf(m).kind === "refs";
1262
1276
  if (carriesMatte) delete inner.mask;
1263
1277
  return {
1264
1278
  ...carriesMatte ? { mask: m } : {},
@@ -1528,7 +1542,7 @@ function canvasReport(root) {
1528
1542
  const problem = threw.length ? `${name} could not draw at ${threw.map((t) => `${t} s`).join(", ")} (its ${kind === "three" ? "update or render threw" : "draw threw"}, or the layer failed to build): ${kind === "three" ? "the warning above says why" : "it shows a magenta checkerboard there; the warning above says why"}, so the layer cannot be called deterministic` : differs.length ? `${name} does not draw the same picture for the same time (checked at ${differs.map(describeDiff).join(", ")}; drawn in two orders after one draw that was thrown away): ` + (kind === "three" ? "usually its update uses Math.random, Date.now, a clock, or state that builds up from call to call: a mixer advanced with mixer.update(dt) is one (pose a clip with mixer.setTime(t), or threeGltf(url).pose(t)), and so is a value moved by the time since the last call. A skinned model of your own (not loaded by threeGltf) that moves and casts a shadow does it too: three culls a skinned mesh by a bounding sphere that does not follow it, so set mesh.frustumCulled = false. Set every object from t, so playback, a seek and an export agree" : "usually its draw uses Math.random, Date.now or state that outlives the call (a value it keeps from the last call), so playback, a seek and an export would not agree: use info.random() (seeded) and derive everything from t. If it uses none of these, look at the canvases it draws from: make sprites and masks with info.canvas(w, h) (not document.createElement) and draw them into the picture before the strokes that depend on them") : "";
1529
1543
  out.push({ name: seq.spec.name ?? name, moments, drawMs, ok: !differs.length && !threw.length, problem, kind });
1530
1544
  }
1531
- if (seq instanceof chunk3HZV3UHU_cjs.CompositionSequence) walk3(seq);
1545
+ if (seq instanceof chunkDKCMJILW_cjs.CompositionSequence) walk3(seq);
1532
1546
  };
1533
1547
  const walk3 = (comp) => {
1534
1548
  for (const { seq } of comp.layers()) {
@@ -1580,8 +1594,8 @@ function motionOf(keyframes, local, lives, keys = (k) => GEOMETRY.has(k)) {
1580
1594
  let length = 0;
1581
1595
  if (typeof kf.duration === "number") length = kf.duration;
1582
1596
  else if (kf.duration === "auto") {
1583
- const sp = chunkX62H3RBG_cjs.parseSpring(kf.ease);
1584
- length = sp ? Math.round(chunkX62H3RBG_cjs.springDuration(sp) * 1e3) / 1e3 : 0.5;
1597
+ const sp = chunkLALEBV63_cjs.parseSpring(kf.ease);
1598
+ length = sp ? Math.round(chunkLALEBV63_cjs.springDuration(sp) * 1e3) / 1e3 : 0.5;
1585
1599
  }
1586
1600
  if (!(length > 0)) continue;
1587
1601
  const kat = kf.at ?? 0;
@@ -1620,7 +1634,7 @@ function inspectScene(root, frame, time, canvas, opts = {}) {
1620
1634
  const name = seq.spec.name;
1621
1635
  const label = name ?? `${seq.spec.type}#${i}`;
1622
1636
  const alpha = (t?.alpha ?? 1) * carriers.reduce((a, c) => a * (c.target?.alpha ?? 1), 1);
1623
- const alive = (threeD ? display.visible : t?.renderable ?? true) && carriers.every((c) => c.target?.renderable ?? true);
1637
+ const alive = (threeD ? display.visible : (t?.renderable ?? true) && t?.visible !== false) && carriers.every((c) => c.target?.renderable ?? true);
1624
1638
  const visible = parentVisible && alive && alpha > 0;
1625
1639
  let bounds = null;
1626
1640
  if (!insideThreeD) {
@@ -1661,7 +1675,7 @@ function inspectScene(root, frame, time, canvas, opts = {}) {
1661
1675
  if (motion.ever) hasMotion.add(info);
1662
1676
  let shown = bounds ? clip(bounds, view) : null;
1663
1677
  const maskTarget = seq.maskSequence?.target;
1664
- const maskSrc = chunk3HZV3UHU_cjs.maskSourceOf(seq.spec.mask);
1678
+ const maskSrc = chunkDKCMJILW_cjs.maskSourceOf(seq.spec.mask);
1665
1679
  if (shown && maskTarget && !(maskSrc.kind === "inline" && maskSrc.invert)) {
1666
1680
  const m = maskTarget.getBounds();
1667
1681
  shown = clip(shown, { x: m.x, y: m.y, width: m.width, height: m.height });
@@ -1670,7 +1684,7 @@ function inspectScene(root, frame, time, canvas, opts = {}) {
1670
1684
  const group = parentScene ?? (name !== void 0 && blending.has(name) ? name : void 0);
1671
1685
  if (group) scene.set(info, group);
1672
1686
  if (zero || seq.showsNothingYet) scaledToNothing.add(info);
1673
- if (seq instanceof chunk3HZV3UHU_cjs.CompositionSequence) walk3(seq, prefix + label + "/", insideThreeD || threeD, visible, group, motion, zero, parentAlpha * alpha);
1687
+ if (seq instanceof chunkDKCMJILW_cjs.CompositionSequence) walk3(seq, prefix + label + "/", insideThreeD || threeD, visible, group, motion, zero, parentAlpha * alpha);
1674
1688
  });
1675
1689
  };
1676
1690
  walk3(root, "", false, true);
@@ -1800,7 +1814,22 @@ function pickScene(root, x, y, opts = {}, deps = {}) {
1800
1814
  return true;
1801
1815
  };
1802
1816
  const inside = (b, p) => p.x >= b.x && p.x <= b.x + b.width && p.y >= b.y && p.y <= b.y + b.height;
1803
- const walk3 = (comp, prefix, parentVisible) => {
1817
+ const hiddenSources = /* @__PURE__ */ new Map();
1818
+ const forwards = [];
1819
+ const rays = /* @__PURE__ */ new Map();
1820
+ const rayOf = (seq, d, local, lb) => {
1821
+ if (typeof seq.pickAt !== "function") return null;
1822
+ if (!rays.has(seq)) rays.set(seq, seq.pickAt((local.x - lb.x) / lb.width, (local.y - lb.y) / lb.height));
1823
+ return rays.get(seq);
1824
+ };
1825
+ const forwardThrough = (seq, d, path, local, lb) => {
1826
+ const shows = seq.screenOf;
1827
+ if (!shows || !inside(lb, local)) return;
1828
+ const r = rayOf(seq, d, local, lb);
1829
+ const shown = r?.uv ? shows.call(seq, r.material) : null;
1830
+ if (r?.uv && shown) forwards.push({ source: shown.source, uv: r.uv, via: { path, object: String(r.object) }, order: orderOf(d) });
1831
+ };
1832
+ const walk3 = (comp, prefix, parentVisible, at = { x, y }, orderBase, via) => {
1804
1833
  comp.layers().forEach(({ seq, display, threeD, carriers, quad }, i) => {
1805
1834
  const d = display;
1806
1835
  if (!d) return;
@@ -1808,16 +1837,25 @@ function pickScene(root, x, y, opts = {}, deps = {}) {
1808
1837
  const name = seq.spec.name;
1809
1838
  const path = prefix + (name ?? `${seq.spec.type}#${i}`);
1810
1839
  const alpha = (t?.alpha ?? 1) * carriers.reduce((a, c) => a * (c.target?.alpha ?? 1), 1);
1811
- const alive = (threeD ? d.visible : t?.renderable ?? true) && carriers.every((c) => c.target?.renderable ?? true);
1840
+ const alive = (threeD ? d.visible : (t?.renderable ?? true) && t?.visible !== false) && carriers.every((c) => c.target?.renderable ?? true);
1812
1841
  const visible = parentVisible && alive && alpha > 0;
1813
- if (!visible) return;
1814
- const isComposition = seq instanceof chunk3HZV3UHU_cjs.CompositionSequence;
1842
+ if (!visible) {
1843
+ if (seq instanceof chunkDKCMJILW_cjs.CompositionSequence && !threeD && parentVisible && alpha > 0 && t?.visible === false && (t?.renderable ?? true) && carriers.every((c) => c.target?.renderable ?? true)) hiddenSources.set(seq, path);
1844
+ return;
1845
+ }
1846
+ const isComposition = seq instanceof chunkDKCMJILW_cjs.CompositionSequence;
1815
1847
  if (isComposition && !threeD) {
1816
- walk3(seq, path + "/", visible);
1848
+ walk3(seq, path + "/", visible, at, orderBase, via);
1817
1849
  return;
1818
1850
  }
1819
- if (!wanted(name, path)) return;
1820
- const point = { x, y };
1851
+ if (!wanted(name, path)) {
1852
+ if (!threeD) {
1853
+ const lp = d.toLocal(at);
1854
+ forwardThrough(seq, d, path, lp, d.getLocalBounds());
1855
+ }
1856
+ return;
1857
+ }
1858
+ const point = at;
1821
1859
  let hit = null;
1822
1860
  if (threeD) {
1823
1861
  if (quad && pointInQuad(quad, d.toLocal(point).x, d.toLocal(point).y)) hit = { name, path, type: seq.spec.type, kind: "card", point };
@@ -1825,7 +1863,10 @@ function pickScene(root, x, y, opts = {}, deps = {}) {
1825
1863
  const local = d.toLocal(point);
1826
1864
  const lb = d.getLocalBounds();
1827
1865
  if (inside(lb, local)) {
1828
- const three = typeof seq.pickAt === "function" ? seq.pickAt((local.x - lb.x) / lb.width, (local.y - lb.y) / lb.height) : void 0;
1866
+ const isThree = typeof seq.pickAt === "function";
1867
+ const ray = isThree ? rayOf(seq, d, local, lb) : void 0;
1868
+ if (isThree) forwardThrough(seq, d, path, local, lb);
1869
+ const three = ray ? { object: ray.object, point: ray.point, distance: ray.distance, object3d: ray.object3d } : isThree ? null : void 0;
1829
1870
  if (three) {
1830
1871
  const planeOf = seq.dragPlane;
1831
1872
  if (planeOf) {
@@ -1858,12 +1899,18 @@ function pickScene(root, x, y, opts = {}, deps = {}) {
1858
1899
  }
1859
1900
  if (!hit) return;
1860
1901
  const maskTarget = seq.maskSequence?.target;
1861
- const maskSrc = chunk3HZV3UHU_cjs.maskSourceOf(seq.spec.mask);
1902
+ const maskSrc = chunkDKCMJILW_cjs.maskSourceOf(seq.spec.mask);
1862
1903
  if (maskTarget && !(maskSrc.kind === "inline" && maskSrc.invert) && !inside(maskTarget.getBounds(), point)) return;
1863
- found.push({ hit, order: orderOf(d) });
1904
+ found.push({ hit: via ? { ...hit, point: { x, y }, via } : hit, order: orderBase ? [...orderBase, ...orderOf(d)] : orderOf(d) });
1864
1905
  });
1865
1906
  };
1866
1907
  walk3(root, "", true);
1908
+ for (const f of forwards) {
1909
+ const path = hiddenSources.get(f.source);
1910
+ if (path === void 0) continue;
1911
+ const g = f.source.target.toGlobal({ x: f.uv.u * f.source.intrinsicWidth, y: (1 - f.uv.v) * f.source.intrinsicHeight });
1912
+ walk3(f.source, path + "/", true, g, f.order, f.via);
1913
+ }
1867
1914
  return found.sort((a, b) => above(a.order, b.order)).map((f) => f.hit);
1868
1915
  }
1869
1916
  var cache = /* @__PURE__ */ new WeakMap();
@@ -1896,7 +1943,7 @@ function walk(root, visit) {
1896
1943
  visit(seq);
1897
1944
  const mask = seq.maskSequence;
1898
1945
  if (mask) visit(mask);
1899
- if (seq instanceof chunk3HZV3UHU_cjs.CompositionSequence) walk(seq, visit);
1946
+ if (seq instanceof chunkDKCMJILW_cjs.CompositionSequence) walk(seq, visit);
1900
1947
  }
1901
1948
  }
1902
1949
  function reusePoolOf(old) {
@@ -1997,7 +2044,7 @@ var round = (n) => Math.round(n * 1e6) / 1e6;
1997
2044
  var family = (name) => name.replace(/\d+/g, "#");
1998
2045
  function detailOf(seq) {
1999
2046
  const s = seq.spec;
2000
- const spans = chunkX62H3RBG_cjs.spansMeta.get(seq.spec)?.preview;
2047
+ const spans = chunkLALEBV63_cjs.spansMeta.get(seq.spec)?.preview;
2001
2048
  if (spans) return spans.length > 60 ? spans.slice(0, 57) + "\u2026" : spans;
2002
2049
  if (typeof s.text === "string") return s.text.length > 60 ? s.text.slice(0, 57) + "\u2026" : s.text;
2003
2050
  if (typeof s.asset === "string") return `asset \u201C${s.asset}\u201D`;
@@ -2013,10 +2060,10 @@ function walk2(comp, parentStart, parentEnd, depth, prefix, rows, transitions, l
2013
2060
  const start = parentStart + (seq.at ?? 0);
2014
2061
  const end = Math.min(parentEnd, seq.duration !== void 0 ? start + seq.duration : parentEnd);
2015
2062
  const span = end - start;
2016
- const keys = (seq.spec.keyframes ?? []).map((kf) => round(start + chunkX62H3RBG_cjs.resolveAt(kf.at, span))).sort((a, b) => a - b).slice(0, MAX_KEYS);
2063
+ const keys = (seq.spec.keyframes ?? []).map((kf) => round(start + chunkLALEBV63_cjs.resolveAt(kf.at, span))).sort((a, b) => a - b).slice(0, MAX_KEYS);
2017
2064
  const row = { path: prefix + name, name, type: seq.spec.type, depth, start: round(start), end: round(end), keys, detail: detailOf(seq), ...local ? { local } : {} };
2018
2065
  const inner = [];
2019
- const isComp = seq instanceof chunk3HZV3UHU_cjs.CompositionSequence;
2066
+ const isComp = seq instanceof chunkDKCMJILW_cjs.CompositionSequence;
2020
2067
  if (isComp) {
2021
2068
  const label = seq.remapLabel();
2022
2069
  walk2(seq, label ? 0 : start, label ? seq.contentSpan : end, depth + 1, prefix + name + "/", inner, transitions, label ? `${name} ${label}` : local);
@@ -2175,7 +2222,7 @@ function sheetLayout(n, columns, cellW, srcW, srcH, labelH) {
2175
2222
  }
2176
2223
 
2177
2224
  // src/core/vars.ts
2178
- var RESERVED_NAMES = ["w", "h", "W", "H", "GW", "GH", "t", "d", "T", "contain", "cover", "value", ...chunkX62H3RBG_cjs.EXPRESSION_NAMES];
2225
+ var RESERVED_NAMES = ["w", "h", "W", "H", "GW", "GH", "t", "d", "T", "contain", "cover", "value", ...chunkLALEBV63_cjs.EXPRESSION_NAMES];
2179
2226
  var NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
2180
2227
  var isPlain = (v) => !!v && typeof v === "object" && (Object.getPrototypeOf(v) === Object.prototype || Object.getPrototypeOf(v) === null);
2181
2228
  function normalizeVars(raw, where, warn = (m) => console.warn(m)) {
@@ -2619,6 +2666,7 @@ var Movie = class {
2619
2666
  /** What went into the mix, and its peak before limiting — kept for inspectAudio(). */
2620
2667
  _audioSources = [];
2621
2668
  _mixStats = null;
2669
+ _loudness = null;
2622
2670
  on(event, fn) {
2623
2671
  (this._events[event] ??= []).push(fn);
2624
2672
  if (event === "ready" && this._initState === "ready") {
@@ -2693,7 +2741,11 @@ var Movie = class {
2693
2741
  return this._audioContext;
2694
2742
  }
2695
2743
  async init(options = {}) {
2696
- chunkTWZNOUM7_cjs.warnUnknownOptions("movie.init()", options, ["width", "height", "duration", "frameRate", "background", "canvas", "assets", "composition", "vars", "poster", "motionBlur", "loader", "loop"]);
2744
+ chunkTWZNOUM7_cjs.warnUnknownOptions("movie.init()", options, ["width", "height", "duration", "frameRate", "background", "canvas", "assets", "composition", "vars", "poster", "motionBlur", "loader", "loop", "loudness"]);
2745
+ if (options.loudness !== void 0 && options.loudness !== false && !(typeof options.loudness === "number" && Number.isFinite(options.loudness) && options.loudness <= 0 && options.loudness >= -70)) {
2746
+ throw new Error(`pixi-effects: movie.init({ loudness }) is the loudness of the whole mix in LUFS: a number from -70 to 0 (-16 is usual for web video: loudness: -16), or false for no change; got ${JSON.stringify(options.loudness)}`);
2747
+ }
2748
+ this._loudness = options.loudness === void 0 || options.loudness === false ? null : options.loudness;
2697
2749
  this._initState = "pending";
2698
2750
  try {
2699
2751
  this.width = options.width ?? 1920;
@@ -2714,8 +2766,8 @@ var Movie = class {
2714
2766
  this.stops = normalizeStops(applyVars(options.composition, this._vars)?.stops, this.duration, this.frameRate);
2715
2767
  this._posterFrame = poster.frame;
2716
2768
  this.background = options.background ?? "#000000";
2717
- this.timeline = chunkX62H3RBG_cjs.createTimeline({ paused: true, defaults: { ease: "none" } });
2718
- chunkX62H3RBG_cjs.lengthen(this.timeline, this.duration);
2769
+ this.timeline = chunkLALEBV63_cjs.createTimeline({ paused: true, defaults: { ease: "none" } });
2770
+ chunkLALEBV63_cjs.lengthen(this.timeline, this.duration);
2719
2771
  const baseAppOptions = {
2720
2772
  width: this.width,
2721
2773
  height: this.height,
@@ -2801,6 +2853,7 @@ var Movie = class {
2801
2853
  frameRate: this.frameRate,
2802
2854
  vars: numericVars(vars),
2803
2855
  ...gpuField,
2856
+ ...this.app?.renderer ? { pixiRenderer: this.app.renderer } : {},
2804
2857
  ...reuse ? { reuse } : {},
2805
2858
  ...lp ? { onLayerBuilt: async () => {
2806
2859
  lp.tick();
@@ -2813,7 +2866,7 @@ var Movie = class {
2813
2866
  height: sizeWins ? height : spec.height ?? height,
2814
2867
  duration: spec.duration ?? this.duration
2815
2868
  } : void 0;
2816
- await chunkX62H3RBG_cjs.ensureFilterLibrary(seededComposition);
2869
+ await chunkLALEBV63_cjs.ensureFilterLibrary(seededComposition);
2817
2870
  if (chunkJN7JHNZR_cjs.usesAdvancedBlend(seededComposition)) await chunkJN7JHNZR_cjs.enableAdvancedBlend(this.app.renderer);
2818
2871
  const userComposition = seededComposition ? expandTransitions(seededComposition) : void 0;
2819
2872
  const rootSeqSpec = {
@@ -2824,7 +2877,7 @@ var Movie = class {
2824
2877
  ...userComposition
2825
2878
  };
2826
2879
  if (userComposition) carryTransitionWindows(userComposition, rootSeqSpec);
2827
- const sequence = new chunk3HZV3UHU_cjs.CompositionSequence(rootSeqSpec, null, rootShape);
2880
+ const sequence = new chunkDKCMJILW_cjs.CompositionSequence(rootSeqSpec, null, rootShape);
2828
2881
  lp?.begin("build", countLayers(rootSeqSpec));
2829
2882
  try {
2830
2883
  await sequence.build();
@@ -2844,7 +2897,8 @@ var Movie = class {
2844
2897
  this._mixStats = null;
2845
2898
  this._mixCache.set(null, null);
2846
2899
  } else {
2847
- const key = mixKey(audios, this.duration, audioContext.sampleRate);
2900
+ const baseKey = mixKey(audios, this.duration, audioContext.sampleRate);
2901
+ const key = baseKey === null ? null : `${baseKey}|loudness:${this._loudness}`;
2848
2902
  const hit = this._mixCache.get(key);
2849
2903
  if (hit) {
2850
2904
  this.audioBuffer = hit.buffer;
@@ -2855,7 +2909,8 @@ var Movie = class {
2855
2909
  lp.part(done);
2856
2910
  await lp.yieldIfDue();
2857
2911
  } : void 0);
2858
- this._mixStats = this.audioBuffer ? limitMix(this.audioBuffer, audios) : null;
2912
+ const loudness = this.audioBuffer && this._loudness !== null ? normalizeMix(this.audioBuffer, this._loudness) : void 0;
2913
+ this._mixStats = this.audioBuffer ? { ...limitMix(this.audioBuffer, audios), ...loudness ? { loudness } : {} } : null;
2859
2914
  this._mixCache.set(key, this.audioBuffer, this._mixStats);
2860
2915
  status = "mixed";
2861
2916
  }
@@ -2960,8 +3015,8 @@ var Movie = class {
2960
3015
  if (!changed) return { reason, ms: 0, audio: "none", frame: this.currentFrame, playing: this.isPlaying, unchanged: true };
2961
3016
  sequence = await this._construct(composition, vars, width, height, void 0, this._resized || sizeChanged, reusePoolOf(this._rootSequence));
2962
3017
  alive();
2963
- const timeline = chunkX62H3RBG_cjs.createTimeline({ paused: true, defaults: { ease: "none" } });
2964
- chunkX62H3RBG_cjs.lengthen(timeline, this.duration);
3018
+ const timeline = chunkLALEBV63_cjs.createTimeline({ paused: true, defaults: { ease: "none" } });
3019
+ chunkLALEBV63_cjs.lengthen(timeline, this.duration);
2965
3020
  sequence.bindTimeline(timeline);
2966
3021
  const hadSound = this.audioBuffer !== null;
2967
3022
  const audio = await this._mixSound(sequence, this._ensureAudioContext());
@@ -3368,7 +3423,7 @@ var Movie = class {
3368
3423
  chunkTWZNOUM7_cjs.warnUnknownOptions("movie.inspectAudio()", opts, ["window", "scenes"]);
3369
3424
  this._requireReady("inspectAudio");
3370
3425
  const scenes = opts.scenes ?? namedScenes(this.timelineData().rows);
3371
- return chunkLQDDDIC4_cjs.analyzeAudio(this.audioBuffer, this._audioSources, this._mixStats, this.duration, { ...opts, scenes });
3426
+ return chunkO66JGPVV_cjs.analyzeAudio(this.audioBuffer, this._audioSources, this._mixStats, this.duration, { ...opts, scenes });
3372
3427
  }
3373
3428
  /**
3374
3429
  * Draw the stage now. PixiJS's CullerPlugin only culls inside `app.render()` (the ticker), and we render
@@ -3730,7 +3785,7 @@ function countLayers(spec) {
3730
3785
  n += 1 + countLayers(s);
3731
3786
  const spans = s.spans;
3732
3787
  if (Array.isArray(spans)) n += spans.length;
3733
- const src = chunk3HZV3UHU_cjs.maskSourceOf(mask);
3788
+ const src = chunkDKCMJILW_cjs.maskSourceOf(mask);
3734
3789
  if (src.kind === "inline") n += 1 + countLayers(src.spec);
3735
3790
  }
3736
3791
  return n;
@@ -3819,8 +3874,8 @@ function splitVertical(text, style, ts, right, top, align, by) {
3819
3874
 
3820
3875
  // src/presets/_ease.ts
3821
3876
  function parseEase2(name) {
3822
- chunkX62H3RBG_cjs.checkEase(name, "a preset");
3823
- return chunkX62H3RBG_cjs.parseEase(name);
3877
+ chunkLALEBV63_cjs.checkEase(name, "a preset");
3878
+ return chunkLALEBV63_cjs.parseEase(name);
3824
3879
  }
3825
3880
 
3826
3881
  // src/presets/kenBurns.ts
@@ -3955,17 +4010,17 @@ function orbit(opts) {
3955
4010
  throw new Error("pixi-effects: orbit() cannot combine `radius` with `dollyZoom` \u2014 the radius follows the animated fov");
3956
4011
  }
3957
4012
  const [cx, cy] = opts.center ?? [width / 2, height / 2];
3958
- const fixedR = opts.radius ?? chunkX62H3RBG_cjs.homeDistance(height, fov ?? chunkX62H3RBG_cjs.DEFAULT_FOV);
4013
+ const fixedR = opts.radius ?? chunkLALEBV63_cjs.homeDistance(height, fov ?? chunkLALEBV63_cjs.DEFAULT_FOV);
3959
4014
  const start = opts.start ?? -degrees / 2;
3960
4015
  const steps = Math.max(1, Math.round(duration * stepsPerSecond));
3961
4016
  const easeFn = parseEase2(ease);
3962
4017
  const dollyEase = dolly ? parseEase2(dolly.ease ?? ease) : null;
3963
4018
  const pos = (i) => {
3964
4019
  const p = i / steps;
3965
- const a = (start + degrees * easeFn(p)) * chunkX62H3RBG_cjs.DEG;
4020
+ const a = (start + degrees * easeFn(p)) * chunkLALEBV63_cjs.DEG;
3966
4021
  if (!dolly) return { x: cx + fixedR * Math.sin(a), z: fixedR * Math.cos(a) };
3967
4022
  const f = dolly.from + (dolly.to - dolly.from) * dollyEase(p);
3968
- const R = chunkX62H3RBG_cjs.homeDistance(height, f);
4023
+ const R = chunkLALEBV63_cjs.homeDistance(height, f);
3969
4024
  return { x: cx + R * Math.sin(a), z: R * Math.cos(a), fov: f };
3970
4025
  };
3971
4026
  const keyframes = [];
@@ -4020,7 +4075,7 @@ function particles(opts) {
4020
4075
  const round2 = (v) => Math.round(v * 1e6) / 1e6;
4021
4076
  const out = [];
4022
4077
  for (let i = 0; i < count; i++) {
4023
- const r = chunkX62H3RBG_cjs.random(seed * 1000003 + i * 7919 + 17);
4078
+ const r = chunkLALEBV63_cjs.random(seed * 1000003 + i * 7919 + 17);
4024
4079
  const pick = ([a2, b]) => a2 + r() * (b - a2);
4025
4080
  const born = at + r() * emit;
4026
4081
  const lifetime = pick(life);
@@ -4033,7 +4088,7 @@ function particles(opts) {
4033
4088
  const turn = spin ? pick(spin) : 0;
4034
4089
  const startAngle = spin ? r() * 360 : 0;
4035
4090
  const swayAmp = sway?.amp ?? 0, swayFreq = sway?.freq ?? 1;
4036
- const n0 = sway ? chunkX62H3RBG_cjs.noise(0, seed * 1000003 + i) : 0;
4091
+ const n0 = sway ? chunkLALEBV63_cjs.noise(0, seed * 1000003 + i) : 0;
4037
4092
  const at_ = (t) => {
4038
4093
  const axis = (p0, v0, acc) => {
4039
4094
  if (drag > 0) {
@@ -4042,7 +4097,7 @@ function particles(opts) {
4042
4097
  }
4043
4098
  return p0 + v0 * t + 0.5 * acc * t * t;
4044
4099
  };
4045
- const sw = sway ? swayAmp * (chunkX62H3RBG_cjs.noise(t * swayFreq, seed * 1000003 + i) - n0) : 0;
4100
+ const sw = sway ? swayAmp * (chunkLALEBV63_cjs.noise(t * swayFreq, seed * 1000003 + i) - n0) : 0;
4046
4101
  return { x: axis(x0, vx, wind) + sw, y: axis(y0, vy, gravity) };
4047
4102
  };
4048
4103
  const kfs = [];
@@ -4104,7 +4159,7 @@ function followPath(opts) {
4104
4159
  if (steps > MAX_STEPS) throw new Error(`followPath(): ${steps} samples is too many (max ${MAX_STEPS}); lower frameRate or duration`);
4105
4160
  const segments = [];
4106
4161
  let total = 0;
4107
- for (const line of chunkX62H3RBG_cjs.flattenSvgPath(d)) {
4162
+ for (const line of chunkLALEBV63_cjs.flattenSvgPath(d)) {
4108
4163
  const p = line.closed ? [...line.pts, line.pts[0], line.pts[1]] : line.pts;
4109
4164
  for (let i = 2; i < p.length; i += 2) {
4110
4165
  const len = Math.hypot(p[i] - p[i - 2], p[i + 1] - p[i - 1]);
@@ -4157,7 +4212,7 @@ function followPath(opts) {
4157
4212
  // src/core/pathSample.ts
4158
4213
  function samplePath(d) {
4159
4214
  if (typeof d !== "string" || d.trim() === "") throw new Error('samplePath(): d must be SVG path data, e.g. "M 40 300 C 200 20 400 20 560 300"');
4160
- const lines = chunkX62H3RBG_cjs.flattenSvgPath(d);
4215
+ const lines = chunkLALEBV63_cjs.flattenSvgPath(d);
4161
4216
  const segments = [];
4162
4217
  let length = 0;
4163
4218
  for (const line of lines) {
@@ -4188,7 +4243,7 @@ function samplePath(d) {
4188
4243
  return { x: g.x0 + (g.x1 - g.x0) * t, y: g.y0 + (g.y1 - g.y0) * t, angle: Math.atan2(g.y1 - g.y0, g.x1 - g.x0) };
4189
4244
  },
4190
4245
  trim(start = 0, end = 1) {
4191
- return chunkX62H3RBG_cjs.trimPolylines(lines, start, end).map((l) => l.closed ? [...l.pts, l.pts[0], l.pts[1]] : l.pts);
4246
+ return chunkLALEBV63_cjs.trimPolylines(lines, start, end).map((l) => l.closed ? [...l.pts, l.pts[0], l.pts[1]] : l.pts);
4192
4247
  }
4193
4248
  };
4194
4249
  }
@@ -4297,7 +4352,7 @@ function distances(n, from, grid, seed) {
4297
4352
  if (n === 0) return [];
4298
4353
  if (from === "random") {
4299
4354
  const order = Array.from({ length: n }, (_, i) => i);
4300
- const r = chunkX62H3RBG_cjs.random(seed);
4355
+ const r = chunkLALEBV63_cjs.random(seed);
4301
4356
  for (let i = n - 1; i > 0; i--) {
4302
4357
  const j = Math.floor(r() * (i + 1));
4303
4358
  [order[i], order[j]] = [order[j], order[i]];
@@ -4359,7 +4414,7 @@ function wiggle(opts) {
4359
4414
  to[n] = p.around;
4360
4415
  return;
4361
4416
  }
4362
- const off = (chunkX62H3RBG_cjs.rand(seed * 7919 + pi * 104729 + s * 31 + 1) * 2 - 1) * p.amp;
4417
+ const off = (chunkLALEBV63_cjs.rand(seed * 7919 + pi * 104729 + s * 31 + 1) * 2 - 1) * p.amp;
4363
4418
  to[n] = typeof p.around === "number" ? p.around + off : `(${p.around}) ${off < 0 ? "-" : "+"} ${Math.abs(off)}`;
4364
4419
  });
4365
4420
  out.push({ at: at + s * stepLen, to, duration: stepLen, ease });
@@ -4652,7 +4707,7 @@ function computeEnvelope(audio, options = {}) {
4652
4707
  re[i] = k >= 0 && k < length ? mono[k] * win[i] : 0;
4653
4708
  im[i] = 0;
4654
4709
  }
4655
- chunkLQDDDIC4_cjs.fft(re, im);
4710
+ chunkO66JGPVV_cjs.fft(re, im);
4656
4711
  for (const n of names) {
4657
4712
  const [lo, hi] = bands[n];
4658
4713
  let p = 0;
@@ -4818,5 +4873,5 @@ exports.splitText = splitText;
4818
4873
  exports.stagger = stagger;
4819
4874
  exports.wiggle = wiggle;
4820
4875
  exports.withFade = withFade;
4821
- //# sourceMappingURL=chunk-MAAM45ZX.cjs.map
4822
- //# sourceMappingURL=chunk-MAAM45ZX.cjs.map
4876
+ //# sourceMappingURL=chunk-7M63Y6MO.cjs.map
4877
+ //# sourceMappingURL=chunk-7M63Y6MO.cjs.map