pixi-effects 0.26.2 → 0.26.3

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 (42) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +5 -5
  3. package/ai/chat-template.html +3 -3
  4. package/ai/tools/render.mjs +16 -11
  5. package/dist/Composition-3O7L2VL5.js +7 -0
  6. package/dist/{Composition-H4QA24MV.js.map → Composition-3O7L2VL5.js.map} +1 -1
  7. package/dist/Composition-EE3KEE2L.cjs +16 -0
  8. package/dist/{Composition-HM4YT5L7.cjs.map → Composition-EE3KEE2L.cjs.map} +1 -1
  9. package/dist/Controller.d.cts +1 -1
  10. package/dist/Controller.d.ts +1 -1
  11. package/dist/{Movie-i05Oh9mJ.d.cts → Movie-BWLLWRRe.d.cts} +10 -3
  12. package/dist/{Movie-DVeDTdnc.d.ts → Movie-BkP82KXS.d.ts} +10 -3
  13. package/dist/Presenter.d.cts +1 -1
  14. package/dist/Presenter.d.ts +1 -1
  15. package/dist/{chunk-WPXDRSGN.cjs → chunk-A225HYSY.cjs} +205 -27
  16. package/dist/chunk-A225HYSY.cjs.map +1 -0
  17. package/dist/{chunk-X3HB4DSR.js → chunk-FXAHNPBP.js} +205 -27
  18. package/dist/chunk-FXAHNPBP.js.map +1 -0
  19. package/dist/{chunk-555URQHQ.cjs → chunk-IIDBEYQH.cjs} +59 -59
  20. package/dist/{chunk-555URQHQ.cjs.map → chunk-IIDBEYQH.cjs.map} +1 -1
  21. package/dist/{chunk-GBUM7X2S.js → chunk-WMMZDPWK.js} +3 -3
  22. package/dist/{chunk-GBUM7X2S.js.map → chunk-WMMZDPWK.js.map} +1 -1
  23. package/dist/index.cjs +142 -48
  24. package/dist/index.cjs.map +1 -1
  25. package/dist/index.d.cts +1 -1
  26. package/dist/index.d.ts +1 -1
  27. package/dist/index.js +119 -25
  28. package/dist/index.js.map +1 -1
  29. package/dist/loader.css +1 -1
  30. package/dist/three.cjs +5 -5
  31. package/dist/three.js +1 -1
  32. package/docs/api.md +11 -5
  33. package/docs/dsl.md +2 -0
  34. package/llms-full.txt +17 -9
  35. package/package.json +1 -1
  36. package/skills/pixi-effects/SKILL.md +1 -1
  37. package/skills/pixi-effects/reference/cheatsheet.md +3 -3
  38. package/skills/pixi-effects/template.html +6 -6
  39. package/dist/Composition-H4QA24MV.js +0 -7
  40. package/dist/Composition-HM4YT5L7.cjs +0 -16
  41. package/dist/chunk-WPXDRSGN.cjs.map +0 -1
  42. package/dist/chunk-X3HB4DSR.js.map +0 -1
