pixi-effects 0.42.0 → 0.43.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 (37) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/README.md +5 -5
  3. package/ai/chat-template.html +3 -3
  4. package/dist/{Base-Jgkud1Aw.d.ts → Base-DKFQtqzW.d.ts} +1 -1
  5. package/dist/{Base-DQWIJ6bJ.d.cts → Base-lb5Uhaqu.d.cts} +1 -1
  6. package/dist/Controller.d.cts +2 -2
  7. package/dist/Controller.d.ts +2 -2
  8. package/dist/{Movie-CBVFZ1kg.d.ts → Movie-B_qQIYV6.d.ts} +3 -1
  9. package/dist/{Movie-BHGhg-fJ.d.cts → Movie-BdUm2Mc9.d.cts} +3 -1
  10. package/dist/{chunk-NFJKBWYO.js → chunk-6N5ZBUOU.js} +21 -2
  11. package/dist/chunk-6N5ZBUOU.js.map +1 -0
  12. package/dist/{chunk-WVZLAIKK.cjs → chunk-BEGOKE7Q.cjs} +21 -2
  13. package/dist/chunk-BEGOKE7Q.cjs.map +1 -0
  14. package/dist/index.cjs +25 -25
  15. package/dist/index.d.cts +4 -4
  16. package/dist/index.d.ts +4 -4
  17. package/dist/index.js +1 -1
  18. package/dist/loader.css +1 -1
  19. package/dist/present.cjs +1 -1
  20. package/dist/present.d.cts +2 -2
  21. package/dist/present.d.ts +2 -2
  22. package/dist/present.js +1 -1
  23. package/dist/three.cjs +81 -23
  24. package/dist/three.cjs.map +1 -1
  25. package/dist/three.d.cts +17 -2
  26. package/dist/three.d.ts +17 -2
  27. package/dist/three.js +83 -25
  28. package/dist/three.js.map +1 -1
  29. package/dist/{types-DIh93lFb.d.cts → types-DiNHtdrS.d.cts} +19 -0
  30. package/dist/{types-DIh93lFb.d.ts → types-DiNHtdrS.d.ts} +19 -0
  31. package/docs/dsl.md +3 -2
  32. package/llms-full.txt +4 -3
  33. package/package.json +2 -1
  34. package/skills/pixi-effects/SKILL.md +1 -1
  35. package/skills/pixi-effects/template.html +6 -6
  36. package/dist/chunk-NFJKBWYO.js.map +0 -1
  37. package/dist/chunk-WVZLAIKK.cjs.map +0 -1
@@ -1,3 +1,5 @@
1
+ import { TextureSource } from 'pixi.js';
2
+
1
3
  /** What an author writes in `composition.stops`: a time in seconds, or an object. */
2
4
  interface StopSpec {
3
5
  /** Where playback stops, in seconds (negative counts back from the end). */
@@ -103,6 +105,21 @@ interface FogState {
103
105
  amount: number;
104
106
  }
105
107
 
108
+ /**
109
+ * The GPU device PixiJS draws with, for a layer that draws on the same device (a `three` layer with `renderer: 'webgpu'`): it renders into a texture of its own and PixiJS samples that
110
+ * texture directly, with no canvas of its own and no copy. A page keeps only so many canvases with a graphics context (about 32 for WebGPU); a device has no such limit.
111
+ *
112
+ * This leans on one PixiJS detail: `GpuTextureSystem` takes the `GPUTexture` of a source from `source._gpuData[renderer.uid]` when one is already there, and never makes its own.
113
+ * `adopt` checks that the field is where it expects and throws when it is not (a PixiJS that changed it), so a layer falls back to its own canvas instead of drawing nothing.
114
+ */
115
+
116
+ interface SharedGpu {
117
+ /** The `GPUDevice` of PixiJS's WebGPU renderer. */
118
+ device: unknown;
119
+ /** Make `source` sample `texture` (a `GPUTexture` made on `device` by someone else). The texture stays its maker's: destroying the source does not destroy it. */
120
+ adopt(source: TextureSource, texture: unknown): void;
121
+ }
122
+
106
123
  /**
107
124
  * pixi-effects public DSL types.
108
125
  *
@@ -858,6 +875,8 @@ interface CompositionShape {
858
875
  vars?: Record<string, number>;
859
876
  /** What a layer that lives in the composition's space (a `three` layer with `followCamera` / `followLights`) asks for: the camera and the lights alive at time `t` (the composition's own clock). Set by the composition. */
860
877
  spaceAt?: (t: number) => SpaceNow;
878
+ /** The GPU device PixiJS draws with, on the root shape only and only when PixiJS runs on WebGPU: a `three` layer with `renderer: 'webgpu'` draws on it instead of on a canvas of its own. */
879
+ gpu?: SharedGpu;
861
880
  }
