pixi-effects 0.32.0 → 0.34.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.
- package/CHANGELOG.md +20 -0
- package/README.md +5 -5
- package/ai/chat-template.html +3 -3
- package/ai/tools/lookup.mjs +29 -6
- package/ai/tools/pixi-effects.mjs +1 -1
- package/dist/Composition-NDXKQHB4.js +7 -0
- package/dist/{Composition-RF7QOEXT.js.map → Composition-NDXKQHB4.js.map} +1 -1
- package/dist/Composition-OLH7RC5Z.cjs +16 -0
- package/dist/{Composition-LNMKWXSL.cjs.map → Composition-OLH7RC5Z.cjs.map} +1 -1
- package/dist/{Spans-SDXABOV6.js → Spans-QHHWC2PB.js} +6 -6
- package/dist/{Spans-SDXABOV6.js.map → Spans-QHHWC2PB.js.map} +1 -1
- package/dist/{Spans-NQGKVMR5.cjs → Spans-QRMN2EZO.cjs} +11 -11
- package/dist/{Spans-NQGKVMR5.cjs.map → Spans-QRMN2EZO.cjs.map} +1 -1
- package/dist/{blendModes-MVRLG3T7.js → blendModes-PP5NPBXY.js} +4 -4
- package/dist/{blendModes-MVRLG3T7.js.map → blendModes-PP5NPBXY.js.map} +1 -1
- package/dist/{blendModes-VTKDXWEN.cjs → blendModes-WIUCYIMC.cjs} +5 -5
- package/dist/{blendModes-VTKDXWEN.cjs.map → blendModes-WIUCYIMC.cjs.map} +1 -1
- package/dist/{chunk-CRUDOKSD.cjs → chunk-5IJENZOG.cjs} +91 -91
- package/dist/{chunk-CRUDOKSD.cjs.map → chunk-5IJENZOG.cjs.map} +1 -1
- package/dist/{chunk-FCMOREIS.js → chunk-67LG7MN5.js} +48 -15
- package/dist/chunk-67LG7MN5.js.map +1 -0
- package/dist/{chunk-K3MAEKFM.cjs → chunk-FVZEBYLW.cjs} +78 -45
- package/dist/chunk-FVZEBYLW.cjs.map +1 -0
- package/dist/{chunk-FCGYJCVO.cjs → chunk-GL2AQFLQ.cjs} +23 -5
- package/dist/chunk-GL2AQFLQ.cjs.map +1 -0
- package/dist/{chunk-Z437UWI4.cjs → chunk-IMQJVRMT.cjs} +5 -5
- package/dist/{chunk-Z437UWI4.cjs.map → chunk-IMQJVRMT.cjs.map} +1 -1
- package/dist/{chunk-C5BNHVKK.js → chunk-ITDYFBB6.js} +23 -5
- package/dist/chunk-ITDYFBB6.js.map +1 -0
- package/dist/{chunk-QHDN6TF5.cjs → chunk-LEECUCTG.cjs} +44 -25
- package/dist/chunk-LEECUCTG.cjs.map +1 -0
- package/dist/{chunk-QFNS6BFL.js → chunk-RTJD3ZSW.js} +4 -4
- package/dist/{chunk-QFNS6BFL.js.map → chunk-RTJD3ZSW.js.map} +1 -1
- package/dist/{chunk-D5BFQVKL.js → chunk-XV4L72FW.js} +5 -5
- package/dist/{chunk-D5BFQVKL.js.map → chunk-XV4L72FW.js.map} +1 -1
- package/dist/{chunk-AW5NPDE4.js → chunk-Y253JQOH.js} +24 -7
- package/dist/chunk-Y253JQOH.js.map +1 -0
- package/dist/index.cjs +33 -33
- package/dist/index.js +5 -5
- package/dist/loader.css +1 -1
- package/dist/present.cjs +5 -5
- package/dist/present.js +5 -5
- package/dist/three.cjs +6 -6
- package/dist/three.js +2 -2
- package/docs/dsl.md +9 -2
- package/llms-full.txt +19 -7
- package/package.json +1 -1
- package/skills/pixi-effects/SKILL.md +1 -1
- package/skills/pixi-effects/reference/pitfalls.md +5 -1
- package/skills/pixi-effects/reference/quickstart.md +3 -2
- package/skills/pixi-effects/reference/recipes.md +1 -1
- package/skills/pixi-effects/template.html +6 -6
- package/dist/Composition-LNMKWXSL.cjs +0 -16
- package/dist/Composition-RF7QOEXT.js +0 -7
- package/dist/chunk-AW5NPDE4.js.map +0 -1
- package/dist/chunk-C5BNHVKK.js.map +0 -1
- package/dist/chunk-FCGYJCVO.cjs.map +0 -1
- package/dist/chunk-FCMOREIS.js.map +0 -1
- package/dist/chunk-K3MAEKFM.cjs.map +0 -1
- package/dist/chunk-QHDN6TF5.cjs.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -1,125 +1,125 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
4
|
-
require('./chunk-
|
|
5
|
-
var
|
|
6
|
-
require('./chunk-
|
|
7
|
-
require('./chunk-
|
|
3
|
+
var chunkFVZEBYLW_cjs = require('./chunk-FVZEBYLW.cjs');
|
|
4
|
+
require('./chunk-5IJENZOG.cjs');
|
|
5
|
+
var chunkLEECUCTG_cjs = require('./chunk-LEECUCTG.cjs');
|
|
6
|
+
require('./chunk-IMQJVRMT.cjs');
|
|
7
|
+
require('./chunk-GL2AQFLQ.cjs');
|
|
8
8
|
var chunkTWZNOUM7_cjs = require('./chunk-TWZNOUM7.cjs');
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
Object.defineProperty(exports, "Movie", {
|
|
13
13
|
enumerable: true,
|
|
14
|
-
get: function () { return
|
|
14
|
+
get: function () { return chunkFVZEBYLW_cjs.Movie; }
|
|
15
15
|
});
|
|
16
16
|
Object.defineProperty(exports, "animateText", {
|
|
17
17
|
enumerable: true,
|
|
18
|
-
get: function () { return
|
|
18
|
+
get: function () { return chunkFVZEBYLW_cjs.animateText; }
|
|
19
19
|
});
|
|
20
20
|
Object.defineProperty(exports, "audioEnvelope", {
|
|
21
21
|
enumerable: true,
|
|
22
|
-
get: function () { return
|
|
22
|
+
get: function () { return chunkFVZEBYLW_cjs.audioEnvelope; }
|
|
23
23
|
});
|
|
24
24
|
Object.defineProperty(exports, "bpmEnvelope", {
|
|
25
25
|
enumerable: true,
|
|
26
|
-
get: function () { return
|
|
26
|
+
get: function () { return chunkFVZEBYLW_cjs.bpmEnvelope; }
|
|
27
27
|
});
|
|
28
28
|
Object.defineProperty(exports, "cameraPath", {
|
|
29
29
|
enumerable: true,
|
|
30
|
-
get: function () { return
|
|
30
|
+
get: function () { return chunkFVZEBYLW_cjs.cameraPath; }
|
|
31
31
|
});
|
|
32
32
|
Object.defineProperty(exports, "computeEnvelope", {
|
|
33
33
|
enumerable: true,
|
|
34
|
-
get: function () { return
|
|
34
|
+
get: function () { return chunkFVZEBYLW_cjs.computeEnvelope; }
|
|
35
35
|
});
|
|
36
36
|
Object.defineProperty(exports, "dismissLoader", {
|
|
37
37
|
enumerable: true,
|
|
38
|
-
get: function () { return
|
|
38
|
+
get: function () { return chunkFVZEBYLW_cjs.dismissLoader; }
|
|
39
39
|
});
|
|
40
40
|
Object.defineProperty(exports, "ease", {
|
|
41
41
|
enumerable: true,
|
|
42
|
-
get: function () { return
|
|
42
|
+
get: function () { return chunkFVZEBYLW_cjs.parseEase; }
|
|
43
43
|
});
|
|
44
44
|
Object.defineProperty(exports, "failLoader", {
|
|
45
45
|
enumerable: true,
|
|
46
|
-
get: function () { return
|
|
46
|
+
get: function () { return chunkFVZEBYLW_cjs.failLoader; }
|
|
47
47
|
});
|
|
48
48
|
Object.defineProperty(exports, "followPath", {
|
|
49
49
|
enumerable: true,
|
|
50
|
-
get: function () { return
|
|
50
|
+
get: function () { return chunkFVZEBYLW_cjs.followPath; }
|
|
51
51
|
});
|
|
52
52
|
Object.defineProperty(exports, "kenBurns", {
|
|
53
53
|
enumerable: true,
|
|
54
|
-
get: function () { return
|
|
54
|
+
get: function () { return chunkFVZEBYLW_cjs.kenBurns; }
|
|
55
55
|
});
|
|
56
56
|
Object.defineProperty(exports, "measureText", {
|
|
57
57
|
enumerable: true,
|
|
58
|
-
get: function () { return
|
|
58
|
+
get: function () { return chunkFVZEBYLW_cjs.measureText; }
|
|
59
59
|
});
|
|
60
60
|
Object.defineProperty(exports, "musicBuffer", {
|
|
61
61
|
enumerable: true,
|
|
62
|
-
get: function () { return
|
|
62
|
+
get: function () { return chunkFVZEBYLW_cjs.musicBuffer; }
|
|
63
63
|
});
|
|
64
64
|
Object.defineProperty(exports, "musicEnvelope", {
|
|
65
65
|
enumerable: true,
|
|
66
|
-
get: function () { return
|
|
66
|
+
get: function () { return chunkFVZEBYLW_cjs.musicEnvelope; }
|
|
67
67
|
});
|
|
68
68
|
Object.defineProperty(exports, "musicEvents", {
|
|
69
69
|
enumerable: true,
|
|
70
|
-
get: function () { return
|
|
70
|
+
get: function () { return chunkFVZEBYLW_cjs.musicEvents; }
|
|
71
71
|
});
|
|
72
72
|
Object.defineProperty(exports, "orbit", {
|
|
73
73
|
enumerable: true,
|
|
74
|
-
get: function () { return
|
|
74
|
+
get: function () { return chunkFVZEBYLW_cjs.orbit; }
|
|
75
75
|
});
|
|
76
76
|
Object.defineProperty(exports, "particles", {
|
|
77
77
|
enumerable: true,
|
|
78
|
-
get: function () { return
|
|
78
|
+
get: function () { return chunkFVZEBYLW_cjs.particles; }
|
|
79
79
|
});
|
|
80
80
|
Object.defineProperty(exports, "react", {
|
|
81
81
|
enumerable: true,
|
|
82
|
-
get: function () { return
|
|
82
|
+
get: function () { return chunkFVZEBYLW_cjs.react; }
|
|
83
83
|
});
|
|
84
84
|
Object.defineProperty(exports, "resolveLoader", {
|
|
85
85
|
enumerable: true,
|
|
86
|
-
get: function () { return
|
|
86
|
+
get: function () { return chunkFVZEBYLW_cjs.resolveLoader; }
|
|
87
87
|
});
|
|
88
88
|
Object.defineProperty(exports, "splitText", {
|
|
89
89
|
enumerable: true,
|
|
90
|
-
get: function () { return
|
|
90
|
+
get: function () { return chunkFVZEBYLW_cjs.splitText; }
|
|
91
91
|
});
|
|
92
92
|
Object.defineProperty(exports, "stagger", {
|
|
93
93
|
enumerable: true,
|
|
94
|
-
get: function () { return
|
|
94
|
+
get: function () { return chunkFVZEBYLW_cjs.stagger; }
|
|
95
95
|
});
|
|
96
96
|
Object.defineProperty(exports, "wiggle", {
|
|
97
97
|
enumerable: true,
|
|
98
|
-
get: function () { return
|
|
98
|
+
get: function () { return chunkFVZEBYLW_cjs.wiggle; }
|
|
99
99
|
});
|
|
100
100
|
Object.defineProperty(exports, "withFade", {
|
|
101
101
|
enumerable: true,
|
|
102
|
-
get: function () { return
|
|
102
|
+
get: function () { return chunkFVZEBYLW_cjs.withFade; }
|
|
103
103
|
});
|
|
104
104
|
Object.defineProperty(exports, "Sequence", {
|
|
105
105
|
enumerable: true,
|
|
106
|
-
get: function () { return
|
|
106
|
+
get: function () { return chunkLEECUCTG_cjs.Sequence; }
|
|
107
107
|
});
|
|
108
108
|
Object.defineProperty(exports, "noise", {
|
|
109
109
|
enumerable: true,
|
|
110
|
-
get: function () { return
|
|
110
|
+
get: function () { return chunkLEECUCTG_cjs.noise; }
|
|
111
111
|
});
|
|
112
112
|
Object.defineProperty(exports, "rand", {
|
|
113
113
|
enumerable: true,
|
|
114
|
-
get: function () { return
|
|
114
|
+
get: function () { return chunkLEECUCTG_cjs.rand; }
|
|
115
115
|
});
|
|
116
116
|
Object.defineProperty(exports, "random", {
|
|
117
117
|
enumerable: true,
|
|
118
|
-
get: function () { return
|
|
118
|
+
get: function () { return chunkLEECUCTG_cjs.random; }
|
|
119
119
|
});
|
|
120
120
|
Object.defineProperty(exports, "registerSequenceType", {
|
|
121
121
|
enumerable: true,
|
|
122
|
-
get: function () { return
|
|
122
|
+
get: function () { return chunkLEECUCTG_cjs.registerSequenceType; }
|
|
123
123
|
});
|
|
124
124
|
Object.defineProperty(exports, "warnUnknownOptions", {
|
|
125
125
|
enumerable: true,
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
export { Movie, animateText, audioEnvelope, bpmEnvelope, cameraPath, computeEnvelope, dismissLoader, parseEase as ease, failLoader, followPath, kenBurns, measureText, musicBuffer, musicEnvelope, musicEvents, orbit, particles, react, resolveLoader, splitText, stagger, wiggle, withFade } from './chunk-
|
|
2
|
-
import './chunk-
|
|
3
|
-
export { Sequence, noise, rand, random, registerSequenceType } from './chunk-
|
|
4
|
-
import './chunk-
|
|
5
|
-
import './chunk-
|
|
1
|
+
export { Movie, animateText, audioEnvelope, bpmEnvelope, cameraPath, computeEnvelope, dismissLoader, parseEase as ease, failLoader, followPath, kenBurns, measureText, musicBuffer, musicEnvelope, musicEvents, orbit, particles, react, resolveLoader, splitText, stagger, wiggle, withFade } from './chunk-67LG7MN5.js';
|
|
2
|
+
import './chunk-XV4L72FW.js';
|
|
3
|
+
export { Sequence, noise, rand, random, registerSequenceType } from './chunk-Y253JQOH.js';
|
|
4
|
+
import './chunk-RTJD3ZSW.js';
|
|
5
|
+
import './chunk-ITDYFBB6.js';
|
|
6
6
|
export { warnUnknownOptions } from './chunk-BXOCEZ4U.js';
|
|
7
7
|
//# sourceMappingURL=index.js.map
|
|
8
8
|
//# sourceMappingURL=index.js.map
|
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.
|
|
4
|
+
* <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/pixi-effects@0.34.0/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/present.cjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
require('./chunk-
|
|
4
|
-
require('./chunk-
|
|
5
|
-
require('./chunk-
|
|
6
|
-
require('./chunk-
|
|
7
|
-
require('./chunk-
|
|
3
|
+
require('./chunk-FVZEBYLW.cjs');
|
|
4
|
+
require('./chunk-5IJENZOG.cjs');
|
|
5
|
+
require('./chunk-LEECUCTG.cjs');
|
|
6
|
+
require('./chunk-IMQJVRMT.cjs');
|
|
7
|
+
require('./chunk-GL2AQFLQ.cjs');
|
|
8
8
|
var chunkTWZNOUM7_cjs = require('./chunk-TWZNOUM7.cjs');
|
|
9
9
|
|
|
10
10
|
// src/present/stops.ts
|
package/dist/present.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import './chunk-
|
|
2
|
-
import './chunk-
|
|
3
|
-
import './chunk-
|
|
4
|
-
import './chunk-
|
|
5
|
-
import './chunk-
|
|
1
|
+
import './chunk-67LG7MN5.js';
|
|
2
|
+
import './chunk-XV4L72FW.js';
|
|
3
|
+
import './chunk-Y253JQOH.js';
|
|
4
|
+
import './chunk-RTJD3ZSW.js';
|
|
5
|
+
import './chunk-ITDYFBB6.js';
|
|
6
6
|
import { warnUnknownOptions } from './chunk-BXOCEZ4U.js';
|
|
7
7
|
|
|
8
8
|
// src/present/stops.ts
|
package/dist/three.cjs
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
4
|
-
require('./chunk-
|
|
3
|
+
var chunkLEECUCTG_cjs = require('./chunk-LEECUCTG.cjs');
|
|
4
|
+
require('./chunk-GL2AQFLQ.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
|
|
9
|
+
var ThreeSequence = class extends chunkLEECUCTG_cjs.Sequence {
|
|
10
10
|
_renderer = null;
|
|
11
11
|
_scene = null;
|
|
12
12
|
_camera = null;
|
|
@@ -25,7 +25,7 @@ var ThreeSequence = class extends chunkQHDN6TF5_cjs.Sequence {
|
|
|
25
25
|
if (this.duration === void 0) {
|
|
26
26
|
this.duration = this.parent?.duration ?? this.root.duration;
|
|
27
27
|
}
|
|
28
|
-
const scope =
|
|
28
|
+
const scope = chunkLEECUCTG_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 chunkQHDN6TF5_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
|
|
148
|
+
return chunkLEECUCTG_cjs.evaluateExpr(v, scope);
|
|
149
149
|
}
|
|
150
150
|
|
|
151
151
|
// src/three/index.ts
|
|
152
152
|
function registerThree() {
|
|
153
|
-
|
|
153
|
+
chunkLEECUCTG_cjs.registerSequenceType("three", ThreeSequence);
|
|
154
154
|
}
|
|
155
155
|
function three(spec) {
|
|
156
156
|
return spec;
|
package/dist/three.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Sequence, buildScope, evaluateExpr, registerSequenceType } from './chunk-
|
|
2
|
-
import './chunk-
|
|
1
|
+
import { Sequence, buildScope, evaluateExpr, registerSequenceType } from './chunk-Y253JQOH.js';
|
|
2
|
+
import './chunk-ITDYFBB6.js';
|
|
3
3
|
import './chunk-BXOCEZ4U.js';
|
|
4
4
|
import { Texture, Sprite } from 'pixi.js';
|
|
5
5
|
import { WebGLRenderer, Scene, PerspectiveCamera } from 'three';
|
package/docs/dsl.md
CHANGED
|
@@ -90,6 +90,10 @@ Renders a [PIXI.Text](https://pixijs.com/8.x/guides/components/scene-objects/tex
|
|
|
90
90
|
}
|
|
91
91
|
```
|
|
92
92
|
|
|
93
|
+
**Wrapping and fonts for Japanese, Chinese and Korean.** `wordWrap: true` breaks a line at spaces. Japanese and Chinese have none: with `wordWrap` alone a long line does not wrap and runs off the canvas, and `breakWords: true` makes it break between any two characters, so a line can end with one to three characters alone (an orphan) and split a word or phrase in the middle. For a clean break write the line break yourself (`'あの日言えなかった言葉が、\n風に溶けていく'`, after a comma or a particle) and size the font so each line fits; `check` lists a line that is cut by the canvas edge, but it does not judge an ugly break, so look at the sheet. (Korean writes spaces between words, so `wordWrap` breaks there; a very long word still needs `breakWords`.) No font is bundled: the machine's own fonts are used, and a font that lacks the script's characters is replaced for those characters by whatever the machine has, so the look differs from machine to machine. For a result that looks the same everywhere, load a web font that covers the script (for example Noto Sans JP, Noto Sans KR for Hangul, Noto Sans SC) and `await document.fonts.load('40px "Noto Sans KR"')` before `movie.init` (see pitfall 103); give the family in `style.fontFamily`.
|
|
94
|
+
|
|
95
|
+
**Right-to-left and complex scripts (measured in trials).** With a font that covers the script the text is shaped correctly: Arabic letters connect and read right to left, Hebrew reads right to left, Thai and Devanagari marks stack. The direction follows the first strong letter of the line; place it with `anchorX: 1` and `style.align: 'right'`. Three traps: (1) marks that hang below the font's box can be cut off (the dots of an Arabic ي turn "في" into "فى"): give the layer `style.padding` (30 at a size of 120), and `check` cannot see it, so look at the picture; (2) `splitText` and `animateText` place words and letters from left to right, so an Arabic line comes out in reverse order, and `by: 'chars'` breaks the connected forms: split the line by words yourself and place each word at the right edge minus `measureText` of the logical text up to that word; (3) `visibleChars` shapes each prefix alone, so the last letter can show in its isolated form until the next one arrives: uncovering the whole line with a mask whose width grows keeps the connected forms.
|
|
96
|
+
|
|
93
97
|
The text's `fill` is also animatable via keyframes (under the `to`/`from`/`set` keys, not under `style`). Set `colorSpace` for perceptual interpolation:
|
|
94
98
|
|
|
95
99
|
```ts
|
|
@@ -1329,6 +1333,8 @@ Animatable: `threshold`, `smoothing`, `spill`. `keyColor` is set at build time.
|
|
|
1329
1333
|
|
|
1330
1334
|
### Named filters (`{ type: 'glow', … }`)
|
|
1331
1335
|
|
|
1336
|
+
**Which ones to use.** A filter works on a layer's own pixels (a glow around the letters, an outline, a shadow), which a `shader` layer cannot do (it draws new content). The 38 named filters that come from `pixi-filters` are not equally trustworthy: `tests/tools/filterSweep.test.ts` draws these on WebGPU (the default backend) and on WebGL and requires that each one shows and looks the same: **`glow`, `outline`, `dropShadow`, `bevel`, `bloom`, `pixelate`, `crt`, `rgbSplit`, `dot`, `radialBlur`, `glitch`** (`grain`, `warp` and `blur` are this library's or PixiJS's own). Reach for these first. The others are not tested on both backends; measured differences: `multiColorReplace` draws the whole picture black on WebGPU (use `colorReplace`), `kawaseBlur` blurs far less on WebGPU (use `blur`), `advancedBloom` is sharper on WebGPU (use `bloom`), `ascii` shows stripes on WebGPU. A filter only shows along the edges of a layer's own picture: on a full-bleed opaque layer `glow` and `outline` have nothing to follow, so put them on the shapes and text. `radialBlur` did nothing on WebGPU before 0.34 (an upstream i32 / f32 mismatch, worked around).
|
|
1337
|
+
|
|
1332
1338
|
A filter by name, as plain data. `type` is the filter's class name without `Filter`, in camelCase (case does not matter), and every other key is that filter's own option. No import, and the layer stays JSON:
|
|
1333
1339
|
|
|
1334
1340
|
```ts
|
|
@@ -1358,7 +1364,7 @@ Name a filter (`name: 'halo'`) to animate it with `'filters.halo.<option>'`. `bl
|
|
|
1358
1364
|
| `oldFilm` | `sepia noise scratch scratchDensity vignetting seed` | Aged film |
|
|
1359
1365
|
| `glitch` | `slices offset direction seed average` | Digital glitch |
|
|
1360
1366
|
| `warp` | `kind: 'wave' \| 'haze'`, `strength` (max shift, px, 6), `scale` (wavelength or cell size, px, 80), `speed` (0.5), `angle` (degrees: the way a wave travels, 0), `seed` (`haze`) | Bends the picture: a travelling wave (flags, water) or drifting noise (heat haze). This library's own, time-driven |
|
|
1361
|
-
| `bulgePinch` | `center radius strength` | Bulge (+) / pinch (−) |
|
|
1367
|
+
| `bulgePinch` | `center radius strength` (`center`: 0–1 of the layer) | Bulge (+) / pinch (−) |
|
|
1362
1368
|
| `twist` | `radius angle offset: { x, y }` | Swirl |
|
|
1363
1369
|
| `zoomBlur` | `strength center innerRadius radius` | Radial zoom streaks |
|
|
1364
1370
|
| `kawaseBlur` | `strength quality` | Cheap, wide blur |
|
|
@@ -1383,7 +1389,8 @@ Name a filter (`name: 'halo'`) to animate it with `'filters.halo.<option>'`. `bl
|
|
|
1383
1389
|
Gotchas, all measured:
|
|
1384
1390
|
|
|
1385
1391
|
- **Distortion:** `warp` (`wave`: a flag or the surface of water, `haze`: heat shimmer; moves with the frame's time, no keyframes needed, and pads the layer by `strength` so a shifted edge is not cut), `twist` (a swirl), `bulgePinch` (a lens), `shockwave` (a ring that spreads), `rgbSplit` (colour fringes), `glitch` (slices). `warp`'s time is the time of the frame (inside a time-remapped composition, that composition's clock), `speed: 0` stands still, `strength` is in px and is capped at 200. It bends the layer's picture; to draw something that does not exist yet, use a `shader` layer.
|
|
1386
|
-
-
|
|
1392
|
+
- **`bulgePinch.center` is a fraction of the filtered layer, not pixels** (measured): `{ x: 0.5, y: 0.5 }` is the layer's middle and `{ x: 0.7, y: 0.7 }` is a bit down and to the right of it, and it can be animated (`filters.bp.center.x`, 0 to 1). A pixel value such as `{ x: 900, y: 500 }` puts the bulge far outside the layer and nothing shows (it warns).
|
|
1393
|
+
- **The other centres and offsets are in canvas pixels, not the layer's:** `twist.offset`, `zoomBlur.center`, `radialBlur.center`, `shockwave.center` default to the canvas's top-left corner, so a layer in the middle looks unaffected. Put the centre on the layer: `{ type: 'twist', offset: { x: 640, y: 360 } }` for a layer centred on a 1280×720 canvas.
|
|
1387
1394
|
- **A filter works inside the layer's bounds.** Glow, dropShadow, outline and blur draw outside it and are clipped unless you widen the area (`filterArea: { x: -24, y: -24, width: w + 48, height: h + 48 }`, in the layer's own coordinates). Do not widen it for `grayscale`, `oldFilm` or other filters that paint the whole area: they fill the extra margin with black.
|
|
1388
1395
|
- **Animate scalars by name** (`'filters.halo.outerStrength'`). Options that are points (`rgbSplit.red`, `dropShadow.offset`) are animated one leaf at a time: `'filters.<name>.red.x'` / `'filters.<name>.red.y'` tween like any number (the whole point `red: { x, y }` as a keyframe value switches at its keyframe and does not tween; a path that leads nowhere is warned about once and ignored). `pixelate` animates through `sizeX` / `sizeY`, not `size`.
|
|
1389
1396
|
- `shockwave` and `godray` are driven by `time` (a still frame at `time: 0` looks like nothing): animate it. `radialBlur`, `shockwave` and `tiltShift` positions are canvas pixels too.
|
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.
|
|
9
|
+
> Written for pixi-effects 0.34.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
|
|
|
@@ -76,7 +76,7 @@ Centred text must be positioned by `anchorX/anchorY: 0.5`; estimate text width a
|
|
|
76
76
|
|
|
77
77
|
# pixi-effects quick start
|
|
78
78
|
|
|
79
|
-
npx pixi-effects new my-video # a page to edit
|
|
79
|
+
npx pixi-effects new my-video # a page to edit (--size 1080x1920 --duration 20)
|
|
80
80
|
npx pixi-effects view my-video.html # look at it while you edit
|
|
81
81
|
npx pixi-effects check my-video.html # warnings, layout, sound, a contact sheet: LOOK at the PNG
|
|
82
82
|
npx pixi-effects help <word> # the docs about a word: mask, threeD, keyframes, music
|
|
@@ -101,7 +101,8 @@ What goes wrong most:
|
|
|
101
101
|
- Anchors: rect and circle are centred on x,y; text and image are top-left (use `anchorX: 0.5`).
|
|
102
102
|
- A warning is an instruction: `window.__logs` must be `[]`. Fix what it names.
|
|
103
103
|
- Sound needs no files: `{ type: 'audio', sfx: 'swoosh', at: 2 }`, or `music: { bpm, tracks }`.
|
|
104
|
-
- Never say done before you have looked at the check's PNG.
|
|
104
|
+
- Never say done before you have looked at the check's PNG: it cannot judge a line break, a font, or how a fade ends.
|
|
105
|
+
- Exist already (`help <word>`): `spans` (colour part of a text line), `vars` + `--var name=value` / `--size 1080x1920` (one page, many versions).
|
|
105
106
|
- Anything else: `help <word>` first; the cheatsheet is for grep, not for reading.
|
|
106
107
|
|
|
107
108
|
|
|
@@ -347,7 +348,7 @@ A good habit that catches most of these: after `init`, read `window.__logs` (see
|
|
|
347
348
|
## Tooling (agent harness)
|
|
348
349
|
|
|
349
350
|
30. **A scene change must keep the outgoing scene alive until the covering flood / wipe has finished**, and the next scene starts when it is complete; the flood circle must reach the corners (radius ≥ half the diagonal) and use `power2.in`, not `expo.in`. *(docs)*
|
|
350
|
-
30b. **Unknown option names now warn** (`contactSheet({ cols: 2 })` → did you mean `columns`; `init({ fps })` → `frameRate`). Layer names show up in `inspect` paths, so give layers a `name`. `inspect` ignores faint (alpha < 0.3) layers and
|
|
351
|
+
30b. **Unknown option names now warn** (`contactSheet({ cols: 2 })` → did you mean `columns`; `init({ fps })` → `frameRate`). Layer names show up in `inspect` paths, so give layers a `name`. `inspect` ignores faint (alpha < 0.3) layers and, while a geometry keyframe (x y z scale rotation …) is running on the layer, its `null` parent or its composition, a text crossing the edge on purpose (a marquee, a zoom-through); a fade, a `set` and an x written only in `initial` are no motion, so a cut-off title is reported once it has settled. Pixi may print `[BindGroup] … destroyed while still bound` warnings at `movie.destroy()`: harmless (playback and seeking never warn). *(docs)*
|
|
351
352
|
30c. **Look with the built-in tools, not OS screenshots**: `await movie.contactSheet({ count: 6, as: 'dataURL' })` returns one labelled image of the whole animation; `await movie.inspect(frame)` returns every layer's canvas bounds and `issues`. If you do use a browser tool, `screenshot` needs an **absolute path**, macOS `sed -i` needs `''`, and a static server may redirect `/x.html` → `/x` and drop `?query`. Keep the template's `try/catch` around `init` so failures land in `window.__logs`. Check ≥ 5 timestamps including mid-transition. *(fixed: tools built in)*
|
|
352
353
|
31. **Check audio with `movie.inspectAudio()`**: every sound measured on its own (`sound.length`, `peakDb`, `loudestAt`, `brightnessHz`), when it plays, loudness and brightness per 0.1 s window, and `issues` (the mix was limited, a layer is inaudible, an sfx is cut off by the end). Decoding a render by hand is no longer needed: `render()` encodes the same mix, on time in mp4 / mov / webm / mkv. *(fixed)*
|
|
353
354
|
|
|
@@ -433,6 +434,10 @@ A good habit that catches most of these: after `init`, read `window.__logs` (see
|
|
|
433
434
|
105. **Layout that follows the screen is written in `W` / `H` / `GW` / `GH`, never in pixels.** After `movie.resize(w, h)` an expression sees the new size; a literal `x: 960` stays at 960. A string variable cannot be read in an expression (`x: 'title'` is a number context): use `{title}` in a string, or declare the number. A name that is declared but never read, and a `{name}` that was not declared, are both said at `init` (usually a typo). Load a web font BEFORE the first `setVars` that changes a text (see 103): the text is measured again each rebuild.
|
|
434
435
|
106. **A text outline used to spike; it is round-joined now.** `style: { stroke: { color: '#000', width: 12 } }` was drawn with PixiJS's default `miter` join, so the sharp corners of W, V, A, k and 4 grew spikes ("the outline sticks out"). The library now adds `join: 'round'` to an outline that names no join; say `join: 'miter'` or `'bevel'` if you want one. The stroke is centred on the glyph edge, so half of it eats into the letter: keep `width` to about a tenth of `fontSize`, or for a thick outline put a second copy of the text with the stroke UNDER the plain one.
|
|
435
436
|
107. **`spans` places pieces by measuring, so the font must be loaded first, and a span cannot change size.** A web font that is not loaded yet is measured as the fallback font and the pieces drift apart: `await document.fonts.load('30px "Name"')` before `movie.init`. `fontSize` / `fontFamily` / `lineHeight` / `align` on a span are ignored (warned): they belong to the layer's `style`. `fill`, `text` and `value` keyframes do not reach a spans layer (it is a group of text layers).
|
|
437
|
+
108. **Japanese and Chinese do not wrap at spaces** (Korean has spaces, so `wordWrap` breaks there). `wordWrap: true` alone does nothing for a long line (it runs off the canvas); `breakWords: true` breaks between any two characters and can leave one to three characters alone on the last line. Break the line yourself with `\n` after a comma or a particle and size the font to fit; `check` reports a line cut by the canvas edge but does not judge an ugly break, so look at the contact sheet. Raising `fontSize` can turn a clean line into an orphan: look again after any size change.
|
|
438
|
+
109. **No font is bundled, and each script needs a font that has its characters.** The machine's own fonts are used; characters a font lacks are drawn with whatever the machine falls back to, so the same page can look different on another machine. Load a web font that covers the script (Noto Sans JP, Noto Sans KR for Hangul, Noto Sans SC) with `await document.fonts.load('40px "Noto Sans KR"')` before `movie.init`, and say it in `style.fontFamily` (pitfall 103). `check` lists a layer none of whose fonts is available, but a family that nothing installs or loads is silently replaced by the next one in the list (or the generic name) without a word: the first name in `fontFamily` is not proof it was used.
|
|
439
|
+
110. **Right-to-left text: three traps.** `splitText` / `animateText` order words left to right, so Arabic or Hebrew comes out reversed (split by words yourself, from the right edge, with `measureText` of the logical prefix); a mark below the font box can be clipped (the dots of an Arabic ي) unless the layer has `style.padding`; `check` sees neither. `bulgePinch.center` is 0–1 of the layer, not pixels (pitfalls elsewhere say canvas pixels for the other centres: `twist`, `zoomBlur`, `shockwave`). `npx pixi-effects help arabic` and `help bulgePinch` print the sections.
|
|
440
|
+
111. **Only some named filters are tested on both backends.** `glow`, `outline`, `dropShadow`, `bevel`, `bloom`, `pixelate`, `crt`, `rgbSplit`, `dot`, `radialBlur` and `glitch` look the same on WebGPU (the default) and WebGL; `multiColorReplace` draws everything black on WebGPU, `kawaseBlur` is far weaker and `advancedBloom` sharper there: use `colorReplace`, `blur`, `bloom`. A filter follows the edges of the layer's own picture: `glow` on a full-screen opaque layer shows nothing (put it on the shapes and the text).
|
|
436
441
|
|
|
437
442
|
|
|
438
443
|
<!-- ===== skills/pixi-effects/reference/recipes.md ===== -->
|
|
@@ -1618,7 +1623,7 @@ done < table.csv
|
|
|
1618
1623
|
|
|
1619
1624
|
## One film, several languages and colours (variables)
|
|
1620
1625
|
|
|
1621
|
-
Declare what changes as `vars` and write `{name}` in the strings and the names as identifiers in the expressions. One page then makes every variant: `pixi-effects render film.html --var
|
|
1626
|
+
Declare what changes as `vars` and write `{name}` in the strings and the names as identifiers in the expressions. One page then makes every variant: a declared variable is set from the command line (`pixi-effects render film.html --var accent=#3949ab -o film-blue.mp4`; a name the page did not declare stops the run), the language below is picked by the page from the query (`--query lang=ja`, on `check` as well as `render`), and a page can switch live with `movie.setVars(...)`. Load the fonts of every language before the first build (pitfall 103); `check` says a variable nothing reads, and a `{name}` nobody declared.
|
|
1622
1627
|
|
|
1623
1628
|
```js
|
|
1624
1629
|
// @docs-only variants
|
|
@@ -1902,6 +1907,10 @@ Renders a [PIXI.Text](https://pixijs.com/8.x/guides/components/scene-objects/tex
|
|
|
1902
1907
|
}
|
|
1903
1908
|
```
|
|
1904
1909
|
|
|
1910
|
+
**Wrapping and fonts for Japanese, Chinese and Korean.** `wordWrap: true` breaks a line at spaces. Japanese and Chinese have none: with `wordWrap` alone a long line does not wrap and runs off the canvas, and `breakWords: true` makes it break between any two characters, so a line can end with one to three characters alone (an orphan) and split a word or phrase in the middle. For a clean break write the line break yourself (`'あの日言えなかった言葉が、\n風に溶けていく'`, after a comma or a particle) and size the font so each line fits; `check` lists a line that is cut by the canvas edge, but it does not judge an ugly break, so look at the sheet. (Korean writes spaces between words, so `wordWrap` breaks there; a very long word still needs `breakWords`.) No font is bundled: the machine's own fonts are used, and a font that lacks the script's characters is replaced for those characters by whatever the machine has, so the look differs from machine to machine. For a result that looks the same everywhere, load a web font that covers the script (for example Noto Sans JP, Noto Sans KR for Hangul, Noto Sans SC) and `await document.fonts.load('40px "Noto Sans KR"')` before `movie.init` (see pitfall 103); give the family in `style.fontFamily`.
|
|
1911
|
+
|
|
1912
|
+
**Right-to-left and complex scripts (measured in trials).** With a font that covers the script the text is shaped correctly: Arabic letters connect and read right to left, Hebrew reads right to left, Thai and Devanagari marks stack. The direction follows the first strong letter of the line; place it with `anchorX: 1` and `style.align: 'right'`. Three traps: (1) marks that hang below the font's box can be cut off (the dots of an Arabic ي turn "في" into "فى"): give the layer `style.padding` (30 at a size of 120), and `check` cannot see it, so look at the picture; (2) `splitText` and `animateText` place words and letters from left to right, so an Arabic line comes out in reverse order, and `by: 'chars'` breaks the connected forms: split the line by words yourself and place each word at the right edge minus `measureText` of the logical text up to that word; (3) `visibleChars` shapes each prefix alone, so the last letter can show in its isolated form until the next one arrives: uncovering the whole line with a mask whose width grows keeps the connected forms.
|
|
1913
|
+
|
|
1905
1914
|
The text's `fill` is also animatable via keyframes (under the `to`/`from`/`set` keys, not under `style`). Set `colorSpace` for perceptual interpolation:
|
|
1906
1915
|
|
|
1907
1916
|
```ts
|
|
@@ -3141,6 +3150,8 @@ Animatable: `threshold`, `smoothing`, `spill`. `keyColor` is set at build time.
|
|
|
3141
3150
|
|
|
3142
3151
|
### Named filters (`{ type: 'glow', … }`)
|
|
3143
3152
|
|
|
3153
|
+
**Which ones to use.** A filter works on a layer's own pixels (a glow around the letters, an outline, a shadow), which a `shader` layer cannot do (it draws new content). The 38 named filters that come from `pixi-filters` are not equally trustworthy: `tests/tools/filterSweep.test.ts` draws these on WebGPU (the default backend) and on WebGL and requires that each one shows and looks the same: **`glow`, `outline`, `dropShadow`, `bevel`, `bloom`, `pixelate`, `crt`, `rgbSplit`, `dot`, `radialBlur`, `glitch`** (`grain`, `warp` and `blur` are this library's or PixiJS's own). Reach for these first. The others are not tested on both backends; measured differences: `multiColorReplace` draws the whole picture black on WebGPU (use `colorReplace`), `kawaseBlur` blurs far less on WebGPU (use `blur`), `advancedBloom` is sharper on WebGPU (use `bloom`), `ascii` shows stripes on WebGPU. A filter only shows along the edges of a layer's own picture: on a full-bleed opaque layer `glow` and `outline` have nothing to follow, so put them on the shapes and text. `radialBlur` did nothing on WebGPU before 0.34 (an upstream i32 / f32 mismatch, worked around).
|
|
3154
|
+
|
|
3144
3155
|
A filter by name, as plain data. `type` is the filter's class name without `Filter`, in camelCase (case does not matter), and every other key is that filter's own option. No import, and the layer stays JSON:
|
|
3145
3156
|
|
|
3146
3157
|
```ts
|
|
@@ -3170,7 +3181,7 @@ Name a filter (`name: 'halo'`) to animate it with `'filters.halo.<option>'`. `bl
|
|
|
3170
3181
|
| `oldFilm` | `sepia noise scratch scratchDensity vignetting seed` | Aged film |
|
|
3171
3182
|
| `glitch` | `slices offset direction seed average` | Digital glitch |
|
|
3172
3183
|
| `warp` | `kind: 'wave' \| 'haze'`, `strength` (max shift, px, 6), `scale` (wavelength or cell size, px, 80), `speed` (0.5), `angle` (degrees: the way a wave travels, 0), `seed` (`haze`) | Bends the picture: a travelling wave (flags, water) or drifting noise (heat haze). This library's own, time-driven |
|
|
3173
|
-
| `bulgePinch` | `center radius strength` | Bulge (+) / pinch (−) |
|
|
3184
|
+
| `bulgePinch` | `center radius strength` (`center`: 0–1 of the layer) | Bulge (+) / pinch (−) |
|
|
3174
3185
|
| `twist` | `radius angle offset: { x, y }` | Swirl |
|
|
3175
3186
|
| `zoomBlur` | `strength center innerRadius radius` | Radial zoom streaks |
|
|
3176
3187
|
| `kawaseBlur` | `strength quality` | Cheap, wide blur |
|
|
@@ -3195,7 +3206,8 @@ Name a filter (`name: 'halo'`) to animate it with `'filters.halo.<option>'`. `bl
|
|
|
3195
3206
|
Gotchas, all measured:
|
|
3196
3207
|
|
|
3197
3208
|
- **Distortion:** `warp` (`wave`: a flag or the surface of water, `haze`: heat shimmer; moves with the frame's time, no keyframes needed, and pads the layer by `strength` so a shifted edge is not cut), `twist` (a swirl), `bulgePinch` (a lens), `shockwave` (a ring that spreads), `rgbSplit` (colour fringes), `glitch` (slices). `warp`'s time is the time of the frame (inside a time-remapped composition, that composition's clock), `speed: 0` stands still, `strength` is in px and is capped at 200. It bends the layer's picture; to draw something that does not exist yet, use a `shader` layer.
|
|
3198
|
-
-
|
|
3209
|
+
- **`bulgePinch.center` is a fraction of the filtered layer, not pixels** (measured): `{ x: 0.5, y: 0.5 }` is the layer's middle and `{ x: 0.7, y: 0.7 }` is a bit down and to the right of it, and it can be animated (`filters.bp.center.x`, 0 to 1). A pixel value such as `{ x: 900, y: 500 }` puts the bulge far outside the layer and nothing shows (it warns).
|
|
3210
|
+
- **The other centres and offsets are in canvas pixels, not the layer's:** `twist.offset`, `zoomBlur.center`, `radialBlur.center`, `shockwave.center` default to the canvas's top-left corner, so a layer in the middle looks unaffected. Put the centre on the layer: `{ type: 'twist', offset: { x: 640, y: 360 } }` for a layer centred on a 1280×720 canvas.
|
|
3199
3211
|
- **A filter works inside the layer's bounds.** Glow, dropShadow, outline and blur draw outside it and are clipped unless you widen the area (`filterArea: { x: -24, y: -24, width: w + 48, height: h + 48 }`, in the layer's own coordinates). Do not widen it for `grayscale`, `oldFilm` or other filters that paint the whole area: they fill the extra margin with black.
|
|
3200
3212
|
- **Animate scalars by name** (`'filters.halo.outerStrength'`). Options that are points (`rgbSplit.red`, `dropShadow.offset`) are animated one leaf at a time: `'filters.<name>.red.x'` / `'filters.<name>.red.y'` tween like any number (the whole point `red: { x, y }` as a keyframe value switches at its keyframe and does not tween; a path that leads nowhere is warned about once and ignored). `pixelate` animates through `sizeX` / `sizeY`, not `size`.
|
|
3201
3213
|
- `shockwave` and `godray` are driven by `time` (a still frame at `time: 0` looks like nothing): animate it. `radialBlur`, `shockwave` and `tiltShift` positions are canvas pixels too.
|
package/package.json
CHANGED
|
@@ -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.
|
|
6
|
+
> Written for pixi-effects 0.34.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
|
|
|
@@ -49,7 +49,7 @@ A good habit that catches most of these: after `init`, read `window.__logs` (see
|
|
|
49
49
|
## Tooling (agent harness)
|
|
50
50
|
|
|
51
51
|
30. **A scene change must keep the outgoing scene alive until the covering flood / wipe has finished**, and the next scene starts when it is complete; the flood circle must reach the corners (radius ≥ half the diagonal) and use `power2.in`, not `expo.in`. *(docs)*
|
|
52
|
-
30b. **Unknown option names now warn** (`contactSheet({ cols: 2 })` → did you mean `columns`; `init({ fps })` → `frameRate`). Layer names show up in `inspect` paths, so give layers a `name`. `inspect` ignores faint (alpha < 0.3) layers and
|
|
52
|
+
30b. **Unknown option names now warn** (`contactSheet({ cols: 2 })` → did you mean `columns`; `init({ fps })` → `frameRate`). Layer names show up in `inspect` paths, so give layers a `name`. `inspect` ignores faint (alpha < 0.3) layers and, while a geometry keyframe (x y z scale rotation …) is running on the layer, its `null` parent or its composition, a text crossing the edge on purpose (a marquee, a zoom-through); a fade, a `set` and an x written only in `initial` are no motion, so a cut-off title is reported once it has settled. Pixi may print `[BindGroup] … destroyed while still bound` warnings at `movie.destroy()`: harmless (playback and seeking never warn). *(docs)*
|
|
53
53
|
30c. **Look with the built-in tools, not OS screenshots**: `await movie.contactSheet({ count: 6, as: 'dataURL' })` returns one labelled image of the whole animation; `await movie.inspect(frame)` returns every layer's canvas bounds and `issues`. If you do use a browser tool, `screenshot` needs an **absolute path**, macOS `sed -i` needs `''`, and a static server may redirect `/x.html` → `/x` and drop `?query`. Keep the template's `try/catch` around `init` so failures land in `window.__logs`. Check ≥ 5 timestamps including mid-transition. *(fixed: tools built in)*
|
|
54
54
|
31. **Check audio with `movie.inspectAudio()`**: every sound measured on its own (`sound.length`, `peakDb`, `loudestAt`, `brightnessHz`), when it plays, loudness and brightness per 0.1 s window, and `issues` (the mix was limited, a layer is inaudible, an sfx is cut off by the end). Decoding a render by hand is no longer needed: `render()` encodes the same mix, on time in mp4 / mov / webm / mkv. *(fixed)*
|
|
55
55
|
|
|
@@ -135,3 +135,7 @@ A good habit that catches most of these: after `init`, read `window.__logs` (see
|
|
|
135
135
|
105. **Layout that follows the screen is written in `W` / `H` / `GW` / `GH`, never in pixels.** After `movie.resize(w, h)` an expression sees the new size; a literal `x: 960` stays at 960. A string variable cannot be read in an expression (`x: 'title'` is a number context): use `{title}` in a string, or declare the number. A name that is declared but never read, and a `{name}` that was not declared, are both said at `init` (usually a typo). Load a web font BEFORE the first `setVars` that changes a text (see 103): the text is measured again each rebuild.
|
|
136
136
|
106. **A text outline used to spike; it is round-joined now.** `style: { stroke: { color: '#000', width: 12 } }` was drawn with PixiJS's default `miter` join, so the sharp corners of W, V, A, k and 4 grew spikes ("the outline sticks out"). The library now adds `join: 'round'` to an outline that names no join; say `join: 'miter'` or `'bevel'` if you want one. The stroke is centred on the glyph edge, so half of it eats into the letter: keep `width` to about a tenth of `fontSize`, or for a thick outline put a second copy of the text with the stroke UNDER the plain one.
|
|
137
137
|
107. **`spans` places pieces by measuring, so the font must be loaded first, and a span cannot change size.** A web font that is not loaded yet is measured as the fallback font and the pieces drift apart: `await document.fonts.load('30px "Name"')` before `movie.init`. `fontSize` / `fontFamily` / `lineHeight` / `align` on a span are ignored (warned): they belong to the layer's `style`. `fill`, `text` and `value` keyframes do not reach a spans layer (it is a group of text layers).
|
|
138
|
+
108. **Japanese and Chinese do not wrap at spaces** (Korean has spaces, so `wordWrap` breaks there). `wordWrap: true` alone does nothing for a long line (it runs off the canvas); `breakWords: true` breaks between any two characters and can leave one to three characters alone on the last line. Break the line yourself with `\n` after a comma or a particle and size the font to fit; `check` reports a line cut by the canvas edge but does not judge an ugly break, so look at the contact sheet. Raising `fontSize` can turn a clean line into an orphan: look again after any size change.
|
|
139
|
+
109. **No font is bundled, and each script needs a font that has its characters.** The machine's own fonts are used; characters a font lacks are drawn with whatever the machine falls back to, so the same page can look different on another machine. Load a web font that covers the script (Noto Sans JP, Noto Sans KR for Hangul, Noto Sans SC) with `await document.fonts.load('40px "Noto Sans KR"')` before `movie.init`, and say it in `style.fontFamily` (pitfall 103). `check` lists a layer none of whose fonts is available, but a family that nothing installs or loads is silently replaced by the next one in the list (or the generic name) without a word: the first name in `fontFamily` is not proof it was used.
|
|
140
|
+
110. **Right-to-left text: three traps.** `splitText` / `animateText` order words left to right, so Arabic or Hebrew comes out reversed (split by words yourself, from the right edge, with `measureText` of the logical prefix); a mark below the font box can be clipped (the dots of an Arabic ي) unless the layer has `style.padding`; `check` sees neither. `bulgePinch.center` is 0–1 of the layer, not pixels (pitfalls elsewhere say canvas pixels for the other centres: `twist`, `zoomBlur`, `shockwave`). `npx pixi-effects help arabic` and `help bulgePinch` print the sections.
|
|
141
|
+
111. **Only some named filters are tested on both backends.** `glow`, `outline`, `dropShadow`, `bevel`, `bloom`, `pixelate`, `crt`, `rgbSplit`, `dot`, `radialBlur` and `glitch` look the same on WebGPU (the default) and WebGL; `multiColorReplace` draws everything black on WebGPU, `kawaseBlur` is far weaker and `advancedBloom` sharper there: use `colorReplace`, `blur`, `bloom`. A filter follows the edges of the layer's own picture: `glow` on a full-screen opaque layer shows nothing (put it on the shapes and the text).
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# pixi-effects quick start
|
|
2
2
|
|
|
3
|
-
npx pixi-effects new my-video # a page to edit
|
|
3
|
+
npx pixi-effects new my-video # a page to edit (--size 1080x1920 --duration 20)
|
|
4
4
|
npx pixi-effects view my-video.html # look at it while you edit
|
|
5
5
|
npx pixi-effects check my-video.html # warnings, layout, sound, a contact sheet: LOOK at the PNG
|
|
6
6
|
npx pixi-effects help <word> # the docs about a word: mask, threeD, keyframes, music
|
|
@@ -25,5 +25,6 @@ What goes wrong most:
|
|
|
25
25
|
- Anchors: rect and circle are centred on x,y; text and image are top-left (use `anchorX: 0.5`).
|
|
26
26
|
- A warning is an instruction: `window.__logs` must be `[]`. Fix what it names.
|
|
27
27
|
- Sound needs no files: `{ type: 'audio', sfx: 'swoosh', at: 2 }`, or `music: { bpm, tracks }`.
|
|
28
|
-
- Never say done before you have looked at the check's PNG.
|
|
28
|
+
- Never say done before you have looked at the check's PNG: it cannot judge a line break, a font, or how a fade ends.
|
|
29
|
+
- Exist already (`help <word>`): `spans` (colour part of a text line), `vars` + `--var name=value` / `--size 1080x1920` (one page, many versions).
|
|
29
30
|
- Anything else: `help <word>` first; the cheatsheet is for grep, not for reading.
|
|
@@ -1179,7 +1179,7 @@ done < table.csv
|
|
|
1179
1179
|
|
|
1180
1180
|
## One film, several languages and colours (variables)
|
|
1181
1181
|
|
|
1182
|
-
Declare what changes as `vars` and write `{name}` in the strings and the names as identifiers in the expressions. One page then makes every variant: `pixi-effects render film.html --var
|
|
1182
|
+
Declare what changes as `vars` and write `{name}` in the strings and the names as identifiers in the expressions. One page then makes every variant: a declared variable is set from the command line (`pixi-effects render film.html --var accent=#3949ab -o film-blue.mp4`; a name the page did not declare stops the run), the language below is picked by the page from the query (`--query lang=ja`, on `check` as well as `render`), and a page can switch live with `movie.setVars(...)`. Load the fonts of every language before the first build (pitfall 103); `check` says a variable nothing reads, and a `{name}` nobody declared.
|
|
1183
1183
|
|
|
1184
1184
|
```js
|
|
1185
1185
|
// @docs-only variants
|
|
@@ -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.
|
|
7
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/pixi-effects@0.34.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,16 +37,16 @@
|
|
|
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.
|
|
41
|
-
"pixi-effects/controller": "https://cdn.jsdelivr.net/npm/pixi-effects@0.
|
|
42
|
-
"pixi-effects/present": "https://cdn.jsdelivr.net/npm/pixi-effects@0.
|
|
43
|
-
"pixi-effects/three": "https://cdn.jsdelivr.net/npm/pixi-effects@0.
|
|
40
|
+
"pixi-effects": "https://cdn.jsdelivr.net/npm/pixi-effects@0.34.0/dist/index.js",
|
|
41
|
+
"pixi-effects/controller": "https://cdn.jsdelivr.net/npm/pixi-effects@0.34.0/dist/Controller.js",
|
|
42
|
+
"pixi-effects/present": "https://cdn.jsdelivr.net/npm/pixi-effects@0.34.0/dist/present.js",
|
|
43
|
+
"pixi-effects/three": "https://cdn.jsdelivr.net/npm/pixi-effects@0.34.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
|
}
|
|
47
47
|
}
|
|
48
48
|
</script>
|
|
49
|
-
<link rel="modulepreload" href="https://cdn.jsdelivr.net/npm/pixi-effects@0.
|
|
49
|
+
<link rel="modulepreload" href="https://cdn.jsdelivr.net/npm/pixi-effects@0.34.0/dist/index.js" /> <!-- after the import map: the library downloads while the page is parsed -->
|
|
50
50
|
|
|
51
51
|
<script type="module">
|
|
52
52
|
import { Movie, kenBurns, withFade } from 'pixi-effects';
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
var chunkCRUDOKSD_cjs = require('./chunk-CRUDOKSD.cjs');
|
|
4
|
-
require('./chunk-QHDN6TF5.cjs');
|
|
5
|
-
require('./chunk-Z437UWI4.cjs');
|
|
6
|
-
require('./chunk-FCGYJCVO.cjs');
|
|
7
|
-
require('./chunk-TWZNOUM7.cjs');
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
Object.defineProperty(exports, "CompositionSequence", {
|
|
12
|
-
enumerable: true,
|
|
13
|
-
get: function () { return chunkCRUDOKSD_cjs.CompositionSequence; }
|
|
14
|
-
});
|
|
15
|
-
//# sourceMappingURL=Composition-LNMKWXSL.cjs.map
|
|
16
|
-
//# sourceMappingURL=Composition-LNMKWXSL.cjs.map
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
export { CompositionSequence } from './chunk-D5BFQVKL.js';
|
|
2
|
-
import './chunk-AW5NPDE4.js';
|
|
3
|
-
import './chunk-QFNS6BFL.js';
|
|
4
|
-
import './chunk-C5BNHVKK.js';
|
|
5
|
-
import './chunk-BXOCEZ4U.js';
|
|
6
|
-
//# sourceMappingURL=Composition-RF7QOEXT.js.map
|
|
7
|
-
//# sourceMappingURL=Composition-RF7QOEXT.js.map
|