package/dist/loader.css CHANGED
@@ -1,7 +1,7 @@
1
1
  /*
2
2
  * pixi-effects loader: one line of HTML on the page, a few lines of CSS, no JavaScript of your own.
3
3
  *
4
- * <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/pixi-effects@0.26.2/dist/loader.css">
4
+ * <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/pixi-effects@0.26.3/dist/loader.css">
5
5
  * <div class="stage" style="position: relative"> <!-- any box the canvas sits in, with position: relative -->
6
6
  * <canvas id="stage" width="1280" height="720"></canvas>
7
7
  * <div class="pe-loader" data-label="LOADING"></div> <!-- or class="pe-loader pe-loader--bar" -->
package/dist/three.cjs CHANGED
@@ -1,12 +1,12 @@
1
1
  'use strict';
2
2
 
3
- var chunkWPXDRSGN_cjs = require('./chunk-WPXDRSGN.cjs');
3
+ var chunkA225HYSY_cjs = require('./chunk-A225HYSY.cjs');
4
4
  require('./chunk-ECA2WNRX.cjs');
5
5
  require('./chunk-TWZNOUM7.cjs');
6
6
  var pixi_js = require('pixi.js');
7
7
  var three$1 = require('three');
8
8
 
9
- var ThreeSequence = class extends chunkWPXDRSGN_cjs.Sequence {
9
+ var ThreeSequence = class extends chunkA225HYSY_cjs.Sequence {
10
10
  _renderer = null;
11
11
  _scene = null;
12
12
  _camera = null;
@@ -25,7 +25,7 @@ var ThreeSequence = class extends chunkWPXDRSGN_cjs.Sequence {
25
25
  if (this.duration === void 0) {
26
26
  this.duration = this.parent?.duration ?? this.root.duration;
27
27
  }
28
- const scope = chunkWPXDRSGN_cjs.buildScope(this, this.parent, this.root);
28
+ const scope = chunkA225HYSY_cjs.buildScope(this, this.parent, this.root);
29
29
  const width = resolveDim(spec.width, scope) ?? this.parent?.width ?? this.root.width;
30
30
  const height = resolveDim(spec.height, scope) ?? this.parent?.height ?? this.root.height;
31
31
  const resolution = spec.resolution ?? 1;
@@ -145,12 +145,12 @@ var ThreeSequence = class extends chunkWPXDRSGN_cjs.Sequence {
145
145
  function resolveDim(v, scope) {
146
146
  if (v === void 0) return void 0;
147
147
  if (typeof v === "number") return v;
148
- return chunkWPXDRSGN_cjs.evaluateExpr(v, scope);
148
+ return chunkA225HYSY_cjs.evaluateExpr(v, scope);
149
149
  }
150
150
 
151
151
  // src/three/index.ts
152
152
  function registerThree() {
153
- chunkWPXDRSGN_cjs.registerSequenceType("three", ThreeSequence);
153
+ chunkA225HYSY_cjs.registerSequenceType("three", ThreeSequence);
154
154
  }
155
155
  function three(spec) {
156
156
  return spec;
package/dist/three.js CHANGED
@@ -1,4 +1,4 @@
1
- import { Sequence, buildScope, evaluateExpr, registerSequenceType } from './chunk-X3HB4DSR.js';
1
+ import { Sequence, buildScope, evaluateExpr, registerSequenceType } from './chunk-FXAHNPBP.js';
2
2
  import './chunk-TLYTYBWX.js';
3
3
  import './chunk-BXOCEZ4U.js';
4
4
  import { Texture, Sprite } from 'pixi.js';
package/docs/api.md CHANGED
@@ -155,14 +155,16 @@ Renders the entire timeline to a single video file. Pauses playback first.
155
155
 
156
156
  ```ts
157
157
  interface RenderOptions {
158
- format?: 'mp4' | 'mov' | 'webm' | 'mkv'; // default 'mp4'
158
+ format?: 'mp4' | 'mov' | 'webm' | 'mkv' | 'wav' | 'ogg'; // default 'mp4'; wav / ogg: ONLY the movie's sound (an error when it has none)
159
159
  video?: {
160
- codec?: string; // default per format (mp4/mov→avc, webm/mkv→vp9)
161
- bitrate?: 'very-low' | 'low' | 'medium' | 'high' | 'very-high'; // default 'high'
160
+ codec?: string; // 'avc' (H.264) | 'hevc' | 'vp9' | 'av1' | 'vp8'; default per format (mp4/mov→avc, webm→vp9, mkv→vp9)
161
+ bitrate?: 'very-low' | 'low' | 'medium' | 'high' | 'very-high' | number | string; // default 'high'; or bits a second: 8_000_000, '8M', '800k'
162
+ hardware?: 'no-preference' | 'prefer-hardware' | 'prefer-software'; // where to encode (default 'no-preference')
163
+ keyFrameInterval?: number; // seconds between keyframes (default 2): shorter seeks faster, a bigger file
162
164
  };
163
165
  audio?: {
164
- codec?: string; // default per format (mp4/mov→aac, webm/mkv→opus)
165
- bitrate?: 'very-low' | 'low' | 'medium' | 'high' | 'very-high'; // default 'high'
166
+ codec?: string; // default per format (mp4/mov→aac, webm/mkv/ogg→opus, wav→pcm-s16)
167
+ bitrate?: 'very-low' | 'low' | 'medium' | 'high' | 'very-high' | number | string; // default 'high' (not for wav: it is not compressed)
166
168
  };
167
169
  motionBlur?: boolean | number | { samples?: number; shutter?: number }; // overrides movie.init's; false turns it off
168
170
  }
@@ -177,6 +179,10 @@ interface RenderOptions {
177
179
  - After a blurred snapshot, contact sheet or render the playhead and the stage are on an exact frame again, not a sample.
178
180
  - `pixi-effects-render --motion-blur 8 [--shutter 0.5]` sets it for one file.
179
181
 
182
+ **Video codec:** unless you name `video.codec`, the usual one for the format is used (mp4 / mov: `avc`; webm: `vp9`); if this browser cannot encode it, the next that works is used and a warning says so. A codec you name is never swapped: it is used, or the error names the one that would work (`"av1" ... "avc" would work: render({ format: 'mp4', video: { codec: 'avc' } })`). `hevc` and `av1` make smaller files at the same quality but play in fewer places than `avc`; `hardware: 'prefer-hardware'` asks the GPU's encoder.
183
+
184
+ **Sound only:** `render({ format: 'wav' })` (16-bit PCM, no loss) or `render({ format: 'ogg' })` (Opus) writes the movie's mixed sound without drawing a frame: a tune written as `music` becomes an audio file. `range` cuts it like a video. `pixi-effects render page.html -o mix.wav` does the same from a script. `mp3` and `flac` are not offered: browsers cannot encode them.
185
+
180
186
  **Audio codec fallback:** unless you name `audio.codec`, the first codec this browser can actually encode is used (mp4: `aac`, then `opus`, `mp3`, `flac`; webm: `opus`, `vorbis`). Chrome on Linux has no AAC encoder, so there an mp4 with sound carries Opus and a warning says so (before, the export failed with a message about encoder configurations). A codec you name is never swapped: if it cannot be encoded the error says which one would work.
181
187
 
182
188
  Returns a `Blob` whose `type` is the container's MIME (e.g. `video/mp4`). Emits `'progress'` repeatedly during the render.
package/docs/dsl.md CHANGED
@@ -932,6 +932,8 @@ interface Keyframe {
932
932
  }
933
933
  ```
934
934
 
935
+ **Relative values.** A number in `to`, `from` or `set` may be written `'+=36'` or `'-=36'`: it is measured from where the property stands when the keyframe starts (GSAP's own relative values), and the right side is an expression (`'-=GW*0.1'`). `from: { x: '-=36' }` starts 36 px to the left of where the layer is and arrives there (the usual slide-in); `to: { y: '+=40' }` moves 40 px further. Several keyframes one after another stack, whatever order you seek in; with `repeat` the same move plays again from the same start (it does not stack). A relative value in `initial`, a style, or the start side of a `from` + `to` keyframe has nothing to be measured from: it warns and is read as the number.
936
+
935
937
  The four kinds are mutually exclusive per keyframe:
936
938
 
937
939
  - **`set`** — instantaneous property assignment at `at`.
package/llms-full.txt CHANGED
@@ -6,7 +6,7 @@ Concatenation of the skill, cheatsheet, pitfalls, recipes and the DSL / API refe
6
6
 
7
7
  <!-- ===== skills/pixi-effects/SKILL.md ===== -->
8
8
 
9
- > Written for pixi-effects 0.26.2. Update this skill with `npx skills update`; the newest docs are at https://yjmtmtk.github.io/pixi-effects/
9
+ > Written for pixi-effects 0.26.3. 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
 
@@ -75,7 +75,7 @@ A video is **data**: a tree of plain objects. Everything below is a JS object yo
75
75
  | Canvas | pixels, origin top-left, `+x` right, `+y` down. Typical: 1280×720 @ 30 fps |
76
76
  | Angles | **degrees**: `rotation`, `skew*`, `rotationX`, `rotationY` |
77
77
  | Depth (`z`) | `+z` toward the viewer (bigger, nearer) |
78
- | Numbers | any number may be an **expression string**: `'GW/2 - w/2'`, `'min(W,H)*0.4'` (build-time only; no per-frame time variable). Functions: `min max abs floor ceil round sqrt pow sin cos tan lerp clamp smoothstep mod step`, `PI`, and **seeded** `rand(seed)` (0…1) / `noise(x, seed)` (−1…1): give each layer its own seed, never `Math.random()`. **No relative values** (`'+=36'` does not exist: write the absolute start, `from: { x: 'GW/2 - 36' }`). Expressions work in layer `x y` and the other numeric props, **not** in `line.from / to`, `polygon.points` or `path.d`: compute those in JS (`const W = 1920`) |
78
+ | Numbers | any number may be an **expression string**: `'GW/2 - w/2'`, `'min(W,H)*0.4'` (build-time only; no per-frame time variable). Functions: `min max abs floor ceil round sqrt pow sin cos tan lerp clamp smoothstep mod step`, `PI`, and **seeded** `rand(seed)` (0…1) / `noise(x, seed)` (−1…1): give each layer its own seed, never `Math.random()`. **Relative values** work in a keyframe's `to` / `from` / `set` (not in `initial`, a style, or the start of a `from`+`to`): `'+=36'` / `'-=36'` are measured from where the layer stands, and the right side is an expression (`'-=GW*0.1'`). A slide-in is `from: { x: '-=36' }`, a nudge `to: { y: '+=40' }`; keyframes one after another stack; with `repeat` the same move replays from the same start (it does not stack). Expressions work in layer `x y` and the other numeric props, **not** in `line.from / to`, `polygon.points` or `path.d`: compute those in JS (`const W = 1920`) |
79
79
  | Colours | `'#rrggbb'` strings or numbers; text colour is `style.fill`, shape colour is `fillColor`/`strokeColor`, image colour is `tint` |
80
80
  | Z-order | **array order**, later = on top. A layer is hidden (not removed) outside `[at, at+duration)` and keeps its last values |
81
81
  | Time of a sequence's `at` | seconds from the start of its **parent composition** (default 0) |
@@ -111,7 +111,7 @@ blendMode: 'normal' | 'add' | 'screen' | 'multiply' | 'overlay' | 'soft-light' |
111
111
 
112
112
  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`.
113
113
 
114
- 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.
114
+ Keyframe kinds: `set` (jump at `at`; undone when you seek back), `to` (animate to), `from` (animate from), `from`+`to`; a value may be relative to where the layer stands (`from: { x: '-=36' }` slides in from 36 px to the left of its place). `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.
115
115
 
116
116
  ## Layer types
117
117
 
@@ -233,7 +233,7 @@ const movie = new Movie();
233
233
  await movie.init({ canvas, width, height, duration, frameRate, background, assets, composition });
234
234
  movie.play(); movie.pause();
235
235
  await movie.gotoFrame(n, true); // seek (frames; 30 fps => t = n/30)
236
- const blob = await movie.render({ format: 'mp4' }); // 'mp4' | 'webm' | 'mov' | 'mkv'; ~real-time. Part of it: `range: [2, 5]` (seconds) or `range: 'title'` (a top-level layer's span; picture and sound); `scale: 0.5` / `draft: true` (half size, low quality, no motion blur: a smaller file, not a faster drawing). Fast motion: `movie.init({ motionBlur: true })` (8 samples, 180° shutter; or a number / { samples: 2–64, shutter: 0–1 }) blurs render / snapshot / contactSheet, not live playback; a render takes `samples`× as long
236
+ const blob = await movie.render({ format: 'mp4' }); // 'mp4' | 'webm' | 'mov' | 'mkv'; ~real-time. `format: 'wav'` / `'ogg'` writes only the movie's SOUND (a `music` score as an audio file). `video: { codec: 'hevc' | 'av1' | 'vp9' | 'avc', bitrate: '8M', hardware, keyFrameInterval }` (a codec this browser cannot encode names the one that works). Part of it: `range: [2, 5]` (seconds) or `range: 'title'` (a top-level layer's span; picture and sound); `scale: 0.5` / `draft: true` (half size, low quality, no motion blur: a smaller file, not a faster drawing). Fast motion: `movie.init({ motionBlur: true })` (8 samples, 180° shutter; or a number / { samples: 2–64, shutter: 0–1 }) blurs render / snapshot / contactSheet, not live playback; a render takes `samples`× as long
237
237
  movie.on('progress', e => e.progress /* 0–100 */);
