@pixerate/flair 0.17.0 → 0.18.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 CHANGED
@@ -1,5 +1,11 @@
1
1
  # @pixerate/flair
2
2
 
3
+ ## 0.18.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 7fcc7c7: Effects on HTML5 video: `ShaderRenderer.setLiveSource(name, video)` and `ShaderFrames.setLiveSource` show a playing `<video>` (or a canvas) in an image control. The current frame is uploaded each time it changes, and a paused preview still follows the video. `seekVideo` steps a video for frame-exact export, and `ready()` waits for each video's first frame. Shader effects with image controls now carry `video: "filter" | "transition" | "interactive" | "stills" | "none"`, which says how they suit video. `findEffects({ video })` and the `catalog.search` command filter by it.
8
+
3
9
  ## 0.17.0
4
10
 
5
11
  ### Minor Changes
package/README.md CHANGED
@@ -42,7 +42,7 @@ Flair gives products a library of good-looking, configurable visual effects (her
42
42
  | Import | What it provides | Runtime dependencies |
43
43
  | :--- | :--- | :--- |
44
44
  | `@pixerate/flair` | Shader engine, vector motion, time and easing, colour, blending, transitions, scenes, interchange exporters | None (browser-safe) |
45
- | `@pixerate/flair/catalog` | `EFFECTS`, `getEffect`, `findEffects`, `effectDefaults`, `renderVectorEffect`, `vectorControls`, GLSL helpers, sample images, `textImage` | None |
45
+ | `@pixerate/flair/catalog` | `EFFECTS`, `getEffect`, `findEffects` (also by `video` use), `effectDefaults`, `renderVectorEffect`, `vectorControls`, GLSL helpers, sample images, `textImage` | None |
46
46
  | `@pixerate/flair/vector` | `SvgMotion`, `svgFrame`, `svgView`, `SvgPlayer`, `svgFrameMarkup`, `svgViewBox`, `animateCount`, `formatCount`, `morphPaths`, `morphSequence`, `svgMotionIssues` | None |
47
47
  | `@pixerate/flair/react` | `<VectorEffect>`, `<SvgPlayerView>`: vector effects as React components, server-rendered as a still | `react` ≥ 18 (optional peer) |
48
48
  | `@pixerate/flair/svelte` | `vectorEffect` action, `vectorEffectStill`: vector effects in Svelte, server-rendered as a still | `svelte` ≥ 5 (optional peer; types only) |
@@ -152,6 +152,31 @@ Find effects with `findEffects({ text: "glow", category: "light", kind: "shader"
152
152
 
153
153
  Effects that show a picture (image, transition, morph, time-warp and the like) take transparent pictures too, such as SVG icons (Lucide and similar sets draw black `currentColor` strokes on a clear background). They lay the picture over a backdrop: `uBackdrop` (white by default) shows through its clear parts. Set `uBackdropAlpha` to 0 to keep the picture's alpha instead, and render with `{ alpha: true }` to lay the result over the page. `SAMPLE_IMAGES.icon` is a Lucide-style sample icon. SVGs are rasterised at 2048 px on the long edge, so icons stay sharp.
154
154
 
155
+ #### Effects on video
156
+
157
+ Image controls can show a playing `<video>` (or a canvas) instead of a picture. `setLiveSource(name, video)` puts it in the control named `name`. The video's current frame is uploaded each time it changes, and a paused preview still follows the video. The renderer never plays, seeks or loops the video itself. The control's value keeps its URL, so documents, presets and agents still see a plain string.
158
+
159
+ ```ts
160
+ const crt = getEffect("crt") as ShaderEffect;
161
+ const video = Object.assign(document.createElement("video"), { src: "/clip.mp4", muted: true, loop: true, crossOrigin: "anonymous" });
162
+ await video.play();
163
+ const preview = new ShaderRenderer(host);
164
+ preview.setSource(crt.source);
165
+ preview.setLiveSource("uImage", video); // null goes back to the URL
166
+ ```
167
+
168
+ Each shader with an image control has a `video` field that says how it suits video:
169
+
170
+ - `filter`: treats each frame on its own, so it works as a live filter (CRT, film look, duotone, glitch, ASCII and others).
171
+ - `transition`: blends two pictures, so it works between two clips (put one video in `uFrom` and one in `uTo`).
172
+ - `interactive`: reacts to the pointer, and works over video too (magnifier, hover reveal and others).
173
+ - `stills`: runs on video but is made for a still picture. Time-warp effects move the picture to fake motion of their own, and the morph effects need shapes in fixed places.
174
+ - `none`: its picture is rendered text or a silhouette mask.
175
+
176
+ `findEffects({ video: ["filter", "transition"] })` lists the effects that suit video. The `catalog.search` command takes the same filter.
177
+
178
+ A video from another site needs `crossOrigin = "anonymous"` and that site's CORS consent. Without it, the control shows black and `onImageError` explains why. For frame-exact output, step the video with `ShaderFrames`: pause the video, then for each frame `await seekVideo(video, t); frames.render(t)`. Both `ShaderRenderer.ready()` and `ShaderFrames.ready()` wait for each live video's first frame.
179
+
155
180
  Overlay shaders (`category: "overlay"`) have `transparent: true`: they write coverage to `fragColor.a` and draw nothing elsewhere, so render them with `new ShaderRenderer(host, { alpha: true })` (or `new ThumbnailPool({ alpha: true })`) and lay the canvas over a picture, video or page. Interaction effects answer the pointer. Interactive shaders read `iPointer` and `iClicks`, so they work in any `ShaderRenderer` with no wiring, and play an idle demo when nobody is pointing (turn off "Demo when idle" to stop it). Vector micro-interactions carry a `trigger`: pass it to the player, `new SvgPlayer(host, renderVectorEffect(effect), { trigger: effect.trigger })`. A hover or press trigger plays to the end while hovered or held and back again after; a click trigger plays the next step on each click, so toggles turn on and off. Hosts can drive them too, with `player.setActive(on)` and `player.advance()` (from a keyboard handler, say).
156
181
 
157
182
  To morph between any two SVG shapes, `morphPaths(from, to)` (from `@pixerate/flair/vector`) returns two paths with one structure, so `attrs: { d: [a, b] }` blends them point by point: curves and arcs are read, pieces are paired by position and size, and a piece with no partner grows from or shrinks to a point (draw them with `fill-rule="evenodd"`). `morphSequence` does the same for three or more shapes. `feature-morph` morphs two photos from five matching points, and `silhouette-morph` morphs any two silhouettes or logos (or words, through `textImage`).
@@ -1,6 +1,6 @@
1
1
  import { a as ControlDef, C as ControlValue } from '../controls-Ec6P0Vpy.js';
2
- import { E as EffectDefinition, a as EffectCategory, S as ShaderEffect } from '../types-C7g8-lTX.js';
3
- export { C as CURRENT_COLOR, b as EFFECT_CATEGORIES, c as EffectPreset, d as VECTOR_CONTROL_TYPES, e as VectorControl, V as VectorEffect, f as colorControl, i as isCurrentColor, n as numberControl, r as renderVectorEffect, v as vectorControlDef, g as vectorControlIssues, h as vectorControls, j as vectorEffectControls } from '../types-C7g8-lTX.js';
2
+ import { E as EffectDefinition, a as EffectCategory, b as VideoUse, S as ShaderEffect } from '../types-kAFjaq2N.js';
3
+ export { C as CURRENT_COLOR, c as EFFECT_CATEGORIES, d as EffectPreset, e as VECTOR_CONTROL_TYPES, f as VIDEO_USES, g as VectorControl, V as VectorEffect, h as colorControl, i as isCurrentColor, n as numberControl, r as renderVectorEffect, v as vectorControlDef, j as vectorControlIssues, k as vectorControls, l as vectorEffectControls } from '../types-kAFjaq2N.js';
4
4
  import '../svg-XugR-6Ec.js';
5
5
 
6
6
  /**
@@ -68,6 +68,8 @@ declare function glsl(...parts: string[]): string;
68
68
  * controls and presets, to drop into a product or start an edit from.
69
69
  *
70
70
  * - Browsing: `EFFECTS`, `getEffect`, `findEffects`, `EFFECT_CATEGORIES`.
71
+ * - Video: shader effects with image controls say how they suit video
72
+ * (`video`, see `VideoUse`); `findEffects({ video: ["filter"] })` finds them.
71
73
  * - Controls: `effectControls`, `effectDefaults`.
72
74
  * - Vector effects: their pictures declare their controls (`vectorControls`),
73
75
  * and `renderVectorEffect` fills them in.
@@ -88,6 +90,8 @@ interface EffectQuery {
88
90
  kind?: EffectDefinition["kind"];
89
91
  /** Effects with every one of these tags. */
90
92
  tags?: readonly string[];
93
+ /** Effects that suit video in one of these ways (shader effects with image controls only). */
94
+ video?: readonly VideoUse[];
91
95
  }
92
96
  /** Effects matching a query, in catalog order. */
93
97
  declare function findEffects(query?: EffectQuery): EffectDefinition[];
@@ -99,4 +103,4 @@ declare function effectDefaults(effect: EffectDefinition, options?: {
99
103
  values?: Record<string, ControlValue>;
100
104
  }): Record<string, ControlValue>;
101
105
 
102
- export { EFFECTS, EffectCategory, EffectDefinition, type EffectQuery, FBM, HASH, NOISE, OKLAB_MIX, PALETTE, ROTATE, SAMPLE_IMAGES, SHADER_EFFECTS, ShaderEffect, effectControls, effectDefaults, findEffects, getEffect, glsl, svgDataUrl, textImage };
106
+ export { EFFECTS, EffectCategory, EffectDefinition, type EffectQuery, FBM, HASH, NOISE, OKLAB_MIX, PALETTE, ROTATE, SAMPLE_IMAGES, SHADER_EFFECTS, ShaderEffect, VideoUse, effectControls, effectDefaults, findEffects, getEffect, glsl, svgDataUrl, textImage };
@@ -11,6 +11,7 @@ import {
11
11
  SAMPLE_IMAGES,
12
12
  SHADER_EFFECTS,
13
13
  VECTOR_CONTROL_TYPES,
14
+ VIDEO_USES,
14
15
  colorControl,
15
16
  effectControls,
16
17
  effectDefaults,
@@ -26,7 +27,7 @@ import {
26
27
  vectorControlIssues,
27
28
  vectorControls,
28
29
  vectorEffectControls
29
- } from "../chunk-LURJMWYM.js";
30
+ } from "../chunk-UI5X4D63.js";
30
31
  import "../chunk-OHW7UG6D.js";
31
32
  import "../chunk-EHDJ3CPJ.js";
32
33
  import "../chunk-VYOMXE6I.js";
@@ -43,6 +44,7 @@ export {
43
44
  SAMPLE_IMAGES,
44
45
  SHADER_EFFECTS,
45
46
  VECTOR_CONTROL_TYPES,
47
+ VIDEO_USES,
46
48
  colorControl,
47
49
  effectControls,
48
50
  effectDefaults,
@@ -5,9 +5,9 @@ import {
5
5
  // src/shader/textures.ts
6
6
  var ImageLoadError = class extends Error {
7
7
  url;
8
- constructor(url, cause) {
8
+ constructor(url, cause, message) {
9
9
  super(
10
- `Could not load the image at ${shortUrl(url)}. If it is on another site, that site must allow cross-origin use (CORS).`,
10
+ message ?? `Could not load the image at ${shortUrl(url)}. If it is on another site, that site must allow cross-origin use (CORS).`,
11
11
  { cause }
12
12
  );
13
13
  this.name = "ImageLoadError";
@@ -105,9 +105,31 @@ function releaseImages(urls) {
105
105
  decoded.delete(url);
106
106
  }
107
107
  }
108
+ function isVideo(source) {
109
+ return "readyState" in source && "currentTime" in source;
110
+ }
111
+ var HAVE_CURRENT_DATA = 2;
112
+ function liveName(name, source) {
113
+ return isVideo(source) && (source.currentSrc || source.src) || name;
114
+ }
115
+ async function seekVideo(video, time) {
116
+ if (video.readyState < 1) {
117
+ await new Promise((resolve, reject) => {
118
+ video.addEventListener("loadedmetadata", () => resolve(), { once: true });
119
+ video.addEventListener("error", () => reject(new Error(`Could not load the video ${shortUrl(video.currentSrc || video.src)}.`)), { once: true });
120
+ });
121
+ }
122
+ const target = Math.max(0, Math.min(time, Number.isFinite(video.duration) ? video.duration : time));
123
+ if (Math.abs(video.currentTime - target) < 1e-6 && video.readyState >= HAVE_CURRENT_DATA && !video.seeking) return;
124
+ await new Promise((resolve) => {
125
+ video.addEventListener("seeked", () => resolve(), { once: true });
126
+ video.currentTime = target;
127
+ });
128
+ }
108
129
  var TextureCache = class {
109
130
  gl;
110
131
  entries = /* @__PURE__ */ new Map();
132
+ live = /* @__PURE__ */ new Map();
111
133
  onLoad;
112
134
  load;
113
135
  placeholder = null;
@@ -150,6 +172,84 @@ var TextureCache = class {
150
172
  this.entries.set(url, created);
151
173
  return created;
152
174
  }
175
+ /**
176
+ * Shows `source` in the image control `name` instead of its URL, frame by
177
+ * frame (`null` goes back to the URL). Its texture is uploaded as it is
178
+ * drawn: a video's whenever it has moved to another frame, a canvas's on
179
+ * every frame. Until a video has a frame it shows black. A video from
180
+ * another site needs CORS (`crossOrigin = "anonymous"` and the site's
181
+ * consent); without it the upload fails, `onLoad` hears an
182
+ * {@link ImageLoadError}, and the control shows black.
183
+ */
184
+ setLive(name, source) {
185
+ const existing = this.live.get(name);
186
+ if (existing?.source === source) return;
187
+ if (existing?.texture) this.gl.deleteTexture(existing.texture);
188
+ this.live.delete(name);
189
+ if (source) this.live.set(name, { source, texture: null, uploaded: NaN, error: null });
190
+ }
191
+ liveTexture(name) {
192
+ const live = this.live.get(name);
193
+ if (!live) return void 0;
194
+ if (live.error || this.gl.isContextLost()) return this.blank();
195
+ const { source } = live;
196
+ if (isVideo(source)) {
197
+ if (source.readyState < HAVE_CURRENT_DATA) return live.texture ?? this.blank();
198
+ if (live.texture && live.uploaded === source.currentTime) return live.texture;
199
+ live.uploaded = source.currentTime;
200
+ }
201
+ const gl = this.gl;
202
+ live.texture ??= gl.createTexture();
203
+ gl.bindTexture(gl.TEXTURE_2D, live.texture);
204
+ gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
205
+ try {
206
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, source);
207
+ } catch (e) {
208
+ const url = liveName(name, source);
209
+ live.error = new ImageLoadError(
210
+ url,
211
+ e,
212
+ `Could not draw the video at ${shortUrl(url)}. If it is on another site, set crossOrigin = "anonymous" on the <video> and make sure that site allows cross-origin use (CORS).`
213
+ );
214
+ this.onLoad(url, live.error);
215
+ return this.blank();
216
+ } finally {
217
+ gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);
218
+ }
219
+ gl.generateMipmap(gl.TEXTURE_2D);
220
+ return live.texture;
221
+ }
222
+ /**
223
+ * Whether a live video has moved to a frame not yet drawn (or has its
224
+ * first frame ready), so a paused preview knows to draw again.
225
+ */
226
+ liveChanged() {
227
+ for (const live of this.live.values()) {
228
+ const { source } = live;
229
+ if (live.error || !isVideo(source) || source.readyState < HAVE_CURRENT_DATA) continue;
230
+ if (!live.texture || live.uploaded !== source.currentTime) return true;
231
+ }
232
+ return false;
233
+ }
234
+ /**
235
+ * Settles once every live video has a frame to show. Rejects with an
236
+ * {@link ImageLoadError} when one fails to load.
237
+ */
238
+ async liveReady() {
239
+ await Promise.all(
240
+ [...this.live.entries()].map(([name, { source }]) => {
241
+ if (!isVideo(source) || source.readyState >= HAVE_CURRENT_DATA) return void 0;
242
+ return new Promise((resolve, reject) => {
243
+ source.addEventListener("loadeddata", () => resolve(), { once: true });
244
+ source.addEventListener(
245
+ "error",
246
+ () => reject(new ImageLoadError(liveName(name, source), source.error, `Could not load the video at ${shortUrl(liveName(name, source))}.`)),
247
+ { once: true }
248
+ );
249
+ });
250
+ })
251
+ );
252
+ }
153
253
  /** The error for `url`, if it failed to load. */
154
254
  error(url) {
155
255
  return this.entries.get(url)?.error ?? null;
@@ -176,6 +276,7 @@ var TextureCache = class {
176
276
  /** Deletes every texture; loads still in flight are dropped. */
177
277
  dispose() {
178
278
  this.retain([]);
279
+ for (const name of [...this.live.keys()]) this.setLive(name, null);
179
280
  if (this.placeholder) this.gl.deleteTexture(this.placeholder);
180
281
  this.placeholder = null;
181
282
  this.disposed = true;
@@ -479,7 +580,8 @@ function drawFrame(gl, shader, controls, values, state, textures) {
479
580
  break;
480
581
  case "sampler2D": {
481
582
  gl.activeTexture(gl.TEXTURE0 + unit);
482
- const texture = textures?.texture(v) ?? null;
583
+ const live = textures?.liveTexture?.(control.name);
584
+ const texture = (live === void 0 ? textures?.texture(v) : live) ?? null;
483
585
  gl.bindTexture(gl.TEXTURE_2D, texture);
484
586
  if (texture && control.texture) applyTextureSettings(gl, control.texture);
485
587
  gl.uniform1i(loc, unit++);
@@ -819,6 +921,7 @@ export {
819
921
  loadImage,
820
922
  preloadImages,
821
923
  releaseImages,
924
+ seekVideo,
822
925
  TextureCache,
823
926
  applyTextureSettings,
824
927
  MAX_CLICKS,
@@ -855,4 +958,4 @@ export {
855
958
  getOklabGLSL,
856
959
  getColorGradingGLSL
857
960
  };
858
- //# sourceMappingURL=chunk-PMVJP5I4.js.map
961
+ //# sourceMappingURL=chunk-HA6FIMH2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/shader/textures.ts","../src/shader/pointer.ts","../src/shader/program.ts","../src/shader/color.ts"],"sourcesContent":["import type { TextureSettings } from \"./controls.js\";\n\n/** Why an image control's URL could not be shown. */\nexport class ImageLoadError extends Error {\n readonly url: string;\n\n constructor(url: string, cause?: unknown, message?: string) {\n super(\n message ??\n `Could not load the image at ${shortUrl(url)}. If it is on another site, that site must allow cross-origin use (CORS).`,\n { cause },\n );\n this.name = \"ImageLoadError\";\n this.url = url;\n }\n}\n\n/** A URL short enough for a message: a data URL is cut after its type. */\nexport function shortUrl(url: string): string {\n if (url.startsWith(\"data:\")) return `${url.slice(0, Math.min(url.indexOf(\",\") + 1 || 40, 40))}…`;\n return url.length > 120 ? `${url.slice(0, 119)}…` : url;\n}\n\n/**\n * The long edge, in pixels, SVG images are rasterised at. WebGL2 guarantees\n * textures this large on every device.\n */\nexport const SVG_EDGE = 2048;\n\nconst SVG_TYPE = \"image/svg+xml\";\n\n/** Whether `url` is an SVG: a `data:image/svg+xml` URL, or a path ending in `.svg`. */\nexport function isSvgUrl(url: string): boolean {\n return /^data:image\\/svg\\+xml[;,]/i.test(url) || /^(?!data:)[^?#]*\\.svg([?#]|$)/i.test(url);\n}\n\n/** A length attribute in user units, or undefined for a percentage, `auto` or nothing. */\nfunction svgLength(value: string | null): number | undefined {\n const match = value?.trim().match(/^([\\d.]+)(px)?$/);\n const n = match ? Number(match[1]) : NaN;\n return n > 0 ? n : undefined;\n}\n\n/**\n * An SVG's markup with `width` and `height` set so its long edge is `edge`\n * pixels, keeping its aspect ratio. An image decodes an SVG at its own size\n * (often icon-sized, or 300 × 150 when it has none), so it would make a\n * small, blurry texture; at this size it is rasterised crisply. A `viewBox`\n * is added where missing, so the drawing scales with the new size rather\n * than staying in the corner. Throws for markup that is not an SVG.\n */\nexport function sizeSvg(markup: string, edge: number): string {\n const doc = new DOMParser().parseFromString(markup, SVG_TYPE);\n const svg = doc.documentElement;\n if (svg.localName !== \"svg\" || doc.getElementsByTagName(\"parsererror\").length) {\n throw new Error(\"Not an SVG document.\");\n }\n const box = (svg.getAttribute(\"viewBox\") ?? \"\")\n .trim()\n .split(/[\\s,]+/)\n .map(Number);\n const hasBox = box.length === 4 && box[2]! > 0 && box[3]! > 0;\n const width = svgLength(svg.getAttribute(\"width\")) ?? (hasBox ? box[2]! : 300);\n const height = svgLength(svg.getAttribute(\"height\")) ?? (hasBox ? box[3]! : 150);\n if (!hasBox) svg.setAttribute(\"viewBox\", `0 0 ${width} ${height}`);\n const scale = edge / Math.max(width, height);\n svg.setAttribute(\"width\", String(Math.max(1, Math.round(width * scale))));\n svg.setAttribute(\"height\", String(Math.max(1, Math.round(height * scale))));\n return new XMLSerializer().serializeToString(svg);\n}\n\n/**\n * Loads and decodes an image for WebGL. Images from other origins are\n * requested with CORS (`crossOrigin = \"anonymous\"`): WebGL refuses pixels a\n * page is not allowed to read. SVGs (see {@link isSvgUrl}) are fetched, also\n * with CORS, and sized to {@link SVG_EDGE} pixels on their long edge before\n * they are decoded, so they stay sharp however small they were drawn.\n */\nexport async function loadImage(url: string): Promise<HTMLImageElement> {\n if (isSvgUrl(url)) return loadSvg(url);\n return decode(url, url, !/^(data|blob):/i.test(url));\n}\n\nasync function loadSvg(url: string): Promise<HTMLImageElement> {\n let markup: string;\n try {\n const response = await fetch(url);\n if (!response.ok) throw new Error(`HTTP ${response.status}`);\n markup = sizeSvg(await response.text(), SVG_EDGE);\n } catch (e) {\n throw new ImageLoadError(url, e);\n }\n const sized = URL.createObjectURL(new Blob([markup], { type: SVG_TYPE }));\n try {\n return await decode(sized, url, false);\n } finally {\n URL.revokeObjectURL(sized);\n }\n}\n\nasync function decode(src: string, url: string, cors: boolean): Promise<HTMLImageElement> {\n const image = new Image();\n if (cors) image.crossOrigin = \"anonymous\";\n image.src = src;\n try {\n await image.decode();\n } catch (e) {\n throw new ImageLoadError(url, e);\n }\n return image;\n}\n\n/** Images decoded by {@link preloadImages}, by URL, for every context on the page. */\nconst preloaded = new Map<string, Promise<HTMLImageElement>>();\nconst decoded = new Map<string, HTMLImageElement>();\n\n/**\n * Loads and decodes images ahead of time, for every WebGL context on the\n * page: a texture cache that meets one of them afterwards (in a\n * `ShaderRenderer`, `ShaderFrames` or thumbnail) uploads it at once, so even\n * its first frame shows the image. Otherwise an image arrives a moment after\n * the first frame, which is black where it goes. Use it before drawing frames\n * that cannot wait, such as a video player asking a new `ShaderFrames` for\n * frame _n_.\n *\n * Each URL loads once, however often it is preloaded. Rejects with the first\n * {@link ImageLoadError}; the other images still load. Preloaded images stay\n * in memory until {@link releaseImages}.\n */\nexport async function preloadImages(urls: Iterable<string>): Promise<void> {\n const loads = [...new Set(urls)].filter(Boolean).map((url) => {\n let load = preloaded.get(url);\n if (!load) {\n load = loadImage(url).then(\n (image) => {\n if (preloaded.get(url) === load) decoded.set(url, image);\n return image;\n },\n (e: unknown) => {\n if (preloaded.get(url) === load) preloaded.delete(url);\n throw e;\n },\n );\n preloaded.set(url, load);\n }\n return load;\n });\n const results = await Promise.allSettled(loads);\n const failed = results.find((r) => r.status === \"rejected\");\n if (failed) throw failed.reason;\n}\n\n/** Forgets preloaded images (all of them without `urls`). Textures already made from them stay. */\nexport function releaseImages(urls?: Iterable<string>) {\n for (const url of urls ?? [...preloaded.keys()]) {\n preloaded.delete(url);\n decoded.delete(url);\n }\n}\n\n/** Something that hands `drawFrame` a texture for an image URL. */\nexport interface TextureSource {\n /** The texture for `url`: the image once loaded, a 1 × 1 opaque black placeholder until then. */\n texture(url: string): WebGLTexture | null;\n /**\n * The texture of the live source set for the image control `name` (see\n * {@link TextureCache.setLive}), with its current frame; undefined when\n * the control has none, so its URL is drawn instead.\n */\n liveTexture?(name: string): WebGLTexture | null | undefined;\n}\n\n/**\n * A picture that changes while it is shown, for an image control: a playing\n * `<video>`, or a canvas something else draws on. Videos re-upload when\n * their frame changes; canvases on every frame drawn.\n */\nexport type LiveSource = HTMLVideoElement | HTMLCanvasElement | OffscreenCanvas;\n\ninterface Live {\n source: LiveSource;\n texture: WebGLTexture | null;\n /** The video time last uploaded (NaN before the first upload). */\n uploaded: number;\n /** Set once the source could not be uploaded: it shows black, and is not tried again. */\n error: ImageLoadError | null;\n}\n\n/** Whether `source` is a video element (without touching `HTMLVideoElement`, which tests and workers lack). */\nfunction isVideo(source: LiveSource): source is HTMLVideoElement {\n return \"readyState\" in source && \"currentTime\" in source;\n}\n\n/** `HAVE_CURRENT_DATA`: a video has a frame to show. */\nconst HAVE_CURRENT_DATA = 2;\n\n/** A name for a live source in messages: its video URL, or the control's name. */\nfunction liveName(name: string, source: LiveSource): string {\n return (isVideo(source) && (source.currentSrc || source.src)) || name;\n}\n\n/**\n * Seeks a video to `time` seconds and settles once the frame there can be\n * drawn, for stepping a video in time with `ShaderFrames`: `await\n * seekVideo(video, t); frames.render(t)`. Pause the video first, or it\n * moves on. The time is clamped to the video's length.\n */\nexport async function seekVideo(video: HTMLVideoElement, time: number): Promise<void> {\n if (video.readyState < 1) {\n await new Promise<void>((resolve, reject) => {\n video.addEventListener(\"loadedmetadata\", () => resolve(), { once: true });\n video.addEventListener(\"error\", () => reject(new Error(`Could not load the video ${shortUrl(video.currentSrc || video.src)}.`)), { once: true });\n });\n }\n const target = Math.max(0, Math.min(time, Number.isFinite(video.duration) ? video.duration : time));\n if (Math.abs(video.currentTime - target) < 1e-6 && video.readyState >= HAVE_CURRENT_DATA && !video.seeking) return;\n await new Promise<void>((resolve) => {\n video.addEventListener(\"seeked\", () => resolve(), { once: true });\n video.currentTime = target;\n });\n}\n\ninterface Entry {\n texture: WebGLTexture | null;\n /** Settles when the image is uploaded or has failed. */\n loaded: Promise<void>;\n error: ImageLoadError | null;\n}\n\n/**\n * The textures of one WebGL context, by image URL. A URL starts loading the\n * first time it is asked for; until it arrives the placeholder is drawn, and\n * `onLoad` hears when it does (or fails) so the owner can redraw. An image\n * already decoded by {@link preloadImages} is uploaded at once instead. Images are\n * uploaded upright (`fragCoord / iResolution` samples them the right way up)\n * with mipmaps.\n */\nexport class TextureCache implements TextureSource {\n private readonly gl: WebGL2RenderingContext;\n private readonly entries = new Map<string, Entry>();\n private readonly live = new Map<string, Live>();\n private readonly onLoad: (url: string, error: ImageLoadError | null) => void;\n private readonly load: (url: string) => Promise<TexImageSource>;\n private placeholder: WebGLTexture | null = null;\n private disposed = false;\n\n constructor(\n gl: WebGL2RenderingContext,\n onLoad: (url: string, error: ImageLoadError | null) => void = () => {},\n load: (url: string) => Promise<TexImageSource> = loadImage,\n ) {\n this.gl = gl;\n this.onLoad = onLoad;\n this.load = load;\n }\n\n texture(url: string): WebGLTexture | null {\n return (url && this.entry(url).texture) || this.blank();\n }\n\n /** Starts loading `url` if it is new. */\n request(url: string) {\n if (url) this.entry(url);\n }\n\n private entry(url: string): Entry {\n const existing = this.entries.get(url);\n if (existing) return existing;\n const created: Entry = { texture: null, loaded: Promise.resolve(), error: null };\n const ready = decoded.get(url);\n if (ready && !this.gl.isContextLost()) {\n created.texture = this.upload(ready);\n this.entries.set(url, created);\n return created;\n }\n created.loaded = this.load(url).then(\n (image) => {\n if (this.entries.get(url) !== created || this.disposed || this.gl.isContextLost()) return;\n created.texture = this.upload(image);\n this.onLoad(url, null);\n },\n (e: unknown) => {\n if (this.entries.get(url) !== created || this.disposed) return;\n created.error = e instanceof ImageLoadError ? e : new ImageLoadError(url, e);\n this.onLoad(url, created.error);\n },\n );\n this.entries.set(url, created);\n return created;\n }\n\n /**\n * Shows `source` in the image control `name` instead of its URL, frame by\n * frame (`null` goes back to the URL). Its texture is uploaded as it is\n * drawn: a video's whenever it has moved to another frame, a canvas's on\n * every frame. Until a video has a frame it shows black. A video from\n * another site needs CORS (`crossOrigin = \"anonymous\"` and the site's\n * consent); without it the upload fails, `onLoad` hears an\n * {@link ImageLoadError}, and the control shows black.\n */\n setLive(name: string, source: LiveSource | null) {\n const existing = this.live.get(name);\n if (existing?.source === source) return;\n if (existing?.texture) this.gl.deleteTexture(existing.texture);\n this.live.delete(name);\n if (source) this.live.set(name, { source, texture: null, uploaded: NaN, error: null });\n }\n\n liveTexture(name: string): WebGLTexture | null | undefined {\n const live = this.live.get(name);\n if (!live) return undefined;\n if (live.error || this.gl.isContextLost()) return this.blank();\n const { source } = live;\n if (isVideo(source)) {\n if (source.readyState < HAVE_CURRENT_DATA) return live.texture ?? this.blank();\n if (live.texture && live.uploaded === source.currentTime) return live.texture;\n live.uploaded = source.currentTime;\n }\n const gl = this.gl;\n live.texture ??= gl.createTexture();\n gl.bindTexture(gl.TEXTURE_2D, live.texture);\n gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);\n try {\n gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, source);\n } catch (e) {\n const url = liveName(name, source);\n live.error = new ImageLoadError(\n url,\n e,\n `Could not draw the video at ${shortUrl(url)}. If it is on another site, set crossOrigin = \"anonymous\" on the <video> and make sure that site allows cross-origin use (CORS).`,\n );\n this.onLoad(url, live.error);\n return this.blank();\n } finally {\n gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);\n }\n gl.generateMipmap(gl.TEXTURE_2D);\n return live.texture;\n }\n\n /**\n * Whether a live video has moved to a frame not yet drawn (or has its\n * first frame ready), so a paused preview knows to draw again.\n */\n liveChanged(): boolean {\n for (const live of this.live.values()) {\n const { source } = live;\n if (live.error || !isVideo(source) || source.readyState < HAVE_CURRENT_DATA) continue;\n if (!live.texture || live.uploaded !== source.currentTime) return true;\n }\n return false;\n }\n\n /**\n * Settles once every live video has a frame to show. Rejects with an\n * {@link ImageLoadError} when one fails to load.\n */\n async liveReady(): Promise<void> {\n await Promise.all(\n [...this.live.entries()].map(([name, { source }]) => {\n if (!isVideo(source) || source.readyState >= HAVE_CURRENT_DATA) return undefined;\n return new Promise<void>((resolve, reject) => {\n source.addEventListener(\"loadeddata\", () => resolve(), { once: true });\n source.addEventListener(\n \"error\",\n () => reject(new ImageLoadError(liveName(name, source), source.error, `Could not load the video at ${shortUrl(liveName(name, source))}.`)),\n { once: true },\n );\n });\n }),\n );\n }\n\n /** The error for `url`, if it failed to load. */\n error(url: string): ImageLoadError | null {\n return this.entries.get(url)?.error ?? null;\n }\n\n /**\n * Settles once every one of `urls` has loaded or failed (starting any that\n * are new). Rejects with the first {@link ImageLoadError}.\n */\n async ready(urls: Iterable<string>): Promise<void> {\n const entries = [...urls].filter(Boolean).map((url) => this.entry(url));\n await Promise.all(entries.map((e) => e.loaded));\n const failed = entries.find((e) => e.error);\n if (failed) throw failed.error;\n }\n\n /** Deletes the textures of every URL not in `urls`; a later request loads them again. */\n retain(urls: Iterable<string>) {\n const keep = new Set(urls);\n for (const [url, entry] of this.entries) {\n if (keep.has(url)) continue;\n if (entry.texture) this.gl.deleteTexture(entry.texture);\n this.entries.delete(url);\n }\n }\n\n /** Deletes every texture; loads still in flight are dropped. */\n dispose() {\n this.retain([]);\n for (const name of [...this.live.keys()]) this.setLive(name, null);\n if (this.placeholder) this.gl.deleteTexture(this.placeholder);\n this.placeholder = null;\n this.disposed = true;\n }\n\n private blank(): WebGLTexture | null {\n if (this.placeholder) return this.placeholder;\n const gl = this.gl;\n this.placeholder = gl.createTexture();\n gl.bindTexture(gl.TEXTURE_2D, this.placeholder);\n const black = new Uint8Array([0, 0, 0, 255]);\n gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, black);\n return this.placeholder;\n }\n\n private upload(image: TexImageSource): WebGLTexture | null {\n const gl = this.gl;\n const texture = gl.createTexture();\n gl.bindTexture(gl.TEXTURE_2D, texture);\n gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);\n gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);\n gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);\n gl.generateMipmap(gl.TEXTURE_2D);\n return texture;\n }\n}\n\n/** Sets the bound `TEXTURE_2D`'s filter and wrap. */\nexport function applyTextureSettings(gl: WebGL2RenderingContext, settings: TextureSettings) {\n const min = { mipmap: gl.LINEAR_MIPMAP_LINEAR, linear: gl.LINEAR, nearest: gl.NEAREST }[\n settings.filter\n ];\n const wrap = { repeat: gl.REPEAT, clamp: gl.CLAMP_TO_EDGE, mirror: gl.MIRRORED_REPEAT }[\n settings.wrap\n ];\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, min);\n gl.texParameteri(\n gl.TEXTURE_2D,\n gl.TEXTURE_MAG_FILTER,\n settings.filter === \"nearest\" ? gl.NEAREST : gl.LINEAR,\n );\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, wrap);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, wrap);\n}\n","/**\n * The pointer as shaders see it, beyond Shadertoy's `iMouse`: where it is\n * while it is over the canvas, how hovered the canvas is (eased in and out),\n * how long it has been pressed, and the last few clicks, so effects can\n * answer hovers, presses and taps. Times are in `iTime` seconds, so a\n * recording can script them (see `ShaderFrames`' `pointer` and `clicks`).\n */\n\n/** How many clicks `iClicks` holds; older ones drop off. */\nexport const MAX_CLICKS = 8;\n\n/** How long hovering eases in or out, in seconds. */\nexport const HOVER_SECONDS = 0.25;\n\n/** A click: where (pixels from the bottom left, like `iMouse`) and when (`iTime` seconds). */\nexport interface ShaderClick {\n x: number;\n y: number;\n time: number;\n}\n\nconst smooth = (t: number) => (t <= 0 ? 0 : t >= 1 ? 1 : t * t * (3 - 2 * t));\n\n/**\n * `iClicks` from a list of clicks, newest first: `vec4(x, y, time, 1)` for\n * each click at or before `time`, zeros for the rest.\n */\nexport function clicksUniform(clicks: readonly ShaderClick[], time: number): Float32Array {\n const out = new Float32Array(MAX_CLICKS * 4);\n let i = 0;\n for (let k = clicks.length - 1; k >= 0 && i < MAX_CLICKS; k--) {\n const c = clicks[k]!;\n if (c.time > time) continue;\n out.set([c.x, c.y, c.time, 1], i * 4);\n i++;\n }\n return out;\n}\n\n/**\n * Follows one canvas's pointer for `iPointer` and `iClicks`: feed it pointer\n * events with the `iTime` they happen at, and read the uniforms for a frame.\n */\nexport class PointerTracker {\n private x = 0;\n private y = 0;\n private over = false;\n private hoverFrom = 0;\n private changedAt = -Infinity;\n private pressedAt: number | null = null;\n private releasedAt: number | null = null;\n private clicks: ShaderClick[] = [];\n\n /** The pointer moved to (x, y), in pixels from the bottom left. */\n move(x: number, y: number) {\n this.x = x;\n this.y = y;\n }\n\n /** The pointer came over the canvas (`true`) or left it. */\n hover(over: boolean, time: number) {\n if (over === this.over) return;\n this.hoverFrom = this.hovered(time);\n this.changedAt = time;\n this.over = over;\n }\n\n /** A press at (x, y): counts as a click. */\n down(x: number, y: number, time: number) {\n this.move(x, y);\n this.pressedAt = time;\n this.clicks.push({ x, y, time });\n if (this.clicks.length > MAX_CLICKS) this.clicks.shift();\n }\n\n up(time: number) {\n if (this.pressedAt !== null) this.releasedAt = time;\n this.pressedAt = null;\n }\n\n /** Forgets clicks and presses (when time jumps back, say). */\n clear() {\n this.clicks = [];\n this.pressedAt = null;\n this.releasedAt = null;\n }\n\n /** How hovered the canvas is at `time`, 0–1, eased. */\n hovered(time: number): number {\n const t = smooth((time - this.changedAt) / HOVER_SECONDS);\n return this.hoverFrom + ((this.over ? 1 : 0) - this.hoverFrom) * t;\n }\n\n /**\n * `iPointer` at `time`: x, y, how hovered (0–1), and the press: seconds\n * held while pressed, minus the seconds since it was let go after, 0\n * before the first press.\n */\n pointer(time: number): [number, number, number, number] {\n let press = 0;\n if (this.pressedAt !== null) press = Math.max(0, time - this.pressedAt);\n else if (this.releasedAt !== null) press = -Math.max(1e-3, time - this.releasedAt);\n return [this.x, this.y, this.hovered(time), press];\n }\n\n /** `iClicks` at `time` (see {@link clicksUniform}). */\n clicksAt(time: number): Float32Array {\n return clicksUniform(this.clicks, time);\n }\n}\n","import { controlValue, type ControlDef, type ControlValue } from \"./controls.js\";\nimport { MAX_CLICKS } from \"./pointer.js\";\nimport { applyTextureSettings, type TextureSource } from \"./textures.js\";\n\n/**\n * Shaders are written Shadertoy-style: the source defines\n * `void mainImage(out vec4 fragColor, in vec2 fragCoord)` and may use the\n * built-in uniforms below. It is compiled as GLSL ES 3.00 (WebGL2) inside a\n * small wrapper; `#line 1` keeps compiler line numbers matching the source.\n */\nexport const BUILTIN_UNIFORMS = [\n { name: \"iResolution\", type: \"vec3\", doc: \"viewport size in pixels (z = 1)\" },\n { name: \"iTime\", type: \"float\", doc: \"seconds since the shader started\" },\n { name: \"iTimeDelta\", type: \"float\", doc: \"seconds since the previous frame\" },\n { name: \"iFrame\", type: \"int\", doc: \"frame number\" },\n {\n name: \"iMouse\",\n type: \"vec4\",\n doc: \"xy = pointer while pressed (pixels), zw = press position (negative once released)\",\n },\n {\n name: \"iPointer\",\n type: \"vec4\",\n doc: \"xy = pointer while over the canvas (pixels), z = how hovered (0–1, eased), w = seconds held while pressed, minus seconds since release after (0 before any press)\",\n },\n {\n name: \"iClicks\",\n type: \"vec4\",\n size: MAX_CLICKS,\n doc: \"the latest clicks, newest first: xy = where (pixels), z = when (iTime seconds), w = 1 for a click, 0 for an empty slot\",\n },\n] as const;\n\nconst PRELUDE = `#version 300 es\nprecision highp float;\nprecision highp int;\nuniform vec3 iResolution;\nuniform float iTime;\nuniform float iTimeDelta;\nuniform int iFrame;\nuniform vec4 iMouse;\nuniform vec4 iPointer;\nuniform vec4 iClicks[${MAX_CLICKS}];\nout vec4 flair_FragColor;\n`;\n\nconst EPILOGUE = `\nvoid main() {\n vec4 flair_color = vec4(0.0, 0.0, 0.0, 1.0);\n mainImage(flair_color, gl_FragCoord.xy);\n flair_FragColor = vec4(flair_color.rgb, clamp(flair_color.a, 0.0, 1.0));\n}\n`;\n\n/** A full-screen triangle from `gl_VertexID` alone: no buffers or attributes. */\nexport const VERTEX_SOURCE = `#version 300 es\nvoid main() {\n vec2 p = vec2(float((gl_VertexID << 1) & 2), float(gl_VertexID & 2));\n gl_Position = vec4(p * 2.0 - 1.0, 0.0, 1.0);\n}\n`;\n\n/** One compiler message, mapped back to the user's source. */\nexport interface CompileError {\n /** 1-based line in the user's source (0 when the driver gave none, or it is outside the source). */\n line: number;\n message: string;\n}\n\n/** A linked program and the uniform locations `drawFrame` sets. */\nexport interface CompiledShader {\n program: WebGLProgram;\n builtins: Record<string, WebGLUniformLocation | null>;\n uniforms: Map<string, WebGLUniformLocation | null>;\n}\n\nexport type CompileResult =\n { ok: true; shader: CompiledShader } | { ok: false; errors: CompileError[] };\n\n/** Thrown by the APIs that cannot carry on without a program (`ShaderFrames`, `recordGif`). */\nexport class ShaderCompileError extends Error {\n readonly errors: CompileError[];\n\n constructor(errors: CompileError[]) {\n super(formatCompileError(errors));\n this.name = \"ShaderCompileError\";\n this.errors = errors;\n }\n}\n\n/** \"The shader does not compile (line 3): 'x' : undeclared identifier\", from the first error. */\nexport function formatCompileError(errors: CompileError[]): string {\n const first = errors[0];\n return `The shader does not compile${first?.line ? ` (line ${first.line})` : \"\"}: ${first?.message ?? \"unknown error\"}`;\n}\n\n/** The complete GLSL ES 3.00 fragment shader for a `mainImage` source. */\nexport function wrapSource(source: string): string {\n // `#line 1` must follow the prelude; drop a user `#version` (the wrapper owns it).\n return `${PRELUDE}#line 1\\n${source.replace(/^\\s*#version[^\\n]*/m, \"\")}\\n${EPILOGUE}`;\n}\n\n/** Turns a driver log (\"ERROR: 0:12: 'foo' : undeclared identifier\") into line-numbered errors. */\nexport function parseCompileLog(log: string, lineCount: number): CompileError[] {\n const errors: CompileError[] = [];\n for (const raw of log.split(\"\\n\")) {\n const text = raw.trim();\n if (!text) continue;\n const m = /^(?:ERROR|WARNING):\\s*\\d+:(\\d+):\\s*(.*)$/.exec(text);\n if (m) {\n const line = Number(m[1]);\n errors.push({ line: line >= 1 && line <= lineCount ? line : 0, message: m[2]!.trim() });\n } else if (!/^\\W*$/.test(text)) {\n errors.push({ line: 0, message: text.replace(/^ERROR:\\s*/, \"\") });\n }\n }\n if (!errors.length) errors.push({ line: 0, message: \"The shader did not compile.\" });\n // Drivers often repeat the same error.\n const seen = new Set<string>();\n return errors.filter((e) => {\n const key = `${e.line}:${e.message}`;\n if (seen.has(key)) return false;\n seen.add(key);\n return true;\n });\n}\n\nlet vertexCache = new WeakMap<WebGL2RenderingContext, WebGLShader>();\n\nfunction vertexShader(gl: WebGL2RenderingContext): WebGLShader {\n let vs = vertexCache.get(gl);\n if (!vs) {\n vs = gl.createShader(gl.VERTEX_SHADER)!;\n gl.shaderSource(vs, VERTEX_SOURCE);\n gl.compileShader(vs);\n vertexCache.set(gl, vs);\n }\n return vs;\n}\n\n/** Forgets cached GL objects. Call it after a context loss, before compiling on the restored context. */\nexport function resetGlCaches() {\n vertexCache = new WeakMap();\n}\n\n/**\n * Compiles and links a `mainImage` source. Errors come back with lines in\n * the user's source, never thrown. `controls` (from `parseControls`) says\n * which uniform locations to look up.\n */\nexport function compileShader(\n gl: WebGL2RenderingContext,\n source: string,\n controls: ControlDef[],\n): CompileResult {\n const lineCount = source.split(\"\\n\").length;\n if (gl.isContextLost()) {\n return {\n ok: false,\n errors: [\n {\n line: 0,\n message:\n \"The graphics context was lost; reload the page if the preview does not come back.\",\n },\n ],\n };\n }\n if (!/\\bmainImage\\s*\\(/.test(source)) {\n return {\n ok: false,\n errors: [\n { line: 0, message: \"Define void mainImage(out vec4 fragColor, in vec2 fragCoord).\" },\n ],\n };\n }\n const fs = gl.createShader(gl.FRAGMENT_SHADER);\n if (!fs) {\n return {\n ok: false,\n errors: [{ line: 0, message: \"WebGL could not create a shader (the context may be lost).\" }],\n };\n }\n gl.shaderSource(fs, wrapSource(source));\n gl.compileShader(fs);\n if (!gl.getShaderParameter(fs, gl.COMPILE_STATUS)) {\n const errors = parseCompileLog(gl.getShaderInfoLog(fs) ?? \"\", lineCount);\n gl.deleteShader(fs);\n return { ok: false, errors };\n }\n const program = gl.createProgram()!;\n gl.attachShader(program, vertexShader(gl));\n gl.attachShader(program, fs);\n gl.linkProgram(program);\n gl.deleteShader(fs);\n if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {\n const errors = parseCompileLog(gl.getProgramInfoLog(program) ?? \"\", lineCount);\n gl.deleteProgram(program);\n return { ok: false, errors };\n }\n const builtins: CompiledShader[\"builtins\"] = {};\n for (const u of BUILTIN_UNIFORMS) builtins[u.name] = gl.getUniformLocation(program, u.name);\n const uniforms = new Map(\n controls.map((c) => [c.name, gl.getUniformLocation(program, c.name)] as const),\n );\n return { ok: true, shader: { program, builtins, uniforms } };\n}\n\nconst NO_CLICKS = new Float32Array(MAX_CLICKS * 4);\n\n/** What one frame shows: its size in pixels, its time and the pointer. */\nexport interface FrameState {\n width: number;\n height: number;\n /** `iTime`, in seconds. */\n time: number;\n /** `iTimeDelta`, in seconds. */\n delta: number;\n /** `iFrame`. */\n frame: number;\n /** `iMouse`, Shadertoy semantics. */\n mouse: [number, number, number, number];\n /** `iPointer` (default all zero: nobody is pointing). */\n pointer?: readonly [number, number, number, number];\n /** `iClicks`: {@link MAX_CLICKS} × (x, y, time, 1), newest first (default none; see `clicksUniform`). */\n clicks?: Float32Array;\n}\n\n/**\n * Draws one frame of a compiled shader into the bound framebuffer, from (0, 0).\n * Image controls are bound to texture units 0, 1, … in source order, with\n * textures from `textures` (a {@link TextureCache} for the same context): a\n * control's live source when it has one, else its URL's image. Without\n * `textures` they sample nothing.\n */\nexport function drawFrame(\n gl: WebGL2RenderingContext,\n shader: CompiledShader,\n controls: ControlDef[],\n values: Record<string, ControlValue> | undefined,\n state: FrameState,\n textures?: TextureSource,\n) {\n gl.viewport(0, 0, state.width, state.height);\n gl.useProgram(shader.program);\n const b = shader.builtins;\n if (b.iResolution) gl.uniform3f(b.iResolution, state.width, state.height, 1);\n if (b.iTime) gl.uniform1f(b.iTime, state.time);\n if (b.iTimeDelta) gl.uniform1f(b.iTimeDelta, state.delta);\n if (b.iFrame) gl.uniform1i(b.iFrame, state.frame);\n if (b.iMouse) gl.uniform4f(b.iMouse, ...state.mouse);\n if (b.iPointer) gl.uniform4f(b.iPointer, ...(state.pointer ?? [0, 0, 0, 0]));\n if (b.iClicks) gl.uniform4fv(b.iClicks, state.clicks ?? NO_CLICKS);\n let unit = 0;\n for (const control of controls) {\n const loc = shader.uniforms.get(control.name);\n if (!loc) continue;\n const v = controlValue(control, values);\n switch (control.type) {\n case \"float\":\n gl.uniform1f(loc, v as number);\n break;\n case \"int\":\n gl.uniform1i(loc, Math.round(v as number));\n break;\n case \"bool\":\n gl.uniform1i(loc, v ? 1 : 0);\n break;\n case \"vec2\":\n gl.uniform2fv(loc, v as number[]);\n break;\n case \"vec3\":\n gl.uniform3fv(loc, v as number[]);\n break;\n case \"vec4\":\n gl.uniform4fv(loc, v as number[]);\n break;\n case \"sampler2D\": {\n gl.activeTexture(gl.TEXTURE0 + unit);\n const live = textures?.liveTexture?.(control.name);\n const texture = (live === undefined ? textures?.texture(v as string) : live) ?? null;\n gl.bindTexture(gl.TEXTURE_2D, texture);\n if (texture && control.texture) applyTextureSettings(gl, control.texture);\n gl.uniform1i(loc, unit++);\n break;\n }\n }\n }\n gl.drawArrays(gl.TRIANGLES, 0, 3);\n}\n\n/**\n * A WebGL2 context set up for full-screen fragment shaders: opaque by default,\n * no depth, stencil or antialiasing. `preserveDrawingBuffer` keeps the last\n * frame readable after the task that drew it (for captures). `alpha` enables a\n * transparent drawing buffer for overlay shaders. Null without WebGL2.\n */\nexport function createContext(\n canvas: HTMLCanvasElement | OffscreenCanvas,\n preserveDrawingBuffer = false,\n alpha = false,\n): WebGL2RenderingContext | null {\n return canvas.getContext(\"webgl2\", {\n alpha,\n antialias: false,\n depth: false,\n stencil: false,\n premultipliedAlpha: false,\n preserveDrawingBuffer,\n powerPreference: \"high-performance\",\n }) as WebGL2RenderingContext | null;\n}\n","/**\n * Perceptual Color Science & Film Grading Engine.\n *\n * Implements:\n * - Oklab & Oklch: Perceptually uniform color spaces that eliminate hue shifts and gray dead zones.\n * - Standard Color Spaces: sRGB, Linear sRGB, Rec.709, Display P3, and ACEScg.\n * - Film Color Grading: ASC CDL (Slope, Offset, Power), Lift/Gamma/Gain, Vibrance (skin tone protection),\n * and White Balance (Kelvin color temperature & tint).\n * - GLSL code generators for real-time WebGL fragment shader execution.\n */\n\nexport type ColorRGB = [number, number, number];\nexport type ColorRGBA = [number, number, number, number];\n\n// -----------------------------------------------------------------------------\n// sRGB <-> Linear Transfer Curves\n// -----------------------------------------------------------------------------\n\nexport function srgbToLinear(c: number): number {\n const v = Math.max(0, c);\n return v <= 0.04045 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);\n}\n\nexport function linearToSrgb(c: number): number {\n const v = Math.max(0, c);\n return v <= 0.0031308 ? v * 12.92 : 1.055 * Math.pow(v, 1.0 / 2.4) - 0.055;\n}\n\nexport function srgbToLinearRgb(rgb: ColorRGB): ColorRGB {\n return [srgbToLinear(rgb[0]), srgbToLinear(rgb[1]), srgbToLinear(rgb[2])];\n}\n\nexport function linearRgbToSrgb(rgb: ColorRGB): ColorRGB {\n return [linearToSrgb(rgb[0]), linearToSrgb(rgb[1]), linearToSrgb(rgb[2])];\n}\n\n// -----------------------------------------------------------------------------\n// Oklab & Oklch Perceptual Color Spaces (Björn Ottosson, 2020)\n// -----------------------------------------------------------------------------\n\nexport function linearSrgbToOklab(rgb: ColorRGB): [number, number, number] {\n const [r, g, b] = rgb;\n\n // Linear sRGB to LMS cone response\n const l = Math.cbrt(0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b);\n const m = Math.cbrt(0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b);\n const s = Math.cbrt(0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b);\n\n // LMS to Oklab (L: lightness [0..1], a: green-red, b: blue-yellow)\n const L = 0.2104542553 * l + 0.793617785 * m - 0.0040720468 * s;\n const a = 1.9779984951 * l - 2.428592205 * m + 0.4505937099 * s;\n const bVal = 0.0259040371 * l + 0.7827717662 * m - 0.808675766 * s;\n\n return [L, a, bVal];\n}\n\nexport function oklabToLinearSrgb(lab: [number, number, number]): ColorRGB {\n const [L, a, b] = lab;\n\n const l_ = L + 0.3963377774 * a + 0.2158037573 * b;\n const m_ = L - 0.1055613458 * a - 0.0638541728 * b;\n const s_ = L - 0.0894841775 * a - 1.291485548 * b;\n\n const l = l_ * l_ * l_;\n const m = m_ * m_ * m_;\n const s = s_ * s_ * s_;\n\n const r = +4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s;\n const g = -1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s;\n const bVal = -0.0041960863 * l - 0.7034186147 * m + 1.707614701 * s;\n\n return [r, g, bVal];\n}\n\nexport function srgbToOklab(rgb: ColorRGB): [number, number, number] {\n return linearSrgbToOklab(srgbToLinearRgb(rgb));\n}\n\nexport function oklabToSrgb(lab: [number, number, number]): ColorRGB {\n const linear = oklabToLinearSrgb(lab);\n return linearRgbToSrgb(linear);\n}\n\nexport function oklabToOklch(lab: [number, number, number]): [number, number, number] {\n const [L, a, b] = lab;\n const C = Math.sqrt(a * a + b * b);\n let h = Math.atan2(b, a);\n if (h < 0) h += 2 * Math.PI;\n return [L, C, h];\n}\n\nexport function oklchToOklab(lch: [number, number, number]): [number, number, number] {\n const [L, C, h] = lch;\n return [L, C * Math.cos(h), C * Math.sin(h)];\n}\n\n/**\n * Perceptually uniform color interpolation using Oklab space.\n * Prevents intermediate grayish dead spots.\n */\nexport function interpolateOklab(c1: ColorRGB, c2: ColorRGB, t: number): ColorRGB {\n const lab1 = srgbToOklab(c1);\n const lab2 = srgbToOklab(c2);\n const clampedT = Math.max(0, Math.min(1, t));\n\n const lab: [number, number, number] = [\n lab1[0] + (lab2[0] - lab1[0]) * clampedT,\n lab1[1] + (lab2[1] - lab1[1]) * clampedT,\n lab1[2] + (lab2[2] - lab1[2]) * clampedT,\n ];\n\n const res = oklabToSrgb(lab);\n return [\n Math.max(0, Math.min(1, res[0])),\n Math.max(0, Math.min(1, res[1])),\n Math.max(0, Math.min(1, res[2])),\n ];\n}\n\n/**\n * Perceptually uniform color interpolation using Oklch cylindrical space.\n * Interpolates along the shortest hue arc for vibrant gradients.\n */\nexport function interpolateOklch(c1: ColorRGB, c2: ColorRGB, t: number): ColorRGB {\n const lch1 = oklabToOklch(srgbToOklab(c1));\n const lch2 = oklabToOklch(srgbToOklab(c2));\n const clampedT = Math.max(0, Math.min(1, t));\n\n const L = lch1[0] + (lch2[0] - lch1[0]) * clampedT;\n const C = lch1[1] + (lch2[1] - lch1[1]) * clampedT;\n\n // Shortest arc hue interpolation\n let dHue = lch2[2] - lch1[2];\n if (dHue > Math.PI) dHue -= 2 * Math.PI;\n if (dHue < -Math.PI) dHue += 2 * Math.PI;\n let h = lch1[2] + dHue * clampedT;\n if (h < 0) h += 2 * Math.PI;\n if (h >= 2 * Math.PI) h -= 2 * Math.PI;\n\n const res = oklabToSrgb(oklchToOklab([L, C, h]));\n return [\n Math.max(0, Math.min(1, res[0])),\n Math.max(0, Math.min(1, res[1])),\n Math.max(0, Math.min(1, res[2])),\n ];\n}\n\n// -----------------------------------------------------------------------------\n// Color Space Transformations (sRGB, Rec.709, Display P3, ACEScg)\n// -----------------------------------------------------------------------------\n\nexport type ColorSpace = 'srgb' | 'linear-srgb' | 'rec709' | 'display-p3' | 'acescg';\n\ntype Matrix3x3 = [\n number, number, number,\n number, number, number,\n number, number, number\n];\n\nfunction mulMat3(m: Matrix3x3, v: ColorRGB): ColorRGB {\n return [\n m[0] * v[0] + m[1] * v[1] + m[2] * v[2],\n m[3] * v[0] + m[4] * v[1] + m[5] * v[2],\n m[6] * v[0] + m[7] * v[1] + m[8] * v[2],\n ];\n}\n\n// Linear sRGB to Display P3 (linear)\nconst MAT_LINEAR_SRGB_TO_P3: Matrix3x3 = [\n 0.822462, 0.177538, 0.000000,\n 0.033194, 0.966806, 0.000000,\n 0.017083, 0.072397, 0.910520,\n];\n\n// Display P3 (linear) to Linear sRGB\nconst MAT_P3_TO_LINEAR_SRGB: Matrix3x3 = [\n 1.224940, -0.224940, 0.000000,\n -0.042057, 1.042057, 0.000000,\n -0.019638, -0.078636, 1.098274,\n];\n\n// Linear sRGB to ACEScg (AP1 primaries with Bradford adaptation to ACES white)\nconst MAT_LINEAR_SRGB_TO_ACESCG: Matrix3x3 = [\n 0.613097, 0.339523, 0.047379,\n 0.070194, 0.916354, 0.013452,\n 0.020616, 0.109570, 0.869815,\n];\n\n// ACEScg to Linear sRGB\nconst MAT_ACESCG_TO_LINEAR_SRGB: Matrix3x3 = [\n 1.704859, -0.621714, -0.083299,\n -0.130078, 1.140735, -0.010560,\n -0.023964, -0.128975, 1.153014,\n];\n\n/**\n * Converts a color between standard imaging color spaces.\n */\nexport function convertColorSpace(color: ColorRGB, from: ColorSpace, to: ColorSpace): ColorRGB {\n if (from === to) return [...color];\n\n // First convert `from` to Linear sRGB\n let lin: ColorRGB;\n switch (from) {\n case 'srgb':\n lin = srgbToLinearRgb(color);\n break;\n case 'linear-srgb':\n lin = [...color];\n break;\n case 'rec709':\n // BT.709 gamma 2.4 EOTF\n lin = [\n Math.pow(Math.max(0, color[0]), 2.4),\n Math.pow(Math.max(0, color[1]), 2.4),\n Math.pow(Math.max(0, color[2]), 2.4),\n ];\n break;\n case 'display-p3': {\n // P3 sRGB transfer to linear P3, then matrix to linear sRGB\n const p3Lin = srgbToLinearRgb(color);\n lin = mulMat3(MAT_P3_TO_LINEAR_SRGB, p3Lin);\n break;\n }\n case 'acescg':\n lin = mulMat3(MAT_ACESCG_TO_LINEAR_SRGB, color);\n break;\n }\n\n // Then convert from Linear sRGB to `to`\n switch (to) {\n case 'linear-srgb':\n return lin;\n case 'srgb':\n return linearRgbToSrgb(lin);\n case 'rec709':\n // BT.709 gamma 1/2.4\n return [\n Math.pow(Math.max(0, lin[0]), 1 / 2.4),\n Math.pow(Math.max(0, lin[1]), 1 / 2.4),\n Math.pow(Math.max(0, lin[2]), 1 / 2.4),\n ];\n case 'display-p3': {\n const p3Lin = mulMat3(MAT_LINEAR_SRGB_TO_P3, lin);\n return linearRgbToSrgb(p3Lin);\n }\n case 'acescg':\n return mulMat3(MAT_LINEAR_SRGB_TO_ACESCG, lin);\n }\n}\n\n// -----------------------------------------------------------------------------\n// Film Color Grading: ASC CDL, Lift/Gamma/Gain, Vibrance, White Balance\n// -----------------------------------------------------------------------------\n\nexport interface AscCdl {\n /** Multiplicative scaling (default [1, 1, 1]) */\n slope?: ColorRGB;\n /** Additive offset (default [0, 0, 0]) */\n offset?: ColorRGB;\n /** Exponential power (default [1, 1, 1]) */\n power?: ColorRGB;\n /** Master saturation (default 1.0) */\n saturation?: number;\n}\n\n/**\n * Standard ASC CDL (American Society of Cinematographers Color Decision List).\n * Formula: out = (in * slope + offset)^power, followed by Rec.709 luma-weighted saturation.\n */\nexport function applyAscCdl(color: ColorRGB, cdl: AscCdl): ColorRGB {\n const slope = cdl.slope ?? [1, 1, 1];\n const offset = cdl.offset ?? [0, 0, 0];\n const power = cdl.power ?? [1, 1, 1];\n const sat = cdl.saturation ?? 1.0;\n\n let r = Math.pow(Math.max(0, color[0] * slope[0] + offset[0]), power[0]);\n let g = Math.pow(Math.max(0, color[1] * slope[1] + offset[1]), power[1]);\n let b = Math.pow(Math.max(0, color[2] * slope[2] + offset[2]), power[2]);\n\n if (sat !== 1.0) {\n const luma = 0.2126 * r + 0.7152 * g + 0.0722 * b;\n r = luma + sat * (r - luma);\n g = luma + sat * (g - luma);\n b = luma + sat * (b - luma);\n }\n\n return [Math.max(0, r), Math.max(0, g), Math.max(0, b)];\n}\n\nexport interface LiftGammaGain {\n /** Shadows adjustment (default [0, 0, 0]) */\n lift?: ColorRGB;\n /** Midtones gamma adjustment (default [1, 1, 1]) */\n gamma?: ColorRGB;\n /** Highlights gain adjustment (default [1, 1, 1]) */\n gain?: ColorRGB;\n}\n\n/**\n * Three-way Lift / Gamma / Gain film color grading.\n * Formula: out = (gain * (in + lift * (1 - in)))^(1 / gamma)\n */\nexport function applyLiftGammaGain(color: ColorRGB, lgg: LiftGammaGain): ColorRGB {\n const lift = lgg.lift ?? [0, 0, 0];\n const gamma = lgg.gamma ?? [1, 1, 1];\n const gain = lgg.gain ?? [1, 1, 1];\n\n const processChannel = (val: number, l: number, gm: number, gn: number): number => {\n const lifted = val + l * (1.0 - val);\n const gained = Math.max(0, lifted * gn);\n const safeGamma = Math.max(1e-4, gm);\n return Math.pow(gained, 1.0 / safeGamma);\n };\n\n return [\n processChannel(color[0], lift[0], gamma[0], gain[0]),\n processChannel(color[1], lift[1], gamma[1], gain[1]),\n processChannel(color[2], lift[2], gamma[2], gain[2]),\n ];\n}\n\n/**\n * Vibrance adjustment with skin tone protection.\n * Selectively boosts muted colors while preventing already saturated colors and skin tones\n * from clipping.\n * @param amount Vibrance delta in [-1..1]\n */\nexport function applyVibrance(color: ColorRGB, amount: number): ColorRGB {\n if (Math.abs(amount) < 1e-4) return [...color];\n\n const [r, g, b] = color;\n const maxC = Math.max(r, g, b);\n const minC = Math.min(r, g, b);\n const sat = maxC > 1e-5 ? (maxC - minC) / maxC : 0;\n\n // Protect warm skin tones (hue around red/orange/yellow: r > g > b)\n let skinProtection = 1.0;\n if (r > g && g > b) {\n const skinFactor = (g - b) / (r - b + 1e-5);\n skinProtection = Math.max(0.2, 1.0 - skinFactor * 0.5);\n }\n\n // Weight inversely proportional to current saturation\n const boost = amount * (1.0 - sat) * skinProtection;\n const luma = 0.2126 * r + 0.7152 * g + 0.0722 * b;\n\n const newR = Math.max(0, Math.min(1, luma + (1 + boost) * (r - luma)));\n const newG = Math.max(0, Math.min(1, luma + (1 + boost) * (g - luma)));\n const newB = Math.max(0, Math.min(1, luma + (1 + boost) * (b - luma)));\n\n return [newR, newG, newB];\n}\n\n/**\n * Calculates RGB multiplier from Kelvin blackbody color temperature and green/magenta tint.\n * @param kelvin Temperature in Kelvin [2000..12000], D65 neutral is 6500K.\n * @param tint Green/magenta shift in [-100..100] (0 = neutral).\n */\nexport function getWhiteBalanceGains(kelvin: number, tint = 0): ColorRGB {\n const k = Math.max(2000, Math.min(12000, kelvin)) / 100.0;\n\n let r: number;\n let g: number;\n let b: number;\n\n if (k <= 66) {\n r = 255;\n g = 99.4708025861 * Math.log(k) - 161.1195681661;\n b = k <= 19 ? 0 : 138.5177312231 * Math.log(k - 10) - 305.0447927307;\n } else {\n r = 329.698727446 * Math.pow(k - 60, -0.1332047592);\n g = 288.1221695283 * Math.pow(k - 60, -0.0755148492);\n b = 255;\n }\n\n // Normalize D65 (6500K) to [1, 1, 1]\n const d65R = 255;\n const d65G = 250.7;\n const d65B = 255;\n\n let gainR = r / d65R;\n let gainG = g / d65G;\n let gainB = b / d65B;\n\n // Tint adjustment (green vs magenta)\n const tintFactor = tint / 100.0;\n gainG *= 1.0 - tintFactor * 0.15;\n gainR *= 1.0 + tintFactor * 0.05;\n gainB *= 1.0 + tintFactor * 0.05;\n\n return [gainR, gainG, gainB];\n}\n\n// -----------------------------------------------------------------------------\n// GLSL Shaders\n// -----------------------------------------------------------------------------\n\n/**\n * Self-contained GLSL functions for fast Oklab <-> Linear sRGB transforms.\n */\nexport function getOklabGLSL(): string {\n return `\n// Oklab <-> Linear sRGB (Björn Ottosson)\nvec3 flair_linear_to_oklab(vec3 c) {\n float l = pow(0.4122214708 * c.r + 0.5363325363 * c.g + 0.0514459929 * c.b, 1.0 / 3.0);\n float m = pow(0.2119034982 * c.r + 0.6806995451 * c.g + 0.1073969566 * c.b, 1.0 / 3.0);\n float s = pow(0.0883024619 * c.r + 0.2817188376 * c.g + 0.6299787005 * c.b, 1.0 / 3.0);\n return vec3(\n 0.2104542553 * l + 0.7936177850 * m - 0.0040720468 * s,\n 1.9779984951 * l - 2.4285922050 * m + 0.4505937099 * s,\n 0.0259040371 * l + 0.7827717662 * m - 0.8086757660 * s\n );\n}\n\nvec3 flair_oklab_to_linear(vec3 lab) {\n float l_ = lab.x + 0.3963377774 * lab.y + 0.2158037573 * lab.z;\n float m_ = lab.x - 0.1055613458 * lab.y - 0.0638541728 * lab.z;\n float s_ = lab.x - 0.0894841775 * lab.y - 1.2914855480 * lab.z;\n float l = l_ * l_ * l_;\n float m = m_ * m_ * m_;\n float s = s_ * s_ * s_;\n return vec3(\n +4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s,\n -1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s,\n -0.0041960863 * l - 0.7034186147 * m + 1.7076147010 * s\n );\n}\n`.trim();\n}\n\n/**\n * Self-contained GLSL functions for ASC CDL and Lift/Gamma/Gain grading.\n */\nexport function getColorGradingGLSL(): string {\n return `\n// ASC CDL Grading\nvec3 flair_cdl(vec3 color, vec3 slope, vec3 offset, vec3 power, float sat) {\n vec3 c = pow(max(color * slope + offset, vec3(0.0)), power);\n float luma = dot(c, vec3(0.2126, 0.7152, 0.0722));\n return mix(vec3(luma), c, sat);\n}\n\n// 3-Way Lift / Gamma / Gain Grading\nvec3 flair_lift_gamma_gain(vec3 color, vec3 lift, vec3 gamma, vec3 gain) {\n vec3 lifted = color + lift * (vec3(1.0) - color);\n vec3 gained = max(lifted * gain, vec3(0.0));\n return pow(gained, 1.0 / max(gamma, vec3(0.0001)));\n}\n`.trim();\n}\n"],"mappings":"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","names":[]}
@@ -21,7 +21,7 @@ import {
21
21
  drawFrame,
22
22
  resetGlCaches,
23
23
  wrapSource
24
- } from "./chunk-PMVJP5I4.js";
24
+ } from "./chunk-HA6FIMH2.js";
25
25
  import {
26
26
  controlValue,
27
27
  imageUrls,
@@ -35,6 +35,8 @@ var ShaderRenderer = class {
35
35
  alpha;
36
36
  shader = null;
37
37
  textures = null;
38
+ /** Live sources by image control, kept to set again on a restored context. */
39
+ live = /* @__PURE__ */ new Map();
38
40
  controls = [];
39
41
  values;
40
42
  source = "";
@@ -65,7 +67,7 @@ var ShaderRenderer = class {
65
67
  onStats;
66
68
  /** Called with `true` when the GL context is lost and `false` once it is restored and recompiled. */
67
69
  onContextLost;
68
- /** Called when an image control's URL cannot be loaded; the control shows black. */
70
+ /** Called when an image control's URL (or live video) cannot be loaded; the control shows black. */
69
71
  onImageError;
70
72
  /**
71
73
  * Creates a canvas in `target` (with `className` or options), or attaches to an existing
@@ -163,11 +165,31 @@ var ShaderRenderer = class {
163
165
  if (!this.playing) this.render();
164
166
  }
165
167
  /**
166
- * Settles once the images the current controls show have loaded. Rejects
167
- * with an {@link ImageLoadError} when one cannot be (it shows black).
168
+ * Shows a `<video>` (or a canvas) in the image control `name` (the
169
+ * uniform's name, such as `uImage`) in place of its URL, frame by frame;
170
+ * `null` goes back to the URL. The preview follows the video even while
171
+ * the shader's own clock is paused, and the video plays, seeks and loops
172
+ * on its own: the renderer never controls it. A video from another site
173
+ * needs `crossOrigin = "anonymous"` and CORS, or `onImageError` hears why
174
+ * it shows black. The value stays as it is, so documents, presets and
175
+ * agents keep seeing the URL.
176
+ */
177
+ setLiveSource(name, source) {
178
+ if (source) this.live.set(name, source);
179
+ else this.live.delete(name);
180
+ this.textures?.setLive(name, source);
181
+ if (!this.playing) this.render();
182
+ }
183
+ /**
184
+ * Settles once the images the current controls show have loaded, and
185
+ * every live video has a frame. Rejects with an {@link ImageLoadError}
186
+ * when one cannot be (it shows black).
168
187
  */
169
188
  async ready() {
170
- await this.textures?.ready(imageUrls(this.controls, this.values));
189
+ await Promise.all([
190
+ this.textures?.ready(imageUrls(this.controls, this.values)),
191
+ this.textures?.liveReady()
192
+ ]);
171
193
  }
172
194
  play() {
173
195
  this.playing = true;
@@ -267,10 +289,12 @@ var ShaderRenderer = class {
267
289
  );
268
290
  }
269
291
  textureCache(gl) {
270
- return new TextureCache(gl, (_url, error) => {
292
+ const cache = new TextureCache(gl, (_url, error) => {
271
293
  if (error) this.onImageError?.(error);
272
294
  else if (!this.playing) this.render();
273
295
  });
296
+ for (const [name, source] of this.live) cache.setLive(name, source);
297
+ return cache;
274
298
  }
275
299
  /** Starts loading the images now shown, and frees the ones no longer shown. */
276
300
  syncTextures() {
@@ -289,6 +313,8 @@ var ShaderRenderer = class {
289
313
  this.frame += 1;
290
314
  this.render(delta * this.speed);
291
315
  this.fpsFrames += 1;
316
+ } else if (this.textures?.liveChanged()) {
317
+ this.render();
292
318
  }
293
319
  if (now - this.fpsSince >= 500) {
294
320
  this.fps = this.playing ? Math.round(this.fpsFrames * 1e3 / (now - this.fpsSince || 1)) : 0;
@@ -634,11 +660,20 @@ var ShaderFrames = class {
634
660
  this.syncTextures();
635
661
  }
636
662
  /**
637
- * Settles once every image the controls show has loaded. Rejects with an
638
- * {@link ImageLoadError} when one cannot be.
663
+ * Draws a `<video>` (or a canvas) in the image control `name` instead of
664
+ * its URL, at the frame it is on when `render` runs; `null` goes back to
665
+ * the URL. See `ShaderRenderer.setLiveSource`.
666
+ */
667
+ setLiveSource(name, source) {
668
+ this.textures.setLive(name, source);
669
+ }
670
+ /**
671
+ * Settles once every image the controls show has loaded, and every live
672
+ * video has a frame. Rejects with an {@link ImageLoadError} when one
673
+ * cannot be.
639
674
  */
640
675
  async ready() {
641
- await this.textures.ready(imageUrls(this.controls, this.values));
676
+ await Promise.all([this.textures.ready(imageUrls(this.controls, this.values)), this.textures.liveReady()]);
642
677
  }
643
678
  /** Draws the frame at `time` seconds onto `canvas`. */
644
679
  render(time, options = {}) {
@@ -1617,4 +1652,4 @@ export {
1617
1652
  validateScene,
1618
1653
  standaloneSceneHtml
1619
1654
  };
1620
- //# sourceMappingURL=chunk-LKQM3GSQ.js.map
1655
+ //# sourceMappingURL=chunk-HP7CYHS5.js.map