pixi-effects 0.28.1 → 0.28.2

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 (33) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +6 -6
  3. package/ai/chat-template.html +3 -3
  4. package/ai/tools/view.mjs +102 -14
  5. package/dist/Composition-H7UNR5F5.js +7 -0
  6. package/dist/{Composition-HFEVIYHN.js.map → Composition-H7UNR5F5.js.map} +1 -1
  7. package/dist/Composition-K6B3APM5.cjs +16 -0
  8. package/dist/{Composition-NI74UJT3.cjs.map → Composition-K6B3APM5.cjs.map} +1 -1
  9. package/dist/{chunk-WEJAQAFX.js → chunk-5MZWXVTT.js} +3 -3
  10. package/dist/{chunk-WEJAQAFX.js.map → chunk-5MZWXVTT.js.map} +1 -1
  11. package/dist/{chunk-AK54FIWM.js → chunk-7VN2KI3U.js} +6 -3
  12. package/dist/chunk-7VN2KI3U.js.map +1 -0
  13. package/dist/{chunk-SBOVUZZY.cjs → chunk-KBH33IQX.cjs} +61 -61
  14. package/dist/{chunk-SBOVUZZY.cjs.map → chunk-KBH33IQX.cjs.map} +1 -1
  15. package/dist/{chunk-MZCDBRTY.cjs → chunk-MJW7KILM.cjs} +6 -3
  16. package/dist/chunk-MJW7KILM.cjs.map +1 -0
  17. package/dist/index.cjs +35 -33
  18. package/dist/index.cjs.map +1 -1
  19. package/dist/index.js +7 -5
  20. package/dist/index.js.map +1 -1
  21. package/dist/loader.css +1 -1
  22. package/dist/three.cjs +5 -5
  23. package/dist/three.js +1 -1
  24. package/llms-full.txt +5 -4
  25. package/package.json +1 -1
  26. package/skills/pixi-effects/SKILL.md +2 -2
  27. package/skills/pixi-effects/reference/direction.md +2 -2
  28. package/skills/pixi-effects/reference/pitfalls.md +1 -0
  29. package/skills/pixi-effects/template.html +6 -6
  30. package/dist/Composition-HFEVIYHN.js +0 -7
  31. package/dist/Composition-NI74UJT3.cjs +0 -16
  32. package/dist/chunk-AK54FIWM.js.map +0 -1
  33. package/dist/chunk-MZCDBRTY.cjs.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.28.1/dist/loader.css">