238
238
  movie.audioBuffer // mixed audio (after init), if any audio layers
239
239
  await movie.contactSheet({ count: 6, as: 'dataURL' }) // ONE image of several labelled frames — look at it. Options: frames | times | count, columns, cellWidth, as
@@ -2453,6 +2453,8 @@ interface Keyframe {
2453
2453
  }
2454
2454
  ```
2455
2455
 
2456
+ **Relative values.** A number in `to`, `from` or `set` may be written `'+=36'` or `'-=36'`: it is measured from where the property stands when the keyframe starts (GSAP's own relative values), and the right side is an expression (`'-=GW*0.1'`). `from: { x: '-=36' }` starts 36 px to the left of where the layer is and arrives there (the usual slide-in); `to: { y: '+=40' }` moves 40 px further. Several keyframes one after another stack, whatever order you seek in; with `repeat` the same move plays again from the same start (it does not stack). A relative value in `initial`, a style, or the start side of a `from` + `to` keyframe has nothing to be measured from: it warns and is read as the number.
2457
+
2456
2458
  The four kinds are mutually exclusive per keyframe:
2457
2459
 
2458
2460
  - **`set`** — instantaneous property assignment at `at`.
@@ -3347,14 +3349,16 @@ Renders the entire timeline to a single video file. Pauses playback first.
3347
3349
 
3348
3350
  ```ts
