@pixerate/flair 0.28.0 → 0.30.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 +19 -0
- package/README.md +54 -6
- package/dist/catalog/index.d.ts +11 -6
- package/dist/catalog/index.js +1 -1
- package/dist/{chunk-JL6TI6II.js → chunk-7KYQ4R76.js} +2 -2
- package/dist/chunk-7KYQ4R76.js.map +1 -0
- package/dist/{chunk-STKEKXCK.js → chunk-AUH3PEFW.js} +2 -2
- package/dist/{chunk-PFGGPIMT.js → chunk-DA4UUSDO.js} +58 -3
- package/dist/chunk-DA4UUSDO.js.map +1 -0
- package/dist/{chunk-RDU63CDO.js → chunk-HDYBY3PQ.js} +380 -1
- package/dist/chunk-HDYBY3PQ.js.map +1 -0
- package/dist/{chunk-GXP4YLAP.js → chunk-HSYDTVTD.js} +9 -9
- package/dist/chunk-HSYDTVTD.js.map +1 -0
- package/dist/{chunk-VLQLRKGW.js → chunk-MAYKC3RF.js} +260 -33
- package/dist/chunk-MAYKC3RF.js.map +1 -0
- package/dist/{chunk-H6VDFBIO.js → chunk-SMR2MJXB.js} +156 -5
- package/dist/chunk-SMR2MJXB.js.map +1 -0
- package/dist/{chunk-4LKAPRFM.js → chunk-UIODYJ6B.js} +2274 -176
- package/dist/chunk-UIODYJ6B.js.map +1 -0
- package/dist/{chunk-YBGRXVAE.js → chunk-X3C2VFTM.js} +3 -3
- package/dist/{chunk-CK6EV37I.js → chunk-ZULFHADW.js} +69 -6
- package/dist/chunk-ZULFHADW.js.map +1 -0
- package/dist/cli.js +6 -6
- package/dist/editor/index.d.ts +3 -3
- package/dist/editor/index.js +3 -3
- package/dist/{effect-GGP66JI6.js → effect-RGABT3BK.js} +10 -10
- package/dist/embed/index.d.ts +36 -8
- package/dist/embed/index.js +5 -5
- package/dist/{frames-VEcqa40g.d.ts → frames-CGJudAbu.d.ts} +11 -1
- package/dist/index.d.ts +69 -6
- package/dist/index.js +35 -7
- package/dist/index.js.map +1 -1
- package/dist/node.d.ts +4 -4
- package/dist/node.js +9 -9
- package/dist/react/index.d.ts +4 -4
- package/dist/react/index.js +7 -7
- package/dist/react/index.js.map +1 -1
- package/dist/{recorder-CDPPTJMG.js → recorder-TYMEZHOT.js} +3 -3
- package/dist/{renderer-teaa98RE.d.ts → renderer-tk1nbVkW.d.ts} +252 -2
- package/dist/svelte/index.d.ts +4 -4
- package/dist/svelte/index.js +7 -7
- package/dist/svelte/index.js.map +1 -1
- package/dist/{svg-BhRboN9q.d.ts → svg-DaKRPA9v.d.ts} +39 -7
- package/dist/{types-DUOMxZaq.d.ts → types-DuGVdJbt.d.ts} +2 -2
- package/dist/vector/index.d.ts +2 -2
- package/dist/vector/index.js +5 -3
- package/package.json +1 -1
- package/dist/chunk-4LKAPRFM.js.map +0 -1
- package/dist/chunk-CK6EV37I.js.map +0 -1
- package/dist/chunk-GXP4YLAP.js.map +0 -1
- package/dist/chunk-H6VDFBIO.js.map +0 -1
- package/dist/chunk-JL6TI6II.js.map +0 -1
- package/dist/chunk-PFGGPIMT.js.map +0 -1
- package/dist/chunk-RDU63CDO.js.map +0 -1
- package/dist/chunk-VLQLRKGW.js.map +0 -1
- /package/dist/{chunk-STKEKXCK.js.map → chunk-AUH3PEFW.js.map} +0 -0
- /package/dist/{chunk-YBGRXVAE.js.map → chunk-X3C2VFTM.js.map} +0 -0
- /package/dist/{effect-GGP66JI6.js.map → effect-RGABT3BK.js.map} +0 -0
- /package/dist/{recorder-CDPPTJMG.js.map → recorder-TYMEZHOT.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/shader/textures.ts","../src/shader/audio.ts","../src/shader/pointer.ts","../src/shader/program.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 * Sound as shaders see it: `iAudio` (level, bass, mid and treble, 0–1,\n * smoothed), `iSpectrum[16]` (64 log-spaced frequency bins, 0–1, four to a\n * `vec4`, low to high) and `iBeat` (seconds since the last beat on the bass,\n * -1 before the first).\n *\n * {@link AudioAnalysis} turns frequency data (an `AnalyserNode`'s, or an\n * FFT's) into those uniforms, deterministically for given data and times.\n * {@link LiveAudio} feeds it from a playing media element, a microphone or\n * any Web Audio node (what `ShaderRenderer.setAudioSource` uses), and\n * {@link analyseAudioBuffer} from a decoded `AudioBuffer` offline, frame-exact\n * for recordings.\n *\n * With no audio at all every audio uniform is zero, `iBeat` included, so an\n * audio-reactive effect can tell silence from \"nothing connected\" (where\n * `iBeat` is -1 or above 0) and play an idle demo instead.\n */\n\n/** How many frequency bins `iSpectrum` holds (16 `vec4`s). */\nexport const SPECTRUM_BINS = 64;\n\n/** One frame of the audio uniforms. */\nexport interface AudioFrame {\n /** `iAudio`: level (loudness, 0–1), bass, mid, treble (0–1), smoothed. */\n audio: readonly [number, number, number, number];\n /** `iSpectrum`: {@link SPECTRUM_BINS} values, 0–1, low to high (log-spaced). */\n spectrum: Float32Array;\n /** `iBeat`: seconds since the last beat, or -1 before the first. */\n beat: number;\n}\n\nexport interface AudioAnalysisOptions {\n /** The sample rate of the audio analysed, in Hz (default 48000). */\n sampleRate?: number;\n /** The FFT size the frequency data came from (default 2048: `fftSize / 2` values). */\n fftSize?: number;\n /** Decibels that read as 0 and 1 for float (dB) frequency data (default -100 and -30, as `AnalyserNode`). */\n minDecibels?: number;\n maxDecibels?: number;\n /** Seconds a value takes to rise most of the way to a louder reading (default 0.03). */\n attack?: number;\n /** Seconds a value takes to fall most of the way to a quieter one (default 0.25). */\n release?: number;\n /** The lowest and highest frequencies the spectrum covers, in Hz (default 30 and 16000). */\n minHz?: number;\n maxHz?: number;\n /**\n * How far the bass must jump over its recent average to count as a beat,\n * 0–1 (default 0.12). Lower finds more beats.\n */\n beatThreshold?: number;\n /** The least time between two beats, in seconds (default 0.22: about 270 beats a minute). */\n beatGap?: number;\n}\n\n/** The bands `iAudio` averages, in Hz. */\nexport const AUDIO_BANDS = { bass: [20, 250], mid: [250, 2000], treble: [2000, 16000] } as const;\n\nconst clamp01 = (x: number) => (x <= 0 ? 0 : x >= 1 ? 1 : x);\n\n/**\n * Turns frequency data into the audio uniforms: 64 log-spaced bins, band\n * averages, attack and release smoothing, a loudness level, and simple beat\n * detection (onsets on the bass band, over its recent average). Feed it\n * frequency data with the time it was read, in seconds, increasing; read\n * {@link AudioAnalysis.frame} for the uniforms. The same data at the same\n * times always gives the same uniforms.\n *\n * ```ts\n * const analysis = new AudioAnalysis({ sampleRate: ctx.sampleRate, fftSize: analyser.fftSize });\n * analyser.getFloatFrequencyData(freq);\n * analysis.update(freq, performance.now() / 1000);\n * const { audio, spectrum, beat } = analysis.frame(performance.now() / 1000);\n * ```\n */\nexport class AudioAnalysis {\n readonly sampleRate: number;\n readonly fftSize: number;\n private readonly minDb: number;\n private readonly maxDb: number;\n private readonly attack: number;\n private readonly release: number;\n private readonly beatThreshold: number;\n private readonly beatGap: number;\n /** Each spectrum bin's range of FFT bins, [from, to), fractional. */\n private readonly ranges: [number, number][];\n private readonly bands: [number, number][];\n private readonly raw = new Float32Array(SPECTRUM_BINS);\n private readonly smoothed = new Float32Array(SPECTRUM_BINS);\n private values: [number, number, number, number] = [0, 0, 0, 0];\n private last: number | undefined;\n private bassAverage = 0;\n private armed = true;\n private lastBeat: number | undefined;\n private normalised = new Float32Array(0);\n\n constructor(options: AudioAnalysisOptions = {}) {\n this.sampleRate = options.sampleRate ?? 48000;\n this.fftSize = options.fftSize ?? 2048;\n this.minDb = options.minDecibels ?? -100;\n this.maxDb = options.maxDecibels ?? -30;\n this.attack = options.attack ?? 0.03;\n this.release = options.release ?? 0.25;\n this.beatThreshold = options.beatThreshold ?? 0.12;\n this.beatGap = options.beatGap ?? 0.22;\n const binHz = this.sampleRate / this.fftSize;\n const minHz = options.minHz ?? 30;\n const maxHz = Math.min(options.maxHz ?? 16000, this.sampleRate / 2);\n this.ranges = Array.from({ length: SPECTRUM_BINS }, (_, i) => {\n const f0 = minHz * (maxHz / minHz) ** (i / SPECTRUM_BINS);\n const f1 = minHz * (maxHz / minHz) ** ((i + 1) / SPECTRUM_BINS);\n return [f0 / binHz, f1 / binHz];\n });\n this.bands = [AUDIO_BANDS.bass, AUDIO_BANDS.mid, AUDIO_BANDS.treble].map(([a, b]) => [a / binHz, Math.min(b, this.sampleRate / 2) / binHz]);\n }\n\n /** Forgets everything heard: the next update starts afresh. */\n reset() {\n this.smoothed.fill(0);\n this.values = [0, 0, 0, 0];\n this.last = undefined;\n this.bassAverage = 0;\n this.armed = true;\n this.lastBeat = undefined;\n }\n\n /**\n * Takes one reading at `time` seconds: `frequency` is `fftSize / 2`\n * values, bytes 0–255 (`getByteFrequencyData`) or decibels\n * (`getFloatFrequencyData`). `waveform` (time-domain samples, -1–1, or\n * bytes centred on 128) gives the level its RMS; without it the level\n * comes from the spectrum.\n */\n update(frequency: Uint8Array | Float32Array, time: number, waveform?: Uint8Array | Float32Array) {\n const n = frequency.length;\n if (this.normalised.length !== n) this.normalised = new Float32Array(n);\n const v = this.normalised;\n if (frequency instanceof Uint8Array) for (let i = 0; i < n; i++) v[i] = frequency[i]! / 255;\n else {\n const range = this.maxDb - this.minDb;\n for (let i = 0; i < n; i++) {\n const db = frequency[i]!;\n v[i] = Number.isFinite(db) ? clamp01((db - this.minDb) / range) : 0;\n }\n }\n // Log bins: the loudest FFT bin in each, or between the two nearest where a bin is narrower than one.\n for (let b = 0; b < SPECTRUM_BINS; b++) {\n const [k0, k1] = this.ranges[b]!;\n const from = Math.ceil(k0);\n const to = Math.min(n - 1, Math.floor(k1));\n let value = 0;\n if (to >= from) for (let k = from; k <= to; k++) value = Math.max(value, v[k]!);\n else {\n const k = Math.min(n - 1.001, (k0 + k1) / 2);\n const i = Math.floor(k);\n value = v[i]! + (v[Math.min(n - 1, i + 1)]! - v[i]!) * (k - i);\n }\n this.raw[b] = value;\n }\n const band = ([k0, k1]: [number, number]) => {\n const from = Math.max(0, Math.ceil(k0));\n const to = Math.min(n - 1, Math.floor(k1));\n let sum = 0;\n for (let k = from; k <= to; k++) sum += v[k]!;\n return to >= from ? sum / (to - from + 1) : 0;\n };\n const bass = band(this.bands[0]!);\n const mid = band(this.bands[1]!);\n const treble = band(this.bands[2]!);\n let level: number;\n if (waveform && waveform.length) {\n let sum = 0;\n const bytes = waveform instanceof Uint8Array;\n for (let i = 0; i < waveform.length; i++) {\n const s = bytes ? (waveform[i]! - 128) / 128 : waveform[i]!;\n sum += s * s;\n }\n level = rmsLevel(Math.sqrt(sum / waveform.length));\n } else {\n let sum = 0;\n for (let b = 0; b < SPECTRUM_BINS; b++) sum += this.raw[b]! * this.raw[b]!;\n level = Math.sqrt(sum / SPECTRUM_BINS);\n }\n\n const dt = this.last === undefined ? Infinity : Math.max(0, time - this.last);\n const rise = 1 - Math.exp(-dt / this.attack);\n const fall = 1 - Math.exp(-dt / this.release);\n const ease = (from: number, to: number) => from + (to - from) * (to > from ? rise : fall);\n for (let b = 0; b < SPECTRUM_BINS; b++) this.smoothed[b] = ease(this.smoothed[b]!, this.raw[b]!);\n const [l, bs, m, t] = this.values;\n this.values = [ease(l, level), ease(bs, bass), ease(m, mid), ease(t, treble)];\n\n // Beats: the bass jumping over its average of the last half second or so, once until it settles back.\n const onset = bass - this.bassAverage;\n const threshold = this.beatThreshold + 0.15 * this.bassAverage;\n if (this.last !== undefined && this.armed && onset > threshold && bass > 0.2 && (this.lastBeat === undefined || time - this.lastBeat >= this.beatGap)) {\n this.lastBeat = time;\n this.armed = false;\n } else if (onset < threshold * 0.5) this.armed = true;\n this.bassAverage = this.last === undefined ? bass : this.bassAverage + (bass - this.bassAverage) * (1 - Math.exp(-dt / 0.6));\n this.last = time;\n }\n\n /** The uniforms after the latest update, with `iBeat` measured to `time` (default the latest update's). */\n frame(time = this.last ?? 0): AudioFrame {\n return {\n audio: [...this.values],\n spectrum: Float32Array.from(this.smoothed),\n beat: this.lastBeat === undefined ? -1 : Math.max(0, time - this.lastBeat),\n };\n }\n}\n\n/** Loudness from an RMS amplitude: 0 at -60 dBFS (or below), 1 at full scale. */\nexport function rmsLevel(rms: number): number {\n return rms > 0 ? clamp01(1 + (20 * Math.log10(rms)) / 60) : 0;\n}\n\n// ==========================================\n// FFT\n// ==========================================\n\nconst fftTables = new Map<number, { reverse: Uint32Array; cos: Float64Array; sin: Float64Array }>();\n\n/**\n * An in-place radix-2 FFT of `re` and `im` (their length a power of two).\n * Forward, unscaled: `X[k] = Σ x[n] e^(-2πikn/N)`.\n */\nexport function fft(re: Float32Array | Float64Array, im: Float32Array | Float64Array) {\n const n = re.length;\n if (n !== im.length || n < 2 || (n & (n - 1)) !== 0) throw new RangeError(`An FFT needs two arrays of one power-of-two length (got ${re.length} and ${im.length}).`);\n let table = fftTables.get(n);\n if (!table) {\n const bits = Math.log2(n);\n const reverse = new Uint32Array(n);\n for (let i = 0; i < n; i++) {\n let r = 0;\n for (let b = 0; b < bits; b++) r = (r << 1) | ((i >>> b) & 1);\n reverse[i] = r;\n }\n const cos = new Float64Array(n / 2);\n const sin = new Float64Array(n / 2);\n for (let i = 0; i < n / 2; i++) {\n cos[i] = Math.cos((2 * Math.PI * i) / n);\n sin[i] = -Math.sin((2 * Math.PI * i) / n);\n }\n fftTables.set(n, (table = { reverse, cos, sin }));\n }\n const { reverse, cos, sin } = table;\n for (let i = 0; i < n; i++) {\n const j = reverse[i]!;\n if (j > i) {\n let t = re[i]!;\n re[i] = re[j]!;\n re[j] = t;\n t = im[i]!;\n im[i] = im[j]!;\n im[j] = t;\n }\n }\n for (let size = 2; size <= n; size *= 2) {\n const half = size / 2;\n const step = n / size;\n for (let start = 0; start < n; start += size) {\n for (let k = 0; k < half; k++) {\n const wr = cos[k * step]!;\n const wi = sin[k * step]!;\n const a = start + k;\n const b = a + half;\n const xr = re[b]! * wr - im[b]! * wi;\n const xi = re[b]! * wi + im[b]! * wr;\n re[b] = re[a]! - xr;\n im[b] = im[a]! - xi;\n re[a] = re[a]! + xr;\n im[a] = im[a]! + xi;\n }\n }\n }\n}\n\nconst windows = new Map<number, Float32Array>();\n\n/** The Blackman window `AnalyserNode` applies before its FFT. */\nfunction blackman(n: number): Float32Array {\n let w = windows.get(n);\n if (!w) {\n w = new Float32Array(n);\n for (let i = 0; i < n; i++) w[i] = 0.42 - 0.5 * Math.cos((2 * Math.PI * i) / n) + 0.08 * Math.cos((4 * Math.PI * i) / n);\n windows.set(n, w);\n }\n return w;\n}\n\n/**\n * The decibel spectrum of the `fftSize` samples of `samples` ending at\n * `end` (exclusive; samples before the start are silence), as\n * `AnalyserNode.getFloatFrequencyData` gives it with no smoothing: a\n * Blackman window, an FFT, magnitudes over `fftSize`, in dB. Writes\n * `fftSize / 2` values into `out`, and the window's samples into `waveform`\n * when given.\n */\nexport function frequencyData(samples: Float32Array, end: number, fftSize: number, out: Float32Array, waveform?: Float32Array) {\n const re = new Float64Array(fftSize);\n const im = new Float64Array(fftSize);\n const w = blackman(fftSize);\n const start = end - fftSize;\n for (let i = 0; i < fftSize; i++) {\n const at = start + i;\n const s = at >= 0 && at < samples.length ? samples[at]! : 0;\n if (waveform) waveform[i] = s;\n re[i] = s * w[i]!;\n }\n fft(re, im);\n for (let k = 0; k < fftSize / 2; k++) {\n const mag = Math.hypot(re[k]!, im[k]!) / fftSize;\n out[k] = mag > 0 ? 20 * Math.log10(mag) : -Infinity;\n }\n}\n\n/** What {@link analyseAudioBuffer} reads: an `AudioBuffer`, or anything shaped like one. */\nexport interface AudioSamples {\n sampleRate: number;\n length: number;\n numberOfChannels: number;\n getChannelData(channel: number): Float32Array;\n}\n\nexport interface AudioBufferAnalysisOptions extends Omit<AudioAnalysisOptions, \"sampleRate\"> {\n /** Readings per second (default 120). Recordings sample the reading at or just before each frame. */\n rate?: number;\n}\n\n/** The audio uniforms of a whole decoded track, at any time (see {@link analyseAudioBuffer}). */\nexport interface AudioBufferAnalysis {\n /** The track's length in seconds. */\n readonly duration: number;\n /** The uniforms at `time` seconds into the track (silence before 0 and after the end). */\n at(time: number): AudioFrame;\n}\n\n/**\n * Analyses a decoded track offline, so a recording of an audio-reactive\n * effect follows the sound frame for frame: the uniforms at time `t` come\n * from an FFT of the samples just before `t`, as a live `AnalyserNode`\n * would hear them, through an {@link AudioAnalysis} stepped at a fixed\n * `rate` from the start of the track. Readings are worked out as far as\n * asked and kept, so asking for frames in any order gives the same answer.\n *\n * ```ts\n * const track = analyseAudioBuffer(await ctx.decodeAudioData(bytes));\n * frames.render(t, { audio: track.at(t) });\n * ```\n */\nexport function analyseAudioBuffer(buffer: AudioSamples, options: AudioBufferAnalysisOptions = {}): AudioBufferAnalysis {\n const fftSize = options.fftSize ?? 2048;\n const rate = options.rate ?? 120;\n const { sampleRate, length } = buffer;\n // Mono: the channels' average.\n const mono = new Float32Array(length);\n for (let c = 0; c < buffer.numberOfChannels; c++) {\n const data = buffer.getChannelData(c);\n for (let i = 0; i < length; i++) mono[i]! += data[i]! / buffer.numberOfChannels;\n }\n const analysis = new AudioAnalysis({ ...options, sampleRate, fftSize });\n const freq = new Float32Array(fftSize / 2);\n const wave = new Float32Array(fftSize);\n /** Each reading: level, bass, mid, treble, the spectrum, and the last beat's time (NaN for none). */\n const stride = 4 + SPECTRUM_BINS + 1;\n let readings = new Float32Array(stride * 256);\n let count = 0;\n const step = () => {\n const time = count / rate;\n frequencyData(mono, Math.round(time * sampleRate), fftSize, freq, wave);\n analysis.update(freq, time, wave);\n const f = analysis.frame(time);\n if ((count + 1) * stride > readings.length) {\n const grown = new Float32Array(readings.length * 2);\n grown.set(readings);\n readings = grown;\n }\n const at = count * stride;\n readings.set(f.audio, at);\n readings.set(f.spectrum, at + 4);\n readings[at + stride - 1] = f.beat < 0 ? NaN : time - f.beat;\n count++;\n };\n return {\n duration: length / sampleRate,\n at(time) {\n const index = Math.max(0, Math.floor(time * rate + 1e-6));\n while (count <= index) step();\n const at = index * stride;\n const lastBeat = readings[at + stride - 1]!;\n return {\n audio: [readings[at]!, readings[at + 1]!, readings[at + 2]!, readings[at + 3]!],\n spectrum: readings.slice(at + 4, at + 4 + SPECTRUM_BINS),\n beat: Number.isNaN(lastBeat) || time < 0 ? -1 : Math.max(0, time - lastBeat),\n };\n },\n };\n}\n\n// ==========================================\n// Live audio (browser)\n// ==========================================\n\n/** What `ShaderRenderer.setAudioSource` listens to: a media element (it keeps playing to the speakers), a stream (a microphone: not played) or any Web Audio node. */\nexport type AudioSource = HTMLMediaElement | MediaStream | AudioNode;\n\nlet shared: AudioContext | undefined;\n\n/**\n * The `AudioContext` Flair listens with, made on first use and shared by\n * every renderer (browsers limit how many a page may have). Make nodes in it\n * to pass to `setAudioSource` (an oscillator, a decoded buffer). Browsers\n * start it suspended until the page has had a click or key press: Flair\n * resumes it on the first one, or call `resume()` from your own gesture.\n * Closing it is allowed: the next use makes a new one.\n */\nexport function sharedAudioContext(): AudioContext {\n // A closed context (someone closed it) cannot be resumed: start a new one.\n if (!shared || shared.state === \"closed\") {\n const Context = globalThis.AudioContext ?? (globalThis as unknown as { webkitAudioContext?: typeof AudioContext }).webkitAudioContext;\n if (!Context) throw new Error(\"This browser has no Web Audio (AudioContext), so effects cannot hear audio.\");\n shared = new Context();\n const ctx = shared;\n const resume = () => {\n if (ctx.state === \"running\") return;\n void ctx.resume().catch(() => undefined);\n };\n if (typeof window !== \"undefined\") {\n for (const type of [\"pointerdown\", \"keydown\", \"touchend\"] as const) window.addEventListener(type, resume, { capture: true, passive: true });\n ctx.addEventListener?.(\"statechange\", () => {\n if (ctx.state !== \"running\") return;\n for (const type of [\"pointerdown\", \"keydown\", \"touchend\"] as const) window.removeEventListener(type, resume, { capture: true });\n });\n }\n }\n return shared;\n}\n\n/** A media element can only be wired into Web Audio once, ever: its source node, kept. */\nconst elementSources = new WeakMap<HTMLMediaElement, MediaElementAudioSourceNode>();\n\n/**\n * Listens to a live {@link AudioSource} through an `AnalyserNode` (in the\n * shared context, or the node's own) and reads the audio uniforms from it.\n * A media element keeps playing to the speakers through Web Audio; a stream\n * (a microphone) is only listened to. A media element from another site\n * needs `crossOrigin = \"anonymous\"` and CORS, or the browser feeds silence.\n */\nexport class LiveAudio {\n readonly context: BaseAudioContext;\n readonly analyser: AnalyserNode;\n private readonly analysis: AudioAnalysis;\n private readonly input: AudioNode;\n private readonly ownsInput: boolean;\n private readonly freq: Float32Array<ArrayBuffer>;\n private readonly wave: Float32Array<ArrayBuffer>;\n\n constructor(source: AudioSource, options: AudioAnalysisOptions = {}) {\n const isNode = typeof AudioNode !== \"undefined\" && source instanceof AudioNode;\n const context = isNode ? (source as AudioNode).context : sharedAudioContext();\n this.context = context;\n if (isNode) {\n this.input = source as AudioNode;\n this.ownsInput = false;\n } else if (typeof MediaStream !== \"undefined\" && source instanceof MediaStream) {\n this.input = (context as AudioContext).createMediaStreamSource(source);\n this.ownsInput = true;\n } else {\n const element = source as HTMLMediaElement;\n let node = elementSources.get(element);\n if (!node) {\n node = (context as AudioContext).createMediaElementSource(element);\n // Wired into Web Audio, the element is heard only through it: keep it playing to the speakers.\n node.connect(context.destination);\n elementSources.set(element, node);\n }\n this.input = node;\n this.ownsInput = false;\n }\n this.analyser = context.createAnalyser();\n this.analyser.fftSize = options.fftSize ?? 2048;\n // No smoothing here: AudioAnalysis smooths, the same live as offline.\n this.analyser.smoothingTimeConstant = 0;\n this.analyser.minDecibels = options.minDecibels ?? -100;\n this.analyser.maxDecibels = options.maxDecibels ?? -30;\n this.input.connect(this.analyser);\n this.analysis = new AudioAnalysis({ ...options, sampleRate: context.sampleRate, fftSize: this.analyser.fftSize });\n this.freq = new Float32Array(this.analyser.frequencyBinCount);\n this.wave = new Float32Array(this.analyser.fftSize);\n if (\"resume\" in context && context.state === \"suspended\") void (context as AudioContext).resume().catch(() => undefined);\n }\n\n /** Reads the analyser now and returns the uniforms, `time` in seconds (a steady clock, such as `performance.now() / 1000`). */\n read(time: number): AudioFrame {\n this.analyser.getFloatFrequencyData(this.freq);\n this.analyser.getFloatTimeDomainData(this.wave);\n this.analysis.update(this.freq, time, this.wave);\n return this.analysis.frame(time);\n }\n\n /** Stops listening. A media element goes on playing to the speakers. */\n dispose() {\n try {\n this.input.disconnect(this.analyser);\n } catch {\n // Already disconnected.\n }\n if (this.ownsInput) this.input.disconnect();\n }\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, the last few clicks, and where it has just\n * been (its trail), so effects can answer hovers, presses, taps and moves.\n * Times are in `iTime` seconds, so a recording can script them (see\n * `ShaderFrames`' `pointer`, `clicks` and `trail`).\n */\n\n/** How many clicks `iClicks` holds; older ones drop off. */\nexport const MAX_CLICKS = 8;\n\n/** How many of the pointer's recent positions `iTrail` holds; older ones drop off. */\nexport const MAX_TRAIL = 32;\n\n/**\n * The least time between two positions in the trail, in seconds: moves in\n * between update the newest. With {@link MAX_TRAIL}, about a second of path.\n */\nexport const TRAIL_SECONDS = 1 / 30;\n\n/** How long hovering eases in or out, in seconds. */\nexport const HOVER_SECONDS = 0.25;\n\n/** A click, or a point of the trail: 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 return latest(clicks, time, MAX_CLICKS);\n}\n\n/**\n * `iTrail` from the pointer's positions, oldest first in the list: the\n * newest {@link MAX_TRAIL} at or before `time`, newest first, as\n * `vec4(x, y, time, 1)`, zeros for the rest.\n */\nexport function trailUniform(trail: readonly ShaderClick[], time: number): Float32Array {\n return latest(trail, time, MAX_TRAIL);\n}\n\nfunction latest(events: readonly ShaderClick[], time: number, max: number): Float32Array {\n const out = new Float32Array(max * 4);\n let i = 0;\n for (let k = events.length - 1; k >= 0 && i < max; k--) {\n const c = events[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 private trail: ShaderClick[] = [];\n\n /**\n * The pointer moved to (x, y), in pixels from the bottom left. With a\n * `time`, the move joins the trail: a new point at most every\n * {@link TRAIL_SECONDS}, the newest point following the pointer between.\n */\n move(x: number, y: number, time?: number) {\n this.x = x;\n this.y = y;\n if (time === undefined) return;\n const last = this.trail[this.trail.length - 1];\n if (last && time >= last.time && time - last.time < TRAIL_SECONDS) {\n last.x = x;\n last.y = y;\n return;\n }\n this.trail.push({ x, y, time });\n if (this.trail.length > MAX_TRAIL) this.trail.shift();\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, presses and the trail (when time jumps back, say). */\n clear() {\n this.clicks = [];\n this.trail = [];\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 /** `iTrail` at `time` (see {@link trailUniform}). */\n trailAt(time: number): Float32Array {\n return trailUniform(this.trail, time);\n }\n}\n","import { controlValue, type ControlDef, type ControlValue } from \"./controls.js\";\nimport { SPECTRUM_BINS } from \"./audio.js\";\nimport { MAX_CLICKS, MAX_TRAIL } 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 {\n name: \"iTrail\",\n type: \"vec4\",\n size: MAX_TRAIL,\n doc: \"where the pointer has just been over the canvas, newest first (about 30 points a second, so a second of path): xy = where (pixels), z = when (iTime seconds), w = 1 for a point, 0 for an empty slot\",\n },\n {\n name: \"iAudio\",\n type: \"vec4\",\n doc: \"the sound (see setAudioSource): x = level (loudness 0–1), y = bass, z = mid, w = treble (0–1, smoothed); all zero with no audio\",\n },\n {\n name: \"iSpectrum\",\n type: \"vec4\",\n size: SPECTRUM_BINS / 4,\n doc: \"64 log-spaced frequency bins (about 30 Hz to 16 kHz), 0–1, four to a vec4, low to high: bin i is iSpectrum[i / 4][i % 4]\",\n },\n {\n name: \"iBeat\",\n type: \"float\",\n doc: \"seconds since the last beat (an onset on the bass), -1 before the first; 0 like every audio uniform when there is no audio at all\",\n },\n {\n name: \"iScroll\",\n type: \"vec4\",\n doc: \"scrolling: x = the canvas's progress through the viewport (0 as its top enters at the bottom, 1 as its bottom leaves at the top), y = page scroll (0–1), z = velocity (viewport heights per second, + down, smoothed), w = 1 while scrolling, easing to 0; all zero where nothing scrolls\",\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}];\nuniform vec4 iTrail[${MAX_TRAIL}];\nuniform vec4 iAudio;\nuniform vec4 iSpectrum[${SPECTRUM_BINS / 4}];\nuniform float iBeat;\nuniform vec4 iScroll;\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);\nconst NO_TRAIL = new Float32Array(MAX_TRAIL * 4);\nconst NO_SPECTRUM = new Float32Array(SPECTRUM_BINS);\n\n/** What one frame shows: its size in pixels, its time, the pointer, the sound and the scroll. */\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 /** `iTrail`: {@link MAX_TRAIL} × (x, y, time, 1), newest first (default none; see `trailUniform`). */\n trail?: Float32Array;\n /** `iAudio`: level, bass, mid, treble (default all zero: no audio). */\n audio?: readonly [number, number, number, number];\n /** `iSpectrum`: {@link SPECTRUM_BINS} values, 0–1, low to high (default all zero). */\n spectrum?: Float32Array;\n /** `iBeat`: seconds since the last beat, -1 before the first (default 0, with no audio at all). */\n beat?: number;\n /** `iScroll`: progress, page, velocity, scrolling (default all zero: nothing scrolls it). */\n scroll?: readonly [number, number, number, number];\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 if (b.iTrail) gl.uniform4fv(b.iTrail, state.trail ?? NO_TRAIL);\n if (b.iAudio) gl.uniform4f(b.iAudio, ...(state.audio ?? ([0, 0, 0, 0] as const)));\n if (b.iSpectrum) gl.uniform4fv(b.iSpectrum, state.spectrum ?? NO_SPECTRUM);\n if (b.iBeat) gl.uniform1f(b.iBeat, state.beat ?? 0);\n if (b.iScroll) gl.uniform4f(b.iScroll, ...(state.scroll ?? ([0, 0, 0, 0] as const)));\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"],"mappings":"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","names":["reverse","cos","sin"]}
|
|
@@ -5,20 +5,20 @@ import {
|
|
|
5
5
|
pageControls,
|
|
6
6
|
planOutput,
|
|
7
7
|
runRender
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-7KYQ4R76.js";
|
|
9
9
|
import {
|
|
10
10
|
svgAnimatedMarkup
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-X3C2VFTM.js";
|
|
12
12
|
import {
|
|
13
13
|
svgFrameMarkup,
|
|
14
14
|
svgViewBox
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-AUH3PEFW.js";
|
|
16
16
|
import {
|
|
17
17
|
subjectValues
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-MAYKC3RF.js";
|
|
19
19
|
import {
|
|
20
20
|
wrapSource
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-HDYBY3PQ.js";
|
|
22
22
|
import {
|
|
23
23
|
EFFECT_CATEGORIES,
|
|
24
24
|
EFFECT_INPUTS,
|
|
@@ -28,14 +28,14 @@ import {
|
|
|
28
28
|
effectInputs,
|
|
29
29
|
getEffect,
|
|
30
30
|
renderVectorEffect
|
|
31
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-UIODYJ6B.js";
|
|
32
32
|
import {
|
|
33
33
|
checkControlValue,
|
|
34
34
|
parseColor
|
|
35
35
|
} from "./chunk-VYOMXE6I.js";
|
|
36
36
|
import {
|
|
37
37
|
svgDuration
|
|
38
|
-
} from "./chunk-
|
|
38
|
+
} from "./chunk-DA4UUSDO.js";
|
|
39
39
|
|
|
40
40
|
// src/export/effect.ts
|
|
41
41
|
import { spawn } from "child_process";
|
|
@@ -110,7 +110,7 @@ var REST = 300;
|
|
|
110
110
|
function vectorTimeline(effect, motion) {
|
|
111
111
|
const hold = effect.hold ?? 600;
|
|
112
112
|
const d = svgDuration(motion);
|
|
113
|
-
if (effect.trigger === "hover" || effect.trigger === "press") {
|
|
113
|
+
if (effect.trigger === "hover" || effect.trigger === "press" || effect.trigger === "scroll") {
|
|
114
114
|
const length2 = REST + d + hold + d;
|
|
115
115
|
return {
|
|
116
116
|
length: length2,
|
|
@@ -411,4 +411,4 @@ export {
|
|
|
411
411
|
effectQueryFromFlags,
|
|
412
412
|
describeEffect
|
|
413
413
|
};
|
|
414
|
-
//# sourceMappingURL=chunk-
|
|
414
|
+
//# sourceMappingURL=chunk-HSYDTVTD.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/export/effect.ts"],"sourcesContent":["/**\n * Renders a catalog effect, applied to a subject, to a file in Node: video\n * (MP4, WebM, MOV), GIF, PNG frames, or (vector effects) an animated SVG.\n */\n\nimport { spawn } from 'node:child_process';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport {\n EFFECT_CATEGORIES,\n EFFECT_INPUTS,\n EFFECT_INTERACTIONS,\n effectControls,\n effectDefaults,\n effectInputs,\n getEffect,\n renderVectorEffect,\n type EffectCategory,\n type EffectDefinition,\n type EffectInput,\n type EffectInteraction,\n type VectorEffect,\n} from '../catalog/index.js';\nimport { subjectValues, type EffectSubject } from '../embed/index.js';\nimport { checkControlValue, parseColor, type ControlDef, type ControlValue } from '../shader/controls.js';\nimport { wrapSource } from '../shader/program.js';\nimport { svgAnimatedMarkup } from '../vector/animated.js';\nimport type { SvgMotion } from '../vector/motion.js';\nimport { svgFrameMarkup, svgViewBox } from '../vector/static.js';\nimport { svgDuration } from '../vector/svg.js';\nimport {\n ffmpegPath,\n frameStates,\n imageDataUrl,\n pageControls,\n planOutput,\n runRender,\n type FrameState,\n type PointerMode,\n} from './recorder.js';\n\n/**\n * What an effect is applied to, in Node:\n * - `{ image }`: a picture's file path or URL (or a `data:` URL).\n * - `{ svg }`: an SVG's file path, URL, or markup (`\"<svg …>\"`).\n * - `{ text }`: words, for a vector effect's text, or drawn with `textImage` for a shader's text or mask.\n * - `{ path }`: SVG path data (`d`), for a shape control.\n * - `{ video }`: a video file's path, decoded to frames with ffmpeg and shown one per frame.\n */\nexport type NodeSubject =\n | { image: string }\n | { svg: string }\n | { text: string; font?: string; weight?: number | string }\n | { path: string }\n | { video: string };\n\nexport interface EffectExportOptions {\n /** What to apply it to; several fill several controls in order (a transition's from and to). */\n subject?: NodeSubject | readonly NodeSubject[];\n /** A preset by name, under `values`. */\n preset?: string;\n /** Control values over the defaults (and the subject). An image control also takes a file path. */\n values?: Record<string, ControlValue>;\n /** .mp4, .webm, .mov, .gif, .svg (vector effects), or PNG frames (a folder, or `out_%04d.png`). */\n outputPath: string;\n /** Pixels (default: 1280 × 720 for effects that show a picture, 720 × 720 for other shaders, a vector's own shape 720 px on its long edge). */\n width?: number;\n height?: number;\n fps?: number;\n /**\n * Seconds. Default: a shader's loop (when it loops in 12 s or less), a\n * video subject's length (up to 10 s), else 3; a vector effect's one\n * play-through (with its trigger acted out).\n */\n duration?: number;\n /** Keep transparency (WebM, MOV, GIF, PNG and SVG; MP4 is always opaque). */\n alpha?: boolean;\n /** CSS colour under transparent pixels without `alpha` (default black for shaders, white for vector effects). */\n background?: string;\n /**\n * The pointer, for effects that answer it: `auto` (the default for them),\n * a scripted pointer that hovers, wanders and clicks; `none` leaves it\n * still. Vector effects with a trigger have it acted out (clicked through\n * their steps, or hovered and let go).\n */\n pointer?: PointerMode;\n onProgress?: (frame: number, total: number) => void;\n}\n\nconst isUrl = (s: string) => /^(https?|data):/i.test(s);\n\n/** An SVG subject as markup: given markup stays, a URL is fetched, a path read. */\nasync function svgMarkup(svg: string): Promise<string> {\n const text = svg.trim();\n if (text.startsWith('<')) return text;\n if (/^data:image\\/svg\\+xml/i.test(text)) {\n const comma = text.indexOf(',');\n const meta = text.slice(0, comma);\n const body = text.slice(comma + 1);\n return /;base64/i.test(meta) ? Buffer.from(body, 'base64').toString('utf8') : decodeURIComponent(body);\n }\n if (/^https?:/i.test(text)) {\n const res = await fetch(text);\n if (!res.ok) throw new Error(`Could not download the SVG at ${text} (HTTP ${res.status}).`);\n return res.text();\n }\n const file = path.resolve(text);\n if (!fs.existsSync(file)) throw new Error(`There is no SVG at ${file}; give a file path, a URL, or markup starting with \"<svg\".`);\n return fs.readFileSync(file, 'utf8');\n}\n\n/** What a Node subject becomes: control values, and video files by image control. */\nexport interface NodeSubjectValues {\n values: Record<string, ControlValue>;\n /** Image control → video file. */\n video: Record<string, string>;\n}\n\n/**\n * The control values that put Node subjects in `effect` (see\n * `subjectValues` in `@pixerate/flair/embed`, which picks the controls):\n * image files and URLs become `data:` URLs, SVG files and URLs are read as\n * markup, and a video is kept as its file for its control, to be decoded to\n * frames. Throws a sentence when the effect takes no such thing.\n */\nexport async function nodeSubjectValues(effect: EffectDefinition, subject: NodeSubject | readonly NodeSubject[]): Promise<NodeSubjectValues> {\n const list = (Array.isArray(subject) ? subject : [subject]) as readonly NodeSubject[];\n const browser: EffectSubject[] = [];\n const videoFiles: string[] = [];\n for (const s of list) {\n if ('image' in s) browser.push({ image: await imageDataUrl(s.image) });\n else if ('svg' in s) browser.push({ svg: await svgMarkup(s.svg) });\n else if ('video' in s) {\n const file = path.resolve(s.video);\n if (!isUrl(s.video) && !fs.existsSync(file)) throw new Error(`There is no video at ${file}.`);\n videoFiles.push(isUrl(s.video) ? s.video : file);\n // A stand-in: subjectValues only needs to know it is a video to pick its control.\n browser.push({ video: { readyState: 0, currentTime: 0 } as unknown as HTMLVideoElement });\n } else browser.push(s);\n }\n const { values, live } = subjectValues(effect, browser);\n const video: Record<string, string> = {};\n Object.keys(live).forEach((name, i) => (video[name] = videoFiles[i]!));\n return { values, video };\n}\n\n/** The effect for an id (or the definition itself); a sentence when there is none. */\nexport function resolveEffect(effect: string | EffectDefinition): EffectDefinition {\n if (typeof effect !== 'string') return effect;\n const def = getEffect(effect);\n if (!def) throw new Error(`There is no effect \"${effect}\" in the catalog; \"flair effects\" (or findEffects()) lists them.`);\n return def;\n}\n\n/**\n * Checks control values against an effect's controls: unknown names, and\n * values a control cannot take, are sentences. Image controls take file\n * paths too (read into `data:` URLs).\n */\nexport async function checkEffectValues(effect: EffectDefinition, values: Record<string, ControlValue>): Promise<Record<string, ControlValue>> {\n const controls = new Map(effectControls(effect).map((c) => [c.name, c]));\n const out: Record<string, ControlValue> = {};\n for (const [name, raw] of Object.entries(values)) {\n const control = controls.get(name);\n if (!control) throw new Error(`\"${effect.id}\" has no control \"${name}\"; it has ${[...controls.keys()].join(', ') || 'none'}.`);\n if (control.type === 'sampler2D' && typeof raw === 'string') {\n out[name] = await imageDataUrl(raw);\n continue;\n }\n if (effect.kind === 'vector' && control.color && typeof raw === 'string' && raw.trim().toLowerCase() === 'currentcolor') {\n out[name] = raw;\n continue;\n }\n const check = checkControlValue(control, raw);\n if (!check.ok) throw new Error(check.message);\n out[name] = check.value;\n }\n return out;\n}\n\n/** How a vector effect plays over time in a recording: the frame at each ms, and its loop. */\nexport interface VectorTimeline {\n /** One play-through, ms. */\n length: number;\n /** Step start times (ms) for the whole loop, for an animated SVG. */\n steps: number[];\n /** The picture's time and steps at `ms` into the recording. */\n at(ms: number): { time: number; steps: number[] };\n}\n\n/** Ms an effect rests before its trigger is acted out. */\nconst REST = 300;\n\n/**\n * A vector effect's timeline: a loop plays and rests `hold` ms; a hover, press or\n * scroll trigger plays to the end, rests, and plays back (a scroll down and up); a click trigger is\n * clicked through each of its steps (or, with none, clicked again after\n * each play-through).\n */\nexport function vectorTimeline(effect: Pick<VectorEffect, 'hold' | 'trigger'>, motion: SvgMotion): VectorTimeline {\n const hold = effect.hold ?? 600;\n const d = svgDuration(motion);\n if (effect.trigger === 'hover' || effect.trigger === 'press' || effect.trigger === 'scroll') {\n const length = REST + d + hold + d;\n return {\n length,\n steps: [],\n at(ms) {\n const p = ms % length;\n const time = p < REST ? 0 : p < REST + d ? p - REST : p < REST + d + hold ? d : Math.max(0, d - (p - REST - d - hold));\n return { time, steps: [] };\n },\n };\n }\n if (effect.trigger === 'click') {\n const anims = motion.animate ?? [];\n const last = anims.reduce((n, a) => Math.max(n, a.step ?? 0), 0);\n if (last === 0) {\n const length = REST + d + hold;\n return { length, steps: [], at: (ms) => ({ time: Math.max(0, (ms % length) - REST), steps: [] }) };\n }\n // One click per step, each when the step before has played and rested.\n const clicks: number[] = [];\n let at = REST;\n for (let step = 1; step <= last; step++) {\n clicks.push(at);\n const ends = anims.filter((a) => (a.step ?? 0) === step).map((a) => (a.delay ?? 0) + (a.ms ?? 800));\n at += Math.max(0, ...ends) + hold;\n }\n const length = at;\n return {\n length,\n steps: clicks,\n at(ms) {\n const p = ms % length;\n return { time: p, steps: clicks.filter((c) => c <= p) };\n },\n };\n }\n const length = d + hold;\n return { length, steps: [], at: (ms) => ({ time: ms % length, steps: [] }) };\n}\n\nconst even = (n: number) => Math.max(2, Math.round(n / 2) * 2);\n\n/** Default pixel size for an effect (see {@link EffectExportOptions.width}). */\nexport function defaultSize(effect: EffectDefinition, motion?: SvgMotion): { width: number; height: number } {\n if (effect.kind === 'vector') {\n const box = svgViewBox((motion ?? effect.motion).svg);\n const aspect = box && box[2] > 0 && box[3] > 0 ? box[2] / box[3] : 1;\n return aspect >= 1 ? { width: 720, height: even(720 / aspect) } : { width: even(720 * aspect), height: 720 };\n }\n return effectInputs(effect).images.length ? { width: 1280, height: 720 } : { width: 720, height: 720 };\n}\n\nfunction run(bin: string, args: string[]): Promise<{ code: number; stderr: string }> {\n return new Promise((resolve, reject) => {\n const proc = spawn(bin, args, { stdio: ['ignore', 'ignore', 'pipe'] });\n let stderr = '';\n proc.stderr!.on('data', (d) => (stderr += d.toString()));\n proc.on('error', (err: NodeJS.ErrnoException) =>\n reject(err.code === 'ENOENT' ? new Error('A video subject needs ffmpeg to decode it, and it is not installed (or not on the PATH); install it, or set FFMPEG_PATH.') : err),\n );\n proc.on('close', (code) => resolve({ code: code ?? 1, stderr }));\n });\n}\n\n/** A video's length in seconds, from ffmpeg's report (undefined when it does not say). */\nexport async function videoSeconds(file: string): Promise<number | undefined> {\n const { stderr } = await run(ffmpegPath(), ['-hide_banner', '-i', file]);\n const m = /Duration:\\s*(\\d+):(\\d+):(\\d+(?:\\.\\d+)?)/.exec(stderr);\n return m ? Number(m[1]) * 3600 + Number(m[2]) * 60 + Number(m[3]) : undefined;\n}\n\n/**\n * Decodes `frames` frames of a video at `fps` into PNGs in `dir` (looping a\n * short video), at most 2048 px on the long edge. Returns their paths.\n */\nexport async function extractVideoFrames(file: string, fps: number, frames: number, dir: string): Promise<string[]> {\n fs.mkdirSync(dir, { recursive: true });\n const { code, stderr } = await run(ffmpegPath(), [\n '-hide_banner', '-loglevel', 'error', '-y',\n '-stream_loop', '-1', '-i', file,\n '-vf', `fps=${fps},scale='if(gte(iw,ih),min(2048,iw),-2)':'if(gte(iw,ih),-2,min(2048,ih))'`,\n '-frames:v', String(frames), '-start_number', '0',\n path.join(dir, '%06d.png'),\n ]);\n const files = fs.readdirSync(dir).filter((f) => f.endsWith('.png')).sort().map((f) => path.join(dir, f));\n if (code !== 0 || !files.length) throw new Error(`ffmpeg could not decode the video at ${file}: ${stderr.trim() || `exit ${code}`}`);\n return files;\n}\n\n/**\n * Renders a catalog effect (by id or definition), applied to `subject`, to\n * a file: a shader through WebGL2 in headless Chrome, a vector effect by\n * drawing each frame's SVG there, or (to `.svg`) a vector effect as one\n * animated SVG with no browser at all. MP4, WebM, MOV and video subjects\n * need ffmpeg. Throws a sentence when the effect is unknown, takes no such\n * subject, or cannot be written in that format.\n *\n * ```ts\n * await exportEffect(\"image-duotone\", { subject: { image: \"team.jpg\" }, preset: \"Spotify\", outputPath: \"team.mp4\" });\n * await exportEffect(\"click-ripple\", { alpha: true, outputPath: \"ripple.webm\" });\n * await exportEffect(\"typewriter\", { subject: { text: \"Hello\" }, outputPath: \"hello.gif\" });\n * await exportEffect(\"toggle-switch\", { outputPath: \"toggle.svg\" });\n * ```\n */\nexport async function exportEffect(effect: string | EffectDefinition, options: EffectExportOptions): Promise<string> {\n const def = resolveEffect(effect);\n if (options.preset && !def.presets?.some((p) => p.name === options.preset)) {\n const names = def.presets?.map((p) => `\"${p.name}\"`).join(', ');\n throw new Error(`\"${def.id}\" has no preset \"${options.preset}\"; ${names ? `its presets are ${names}` : 'it has no presets'}.`);\n }\n const fromSubject = options.subject ? await nodeSubjectValues(def, options.subject) : { values: {}, video: {} };\n const explicit = await checkEffectValues(def, options.values ?? {});\n const values = { ...effectDefaults(def, options.preset ? { preset: options.preset } : {}), ...fromSubject.values, ...explicit };\n const fps = options.fps ?? 30;\n const background = options.background ?? (def.kind === 'vector' ? '#ffffff' : '#000000');\n\n if (def.kind === 'vector') {\n const motion = renderVectorEffect(def, values);\n const timeline = vectorTimeline(def, motion);\n const duration = options.duration ?? timeline.length / 1000;\n const frames = Math.max(1, Math.round(duration * fps));\n const plan = planOutput(options.outputPath, { alpha: options.alpha, frames });\n if (plan.format === 'svg') {\n const markup = svgAnimatedMarkup(motion, {\n ...(def.hold !== undefined ? { hold: def.hold } : {}),\n ...(timeline.steps.length ? { steps: timeline.steps, duration: timeline.length } : {}),\n ...(options.duration !== undefined ? { duration: options.duration * 1000 } : {}),\n fps,\n });\n if (!markup) throw new Error(`\"${def.id}\" has no <svg> to write.`);\n // A file gets a size of its own (it fills its container otherwise); a background only when asked for.\n const size = defaultSize(def, motion);\n const w = Math.round(options.width ?? size.width);\n const h = Math.round(options.height ?? (options.width ? (options.width * size.height) / size.width : size.height));\n const style = !plan.alpha && options.background ? ` style=\"background:${options.background.replace(/[\"<>&]/g, '')}\"` : '';\n const out = markup.replace(/^<svg\\b[^>]*>/, (open) =>\n open.replace(/\\swidth=\"100%\"/, ` width=\"${w}\"`).replace(/\\sheight=\"100%\"/, ` height=\"${h}\"`).replace(/>$/, `${style}>`),\n );\n fs.mkdirSync(path.dirname(plan.path), { recursive: true });\n fs.writeFileSync(plan.path, out, 'utf8');\n options.onProgress?.(1, 1);\n return plan.path;\n }\n const size = defaultSize(def, motion);\n const width = Math.max(1, Math.round(options.width ?? size.width));\n const height = Math.max(1, Math.round(options.height ?? (options.width ? (options.width * size.height) / size.width : size.height)));\n const base = frameStates(frames, fps, width, height, 'none');\n const pointer = options.pointer ?? 'auto';\n const state = (i: number): FrameState => {\n const ms = (i / fps) * 1000;\n const { time, steps } = pointer === 'none' && def.trigger ? { time: 0, steps: [] } : timeline.at(ms);\n return { ...base[i]!, svg: svgFrameMarkup(motion, { at: time, steps })! };\n };\n return runRender({\n page: { width, height, frames, alpha: plan.alpha, background, send: plan.format === 'gif' ? 'rgba' : 'png' },\n state,\n plan,\n fps,\n ...(options.onProgress ? { onProgress: options.onProgress } : {}),\n });\n }\n\n // A shader.\n const videoEntries = Object.entries(fromSubject.video);\n let duration = options.duration;\n if (duration === undefined && videoEntries.length) {\n const seconds = await videoSeconds(videoEntries[0]![1]);\n duration = seconds ? Math.min(10, seconds) : undefined;\n }\n duration ??= def.loop && def.loop <= 12 ? def.loop : 3;\n const frames = Math.max(1, Math.round(duration * fps));\n const plan = planOutput(options.outputPath, { alpha: options.alpha, frames });\n if (plan.format === 'svg') {\n throw new Error(`\"${def.id}\" is a shader effect, which draws pixels; write .mp4, .webm, .mov, .gif or .png frames (only vector effects become SVG).`);\n }\n const size = defaultSize(def);\n const width = Math.max(1, Math.round(options.width ?? size.width));\n const height = Math.max(1, Math.round(options.height ?? (options.width ? (options.width * size.height) / size.width : size.height)));\n const interactive = effectInputs(def).interactions.length > 0;\n const states = frameStates(frames, fps, width, height, options.pointer ?? (interactive ? 'auto' : 'none'));\n\n const tmp = videoEntries.length ? fs.mkdtempSync(path.join(os.tmpdir(), 'flair-video-')) : undefined;\n try {\n const videoFrames: Record<string, string[]> = {};\n for (const [name, file] of videoEntries) videoFrames[name] = await extractVideoFrames(file, fps, frames, path.join(tmp!, name));\n const state = (i: number): FrameState => {\n if (!videoEntries.length) return states[i]!;\n const video = Object.fromEntries(Object.entries(videoFrames).map(([name, files]) => [name, files[Math.min(i, files.length - 1)]!]));\n return { ...states[i]!, video };\n };\n return await runRender({\n page: {\n width,\n height,\n frames,\n alpha: plan.alpha,\n background,\n send: plan.format === 'gif' ? 'rgba' : 'png',\n shader: {\n fragment: wrapSource(def.source),\n controls: await pageControls(def.source, values),\n opaque: !plan.alpha && !def.transparent,\n },\n },\n state,\n plan,\n fps,\n ...(options.onProgress ? { onProgress: options.onProgress } : {}),\n });\n } finally {\n if (tmp) fs.rmSync(tmp, { recursive: true, force: true });\n }\n}\n\n// ==========================================\n// Command-line helpers\n// ==========================================\n\n/** `\"1280x720\"` → `{ width: 1280, height: 720 }`; a sentence for anything else. */\nexport function parseSize(size: string): { width: number; height: number } {\n const m = /^\\s*(\\d+)\\s*[x×X]\\s*(\\d+)\\s*$/.exec(size);\n if (!m || Number(m[1]) < 1 || Number(m[2]) < 1) throw new Error(`--size takes width x height in pixels, like 1280x720, not \"${size}\".`);\n return { width: Number(m[1]), height: Number(m[2]) };\n}\n\n/**\n * A `--set` value read for its control: numbers for `float` and `int`,\n * `true`/`false` (or 1/0) for `bool`, a colour (`#77C16A`, `rgb(…)`) or\n * numbers separated by commas or spaces for vectors, and the text itself\n * for images (a path or URL) and text.\n */\nexport function parseSetValue(control: ControlDef, raw: string): ControlValue {\n const text = raw.trim();\n switch (control.type) {\n case 'float':\n case 'int': {\n const n = Number(text);\n if (text === '' || !Number.isFinite(n)) throw new Error(`${control.name} takes a number, not \"${raw}\".`);\n return n;\n }\n case 'bool': {\n if (/^(true|1|yes|on)$/i.test(text)) return true;\n if (/^(false|0|no|off)$/i.test(text)) return false;\n throw new Error(`${control.name} takes true or false, not \"${raw}\".`);\n }\n case 'sampler2D':\n case 'text':\n return raw;\n default: {\n if (control.color && (parseColor(text) || /^currentcolor$/i.test(text)) && !/^[-\\d.]+([\\s,]+[-\\d.]+)*$/.test(text)) return text;\n const parts = text.split(/[\\s,]+/).filter(Boolean).map(Number);\n if (!parts.length || parts.some((n) => !Number.isFinite(n))) {\n throw new Error(`${control.name} takes ${control.type === 'vec2' ? 2 : control.type === 'vec3' ? 3 : 4} numbers separated by commas${control.color ? ', or a colour like \"#77C16A\"' : ''}, not \"${raw}\".`);\n }\n return parts;\n }\n }\n}\n\n/** `--set name=value` flags as control values for an effect; sentences for unknown names and bad values. */\nexport function parseSets(effect: EffectDefinition, sets: readonly string[]): Record<string, ControlValue> {\n const controls = new Map(effectControls(effect).map((c) => [c.name, c]));\n const out: Record<string, ControlValue> = {};\n for (const set of sets) {\n const eq = set.indexOf('=');\n if (eq <= 0) throw new Error(`--set takes name=value, like --set uSpeed=2, not \"${set}\".`);\n const name = set.slice(0, eq).trim();\n const control = controls.get(name);\n if (!control) throw new Error(`\"${effect.id}\" has no control \"${name}\"; it has ${[...controls.keys()].join(', ') || 'none'}.`);\n out[name] = parseSetValue(control, set.slice(eq + 1));\n }\n return out;\n}\n\n/** The render command's subject flags. */\nexport interface SubjectFlags {\n image?: readonly string[];\n svg?: readonly string[];\n text?: readonly string[];\n path?: readonly string[];\n video?: readonly string[];\n font?: string;\n weight?: string;\n}\n\n/** Subjects from the render command's flags, in the order images, SVGs, videos, texts, paths. */\nexport function subjectsFromFlags(flags: SubjectFlags): NodeSubject[] {\n const weight = flags.weight === undefined ? undefined : /^\\d+$/.test(flags.weight) ? Number(flags.weight) : flags.weight;\n return [\n ...(flags.image ?? []).map((image) => ({ image })),\n ...(flags.svg ?? []).map((svg) => ({ svg })),\n ...(flags.video ?? []).map((video) => ({ video })),\n ...(flags.text ?? []).map((text) => ({ text, ...(flags.font ? { font: flags.font } : {}), ...(weight !== undefined ? { weight } : {}) })),\n ...(flags.path ?? []).map((p) => ({ path: p })),\n ];\n}\n\n/** Comma-separated (or repeated) flag values checked against a list; a sentence for the rest. */\nexport function parseChoices<T extends string>(flag: string, values: readonly string[] | undefined, allowed: readonly T[]): T[] | undefined {\n if (!values?.length) return undefined;\n const list = values.flatMap((v) => v.split(',')).map((v) => v.trim()).filter(Boolean);\n const bad = list.filter((v) => !(allowed as readonly string[]).includes(v));\n if (bad.length) throw new Error(`${flag} takes ${allowed.join(', ')}; not ${bad.map((b) => `\"${b}\"`).join(', ')}.`);\n return list as T[];\n}\n\n/** The effects command's filters, checked. */\nexport function effectQueryFromFlags(flags: { accepts?: readonly string[]; interaction?: readonly string[]; category?: string; kind?: string; search?: string; transparent?: boolean }) {\n const accepts = parseChoices<EffectInput>('--accepts', flags.accepts, EFFECT_INPUTS);\n const interaction = parseChoices<EffectInteraction>('--interaction', flags.interaction, EFFECT_INTERACTIONS);\n if (flags.category && !(EFFECT_CATEGORIES as readonly string[]).includes(flags.category)) {\n throw new Error(`--category takes one of ${EFFECT_CATEGORIES.join(', ')}; not \"${flags.category}\".`);\n }\n if (flags.kind && flags.kind !== 'shader' && flags.kind !== 'vector') throw new Error(`--kind takes shader or vector; not \"${flags.kind}\".`);\n return {\n ...(accepts ? { accepts } : {}),\n ...(interaction ? { interaction } : {}),\n ...(flags.category ? { category: flags.category as EffectCategory } : {}),\n ...(flags.kind ? { kind: flags.kind as 'shader' | 'vector' } : {}),\n ...(flags.search ? { text: flags.search } : {}),\n ...(flags.transparent !== undefined ? { transparent: flags.transparent } : {}),\n };\n}\n\n/** One line about an effect for the effects command: id, kind, category, and what it takes. */\nexport function describeEffect(effect: EffectDefinition): string {\n const inputs = effectInputs(effect);\n const parts = [\n `${effect.kind}/${effect.category}`,\n `takes ${inputs.accepts.join(', ') || 'nothing'}`,\n ...(inputs.video ? [`video: ${inputs.video}`] : []),\n ...(inputs.interactions.length ? [`answers ${inputs.interactions.join(', ')}`] : []),\n ...(inputs.transparent ? ['overlay'] : []),\n ...(effect.presets?.length ? [`presets: ${effect.presets.map((p) => p.name).join(', ')}`] : []),\n ];\n return `${effect.id.padEnd(24)} ${parts.join(' · ')}`;\n}\n"],"mappings":"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","names":["length","duration","frames","plan","size","width","height"]}
|