4
+ * <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/pixi-effects@0.28.2/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 chunkMZCDBRTY_cjs = require('./chunk-MZCDBRTY.cjs');
3
+ var chunkMJW7KILM_cjs = require('./chunk-MJW7KILM.cjs');
4
4
  require('./chunk-ZMAD3KJU.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 chunkMZCDBRTY_cjs.Sequence {
9
+ var ThreeSequence = class extends chunkMJW7KILM_cjs.Sequence {
10
10
  _renderer = null;
11
11
  _scene = null;
12
12
  _camera = null;
@@ -25,7 +25,7 @@ var ThreeSequence = class extends chunkMZCDBRTY_cjs.Sequence {
25
25
  if (this.duration === void 0) {
26
26
  this.duration = this.parent?.duration ?? this.root.duration;
27
27
  }
28
- const scope = chunkMZCDBRTY_cjs.buildScope(this, this.parent, this.root);
28
+ const scope = chunkMJW7KILM_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 chunkMZCDBRTY_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 chunkMZCDBRTY_cjs.evaluateExpr(v, scope);
148
+ return chunkMJW7KILM_cjs.evaluateExpr(v, scope);
149
149
  }
150
150
 
151
151
  // src/three/index.ts
152
152
  function registerThree() {
153
- chunkMZCDBRTY_cjs.registerSequenceType("three", ThreeSequence);
153
+ chunkMJW7KILM_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-AK54FIWM.js';
1
+ import { Sequence, buildScope, evaluateExpr, registerSequenceType } from './chunk-7VN2KI3U.js';
2
2
  import './chunk-45QKLD6M.js';
3
3
  import './chunk-BXOCEZ4U.js';
4
4
  import { Texture, Sprite } from 'pixi.js';
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.28.1. Update this skill with `npx skills update`; the newest docs are at https://yjmtmtk.github.io/pixi-effects/
9
+ > Written for pixi-effects 0.28.2. 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
 
@@ -17,7 +17,7 @@ You describe a video as a tree of plain objects; the library plays it in a canva
17
17
  1. **Start from `template.html`** (copy it; it also has the shared loader (`<div class="pe-loader" data-label="LOADING">` + `pixi-effects/loader.css`: HTML and CSS only, animating just `transform` / `opacity`, so it keeps moving while the page builds the video; `movie.init()` fades it out). Keep it; a loader of your own is fine (`pe-loader--custom`) as long as it only animates `transform` / `opacity`, never with JavaScript or `requestAnimationFrame`; it loads the library from a CDN, collects every console warning into `window.__logs`, and exposes `window.movie`). One HTML file, plain JS. Default canvas 1280×720 @ 30 fps.
18
18
  2. **Give the movie a poster time.** In `movie.init` add `poster: <seconds>`: the most striking moment (a title fully on screen, not the first frame, which is often black). The canvas shows that picture before play (the playhead stays at 0 and play starts from 0), and the check writes it as `poster.jpg`.
19
19
  3. **Write the composition as data.** Anything repeated (letters, bars, particles, scenes) is a JS function or loop that returns specs — never copy-paste objects. Derive coordinates and times from constants/data, not magic numbers.
20
- 4. **Show the human:** `npx pixi-effects view my-video.html` opens their browser on the page with a scrubbable, zoomable timeline under it (click / drag to seek, + / − or Ctrl/⌘ + wheel to zoom); it keeps running until Ctrl-C. **Deliver the file:** `npx pixi-effects render my-video.html -o my-video.mp4` renders the page headless to mp4 / webm / mov / mkv and exits 0 only when the file is written; run it AFTER the check below passes. **Run the check — one command does steps 3, 4 and the export:** `npx pixi-effects check my-video.html` (Node ≥ 22 and Chrome installed). **If `npx` says command not found, or prints nothing** (your npm may hold back a release that is a few days old, or an old release is cached): `npm i pixi-effects`, then `node node_modules/pixi-effects/ai/tools/check.mjs my-video.html` (the same for `render.mjs` and `view.mjs`). It starts its own private Chrome (nothing is shared with other sessions), waits for `window.__ready`, and writes `timeline.html` (every layer as a bar on a time axis: ◆ keyframes, shaded transitions; hand it to the human to open) and prints: console warnings, `movie.inspect` over the WHOLE timeline (grouped), `movie.inspectAudio()`, then exports an mp4 and decodes it again (video size and length, audio length, loudness per second, silence). It writes `check-out/<name>/sheet.png` — **open that picture and look at it** — plus the mp4 and `report.json`. Exit 0 = nothing to fix; exit 1 = a list of problems; fix them and run it again. A key a layer does not have (a typo in a layer key, a keyframe property or a text style key; `anchorX` outside `initial`; `pitch` outside `sfx`) is a warning that names the key and what you probably meant: treat it like any other warning. Text that overlaps text is listed under `review` (not a failure: ghost layers, glow copies and per-letter boxes are often intentional; look at the sheet; `--strict` makes them fail). The manual steps below are for looking at one frame closely. The same judgement is in the page: `await movie.review({ at: 'title@end' })` returns `{ frames, problems, review, fonts, audio, at }` (what `check` prints, as data) and `movie.resolveAt('3.5, title@end')` turns moments into frames.
20
+ 4. **Show the human, EARLY: the first address goes out within two minutes.** Do it right after step 1, BEFORE you write the brief or collect assets: the copied template is already a page. Run `npx pixi-effects view my-video.html` (`--no-open` only prints the address; in the background, it keeps running), tell the human the address in your first message, and keep editing the same file: it opens their browser on the page with a scrubbable, zoomable timeline under it (click / drag to seek, + / − or Ctrl/⌘ + wheel to zoom) and runs until Ctrl-C. When the folder changes the viewer shows "Updated hh:mm:ss — reload (R)" and nothing else: the picture they are looking at never switches by itself, a reload returns to the frame they were on, and the switch "auto" (off by default) reloads only while the movie is paused. Do not restart it after every edit; they see the work grow instead of waiting for the end. **Deliver the file:** `npx pixi-effects render my-video.html -o my-video.mp4` renders the page headless to mp4 / webm / mov / mkv and exits 0 only when the file is written; run it AFTER the check below passes. **Run the check — one command does steps 3, 4 and the export:** `npx pixi-effects check my-video.html` (Node ≥ 22 and Chrome installed). **If `npx` says command not found, or prints nothing** (your npm may hold back a release that is a few days old, or an old release is cached): `npm i pixi-effects`, then `node node_modules/pixi-effects/ai/tools/check.mjs my-video.html` (the same for `render.mjs` and `view.mjs`). It starts its own private Chrome (nothing is shared with other sessions), waits for `window.__ready`, and writes `timeline.html` (every layer as a bar on a time axis: ◆ keyframes, shaded transitions; hand it to the human to open) and prints: console warnings, `movie.inspect` over the WHOLE timeline (grouped), `movie.inspectAudio()`, then exports an mp4 and decodes it again (video size and length, audio length, loudness per second, silence). It writes `check-out/<name>/sheet.png` — **open that picture and look at it** — plus the mp4 and `report.json`. Exit 0 = nothing to fix; exit 1 = a list of problems; fix them and run it again. A key a layer does not have (a typo in a layer key, a keyframe property or a text style key; `anchorX` outside `initial`; `pitch` outside `sfx`) is a warning that names the key and what you probably meant: treat it like any other warning. Text that overlaps text is listed under `review` (not a failure: ghost layers, glow copies and per-letter boxes are often intentional; look at the sheet; `--strict` makes them fail). The manual steps below are for looking at one frame closely. The same judgement is in the page: `await movie.review({ at: 'title@end' })` returns `{ frames, problems, review, fonts, audio, at }` (what `check` prints, as data) and `movie.resolveAt('3.5, title@end')` turns moments into frames.
21
21
  **Or by hand: run it and read the logs first.** `window.__ready` must be `true` and `window.__logs` should be `[]`. A pixi-effects warning is an instruction: fix what it names.
22
22
  5. **Look at it** — you cannot judge motion from code. The library has the tools built in:
23
23
  - `await movie.contactSheet({ count: 6, as: 'dataURL' })` → ONE image of several labelled frames (pass `frames: [...]` / `times: [...]` to choose; include the middle of every transition and the last second). Save it and view it.
@@ -392,6 +392,7 @@ A good habit that catches most of these: after `init`, read `window.__logs` (see
392
392
  100. **Read the score back before you trust it.** `check` prints a `music` block: every track's length in bars (a track that ends early or has a short bar is named), the names that start in each bar with their beat (`Am(2)` starts on beat 2; no mark = the downbeat; the first four names of a bar are shown, `musicEvents()` lists everything), and the drum span. Count bars by the read-back, not by hand: a bar of 3 beats in a score with no `meter` is shown as bars of 4, so set `meter: 3` for a waltz. Drums repeat by themselves and `from` / `to` are beats; `swing` only moves notes that start off the beat, so a comp on beats 2 and 4 stays straight.
393
393
  101. **A film built from one adjective looks like every other one.** Read `reference/direction.md` and write `brief.md` / `style-guide.md` / `shotlist.md` first: real assets with paths, the one sentence to remember, a row per beat with entry and exit state. If a real asset is missing, ask: a drawn fake (a logo, a dashboard, a figure) is the failure that matters. Judge the contact sheet cold: three defects, each with a timestamp, evidence and a local fix.
394
394
  102. **A vertical cut is a second composition, not a crop, and only when someone asked for it.** One source, a layout function, `?f=9x16`; `check` and `render` take `--query f=9x16` (recipe `two-formats`). Look at a contact sheet of each format: type that fits wide is tiny tall. Ask which formats are wanted; make one if the answer is open. In a 1080p film keep every line of text at 34 px or more; if you cannot read it in a sheet tile, a phone cannot.
395
+ 103. **Load a web font BEFORE you measure with it.** `measureText` / `splitText` / `animateText` measure once, when the composition is built. Measured in Chrome: "Respect the Local, Loop the Value." at 80 px Lato 900 is 1487 px wide before the Google font has arrived (the fallback) and 1231 px after; Japanese text in a similar fallback was within 3 px. Every box, line break and centred position built from the wrong width stays wrong. `await document.fonts.load('900 80px "Lato"', theTextYouUse)` first (the text argument loads exactly the glyphs you need), then build the composition.
395
396
 
396
397
 
397
398
  <!-- ===== skills/pixi-effects/reference/recipes.md ===== -->
@@ -1656,7 +1657,7 @@ Many AI films look finished with the sound off; then the click lands after the b
1656
1657
 
1657
1658
  ## Review the rendered frames, not your intent
1658
1659
 
1659
- Do not start by watching a full export ten times, and do not describe what you meant to make. Ask the renderer for a contact sheet: one frame at every beat, a short strip around the fastest transitions, and a frame from the middle of each cut taken from the exported file. Look cold, then write the **three largest defects**, each with a **timestamp**, the **evidence** (what you see) and a **local fix**; patch only those, render those frames again. Ask for a second pair of eyes on the sheet whenever one is available: the author reads their own intent into it.
1660
+ Do not start by watching a full export ten times, and do not describe what you meant to make. Ask the renderer for a contact sheet: one frame at every beat, a short strip around the fastest transitions, and a frame from the middle of each cut taken from the exported file. Look cold, then write the **three largest defects** into `reviews/notes.md`, each with a **timestamp**, the **evidence** (what you see) and a **local fix**; patch only those, render those frames again, and note the result. A defect you fixed but did not write down is lost for the next film. Two things are easy to miss on a sheet, so do them on purpose: **measure the smallest text** on screen (its pixel size scaled to a 1080p frame, not the look of it), and look at **both ends of every cut and at every state a card passes through** (an empty frame that holds content a beat later is a defect). Ask for a second pair of eyes on the sheet whenever one is available: the author reads their own intent into it.
1660
1661
 
1661
1662
  ```
1662
1663
  [ ] text readable at phone size (about 34 px or more on a 1080p frame)
@@ -1686,7 +1687,7 @@ Long runs need places to stop. With a human present:
1686
1687
  5. repair the three highest-impact defects
1687
1688
  6. check sound, formats and deliverables
1688
1689
 
1689
- Between gates the agent may work for hours; it never makes an irreversible brand decision (logo, claim, number) for the human, and a missing real asset is a blocker, not an invitation to invent one. If the reference is unclear, the right output is a question or one small test frame, not nine hundred polished wrong ones. With nobody to ask, record each decision you took as a "ruling" in the review notes, with what it costs if wrong, and finish with a short note of what still needs a human.
1690
+ Show the work early: serve the page the moment it exists (a copied template is enough, before the brief is written) and give the human the address in your first message; every later save then shows up as a notice they can act on when they choose. A first draft of the opening scene, served where the human can open it, is worth more than a perfect film at the end; a wrong direction is cheap to fix in the first minutes. Between gates the agent may work for hours; it never makes an irreversible brand decision (logo, claim, number) for the human, and a missing real asset is a blocker, not an invitation to invent one. If the reference is unclear, the right output is a question or one small test frame, not nine hundred polished wrong ones. With nobody to ask, record each decision you took as a "ruling" in the review notes, with what it costs if wrong, and finish with a short note of what still needs a human.
1690
1691
 
1691
1692
  ## The loop
1692
1693
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pixi-effects",
3
- "version": "0.28.1",
3
+ "version": "0.28.2",
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.28.1. Update this skill with `npx skills update`; the newest docs are at https://yjmtmtk.github.io/pixi-effects/
6
+ > Written for pixi-effects 0.28.2. 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
 
@@ -14,7 +14,7 @@ You describe a video as a tree of plain objects; the library plays it in a canva
14
14
  1. **Start from `template.html`** (copy it; it also has the shared loader (`<div class="pe-loader" data-label="LOADING">` + `pixi-effects/loader.css`: HTML and CSS only, animating just `transform` / `opacity`, so it keeps moving while the page builds the video; `movie.init()` fades it out). Keep it; a loader of your own is fine (`pe-loader--custom`) as long as it only animates `transform` / `opacity`, never with JavaScript or `requestAnimationFrame`; it loads the library from a CDN, collects every console warning into `window.__logs`, and exposes `window.movie`). One HTML file, plain JS. Default canvas 1280×720 @ 30 fps.
15
15
  2. **Give the movie a poster time.** In `movie.init` add `poster: <seconds>`: the most striking moment (a title fully on screen, not the first frame, which is often black). The canvas shows that picture before play (the playhead stays at 0 and play starts from 0), and the check writes it as `poster.jpg`.
16
16
  3. **Write the composition as data.** Anything repeated (letters, bars, particles, scenes) is a JS function or loop that returns specs — never copy-paste objects. Derive coordinates and times from constants/data, not magic numbers.
17
- 4. **Show the human:** `npx pixi-effects view my-video.html` opens their browser on the page with a scrubbable, zoomable timeline under it (click / drag to seek, + / − or Ctrl/⌘ + wheel to zoom); it keeps running until Ctrl-C. **Deliver the file:** `npx pixi-effects render my-video.html -o my-video.mp4` renders the page headless to mp4 / webm / mov / mkv and exits 0 only when the file is written; run it AFTER the check below passes. **Run the check — one command does steps 3, 4 and the export:** `npx pixi-effects check my-video.html` (Node ≥ 22 and Chrome installed). **If `npx` says command not found, or prints nothing** (your npm may hold back a release that is a few days old, or an old release is cached): `npm i pixi-effects`, then `node node_modules/pixi-effects/ai/tools/check.mjs my-video.html` (the same for `render.mjs` and `view.mjs`). It starts its own private Chrome (nothing is shared with other sessions), waits for `window.__ready`, and writes `timeline.html` (every layer as a bar on a time axis: ◆ keyframes, shaded transitions; hand it to the human to open) and prints: console warnings, `movie.inspect` over the WHOLE timeline (grouped), `movie.inspectAudio()`, then exports an mp4 and decodes it again (video size and length, audio length, loudness per second, silence). It writes `check-out/<name>/sheet.png` — **open that picture and look at it** — plus the mp4 and `report.json`. Exit 0 = nothing to fix; exit 1 = a list of problems; fix them and run it again. A key a layer does not have (a typo in a layer key, a keyframe property or a text style key; `anchorX` outside `initial`; `pitch` outside `sfx`) is a warning that names the key and what you probably meant: treat it like any other warning. Text that overlaps text is listed under `review` (not a failure: ghost layers, glow copies and per-letter boxes are often intentional; look at the sheet; `--strict` makes them fail). The manual steps below are for looking at one frame closely. The same judgement is in the page: `await movie.review({ at: 'title@end' })` returns `{ frames, problems, review, fonts, audio, at }` (what `check` prints, as data) and `movie.resolveAt('3.5, title@end')` turns moments into frames.
17
+ 4. **Show the human, EARLY: the first address goes out within two minutes.** Do it right after step 1, BEFORE you write the brief or collect assets: the copied template is already a page. Run `npx pixi-effects view my-video.html` (`--no-open` only prints the address; in the background, it keeps running), tell the human the address in your first message, and keep editing the same file: it opens their browser on the page with a scrubbable, zoomable timeline under it (click / drag to seek, + / − or Ctrl/⌘ + wheel to zoom) and runs until Ctrl-C. When the folder changes the viewer shows "Updated hh:mm:ss — reload (R)" and nothing else: the picture they are looking at never switches by itself, a reload returns to the frame they were on, and the switch "auto" (off by default) reloads only while the movie is paused. Do not restart it after every edit; they see the work grow instead of waiting for the end. **Deliver the file:** `npx pixi-effects render my-video.html -o my-video.mp4` renders the page headless to mp4 / webm / mov / mkv and exits 0 only when the file is written; run it AFTER the check below passes. **Run the check — one command does steps 3, 4 and the export:** `npx pixi-effects check my-video.html` (Node ≥ 22 and Chrome installed). **If `npx` says command not found, or prints nothing** (your npm may hold back a release that is a few days old, or an old release is cached): `npm i pixi-effects`, then `node node_modules/pixi-effects/ai/tools/check.mjs my-video.html` (the same for `render.mjs` and `view.mjs`). It starts its own private Chrome (nothing is shared with other sessions), waits for `window.__ready`, and writes `timeline.html` (every layer as a bar on a time axis: ◆ keyframes, shaded transitions; hand it to the human to open) and prints: console warnings, `movie.inspect` over the WHOLE timeline (grouped), `movie.inspectAudio()`, then exports an mp4 and decodes it again (video size and length, audio length, loudness per second, silence). It writes `check-out/<name>/sheet.png` — **open that picture and look at it** — plus the mp4 and `report.json`. Exit 0 = nothing to fix; exit 1 = a list of problems; fix them and run it again. A key a layer does not have (a typo in a layer key, a keyframe property or a text style key; `anchorX` outside `initial`; `pitch` outside `sfx`) is a warning that names the key and what you probably meant: treat it like any other warning. Text that overlaps text is listed under `review` (not a failure: ghost layers, glow copies and per-letter boxes are often intentional; look at the sheet; `--strict` makes them fail). The manual steps below are for looking at one frame closely. The same judgement is in the page: `await movie.review({ at: 'title@end' })` returns `{ frames, problems, review, fonts, audio, at }` (what `check` prints, as data) and `movie.resolveAt('3.5, title@end')` turns moments into frames.
18
18
  **Or by hand: run it and read the logs first.** `window.__ready` must be `true` and `window.__logs` should be `[]`. A pixi-effects warning is an instruction: fix what it names.
19
19
  5. **Look at it** — you cannot judge motion from code. The library has the tools built in:
20
20
  - `await movie.contactSheet({ count: 6, as: 'dataURL' })` → ONE image of several labelled frames (pass `frames: [...]` / `times: [...]` to choose; include the middle of every transition and the last second). Save it and view it.
@@ -97,7 +97,7 @@ Many AI films look finished with the sound off; then the click lands after the b
97
97
 
98
98
  ## Review the rendered frames, not your intent
99
99
 
100
- Do not start by watching a full export ten times, and do not describe what you meant to make. Ask the renderer for a contact sheet: one frame at every beat, a short strip around the fastest transitions, and a frame from the middle of each cut taken from the exported file. Look cold, then write the **three largest defects**, each with a **timestamp**, the **evidence** (what you see) and a **local fix**; patch only those, render those frames again. Ask for a second pair of eyes on the sheet whenever one is available: the author reads their own intent into it.
100
+ Do not start by watching a full export ten times, and do not describe what you meant to make. Ask the renderer for a contact sheet: one frame at every beat, a short strip around the fastest transitions, and a frame from the middle of each cut taken from the exported file. Look cold, then write the **three largest defects** into `reviews/notes.md`, each with a **timestamp**, the **evidence** (what you see) and a **local fix**; patch only those, render those frames again, and note the result. A defect you fixed but did not write down is lost for the next film. Two things are easy to miss on a sheet, so do them on purpose: **measure the smallest text** on screen (its pixel size scaled to a 1080p frame, not the look of it), and look at **both ends of every cut and at every state a card passes through** (an empty frame that holds content a beat later is a defect). Ask for a second pair of eyes on the sheet whenever one is available: the author reads their own intent into it.
101
101
 
102
102
  ```
103
103
  [ ] text readable at phone size (about 34 px or more on a 1080p frame)
@@ -127,7 +127,7 @@ Long runs need places to stop. With a human present:
127
127
  5. repair the three highest-impact defects
128
128
  6. check sound, formats and deliverables
129
129
 
130
- Between gates the agent may work for hours; it never makes an irreversible brand decision (logo, claim, number) for the human, and a missing real asset is a blocker, not an invitation to invent one. If the reference is unclear, the right output is a question or one small test frame, not nine hundred polished wrong ones. With nobody to ask, record each decision you took as a "ruling" in the review notes, with what it costs if wrong, and finish with a short note of what still needs a human.
130
+ Show the work early: serve the page the moment it exists (a copied template is enough, before the brief is written) and give the human the address in your first message; every later save then shows up as a notice they can act on when they choose. A first draft of the opening scene, served where the human can open it, is worth more than a perfect film at the end; a wrong direction is cheap to fix in the first minutes. Between gates the agent may work for hours; it never makes an irreversible brand decision (logo, claim, number) for the human, and a missing real asset is a blocker, not an invitation to invent one. If the reference is unclear, the right output is a question or one small test frame, not nine hundred polished wrong ones. With nobody to ask, record each decision you took as a "ruling" in the review notes, with what it costs if wrong, and finish with a short note of what still needs a human.
131
131
 
132
132
  ## The loop
133
133
 
@@ -130,3 +130,4 @@ A good habit that catches most of these: after `init`, read `window.__logs` (see
130
130
  100. **Read the score back before you trust it.** `check` prints a `music` block: every track's length in bars (a track that ends early or has a short bar is named), the names that start in each bar with their beat (`Am(2)` starts on beat 2; no mark = the downbeat; the first four names of a bar are shown, `musicEvents()` lists everything), and the drum span. Count bars by the read-back, not by hand: a bar of 3 beats in a score with no `meter` is shown as bars of 4, so set `meter: 3` for a waltz. Drums repeat by themselves and `from` / `to` are beats; `swing` only moves notes that start off the beat, so a comp on beats 2 and 4 stays straight.
131
131
  101. **A film built from one adjective looks like every other one.** Read `reference/direction.md` and write `brief.md` / `style-guide.md` / `shotlist.md` first: real assets with paths, the one sentence to remember, a row per beat with entry and exit state. If a real asset is missing, ask: a drawn fake (a logo, a dashboard, a figure) is the failure that matters. Judge the contact sheet cold: three defects, each with a timestamp, evidence and a local fix.
132
132
  102. **A vertical cut is a second composition, not a crop, and only when someone asked for it.** One source, a layout function, `?f=9x16`; `check` and `render` take `--query f=9x16` (recipe `two-formats`). Look at a contact sheet of each format: type that fits wide is tiny tall. Ask which formats are wanted; make one if the answer is open. In a 1080p film keep every line of text at 34 px or more; if you cannot read it in a sheet tile, a phone cannot.
133
+ 103. **Load a web font BEFORE you measure with it.** `measureText` / `splitText` / `animateText` measure once, when the composition is built. Measured in Chrome: "Respect the Local, Loop the Value." at 80 px Lato 900 is 1487 px wide before the Google font has arrived (the fallback) and 1231 px after; Japanese text in a similar fallback was within 3 px. Every box, line break and centred position built from the wrong width stays wrong. `await document.fonts.load('900 80px "Lato"', theTextYouUse)` first (the text argument loads exactly the glyphs you need), then build the composition.
@@ -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.28.1/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.28.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 -->
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.28.1/dist/index.js",
41
- "pixi-effects/controller": "https://cdn.jsdelivr.net/npm/pixi-effects@0.28.1/dist/Controller.js",
42
- "pixi-effects/presenter": "https://cdn.jsdelivr.net/npm/pixi-effects@0.28.1/dist/Presenter.js",
43
- "pixi-effects/three": "https://cdn.jsdelivr.net/npm/pixi-effects@0.28.1/dist/three.js",
40
+ "pixi-effects": "https://cdn.jsdelivr.net/npm/pixi-effects@0.28.2/dist/index.js",
41
+ "pixi-effects/controller": "https://cdn.jsdelivr.net/npm/pixi-effects@0.28.2/dist/Controller.js",
42
+ "pixi-effects/presenter": "https://cdn.jsdelivr.net/npm/pixi-effects@0.28.2/dist/Presenter.js",
43
+ "pixi-effects/three": "https://cdn.jsdelivr.net/npm/pixi-effects@0.28.2/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.28.1/dist/index.js" /> <!-- after the import map: the library downloads while the page is parsed -->
49
+ <link rel="modulepreload" href="https://cdn.jsdelivr.net/npm/pixi-effects@0.28.2/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,7 +0,0 @@
1
- export { CompositionSequence } from './chunk-WEJAQAFX.js';
2
- import './chunk-AK54FIWM.js';
3
- import './chunk-TYWFKCPY.js';
4
- import './chunk-45QKLD6M.js';
5
- import './chunk-BXOCEZ4U.js';
6
- //# sourceMappingURL=Composition-HFEVIYHN.js.map
7
- //# sourceMappingURL=Composition-HFEVIYHN.js.map
@@ -1,16 +0,0 @@
1
- 'use strict';
2
-
3
- var chunkSBOVUZZY_cjs = require('./chunk-SBOVUZZY.cjs');
4
- require('./chunk-MZCDBRTY.cjs');
5
- require('./chunk-P4GY3Z4G.cjs');
6
- require('./chunk-ZMAD3KJU.cjs');
7
- require('./chunk-TWZNOUM7.cjs');
8
-
9
-
10
-
11
- Object.defineProperty(exports, "CompositionSequence", {
12
- enumerable: true,
13
- get: function () { return chunkSBOVUZZY_cjs.CompositionSequence; }
14
- });
15
- //# sourceMappingURL=Composition-NI74UJT3.cjs.map
16
- //# sourceMappingURL=Composition-NI74UJT3.cjs.map