@pixerate/flair 0.1.1 → 0.3.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 +21 -0
- package/README.md +65 -14
- package/dist/catalog/index.d.ts +5 -4
- package/dist/catalog/index.js +12 -4
- package/dist/{chunk-JWZCPMFG.js → chunk-D7NLHC7K.js} +117 -674
- package/dist/chunk-D7NLHC7K.js.map +1 -0
- package/dist/{chunk-3FAUZOGL.js → chunk-FIJOMGPG.js} +2 -2
- package/dist/{chunk-UQQNZSYL.js → chunk-GTIAOYOK.js} +211 -7
- package/dist/chunk-GTIAOYOK.js.map +1 -0
- package/dist/{chunk-MWVTQFV5.js → chunk-IWKM5FJK.js} +410 -59
- package/dist/chunk-IWKM5FJK.js.map +1 -0
- package/dist/{chunk-HKXOHRCD.js → chunk-VA74HBPK.js} +2 -2
- package/dist/chunk-WAMA45G3.js +998 -0
- package/dist/chunk-WAMA45G3.js.map +1 -0
- package/dist/{chunk-J55UBNAT.js → chunk-WC3VADZC.js} +42 -36
- package/dist/chunk-WC3VADZC.js.map +1 -0
- package/dist/cli.js +12 -11
- package/dist/cli.js.map +1 -1
- package/dist/{controls-DdXMbovi.d.ts → controls-Syr6drzO.d.ts} +4 -3
- package/dist/editor/index.d.ts +101 -49
- package/dist/editor/index.js +198 -99
- package/dist/editor/index.js.map +1 -1
- package/dist/{export-FEBPEEO7.js → export-DH2HMACO.js} +4 -3
- package/dist/index.d.ts +193 -17
- package/dist/index.js +69 -33
- package/dist/index.js.map +1 -1
- package/dist/node.d.ts +1 -1
- package/dist/node.js +2 -2
- package/dist/recorder-KEX3U35D.js +10 -0
- package/dist/{renderer-Oyynz-fs.d.ts → renderer-mzvhKzKa.d.ts} +1 -1
- package/dist/types-CsFUZZIv.d.ts +102 -0
- package/package.json +3 -3
- package/dist/chunk-36DTNSHU.js +0 -160
- package/dist/chunk-36DTNSHU.js.map +0 -1
- package/dist/chunk-J55UBNAT.js.map +0 -1
- package/dist/chunk-JWZCPMFG.js.map +0 -1
- package/dist/chunk-MWVTQFV5.js.map +0 -1
- package/dist/chunk-UQQNZSYL.js.map +0 -1
- package/dist/recorder-WJTWMEQN.js +0 -10
- package/dist/types-CkKkavvB.d.ts +0 -71
- /package/dist/{chunk-3FAUZOGL.js.map → chunk-FIJOMGPG.js.map} +0 -0
- /package/dist/{chunk-HKXOHRCD.js.map → chunk-VA74HBPK.js.map} +0 -0
- /package/dist/{export-FEBPEEO7.js.map → export-DH2HMACO.js.map} +0 -0
- /package/dist/{recorder-WJTWMEQN.js.map → recorder-KEX3U35D.js.map} +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/catalog/glsl.ts","../src/catalog/shaders/gradients.ts","../src/catalog/images.ts","../src/catalog/shaders/image.ts","../src/catalog/shaders/light.ts","../src/catalog/shaders/organic.ts","../src/catalog/shaders/patterns.ts","../src/catalog/shaders/retro.ts","../src/catalog/types.ts","../src/catalog/vector.ts","../src/catalog/index.ts"],"sourcesContent":["/**\n * GLSL helpers the catalog's shaders are built from. Each effect's source\n * inlines the helpers it uses, so a source stays self-contained: copy it into\n * any Shadertoy-style host and it compiles.\n */\n\n/** Hashes: `hash12` (vec2 → float), `hash22` (vec2 → vec2), both 0–1. */\nexport const HASH = `\nfloat hash12(vec2 p) {\n vec3 p3 = fract(vec3(p.xyx) * 0.1031);\n p3 += dot(p3, p3.yzx + 33.33);\n return fract((p3.x + p3.y) * p3.z);\n}\nvec2 hash22(vec2 p) {\n vec3 p3 = fract(vec3(p.xyx) * vec3(0.1031, 0.1030, 0.0973));\n p3 += dot(p3, p3.yzx + 33.33);\n return fract((p3.xx + p3.yz) * p3.zy);\n}`.trim();\n\n/** Smooth value noise, 0–1 (needs {@link HASH}). */\nexport const NOISE = `\nfloat vnoise(vec2 p) {\n vec2 i = floor(p);\n vec2 f = fract(p);\n vec2 u = f * f * (3.0 - 2.0 * f);\n float a = hash12(i);\n float b = hash12(i + vec2(1.0, 0.0));\n float c = hash12(i + vec2(0.0, 1.0));\n float d = hash12(i + vec2(1.0, 1.0));\n return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);\n}`.trim();\n\n/** Fractal noise: `fbm(p, octaves)`, about 0–1 (needs {@link NOISE}). */\nexport const FBM = `\nfloat fbm(vec2 p, int octaves) {\n float sum = 0.0;\n float amp = 0.5;\n mat2 turn = mat2(0.8, 0.6, -0.6, 0.8);\n for (int i = 0; i < 8; i++) {\n if (i >= octaves) break;\n sum += amp * vnoise(p);\n p = turn * p * 2.02 + 17.0;\n amp *= 0.5;\n }\n return sum;\n}`.trim();\n\n/** Inigo Quilez's cosine palette: `palette(t, a, b, c, d)`. */\nexport const PALETTE = `\nvec3 palette(float t, vec3 a, vec3 b, vec3 c, vec3 d) {\n return a + b * cos(6.28318 * (c * t + d));\n}`.trim();\n\n/** Mixes colours in Oklab, so blends stay bright: `oklabMix(a, b, t)` on sRGB colours. */\nexport const OKLAB_MIX = `\nvec3 toOklab(vec3 c) {\n c = pow(max(c, 0.0), vec3(2.2));\n vec3 lms = mat3(0.4122, 0.2119, 0.0883, 0.5363, 0.6807, 0.2817, 0.0514, 0.1074, 0.6300) * c;\n lms = pow(lms, vec3(1.0 / 3.0));\n return mat3(0.2105, 1.9780, 0.0259, 0.7936, -2.4286, 0.7828, -0.0041, 0.4506, -0.8087) * lms;\n}\nvec3 fromOklab(vec3 c) {\n vec3 lms = mat3(1.0, 1.0, 1.0, 0.3963, -0.1056, -0.0895, 0.2158, -0.0639, -1.2915) * c;\n lms = lms * lms * lms;\n vec3 rgb = mat3(4.0767, -1.2684, -0.0042, -3.3077, 2.6098, -0.7034, 0.2310, -0.3413, 1.7076) * lms;\n return pow(max(rgb, 0.0), vec3(1.0 / 2.2));\n}\nvec3 oklabMix(vec3 a, vec3 b, float t) {\n return fromOklab(mix(toOklab(a), toOklab(b), t));\n}`.trim();\n\n/** 2D rotation matrix: `rot(angle)`. */\nexport const ROTATE = `\nmat2 rot(float a) {\n float c = cos(a);\n float s = sin(a);\n return mat2(c, s, -s, c);\n}`.trim();\n\n/** Joins helpers and a body into one source, a blank line apart. */\nexport function glsl(...parts: string[]): string {\n return parts.map((p) => p.trim()).join(\"\\n\\n\") + \"\\n\";\n}\n","import { FBM, glsl, HASH, NOISE, OKLAB_MIX, ROTATE } from \"../glsl.js\";\nimport type { ShaderEffect } from \"../types.js\";\n\nexport const aurora: ShaderEffect = {\n kind: \"shader\",\n id: \"aurora\",\n name: \"Aurora\",\n description: \"Soft curtains of light drifting over a night sky.\",\n category: \"gradient\",\n tags: [\"background\", \"hero\", \"calm\", \"night\"],\n source: glsl(\n `// Aurora: curtains of light drifting over a night sky.\nuniform vec3 uSky; // @control color default=#050816 label=\"Sky\"\nuniform vec3 uGlowA; // @control color default=#22D3A6 label=\"Glow A\"\nuniform vec3 uGlowB; // @control color default=#7C3AED label=\"Glow B\"\nuniform float uSpeed; // @control min=0 max=2 default=0.35 label=\"Speed\"\nuniform float uIntensity; // @control min=0 max=2 default=1 label=\"Intensity\"\nuniform float uHeight; // @control min=0 max=1 default=0.55 label=\"Height\"`,\n HASH,\n NOISE,\n FBM,\n `void mainImage(out vec4 fragColor, in vec2 fragCoord) {\n vec2 uv = fragCoord / iResolution.xy;\n float t = iTime * uSpeed;\n vec3 col = uSky * (0.6 + 0.4 * uv.y);\n for (int i = 0; i < 3; i++) {\n float fi = float(i);\n float x = uv.x * (1.4 + 0.3 * fi) + fi * 3.7;\n float wave = fbm(vec2(x * 1.5 + t * 0.4, t * 0.25 + fi), 4);\n float centre = uHeight + 0.18 * (wave - 0.5) + 0.06 * fi;\n float band = exp(-pow((uv.y - centre) * (7.0 + 2.0 * fi), 2.0));\n float rays = 0.55 + 0.45 * fbm(vec2(x * 12.0, t * 0.6 + fi * 9.0), 3);\n float above = smoothstep(centre - 0.05, centre + 0.35, uv.y) * 0.35;\n vec3 glow = mix(uGlowA, uGlowB, clamp(uv.y * 1.4 - 0.2 + 0.25 * fi, 0.0, 1.0));\n col += glow * (band * rays + above * band) * 0.6 * uIntensity;\n }\n float stars = step(0.997, hash12(floor(fragCoord * 0.5))) * smoothstep(0.3, 1.0, uv.y);\n col += stars * 0.6 * (0.5 + 0.5 * sin(iTime * 3.0 + hash12(floor(fragCoord)) * 40.0));\n fragColor = vec4(col, 1.0);\n}`,\n ),\n presets: [\n { name: \"Arctic\", values: { uGlowA: [0.13, 0.83, 0.65], uGlowB: [0.49, 0.23, 0.93] } },\n { name: \"Ember\", values: { uSky: [0.06, 0.02, 0.03], uGlowA: [1, 0.45, 0.1], uGlowB: [0.9, 0.1, 0.4] } },\n { name: \"Ice\", values: { uSky: [0.01, 0.03, 0.06], uGlowA: [0.5, 0.9, 1], uGlowB: [0.2, 0.4, 1] } },\n ],\n};\n\nexport const meshGradient: ShaderEffect = {\n kind: \"shader\",\n id: \"mesh-gradient\",\n name: \"Mesh gradient\",\n description: \"Four colours blended across a slowly warping mesh, mixed in Oklab.\",\n category: \"gradient\",\n tags: [\"background\", \"hero\", \"brand\", \"soft\"],\n source: glsl(\n `// Mesh gradient: four colours on drifting points, blended in Oklab.\nuniform vec3 uColorA; // @control color default=#FF6B6B label=\"Colour A\"\nuniform vec3 uColorB; // @control color default=#FFD93D label=\"Colour B\"\nuniform vec3 uColorC; // @control color default=#6BCB77 label=\"Colour C\"\nuniform vec3 uColorD; // @control color default=#4D96FF label=\"Colour D\"\nuniform float uSpeed; // @control min=0 max=2 default=0.3 label=\"Speed\"\nuniform float uWarp; // @control min=0 max=1 default=0.35 label=\"Warp\"\nuniform float uGrain; // @control min=0 max=0.2 default=0.04 label=\"Grain\"`,\n HASH,\n NOISE,\n OKLAB_MIX,\n `void mainImage(out vec4 fragColor, in vec2 fragCoord) {\n vec2 uv = fragCoord / iResolution.xy;\n float t = iTime * uSpeed;\n vec2 w = uv + uWarp * 0.3 * vec2(vnoise(uv * 2.0 + t), vnoise(uv * 2.0 - t + 5.0)) - uWarp * 0.15;\n vec2 pa = vec2(0.2, 0.2) + 0.15 * vec2(sin(t * 1.1), cos(t * 0.9));\n vec2 pb = vec2(0.8, 0.25) + 0.15 * vec2(cos(t * 0.7), sin(t * 1.3));\n vec2 pc = vec2(0.25, 0.8) + 0.15 * vec2(sin(t * 0.8 + 2.0), cos(t * 1.2));\n vec2 pd = vec2(0.8, 0.8) + 0.15 * vec2(cos(t * 1.0 + 1.0), sin(t * 0.6));\n vec4 d = vec4(distance(w, pa), distance(w, pb), distance(w, pc), distance(w, pd));\n vec4 k = 1.0 / (d * d + 0.02);\n k /= dot(k, vec4(1.0));\n vec3 ab = oklabMix(uColorA, uColorB, k.y / max(k.x + k.y, 1e-4));\n vec3 cd = oklabMix(uColorC, uColorD, k.w / max(k.z + k.w, 1e-4));\n vec3 col = oklabMix(ab, cd, k.z + k.w);\n col += (hash12(fragCoord + fract(iTime) * 100.0) - 0.5) * uGrain;\n fragColor = vec4(col, 1.0);\n}`,\n ),\n presets: [\n { name: \"Sunset\", values: { uColorA: [1, 0.42, 0.42], uColorB: [1, 0.85, 0.24], uColorC: [0.9, 0.3, 0.6], uColorD: [0.36, 0.2, 0.7] } },\n { name: \"Lagoon\", values: { uColorA: [0.0, 0.6, 0.7], uColorB: [0.5, 0.95, 0.85], uColorC: [0.1, 0.3, 0.6], uColorD: [0.85, 0.95, 1] } },\n { name: \"Mono\", values: { uColorA: [0.1, 0.1, 0.12], uColorB: [0.3, 0.3, 0.34], uColorC: [0.2, 0.2, 0.24], uColorD: [0.6, 0.6, 0.65] } },\n ],\n};\n\nexport const conicSweep: ShaderEffect = {\n kind: \"shader\",\n id: \"conic-sweep\",\n name: \"Conic sweep\",\n description: \"A rotating conic gradient with a soft glowing core.\",\n category: \"gradient\",\n tags: [\"background\", \"spotlight\", \"brand\"],\n loop: 10,\n source: glsl(\n `// Conic sweep: a rotating conic gradient with a glowing core.\nuniform vec3 uColorA; // @control color default=#0EA5E9 label=\"Colour A\"\nuniform vec3 uColorB; // @control color default=#A855F7 label=\"Colour B\"\nuniform vec3 uColorC; // @control color default=#F43F5E label=\"Colour C\"\nuniform float uTurns; // @control min=-3 max=3 default=1 step=1 label=\"Turns per loop\"\nuniform float uCore; // @control min=0 max=1 default=0.4 label=\"Core glow\"\nuniform vec2 uCenter; // @control min=-0.5 max=0.5 default=0,0 label=\"Centre\"`,\n OKLAB_MIX,\n `void mainImage(out vec4 fragColor, in vec2 fragCoord) {\n vec2 p = (fragCoord - 0.5 * iResolution.xy) / iResolution.y - uCenter;\n float a = atan(p.y, p.x) / 6.28318 + 0.5;\n a = fract(a + iTime * uTurns / 10.0);\n float s = a * 3.0;\n vec3 col = s < 1.0 ? oklabMix(uColorA, uColorB, s)\n : s < 2.0 ? oklabMix(uColorB, uColorC, s - 1.0)\n : oklabMix(uColorC, uColorA, s - 2.0);\n float r = length(p);\n col *= 0.75 + 0.25 * smoothstep(0.9, 0.0, r);\n col += uCore * exp(-r * 6.0);\n fragColor = vec4(col, 1.0);\n}`,\n ),\n};\n\nexport const silk: ShaderEffect = {\n kind: \"shader\",\n id: \"silk\",\n name: \"Silk\",\n description: \"Folds of glossy fabric rippling in a slow breeze.\",\n category: \"gradient\",\n tags: [\"background\", \"luxury\", \"soft\"],\n source: glsl(\n `// Silk: glossy folds of fabric rippling slowly.\nuniform vec3 uBase; // @control color default=#3B0764 label=\"Base\"\nuniform vec3 uSheen; // @control color default=#F0ABFC label=\"Sheen\"\nuniform float uSpeed; // @control min=0 max=2 default=0.4 label=\"Speed\"\nuniform float uFolds; // @control min=1 max=10 default=5 label=\"Folds\"\nuniform float uGloss; // @control min=0 max=2 default=1 label=\"Gloss\"`,\n ROTATE,\n `float folds(vec2 p, float t) {\n float h = 0.0;\n for (int i = 1; i <= 4; i++) {\n float fi = float(i);\n h += sin(p.x * uFolds * fi * 0.7 + t * (1.0 + 0.3 * fi) + sin(p.y * 2.0 * fi + t)) / fi;\n }\n return h;\n}\n\nvoid mainImage(out vec4 fragColor, in vec2 fragCoord) {\n vec2 p = rot(0.6) * (fragCoord - 0.5 * iResolution.xy) / iResolution.y;\n float t = iTime * uSpeed;\n float e = 0.002;\n float hx = (folds(p + vec2(e, 0.0), t) - folds(p - vec2(e, 0.0), t)) / (2.0 * e);\n float hy = (folds(p + vec2(0.0, e), t) - folds(p - vec2(0.0, e), t)) / (2.0 * e);\n vec3 n = normalize(vec3(-hx * 0.12, -hy * 0.12, 1.0));\n vec3 l = normalize(vec3(-0.4, 0.6, 0.7));\n float diff = clamp(dot(n, l), 0.0, 1.0);\n float spec = pow(clamp(dot(reflect(-l, n), vec3(0.0, 0.0, 1.0)), 0.0, 1.0), 20.0) * uGloss;\n vec3 col = uBase * (0.35 + 0.9 * diff) + uSheen * (spec + 0.12 * pow(diff, 6.0));\n fragColor = vec4(col, 1.0);\n}`,\n ),\n presets: [\n { name: \"Champagne\", values: { uBase: [0.55, 0.42, 0.25], uSheen: [1, 0.95, 0.8] } },\n { name: \"Midnight\", values: { uBase: [0.04, 0.08, 0.2], uSheen: [0.6, 0.75, 1] } },\n ],\n};\n\nexport const duotoneFlow: ShaderEffect = {\n kind: \"shader\",\n id: \"gradient-flow\",\n name: \"Gradient flow\",\n description: \"Two colours flowing through each other along a warped noise field.\",\n category: \"gradient\",\n tags: [\"background\", \"brand\", \"liquid\"],\n source: glsl(\n `// Gradient flow: two colours flowing through a warped noise field.\nuniform vec3 uColorA; // @control color default=#1E3A8A label=\"Colour A\"\nuniform vec3 uColorB; // @control color default=#F97316 label=\"Colour B\"\nuniform float uSpeed; // @control min=0 max=2 default=0.25 label=\"Speed\"\nuniform float uScale; // @control min=0.5 max=6 default=2 label=\"Scale\"\nuniform float uContrast; // @control min=0.5 max=4 default=1.5 label=\"Contrast\"`,\n HASH,\n NOISE,\n FBM,\n OKLAB_MIX,\n `void mainImage(out vec4 fragColor, in vec2 fragCoord) {\n vec2 p = fragCoord / iResolution.y * uScale;\n float t = iTime * uSpeed;\n vec2 q = vec2(fbm(p + t, 4), fbm(p + vec2(5.2, 1.3) - t, 4));\n float f = fbm(p + 3.0 * q + vec2(1.7, 9.2) + 0.5 * t, 5);\n f = clamp((f - 0.5) * uContrast + 0.5, 0.0, 1.0);\n fragColor = vec4(oklabMix(uColorA, uColorB, smoothstep(0.0, 1.0, f)), 1.0);\n}`,\n ),\n};\n","/**\n * Sample pictures for the catalog's image effects: small SVGs as `data:` URLs,\n * so image controls have something to show with nothing to download.\n */\n\n/** An ASCII SVG as a base64 `data:image/svg+xml` URL. */\nexport function svgDataUrl(svg: string): string {\n const base64 =\n typeof btoa === \"function\" ? btoa(svg) : (globalThis as { Buffer?: { from(s: string): { toString(e: string): string } } }).Buffer!.from(svg).toString(\"base64\");\n return `data:image/svg+xml;base64,${base64}`;\n}\n\nconst LANDSCAPE = `<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 640 360\">\n<defs><linearGradient id=\"s\" x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\"><stop offset=\"0\" stop-color=\"#3b4cb8\"/><stop offset=\"1\" stop-color=\"#ffa36b\"/></linearGradient></defs>\n<rect width=\"640\" height=\"360\" fill=\"url(#s)\"/>\n<circle cx=\"420\" cy=\"170\" r=\"62\" fill=\"#ffe9b0\"/>\n<path d=\"M0 250 Q120 190 240 240 T480 230 T640 220 V360 H0Z\" fill=\"#2c4a52\"/>\n<path d=\"M0 290 Q160 240 320 290 T640 280 V360 H0Z\" fill=\"#1b2f33\"/>\n<path d=\"M0 330 Q200 300 400 330 T640 325 V360 H0Z\" fill=\"#0f1c1f\"/>\n</svg>`;\n\nconst CITY = `<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 640 360\">\n<rect width=\"640\" height=\"360\" fill=\"#0d1028\"/>\n<circle cx=\"520\" cy=\"80\" r=\"34\" fill=\"#f5f3d7\"/>\n<g fill=\"#252a52\"><rect x=\"20\" y=\"170\" width=\"70\" height=\"190\"/><rect x=\"110\" y=\"120\" width=\"60\" height=\"240\"/><rect x=\"190\" y=\"200\" width=\"80\" height=\"160\"/><rect x=\"290\" y=\"90\" width=\"55\" height=\"270\"/><rect x=\"365\" y=\"160\" width=\"90\" height=\"200\"/><rect x=\"475\" y=\"130\" width=\"60\" height=\"230\"/><rect x=\"555\" y=\"190\" width=\"70\" height=\"170\"/></g>\n<g fill=\"#ffcf5a\"><rect x=\"125\" y=\"140\" width=\"8\" height=\"8\"/><rect x=\"150\" y=\"170\" width=\"8\" height=\"8\"/><rect x=\"305\" y=\"110\" width=\"8\" height=\"8\"/><rect x=\"320\" y=\"150\" width=\"8\" height=\"8\"/><rect x=\"380\" y=\"180\" width=\"8\" height=\"8\"/><rect x=\"420\" y=\"220\" width=\"8\" height=\"8\"/><rect x=\"490\" y=\"150\" width=\"8\" height=\"8\"/><rect x=\"40\" y=\"200\" width=\"8\" height=\"8\"/><rect x=\"215\" y=\"230\" width=\"8\" height=\"8\"/><rect x=\"575\" y=\"215\" width=\"8\" height=\"8\"/></g>\n</svg>`;\n\nconst PORTRAIT = `<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 640 360\">\n<rect width=\"640\" height=\"360\" fill=\"#f2e6d8\"/>\n<circle cx=\"320\" cy=\"150\" r=\"70\" fill=\"#c98b6b\"/>\n<path d=\"M190 360 Q200 240 320 240 Q440 240 450 360Z\" fill=\"#33415c\"/>\n<path d=\"M250 130 Q260 60 320 62 Q385 60 392 135 Q370 100 320 100 Q275 100 250 130Z\" fill=\"#2b1d16\"/>\n<circle cx=\"295\" cy=\"150\" r=\"6\" fill=\"#2b1d16\"/><circle cx=\"345\" cy=\"150\" r=\"6\" fill=\"#2b1d16\"/>\n<path d=\"M300 185 Q320 198 340 185\" stroke=\"#7a3e2c\" stroke-width=\"5\" fill=\"none\"/>\n</svg>`;\n\n/** Built-in sample pictures, as image control values. */\nexport const SAMPLE_IMAGES = {\n landscape: svgDataUrl(LANDSCAPE),\n city: svgDataUrl(CITY),\n portrait: svgDataUrl(PORTRAIT),\n} as const;\n","import { getTransitionGLSL, type TransitionType } from \"../../shader/transition.js\";\nimport { glsl, HASH, NOISE } from \"../glsl.js\";\nimport { SAMPLE_IMAGES } from \"../images.js\";\nimport type { ShaderEffect } from \"../types.js\";\n\n/**\n * `picture(tex, uv)`: the image, or a test card while there is none (an empty\n * image control is bound to a 1 × 1 placeholder), so effects show something\n * before an image is chosen.\n */\nconst PICTURE = `\nvec3 testCard(vec2 uv) {\n vec3 sky = mix(vec3(0.98, 0.62, 0.42), vec3(0.24, 0.32, 0.72), uv.y);\n float sun = smoothstep(0.16, 0.15, length((uv - vec2(0.65, 0.58)) * vec2(1.6, 1.0)));\n float hills = step(uv.y, 0.32 + 0.06 * sin(uv.x * 9.0) + 0.04 * sin(uv.x * 23.0 + 1.0));\n vec3 col = mix(sky, vec3(1.0, 0.92, 0.7), sun);\n col = mix(col, vec3(0.12, 0.28, 0.24) + 0.08 * sin(uv.x * 40.0), hills);\n vec2 grid = abs(fract(uv * 8.0) - 0.5);\n col += 0.04 * step(0.48, max(grid.x, grid.y));\n return col;\n}\nvec3 picture(sampler2D tex, vec2 uv) {\n return textureSize(tex, 0).x > 1 ? texture(tex, uv).rgb : testCard(fract(uv));\n}`.trim();\n\nexport const imageRipple: ShaderEffect = {\n kind: \"shader\",\n id: \"image-ripple\",\n name: \"Ripple\",\n description: \"An image rippling as if seen through water, from the pointer or the centre.\",\n category: \"image\",\n tags: [\"image\", \"water\", \"interactive\", \"distortion\"],\n source: glsl(\n `// Ripple: an image rippling as if under water.\nuniform sampler2D uImage; // @control filter=linear wrap=mirror default=${SAMPLE_IMAGES.landscape} label=\"Image\"\nuniform float uAmplitude; // @control min=0 max=0.05 default=0.012 label=\"Amplitude\"\nuniform float uFrequency; // @control min=5 max=80 default=30 label=\"Frequency\"\nuniform float uSpeed; // @control min=0 max=6 default=2 label=\"Speed\"`,\n PICTURE,\n `void mainImage(out vec4 fragColor, in vec2 fragCoord) {\n vec2 uv = fragCoord / iResolution.xy;\n vec2 centre = (iMouse.z > 0.0 ? iMouse.xy : 0.5 * iResolution.xy) / iResolution.xy;\n vec2 d = uv - centre;\n d.x *= iResolution.x / iResolution.y;\n float r = length(d);\n float wave = sin(r * uFrequency - iTime * uSpeed) * smoothstep(0.9, 0.0, r);\n vec2 offset = normalize(d + 1e-5) * wave * uAmplitude;\n vec3 col = picture(uImage, uv + offset);\n col += 0.6 * max(wave, 0.0) * uAmplitude * 8.0;\n fragColor = vec4(col, 1.0);\n}`,\n ),\n};\n\nexport const imageGlitch: ShaderEffect = {\n kind: \"shader\",\n id: \"image-glitch\",\n name: \"Glitch\",\n description: \"Digital glitch on an image: RGB split, slice displacement and blocky corruption in bursts.\",\n category: \"image\",\n tags: [\"image\", \"glitch\", \"cyber\", \"transition\"],\n source: glsl(\n `// Glitch: RGB split, slice displacement and blocky corruption in bursts.\nuniform sampler2D uImage; // @control filter=linear wrap=clamp default=${SAMPLE_IMAGES.landscape} label=\"Image\"\nuniform float uAmount; // @control min=0 max=1 default=0.5 label=\"Amount\"\nuniform float uSplit; // @control min=0 max=0.05 default=0.012 label=\"RGB split\"\nuniform float uRate; // @control min=0.5 max=12 default=4 label=\"Bursts per second\"`,\n HASH,\n PICTURE,\n `void mainImage(out vec4 fragColor, in vec2 fragCoord) {\n vec2 uv = fragCoord / iResolution.xy;\n float tick = floor(iTime * uRate);\n float burst = step(1.0 - uAmount * 0.6, hash12(vec2(tick, 1.0)));\n float slice = floor(uv.y * mix(8.0, 40.0, hash12(vec2(tick, 2.0))));\n float shift = (hash12(vec2(slice, tick)) - 0.5) * 0.15 * burst * step(0.6, hash12(vec2(slice, tick + 3.0)));\n vec2 block = floor(uv * vec2(16.0, 9.0));\n float corrupt = burst * step(0.97 - uAmount * 0.05, hash12(block + tick));\n vec2 st = uv + vec2(shift, 0.0);\n st = mix(st, floor(st * 24.0) / 24.0, corrupt);\n float split = uSplit * (0.3 + burst);\n vec3 col = vec3(\n picture(uImage, st + vec2(split, 0.0)).r,\n picture(uImage, st).g,\n picture(uImage, st - vec2(split, 0.0)).b\n );\n col = mix(col, col.bgr, corrupt);\n col *= 0.92 + 0.08 * sin(fragCoord.y * 2.0);\n fragColor = vec4(col, 1.0);\n}`,\n ),\n};\n\nexport const imageDuotone: ShaderEffect = {\n kind: \"shader\",\n id: \"image-duotone\",\n name: \"Duotone\",\n description: \"An image mapped onto two colours, with a slow light sweep.\",\n category: \"image\",\n tags: [\"image\", \"brand\", \"editorial\", \"colour\"],\n source: glsl(\n `// Duotone: an image mapped onto two colours, with a light sweep.\nuniform sampler2D uImage; // @control filter=linear wrap=clamp default=${SAMPLE_IMAGES.landscape} label=\"Image\"\nuniform vec3 uShadow; // @control color default=#1E1B4B label=\"Shadows\"\nuniform vec3 uHighlight; // @control color default=#FB7185 label=\"Highlights\"\nuniform float uContrast; // @control min=0.5 max=3 default=1.2 label=\"Contrast\"\nuniform float uSweep; // @control min=0 max=1 default=0.3 label=\"Light sweep\"`,\n PICTURE,\n `void mainImage(out vec4 fragColor, in vec2 fragCoord) {\n vec2 uv = fragCoord / iResolution.xy;\n vec3 src = picture(uImage, uv);\n float l = dot(src, vec3(0.2126, 0.7152, 0.0722));\n l = clamp((l - 0.5) * uContrast + 0.5, 0.0, 1.0);\n float sweep = exp(-pow((uv.x + uv.y * 0.4 - fract(iTime * 0.15) * 2.0 + 0.4) * 6.0, 2.0));\n vec3 col = mix(uShadow, uHighlight, l) + sweep * uSweep;\n fragColor = vec4(col, 1.0);\n}`,\n ),\n presets: [\n { name: \"Spotify\", values: { uShadow: [0.07, 0.1, 0.35], uHighlight: [0.12, 0.84, 0.38] } },\n { name: \"Newsprint\", values: { uShadow: [0.1, 0.1, 0.1], uHighlight: [0.96, 0.94, 0.88] } },\n ],\n};\n\nexport const imagePixelate: ShaderEffect = {\n kind: \"shader\",\n id: \"image-pixelate\",\n name: \"Pixel reveal\",\n description: \"An image resolving from big blocks to full detail, then back, in a loop.\",\n category: \"image\",\n tags: [\"image\", \"reveal\", \"transition\", \"8-bit\"],\n loop: 4,\n source: glsl(\n `// Pixel reveal: an image resolving from blocks to full detail and back.\nuniform sampler2D uImage; // @control filter=nearest wrap=clamp default=${SAMPLE_IMAGES.landscape} label=\"Image\"\nuniform float uMaxBlock; // @control min=4 max=128 default=48 label=\"Largest block (px)\"\nuniform bool uAnimate; // @control default=true label=\"Animate\"\nuniform float uProgress; // @control min=0 max=1 default=1 label=\"Detail (when still)\"`,\n PICTURE,\n `void mainImage(out vec4 fragColor, in vec2 fragCoord) {\n float k = uAnimate ? 0.5 - 0.5 * cos(iTime * 6.28318 / 4.0) : uProgress;\n float block = max(1.0, floor(mix(uMaxBlock, 1.0, k)));\n vec2 cell = (floor(fragCoord / block) + 0.5) * block;\n vec3 col = picture(uImage, cell / iResolution.xy);\n fragColor = vec4(col, 1.0);\n}`,\n ),\n};\n\nexport const imageHalftone: ShaderEffect = {\n kind: \"shader\",\n id: \"image-halftone\",\n name: \"Halftone\",\n description: \"An image printed as rotated halftone dots, breathing slightly.\",\n category: \"image\",\n tags: [\"image\", \"print\", \"comic\", \"pop-art\"],\n source: glsl(\n `// Halftone: an image as rotated print dots.\nuniform sampler2D uImage; // @control filter=linear wrap=clamp default=${SAMPLE_IMAGES.landscape} label=\"Image\"\nuniform vec3 uInk; // @control color default=#111827 label=\"Ink\"\nuniform vec3 uPaper; // @control color default=#FEF3C7 label=\"Paper\"\nuniform float uSpacing; // @control min=3 max=30 default=8 label=\"Dot spacing (px)\"\nuniform float uAngle; // @control min=0 max=90 default=45 label=\"Screen angle (°)\"`,\n PICTURE,\n `void mainImage(out vec4 fragColor, in vec2 fragCoord) {\n float a = radians(uAngle);\n mat2 r = mat2(cos(a), sin(a), -sin(a), cos(a));\n vec2 q = r * fragCoord / uSpacing;\n vec2 cell = floor(q) + 0.5;\n vec2 centre = transpose(r) * cell * uSpacing;\n float l = dot(picture(uImage, centre / iResolution.xy), vec3(0.2126, 0.7152, 0.0722));\n float radius = sqrt(1.0 - l) * 0.62 * (0.95 + 0.05 * sin(iTime * 2.0));\n float d = length(q - cell);\n float ink = smoothstep(radius + 0.06, radius - 0.06, d);\n fragColor = vec4(mix(uPaper, uInk, ink), 1.0);\n}`,\n ),\n};\n\nexport const crt: ShaderEffect = {\n kind: \"shader\",\n id: \"crt\",\n name: \"CRT screen\",\n description: \"An image on a curved CRT: scanlines, shadow mask, bloom and flicker.\",\n category: \"image\",\n tags: [\"image\", \"retro\", \"screen\", \"arcade\"],\n source: glsl(\n `// CRT screen: an image on a curved tube with scanlines and a shadow mask.\nuniform sampler2D uImage; // @control filter=linear wrap=clamp default=${SAMPLE_IMAGES.landscape} label=\"Image\"\nuniform float uCurve; // @control min=0 max=0.5 default=0.15 label=\"Curvature\"\nuniform float uScanlines; // @control min=0 max=1 default=0.5 label=\"Scanlines\"\nuniform float uMask; // @control min=0 max=1 default=0.35 label=\"Shadow mask\"\nuniform float uFlicker; // @control min=0 max=0.2 default=0.03 label=\"Flicker\"`,\n HASH,\n PICTURE,\n `void mainImage(out vec4 fragColor, in vec2 fragCoord) {\n vec2 uv = fragCoord / iResolution.xy * 2.0 - 1.0;\n uv *= 1.0 + uCurve * dot(uv.yx, uv.yx) * 0.5;\n vec2 st = uv * 0.5 + 0.5;\n if (st.x < 0.0 || st.x > 1.0 || st.y < 0.0 || st.y > 1.0) {\n fragColor = vec4(0.0, 0.0, 0.0, 1.0);\n return;\n }\n vec3 col = picture(uImage, st);\n col += 0.25 * picture(uImage, st + vec2(0.003, 0.0)) * col;\n float scan = 1.0 - uScanlines * 0.5 * (1.0 - cos(st.y * iResolution.y * 3.14159));\n float m = mod(fragCoord.x, 3.0);\n vec3 mask = mix(vec3(1.0), vec3(m < 1.0 ? 1.2 : 0.8, m >= 1.0 && m < 2.0 ? 1.2 : 0.8, m >= 2.0 ? 1.2 : 0.8), uMask);\n col *= scan * mask;\n col *= 1.0 - uFlicker * hash12(vec2(floor(iTime * 24.0), 0.0));\n col *= smoothstep(1.0, 0.7, max(abs(uv.x), abs(uv.y)) * 0.98 + 0.02 * length(uv));\n fragColor = vec4(col, 1.0);\n}`,\n ),\n};\n\nexport const filmGrain: ShaderEffect = {\n kind: \"shader\",\n id: \"film-look\",\n name: \"Film look\",\n description: \"An image graded like film: grain, gate weave, halation and vignette.\",\n category: \"image\",\n tags: [\"image\", \"cinematic\", \"grain\", \"vintage\"],\n source: glsl(\n `// Film look: grain, gate weave, halation and vignette.\nuniform sampler2D uImage; // @control filter=linear wrap=clamp default=${SAMPLE_IMAGES.landscape} label=\"Image\"\nuniform float uGrain; // @control min=0 max=0.3 default=0.08 label=\"Grain\"\nuniform float uWeave; // @control min=0 max=0.01 default=0.0015 label=\"Gate weave\"\nuniform float uHalation; // @control min=0 max=1 default=0.3 label=\"Halation\"\nuniform float uVignette; // @control min=0 max=1 default=0.45 label=\"Vignette\"\nuniform vec3 uTint; // @control color default=#FFF1DC label=\"Print tint\"`,\n HASH,\n NOISE,\n PICTURE,\n `void mainImage(out vec4 fragColor, in vec2 fragCoord) {\n vec2 uv = fragCoord / iResolution.xy;\n float frame = floor(iTime * 24.0);\n uv += uWeave * (vec2(vnoise(vec2(frame * 0.3, 0.0)), vnoise(vec2(0.0, frame * 0.3))) - 0.5);\n vec3 col = picture(uImage, uv);\n vec3 blur = (picture(uImage, uv + vec2(0.006, 0.0)) + picture(uImage, uv - vec2(0.006, 0.0)) +\n picture(uImage, uv + vec2(0.0, 0.006)) + picture(uImage, uv - vec2(0.0, 0.006))) * 0.25;\n float bright = smoothstep(0.6, 1.0, dot(blur, vec3(0.333)));\n col += vec3(1.0, 0.3, 0.1) * bright * uHalation * 0.5;\n col *= uTint;\n col += (hash12(fragCoord + frame * 31.7) - 0.5) * uGrain;\n col *= 1.0 - uVignette * smoothstep(0.3, 0.9, length(uv - 0.5) * 1.3);\n fragColor = vec4(col, 1.0);\n}`,\n ),\n};\n\nconst TRANSITIONS: { type: TransitionType; id: string; name: string; description: string }[] = [\n { type: \"linearWipe\", id: \"wipe\", name: \"Wipe\", description: \"A soft-edged wipe from one image to the next.\" },\n { type: \"radialIris\", id: \"iris\", name: \"Iris\", description: \"A circle opening from the centre onto the next image.\" },\n { type: \"blockDissolve\", id: \"block-dissolve\", name: \"Block dissolve\", description: \"Random blocks switching to the next image.\" },\n { type: \"gridWipe\", id: \"grid-wipe\", name: \"Grid wipe\", description: \"A diagonal cascade of cells revealing the next image.\" },\n { type: \"glassWipe\", id: \"glass-wipe\", name: \"Glass wipe\", description: \"A refracting glass edge sweeping across.\" },\n { type: \"twister\", id: \"twister\", name: \"Twister\", description: \"A swirl that twists one image into the next.\" },\n { type: \"lightLeak\", id: \"light-leak\", name: \"Light leak\", description: \"A warm flare of light burning through to the next image.\" },\n { type: \"crossZoom\", id: \"cross-zoom\", name: \"Cross zoom\", description: \"A zoom blur punching through to the next image.\" },\n];\n\n/** The transitions (see `getTransitionGLSL`) as looping effects between two images. */\nexport const transitions: ShaderEffect[] = TRANSITIONS.map(({ type, id, name, description }) => ({\n kind: \"shader\",\n id: `transition-${id}`,\n name,\n description: `${description} Loops back and forth; or turn Animate off and set Progress.`,\n category: \"transition\",\n tags: [\"transition\", \"image\", \"slideshow\"],\n loop: 4,\n source: glsl(\n `// ${name} transition between two images. Progress runs 0 → 1 → 0 when animated.\nuniform sampler2D uFrom; // @control filter=linear wrap=clamp default=${SAMPLE_IMAGES.landscape} label=\"From image\"\nuniform sampler2D uTo; // @control filter=linear wrap=clamp default=${SAMPLE_IMAGES.city} label=\"To image\"\nuniform bool uAnimate; // @control default=true label=\"Animate\"\nuniform float uProgress; // @control min=0 max=1 default=0.5 label=\"Progress (when still)\"`,\n getTransitionGLSL(type),\n `void mainImage(out vec4 fragColor, in vec2 fragCoord) {\n vec2 uv = fragCoord / iResolution.xy;\n float p = uAnimate ? smoothstep(0.1, 0.9, 0.5 - 0.5 * cos(iTime * 6.28318 / 4.0)) : uProgress;\n fragColor = vec4(flair_transition(uFrom, uTo, uv, p).rgb, 1.0);\n}`,\n ),\n}));\n","import { FBM, glsl, HASH, NOISE, ROTATE } from \"../glsl.js\";\nimport type { ShaderEffect } from \"../types.js\";\n\nexport const bokeh: ShaderEffect = {\n kind: \"shader\",\n id: \"bokeh\",\n name: \"Bokeh\",\n description: \"Out-of-focus city lights drifting softly.\",\n category: \"light\",\n tags: [\"background\", \"festive\", \"dreamy\", \"night\"],\n source: glsl(\n `// Bokeh: soft out-of-focus lights drifting.\nuniform vec3 uBackground; // @control color default=#0B1026 label=\"Background\"\nuniform vec3 uWarm; // @control color default=#FFC56E label=\"Warm light\"\nuniform vec3 uCool; // @control color default=#6EC8FF label=\"Cool light\"\nuniform int uCount; // @control min=4 max=60 default=30 label=\"Lights\"\nuniform float uSize; // @control min=0.02 max=0.25 default=0.08 label=\"Size\"\nuniform float uSpeed; // @control min=0 max=1 default=0.15 label=\"Drift\"`,\n HASH,\n `void mainImage(out vec4 fragColor, in vec2 fragCoord) {\n vec2 p = fragCoord / iResolution.y;\n float aspect = iResolution.x / iResolution.y;\n vec3 col = uBackground * (0.7 + 0.3 * fragCoord.y / iResolution.y);\n for (int i = 0; i < 60; i++) {\n if (i >= uCount) break;\n float fi = float(i);\n vec2 h = hash22(vec2(fi, 3.1));\n float depth = hash12(vec2(fi, 9.7));\n vec2 c = vec2(fract(h.x + iTime * uSpeed * (0.2 + depth) * 0.1) * (aspect + 0.4) - 0.2, h.y);\n c.y += 0.03 * sin(iTime * 0.5 + fi);\n float r = uSize * (0.5 + depth);\n float d = length(p - c);\n float disc = smoothstep(r, r * 0.85, d) * (0.6 + 0.4 * smoothstep(r * 0.4, r, d));\n vec3 light = mix(uWarm, uCool, step(0.5, hash12(vec2(fi, 1.3))));\n col += light * disc * (0.15 + 0.25 * (1.0 - depth));\n }\n fragColor = vec4(col, 1.0);\n}`,\n ),\n};\n\nexport const godRays: ShaderEffect = {\n kind: \"shader\",\n id: \"god-rays\",\n name: \"God rays\",\n description: \"Shafts of light fanning down through haze.\",\n category: \"light\",\n tags: [\"background\", \"hero\", \"heavenly\", \"spotlight\"],\n source: glsl(\n `// God rays: shafts of light fanning through haze.\nuniform vec3 uBackground; // @control color default=#0C0A09 label=\"Background\"\nuniform vec3 uLight; // @control color default=#FDE68A label=\"Light\"\nuniform vec2 uSource; // @control min=-1 max=1 default=0,0.6 label=\"Source\"\nuniform float uRays; // @control min=4 max=60 default=18 label=\"Rays\"\nuniform float uSpeed; // @control min=0 max=2 default=0.3 label=\"Speed\"\nuniform float uStrength; // @control min=0 max=2 default=1 label=\"Strength\"`,\n HASH,\n NOISE,\n `void mainImage(out vec4 fragColor, in vec2 fragCoord) {\n vec2 p = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;\n vec2 d = p - uSource * vec2(iResolution.x / iResolution.y, 1.0) * 0.5;\n float a = atan(d.x, -d.y);\n float r = length(d);\n float t = iTime * uSpeed;\n float rays = vnoise(vec2(a * uRays, t)) * 0.6 + vnoise(vec2(a * uRays * 2.3, -t * 1.3)) * 0.4;\n rays = pow(rays, 2.0) * smoothstep(1.6, 0.0, r);\n float glow = exp(-r * 4.0);\n vec3 col = uBackground + uLight * (rays * 0.9 + glow * 0.8) * uStrength;\n fragColor = vec4(col, 1.0);\n}`,\n ),\n};\n\nexport const sparkles: ShaderEffect = {\n kind: \"shader\",\n id: \"sparkles\",\n name: \"Sparkles\",\n description: \"Four-pointed glints twinkling on and off across the frame.\",\n category: \"light\",\n tags: [\"overlay\", \"magic\", \"celebration\", \"premium\"],\n source: glsl(\n `// Sparkles: four-pointed glints twinkling across the frame.\nuniform vec3 uBackground; // @control color default=#1E1B4B label=\"Background\"\nuniform vec3 uSparkle; // @control color default=#FFFFFF label=\"Sparkle\"\nuniform float uDensity; // @control min=2 max=30 default=10 label=\"Density\"\nuniform float uSize; // @control min=0.05 max=0.5 default=0.35 label=\"Size\"\nuniform float uSpeed; // @control min=0 max=4 default=1 label=\"Twinkle speed\"`,\n HASH,\n `void mainImage(out vec4 fragColor, in vec2 fragCoord) {\n vec2 p = fragCoord / iResolution.y * uDensity;\n vec3 col = uBackground;\n for (int layer = 0; layer < 2; layer++) {\n vec2 q = p * (1.0 + float(layer) * 0.6) + float(layer) * 7.3;\n vec2 id = floor(q);\n vec2 f = fract(q) - 0.5;\n vec2 o = hash22(id) - 0.5;\n vec2 d = f - o * 0.6;\n float phase = hash12(id + 4.0) * 6.28318;\n float tw = pow(max(0.0, sin(iTime * uSpeed * 2.0 + phase)), 4.0);\n float r = length(d);\n float rays = max(0.0, 1.0 - abs(d.x * d.y) * 3000.0);\n float core = 0.01 / (r + 0.01);\n float star = (rays * 0.8 + core) * smoothstep(uSize, 0.0, r) * tw;\n col += uSparkle * star;\n }\n fragColor = vec4(col, 1.0);\n}`,\n ),\n};\n\nexport const neonRipple: ShaderEffect = {\n kind: \"shader\",\n id: \"neon-ripple\",\n name: \"Neon ripple\",\n description: \"Glowing neon rings radiating out from the pointer or the centre.\",\n category: \"light\",\n tags: [\"interactive\", \"glow\", \"music\", \"pulse\"],\n loop: 2,\n source: glsl(\n `// Neon ripple: glowing rings radiating from the pointer (or the centre).\nuniform vec3 uBackground; // @control color default=#09090B label=\"Background\"\nuniform vec3 uNeon; // @control color default=#FF2BD6 label=\"Neon\"\nuniform float uRings; // @control min=1 max=10 default=4 label=\"Rings\"\nuniform float uWidth; // @control min=0.002 max=0.05 default=0.01 label=\"Width\"\nuniform float uGlow; // @control min=0 max=3 default=1 label=\"Glow\"`,\n `void mainImage(out vec4 fragColor, in vec2 fragCoord) {\n vec2 centre = iMouse.z > 0.0 ? iMouse.xy : 0.5 * iResolution.xy;\n float r = length(fragCoord - centre) / iResolution.y;\n float phase = fract(r * uRings - iTime * 0.5);\n float ringDist = min(phase, 1.0 - phase) / uRings;\n float fade = smoothstep(1.2, 0.0, r);\n float core = smoothstep(uWidth, 0.0, ringDist);\n float glow = uWidth * 2.0 / (ringDist + uWidth) * 0.25 * uGlow;\n vec3 col = uBackground + uNeon * (core + glow) * fade;\n fragColor = vec4(col, 1.0);\n}`,\n ),\n};\n\nexport const starfield: ShaderEffect = {\n kind: \"shader\",\n id: \"starfield\",\n name: \"Warp starfield\",\n description: \"Flying through a field of stars, from cruising to warp speed.\",\n category: \"space\",\n tags: [\"background\", \"space\", \"speed\", \"sci-fi\"],\n source: glsl(\n `// Warp starfield: flying through layers of stars.\nuniform vec3 uBackground; // @control color default=#000005 label=\"Background\"\nuniform vec3 uStar; // @control color default=#E0E7FF label=\"Star\"\nuniform float uSpeed; // @control min=0 max=4 default=0.6 label=\"Speed\"\nuniform float uStreak; // @control min=0 max=1 default=0.3 label=\"Streak\"\nuniform int uLayers; // @control min=1 max=8 default=5 label=\"Layers\"`,\n HASH,\n ROTATE,\n `void mainImage(out vec4 fragColor, in vec2 fragCoord) {\n vec2 p = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;\n vec3 col = uBackground;\n float n = float(uLayers);\n for (int i = 0; i < 8; i++) {\n if (i >= uLayers) break;\n float depth = fract(float(i) / n + iTime * uSpeed * 0.1);\n float scale = mix(20.0, 0.5, depth);\n float fade = depth * smoothstep(1.0, 0.85, depth);\n vec2 q = rot(float(i) * 1.7) * p * scale + float(i) * 13.1;\n vec2 id = floor(q);\n vec2 f = fract(q) - 0.5;\n vec2 o = hash22(id) - 0.5;\n vec2 d = f - o * 0.8;\n vec2 dir = normalize(p + 1e-4);\n float along = dot(d, dir);\n float across = length(d - dir * along);\n float len = 0.02 + uStreak * depth * 0.4;\n float star = smoothstep(0.03, 0.0, across) * smoothstep(len, 0.0, abs(along));\n star *= step(0.6, hash12(id + 2.0));\n col += uStar * star * fade * 1.5;\n }\n fragColor = vec4(col, 1.0);\n}`,\n ),\n};\n\nexport const nebula: ShaderEffect = {\n kind: \"shader\",\n id: \"nebula\",\n name: \"Nebula\",\n description: \"Glowing gas clouds and stars drifting in deep space.\",\n category: \"space\",\n tags: [\"background\", \"space\", \"cosmic\", \"hero\"],\n source: glsl(\n `// Nebula: glowing gas clouds over a field of stars.\nuniform vec3 uGasA; // @control color default=#7C3AED label=\"Gas A\"\nuniform vec3 uGasB; // @control color default=#EC4899 label=\"Gas B\"\nuniform vec3 uDeep; // @control color default=#020010 label=\"Deep space\"\nuniform float uSpeed; // @control min=0 max=1 default=0.05 label=\"Drift\"\nuniform float uDensity; // @control min=0 max=2 default=1 label=\"Density\"`,\n HASH,\n NOISE,\n FBM,\n `void mainImage(out vec4 fragColor, in vec2 fragCoord) {\n vec2 p = fragCoord / iResolution.y * 2.0;\n float t = iTime * uSpeed;\n vec2 q = vec2(fbm(p + t, 5), fbm(p - t + 3.3, 5));\n float gas = fbm(p * 1.3 + 2.5 * q, 6);\n float dust = smoothstep(0.45, 0.75, fbm(p * 3.0 - q, 5));\n vec3 col = uDeep;\n col += uGasA * smoothstep(0.35, 0.8, gas) * uDensity;\n col += uGasB * smoothstep(0.5, 0.9, gas * q.x * 1.6) * uDensity;\n col *= 1.0 - 0.6 * dust;\n float star = step(0.996, hash12(floor(fragCoord)));\n float bright = step(0.9993, hash12(floor(fragCoord * 0.5) + 7.0));\n col += vec3(star * 0.7 + bright * (0.8 + 0.2 * sin(iTime * 4.0 + fragCoord.x)));\n fragColor = vec4(col, 1.0);\n}`,\n ),\n};\n\nexport const blackHole: ShaderEffect = {\n kind: \"shader\",\n id: \"event-horizon\",\n name: \"Event horizon\",\n description: \"A glowing accretion disc swirling into a black hole.\",\n category: \"space\",\n tags: [\"space\", \"dramatic\", \"vortex\", \"hero\"],\n source: glsl(\n `// Event horizon: an accretion disc swirling into a black hole.\nuniform vec3 uHot; // @control color default=#FFE4B5 label=\"Hot\"\nuniform vec3 uDisc; // @control color default=#FF6A00 label=\"Disc\"\nuniform float uRadius; // @control min=0.05 max=0.4 default=0.15 label=\"Horizon\"\nuniform float uSpin; // @control min=0 max=4 default=1 label=\"Spin\"`,\n HASH,\n NOISE,\n FBM,\n `void mainImage(out vec4 fragColor, in vec2 fragCoord) {\n vec2 p = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;\n p.y *= 2.2;\n float r = length(p);\n float a = atan(p.y, p.x);\n float swirl = a + uSpin * iTime * 0.5 + 1.5 / (r + 0.05);\n float streaks = fbm(vec2(swirl * 3.0, r * 8.0), 5);\n float disc = smoothstep(uRadius, uRadius * 1.6, r) * smoothstep(1.0, uRadius * 1.6, r);\n vec3 col = mix(uDisc, uHot, smoothstep(0.5, 0.9, streaks) * smoothstep(0.6, 0.1, r));\n col *= disc * (0.4 + 1.2 * streaks);\n col += uHot * 0.6 * exp(-abs(r - uRadius * 1.05) * 60.0);\n col *= smoothstep(uRadius * 0.95, uRadius * 1.05, r);\n fragColor = vec4(col, 1.0);\n}`,\n ),\n};\n","import { FBM, glsl, HASH, NOISE, PALETTE } from \"../glsl.js\";\nimport type { ShaderEffect } from \"../types.js\";\n\nexport const marble: ShaderEffect = {\n kind: \"shader\",\n id: \"marble\",\n name: \"Liquid marble\",\n description: \"Veined marble swirling like ink in water (domain-warped noise).\",\n category: \"organic\",\n tags: [\"background\", \"ink\", \"liquid\", \"luxury\"],\n source: glsl(\n `// Liquid marble: domain-warped noise swirling like ink in water.\nuniform vec3 uStone; // @control color default=#F5F1E8 label=\"Stone\"\nuniform vec3 uVein; // @control color default=#1F2937 label=\"Vein\"\nuniform vec3 uAccent; // @control color default=#B08D57 label=\"Accent\"\nuniform float uSpeed; // @control min=0 max=1 default=0.15 label=\"Speed\"\nuniform float uScale; // @control min=0.5 max=6 default=2.5 label=\"Scale\"\nuniform float uWarp; // @control min=0 max=8 default=4 label=\"Warp\"`,\n HASH,\n NOISE,\n FBM,\n `void mainImage(out vec4 fragColor, in vec2 fragCoord) {\n vec2 p = fragCoord / iResolution.y * uScale;\n float t = iTime * uSpeed;\n vec2 q = vec2(fbm(p + vec2(0.0, t), 5), fbm(p + vec2(5.2, 1.3 - t), 5));\n vec2 r = vec2(fbm(p + uWarp * q + vec2(1.7, 9.2) + 0.3 * t, 5), fbm(p + uWarp * q + vec2(8.3, 2.8), 5));\n float f = fbm(p + uWarp * r, 5);\n float vein = 1.0 - smoothstep(0.0, 0.06, abs(sin(f * 12.0 + r.x * 6.0)));\n vec3 col = mix(uStone, uStone * 0.85, length(q) * 0.6);\n col = mix(col, uAccent, smoothstep(0.55, 0.9, r.y) * 0.5);\n col = mix(col, uVein, vein * 0.85);\n fragColor = vec4(col, 1.0);\n}`,\n ),\n presets: [\n { name: \"Nero\", values: { uStone: [0.08, 0.08, 0.09], uVein: [0.9, 0.88, 0.82], uAccent: [0.7, 0.55, 0.3] } },\n { name: \"Jade\", values: { uStone: [0.18, 0.45, 0.35], uVein: [0.85, 0.95, 0.88], uAccent: [0.05, 0.2, 0.15] } },\n ],\n};\n\nexport const smoke: ShaderEffect = {\n kind: \"shader\",\n id: \"smoke\",\n name: \"Smoke\",\n description: \"Billowing smoke or cloud drifting upwards.\",\n category: \"organic\",\n tags: [\"background\", \"atmosphere\", \"cloud\", \"mist\"],\n source: glsl(\n `// Smoke: billowing fractal noise drifting upwards.\nuniform vec3 uBackground; // @control color default=#0B0F19 label=\"Background\"\nuniform vec3 uSmoke; // @control color default=#CBD5E1 label=\"Smoke\"\nuniform float uSpeed; // @control min=0 max=2 default=0.3 label=\"Speed\"\nuniform float uDensity; // @control min=0 max=2 default=1 label=\"Density\"\nuniform int uDetail; // @control min=1 max=8 default=6 label=\"Detail\"`,\n HASH,\n NOISE,\n FBM,\n `void mainImage(out vec4 fragColor, in vec2 fragCoord) {\n vec2 p = fragCoord / iResolution.y * 3.0;\n float t = iTime * uSpeed;\n p.y -= t;\n float n = fbm(p + fbm(p + vec2(t * 0.3, 0.0), uDetail), uDetail);\n float body = smoothstep(0.35, 0.85, n) * uDensity;\n float fade = smoothstep(0.0, 0.7, fragCoord.y / iResolution.y + 0.2);\n vec3 col = mix(uBackground, uSmoke, clamp(body * fade, 0.0, 1.0));\n fragColor = vec4(col, 1.0);\n}`,\n ),\n};\n\nexport const lavaLamp: ShaderEffect = {\n kind: \"shader\",\n id: \"lava-lamp\",\n name: \"Lava lamp\",\n description: \"Soft metaballs merging and parting as they rise and fall.\",\n category: \"organic\",\n tags: [\"background\", \"playful\", \"blobs\", \"retro\"],\n source: glsl(\n `// Lava lamp: metaballs merging as they rise and fall.\nuniform vec3 uBackground; // @control color default=#1A0B2E label=\"Background\"\nuniform vec3 uBlobA; // @control color default=#FF5E8A label=\"Blob A\"\nuniform vec3 uBlobB; // @control color default=#FFB347 label=\"Blob B\"\nuniform int uCount; // @control min=2 max=12 default=7 label=\"Blobs\"\nuniform float uSpeed; // @control min=0 max=2 default=0.5 label=\"Speed\"\nuniform float uSize; // @control min=0.02 max=0.3 default=0.12 label=\"Size\"`,\n HASH,\n `void mainImage(out vec4 fragColor, in vec2 fragCoord) {\n vec2 p = fragCoord / iResolution.y;\n float aspect = iResolution.x / iResolution.y;\n float t = iTime * uSpeed;\n float field = 0.0;\n float tint = 0.0;\n for (int i = 0; i < 12; i++) {\n if (i >= uCount) break;\n float fi = float(i);\n vec2 seed = hash22(vec2(fi, 7.0));\n vec2 c = vec2(seed.x * aspect, 0.5 + 0.45 * sin(t * (0.4 + seed.y) + fi * 2.1));\n c.x += 0.1 * sin(t * 0.7 + fi);\n float r = uSize * (0.7 + 0.6 * seed.y);\n float k = r * r / dot(p - c, p - c);\n field += k;\n tint += k * seed.x;\n }\n float inside = smoothstep(0.9, 1.1, field);\n vec3 blob = mix(uBlobA, uBlobB, clamp(tint / max(field, 1e-3), 0.0, 1.0));\n blob *= 0.85 + 0.15 * smoothstep(1.0, 3.0, field);\n vec3 col = mix(uBackground, blob, inside);\n col += blob * 0.15 * smoothstep(0.3, 0.9, field) * (1.0 - inside);\n fragColor = vec4(col, 1.0);\n}`,\n ),\n};\n\nexport const plasma: ShaderEffect = {\n kind: \"shader\",\n id: \"plasma\",\n name: \"Plasma\",\n description: \"The demoscene classic: interfering sine waves through a colour palette.\",\n category: \"organic\",\n tags: [\"background\", \"retro\", \"psychedelic\", \"loop\"],\n loop: 6.28318,\n source: glsl(\n `// Plasma: interfering sine waves through a cosine palette.\nuniform float uScale; // @control min=1 max=20 default=8 label=\"Scale\"\nuniform float uShift; // @control min=0 max=1 default=0 label=\"Hue shift\"\nuniform vec3 uPhase; // @control min=0 max=1 default=0,0.33,0.67 label=\"Palette phase\"`,\n PALETTE,\n `void mainImage(out vec4 fragColor, in vec2 fragCoord) {\n vec2 p = (fragCoord - 0.5 * iResolution.xy) / iResolution.y * uScale;\n float t = iTime;\n float v = sin(p.x + t);\n v += sin((p.y + t) * 0.5);\n v += sin((p.x + p.y + t) * 0.5);\n vec2 c = p + vec2(sin(t * 0.5), cos(t * 0.5)) * uScale * 0.3;\n v += sin(sqrt(dot(c, c) + 1.0) + t);\n vec3 col = palette(v * 0.25 + uShift, vec3(0.5), vec3(0.5), vec3(1.0), uPhase);\n fragColor = vec4(col, 1.0);\n}`,\n ),\n};\n\nexport const caustics: ShaderEffect = {\n kind: \"shader\",\n id: \"caustics\",\n name: \"Caustics\",\n description: \"Light dancing on the floor of a swimming pool.\",\n category: \"organic\",\n tags: [\"background\", \"water\", \"summer\", \"calm\"],\n source: glsl(\n `// Caustics: light dancing on the floor of a pool.\nuniform vec3 uWater; // @control color default=#0369A1 label=\"Water\"\nuniform vec3 uLight; // @control color default=#E0F2FE label=\"Light\"\nuniform float uSpeed; // @control min=0 max=2 default=0.5 label=\"Speed\"\nuniform float uScale; // @control min=1 max=10 default=4 label=\"Scale\"\nuniform float uBright; // @control min=0 max=2 default=1 label=\"Brightness\"`,\n `void mainImage(out vec4 fragColor, in vec2 fragCoord) {\n // After Dave Hoskins' \"Tileable water caustic\".\n vec2 uv = fragCoord / iResolution.y * uScale * 0.25;\n float t = iTime * uSpeed * 0.5 + 23.0;\n vec2 p = mod(uv * 6.28318, 6.28318) - 250.0;\n vec2 i = p;\n float c = 1.0;\n float inten = 0.005;\n for (int n = 0; n < 5; n++) {\n float tn = t * (1.0 - 3.5 / float(n + 1));\n i = p + vec2(cos(tn - i.x) + sin(tn + i.y), sin(tn - i.y) + cos(tn + i.x));\n c += 1.0 / length(vec2(p.x / (sin(i.x + tn) / inten), p.y / (cos(i.y + tn) / inten)));\n }\n c /= 5.0;\n c = 1.17 - pow(c, 1.4);\n float light = pow(abs(c), 8.0) * uBright;\n vec3 col = uWater + uLight * clamp(light, 0.0, 1.5);\n fragColor = vec4(col, 1.0);\n}`,\n ),\n};\n\nexport const flames: ShaderEffect = {\n kind: \"shader\",\n id: \"flames\",\n name: \"Flames\",\n description: \"A wall of fire licking upwards from the bottom edge.\",\n category: \"organic\",\n tags: [\"fire\", \"energy\", \"hot\", \"overlay\"],\n source: glsl(\n `// Flames: a wall of fire licking upwards.\nuniform vec3 uHot; // @control color default=#FFF3B0 label=\"Hot\"\nuniform vec3 uMid; // @control color default=#FF7A00 label=\"Mid\"\nuniform vec3 uCool; // @control color default=#7A0000 label=\"Cool\"\nuniform float uSpeed; // @control min=0 max=3 default=1.2 label=\"Speed\"\nuniform float uHeight; // @control min=0.1 max=1.5 default=0.7 label=\"Height\"`,\n HASH,\n NOISE,\n FBM,\n `void mainImage(out vec4 fragColor, in vec2 fragCoord) {\n vec2 uv = fragCoord / iResolution.xy;\n vec2 p = vec2(fragCoord.x / iResolution.y, uv.y);\n float t = iTime * uSpeed;\n float n = fbm(vec2(p.x * 4.0, p.y * 3.0 - t * 1.5), 5);\n float heat = (1.0 - uv.y / uHeight) + (n - 0.5) * 1.1;\n heat = clamp(heat, 0.0, 1.0);\n vec3 col = mix(vec3(0.0), uCool, smoothstep(0.05, 0.3, heat));\n col = mix(col, uMid, smoothstep(0.3, 0.6, heat));\n col = mix(col, uHot, smoothstep(0.6, 0.95, heat));\n fragColor = vec4(col, 1.0);\n}`,\n ),\n presets: [\n { name: \"Spirit\", values: { uHot: [0.85, 1, 1], uMid: [0.2, 0.6, 1], uCool: [0.05, 0.05, 0.4] } },\n { name: \"Toxic\", values: { uHot: [0.95, 1, 0.7], uMid: [0.4, 0.95, 0.2], uCool: [0.05, 0.25, 0.05] } },\n ],\n};\n","import { glsl, HASH, ROTATE } from \"../glsl.js\";\nimport type { ShaderEffect } from \"../types.js\";\n\nexport const dotWave: ShaderEffect = {\n kind: \"shader\",\n id: \"dot-wave\",\n name: \"Dot wave\",\n description: \"A halftone grid of dots swelling in a travelling wave.\",\n category: \"pattern\",\n tags: [\"background\", \"halftone\", \"tech\", \"minimal\"],\n loop: 4,\n source: glsl(\n `// Dot wave: a grid of dots swelling in a travelling wave.\nuniform vec3 uBackground; // @control color default=#0F172A label=\"Background\"\nuniform vec3 uDot; // @control color default=#38BDF8 label=\"Dot\"\nuniform float uSpacing; // @control min=8 max=64 default=22 label=\"Spacing (px)\"\nuniform float uWaves; // @control min=0.5 max=6 default=2 label=\"Waves\"\nuniform bool uRadial; // @control default=true label=\"Radial\"`,\n `void mainImage(out vec4 fragColor, in vec2 fragCoord) {\n vec2 cell = floor(fragCoord / uSpacing);\n vec2 local = fract(fragCoord / uSpacing) - 0.5;\n vec2 centre = (cell + 0.5) * uSpacing;\n vec2 p = (centre - 0.5 * iResolution.xy) / iResolution.y;\n float d = uRadial ? length(p) : p.x + p.y * 0.3;\n float phase = d * uWaves * 6.28318 - iTime * 6.28318 / 4.0;\n float size = 0.1 + 0.32 * (0.5 + 0.5 * sin(phase));\n float aa = 1.5 / uSpacing;\n float disc = 1.0 - smoothstep(size - aa, size + aa, length(local));\n vec3 col = mix(uBackground, uDot * (0.6 + 0.4 * size / 0.42), disc);\n fragColor = vec4(col, 1.0);\n}`,\n ),\n};\n\nexport const truchet: ShaderEffect = {\n kind: \"shader\",\n id: \"truchet\",\n name: \"Truchet flow\",\n description: \"Quarter-circle Truchet tiles forming endless winding paths, with light flowing along them.\",\n category: \"pattern\",\n tags: [\"background\", \"maze\", \"geometric\", \"tech\"],\n source: glsl(\n `// Truchet flow: winding quarter-circle tiles with light flowing along them.\nuniform vec3 uBackground; // @control color default=#111827 label=\"Background\"\nuniform vec3 uLine; // @control color default=#F472B6 label=\"Line\"\nuniform float uTiles; // @control min=3 max=30 default=10 label=\"Tiles\"\nuniform float uWidth; // @control min=0.02 max=0.3 default=0.1 label=\"Line width\"\nuniform float uSpeed; // @control min=0 max=3 default=1 label=\"Flow speed\"`,\n HASH,\n `void mainImage(out vec4 fragColor, in vec2 fragCoord) {\n vec2 p = fragCoord / iResolution.y * uTiles;\n vec2 id = floor(p);\n vec2 f = fract(p) - 0.5;\n float flip = hash12(id) > 0.5 ? 1.0 : -1.0;\n f.x *= flip;\n vec2 c = f.x > -f.y ? vec2(0.5) : vec2(-0.5);\n vec2 q = f - c;\n float d = abs(length(q) - 0.5);\n float angle = atan(q.y, q.x);\n float checker = mod(id.x + id.y, 2.0) * 2.0 - 1.0;\n float flow = 0.5 + 0.5 * sin(angle * 4.0 * checker * flip + iTime * uSpeed * 4.0);\n float line = 1.0 - smoothstep(uWidth - 0.02, uWidth, d);\n vec3 col = mix(uBackground, uLine * (0.45 + 0.75 * flow), line);\n fragColor = vec4(col, 1.0);\n}`,\n ),\n};\n\nexport const hexPulse: ShaderEffect = {\n kind: \"shader\",\n id: \"hex-pulse\",\n name: \"Hex pulse\",\n description: \"A honeycomb grid lighting up cell by cell in ripples.\",\n category: \"pattern\",\n tags: [\"background\", \"tech\", \"honeycomb\", \"sci-fi\"],\n source: glsl(\n `// Hex pulse: a honeycomb grid lighting up in ripples.\nuniform vec3 uBackground; // @control color default=#020617 label=\"Background\"\nuniform vec3 uEdge; // @control color default=#22D3EE label=\"Edge\"\nuniform vec3 uFill; // @control color default=#0E7490 label=\"Fill\"\nuniform float uCells; // @control min=3 max=40 default=12 label=\"Cells\"\nuniform float uSpeed; // @control min=0 max=3 default=1 label=\"Speed\"`,\n HASH,\n `vec4 hexCoords(vec2 p) {\n vec2 r = vec2(1.0, 1.7320508);\n vec2 h = r * 0.5;\n vec2 a = mod(p, r) - h;\n vec2 b = mod(p - h, r) - h;\n vec2 gv = dot(a, a) < dot(b, b) ? a : b;\n return vec4(gv, p - gv);\n}\n\nvoid mainImage(out vec4 fragColor, in vec2 fragCoord) {\n vec2 p = (fragCoord - 0.5 * iResolution.xy) / iResolution.y * uCells;\n vec4 h = hexCoords(p);\n vec2 gv = abs(h.xy);\n float edgeDist = 0.5 - max(dot(gv, normalize(vec2(1.0, 1.7320508))), gv.x);\n float ripple = 0.5 + 0.5 * sin(length(h.zw) * 0.6 - iTime * uSpeed * 3.0);\n float spark = step(0.92, hash12(h.zw + floor(iTime * uSpeed * 2.0)));\n float lit = clamp(pow(ripple, 6.0) + spark * 0.6, 0.0, 1.0);\n vec3 col = mix(uBackground, uFill, lit * smoothstep(0.0, 0.1, edgeDist));\n col = mix(col, uEdge, (1.0 - smoothstep(0.0, 0.04, edgeDist)) * (0.3 + 0.7 * ripple));\n fragColor = vec4(col, 1.0);\n}`,\n ),\n};\n\nexport const moire: ShaderEffect = {\n kind: \"shader\",\n id: \"moire\",\n name: \"Moiré rings\",\n description: \"Two sets of concentric rings sliding over each other into shimmering interference.\",\n category: \"pattern\",\n tags: [\"background\", \"op-art\", \"hypnotic\", \"minimal\"],\n source: glsl(\n `// Moiré rings: two ring sets interfering as they slide.\nuniform vec3 uInk; // @control color default=#111111 label=\"Ink\"\nuniform vec3 uPaper; // @control color default=#F5F5F0 label=\"Paper\"\nuniform float uRings; // @control min=10 max=200 default=80 label=\"Rings\"\nuniform float uDrift; // @control min=0 max=0.5 default=0.15 label=\"Drift\"`,\n `void mainImage(out vec4 fragColor, in vec2 fragCoord) {\n vec2 p = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;\n vec2 o = uDrift * vec2(sin(iTime * 0.4), cos(iTime * 0.3));\n float a = sin(length(p - o) * uRings);\n float b = sin(length(p + o) * uRings);\n float v = smoothstep(-0.2, 0.2, a * b);\n fragColor = vec4(mix(uInk, uPaper, v), 1.0);\n}`,\n ),\n};\n\nexport const barberStripes: ShaderEffect = {\n kind: \"shader\",\n id: \"stripes\",\n name: \"Stripes\",\n description: \"Diagonal stripes marching across, for progress bars, banners and warnings.\",\n category: \"pattern\",\n tags: [\"progress\", \"banner\", \"warning\", \"loop\"],\n loop: 1,\n source: glsl(\n `// Stripes: diagonal stripes marching across.\nuniform vec3 uColorA; // @control color default=#FACC15 label=\"Colour A\"\nuniform vec3 uColorB; // @control color default=#18181B label=\"Colour B\"\nuniform float uWidth; // @control min=4 max=120 default=28 label=\"Stripe width (px)\"\nuniform float uAngle; // @control min=-90 max=90 default=45 label=\"Angle (°)\"\nuniform float uSoft; // @control min=0 max=1 default=0.05 label=\"Softness\"`,\n `void mainImage(out vec4 fragColor, in vec2 fragCoord) {\n float a = radians(uAngle);\n float d = dot(fragCoord, vec2(cos(a), sin(a))) / (uWidth * 2.0);\n float s = fract(d - iTime);\n float edge = max(uSoft * 0.5, 0.5 / uWidth);\n float m = smoothstep(0.5 - edge, 0.5 + edge, s) - smoothstep(1.0 - edge, 1.0, s) + (1.0 - smoothstep(0.0, edge, s));\n fragColor = vec4(mix(uColorA, uColorB, clamp(m, 0.0, 1.0)), 1.0);\n}`,\n ),\n};\n\nexport const voronoi: ShaderEffect = {\n kind: \"shader\",\n id: \"voronoi\",\n name: \"Living cells\",\n description: \"Voronoi cells drifting and dividing, like a microscope slide.\",\n category: \"pattern\",\n tags: [\"background\", \"biology\", \"cells\", \"organic\"],\n source: glsl(\n `// Living cells: drifting Voronoi cells with glowing walls.\nuniform vec3 uBackground; // @control color default=#052E16 label=\"Background\"\nuniform vec3 uCell; // @control color default=#4ADE80 label=\"Cell\"\nuniform vec3 uWall; // @control color default=#DCFCE7 label=\"Wall\"\nuniform float uScale; // @control min=2 max=20 default=6 label=\"Scale\"\nuniform float uSpeed; // @control min=0 max=2 default=0.5 label=\"Speed\"`,\n HASH,\n `void mainImage(out vec4 fragColor, in vec2 fragCoord) {\n vec2 p = fragCoord / iResolution.y * uScale;\n vec2 i = floor(p);\n vec2 f = fract(p);\n float d1 = 8.0;\n float d2 = 8.0;\n float shade = 0.0;\n for (int y = -1; y <= 1; y++) {\n for (int x = -1; x <= 1; x++) {\n vec2 g = vec2(float(x), float(y));\n vec2 o = hash22(i + g);\n o = 0.5 + 0.4 * sin(iTime * uSpeed + 6.28318 * o);\n float d = length(g + o - f);\n if (d < d1) { d2 = d1; d1 = d; shade = hash12(i + g); }\n else if (d < d2) { d2 = d; }\n }\n }\n float wall = 1.0 - smoothstep(0.0, 0.08, d2 - d1);\n vec3 col = mix(uBackground, uCell * (0.4 + 0.6 * shade), smoothstep(0.9, 0.1, d1));\n col = mix(col, uWall, wall);\n fragColor = vec4(col, 1.0);\n}`,\n ),\n};\n\nexport const kaleidoscope: ShaderEffect = {\n kind: \"shader\",\n id: \"kaleidoscope\",\n name: \"Kaleidoscope\",\n description: \"Mirrored wedges of shifting colour turning around the centre.\",\n category: \"pattern\",\n tags: [\"psychedelic\", \"music\", \"symmetry\"],\n source: glsl(\n `// Kaleidoscope: mirrored wedges of shifting colour.\nuniform int uSegments; // @control min=2 max=16 default=8 label=\"Segments\"\nuniform float uZoom; // @control min=0.5 max=8 default=3 label=\"Zoom\"\nuniform float uSpeed; // @control min=0 max=2 default=0.4 label=\"Speed\"\nuniform vec3 uTint; // @control color default=#FFFFFF label=\"Tint\"`,\n ROTATE,\n `void mainImage(out vec4 fragColor, in vec2 fragCoord) {\n vec2 p = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;\n float t = iTime * uSpeed;\n float n = float(uSegments);\n float a = atan(p.y, p.x) + t * 0.3;\n float r = length(p);\n float wedge = 6.28318 / n;\n a = mod(a, wedge);\n a = abs(a - wedge * 0.5);\n vec2 q = vec2(cos(a), sin(a)) * r * uZoom;\n q = rot(t * 0.2) * q + t;\n vec3 col = 0.5 + 0.5 * cos(vec3(0.0, 2.0, 4.0) + q.x * 2.0 + sin(q.y * 3.0) + t);\n col *= 0.6 + 0.4 * sin(q.x * 5.0) * sin(q.y * 5.0);\n col *= smoothstep(1.2, 0.2, r);\n fragColor = vec4(col * uTint, 1.0);\n}`,\n ),\n};\n","import { glsl, HASH } from \"../glsl.js\";\nimport type { ShaderEffect } from \"../types.js\";\n\nexport const synthwaveGrid: ShaderEffect = {\n kind: \"shader\",\n id: \"synthwave\",\n name: \"Synthwave\",\n description: \"A neon perspective grid racing towards a striped retro sun.\",\n category: \"retro\",\n tags: [\"background\", \"80s\", \"neon\", \"music\", \"loop\"],\n loop: 1,\n source: glsl(\n `// Synthwave: a neon grid racing towards a striped sun.\nuniform vec3 uSkyTop; // @control color default=#12002B label=\"Sky top\"\nuniform vec3 uSkyBottom; // @control color default=#7A1A6B label=\"Sky bottom\"\nuniform vec3 uSunTop; // @control color default=#FFE45E label=\"Sun top\"\nuniform vec3 uSunBottom; // @control color default=#FF3D7F label=\"Sun bottom\"\nuniform vec3 uGrid; // @control color default=#00F0FF label=\"Grid\"\nuniform float uHorizon; // @control min=0.2 max=0.7 default=0.42 label=\"Horizon\"`,\n `void mainImage(out vec4 fragColor, in vec2 fragCoord) {\n vec2 uv = fragCoord / iResolution.xy;\n vec2 p = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;\n float horizon = uHorizon - 0.5;\n vec3 col;\n if (p.y > horizon) {\n float h = (p.y - horizon) / (0.5 - horizon);\n col = mix(uSkyBottom, uSkyTop, h);\n vec2 s = p - vec2(0.0, horizon + 0.18);\n float sun = smoothstep(0.2, 0.195, length(s));\n float bands = step(0.0, sin((s.y + 0.2) * 60.0 - 1.5)) + step(0.06, s.y);\n sun *= clamp(bands, 0.0, 1.0);\n col = mix(col, mix(uSunBottom, uSunTop, clamp(s.y / 0.2 * 0.5 + 0.5, 0.0, 1.0)), sun);\n col += uSunBottom * 0.25 * exp(-length(s) * 4.0);\n } else {\n float depth = (horizon - p.y);\n vec2 g = vec2(p.x / depth, 1.0 / depth);\n g.y += iTime * 2.0;\n vec2 grid = abs(fract(g * 0.5) - 0.5) / fwidth(g * 0.5);\n float line = 1.0 - clamp(min(grid.x, grid.y), 0.0, 1.0);\n float fade = smoothstep(0.0, 0.25, depth);\n col = mix(uSkyTop * 0.4, uSkyBottom * 0.3, uv.y);\n col += uGrid * line * fade + uGrid * 0.25 * exp(-depth * 20.0);\n }\n fragColor = vec4(col, 1.0);\n}`,\n ),\n};\n\nexport const tvStatic: ShaderEffect = {\n kind: \"shader\",\n id: \"tv-static\",\n name: \"TV static\",\n description: \"Analogue snow with rolling bars and scanlines.\",\n category: \"retro\",\n tags: [\"glitch\", \"analogue\", \"transition\", \"noise\"],\n source: glsl(\n `// TV static: analogue snow with a rolling bar and scanlines.\nuniform float uAmount; // @control min=0 max=1 default=0.9 label=\"Snow\"\nuniform float uScanlines; // @control min=0 max=1 default=0.35 label=\"Scanlines\"\nuniform float uRoll; // @control min=0 max=1 default=0.3 label=\"Rolling bar\"\nuniform vec3 uTint; // @control color default=#E6E6E6 label=\"Tint\"`,\n HASH,\n `void mainImage(out vec4 fragColor, in vec2 fragCoord) {\n vec2 uv = fragCoord / iResolution.xy;\n float n = hash12(floor(fragCoord * 0.5) + floor(iTime * 30.0) * 17.31);\n float scan = 1.0 - uScanlines * 0.5 * (1.0 + sin(fragCoord.y * 3.14159));\n float bar = 1.0 + uRoll * 0.6 * smoothstep(0.15, 0.0, abs(fract(uv.y - iTime * 0.2) - 0.5) - 0.3);\n vec3 col = uTint * mix(0.5, n, uAmount) * scan * bar;\n col *= smoothstep(1.2, 0.4, length(uv - 0.5) * 1.4);\n fragColor = vec4(col, 1.0);\n}`,\n ),\n};\n\nexport const digitalRain: ShaderEffect = {\n kind: \"shader\",\n id: \"digital-rain\",\n name: \"Digital rain\",\n description: \"Columns of glyph-like characters raining down a terminal.\",\n category: \"retro\",\n tags: [\"hacker\", \"code\", \"tech\", \"cyber\"],\n source: glsl(\n `// Digital rain: glyph-like cells raining down in columns.\nuniform vec3 uBackground; // @control color default=#000000 label=\"Background\"\nuniform vec3 uGlyph; // @control color default=#22FF66 label=\"Glyph\"\nuniform float uCell; // @control min=6 max=40 default=14 label=\"Cell size (px)\"\nuniform float uSpeed; // @control min=0 max=4 default=1 label=\"Speed\"`,\n HASH,\n `float glyph(vec2 cell, vec2 f) {\n vec2 g = floor(f * vec2(3.0, 5.0));\n float on = step(0.45, hash12(g + cell * 13.7 + floor(iTime * 4.0 * hash12(cell))));\n vec2 inset = abs(fract(f * vec2(3.0, 5.0)) - 0.5);\n return on * step(max(inset.x, inset.y), 0.38);\n}\n\nvoid mainImage(out vec4 fragColor, in vec2 fragCoord) {\n vec2 q = fragCoord / uCell;\n vec2 cell = floor(q);\n vec2 f = fract(q);\n float col = cell.x;\n float speed = (0.5 + hash12(vec2(col, 1.0))) * uSpeed * 8.0;\n float offset = hash12(vec2(col, 2.0)) * 100.0;\n float head = fract((iTime * speed + offset) / 40.0) * (iResolution.y / uCell + 30.0);\n float y = iResolution.y / uCell - cell.y;\n float trail = head - y;\n float lit = trail >= 0.0 ? exp(-trail * 0.15) : 0.0;\n float isHead = step(abs(trail), 0.5);\n float g = glyph(cell, f);\n vec3 c = uBackground + uGlyph * g * lit + vec3(0.8) * g * isHead;\n fragColor = vec4(c, 1.0);\n}`,\n ),\n};\n\nexport const pixelSunset: ShaderEffect = {\n kind: \"shader\",\n id: \"dither-gradient\",\n name: \"Dithered gradient\",\n description: \"A 1-bit Bayer-dithered gradient wave, like an old Mac or Game Boy screen.\",\n category: \"retro\",\n tags: [\"pixel\", \"1-bit\", \"lo-fi\", \"background\"],\n source: glsl(\n `// Dithered gradient: a Bayer-dithered gradient wave in two colours.\nuniform vec3 uDark; // @control color default=#0F380F label=\"Dark\"\nuniform vec3 uLight; // @control color default=#9BBC0F label=\"Light\"\nuniform float uPixel; // @control min=1 max=12 default=4 step=1 label=\"Pixel size\"\nuniform float uSpeed; // @control min=0 max=2 default=0.5 label=\"Speed\"`,\n `float bayer4(vec2 p) {\n vec2 q = mod(floor(p), 4.0);\n int i = int(q.x) + int(q.y) * 4;\n int m[16] = int[16](0, 8, 2, 10, 12, 4, 14, 6, 3, 11, 1, 9, 15, 7, 13, 5);\n return (float(m[i]) + 0.5) / 16.0;\n}\n\nvoid mainImage(out vec4 fragColor, in vec2 fragCoord) {\n vec2 px = floor(fragCoord / uPixel);\n vec2 uv = px * uPixel / iResolution.xy;\n float v = uv.y + 0.15 * sin(uv.x * 6.0 + iTime * uSpeed * 2.0) + 0.1 * sin(uv.x * 13.0 - iTime * uSpeed * 3.0);\n v = clamp(v, 0.0, 1.0);\n float on = step(bayer4(px), v);\n fragColor = vec4(mix(uDark, uLight, on), 1.0);\n}`,\n ),\n presets: [\n { name: \"Mac\", values: { uDark: [0.05, 0.05, 0.05], uLight: [0.93, 0.93, 0.9] } },\n { name: \"Amber\", values: { uDark: [0.1, 0.04, 0], uLight: [1, 0.69, 0] } },\n ],\n};\n","import { hexToRgb, rgbToHex, type ControlDef, type ControlValue } from \"../shader/controls.js\";\nimport type { SvgMotion } from \"../vector/motion.js\";\n\n/** What an effect is for, used to group the catalog. */\nexport type EffectCategory =\n | \"gradient\"\n | \"organic\"\n | \"pattern\"\n | \"light\"\n | \"space\"\n | \"retro\"\n | \"image\"\n | \"transition\"\n | \"loader\"\n | \"data\"\n | \"icon\";\n\n/** Every category, in the order the catalog lists them. */\nexport const EFFECT_CATEGORIES: readonly EffectCategory[] = [\n \"gradient\",\n \"organic\",\n \"pattern\",\n \"light\",\n \"space\",\n \"retro\",\n \"image\",\n \"transition\",\n \"loader\",\n \"data\",\n \"icon\",\n];\n\n/** Named control values: a look an effect can be switched to. */\nexport interface EffectPreset {\n name: string;\n values: Record<string, ControlValue>;\n}\n\ninterface EffectBase {\n /** Stable id, kebab-case: `aurora`, `image-glitch`. */\n id: string;\n name: string;\n /** One sentence: what it looks like. */\n description: string;\n category: EffectCategory;\n tags: readonly string[];\n presets?: readonly EffectPreset[];\n}\n\n/**\n * A WebGL2 fragment shader (Shadertoy-style `mainImage`). Its controls are the\n * `// @control` uniforms in `source`; run it with `ShaderRenderer`,\n * `ShaderFrames`, `ThumbnailPool`, `recordGif` or `standaloneHtml`.\n */\nexport interface ShaderEffect extends EffectBase {\n kind: \"shader\";\n source: string;\n /** Seconds after which it repeats exactly, when it loops seamlessly. */\n loop?: number;\n}\n\n/**\n * A control of a vector effect. The same shape as a shader's controls (see\n * `ControlDef`) less what only sources have; `vec3` colours are written into\n * the picture as `#rrggbb`.\n */\nexport type VectorControl = Pick<ControlDef, \"name\" | \"type\" | \"default\"> &\n Partial<Pick<ControlDef, \"label\" | \"color\" | \"min\" | \"max\" | \"step\">>;\n\n/**\n * An animated SVG picture (see `SvgMotion`). `{{name}}` in the motion is\n * replaced by the control's value (see {@link renderVectorEffect}); run the\n * result with `SvgPlayer`.\n */\nexport interface VectorEffect extends EffectBase {\n kind: \"vector\";\n motion: SvgMotion;\n controls: readonly VectorControl[];\n /**\n * More placeholders worked out from the control values (a bar's height\n * from its value, say), filled in after the controls.\n */\n derived?: (values: Record<string, ControlValue>) => Record<string, string | number>;\n /** Ms to rest on the last frame before it loops (default 600). */\n hold?: number;\n}\n\nexport type EffectDefinition = ShaderEffect | VectorEffect;\n\n/** A vector control's value written into markup: colours as hex, numbers and booleans as they are. */\nfunction written(control: VectorControl, value: ControlValue): string | number | boolean {\n if (control.color && Array.isArray(value)) return rgbToHex(value);\n if (Array.isArray(value)) return value.join(\" \");\n return value;\n}\n\n/** A vector control as a full `ControlDef`, so editors treat both kinds of effect alike. */\nexport function vectorControlDef(control: VectorControl, line = 0): ControlDef {\n const min = control.min ?? 0;\n return {\n name: control.name,\n type: control.type,\n label: control.label ?? control.name,\n color: control.color ?? false,\n min,\n max: control.max ?? Math.max(min + 1, 1),\n step: control.step ?? (control.type === \"int\" ? 1 : 0.01),\n default: control.default,\n line,\n };\n}\n\n/**\n * A vector effect's motion with its controls filled in: `\"{{name}}\"` (a\n * whole JSON string) becomes the value itself, so numbers stay numbers, and\n * `{{name}}` inside a string becomes its text. Missing values use defaults.\n */\nexport function renderVectorEffect(\n effect: Pick<VectorEffect, \"motion\" | \"controls\" | \"derived\">,\n values: Record<string, ControlValue> = {},\n): SvgMotion {\n const filled: Record<string, ControlValue> = {};\n const placeholders = new Map<string, string | number | boolean>();\n for (const control of effect.controls) {\n filled[control.name] = values[control.name] ?? control.default;\n placeholders.set(control.name, written(control, filled[control.name]!));\n }\n for (const [name, value] of Object.entries(effect.derived?.(filled) ?? {})) placeholders.set(name, value);\n let json = JSON.stringify(effect.motion);\n for (const [key, value] of placeholders) {\n const name = key.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n json = json.replace(new RegExp(`\"\\\\{\\\\{${name}\\\\}\\\\}\"`, \"g\"), JSON.stringify(value));\n json = json.replace(new RegExp(`\\\\{\\\\{${name}\\\\}\\\\}`, \"g\"), String(value).replace(/[\"\\\\]/g, \"\\\\$&\"));\n }\n return JSON.parse(json) as SvgMotion;\n}\n\n/** A colour control for vector effects. */\nexport function colorControl(name: string, hex: string, label?: string): VectorControl {\n return { name, type: \"vec3\", color: true, default: hexToRgb(hex) ?? [1, 1, 1], label: label ?? name };\n}\n\n/** A number control for vector effects. */\nexport function numberControl(\n name: string,\n value: number,\n min: number,\n max: number,\n label?: string,\n step?: number,\n): VectorControl {\n return { name, type: \"float\", default: value, min, max, label: label ?? name, step };\n}\n","import type { SvgAnimation } from \"../vector/motion.js\";\nimport { colorControl, numberControl, type VectorEffect } from \"./types.js\";\n\nexport const spinner: VectorEffect = {\n kind: \"vector\",\n id: \"spinner\",\n name: \"Spinner\",\n description: \"An arc chasing its tail: the classic indeterminate loader.\",\n category: \"loader\",\n tags: [\"loader\", \"ui\", \"minimal\"],\n hold: 0,\n controls: [colorControl(\"accent\", \"#6366F1\", \"Arc\"), colorControl(\"track\", \"#E5E7EB\", \"Track\"), numberControl(\"width\", 8, 2, 20, \"Stroke width\", 1)],\n motion: {\n svg: `<svg viewBox=\"0 0 100 100\"><circle cx=\"50\" cy=\"50\" r=\"38\" fill=\"none\" stroke=\"{{track}}\" stroke-width=\"{{width}}\"/><g id=\"spin\" transform=\"rotate(0 50 50)\"><circle id=\"arc\" cx=\"50\" cy=\"50\" r=\"38\" fill=\"none\" stroke=\"{{accent}}\" stroke-width=\"{{width}}\" stroke-linecap=\"round\" pathLength=\"1\" stroke-dasharray=\"0.25 1\"/></g></svg>`,\n animate: [\n { id: \"spin\", attrs: { transform: [\"rotate(0 50 50)\", \"rotate(720 50 50)\"] }, ms: 1600, ease: \"linear\" },\n { id: \"arc\", attrs: { \"stroke-dasharray\": [\"0.08 1\", \"0.6 1\"] }, ms: 800, ease: \"in-out\" },\n { id: \"arc\", attrs: { \"stroke-dasharray\": [\"0.6 1\", \"0.08 1\"] }, delay: 800, ms: 800, ease: \"in-out\" },\n ],\n },\n};\n\nexport const bouncingDots: VectorEffect = {\n kind: \"vector\",\n id: \"bouncing-dots\",\n name: \"Bouncing dots\",\n description: \"Three dots hopping in turn, for typing indicators and loaders.\",\n category: \"loader\",\n tags: [\"loader\", \"chat\", \"typing\", \"ui\"],\n hold: 0,\n controls: [colorControl(\"accent\", \"#0EA5E9\", \"Dots\"), numberControl(\"height\", 18, 4, 30, \"Bounce height\", 1)],\n motion: {\n svg: `<svg viewBox=\"0 0 120 60\"><circle id=\"a\" cx=\"30\" cy=\"40\" r=\"9\" fill=\"{{accent}}\"/><circle id=\"b\" cx=\"60\" cy=\"40\" r=\"9\" fill=\"{{accent}}\"/><circle id=\"c\" cx=\"90\" cy=\"40\" r=\"9\" fill=\"{{accent}}\"/></svg>`,\n animate: [\"a\", \"b\", \"c\"].flatMap((id, i): SvgAnimation[] => [\n { id, attrs: { transform: [\"translate(0 0)\", \"translate(0 -{{height}})\"] }, delay: i * 150, ms: 250, ease: \"out\" as const },\n { id, attrs: { transform: [\"translate(0 -{{height}})\", \"translate(0 0)\"] }, delay: i * 150 + 250, ms: 250, ease: \"in-out\" as const },\n { id, attrs: { opacity: [0.5, 1] }, delay: i * 150, ms: 250 },\n { id, attrs: { opacity: [1, 0.5] }, delay: i * 150 + 250, ms: 250 },\n ]),\n },\n};\n\nexport const progressRing: VectorEffect = {\n kind: \"vector\",\n id: \"progress-ring\",\n name: \"Progress ring\",\n description: \"A ring filling up to a percentage while the number counts with it.\",\n category: \"data\",\n tags: [\"progress\", \"kpi\", \"dashboard\", \"loader\"],\n controls: [\n colorControl(\"accent\", \"#22C55E\", \"Ring\"),\n colorControl(\"track\", \"#E5E7EB\", \"Track\"),\n colorControl(\"text\", \"#111827\", \"Text\"),\n numberControl(\"value\", 72, 0, 100, \"Percent\", 1),\n ],\n derived: (v) => ({ fraction: Math.min(1, Math.max(0, Number(v.value) / 100)) }),\n motion: {\n svg: `<svg viewBox=\"0 0 120 120\" font-family=\"system-ui, sans-serif\"><circle cx=\"60\" cy=\"60\" r=\"48\" fill=\"none\" stroke=\"{{track}}\" stroke-width=\"10\"/><circle id=\"ring\" cx=\"60\" cy=\"60\" r=\"48\" fill=\"none\" stroke=\"{{accent}}\" stroke-width=\"10\" stroke-linecap=\"round\" transform=\"rotate(-90 60 60)\"/><text id=\"label\" x=\"60\" y=\"68\" text-anchor=\"middle\" font-size=\"24\" font-weight=\"700\" fill=\"{{text}}\">0%</text></svg>`,\n animate: [\n { id: \"ring\", draw: [0, \"{{fraction}}\"] as unknown as [number, number], ms: 1600, ease: \"out\" },\n { id: \"label\", count: [0, \"{{value}}\"] as unknown as [number, number], format: \"{n}%\", ms: 1600, ease: \"out\" },\n ],\n },\n};\n\nexport const pulsePing: VectorEffect = {\n kind: \"vector\",\n id: \"pulse-ping\",\n name: \"Pulse ping\",\n description: \"Rings radiating from a dot, like a live indicator or radar ping.\",\n category: \"loader\",\n tags: [\"live\", \"notification\", \"location\", \"status\"],\n hold: 0,\n controls: [colorControl(\"accent\", \"#EF4444\", \"Colour\")],\n motion: {\n svg: `<svg viewBox=\"0 0 100 100\"><circle id=\"r1\" cx=\"50\" cy=\"50\" r=\"8\" fill=\"none\" stroke=\"{{accent}}\" stroke-width=\"3\"/><circle id=\"r2\" cx=\"50\" cy=\"50\" r=\"8\" fill=\"none\" stroke=\"{{accent}}\" stroke-width=\"3\"/><circle cx=\"50\" cy=\"50\" r=\"9\" fill=\"{{accent}}\"/></svg>`,\n animate: [\n { id: \"r1\", attrs: { r: [8, 44], opacity: [0.9, 0], \"stroke-width\": [4, 1] }, ms: 1400, ease: \"out\" },\n { id: \"r2\", attrs: { r: [8, 44], opacity: [0.9, 0], \"stroke-width\": [4, 1] }, delay: 700, ms: 1400, ease: \"out\" },\n ],\n },\n};\n\nexport const successCheck: VectorEffect = {\n kind: \"vector\",\n id: \"success-check\",\n name: \"Success check\",\n description: \"A circle drawing itself closed, then a tick, then a little pop.\",\n category: \"icon\",\n tags: [\"success\", \"confirmation\", \"ui\", \"feedback\"],\n hold: 1200,\n controls: [colorControl(\"accent\", \"#16A34A\", \"Colour\"), numberControl(\"width\", 6, 2, 12, \"Stroke width\", 1)],\n motion: {\n svg: `<svg viewBox=\"0 0 100 100\"><g id=\"badge\" transform=\"translate(50 50) scale(1) translate(-50 -50)\"><circle id=\"circle\" cx=\"50\" cy=\"50\" r=\"40\" fill=\"none\" stroke=\"{{accent}}\" stroke-width=\"{{width}}\" stroke-linecap=\"round\" transform=\"rotate(-90 50 50)\"/><path id=\"tick\" d=\"M31 52 L44 64 L70 37\" fill=\"none\" stroke=\"{{accent}}\" stroke-width=\"{{width}}\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></g></svg>`,\n animate: [\n { id: \"circle\", draw: true, ms: 600, ease: \"in-out\" },\n { id: \"tick\", draw: true, delay: 500, ms: 400, ease: \"out\" },\n { id: \"badge\", attrs: { transform: [\"translate(50 50) scale(1) translate(-50 -50)\", \"translate(50 50) scale(1.12) translate(-50 -50)\"] }, delay: 850, ms: 150, ease: \"out\" },\n { id: \"badge\", attrs: { transform: [\"translate(50 50) scale(1.12) translate(-50 -50)\", \"translate(50 50) scale(1) translate(-50 -50)\"] }, delay: 1000, ms: 250, ease: \"in-out\" },\n ],\n },\n};\n\nexport const errorCross: VectorEffect = {\n kind: \"vector\",\n id: \"error-cross\",\n name: \"Error cross\",\n description: \"A circle and a cross drawing on, with a short shake.\",\n category: \"icon\",\n tags: [\"error\", \"failure\", \"ui\", \"feedback\"],\n hold: 1200,\n controls: [colorControl(\"accent\", \"#DC2626\", \"Colour\"), numberControl(\"width\", 6, 2, 12, \"Stroke width\", 1)],\n motion: {\n svg: `<svg viewBox=\"0 0 100 100\"><g id=\"badge\" transform=\"translate(0 0)\"><circle id=\"circle\" cx=\"50\" cy=\"50\" r=\"40\" fill=\"none\" stroke=\"{{accent}}\" stroke-width=\"{{width}}\" stroke-linecap=\"round\" transform=\"rotate(-90 50 50)\"/><path id=\"a\" d=\"M36 36 L64 64\" fill=\"none\" stroke=\"{{accent}}\" stroke-width=\"{{width}}\" stroke-linecap=\"round\"/><path id=\"b\" d=\"M64 36 L36 64\" fill=\"none\" stroke=\"{{accent}}\" stroke-width=\"{{width}}\" stroke-linecap=\"round\"/></g></svg>`,\n animate: [\n { id: \"circle\", draw: true, ms: 600, ease: \"in-out\" },\n { id: \"a\", draw: true, delay: 500, ms: 250, ease: \"out\" },\n { id: \"b\", draw: true, delay: 700, ms: 250, ease: \"out\" },\n { id: \"badge\", attrs: { transform: [\"translate(0 0)\", \"translate(-6 0)\"] }, delay: 950, ms: 60, ease: \"linear\" },\n { id: \"badge\", attrs: { transform: [\"translate(-6 0)\", \"translate(6 0)\"] }, delay: 1010, ms: 100, ease: \"linear\" },\n { id: \"badge\", attrs: { transform: [\"translate(6 0)\", \"translate(0 0)\"] }, delay: 1110, ms: 80, ease: \"out\" },\n ],\n },\n};\n\nexport const heartbeat: VectorEffect = {\n kind: \"vector\",\n id: \"heartbeat\",\n name: \"Heartbeat\",\n description: \"A heart beating twice, with an ECG line tracing beneath it.\",\n category: \"icon\",\n tags: [\"health\", \"like\", \"love\", \"pulse\"],\n hold: 300,\n controls: [colorControl(\"accent\", \"#F43F5E\", \"Heart\"), colorControl(\"line\", \"#94A3B8\", \"Line\")],\n motion: {\n svg: `<svg viewBox=\"0 0 200 120\"><g id=\"heart\" transform=\"translate(100 46) scale(1)\"><path d=\"M0 22 C-30 0 -30 -26 -10 -26 C-3 -26 0 -20 0 -16 C0 -20 3 -26 10 -26 C30 -26 30 0 0 22Z\" fill=\"{{accent}}\"/></g><path id=\"ecg\" d=\"M10 100 H70 L80 86 L90 112 L100 76 L110 104 L118 100 H190\" fill=\"none\" stroke=\"{{line}}\" stroke-width=\"3\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/></svg>`,\n animate: [\n { id: \"heart\", attrs: { transform: [\"translate(100 46) scale(1)\", \"translate(100 46) scale(1.18)\"] }, ms: 120, ease: \"out\" },\n { id: \"heart\", attrs: { transform: [\"translate(100 46) scale(1.18)\", \"translate(100 46) scale(1)\"] }, delay: 120, ms: 160, ease: \"in-out\" },\n { id: \"heart\", attrs: { transform: [\"translate(100 46) scale(1)\", \"translate(100 46) scale(1.12)\"] }, delay: 300, ms: 120, ease: \"out\" },\n { id: \"heart\", attrs: { transform: [\"translate(100 46) scale(1.12)\", \"translate(100 46) scale(1)\"] }, delay: 420, ms: 300, ease: \"in-out\" },\n { id: \"ecg\", draw: true, ms: 900, ease: \"linear\" },\n ],\n },\n};\n\nexport const barChart: VectorEffect = {\n kind: \"vector\",\n id: \"bar-chart\",\n name: \"Bar chart\",\n description: \"Bars growing into place one after another, values counting above them.\",\n category: \"data\",\n tags: [\"chart\", \"dashboard\", \"report\", \"explainer\"],\n hold: 1500,\n controls: [\n colorControl(\"accent\", \"#6366F1\", \"Bars\"),\n colorControl(\"text\", \"#334155\", \"Text\"),\n numberControl(\"a\", 40, 0, 100, \"Bar 1\", 1),\n numberControl(\"b\", 65, 0, 100, \"Bar 2\", 1),\n numberControl(\"c\", 50, 0, 100, \"Bar 3\", 1),\n numberControl(\"d\", 90, 0, 100, \"Bar 4\", 1),\n ],\n derived: (v) =>\n Object.fromEntries(\n [\"a\", \"b\", \"c\", \"d\"].flatMap((k) => {\n const h = Math.min(100, Math.max(0, Number(v[k]))) * 1.1;\n return [\n [`${k}_height`, +h.toFixed(2)],\n [`${k}_label_y`, +(134 - h).toFixed(2)],\n ];\n }),\n ),\n motion: {\n svg: `<svg viewBox=\"0 0 240 160\" font-family=\"system-ui, sans-serif\"><line x1=\"20\" y1=\"140\" x2=\"220\" y2=\"140\" stroke=\"{{text}}\" stroke-opacity=\"0.3\" stroke-width=\"2\"/>${[\"a\", \"b\", \"c\", \"d\"]\n .map(\n (k, i) =>\n `<g id=\"bar-${k}\" transform=\"translate(${40 + i * 48} 140) scale(1 0)\"><rect x=\"0\" y=\"-1\" width=\"32\" height=\"1\" rx=\"0.02\" fill=\"{{accent}}\"/></g><text id=\"label-${k}\" x=\"${56 + i * 48}\" y=\"132\" text-anchor=\"middle\" font-size=\"12\" fill=\"{{text}}\" opacity=\"0\">0</text>`,\n )\n .join(\"\")}</svg>`,\n animate: [\"a\", \"b\", \"c\", \"d\"].flatMap((k, i): SvgAnimation[] => [\n { id: `bar-${k}`, attrs: { transform: [`translate(${40 + i * 48} 140) scale(1 0)`, `translate(${40 + i * 48} 140) scale(1 {{${k}_height}})`] }, delay: i * 180, ms: 700, ease: \"out\" as const },\n { id: `label-${k}`, attrs: { y: [132, `{{${k}_label_y}}`], opacity: [0, 1] }, delay: i * 180, ms: 700, ease: \"out\" as const },\n { id: `label-${k}`, count: [0, `{{${k}}}`] as unknown as [number, number], delay: i * 180, ms: 700, ease: \"out\" as const },\n ]),\n },\n};\n\nexport const lineChart: VectorEffect = {\n kind: \"vector\",\n id: \"line-chart\",\n name: \"Line chart\",\n description: \"A trend line drawing across with a marker riding its tip and the area fading in below.\",\n category: \"data\",\n tags: [\"chart\", \"trend\", \"growth\", \"dashboard\"],\n hold: 1500,\n controls: [colorControl(\"accent\", \"#0EA5E9\", \"Line\"), colorControl(\"grid\", \"#CBD5E1\", \"Grid\")],\n motion: {\n svg: `<svg viewBox=\"0 0 240 140\"><defs><linearGradient id=\"fill\" x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\"><stop offset=\"0\" stop-color=\"{{accent}}\" stop-opacity=\"0.35\"/><stop offset=\"1\" stop-color=\"{{accent}}\" stop-opacity=\"0\"/></linearGradient></defs><g stroke=\"{{grid}}\" stroke-width=\"1\"><line x1=\"20\" y1=\"30\" x2=\"220\" y2=\"30\"/><line x1=\"20\" y1=\"70\" x2=\"220\" y2=\"70\"/><line x1=\"20\" y1=\"110\" x2=\"220\" y2=\"110\"/></g><path id=\"area\" d=\"M20 110 L60 92 L100 98 L140 64 L180 70 L220 30 L220 120 L20 120Z\" fill=\"url(#fill)\" opacity=\"0\"/><path id=\"trend\" d=\"M20 110 L60 92 L100 98 L140 64 L180 70 L220 30\" fill=\"none\" stroke=\"{{accent}}\" stroke-width=\"3\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/><circle id=\"marker\" r=\"5\" fill=\"#fff\" stroke=\"{{accent}}\" stroke-width=\"3\"/></svg>`,\n animate: [\n { id: \"trend\", draw: true, ms: 1400, ease: \"in-out\" },\n { id: \"marker\", along: \"trend\", ms: 1400, ease: \"in-out\" },\n { id: \"area\", attrs: { opacity: [0, 1] }, delay: 900, ms: 600 },\n ],\n },\n};\n\nexport const statCounter: VectorEffect = {\n kind: \"vector\",\n id: \"stat-counter\",\n name: \"Stat counter\",\n description: \"A headline number counting up, with its caption and underline sweeping in.\",\n category: \"data\",\n tags: [\"kpi\", \"number\", \"headline\", \"explainer\"],\n hold: 1500,\n controls: [\n colorControl(\"accent\", \"#F59E0B\", \"Accent\"),\n colorControl(\"text\", \"#0F172A\", \"Text\"),\n numberControl(\"value\", 1250, 0, 100000, \"Value\", 1),\n ],\n motion: {\n svg: `<svg viewBox=\"0 0 240 120\" font-family=\"system-ui, sans-serif\"><text id=\"number\" x=\"120\" y=\"62\" text-anchor=\"middle\" font-size=\"44\" font-weight=\"800\" fill=\"{{text}}\">0</text><path id=\"underline\" d=\"M60 78 H180\" stroke=\"{{accent}}\" stroke-width=\"6\" stroke-linecap=\"round\"/><text id=\"caption\" x=\"120\" y=\"104\" text-anchor=\"middle\" font-size=\"13\" fill=\"{{text}}\" opacity=\"0\">happy customers</text></svg>`,\n animate: [\n { id: \"number\", count: [0, \"{{value}}\"] as unknown as [number, number], ms: 1500, ease: \"out\" },\n { id: \"underline\", draw: true, delay: 300, ms: 900, ease: \"in-out\" },\n { id: \"caption\", attrs: { opacity: [0, 1], y: [112, 104] }, delay: 800, ms: 500, ease: \"out\" },\n ],\n },\n};\n\nexport const route: VectorEffect = {\n kind: \"vector\",\n id: \"route\",\n name: \"Route\",\n description: \"A dashed journey drawing between two pins, with a traveller following it.\",\n category: \"data\",\n tags: [\"map\", \"travel\", \"delivery\", \"journey\"],\n hold: 1200,\n controls: [colorControl(\"accent\", \"#8B5CF6\", \"Route\"), colorControl(\"pin\", \"#0F172A\", \"Pins\")],\n motion: {\n svg: `<svg viewBox=\"0 0 240 150\"><path id=\"path\" d=\"M30 120 C60 40 120 140 150 70 S210 30 210 30\" fill=\"none\" stroke=\"{{accent}}\" stroke-width=\"4\" stroke-linecap=\"round\"/><circle cx=\"30\" cy=\"120\" r=\"7\" fill=\"{{pin}}\"/><g id=\"end\" opacity=\"0\"><circle cx=\"210\" cy=\"30\" r=\"9\" fill=\"{{pin}}\"/><circle cx=\"210\" cy=\"30\" r=\"3.5\" fill=\"#fff\"/></g><g id=\"traveller\"><circle r=\"7\" fill=\"{{accent}}\"/><circle r=\"3\" fill=\"#fff\"/></g></svg>`,\n animate: [\n { id: \"path\", draw: true, ms: 1800, ease: \"in-out\" },\n { id: \"traveller\", along: \"path\", ms: 1800, ease: \"in-out\" },\n { id: \"end\", attrs: { opacity: [0, 1] }, delay: 1600, ms: 300 },\n ],\n },\n};\n\nexport const blobMorph: VectorEffect = {\n kind: \"vector\",\n id: \"blob-morph\",\n name: \"Blob morph\",\n description: \"An organic blob shape-shifting through three forms.\",\n category: \"organic\",\n tags: [\"shape\", \"background\", \"playful\", \"brand\"],\n hold: 0,\n controls: [colorControl(\"colorA\", \"#F472B6\", \"Colour A\"), colorControl(\"colorB\", \"#818CF8\", \"Colour B\")],\n motion: (() => {\n const shapes = [\n \"M100 30 C140 30 170 60 170 100 C170 140 140 170 100 170 C60 170 30 140 30 100 C30 60 60 30 100 30Z\",\n \"M100 20 C150 40 180 70 160 110 C140 160 120 175 85 165 C45 155 25 120 40 80 C55 40 70 15 100 20Z\",\n \"M110 35 C155 25 175 85 165 120 C155 160 105 180 70 160 C35 140 20 100 45 70 C65 45 80 40 110 35Z\",\n ];\n const step = 1200;\n return {\n svg: `<svg viewBox=\"0 0 200 200\"><defs><linearGradient id=\"g\" x1=\"0\" y1=\"0\" x2=\"1\" y2=\"1\"><stop offset=\"0\" stop-color=\"{{colorA}}\"/><stop offset=\"1\" stop-color=\"{{colorB}}\"/></linearGradient></defs><path id=\"blob\" d=\"${shapes[0]}\" fill=\"url(#g)\"/></svg>`,\n animate: shapes.map((d, i) => ({\n id: \"blob\",\n attrs: { d: [d, shapes[(i + 1) % shapes.length]!] as [string, string] },\n delay: i * step,\n ms: step,\n ease: \"in-out\" as const,\n })),\n };\n })(),\n};\n\nexport const signature: VectorEffect = {\n kind: \"vector\",\n id: \"signature\",\n name: \"Signature\",\n description: \"A handwritten flourish drawing itself as if by pen.\",\n category: \"icon\",\n tags: [\"handwriting\", \"draw-on\", \"personal\", \"reveal\"],\n hold: 1500,\n controls: [colorControl(\"ink\", \"#1E293B\", \"Ink\"), numberControl(\"width\", 3, 1, 8, \"Pen width\", 0.5)],\n motion: {\n svg: `<svg viewBox=\"0 0 260 120\"><path id=\"sig\" d=\"M20 80 C40 20 60 20 55 70 C52 95 70 95 85 60 C95 40 100 75 110 70 C120 65 125 40 135 55 C145 70 150 75 165 50 C175 35 185 60 190 65 C200 75 215 40 240 45\" fill=\"none\" stroke=\"{{ink}}\" stroke-width=\"{{width}}\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/><path id=\"dash\" d=\"M70 100 Q140 92 220 98\" fill=\"none\" stroke=\"{{ink}}\" stroke-width=\"{{width}}\" stroke-linecap=\"round\"/></svg>`,\n animate: [\n { id: \"sig\", draw: true, ms: 1800, ease: \"in-out\" },\n { id: \"dash\", draw: true, delay: 1700, ms: 400, ease: \"out\" },\n ],\n },\n};\n\nexport const equalizer: VectorEffect = {\n kind: \"vector\",\n id: \"equalizer\",\n name: \"Equalizer\",\n description: \"Audio level bars dancing, for now-playing badges and voice states.\",\n category: \"loader\",\n tags: [\"music\", \"audio\", \"voice\", \"now-playing\"],\n hold: 0,\n controls: [colorControl(\"accent\", \"#10B981\", \"Bars\")],\n motion: (() => {\n const heights = [\n [0.3, 0.9, 0.5, 0.3],\n [0.7, 0.4, 1.0, 0.7],\n [0.5, 1.0, 0.3, 0.5],\n [0.9, 0.5, 0.7, 0.9],\n [0.4, 0.8, 0.6, 0.4],\n ];\n const beat = 250;\n return {\n svg: `<svg viewBox=\"0 0 100 100\">${heights\n .map((_, i) => `<g id=\"bar${i}\" transform=\"translate(${14 + i * 16} 85) scale(1 ${heights[i]![0]})\"><rect x=\"0\" y=\"-70\" width=\"10\" height=\"70\" rx=\"5\" fill=\"{{accent}}\"/></g>`)\n .join(\"\")}</svg>`,\n animate: heights.flatMap((h, i) =>\n [0, 1, 2].map((k) => ({\n id: `bar${i}`,\n attrs: { transform: [`translate(${14 + i * 16} 85) scale(1 ${h[k]})`, `translate(${14 + i * 16} 85) scale(1 ${h[k + 1]})`] as [string, string] },\n delay: k * beat,\n ms: beat,\n ease: \"in-out\" as const,\n })),\n ),\n };\n })(),\n};\n\nexport const confetti: VectorEffect = {\n kind: \"vector\",\n id: \"confetti\",\n name: \"Confetti burst\",\n description: \"A burst of confetti flying out and fluttering down.\",\n category: \"icon\",\n tags: [\"celebration\", \"success\", \"party\", \"reward\"],\n hold: 400,\n controls: [colorControl(\"a\", \"#F43F5E\", \"Colour A\"), colorControl(\"b\", \"#FACC15\", \"Colour B\"), colorControl(\"c\", \"#22D3EE\", \"Colour C\")],\n motion: (() => {\n const pieces = Array.from({ length: 18 }, (_, i) => {\n const angle = (i / 18) * Math.PI * 2 + (i % 3) * 0.2;\n const dist = 55 + (i % 4) * 12;\n const x = Math.round(Math.cos(angle) * dist);\n const y = Math.round(Math.sin(angle) * dist * 0.8 - 20);\n const spin = (i % 2 ? 1 : -1) * (180 + (i % 5) * 60);\n return { i, x, y, spin, fill: [\"{{a}}\", \"{{b}}\", \"{{c}}\"][i % 3]! };\n });\n return {\n svg: `<svg viewBox=\"0 0 200 200\">${pieces\n .map((p) => `<rect id=\"p${p.i}\" x=\"-4\" y=\"-2\" width=\"8\" height=\"4\" rx=\"1\" fill=\"${p.fill}\" transform=\"translate(100 110) rotate(0)\" opacity=\"0\"/>`)\n .join(\"\")}</svg>`,\n animate: pieces.flatMap((p): SvgAnimation[] => [\n { id: `p${p.i}`, attrs: { opacity: [0, 1] }, ms: 50 },\n {\n id: `p${p.i}`,\n attrs: { transform: [\"translate(100 110) rotate(0)\", `translate(${100 + p.x} ${110 + p.y}) rotate(${p.spin})`] as [string, string] },\n ms: 600,\n ease: \"out\" as const,\n },\n {\n id: `p${p.i}`,\n attrs: {\n transform: [`translate(${100 + p.x} ${110 + p.y}) rotate(${p.spin})`, `translate(${100 + p.x * 1.2} ${180 + p.y * 0.2}) rotate(${p.spin * 2})`] as [string, string],\n },\n delay: 600,\n ms: 1000,\n ease: \"in-out\" as const,\n },\n { id: `p${p.i}`, attrs: { opacity: [1, 0] }, delay: 1300, ms: 300 },\n ]),\n };\n })(),\n};\n\nexport const vectorEffects: VectorEffect[] = [\n spinner,\n bouncingDots,\n pulsePing,\n equalizer,\n progressRing,\n barChart,\n lineChart,\n statCounter,\n route,\n successCheck,\n errorCross,\n heartbeat,\n signature,\n confetti,\n blobMorph,\n];\n","/**\n * The effect catalog: ready-made shader and vector effects, each with\n * controls and presets, to drop into a product or start an edit from.\n *\n * - Browsing: `EFFECTS`, `getEffect`, `findEffects`, `EFFECT_CATEGORIES`.\n * - Controls: `effectControls`, `effectDefaults`.\n * - Vector effects: `renderVectorEffect` fills in their controls.\n *\n * @packageDocumentation\n */\n\nimport { controlValue, parseControls, type ControlDef, type ControlValue } from \"../shader/controls.js\";\nimport { aurora, conicSweep, duotoneFlow, meshGradient, silk } from \"./shaders/gradients.js\";\nimport {\n crt,\n filmGrain,\n imageDuotone,\n imageGlitch,\n imageHalftone,\n imagePixelate,\n imageRipple,\n transitions,\n} from \"./shaders/image.js\";\nimport { blackHole, bokeh, godRays, nebula, neonRipple, sparkles, starfield } from \"./shaders/light.js\";\nimport { caustics, flames, lavaLamp, marble, plasma, smoke } from \"./shaders/organic.js\";\nimport { barberStripes, dotWave, hexPulse, kaleidoscope, moire, truchet, voronoi } from \"./shaders/patterns.js\";\nimport { digitalRain, pixelSunset, synthwaveGrid, tvStatic } from \"./shaders/retro.js\";\nimport { EFFECT_CATEGORIES, vectorControlDef, type EffectCategory, type EffectDefinition, type ShaderEffect } from \"./types.js\";\nimport { vectorEffects } from \"./vector.js\";\n\nexport {\n colorControl,\n EFFECT_CATEGORIES,\n numberControl,\n renderVectorEffect,\n vectorControlDef,\n type EffectCategory,\n type EffectDefinition,\n type EffectPreset,\n type ShaderEffect,\n type VectorControl,\n type VectorEffect,\n} from \"./types.js\";\nexport { SAMPLE_IMAGES, svgDataUrl } from \"./images.js\";\nexport { FBM, glsl, HASH, NOISE, OKLAB_MIX, PALETTE, ROTATE } from \"./glsl.js\";\n\n/** The catalog's shader effects. */\nexport const SHADER_EFFECTS: readonly ShaderEffect[] = [\n meshGradient,\n aurora,\n duotoneFlow,\n conicSweep,\n silk,\n marble,\n smoke,\n lavaLamp,\n plasma,\n caustics,\n flames,\n dotWave,\n truchet,\n hexPulse,\n voronoi,\n moire,\n kaleidoscope,\n barberStripes,\n bokeh,\n godRays,\n sparkles,\n neonRipple,\n starfield,\n nebula,\n blackHole,\n synthwaveGrid,\n digitalRain,\n pixelSunset,\n tvStatic,\n imageRipple,\n imageGlitch,\n imageDuotone,\n imageHalftone,\n imagePixelate,\n crt,\n filmGrain,\n ...transitions,\n];\n\n/** Every effect in the catalog, shaders first, grouped by category. */\nexport const EFFECTS: readonly EffectDefinition[] = [...SHADER_EFFECTS, ...vectorEffects].sort(\n (a, b) => EFFECT_CATEGORIES.indexOf(a.category) - EFFECT_CATEGORIES.indexOf(b.category),\n);\n\nconst byId = new Map(EFFECTS.map((e) => [e.id, e]));\n\n/** The effect with this id, or undefined. */\nexport function getEffect(id: string): EffectDefinition | undefined {\n return byId.get(id);\n}\n\nexport interface EffectQuery {\n /** Words to look for in the name, description, tags and id (all must match). */\n text?: string;\n category?: EffectCategory;\n kind?: EffectDefinition[\"kind\"];\n /** Effects with every one of these tags. */\n tags?: readonly string[];\n}\n\n/** Effects matching a query, in catalog order. */\nexport function findEffects(query: EffectQuery = {}): EffectDefinition[] {\n const words = (query.text ?? \"\").toLowerCase().split(/\\s+/).filter(Boolean);\n return EFFECTS.filter((e) => {\n if (query.category && e.category !== query.category) return false;\n if (query.kind && e.kind !== query.kind) return false;\n if (query.tags && !query.tags.every((t) => e.tags.includes(t))) return false;\n const haystack = [e.id, e.name, e.description, e.category, ...e.tags].join(\" \").toLowerCase();\n return words.every((w) => haystack.includes(w));\n });\n}\n\n/** An effect's controls: a shader's `@control` uniforms, or a vector effect's declared controls. */\nexport function effectControls(effect: EffectDefinition): ControlDef[] {\n return effect.kind === \"shader\" ? parseControls(effect.source) : effect.controls.map((c, i) => vectorControlDef(c, i + 1));\n}\n\n/** An effect's values: each control's default, overridden by `values` (and a preset by name, when given). */\nexport function effectDefaults(\n effect: EffectDefinition,\n options: { preset?: string; values?: Record<string, ControlValue> } = {},\n): Record<string, ControlValue> {\n const preset = options.preset ? effect.presets?.find((p) => p.name === options.preset)?.values : undefined;\n const merged = { ...preset, ...options.values };\n return Object.fromEntries(effectControls(effect).map((c) => [c.name, controlValue(c, merged)]));\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/export/motn.ts","../src/export/lottie.ts","../src/export/interchange.ts"],"sourcesContent":["import type { Scene, Shot } from '../scene/types.js';\nimport { DEFAULT_STAGE, CAMERA_MS } from '../scene/camera.js';\nimport { isElementCommand } from '../scene/types.js';\n\nexport interface MotnExportOptions {\n frameRate?: number;\n}\n\nfunction escapeXml(unsafe: string): string {\n return unsafe\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n}\n\n/**\n * Serializes a Flair Scene into Apple Motion ozml 4.0 XML document format (.motn).\n */\nexport function exportMotn(scene: Scene, options: MotnExportOptions = {}): string {\n const frameRate = options.frameRate ?? 30;\n const width = scene.stage?.width ?? DEFAULT_STAGE.width;\n const height = scene.stage?.height ?? DEFAULT_STAGE.height;\n\n // Calculate shot frame ranges\n let currentMs = 0;\n const shotTimings = scene.shots.map((shot, idx) => {\n const camMs = idx > 0 && shot.camera ? CAMERA_MS : 0;\n const holdMs = shot.hold ?? 800;\n const durationMs = camMs + holdMs;\n const startMs = currentMs;\n currentMs += durationMs;\n\n const startFrame = Math.round((startMs / 1000) * frameRate);\n const durationFrames = Math.max(1, Math.round((durationMs / 1000) * frameRate));\n const endFrame = startFrame + durationFrames - 1;\n\n return {\n shot,\n startFrame,\n durationFrames,\n endFrame,\n };\n });\n\n const totalFrames = Math.max(1, Math.round((currentMs / 1000) * frameRate));\n\n let nodeId = 10000;\n const lines: string[] = [];\n\n lines.push('<?xml version=\"1.0\" encoding=\"UTF-8\"?>');\n lines.push('<!DOCTYPE ozxmlscene>');\n lines.push('<ozml version=\"4.0\">');\n if (scene.name) {\n lines.push(` <!-- Scene: ${escapeXml(scene.name)} -->`);\n }\n lines.push('');\n lines.push(' <displayversion>5.0</displayversion>');\n lines.push('');\n lines.push(' <factory id=\"1\" uuid=\"044beba5ad3211d7ac9b000393833f6a\">');\n lines.push(' <description>Style</description>');\n lines.push(' <manufacturer>Apple</manufacturer>');\n lines.push(' <version>1</version>');\n lines.push(' </factory>');\n lines.push(' <factory id=\"2\" uuid=\"babfc7777f4711d7aaa7000393833f6a\">');\n lines.push(' <description>Text</description>');\n lines.push(' <manufacturer>Apple</manufacturer>');\n lines.push(' <version>1</version>');\n lines.push(' </factory>');\n lines.push(' <factory id=\"3\" uuid=\"712462a4323911d78f8400039389b702\">');\n lines.push(' <description>Shape</description>');\n lines.push(' <manufacturer>Apple</manufacturer>');\n lines.push(' <version>1</version>');\n lines.push(' </factory>');\n lines.push(' <factory id=\"4\" uuid=\"65cb4dc9d4504fa281921f5f751fba06\">');\n lines.push(' <description>Widget</description>');\n lines.push(' <manufacturer>Apple</manufacturer>');\n lines.push(' <version>1</version>');\n lines.push(' </factory>');\n lines.push('');\n lines.push(' <scene>');\n lines.push(' <sceneSettings>');\n lines.push(` <width>${width}</width>`);\n lines.push(` <height>${height}</height>`);\n lines.push(` <duration>${totalFrames}</duration>`);\n lines.push(` <frameRate>${frameRate}</frameRate>`);\n lines.push(' <NTSC>0</NTSC>');\n lines.push(' <channels>4</channels>');\n lines.push(' <channelDepth>8</channelDepth>');\n lines.push(' <pixelAspectRatio>1</pixelAspectRatio>');\n lines.push(' <backgroundColor red=\"0\" green=\"0\" blue=\"0\" alpha=\"1\"/>');\n lines.push(' </sceneSettings>');\n lines.push(' <currentFrame>0</currentFrame>');\n lines.push(` <timeRange offset=\"0\" duration=\"${totalFrames}\"/>`);\n lines.push(` <playRange offset=\"0\" duration=\"${totalFrames}\"/>`);\n lines.push(' <flags>1</flags>');\n\n // Rig widget export if defined\n if (scene.rig && Object.keys(scene.rig.widgets).length > 0) {\n lines.push(` <scenenode name=\"Rigs\" id=\"${nodeId++}\" factoryID=\"4\" version=\"1\">`);\n for (const [widgetName, widget] of Object.entries(scene.rig.widgets)) {\n lines.push(` <scenenode name=\"${escapeXml(widgetName)}\" id=\"${nodeId++}\" factoryID=\"4\" version=\"1\">`);\n lines.push(' <parameter name=\"Object\" id=\"2\">');\n if (widget.type === 'slider') {\n lines.push(` <parameter name=\"Widget\" id=\"100\" default=\"0\" value=\"${widget.value}\"/>`);\n } else if (widget.type === 'toggle') {\n lines.push(` <parameter name=\"Widget\" id=\"100\" default=\"0\" value=\"${widget.enabled ? 1 : 0}\"/>`);\n } else if (widget.type === 'popup') {\n lines.push(` <parameter name=\"Widget\" id=\"100\" default=\"0\" value=\"${escapeXml(widget.selected)}\"/>`);\n }\n lines.push(' </parameter>');\n lines.push(' </scenenode>');\n }\n lines.push(' </scenenode>');\n }\n\n // Groups and element nodes per shot\n for (const [idx, item] of shotTimings.entries()) {\n const shotLabel = item.shot.title\n ? `Shot ${idx + 1}: ${escapeXml(item.shot.title)}`\n : `Shot ${idx + 1}`;\n lines.push(` <group name=\"${shotLabel}\" id=\"${nodeId++}\">`);\n lines.push(` <timing in=\"${item.startFrame}\" out=\"${item.endFrame}\" offset=\"${item.startFrame}\"/>`);\n\n if (item.shot.run) {\n for (const cmd of item.shot.run) {\n if (isElementCommand(cmd) && cmd.element) {\n const el = cmd.element;\n const isText = el.type === 'text';\n const factoryId = isText ? 2 : 3;\n lines.push(` <scenenode name=\"${escapeXml(el.id)}\" id=\"${nodeId++}\" factoryID=\"${factoryId}\" version=\"1\">`);\n lines.push(` <timing in=\"${item.startFrame}\" out=\"${item.endFrame}\" offset=\"${item.startFrame}\"/>`);\n lines.push(' <parameter name=\"Properties\" id=\"1\">');\n lines.push(' <parameter name=\"Transform\" id=\"100\">');\n lines.push(' <parameter name=\"Position\" id=\"101\">');\n lines.push(` <parameter name=\"X\" id=\"1\" value=\"${el.x}\"/>`);\n lines.push(` <parameter name=\"Y\" id=\"2\" value=\"${-el.y}\"/>`);\n lines.push(' </parameter>');\n if (!isText) {\n lines.push(' <parameter name=\"Scale\" id=\"102\">');\n lines.push(` <parameter name=\"X\" id=\"1\" value=\"${el.width}\"/>`);\n lines.push(` <parameter name=\"Y\" id=\"2\" value=\"${el.height}\"/>`);\n lines.push(' </parameter>');\n }\n lines.push(' </parameter>');\n lines.push(' </parameter>');\n\n if (isText && el.text) {\n lines.push(` <text>${escapeXml(el.text)}</text>`);\n }\n lines.push(' </scenenode>');\n }\n }\n }\n\n lines.push(' </group>');\n }\n\n lines.push(' </scene>');\n lines.push('</ozml>');\n lines.push('');\n\n return lines.join('\\n');\n}\n","/**\n * Lottie JSON Vector Animation Exporter.\n *\n * Converts Flair scenes, elements, shapes, transforms, and keyframes into\n * official Lottie JSON format (v5.5.2) compatible with Airbnb Lottie-Web,\n * iOS/Android Lottie, Figma, and Telegram.\n */\n\nimport type { Scene } from '../scene/types.js';\nimport { DEFAULT_STAGE, CAMERA_MS } from '../scene/camera.js';\nimport { isElementCommand, isUpdateCommand } from '../scene/types.js';\nimport type { KeyframeEase } from '../time/keyframe.js';\n\nexport interface LottieExportOptions {\n /** Target framerate in fps. Default: 30 */\n frameRate?: number;\n}\n\nexport interface LottieKeyframe<T> {\n t: number; // frame number\n s: T; // start value\n e?: T; // end value\n o?: { x: number | number[]; y: number | number[] }; // out tangent ease\n i?: { x: number | number[]; y: number | number[] }; // in tangent ease\n h?: number; // hold keyframe flag\n}\n\nexport interface LottieAnimatable<T, K = T> {\n a: 0 | 1; // 0 = static, 1 = animated\n k: T | LottieKeyframe<K>[];\n}\n\nexport interface LottieTransform {\n p: LottieAnimatable<[number, number, number], [number, number, number]>; // position\n a: LottieAnimatable<[number, number, number], [number, number, number]>; // anchor\n s: LottieAnimatable<[number, number, number], [number, number, number]>; // scale\n r: LottieAnimatable<number, [number]>; // rotation\n o: LottieAnimatable<number, [number]>; // opacity\n}\n\n\nexport interface LottieShapeItem {\n ty: string;\n nm?: string;\n [key: string]: unknown;\n}\n\nexport interface LottieLayer {\n ddd: 0 | 1;\n ind: number;\n ty: number; // 0 = precomp, 1 = solid, 2 = image, 3 = null, 4 = shape, 5 = text\n nm: string;\n sr: number;\n ks: LottieTransform;\n ao: 0 | 1;\n ip: number;\n op: number;\n st: number;\n bm: number;\n shapes?: LottieShapeItem[];\n sw?: number;\n sh?: number;\n sc?: string;\n}\n\nexport interface LottieAnimation {\n v: string;\n fr: number;\n ip: number;\n op: number;\n w: number;\n h: number;\n nm: string;\n ddd: 0;\n assets: unknown[];\n layers: LottieLayer[];\n}\n\n/**\n * Maps AE speed and influence into normalized Lottie Bezier ease control points (o.x, o.y, i.x, i.y).\n */\nexport function createLottieEase(\n durationFrames: number,\n deltaVal: number,\n outEase?: KeyframeEase,\n inEase?: KeyframeEase\n): { o: { x: number; y: number }; i: { x: number; y: number } } {\n const infOut = Math.max(0.001, Math.min(1.0, (outEase?.influence ?? 33.333) / 100));\n const infIn = Math.max(0.001, Math.min(1.0, (inEase?.influence ?? 33.333) / 100));\n\n const safeDelta = Math.abs(deltaVal) > 1e-6 ? deltaVal : 1;\n const speedOut = outEase?.speed ?? 0;\n const speedIn = inEase?.speed ?? 0;\n\n // Lottie normalized control points\n const ox = infOut;\n const oy = Math.max(0, Math.min(1, (speedOut * infOut * durationFrames) / safeDelta));\n\n const ix = 1.0 - infIn;\n const iy = Math.max(0, Math.min(1, 1.0 - (speedIn * infIn * durationFrames) / safeDelta));\n\n return {\n o: { x: Number(ox.toFixed(4)), y: Number(oy.toFixed(4)) },\n i: { x: Number(ix.toFixed(4)), y: Number(iy.toFixed(4)) },\n };\n}\n\n/**\n * Serializes a Flair Scene into standard Lottie JSON animation.\n */\nexport function exportLottie(scene: Scene, options: LottieExportOptions = {}): LottieAnimation {\n const fr = options.frameRate ?? 30;\n const width = scene.stage?.width ?? DEFAULT_STAGE.width;\n const height = scene.stage?.height ?? DEFAULT_STAGE.height;\n\n // Compute shot timeline frames\n let currentMs = 0;\n const shotTimings = scene.shots.map((shot, idx) => {\n const camMs = idx > 0 && shot.camera ? CAMERA_MS : 0;\n const holdMs = shot.hold ?? 800;\n const durationMs = camMs + holdMs;\n const startMs = currentMs;\n currentMs += durationMs;\n\n const startFrame = Math.round((startMs / 1000) * fr);\n const durationFrames = Math.max(1, Math.round((durationMs / 1000) * fr));\n const endFrame = startFrame + durationFrames;\n\n return {\n shot,\n startFrame,\n durationFrames,\n endFrame,\n };\n });\n\n const totalFrames = Math.max(1, Math.round((currentMs / 1000) * fr));\n\n // Collect elements and track property updates across shots\n interface ElementTrack {\n id: string;\n type: string;\n initialX: number;\n initialY: number;\n width: number;\n height: number;\n text?: string;\n opacity: number;\n inFrame: number;\n outFrame: number;\n posKeyframes: LottieKeyframe<[number, number, number]>[];\n opacityKeyframes: LottieKeyframe<[number]>[];\n }\n\n const tracks = new Map<string, ElementTrack>();\n\n for (const timing of shotTimings) {\n const shot = timing.shot;\n if (!shot.run) continue;\n\n for (const cmd of shot.run) {\n if (isElementCommand(cmd)) {\n const el = cmd.element;\n if (!tracks.has(el.id)) {\n tracks.set(el.id, {\n id: el.id,\n type: el.type,\n initialX: el.x,\n initialY: el.y,\n width: el.width,\n height: el.height,\n text: el.text,\n opacity: el.opacity ?? 1,\n inFrame: timing.startFrame,\n outFrame: totalFrames,\n posKeyframes: [\n {\n t: timing.startFrame,\n s: [el.x + el.width * 0.5, el.y + el.height * 0.5, 0],\n },\n ],\n opacityKeyframes: [\n {\n t: timing.startFrame,\n s: [(el.opacity ?? 1) * 100],\n },\n ],\n });\n }\n } else if (isUpdateCommand(cmd)) {\n const track = tracks.get(cmd.id);\n if (track) {\n const props = cmd.props;\n const prevPos = track.posKeyframes[track.posKeyframes.length - 1];\n const newX = props.x !== undefined ? props.x : prevPos.s[0] - track.width * 0.5;\n const newY = props.y !== undefined ? props.y : prevPos.s[1] - track.height * 0.5;\n\n const ease = createLottieEase(timing.durationFrames, 1);\n prevPos.o = ease.o;\n prevPos.i = ease.i;\n\n track.posKeyframes.push({\n t: timing.endFrame,\n s: [newX + track.width * 0.5, newY + track.height * 0.5, 0],\n });\n\n if (props.opacity !== undefined) {\n const prevOp = track.opacityKeyframes[track.opacityKeyframes.length - 1];\n prevOp.o = ease.o;\n prevOp.i = ease.i;\n track.opacityKeyframes.push({\n t: timing.endFrame,\n s: [props.opacity * 100],\n });\n }\n }\n }\n }\n }\n\n // Build Lottie layers\n const layers: LottieLayer[] = [];\n let layerIndex = 1;\n\n for (const track of tracks.values()) {\n const isPosAnimated = track.posKeyframes.length > 1;\n const isOpAnimated = track.opacityKeyframes.length > 1;\n\n const p: LottieAnimatable<[number, number, number], [number, number, number]> = isPosAnimated\n ? { a: 1, k: track.posKeyframes }\n : { a: 0, k: [track.initialX + track.width * 0.5, track.initialY + track.height * 0.5, 0] };\n\n const o: LottieAnimatable<number, [number]> = isOpAnimated\n ? { a: 1, k: track.opacityKeyframes }\n : { a: 0, k: track.opacity * 100 };\n\n const ks: LottieTransform = {\n p,\n a: { a: 0, k: [0, 0, 0] },\n s: { a: 0, k: [100, 100, 100] },\n r: { a: 0, k: 0 },\n o,\n };\n\n // Shape items for rectangle / visual element\n const shapes: LottieShapeItem[] = [\n {\n ty: 'gr',\n nm: track.id,\n it: [\n {\n ty: 'rc',\n nm: 'Rectangle Path',\n d: 1,\n s: { a: 0, k: [track.width, track.height] },\n p: { a: 0, k: [0, 0] },\n r: { a: 0, k: 8 },\n },\n {\n ty: 'fl',\n nm: 'Fill',\n c: { a: 0, k: [0.2, 0.4, 0.9, 1] },\n o: { a: 0, k: 100 },\n r: 1,\n },\n {\n ty: 'tr',\n nm: 'Transform',\n p: { a: 0, k: [0, 0] },\n a: { a: 0, k: [0, 0] },\n s: { a: 0, k: [100, 100] },\n r: { a: 0, k: 0 },\n o: { a: 0, k: 100 },\n },\n ],\n },\n ];\n\n layers.push({\n ddd: 0,\n ind: layerIndex++,\n ty: 4, // Shape layer\n nm: track.id,\n sr: 1,\n ks,\n ao: 0,\n ip: track.inFrame,\n op: track.outFrame,\n st: track.inFrame,\n bm: 0, // Normal blend mode\n shapes,\n });\n }\n\n return {\n v: '5.5.2',\n fr,\n ip: 0,\n op: totalFrames,\n w: width,\n h: height,\n nm: scene.name || 'Flair Animation',\n ddd: 0,\n assets: [],\n layers,\n };\n}\n","import type { Scene, Shot } from '../scene/types.js';\nimport { DEFAULT_STAGE, CAMERA_MS } from '../scene/camera.js';\n\nexport interface Fcp7ExportOptions {\n /** Timeline timebase / frame rate (default 30). */\n frameRate?: number;\n /** NTSC flag (default false, or true if 29.97 / 59.94). */\n ntsc?: boolean;\n /** Starting timecode (default \"00:00:00:00\"). */\n timecodeStart?: string;\n /** Sequence name override. */\n sequenceName?: string;\n /** Media path prefix or file base URL (default \"file://localhost/media/\"). */\n mediaPathPrefix?: string;\n /** Include text generator overlay track for titles/captions (default true). */\n includeTextTrack?: boolean;\n /** Include timeline sequence markers (default true). */\n includeMarkers?: boolean;\n}\n\nexport interface EdlExportOptions {\n /** Timeline timebase / frame rate (default 30). */\n frameRate?: number;\n /** Drop frame flag (default false). */\n dropFrame?: boolean;\n /** Starting timecode (default \"01:00:00:00\" as per standard broadcast EDL practice). */\n timecodeStart?: string;\n /** Reel name identifier (default \"AX\", max 8 chars). */\n reel?: string;\n}\n\nexport interface ShotTiming {\n shot: Shot;\n index: number;\n startFrame: number;\n endFrame: number;\n durationFrames: number;\n startMs: number;\n durationMs: number;\n}\n\nfunction escapeXml(unsafe: string): string {\n return unsafe\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n}\n\n/**\n * Calculates start and duration frame boundaries for each shot in a scene.\n */\nexport function calculateShotTimings(scene: Scene, frameRate: number = 30): ShotTiming[] {\n let currentMs = 0;\n return scene.shots.map((shot, index) => {\n const camMs = index > 0 && shot.camera ? CAMERA_MS : 0;\n const holdMs = shot.hold ?? 800;\n const durationMs = camMs + holdMs;\n const startMs = currentMs;\n currentMs += durationMs;\n\n const startFrame = Math.round((startMs / 1000) * frameRate);\n const durationFrames = Math.max(1, Math.round((durationMs / 1000) * frameRate));\n const endFrame = startFrame + durationFrames;\n\n return {\n shot,\n index,\n startFrame,\n endFrame,\n durationFrames,\n startMs,\n durationMs,\n };\n });\n}\n\n/**\n * Converts frame count to standard SMPTE timecode HH:MM:SS:FF.\n */\nexport function framesToTimecode(\n frame: number,\n timebase: number = 30,\n dropFrame: boolean = false\n): string {\n const roundedFps = Math.round(timebase);\n let totalFrames = Math.max(0, Math.floor(frame));\n\n if (dropFrame && (roundedFps === 30 || roundedFps === 29)) {\n // 29.97 DF: drop frames 0 and 1 at the start of every minute except minutes 0, 10, 20, 30, 40, 50\n const dropFramesPer10Mins = 18;\n const framesPer10Mins = 17982; // 10 * 60 * 30 - 18\n const framesPerMinute = 1798; // 60 * 30 - 2\n\n const d = Math.floor(totalFrames / framesPer10Mins);\n const m = totalFrames % framesPer10Mins;\n\n if (m >= 2) {\n totalFrames += dropFramesPer10Mins * d + 2 * Math.floor((m - 2) / framesPerMinute);\n } else {\n totalFrames += dropFramesPer10Mins * d;\n }\n }\n\n const ff = totalFrames % roundedFps;\n const totalSeconds = Math.floor(totalFrames / roundedFps);\n const ss = totalSeconds % 60;\n const totalMinutes = Math.floor(totalSeconds / 60);\n const mm = totalMinutes % 60;\n const hh = Math.floor(totalMinutes / 60);\n\n const pad = (n: number) => n.toString().padStart(2, '0');\n const separator = dropFrame ? ';' : ':';\n return `${pad(hh)}:${pad(mm)}:${pad(ss)}${separator}${pad(ff)}`;\n}\n\n/**\n * Converts standard SMPTE timecode HH:MM:SS:FF to frame count.\n */\nexport function timecodeToFrames(\n timecode: string,\n timebase: number = 30,\n dropFrame: boolean = false\n): number {\n const parts = timecode.split(/[:;]/).map(Number);\n if (parts.length !== 4 || parts.some(isNaN)) {\n return 0;\n }\n const [hh, mm, ss, ff] = parts;\n const roundedFps = Math.round(timebase);\n\n if (dropFrame && (roundedFps === 30 || roundedFps === 29)) {\n const totalMinutes = hh * 60 + mm;\n const dropCount = (totalMinutes - Math.floor(totalMinutes / 10)) * 2;\n return ((hh * 3600 + mm * 60 + ss) * roundedFps + ff) - dropCount;\n }\n\n return (hh * 3600 + mm * 60 + ss) * roundedFps + ff;\n}\n\n/**\n * Serializes a Flair Scene into Apple Final Cut Pro 7 XML Interchange Format (xmeml v4).\n * Compatible with Premiere Pro, DaVinci Resolve, and Final Cut Pro.\n */\nexport function exportFcp7Xml(scene: Scene, options: Fcp7ExportOptions = {}): string {\n const frameRate = options.frameRate ?? 30;\n const timebase = Math.round(frameRate);\n const ntsc = options.ntsc ?? (Math.abs(frameRate - 29.97) < 0.05 || Math.abs(frameRate - 59.94) < 0.05);\n const ntscStr = ntsc ? 'TRUE' : 'FALSE';\n const width = scene.stage?.width ?? DEFAULT_STAGE.width;\n const height = scene.stage?.height ?? DEFAULT_STAGE.height;\n const sequenceName = options.sequenceName ?? scene.name ?? 'Flair Sequence';\n const timecodeStart = options.timecodeStart ?? '00:00:00:00';\n const mediaPrefix = options.mediaPathPrefix ?? 'file://localhost/media/';\n const includeTextTrack = options.includeTextTrack ?? true;\n const includeMarkers = options.includeMarkers ?? true;\n\n const timings = calculateShotTimings(scene, frameRate);\n const totalFrames = timings.length > 0 ? timings[timings.length - 1].endFrame : 0;\n const startFrameOffset = timecodeToFrames(timecodeStart, frameRate);\n\n const lines: string[] = [];\n\n lines.push('<?xml version=\"1.0\" encoding=\"UTF-8\"?>');\n lines.push('<!DOCTYPE xmeml>');\n lines.push('<xmeml version=\"4\">');\n lines.push(` <sequence id=\"sequence-1\">`);\n lines.push(` <name>${escapeXml(sequenceName)}</name>`);\n lines.push(` <duration>${totalFrames}</duration>`);\n lines.push(' <rate>');\n lines.push(` <timebase>${timebase}</timebase>`);\n lines.push(` <ntsc>${ntscStr}</ntsc>`);\n lines.push(' </rate>');\n lines.push(' <timecode>');\n lines.push(' <rate>');\n lines.push(` <timebase>${timebase}</timebase>`);\n lines.push(` <ntsc>${ntscStr}</ntsc>`);\n lines.push(' </rate>');\n lines.push(` <string>${timecodeStart}</string>`);\n lines.push(` <frame>${startFrameOffset}</frame>`);\n lines.push(' <displayformat>NDF</displayformat>');\n lines.push(' </timecode>');\n\n // Sequence Timeline Markers\n if (includeMarkers) {\n timings.forEach(({ shot, index, startFrame, endFrame }) => {\n const name = shot.title || `Shot ${index + 1}`;\n const comment = shot.caption || '';\n lines.push(' <marker>');\n lines.push(` <name>${escapeXml(name)}</name>`);\n lines.push(` <comment>${escapeXml(comment)}</comment>`);\n lines.push(` <in>${startFrame}</in>`);\n lines.push(` <out>${endFrame}</out>`);\n lines.push(' </marker>');\n });\n }\n\n lines.push(' <media>');\n lines.push(' <video>');\n lines.push(' <format>');\n lines.push(' <samplecharacteristics>');\n lines.push(` <width>${width}</width>`);\n lines.push(` <height>${height}</height>`);\n lines.push(' <pixelaspectratio>square</pixelaspectratio>');\n lines.push(' <rate>');\n lines.push(` <timebase>${timebase}</timebase>`);\n lines.push(` <ntsc>${ntscStr}</ntsc>`);\n lines.push(' </rate>');\n lines.push(' </samplecharacteristics>');\n lines.push(' </format>');\n\n // Video Track 1: Base Shot Clips\n lines.push(' <track>');\n lines.push(' <enabled>TRUE</enabled>');\n lines.push(' <locked>FALSE</locked>');\n\n timings.forEach(({ shot, index, startFrame, endFrame, durationFrames }) => {\n const shotName = shot.title || `Shot_${index + 1}`;\n const fileId = `file-${index + 1}`;\n const clipId = `clipitem-${index + 1}`;\n const fileName = `${shotName.replace(/[^a-zA-Z0-9_-]/g, '_')}.mov`;\n const fileUrl = `${mediaPrefix.replace(/\\/+$/, '')}/${fileName}`;\n\n lines.push(` <clipitem id=\"${clipId}\">`);\n lines.push(` <name>${escapeXml(shotName)}</name>`);\n lines.push(` <duration>${durationFrames}</duration>`);\n lines.push(' <rate>');\n lines.push(` <timebase>${timebase}</timebase>`);\n lines.push(` <ntsc>${ntscStr}</ntsc>`);\n lines.push(' </rate>');\n lines.push(` <start>${startFrame}</start>`);\n lines.push(` <end>${endFrame}</end>`);\n lines.push(' <in>0</in>');\n lines.push(` <out>${durationFrames}</out>`);\n\n // File element\n lines.push(` <file id=\"${fileId}\">`);\n lines.push(` <name>${escapeXml(fileName)}</name>`);\n lines.push(` <pathurl>${escapeXml(fileUrl)}</pathurl>`);\n lines.push(' <rate>');\n lines.push(` <timebase>${timebase}</timebase>`);\n lines.push(` <ntsc>${ntscStr}</ntsc>`);\n lines.push(' </rate>');\n lines.push(` <duration>${durationFrames}</duration>`);\n lines.push(' <timecode>');\n lines.push(' <rate>');\n lines.push(` <timebase>${timebase}</timebase>`);\n lines.push(` <ntsc>${ntscStr}</ntsc>`);\n lines.push(' </rate>');\n lines.push(' <string>00:00:00:00</string>');\n lines.push(' <frame>0</frame>');\n lines.push(' <displayformat>NDF</displayformat>');\n lines.push(' </timecode>');\n lines.push(' <media>');\n lines.push(' <video>');\n lines.push(' <samplecharacteristics>');\n lines.push(` <width>${width}</width>`);\n lines.push(` <height>${height}</height>`);\n lines.push(' </samplecharacteristics>');\n lines.push(' </video>');\n lines.push(' </media>');\n lines.push(' </file>');\n\n // Clip marker if caption is present\n if (shot.caption) {\n lines.push(' <marker>');\n lines.push(` <name>${escapeXml(shotName)} Narration</name>`);\n lines.push(` <comment>${escapeXml(shot.caption)}</comment>`);\n lines.push(' <in>0</in>');\n lines.push(` <out>${durationFrames}</out>`);\n lines.push(' </marker>');\n }\n\n // Basic Motion Filter\n lines.push(' <filter>');\n lines.push(' <effect>');\n lines.push(' <name>Basic Motion</name>');\n lines.push(' <effectid>basic</effectid>');\n lines.push(' <effectcategory>motion</effectcategory>');\n lines.push(' <effecttype>motion</effecttype>');\n lines.push(' <mediatype>video</mediatype>');\n lines.push(' <parameter>');\n lines.push(' <parameterid>scale</parameterid>');\n lines.push(' <name>Scale</name>');\n lines.push(' <value>100</value>');\n lines.push(' </parameter>');\n lines.push(' </effect>');\n lines.push(' </filter>');\n lines.push(' </clipitem>');\n });\n\n lines.push(' </track>');\n\n // Video Track 2: Title / Narration Overlay Track (Generators)\n const hasTextOverlays = timings.some((t) => t.shot.title || t.shot.caption);\n if (includeTextTrack && hasTextOverlays) {\n lines.push(' <track>');\n lines.push(' <enabled>TRUE</enabled>');\n lines.push(' <locked>FALSE</locked>');\n\n timings.forEach(({ shot, index, startFrame, endFrame, durationFrames }) => {\n const text = shot.title ?? shot.caption;\n if (!text) return;\n\n const generatorId = `generator-title-${index + 1}`;\n lines.push(` <clipitem id=\"${generatorId}\">`);\n lines.push(` <name>${escapeXml(text)}</name>`);\n lines.push(` <duration>${durationFrames}</duration>`);\n lines.push(' <rate>');\n lines.push(` <timebase>${timebase}</timebase>`);\n lines.push(` <ntsc>${ntscStr}</ntsc>`);\n lines.push(' </rate>');\n lines.push(` <start>${startFrame}</start>`);\n lines.push(` <end>${endFrame}</end>`);\n lines.push(' <in>0</in>');\n lines.push(` <out>${durationFrames}</out>`);\n lines.push(' <effect>');\n lines.push(' <name>Text</name>');\n lines.push(' <effectid>Text</effectid>');\n lines.push(' <effectcategory>Text</effectcategory>');\n lines.push(' <effecttype>generator</effecttype>');\n lines.push(' <mediatype>video</mediatype>');\n lines.push(' <parameter>');\n lines.push(' <parameterid>str</parameterid>');\n lines.push(' <name>Text</name>');\n lines.push(` <value>${escapeXml(text)}</value>`);\n lines.push(' </parameter>');\n lines.push(' </effect>');\n lines.push(' </clipitem>');\n });\n\n lines.push(' </track>');\n }\n\n lines.push(' </video>');\n lines.push(' </media>');\n lines.push(' </sequence>');\n lines.push('</xmeml>');\n\n return lines.join('\\n');\n}\n\n/**\n * Serializes a Flair Scene into standard CMX 3600 Edit Decision List (EDL).\n * Fully readable by DaVinci Resolve, Premiere Pro, Avid, and Lightworks.\n */\nexport function exportCmx3600Edl(scene: Scene, options: EdlExportOptions = {}): string {\n const frameRate = options.frameRate ?? 30;\n const timebase = Math.round(frameRate);\n const dropFrame = options.dropFrame ?? false;\n const reel = (options.reel ?? 'AX').slice(0, 8).padEnd(8, ' ');\n const title = (scene.name ?? 'UNTITLED').toUpperCase().replace(/[^A-Z0-9_ -]/g, '');\n const startTc = options.timecodeStart ?? '01:00:00:00';\n\n const timings = calculateShotTimings(scene, frameRate);\n let recordFrame = timecodeToFrames(startTc, timebase, dropFrame);\n\n const lines: string[] = [];\n lines.push(`TITLE: ${title}`);\n lines.push(dropFrame ? 'FCM: DROP FRAME' : 'FCM: NON-DROP FRAME');\n lines.push('');\n\n timings.forEach(({ shot, index, durationFrames }) => {\n const eventNum = (index + 1).toString().padStart(3, '0');\n const srcInTc = '00:00:00:00';\n const srcOutTc = framesToTimecode(durationFrames, timebase, dropFrame);\n\n const recInTc = framesToTimecode(recordFrame, timebase, dropFrame);\n recordFrame += durationFrames;\n const recOutTc = framesToTimecode(recordFrame, timebase, dropFrame);\n\n // Standard CMX 3600 line format:\n // Event Reel Track Transition SourceIn SourceOut RecordIn RecordOut\n lines.push(`${eventNum} ${reel} V C ${srcInTc} ${srcOutTc} ${recInTc} ${recOutTc}`);\n\n const clipName = shot.title || `Shot ${index + 1}`;\n lines.push(`* FROM CLIP NAME: ${clipName}`);\n if (shot.caption) {\n lines.push(`* COMMENT: ${shot.caption}`);\n }\n lines.push('');\n });\n\n return lines.join('\\n');\n}\n"],"mappings":"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","names":["escapeXml"]}
|
package/dist/types-CkKkavvB.d.ts
DELETED
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
import { C as ControlValue, a as ControlDef } from './controls-DdXMbovi.js';
|
|
2
|
-
import { S as SvgMotion } from './motion-D0TVv2CF.js';
|
|
3
|
-
|
|
4
|
-
/** What an effect is for, used to group the catalog. */
|
|
5
|
-
type EffectCategory = "gradient" | "organic" | "pattern" | "light" | "space" | "retro" | "image" | "transition" | "loader" | "data" | "icon";
|
|
6
|
-
/** Every category, in the order the catalog lists them. */
|
|
7
|
-
declare const EFFECT_CATEGORIES: readonly EffectCategory[];
|
|
8
|
-
/** Named control values: a look an effect can be switched to. */
|
|
9
|
-
interface EffectPreset {
|
|
10
|
-
name: string;
|
|
11
|
-
values: Record<string, ControlValue>;
|
|
12
|
-
}
|
|
13
|
-
interface EffectBase {
|
|
14
|
-
/** Stable id, kebab-case: `aurora`, `image-glitch`. */
|
|
15
|
-
id: string;
|
|
16
|
-
name: string;
|
|
17
|
-
/** One sentence: what it looks like. */
|
|
18
|
-
description: string;
|
|
19
|
-
category: EffectCategory;
|
|
20
|
-
tags: readonly string[];
|
|
21
|
-
presets?: readonly EffectPreset[];
|
|
22
|
-
}
|
|
23
|
-
/**
|
|
24
|
-
* A WebGL2 fragment shader (Shadertoy-style `mainImage`). Its controls are the
|
|
25
|
-
* `// @control` uniforms in `source`; run it with `ShaderRenderer`,
|
|
26
|
-
* `ShaderFrames`, `ThumbnailPool`, `recordGif` or `standaloneHtml`.
|
|
27
|
-
*/
|
|
28
|
-
interface ShaderEffect extends EffectBase {
|
|
29
|
-
kind: "shader";
|
|
30
|
-
source: string;
|
|
31
|
-
/** Seconds after which it repeats exactly, when it loops seamlessly. */
|
|
32
|
-
loop?: number;
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* A control of a vector effect. The same shape as a shader's controls (see
|
|
36
|
-
* `ControlDef`) less what only sources have; `vec3` colours are written into
|
|
37
|
-
* the picture as `#rrggbb`.
|
|
38
|
-
*/
|
|
39
|
-
type VectorControl = Pick<ControlDef, "name" | "type" | "default"> & Partial<Pick<ControlDef, "label" | "color" | "min" | "max" | "step">>;
|
|
40
|
-
/**
|
|
41
|
-
* An animated SVG picture (see `SvgMotion`). `{{name}}` in the motion is
|
|
42
|
-
* replaced by the control's value (see {@link renderVectorEffect}); run the
|
|
43
|
-
* result with `SvgPlayer`.
|
|
44
|
-
*/
|
|
45
|
-
interface VectorEffect extends EffectBase {
|
|
46
|
-
kind: "vector";
|
|
47
|
-
motion: SvgMotion;
|
|
48
|
-
controls: readonly VectorControl[];
|
|
49
|
-
/**
|
|
50
|
-
* More placeholders worked out from the control values (a bar's height
|
|
51
|
-
* from its value, say), filled in after the controls.
|
|
52
|
-
*/
|
|
53
|
-
derived?: (values: Record<string, ControlValue>) => Record<string, string | number>;
|
|
54
|
-
/** Ms to rest on the last frame before it loops (default 600). */
|
|
55
|
-
hold?: number;
|
|
56
|
-
}
|
|
57
|
-
type EffectDefinition = ShaderEffect | VectorEffect;
|
|
58
|
-
/** A vector control as a full `ControlDef`, so editors treat both kinds of effect alike. */
|
|
59
|
-
declare function vectorControlDef(control: VectorControl, line?: number): ControlDef;
|
|
60
|
-
/**
|
|
61
|
-
* A vector effect's motion with its controls filled in: `"{{name}}"` (a
|
|
62
|
-
* whole JSON string) becomes the value itself, so numbers stay numbers, and
|
|
63
|
-
* `{{name}}` inside a string becomes its text. Missing values use defaults.
|
|
64
|
-
*/
|
|
65
|
-
declare function renderVectorEffect(effect: Pick<VectorEffect, "motion" | "controls" | "derived">, values?: Record<string, ControlValue>): SvgMotion;
|
|
66
|
-
/** A colour control for vector effects. */
|
|
67
|
-
declare function colorControl(name: string, hex: string, label?: string): VectorControl;
|
|
68
|
-
/** A number control for vector effects. */
|
|
69
|
-
declare function numberControl(name: string, value: number, min: number, max: number, label?: string, step?: number): VectorControl;
|
|
70
|
-
|
|
71
|
-
export { type EffectDefinition as E, type ShaderEffect as S, type VectorControl as V, type EffectCategory as a, EFFECT_CATEGORIES as b, type EffectPreset as c, type VectorEffect as d, colorControl as e, numberControl as n, renderVectorEffect as r, vectorControlDef as v };
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|