3349
3351
  interface RenderOptions {
3350
- format?: 'mp4' | 'mov' | 'webm' | 'mkv'; // default 'mp4'
3352
+ format?: 'mp4' | 'mov' | 'webm' | 'mkv' | 'wav' | 'ogg'; // default 'mp4'; wav / ogg: ONLY the movie's sound (an error when it has none)
3351
3353
  video?: {
3352
- codec?: string; // default per format (mp4/mov→avc, webm/mkv→vp9)
3353
- bitrate?: 'very-low' | 'low' | 'medium' | 'high' | 'very-high'; // default 'high'
3354
+ codec?: string; // 'avc' (H.264) | 'hevc' | 'vp9' | 'av1' | 'vp8'; default per format (mp4/mov→avc, webm→vp9, mkv→vp9)
3355
+ bitrate?: 'very-low' | 'low' | 'medium' | 'high' | 'very-high' | number | string; // default 'high'; or bits a second: 8_000_000, '8M', '800k'
3356
+ hardware?: 'no-preference' | 'prefer-hardware' | 'prefer-software'; // where to encode (default 'no-preference')
3357
+ keyFrameInterval?: number; // seconds between keyframes (default 2): shorter seeks faster, a bigger file
3354
3358
  };
3355
3359
  audio?: {
3356
- codec?: string; // default per format (mp4/mov→aac, webm/mkv→opus)
3357
- bitrate?: 'very-low' | 'low' | 'medium' | 'high' | 'very-high'; // default 'high'
3360
+ codec?: string; // default per format (mp4/mov→aac, webm/mkv/ogg→opus, wav→pcm-s16)
3361
+ bitrate?: 'very-low' | 'low' | 'medium' | 'high' | 'very-high' | number | string; // default 'high' (not for wav: it is not compressed)
3358
3362
  };
3359
3363
  motionBlur?: boolean | number | { samples?: number; shutter?: number }; // overrides movie.init's; false turns it off
3360
3364
  }