862
881
  /** The composition's camera and the lights alive at one moment (what its `camera` and `light` layers say, after their keyframes). */
863
882
  interface SpaceNow {
package/docs/dsl.md CHANGED
@@ -786,6 +786,7 @@ three({
786
786
 
787
787
  The layer's own three.js renderer is `WebGLRenderer` unless you ask for `renderer: 'webgpu'`, which makes it three's `WebGPURenderer` (the picture is still copied into the layer's texture, so filters, masks, blend modes and mattes work as on any layer). It needs three 0.17x or later and `three/webgpu` in the import map next to `three` (both files import the same `three.core.js`, so they share one copy of three). Where the browser has no WebGPU, three itself falls back to its WebGL2 backend.
788
788
 
789
+ - **In a movie that PixiJS draws on WebGPU, a `'webgpu'` layer draws on PixiJS's own GPU device** (`shareGpu`, default `true`): it renders into a texture that PixiJS samples directly, with no canvas of its own and no copy. The picture is the same (measured 2026-10-17: 0 of 255 different in every channel of nine pictures, with antialias, fog and tone mapping), it is faster (spike, one Apple machine: 0.7 against 1.1 ms for one layer, 1.6 against 5.7 ms for ten), and the limit of about 32 WebGPU canvases does not apply (60 layers drew in the test film `three-budget`; a layer on the shared device is not counted in the budget warning). `shareGpu: false` gives the layer a canvas of its own as before, and so does a movie PixiJS draws on WebGL (the layer then counts against the 32). If the shared route fails, the layer says so once and is made again on its own canvas before `setup` runs. `movie.inspect()` lists `sharedGpu` for such a layer. This reaches into one field of PixiJS (`source._gpuData`); a PixiJS that moved it makes every layer fall back, with that one warning.
789
790
  - `ctx.renderer` is a `WebGLRenderer` or a `WebGPURenderer`; `setClearColor`, `toneMapping` and `toneMappingExposure` exist on both.
790
791
  - **The default stays `'webgl'`.** A scene built the way AI usually writes three code (`ShaderMaterial` / `RawShaderMaterial` with GLSL, a `WebGLRenderTarget` post chain with fullscreen quads, `onBeforeCompile`) cannot run on three's WebGPU renderer; it needs a rewrite with node materials (TSL). Tried on four large stock clips (custom bloom, 4K): all four stop with `Material "ShaderMaterial" is not compatible`. Use `'webgpu'` for scenes made of the built-in `Mesh*Material`s, `InstancedMesh`, `Points` and lights. At 1280×720 and 3840×2160 an MP4 export took the same time with either renderer.
791
792
  - **One word for a whole page:** `registerThree({ renderer: 'webgpu' })` is the default of every three layer that names no `renderer` of its own (the layer's own value wins; `registerThree()` means `'webgl'`).
@@ -868,7 +869,7 @@ keyframes: [
868
869
 
869
870
  1. Call `registerThree()` before `Movie.init` builds a composition containing a `type: 'three'` sequence — the sequence type is looked up by name at build time.
870
871
  2. `update(t)` must derive all state purely from `t` — no wall clock, no unseeded randomness. Playback and export both seek arbitrarily; anything else in `update` means identical timestamps can render different pixels. **`check` holds the layer to it, as it does a `canvas` layer:** it draws the scene at four moments in two orders (one draw thrown away first) and reports a layer whose same frame is not the same picture (a clock, `Math.random`, a mixer advanced with `mixer.update(dt)`, a speed that builds up from call to call), naming the moments and the fix; a `Math.random` / `Date.now` / `performance.now` / `new Date` in `update` also warns at build. It prints a `three` line when every layer passed, on WebGL and WebGPU alike.
871
- 3. Each three sequence owns one renderer and so one graphics context (an offscreen canvas). A browser keeps about **16 live WebGL contexts for the whole page** (PixiJS and a `shader` layer use some too) and about **32 WebGPU ones** (measured on Chrome, 2026-10-15); layers beyond that draw nothing, without an error. The layer therefore says so: once, when more than 12 WebGL (or 28 WebGPU) three layers are alive (with the count and the way out: put several objects in one layer, destroy layers that are over, or use the other renderer for some), and when the browser takes a layer's context away (`webglcontextlost`: the layer is named). Prefer one three layer with many objects to many layers.
872
+ 3. Each three sequence owns one renderer and so one graphics context (an offscreen canvas). A browser keeps about **16 live WebGL contexts for the whole page** (PixiJS and a `shader` layer use some too) and about **32 WebGPU ones** (measured on Chrome, 2026-10-15; a `'webgpu'` layer that shares PixiJS's GPU device has no context of its own and is not counted); layers beyond that draw nothing, without an error. The layer therefore says so: once, when more than 12 WebGL (or 28 WebGPU) three layers are alive (with the count and the way out: put several objects in one layer, destroy layers that are over, or use the other renderer for some), and when the browser takes a layer's context away (`webglcontextlost`: the layer is named). Prefer one three layer with many objects to many layers.
872
873
  4. Like `custom` filters, the spec carries functions (`setup`, optionally `update` / `dispose`) and is **not** JSON-serializable.
873
874
  5. The scene renders into the layer rectangle: anything that projects outside it is clipped at the layer's edges. Frame the camera with margin for the whole animation — rotating objects often project taller/wider than their resting pose (a `TorusKnotGeometry(radius, tube)`, for instance, extends to `1.5 * radius + tube`, not `radius`).
874
875
 
@@ -1448,7 +1449,7 @@ A filter by name, as plain data. `type` is the filter's class name without `Filt
1448
1449
  keyframes: [{ at: 0, to: { 'filters.halo.outerStrength': 6 }, duration: 1 }] } // animate any scalar option by name
1449
1450
  ```
1450
1451
 
1451
- Name a filter (`name: 'halo'`) to animate it with `'filters.halo.<option>'`. `blur`, `noise`, `alpha`, `colorMatrix` and `grain` need no extra package (`grain` is this library's own). All the others come from the **`pixi-filters`** package, which is loaded the first time a layer uses one (add `"pixi-filters": "https://esm.sh/pixi-filters@6.1.5?external=pixi.js"` to the import map, or `npm i pixi-filters`; `skills/pixi-effects/template.html` already has it). A misspelt type says what you probably meant. Rendering and exporting were verified in a real browser for 40 of them (`examples/_checks/named-filters.html` and `named-filters-2.html`); `colorMap` and `simpleLightmap` need a texture, which JSON cannot hold, so use `custom` for those two:
1452
+ Name a filter (`name: 'halo'`) to animate it with `'filters.halo.<option>'`. `blur`, `noise`, `alpha`, `colorMatrix` and `grain` need no extra package (`grain` is this library's own). All the others come from the **`pixi-filters`** package, which is loaded the first time a layer uses one (add `"pixi-filters": "https://esm.sh/pixi-filters@6.1.5?external=pixi.js"` to the import map, or `npm i pixi-filters`; `skills/pixi-effects/template.html` already has it). A misspelt type says what you probably meant. Rendering and exporting were verified in a real browser for 40 of them (`tests/films/named-filters.html` and `named-filters-2.html`); `colorMap` and `simpleLightmap` need a texture, which JSON cannot hold, so use `custom` for those two:
1452
1453
 
1453
1454
  | `type` | Options (the useful ones) | What it does |
1454
1455
  |---|---|---|
package/llms-full.txt CHANGED
@@ -6,7 +6,7 @@ Concatenation of the skill, cheatsheet, pitfalls, recipes and the DSL / API / me
6
6
 
7
7
  <!-- ===== skills/pixi-effects/SKILL.md ===== -->
8
8
 
9
- > Written for pixi-effects 0.42.0. Update this skill with `npx skills update`; the newest docs are at https://yjmtmtk.github.io/pixi-effects/
9
+ > Written for pixi-effects 0.43.0. Update this skill with `npx skills update`; the newest docs are at https://yjmtmtk.github.io/pixi-effects/
10
10
 
11
11
  # pixi-effects — write a video as data
12
12
 
@@ -3061,6 +3061,7 @@ three({
3061
3061
 
3062
3062
  The layer's own three.js renderer is `WebGLRenderer` unless you ask for `renderer: 'webgpu'`, which makes it three's `WebGPURenderer` (the picture is still copied into the layer's texture, so filters, masks, blend modes and mattes work as on any layer). It needs three 0.17x or later and `three/webgpu` in the import map next to `three` (both files import the same `three.core.js`, so they share one copy of three). Where the browser has no WebGPU, three itself falls back to its WebGL2 backend.
3063
3063
 
3064
+ - **In a movie that PixiJS draws on WebGPU, a `'webgpu'` layer draws on PixiJS's own GPU device** (`shareGpu`, default `true`): it renders into a texture that PixiJS samples directly, with no canvas of its own and no copy. The picture is the same (measured 2026-10-17: 0 of 255 different in every channel of nine pictures, with antialias, fog and tone mapping), it is faster (spike, one Apple machine: 0.7 against 1.1 ms for one layer, 1.6 against 5.7 ms for ten), and the limit of about 32 WebGPU canvases does not apply (60 layers drew in the test film `three-budget`; a layer on the shared device is not counted in the budget warning). `shareGpu: false` gives the layer a canvas of its own as before, and so does a movie PixiJS draws on WebGL (the layer then counts against the 32). If the shared route fails, the layer says so once and is made again on its own canvas before `setup` runs. `movie.inspect()` lists `sharedGpu` for such a layer. This reaches into one field of PixiJS (`source._gpuData`); a PixiJS that moved it makes every layer fall back, with that one warning.
3064
3065
  - `ctx.renderer` is a `WebGLRenderer` or a `WebGPURenderer`; `setClearColor`, `toneMapping` and `toneMappingExposure` exist on both.
3065
3066
  - **The default stays `'webgl'`.** A scene built the way AI usually writes three code (`ShaderMaterial` / `RawShaderMaterial` with GLSL, a `WebGLRenderTarget` post chain with fullscreen quads, `onBeforeCompile`) cannot run on three's WebGPU renderer; it needs a rewrite with node materials (TSL). Tried on four large stock clips (custom bloom, 4K): all four stop with `Material "ShaderMaterial" is not compatible`. Use `'webgpu'` for scenes made of the built-in `Mesh*Material`s, `InstancedMesh`, `Points` and lights. At 1280×720 and 3840×2160 an MP4 export took the same time with either renderer.
3066
3067
  - **One word for a whole page:** `registerThree({ renderer: 'webgpu' })` is the default of every three layer that names no `renderer` of its own (the layer's own value wins; `registerThree()` means `'webgl'`).
@@ -3143,7 +3144,7 @@ keyframes: [
3143
3144
 
3144
3145
  1. Call `registerThree()` before `Movie.init` builds a composition containing a `type: 'three'` sequence — the sequence type is looked up by name at build time.
3145
3146
  2. `update(t)` must derive all state purely from `t` — no wall clock, no unseeded randomness. Playback and export both seek arbitrarily; anything else in `update` means identical timestamps can render different pixels. **`check` holds the layer to it, as it does a `canvas` layer:** it draws the scene at four moments in two orders (one draw thrown away first) and reports a layer whose same frame is not the same picture (a clock, `Math.random`, a mixer advanced with `mixer.update(dt)`, a speed that builds up from call to call), naming the moments and the fix; a `Math.random` / `Date.now` / `performance.now` / `new Date` in `update` also warns at build. It prints a `three` line when every layer passed, on WebGL and WebGPU alike.
3146
- 3. Each three sequence owns one renderer and so one graphics context (an offscreen canvas). A browser keeps about **16 live WebGL contexts for the whole page** (PixiJS and a `shader` layer use some too) and about **32 WebGPU ones** (measured on Chrome, 2026-10-15); layers beyond that draw nothing, without an error. The layer therefore says so: once, when more than 12 WebGL (or 28 WebGPU) three layers are alive (with the count and the way out: put several objects in one layer, destroy layers that are over, or use the other renderer for some), and when the browser takes a layer's context away (`webglcontextlost`: the layer is named). Prefer one three layer with many objects to many layers.
3147
+ 3. Each three sequence owns one renderer and so one graphics context (an offscreen canvas). A browser keeps about **16 live WebGL contexts for the whole page** (PixiJS and a `shader` layer use some too) and about **32 WebGPU ones** (measured on Chrome, 2026-10-15; a `'webgpu'` layer that shares PixiJS's GPU device has no context of its own and is not counted); layers beyond that draw nothing, without an error. The layer therefore says so: once, when more than 12 WebGL (or 28 WebGPU) three layers are alive (with the count and the way out: put several objects in one layer, destroy layers that are over, or use the other renderer for some), and when the browser takes a layer's context away (`webglcontextlost`: the layer is named). Prefer one three layer with many objects to many layers.
3147
3148
  4. Like `custom` filters, the spec carries functions (`setup`, optionally `update` / `dispose`) and is **not** JSON-serializable.
3148
3149
  5. The scene renders into the layer rectangle: anything that projects outside it is clipped at the layer's edges. Frame the camera with margin for the whole animation — rotating objects often project taller/wider than their resting pose (a `TorusKnotGeometry(radius, tube)`, for instance, extends to `1.5 * radius + tube`, not `radius`).
3149
3150
 
@@ -3723,7 +3724,7 @@ A filter by name, as plain data. `type` is the filter's class name without `Filt
3723
3724
  keyframes: [{ at: 0, to: { 'filters.halo.outerStrength': 6 }, duration: 1 }] } // animate any scalar option by name
3724
3725
  ```
3725
3726
 
3726
- Name a filter (`name: 'halo'`) to animate it with `'filters.halo.<option>'`. `blur`, `noise`, `alpha`, `colorMatrix` and `grain` need no extra package (`grain` is this library's own). All the others come from the **`pixi-filters`** package, which is loaded the first time a layer uses one (add `"pixi-filters": "https://esm.sh/pixi-filters@6.1.5?external=pixi.js"` to the import map, or `npm i pixi-filters`; `skills/pixi-effects/template.html` already has it). A misspelt type says what you probably meant. Rendering and exporting were verified in a real browser for 40 of them (`examples/_checks/named-filters.html` and `named-filters-2.html`); `colorMap` and `simpleLightmap` need a texture, which JSON cannot hold, so use `custom` for those two:
3727
+ Name a filter (`name: 'halo'`) to animate it with `'filters.halo.<option>'`. `blur`, `noise`, `alpha`, `colorMatrix` and `grain` need no extra package (`grain` is this library's own). All the others come from the **`pixi-filters`** package, which is loaded the first time a layer uses one (add `"pixi-filters": "https://esm.sh/pixi-filters@6.1.5?external=pixi.js"` to the import map, or `npm i pixi-filters`; `skills/pixi-effects/template.html` already has it). A misspelt type says what you probably meant. Rendering and exporting were verified in a real browser for 40 of them (`tests/films/named-filters.html` and `named-filters-2.html`); `colorMap` and `simpleLightmap` need a texture, which JSON cannot hold, so use `custom` for those two:
3727
3728
 
3728
3729
  | `type` | Options (the useful ones) | What it does |
3729
3730
  |---|---|---|
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pixi-effects",
3
- "version": "0.42.0",
3
+ "version": "0.43.0",
4
4
  "description": "Declarative composition and video rendering for the web. Built on PixiJS v8.",
5
5
  "type": "module",
6
6
  "exports": {
@@ -104,6 +104,7 @@
104
104
  "test": "vitest run",
105
105
  "test:fast": "SKIP_BROWSER_TESTS=1 vitest run",
106
106
  "test:films": "vitest run tests/films",
107
+ "test:site": "vitest run tests/site",
107
108
  "release:check": "node scripts/release-check.mjs",
108
109
  "post-publish:check": "node scripts/post-publish-check.mjs",
109
110
  "test:changed": "node scripts/test-changed.mjs",
@@ -3,7 +3,7 @@ name: pixi-effects
3
3
  description: Use when asked to make, animate, preview or export a video, motion graphics, an animated title, a lower third, kinetic type, a promo, a slideshow with transitions, an animated chart or data story, a social clip, a 2.5D / depth scene, or music and sound for a video, written as code in JavaScript or HTML. pixi-effects is a JS library where a video is a plain-object composition (text, shape, image, video, audio and file-free sfx / music, camera, light and shader layers with keyframes) that plays in the browser, is checked by a tool the agent can run, and exports MP4 / WebM / MOV with no server. Not for a project that already uses another video tool (Remotion, Motion Canvas, ffmpeg); follow that tool instead.
4
4
  ---
5
5
 
6
- > Written for pixi-effects 0.42.0. Update this skill with `npx skills update`; the newest docs are at https://yjmtmtk.github.io/pixi-effects/
6
+ > Written for pixi-effects 0.43.0. Update this skill with `npx skills update`; the newest docs are at https://yjmtmtk.github.io/pixi-effects/
7
7
 
8
8
  # pixi-effects — write a video as data
9
9
 
@@ -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.42.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.43.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 */
@@ -37,17 +37,17 @@
37
37
  "imports": {
38
38
  "pixi.js": "https://esm.sh/pixi.js@8.22.0?bundle-deps",
39
39
  "mediabunny": "https://esm.sh/mediabunny",
40
- "pixi-effects": "https://cdn.jsdelivr.net/npm/pixi-effects@0.42.0/dist/index.js",
41
- "pixi-effects/controller": "https://cdn.jsdelivr.net/npm/pixi-effects@0.42.0/dist/Controller.js",
42
- "pixi-effects/present": "https://cdn.jsdelivr.net/npm/pixi-effects@0.42.0/dist/present.js",
43
- "pixi-effects/three": "https://cdn.jsdelivr.net/npm/pixi-effects@0.42.0/dist/three.js",
40
+ "pixi-effects": "https://cdn.jsdelivr.net/npm/pixi-effects@0.43.0/dist/index.js",
41
+ "pixi-effects/controller": "https://cdn.jsdelivr.net/npm/pixi-effects@0.43.0/dist/Controller.js",
42
+ "pixi-effects/present": "https://cdn.jsdelivr.net/npm/pixi-effects@0.43.0/dist/present.js",
43
+ "pixi-effects/three": "https://cdn.jsdelivr.net/npm/pixi-effects@0.43.0/dist/three.js",
44
44
  "pixi-filters": "https://esm.sh/pixi-filters@6.1.5?external=pixi.js",
45
45
  "three": "https://esm.sh/three@0.178.0",
46
46
  "three/addons/": "https://cdn.jsdelivr.net/npm/three@0.178.0/examples/jsm/"
47
47
  }
48
48
  }
49
49
  </script>
50
- <link rel="modulepreload" href="https://cdn.jsdelivr.net/npm/pixi-effects@0.42.0/dist/index.js" /> <!-- after the import map: the library downloads while the page is parsed -->
50
+ <link rel="modulepreload" href="https://cdn.jsdelivr.net/npm/pixi-effects@0.43.0/dist/index.js" /> <!-- after the import map: the library downloads while the page is parsed -->
51
51
 
52
52
  <script type="module">
53
53
  import { Movie, kenBurns, withFade } from 'pixi-effects';