@@ -3369,6 +3373,10 @@ interface RenderOptions {
3369
3373
  - After a blurred snapshot, contact sheet or render the playhead and the stage are on an exact frame again, not a sample.
3370
3374
  - `pixi-effects-render --motion-blur 8 [--shutter 0.5]` sets it for one file.
3371
3375
 
3376
+ **Video codec:** unless you name `video.codec`, the usual one for the format is used (mp4 / mov: `avc`; webm: `vp9`); if this browser cannot encode it, the next that works is used and a warning says so. A codec you name is never swapped: it is used, or the error names the one that would work (`"av1" ... "avc" would work: render({ format: 'mp4', video: { codec: 'avc' } })`). `hevc` and `av1` make smaller files at the same quality but play in fewer places than `avc`; `hardware: 'prefer-hardware'` asks the GPU's encoder.
3377
+
3378
+ **Sound only:** `render({ format: 'wav' })` (16-bit PCM, no loss) or `render({ format: 'ogg' })` (Opus) writes the movie's mixed sound without drawing a frame: a tune written as `music` becomes an audio file. `range` cuts it like a video. `pixi-effects render page.html -o mix.wav` does the same from a script. `mp3` and `flac` are not offered: browsers cannot encode them.
3379
+
3372
3380
  **Audio codec fallback:** unless you name `audio.codec`, the first codec this browser can actually encode is used (mp4: `aac`, then `opus`, `mp3`, `flac`; webm: `opus`, `vorbis`). Chrome on Linux has no AAC encoder, so there an mp4 with sound carries Opus and a warning says so (before, the export failed with a message about encoder configurations). A codec you name is never swapped: if it cannot be encoded the error says which one would work.
3373
3381
 
3374
3382
  Returns a `Blob` whose `type` is the container's MIME (e.g. `video/mp4`). Emits `'progress'` repeatedly during the render.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pixi-effects",
3
- "version": "0.26.2",
3
+ "version": "0.26.3",
4
4
  "description": "Declarative composition and video rendering for the web. Built on PixiJS v8.",
5
5
  "type": "module",
6
6
  "exports": {
@@ -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.26.2. Update this skill with `npx skills update`; the newest docs are at https://yjmtmtk.github.io/pixi-effects/
6
+ > Written for pixi-effects 0.26.3. 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
 
@@ -9,7 +9,7 @@ A video is **data**: a tree of plain objects. Everything below is a JS object yo
9
9
  | Canvas | pixels, origin top-left, `+x` right, `+y` down. Typical: 1280×720 @ 30 fps |
10
10
  | Angles | **degrees**: `rotation`, `skew*`, `rotationX`, `rotationY` |
11
11
  | Depth (`z`) | `+z` toward the viewer (bigger, nearer) |
12
- | Numbers | any number may be an **expression string**: `'GW/2 - w/2'`, `'min(W,H)*0.4'` (build-time only; no per-frame time variable). Functions: `min max abs floor ceil round sqrt pow sin cos tan lerp clamp smoothstep mod step`, `PI`, and **seeded** `rand(seed)` (0…1) / `noise(x, seed)` (−1…1): give each layer its own seed, never `Math.random()`. **No relative values** (`'+=36'` does not exist: write the absolute start, `from: { x: 'GW/2 - 36' }`). Expressions work in layer `x y` and the other numeric props, **not** in `line.from / to`, `polygon.points` or `path.d`: compute those in JS (`const W = 1920`) |
12
+ | Numbers | any number may be an **expression string**: `'GW/2 - w/2'`, `'min(W,H)*0.4'` (build-time only; no per-frame time variable). Functions: `min max abs floor ceil round sqrt pow sin cos tan lerp clamp smoothstep mod step`, `PI`, and **seeded** `rand(seed)` (0…1) / `noise(x, seed)` (−1…1): give each layer its own seed, never `Math.random()`. **Relative values** work in a keyframe's `to` / `from` / `set` (not in `initial`, a style, or the start of a `from`+`to`): `'+=36'` / `'-=36'` are measured from where the layer stands, and the right side is an expression (`'-=GW*0.1'`). A slide-in is `from: { x: '-=36' }`, a nudge `to: { y: '+=40' }`; keyframes one after another stack; with `repeat` the same move replays from the same start (it does not stack). Expressions work in layer `x y` and the other numeric props, **not** in `line.from / to`, `polygon.points` or `path.d`: compute those in JS (`const W = 1920`) |
13
13
  | Colours | `'#rrggbb'` strings or numbers; text colour is `style.fill`, shape colour is `fillColor`/`strokeColor`, image colour is `tint` |
14
14
  | Z-order | **array order**, later = on top. A layer is hidden (not removed) outside `[at, at+duration)` and keeps its last values |
15
15
  | Time of a sequence's `at` | seconds from the start of its **parent composition** (default 0) |
@@ -45,7 +45,7 @@ blendMode: 'normal' | 'add' | 'screen' | 'multiply' | 'overlay' | 'soft-light' |
45
45
 
46
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
- 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.
48
+ Keyframe kinds: `set` (jump at `at`; undone when you seek back), `to` (animate to), `from` (animate from), `from`+`to`; a value may be relative to where the layer stands (`from: { x: '-=36' }` slides in from 36 px to the left of its place). `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
 
50
50
  ## Layer types
51
51
 
@@ -167,7 +167,7 @@ const movie = new Movie();
167
167
  await movie.init({ canvas, width, height, duration, frameRate, background, assets, composition });
168
168
  movie.play(); movie.pause();
169
169
  await movie.gotoFrame(n, true); // seek (frames; 30 fps => t = n/30)
170
- const blob = await movie.render({ format: 'mp4' }); // 'mp4' | 'webm' | 'mov' | 'mkv'; ~real-time. Part of it: `range: [2, 5]` (seconds) or `range: 'title'` (a top-level layer's span; picture and sound); `scale: 0.5` / `draft: true` (half size, low quality, no motion blur: a smaller file, not a faster drawing). Fast motion: `movie.init({ motionBlur: true })` (8 samples, 180° shutter; or a number / { samples: 2–64, shutter: 0–1 }) blurs render / snapshot / contactSheet, not live playback; a render takes `samples`× as long
170
+ const blob = await movie.render({ format: 'mp4' }); // 'mp4' | 'webm' | 'mov' | 'mkv'; ~real-time. `format: 'wav'` / `'ogg'` writes only the movie's SOUND (a `music` score as an audio file). `video: { codec: 'hevc' | 'av1' | 'vp9' | 'avc', bitrate: '8M', hardware, keyFrameInterval }` (a codec this browser cannot encode names the one that works). Part of it: `range: [2, 5]` (seconds) or `range: 'title'` (a top-level layer's span; picture and sound); `scale: 0.5` / `draft: true` (half size, low quality, no motion blur: a smaller file, not a faster drawing). Fast motion: `movie.init({ motionBlur: true })` (8 samples, 180° shutter; or a number / { samples: 2–64, shutter: 0–1 }) blurs render / snapshot / contactSheet, not live playback; a render takes `samples`× as long
171
171
  movie.on('progress', e => e.progress /* 0–100 */);
172
172
  movie.audioBuffer // mixed audio (after init), if any audio layers
173
173
  await movie.contactSheet({ count: 6, as: 'dataURL' }) // ONE image of several labelled frames — look at it. Options: frames | times | count, columns, cellWidth, as
@@ -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.26.2/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.26.3/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,16 +39,16 @@
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.26.2/dist/index.js",
43
- "pixi-effects/controller": "https://cdn.jsdelivr.net/npm/pixi-effects@0.26.2/dist/Controller.js",
44
- "pixi-effects/presenter": "https://cdn.jsdelivr.net/npm/pixi-effects@0.26.2/dist/Presenter.js",
45
- "pixi-effects/three": "https://cdn.jsdelivr.net/npm/pixi-effects@0.26.2/dist/three.js",
42
+ "pixi-effects": "https://cdn.jsdelivr.net/npm/pixi-effects@0.26.3/dist/index.js",
43
+ "pixi-effects/controller": "https://cdn.jsdelivr.net/npm/pixi-effects@0.26.3/dist/Controller.js",
44
+ "pixi-effects/presenter": "https://cdn.jsdelivr.net/npm/pixi-effects@0.26.3/dist/Presenter.js",
45
+ "pixi-effects/three": "https://cdn.jsdelivr.net/npm/pixi-effects@0.26.3/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
  }
49
49
  }
50
50
  </script>
51
- <link rel="modulepreload" href="https://cdn.jsdelivr.net/npm/pixi-effects@0.26.2/dist/index.js" /> <!-- after the import map: the library downloads while the page is parsed -->
51
+ <link rel="modulepreload" href="https://cdn.jsdelivr.net/npm/pixi-effects@0.26.3/dist/index.js" /> <!-- after the import map: the library downloads while the page is parsed -->
52
52
 
53
53
  <script type="module">
54
54
  import { Movie, kenBurns, withFade } from 'pixi-effects';
@@ -1,7 +0,0 @@
1
- export { CompositionSequence } from './chunk-GBUM7X2S.js';
2
- import './chunk-X3HB4DSR.js';
3
- import './chunk-AXZUKNXF.js';
4
- import './chunk-TLYTYBWX.js';
5
- import './chunk-BXOCEZ4U.js';
6
- //# sourceMappingURL=Composition-H4QA24MV.js.map
7
- //# sourceMappingURL=Composition-H4QA24MV.js.map
@@ -1,16 +0,0 @@
1
- 'use strict';
2
-
3
- var chunk555URQHQ_cjs = require('./chunk-555URQHQ.cjs');
4
- require('./chunk-WPXDRSGN.cjs');
5
- require('./chunk-3DLYAPK7.cjs');
6
- require('./chunk-ECA2WNRX.cjs');
7
- require('./chunk-TWZNOUM7.cjs');
8
-
9
-
10
-
11
- Object.defineProperty(exports, "CompositionSequence", {
12
- enumerable: true,
13
- get: function () { return chunk555URQHQ_cjs.CompositionSequence; }
14
- });
15
- //# sourceMappingURL=Composition-HM4YT5L7.cjs.map
16
- //# sourceMappingURL=Composition-HM4YT5L7.cjs.map