@pixerate/flair 0.7.0 → 0.9.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 +23 -0
- package/README.md +16 -9
- package/dist/catalog/index.d.ts +30 -4
- package/dist/catalog/index.js +4 -1
- package/dist/chunk-F754SFRY.js +4973 -0
- package/dist/chunk-F754SFRY.js.map +1 -0
- package/dist/chunk-OHW7UG6D.js +336 -0
- package/dist/chunk-OHW7UG6D.js.map +1 -0
- package/dist/chunk-P5TSANZY.js +1 -0
- package/dist/{chunk-YAAVMNHN.js → chunk-TDMD5XAU.js} +29 -3
- package/dist/chunk-TDMD5XAU.js.map +1 -0
- package/dist/editor/index.d.ts +2 -2
- package/dist/editor/index.js +4 -3
- package/dist/editor/index.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +14 -4
- package/dist/index.js.map +1 -1
- package/dist/{motion-BwIlugh1.d.ts → motion-Ce0lmzm3.d.ts} +28 -2
- package/dist/{types-CfIFlnMY.d.ts → types-CgHG5JLv.d.ts} +8 -2
- package/dist/vector/index.d.ts +30 -3
- package/dist/vector/index.js +14 -4
- package/package.json +1 -1
- package/dist/chunk-CFCLT2XL.js +0 -1
- package/dist/chunk-J2WIWT4J.js +0 -2275
- package/dist/chunk-J2WIWT4J.js.map +0 -1
- package/dist/chunk-YAAVMNHN.js.map +0 -1
- /package/dist/{chunk-CFCLT2XL.js.map → chunk-P5TSANZY.js.map} +0 -0
package/dist/chunk-J2WIWT4J.js
DELETED
|
@@ -1,2275 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
getTransitionGLSL
|
|
3
|
-
} from "./chunk-EHDJ3CPJ.js";
|
|
4
|
-
import {
|
|
5
|
-
checkControlValue,
|
|
6
|
-
controlFromDeclaration,
|
|
7
|
-
controlValue,
|
|
8
|
-
hexToRgb,
|
|
9
|
-
parseControls,
|
|
10
|
-
rgbToHex
|
|
11
|
-
} from "./chunk-WC3VADZC.js";
|
|
12
|
-
|
|
13
|
-
// src/catalog/glsl.ts
|
|
14
|
-
var HASH = `
|
|
15
|
-
float hash12(vec2 p) {
|
|
16
|
-
vec3 p3 = fract(vec3(p.xyx) * 0.1031);
|
|
17
|
-
p3 += dot(p3, p3.yzx + 33.33);
|
|
18
|
-
return fract((p3.x + p3.y) * p3.z);
|
|
19
|
-
}
|
|
20
|
-
vec2 hash22(vec2 p) {
|
|
21
|
-
vec3 p3 = fract(vec3(p.xyx) * vec3(0.1031, 0.1030, 0.0973));
|
|
22
|
-
p3 += dot(p3, p3.yzx + 33.33);
|
|
23
|
-
return fract((p3.xx + p3.yz) * p3.zy);
|
|
24
|
-
}`.trim();
|
|
25
|
-
var NOISE = `
|
|
26
|
-
float vnoise(vec2 p) {
|
|
27
|
-
vec2 i = floor(p);
|
|
28
|
-
vec2 f = fract(p);
|
|
29
|
-
vec2 u = f * f * (3.0 - 2.0 * f);
|
|
30
|
-
float a = hash12(i);
|
|
31
|
-
float b = hash12(i + vec2(1.0, 0.0));
|
|
32
|
-
float c = hash12(i + vec2(0.0, 1.0));
|
|
33
|
-
float d = hash12(i + vec2(1.0, 1.0));
|
|
34
|
-
return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);
|
|
35
|
-
}`.trim();
|
|
36
|
-
var FBM = `
|
|
37
|
-
float fbm(vec2 p, int octaves) {
|
|
38
|
-
float sum = 0.0;
|
|
39
|
-
float amp = 0.5;
|
|
40
|
-
mat2 turn = mat2(0.8, 0.6, -0.6, 0.8);
|
|
41
|
-
for (int i = 0; i < 8; i++) {
|
|
42
|
-
if (i >= octaves) break;
|
|
43
|
-
sum += amp * vnoise(p);
|
|
44
|
-
p = turn * p * 2.02 + 17.0;
|
|
45
|
-
amp *= 0.5;
|
|
46
|
-
}
|
|
47
|
-
return sum;
|
|
48
|
-
}`.trim();
|
|
49
|
-
var PALETTE = `
|
|
50
|
-
vec3 palette(float t, vec3 a, vec3 b, vec3 c, vec3 d) {
|
|
51
|
-
return a + b * cos(6.28318 * (c * t + d));
|
|
52
|
-
}`.trim();
|
|
53
|
-
var OKLAB_MIX = `
|
|
54
|
-
vec3 toOklab(vec3 c) {
|
|
55
|
-
c = pow(max(c, 0.0), vec3(2.2));
|
|
56
|
-
vec3 lms = mat3(0.4122, 0.2119, 0.0883, 0.5363, 0.6807, 0.2817, 0.0514, 0.1074, 0.6300) * c;
|
|
57
|
-
lms = pow(lms, vec3(1.0 / 3.0));
|
|
58
|
-
return mat3(0.2105, 1.9780, 0.0259, 0.7936, -2.4286, 0.7828, -0.0041, 0.4506, -0.8087) * lms;
|
|
59
|
-
}
|
|
60
|
-
vec3 fromOklab(vec3 c) {
|
|
61
|
-
vec3 lms = mat3(1.0, 1.0, 1.0, 0.3963, -0.1056, -0.0895, 0.2158, -0.0639, -1.2915) * c;
|
|
62
|
-
lms = lms * lms * lms;
|
|
63
|
-
vec3 rgb = mat3(4.0767, -1.2684, -0.0042, -3.3077, 2.6098, -0.7034, 0.2310, -0.3413, 1.7076) * lms;
|
|
64
|
-
return pow(max(rgb, 0.0), vec3(1.0 / 2.2));
|
|
65
|
-
}
|
|
66
|
-
vec3 oklabMix(vec3 a, vec3 b, float t) {
|
|
67
|
-
return fromOklab(mix(toOklab(a), toOklab(b), t));
|
|
68
|
-
}`.trim();
|
|
69
|
-
var ROTATE = `
|
|
70
|
-
mat2 rot(float a) {
|
|
71
|
-
float c = cos(a);
|
|
72
|
-
float s = sin(a);
|
|
73
|
-
return mat2(c, s, -s, c);
|
|
74
|
-
}`.trim();
|
|
75
|
-
function glsl(...parts) {
|
|
76
|
-
return parts.map((p) => p.trim()).join("\n\n") + "\n";
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// src/catalog/shaders/gradients.ts
|
|
80
|
-
var aurora = {
|
|
81
|
-
kind: "shader",
|
|
82
|
-
id: "aurora",
|
|
83
|
-
name: "Aurora",
|
|
84
|
-
description: "Soft curtains of light drifting over a night sky.",
|
|
85
|
-
category: "gradient",
|
|
86
|
-
tags: ["background", "hero", "calm", "night"],
|
|
87
|
-
source: glsl(
|
|
88
|
-
`// Aurora: curtains of light drifting over a night sky.
|
|
89
|
-
uniform vec3 uSky; // @control color default=#050816 label="Sky"
|
|
90
|
-
uniform vec3 uGlowA; // @control color default=#22D3A6 label="Glow A"
|
|
91
|
-
uniform vec3 uGlowB; // @control color default=#7C3AED label="Glow B"
|
|
92
|
-
uniform float uSpeed; // @control min=0 max=2 default=0.35 label="Speed"
|
|
93
|
-
uniform float uIntensity; // @control min=0 max=2 default=1 label="Intensity"
|
|
94
|
-
uniform float uHeight; // @control min=0 max=1 default=0.55 label="Height"`,
|
|
95
|
-
HASH,
|
|
96
|
-
NOISE,
|
|
97
|
-
FBM,
|
|
98
|
-
`void mainImage(out vec4 fragColor, in vec2 fragCoord) {
|
|
99
|
-
vec2 uv = fragCoord / iResolution.xy;
|
|
100
|
-
float t = iTime * uSpeed;
|
|
101
|
-
vec3 col = uSky * (0.6 + 0.4 * uv.y);
|
|
102
|
-
for (int i = 0; i < 3; i++) {
|
|
103
|
-
float fi = float(i);
|
|
104
|
-
float x = uv.x * (1.4 + 0.3 * fi) + fi * 3.7;
|
|
105
|
-
float wave = fbm(vec2(x * 1.5 + t * 0.4, t * 0.25 + fi), 4);
|
|
106
|
-
float centre = uHeight + 0.18 * (wave - 0.5) + 0.06 * fi;
|
|
107
|
-
float band = exp(-pow((uv.y - centre) * (7.0 + 2.0 * fi), 2.0));
|
|
108
|
-
float rays = 0.55 + 0.45 * fbm(vec2(x * 12.0, t * 0.6 + fi * 9.0), 3);
|
|
109
|
-
float above = smoothstep(centre - 0.05, centre + 0.35, uv.y) * 0.35;
|
|
110
|
-
vec3 glow = mix(uGlowA, uGlowB, clamp(uv.y * 1.4 - 0.2 + 0.25 * fi, 0.0, 1.0));
|
|
111
|
-
col += glow * (band * rays + above * band) * 0.6 * uIntensity;
|
|
112
|
-
}
|
|
113
|
-
float stars = step(0.997, hash12(floor(fragCoord * 0.5))) * smoothstep(0.3, 1.0, uv.y);
|
|
114
|
-
col += stars * 0.6 * (0.5 + 0.5 * sin(iTime * 3.0 + hash12(floor(fragCoord)) * 40.0));
|
|
115
|
-
fragColor = vec4(col, 1.0);
|
|
116
|
-
}`
|
|
117
|
-
),
|
|
118
|
-
presets: [
|
|
119
|
-
{ name: "Arctic", values: { uGlowA: [0.13, 0.83, 0.65], uGlowB: [0.49, 0.23, 0.93] } },
|
|
120
|
-
{ name: "Ember", values: { uSky: [0.06, 0.02, 0.03], uGlowA: [1, 0.45, 0.1], uGlowB: [0.9, 0.1, 0.4] } },
|
|
121
|
-
{ name: "Ice", values: { uSky: [0.01, 0.03, 0.06], uGlowA: [0.5, 0.9, 1], uGlowB: [0.2, 0.4, 1] } }
|
|
122
|
-
]
|
|
123
|
-
};
|
|
124
|
-
var meshGradient = {
|
|
125
|
-
kind: "shader",
|
|
126
|
-
id: "mesh-gradient",
|
|
127
|
-
name: "Mesh gradient",
|
|
128
|
-
description: "Four colours blended across a slowly warping mesh, mixed in Oklab.",
|
|
129
|
-
category: "gradient",
|
|
130
|
-
tags: ["background", "hero", "brand", "soft"],
|
|
131
|
-
source: glsl(
|
|
132
|
-
`// Mesh gradient: four colours on drifting points, blended in Oklab.
|
|
133
|
-
uniform vec3 uColorA; // @control color default=#FF6B6B label="Colour A"
|
|
134
|
-
uniform vec3 uColorB; // @control color default=#FFD93D label="Colour B"
|
|
135
|
-
uniform vec3 uColorC; // @control color default=#6BCB77 label="Colour C"
|
|
136
|
-
uniform vec3 uColorD; // @control color default=#4D96FF label="Colour D"
|
|
137
|
-
uniform float uSpeed; // @control min=0 max=2 default=0.3 label="Speed"
|
|
138
|
-
uniform float uWarp; // @control min=0 max=1 default=0.35 label="Warp"
|
|
139
|
-
uniform float uGrain; // @control min=0 max=0.2 default=0.04 label="Grain"`,
|
|
140
|
-
HASH,
|
|
141
|
-
NOISE,
|
|
142
|
-
OKLAB_MIX,
|
|
143
|
-
`void mainImage(out vec4 fragColor, in vec2 fragCoord) {
|
|
144
|
-
vec2 uv = fragCoord / iResolution.xy;
|
|
145
|
-
float t = iTime * uSpeed;
|
|
146
|
-
vec2 w = uv + uWarp * 0.3 * vec2(vnoise(uv * 2.0 + t), vnoise(uv * 2.0 - t + 5.0)) - uWarp * 0.15;
|
|
147
|
-
vec2 pa = vec2(0.2, 0.2) + 0.15 * vec2(sin(t * 1.1), cos(t * 0.9));
|
|
148
|
-
vec2 pb = vec2(0.8, 0.25) + 0.15 * vec2(cos(t * 0.7), sin(t * 1.3));
|
|
149
|
-
vec2 pc = vec2(0.25, 0.8) + 0.15 * vec2(sin(t * 0.8 + 2.0), cos(t * 1.2));
|
|
150
|
-
vec2 pd = vec2(0.8, 0.8) + 0.15 * vec2(cos(t * 1.0 + 1.0), sin(t * 0.6));
|
|
151
|
-
vec4 d = vec4(distance(w, pa), distance(w, pb), distance(w, pc), distance(w, pd));
|
|
152
|
-
vec4 k = 1.0 / (d * d + 0.02);
|
|
153
|
-
k /= dot(k, vec4(1.0));
|
|
154
|
-
vec3 ab = oklabMix(uColorA, uColorB, k.y / max(k.x + k.y, 1e-4));
|
|
155
|
-
vec3 cd = oklabMix(uColorC, uColorD, k.w / max(k.z + k.w, 1e-4));
|
|
156
|
-
vec3 col = oklabMix(ab, cd, k.z + k.w);
|
|
157
|
-
col += (hash12(fragCoord + fract(iTime) * 100.0) - 0.5) * uGrain;
|
|
158
|
-
fragColor = vec4(col, 1.0);
|
|
159
|
-
}`
|
|
160
|
-
),
|
|
161
|
-
presets: [
|
|
162
|
-
{ 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] } },
|
|
163
|
-
{ name: "Lagoon", values: { uColorA: [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] } },
|
|
164
|
-
{ 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] } }
|
|
165
|
-
]
|
|
166
|
-
};
|
|
167
|
-
var conicSweep = {
|
|
168
|
-
kind: "shader",
|
|
169
|
-
id: "conic-sweep",
|
|
170
|
-
name: "Conic sweep",
|
|
171
|
-
description: "A rotating conic gradient with a soft glowing core.",
|
|
172
|
-
category: "gradient",
|
|
173
|
-
tags: ["background", "spotlight", "brand"],
|
|
174
|
-
loop: 10,
|
|
175
|
-
source: glsl(
|
|
176
|
-
`// Conic sweep: a rotating conic gradient with a glowing core.
|
|
177
|
-
uniform vec3 uColorA; // @control color default=#0EA5E9 label="Colour A"
|
|
178
|
-
uniform vec3 uColorB; // @control color default=#A855F7 label="Colour B"
|
|
179
|
-
uniform vec3 uColorC; // @control color default=#F43F5E label="Colour C"
|
|
180
|
-
uniform float uTurns; // @control min=-3 max=3 default=1 step=1 label="Turns per loop"
|
|
181
|
-
uniform float uCore; // @control min=0 max=1 default=0.4 label="Core glow"
|
|
182
|
-
uniform vec2 uCenter; // @control min=-0.5 max=0.5 default=0,0 label="Centre"`,
|
|
183
|
-
OKLAB_MIX,
|
|
184
|
-
`void mainImage(out vec4 fragColor, in vec2 fragCoord) {
|
|
185
|
-
vec2 p = (fragCoord - 0.5 * iResolution.xy) / iResolution.y - uCenter;
|
|
186
|
-
float a = atan(p.y, p.x) / 6.28318 + 0.5;
|
|
187
|
-
a = fract(a + iTime * uTurns / 10.0);
|
|
188
|
-
float s = a * 3.0;
|
|
189
|
-
vec3 col = s < 1.0 ? oklabMix(uColorA, uColorB, s)
|
|
190
|
-
: s < 2.0 ? oklabMix(uColorB, uColorC, s - 1.0)
|
|
191
|
-
: oklabMix(uColorC, uColorA, s - 2.0);
|
|
192
|
-
float r = length(p);
|
|
193
|
-
col *= 0.75 + 0.25 * smoothstep(0.9, 0.0, r);
|
|
194
|
-
col += uCore * exp(-r * 6.0);
|
|
195
|
-
fragColor = vec4(col, 1.0);
|
|
196
|
-
}`
|
|
197
|
-
)
|
|
198
|
-
};
|
|
199
|
-
var silk = {
|
|
200
|
-
kind: "shader",
|
|
201
|
-
id: "silk",
|
|
202
|
-
name: "Silk",
|
|
203
|
-
description: "Folds of glossy fabric rippling in a slow breeze.",
|
|
204
|
-
category: "gradient",
|
|
205
|
-
tags: ["background", "luxury", "soft"],
|
|
206
|
-
source: glsl(
|
|
207
|
-
`// Silk: glossy folds of fabric rippling slowly.
|
|
208
|
-
uniform vec3 uBase; // @control color default=#3B0764 label="Base"
|
|
209
|
-
uniform vec3 uSheen; // @control color default=#F0ABFC label="Sheen"
|
|
210
|
-
uniform float uSpeed; // @control min=0 max=2 default=0.4 label="Speed"
|
|
211
|
-
uniform float uFolds; // @control min=1 max=10 default=5 label="Folds"
|
|
212
|
-
uniform float uGloss; // @control min=0 max=2 default=1 label="Gloss"`,
|
|
213
|
-
ROTATE,
|
|
214
|
-
`float folds(vec2 p, float t) {
|
|
215
|
-
float h = 0.0;
|
|
216
|
-
for (int i = 1; i <= 4; i++) {
|
|
217
|
-
float fi = float(i);
|
|
218
|
-
h += sin(p.x * uFolds * fi * 0.7 + t * (1.0 + 0.3 * fi) + sin(p.y * 2.0 * fi + t)) / fi;
|
|
219
|
-
}
|
|
220
|
-
return h;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
|
|
224
|
-
vec2 p = rot(0.6) * (fragCoord - 0.5 * iResolution.xy) / iResolution.y;
|
|
225
|
-
float t = iTime * uSpeed;
|
|
226
|
-
float e = 0.002;
|
|
227
|
-
float hx = (folds(p + vec2(e, 0.0), t) - folds(p - vec2(e, 0.0), t)) / (2.0 * e);
|
|
228
|
-
float hy = (folds(p + vec2(0.0, e), t) - folds(p - vec2(0.0, e), t)) / (2.0 * e);
|
|
229
|
-
vec3 n = normalize(vec3(-hx * 0.12, -hy * 0.12, 1.0));
|
|
230
|
-
vec3 l = normalize(vec3(-0.4, 0.6, 0.7));
|
|
231
|
-
float diff = clamp(dot(n, l), 0.0, 1.0);
|
|
232
|
-
float spec = pow(clamp(dot(reflect(-l, n), vec3(0.0, 0.0, 1.0)), 0.0, 1.0), 20.0) * uGloss;
|
|
233
|
-
vec3 col = uBase * (0.35 + 0.9 * diff) + uSheen * (spec + 0.12 * pow(diff, 6.0));
|
|
234
|
-
fragColor = vec4(col, 1.0);
|
|
235
|
-
}`
|
|
236
|
-
),
|
|
237
|
-
presets: [
|
|
238
|
-
{ name: "Champagne", values: { uBase: [0.55, 0.42, 0.25], uSheen: [1, 0.95, 0.8] } },
|
|
239
|
-
{ name: "Midnight", values: { uBase: [0.04, 0.08, 0.2], uSheen: [0.6, 0.75, 1] } }
|
|
240
|
-
]
|
|
241
|
-
};
|
|
242
|
-
var duotoneFlow = {
|
|
243
|
-
kind: "shader",
|
|
244
|
-
id: "gradient-flow",
|
|
245
|
-
name: "Gradient flow",
|
|
246
|
-
description: "Two colours flowing through each other along a warped noise field.",
|
|
247
|
-
category: "gradient",
|
|
248
|
-
tags: ["background", "brand", "liquid"],
|
|
249
|
-
source: glsl(
|
|
250
|
-
`// Gradient flow: two colours flowing through a warped noise field.
|
|
251
|
-
uniform vec3 uColorA; // @control color default=#1E3A8A label="Colour A"
|
|
252
|
-
uniform vec3 uColorB; // @control color default=#F97316 label="Colour B"
|
|
253
|
-
uniform float uSpeed; // @control min=0 max=2 default=0.25 label="Speed"
|
|
254
|
-
uniform float uScale; // @control min=0.5 max=6 default=2 label="Scale"
|
|
255
|
-
uniform float uContrast; // @control min=0.5 max=4 default=1.5 label="Contrast"`,
|
|
256
|
-
HASH,
|
|
257
|
-
NOISE,
|
|
258
|
-
FBM,
|
|
259
|
-
OKLAB_MIX,
|
|
260
|
-
`void mainImage(out vec4 fragColor, in vec2 fragCoord) {
|
|
261
|
-
vec2 p = fragCoord / iResolution.y * uScale;
|
|
262
|
-
float t = iTime * uSpeed;
|
|
263
|
-
vec2 q = vec2(fbm(p + t, 4), fbm(p + vec2(5.2, 1.3) - t, 4));
|
|
264
|
-
float f = fbm(p + 3.0 * q + vec2(1.7, 9.2) + 0.5 * t, 5);
|
|
265
|
-
f = clamp((f - 0.5) * uContrast + 0.5, 0.0, 1.0);
|
|
266
|
-
fragColor = vec4(oklabMix(uColorA, uColorB, smoothstep(0.0, 1.0, f)), 1.0);
|
|
267
|
-
}`
|
|
268
|
-
)
|
|
269
|
-
};
|
|
270
|
-
|
|
271
|
-
// src/catalog/images.ts
|
|
272
|
-
function svgDataUrl(svg) {
|
|
273
|
-
const base64 = typeof btoa === "function" ? btoa(svg) : globalThis.Buffer.from(svg).toString("base64");
|
|
274
|
-
return `data:image/svg+xml;base64,${base64}`;
|
|
275
|
-
}
|
|
276
|
-
var LANDSCAPE = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 360">
|
|
277
|
-
<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>
|
|
278
|
-
<rect width="640" height="360" fill="url(#s)"/>
|
|
279
|
-
<circle cx="420" cy="170" r="62" fill="#ffe9b0"/>
|
|
280
|
-
<path d="M0 250 Q120 190 240 240 T480 230 T640 220 V360 H0Z" fill="#2c4a52"/>
|
|
281
|
-
<path d="M0 290 Q160 240 320 290 T640 280 V360 H0Z" fill="#1b2f33"/>
|
|
282
|
-
<path d="M0 330 Q200 300 400 330 T640 325 V360 H0Z" fill="#0f1c1f"/>
|
|
283
|
-
</svg>`;
|
|
284
|
-
var CITY = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 360">
|
|
285
|
-
<rect width="640" height="360" fill="#0d1028"/>
|
|
286
|
-
<circle cx="520" cy="80" r="34" fill="#f5f3d7"/>
|
|
287
|
-
<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>
|
|
288
|
-
<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>
|
|
289
|
-
</svg>`;
|
|
290
|
-
var PORTRAIT = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 360">
|
|
291
|
-
<rect width="640" height="360" fill="#f2e6d8"/>
|
|
292
|
-
<circle cx="320" cy="150" r="70" fill="#c98b6b"/>
|
|
293
|
-
<path d="M190 360 Q200 240 320 240 Q440 240 450 360Z" fill="#33415c"/>
|
|
294
|
-
<path d="M250 130 Q260 60 320 62 Q385 60 392 135 Q370 100 320 100 Q275 100 250 130Z" fill="#2b1d16"/>
|
|
295
|
-
<circle cx="295" cy="150" r="6" fill="#2b1d16"/><circle cx="345" cy="150" r="6" fill="#2b1d16"/>
|
|
296
|
-
<path d="M300 185 Q320 198 340 185" stroke="#7a3e2c" stroke-width="5" fill="none"/>
|
|
297
|
-
</svg>`;
|
|
298
|
-
var SAMPLE_IMAGES = {
|
|
299
|
-
landscape: svgDataUrl(LANDSCAPE),
|
|
300
|
-
city: svgDataUrl(CITY),
|
|
301
|
-
portrait: svgDataUrl(PORTRAIT)
|
|
302
|
-
};
|
|
303
|
-
|
|
304
|
-
// src/catalog/shaders/image.ts
|
|
305
|
-
var PICTURE = `
|
|
306
|
-
vec3 testCard(vec2 uv) {
|
|
307
|
-
vec3 sky = mix(vec3(0.98, 0.62, 0.42), vec3(0.24, 0.32, 0.72), uv.y);
|
|
308
|
-
float sun = smoothstep(0.16, 0.15, length((uv - vec2(0.65, 0.58)) * vec2(1.6, 1.0)));
|
|
309
|
-
float hills = step(uv.y, 0.32 + 0.06 * sin(uv.x * 9.0) + 0.04 * sin(uv.x * 23.0 + 1.0));
|
|
310
|
-
vec3 col = mix(sky, vec3(1.0, 0.92, 0.7), sun);
|
|
311
|
-
col = mix(col, vec3(0.12, 0.28, 0.24) + 0.08 * sin(uv.x * 40.0), hills);
|
|
312
|
-
vec2 grid = abs(fract(uv * 8.0) - 0.5);
|
|
313
|
-
col += 0.04 * step(0.48, max(grid.x, grid.y));
|
|
314
|
-
return col;
|
|
315
|
-
}
|
|
316
|
-
vec3 picture(sampler2D tex, vec2 uv) {
|
|
317
|
-
return textureSize(tex, 0).x > 1 ? texture(tex, uv).rgb : testCard(fract(uv));
|
|
318
|
-
}`.trim();
|
|
319
|
-
var imageRipple = {
|
|
320
|
-
kind: "shader",
|
|
321
|
-
id: "image-ripple",
|
|
322
|
-
name: "Ripple",
|
|
323
|
-
description: "An image rippling as if seen through water, from the pointer or the centre.",
|
|
324
|
-
category: "image",
|
|
325
|
-
tags: ["image", "water", "interactive", "distortion"],
|
|
326
|
-
source: glsl(
|
|
327
|
-
`// Ripple: an image rippling as if under water.
|
|
328
|
-
uniform sampler2D uImage; // @control filter=linear wrap=mirror default=${SAMPLE_IMAGES.landscape} label="Image"
|
|
329
|
-
uniform float uAmplitude; // @control min=0 max=0.05 default=0.012 label="Amplitude"
|
|
330
|
-
uniform float uFrequency; // @control min=5 max=80 default=30 label="Frequency"
|
|
331
|
-
uniform float uSpeed; // @control min=0 max=6 default=2 label="Speed"`,
|
|
332
|
-
PICTURE,
|
|
333
|
-
`void mainImage(out vec4 fragColor, in vec2 fragCoord) {
|
|
334
|
-
vec2 uv = fragCoord / iResolution.xy;
|
|
335
|
-
vec2 centre = (iMouse.z > 0.0 ? iMouse.xy : 0.5 * iResolution.xy) / iResolution.xy;
|
|
336
|
-
vec2 d = uv - centre;
|
|
337
|
-
d.x *= iResolution.x / iResolution.y;
|
|
338
|
-
float r = length(d);
|
|
339
|
-
float wave = sin(r * uFrequency - iTime * uSpeed) * smoothstep(0.9, 0.0, r);
|
|
340
|
-
vec2 offset = normalize(d + 1e-5) * wave * uAmplitude;
|
|
341
|
-
vec3 col = picture(uImage, uv + offset);
|
|
342
|
-
col += 0.6 * max(wave, 0.0) * uAmplitude * 8.0;
|
|
343
|
-
fragColor = vec4(col, 1.0);
|
|
344
|
-
}`
|
|
345
|
-
)
|
|
346
|
-
};
|
|
347
|
-
var imageGlitch = {
|
|
348
|
-
kind: "shader",
|
|
349
|
-
id: "image-glitch",
|
|
350
|
-
name: "Glitch",
|
|
351
|
-
description: "Digital glitch on an image: RGB split, slice displacement and blocky corruption in bursts.",
|
|
352
|
-
category: "image",
|
|
353
|
-
tags: ["image", "glitch", "cyber", "transition"],
|
|
354
|
-
source: glsl(
|
|
355
|
-
`// Glitch: RGB split, slice displacement and blocky corruption in bursts.
|
|
356
|
-
uniform sampler2D uImage; // @control filter=linear wrap=clamp default=${SAMPLE_IMAGES.landscape} label="Image"
|
|
357
|
-
uniform float uAmount; // @control min=0 max=1 default=0.5 label="Amount"
|
|
358
|
-
uniform float uSplit; // @control min=0 max=0.05 default=0.012 label="RGB split"
|
|
359
|
-
uniform float uRate; // @control min=0.5 max=12 default=4 label="Bursts per second"`,
|
|
360
|
-
HASH,
|
|
361
|
-
PICTURE,
|
|
362
|
-
`void mainImage(out vec4 fragColor, in vec2 fragCoord) {
|
|
363
|
-
vec2 uv = fragCoord / iResolution.xy;
|
|
364
|
-
float tick = floor(iTime * uRate);
|
|
365
|
-
float burst = step(1.0 - uAmount * 0.6, hash12(vec2(tick, 1.0)));
|
|
366
|
-
float slice = floor(uv.y * mix(8.0, 40.0, hash12(vec2(tick, 2.0))));
|
|
367
|
-
float shift = (hash12(vec2(slice, tick)) - 0.5) * 0.15 * burst * step(0.6, hash12(vec2(slice, tick + 3.0)));
|
|
368
|
-
vec2 block = floor(uv * vec2(16.0, 9.0));
|
|
369
|
-
float corrupt = burst * step(0.97 - uAmount * 0.05, hash12(block + tick));
|
|
370
|
-
vec2 st = uv + vec2(shift, 0.0);
|
|
371
|
-
st = mix(st, floor(st * 24.0) / 24.0, corrupt);
|
|
372
|
-
float split = uSplit * (0.3 + burst);
|
|
373
|
-
vec3 col = vec3(
|
|
374
|
-
picture(uImage, st + vec2(split, 0.0)).r,
|
|
375
|
-
picture(uImage, st).g,
|
|
376
|
-
picture(uImage, st - vec2(split, 0.0)).b
|
|
377
|
-
);
|
|
378
|
-
col = mix(col, col.bgr, corrupt);
|
|
379
|
-
col *= 0.92 + 0.08 * sin(fragCoord.y * 2.0);
|
|
380
|
-
fragColor = vec4(col, 1.0);
|
|
381
|
-
}`
|
|
382
|
-
)
|
|
383
|
-
};
|
|
384
|
-
var imageDuotone = {
|
|
385
|
-
kind: "shader",
|
|
386
|
-
id: "image-duotone",
|
|
387
|
-
name: "Duotone",
|
|
388
|
-
description: "An image mapped onto two colours, with a slow light sweep.",
|
|
389
|
-
category: "image",
|
|
390
|
-
tags: ["image", "brand", "editorial", "colour"],
|
|
391
|
-
source: glsl(
|
|
392
|
-
`// Duotone: an image mapped onto two colours, with a light sweep.
|
|
393
|
-
uniform sampler2D uImage; // @control filter=linear wrap=clamp default=${SAMPLE_IMAGES.landscape} label="Image"
|
|
394
|
-
uniform vec3 uShadow; // @control color default=#1E1B4B label="Shadows"
|
|
395
|
-
uniform vec3 uHighlight; // @control color default=#FB7185 label="Highlights"
|
|
396
|
-
uniform float uContrast; // @control min=0.5 max=3 default=1.2 label="Contrast"
|
|
397
|
-
uniform float uSweep; // @control min=0 max=1 default=0.3 label="Light sweep"`,
|
|
398
|
-
PICTURE,
|
|
399
|
-
`void mainImage(out vec4 fragColor, in vec2 fragCoord) {
|
|
400
|
-
vec2 uv = fragCoord / iResolution.xy;
|
|
401
|
-
vec3 src = picture(uImage, uv);
|
|
402
|
-
float l = dot(src, vec3(0.2126, 0.7152, 0.0722));
|
|
403
|
-
l = clamp((l - 0.5) * uContrast + 0.5, 0.0, 1.0);
|
|
404
|
-
float sweep = exp(-pow((uv.x + uv.y * 0.4 - fract(iTime * 0.15) * 2.0 + 0.4) * 6.0, 2.0));
|
|
405
|
-
vec3 col = mix(uShadow, uHighlight, l) + sweep * uSweep;
|
|
406
|
-
fragColor = vec4(col, 1.0);
|
|
407
|
-
}`
|
|
408
|
-
),
|
|
409
|
-
presets: [
|
|
410
|
-
{ name: "Spotify", values: { uShadow: [0.07, 0.1, 0.35], uHighlight: [0.12, 0.84, 0.38] } },
|
|
411
|
-
{ name: "Newsprint", values: { uShadow: [0.1, 0.1, 0.1], uHighlight: [0.96, 0.94, 0.88] } }
|
|
412
|
-
]
|
|
413
|
-
};
|
|
414
|
-
var imagePixelate = {
|
|
415
|
-
kind: "shader",
|
|
416
|
-
id: "image-pixelate",
|
|
417
|
-
name: "Pixel reveal",
|
|
418
|
-
description: "An image resolving from big blocks to full detail, then back, in a loop.",
|
|
419
|
-
category: "image",
|
|
420
|
-
tags: ["image", "reveal", "transition", "8-bit"],
|
|
421
|
-
loop: 4,
|
|
422
|
-
source: glsl(
|
|
423
|
-
`// Pixel reveal: an image resolving from blocks to full detail and back.
|
|
424
|
-
uniform sampler2D uImage; // @control filter=nearest wrap=clamp default=${SAMPLE_IMAGES.landscape} label="Image"
|
|
425
|
-
uniform float uMaxBlock; // @control min=4 max=128 default=48 label="Largest block (px)"
|
|
426
|
-
uniform bool uAnimate; // @control default=true label="Animate"
|
|
427
|
-
uniform float uProgress; // @control min=0 max=1 default=1 label="Detail (when still)"`,
|
|
428
|
-
PICTURE,
|
|
429
|
-
`void mainImage(out vec4 fragColor, in vec2 fragCoord) {
|
|
430
|
-
float k = uAnimate ? 0.5 - 0.5 * cos(iTime * 6.28318 / 4.0) : uProgress;
|
|
431
|
-
float block = max(1.0, floor(mix(uMaxBlock, 1.0, k)));
|
|
432
|
-
vec2 cell = (floor(fragCoord / block) + 0.5) * block;
|
|
433
|
-
vec3 col = picture(uImage, cell / iResolution.xy);
|
|
434
|
-
fragColor = vec4(col, 1.0);
|
|
435
|
-
}`
|
|
436
|
-
)
|
|
437
|
-
};
|
|
438
|
-
var imageHalftone = {
|
|
439
|
-
kind: "shader",
|
|
440
|
-
id: "image-halftone",
|
|
441
|
-
name: "Halftone",
|
|
442
|
-
description: "An image printed as rotated halftone dots, breathing slightly.",
|
|
443
|
-
category: "image",
|
|
444
|
-
tags: ["image", "print", "comic", "pop-art"],
|
|
445
|
-
source: glsl(
|
|
446
|
-
`// Halftone: an image as rotated print dots.
|
|
447
|
-
uniform sampler2D uImage; // @control filter=linear wrap=clamp default=${SAMPLE_IMAGES.landscape} label="Image"
|
|
448
|
-
uniform vec3 uInk; // @control color default=#111827 label="Ink"
|
|
449
|
-
uniform vec3 uPaper; // @control color default=#FEF3C7 label="Paper"
|
|
450
|
-
uniform float uSpacing; // @control min=3 max=30 default=8 label="Dot spacing (px)"
|
|
451
|
-
uniform float uAngle; // @control min=0 max=90 default=45 label="Screen angle (\xB0)"`,
|
|
452
|
-
PICTURE,
|
|
453
|
-
`void mainImage(out vec4 fragColor, in vec2 fragCoord) {
|
|
454
|
-
float a = radians(uAngle);
|
|
455
|
-
mat2 r = mat2(cos(a), sin(a), -sin(a), cos(a));
|
|
456
|
-
vec2 q = r * fragCoord / uSpacing;
|
|
457
|
-
vec2 cell = floor(q) + 0.5;
|
|
458
|
-
vec2 centre = transpose(r) * cell * uSpacing;
|
|
459
|
-
float l = dot(picture(uImage, centre / iResolution.xy), vec3(0.2126, 0.7152, 0.0722));
|
|
460
|
-
float radius = sqrt(1.0 - l) * 0.62 * (0.95 + 0.05 * sin(iTime * 2.0));
|
|
461
|
-
float d = length(q - cell);
|
|
462
|
-
float ink = smoothstep(radius + 0.06, radius - 0.06, d);
|
|
463
|
-
fragColor = vec4(mix(uPaper, uInk, ink), 1.0);
|
|
464
|
-
}`
|
|
465
|
-
)
|
|
466
|
-
};
|
|
467
|
-
var crt = {
|
|
468
|
-
kind: "shader",
|
|
469
|
-
id: "crt",
|
|
470
|
-
name: "CRT screen",
|
|
471
|
-
description: "An image on a curved CRT: scanlines, shadow mask, bloom and flicker.",
|
|
472
|
-
category: "image",
|
|
473
|
-
tags: ["image", "retro", "screen", "arcade"],
|
|
474
|
-
source: glsl(
|
|
475
|
-
`// CRT screen: an image on a curved tube with scanlines and a shadow mask.
|
|
476
|
-
uniform sampler2D uImage; // @control filter=linear wrap=clamp default=${SAMPLE_IMAGES.landscape} label="Image"
|
|
477
|
-
uniform float uCurve; // @control min=0 max=0.5 default=0.15 label="Curvature"
|
|
478
|
-
uniform float uScanlines; // @control min=0 max=1 default=0.5 label="Scanlines"
|
|
479
|
-
uniform float uMask; // @control min=0 max=1 default=0.35 label="Shadow mask"
|
|
480
|
-
uniform float uFlicker; // @control min=0 max=0.2 default=0.03 label="Flicker"`,
|
|
481
|
-
HASH,
|
|
482
|
-
PICTURE,
|
|
483
|
-
`void mainImage(out vec4 fragColor, in vec2 fragCoord) {
|
|
484
|
-
vec2 uv = fragCoord / iResolution.xy * 2.0 - 1.0;
|
|
485
|
-
uv *= 1.0 + uCurve * dot(uv.yx, uv.yx) * 0.5;
|
|
486
|
-
vec2 st = uv * 0.5 + 0.5;
|
|
487
|
-
if (st.x < 0.0 || st.x > 1.0 || st.y < 0.0 || st.y > 1.0) {
|
|
488
|
-
fragColor = vec4(0.0, 0.0, 0.0, 1.0);
|
|
489
|
-
return;
|
|
490
|
-
}
|
|
491
|
-
vec3 col = picture(uImage, st);
|
|
492
|
-
col += 0.25 * picture(uImage, st + vec2(0.003, 0.0)) * col;
|
|
493
|
-
float scan = 1.0 - uScanlines * 0.5 * (1.0 - cos(st.y * iResolution.y * 3.14159));
|
|
494
|
-
float m = mod(fragCoord.x, 3.0);
|
|
495
|
-
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);
|
|
496
|
-
col *= scan * mask;
|
|
497
|
-
col *= 1.0 - uFlicker * hash12(vec2(floor(iTime * 24.0), 0.0));
|
|
498
|
-
col *= smoothstep(1.0, 0.7, max(abs(uv.x), abs(uv.y)) * 0.98 + 0.02 * length(uv));
|
|
499
|
-
fragColor = vec4(col, 1.0);
|
|
500
|
-
}`
|
|
501
|
-
)
|
|
502
|
-
};
|
|
503
|
-
var filmGrain = {
|
|
504
|
-
kind: "shader",
|
|
505
|
-
id: "film-look",
|
|
506
|
-
name: "Film look",
|
|
507
|
-
description: "An image graded like film: grain, gate weave, halation and vignette.",
|
|
508
|
-
category: "image",
|
|
509
|
-
tags: ["image", "cinematic", "grain", "vintage"],
|
|
510
|
-
source: glsl(
|
|
511
|
-
`// Film look: grain, gate weave, halation and vignette.
|
|
512
|
-
uniform sampler2D uImage; // @control filter=linear wrap=clamp default=${SAMPLE_IMAGES.landscape} label="Image"
|
|
513
|
-
uniform float uGrain; // @control min=0 max=0.3 default=0.08 label="Grain"
|
|
514
|
-
uniform float uWeave; // @control min=0 max=0.01 default=0.0015 label="Gate weave"
|
|
515
|
-
uniform float uHalation; // @control min=0 max=1 default=0.3 label="Halation"
|
|
516
|
-
uniform float uVignette; // @control min=0 max=1 default=0.45 label="Vignette"
|
|
517
|
-
uniform vec3 uTint; // @control color default=#FFF1DC label="Print tint"`,
|
|
518
|
-
HASH,
|
|
519
|
-
NOISE,
|
|
520
|
-
PICTURE,
|
|
521
|
-
`void mainImage(out vec4 fragColor, in vec2 fragCoord) {
|
|
522
|
-
vec2 uv = fragCoord / iResolution.xy;
|
|
523
|
-
float frame = floor(iTime * 24.0);
|
|
524
|
-
uv += uWeave * (vec2(vnoise(vec2(frame * 0.3, 0.0)), vnoise(vec2(0.0, frame * 0.3))) - 0.5);
|
|
525
|
-
vec3 col = picture(uImage, uv);
|
|
526
|
-
vec3 blur = (picture(uImage, uv + vec2(0.006, 0.0)) + picture(uImage, uv - vec2(0.006, 0.0)) +
|
|
527
|
-
picture(uImage, uv + vec2(0.0, 0.006)) + picture(uImage, uv - vec2(0.0, 0.006))) * 0.25;
|
|
528
|
-
float bright = smoothstep(0.6, 1.0, dot(blur, vec3(0.333)));
|
|
529
|
-
col += vec3(1.0, 0.3, 0.1) * bright * uHalation * 0.5;
|
|
530
|
-
col *= uTint;
|
|
531
|
-
col += (hash12(fragCoord + frame * 31.7) - 0.5) * uGrain;
|
|
532
|
-
col *= 1.0 - uVignette * smoothstep(0.3, 0.9, length(uv - 0.5) * 1.3);
|
|
533
|
-
fragColor = vec4(col, 1.0);
|
|
534
|
-
}`
|
|
535
|
-
)
|
|
536
|
-
};
|
|
537
|
-
var TRANSITIONS = [
|
|
538
|
-
{ type: "linearWipe", id: "wipe", name: "Wipe", description: "A soft-edged wipe from one image to the next." },
|
|
539
|
-
{ type: "radialIris", id: "iris", name: "Iris", description: "A circle opening from the centre onto the next image." },
|
|
540
|
-
{ type: "blockDissolve", id: "block-dissolve", name: "Block dissolve", description: "Random blocks switching to the next image." },
|
|
541
|
-
{ type: "gridWipe", id: "grid-wipe", name: "Grid wipe", description: "A diagonal cascade of cells revealing the next image." },
|
|
542
|
-
{ type: "glassWipe", id: "glass-wipe", name: "Glass wipe", description: "A refracting glass edge sweeping across." },
|
|
543
|
-
{ type: "twister", id: "twister", name: "Twister", description: "A swirl that twists one image into the next." },
|
|
544
|
-
{ type: "lightLeak", id: "light-leak", name: "Light leak", description: "A warm flare of light burning through to the next image." },
|
|
545
|
-
{ type: "crossZoom", id: "cross-zoom", name: "Cross zoom", description: "A zoom blur punching through to the next image." }
|
|
546
|
-
];
|
|
547
|
-
var transitions = TRANSITIONS.map(({ type, id, name, description }) => ({
|
|
548
|
-
kind: "shader",
|
|
549
|
-
id: `transition-${id}`,
|
|
550
|
-
name,
|
|
551
|
-
description: `${description} Loops back and forth; or turn Animate off and set Progress.`,
|
|
552
|
-
category: "transition",
|
|
553
|
-
tags: ["transition", "image", "slideshow"],
|
|
554
|
-
loop: 4,
|
|
555
|
-
source: glsl(
|
|
556
|
-
`// ${name} transition between two images. Progress runs 0 \u2192 1 \u2192 0 when animated.
|
|
557
|
-
uniform sampler2D uFrom; // @control filter=linear wrap=clamp default=${SAMPLE_IMAGES.landscape} label="From image"
|
|
558
|
-
uniform sampler2D uTo; // @control filter=linear wrap=clamp default=${SAMPLE_IMAGES.city} label="To image"
|
|
559
|
-
uniform bool uAnimate; // @control default=true label="Animate"
|
|
560
|
-
uniform float uProgress; // @control min=0 max=1 default=0.5 label="Progress (when still)"`,
|
|
561
|
-
getTransitionGLSL(type),
|
|
562
|
-
`void mainImage(out vec4 fragColor, in vec2 fragCoord) {
|
|
563
|
-
vec2 uv = fragCoord / iResolution.xy;
|
|
564
|
-
float p = uAnimate ? smoothstep(0.1, 0.9, 0.5 - 0.5 * cos(iTime * 6.28318 / 4.0)) : uProgress;
|
|
565
|
-
fragColor = vec4(flair_transition(uFrom, uTo, uv, p).rgb, 1.0);
|
|
566
|
-
}`
|
|
567
|
-
)
|
|
568
|
-
}));
|
|
569
|
-
|
|
570
|
-
// src/catalog/shaders/light.ts
|
|
571
|
-
var bokeh = {
|
|
572
|
-
kind: "shader",
|
|
573
|
-
id: "bokeh",
|
|
574
|
-
name: "Bokeh",
|
|
575
|
-
description: "Out-of-focus city lights drifting softly.",
|
|
576
|
-
category: "light",
|
|
577
|
-
tags: ["background", "festive", "dreamy", "night"],
|
|
578
|
-
source: glsl(
|
|
579
|
-
`// Bokeh: soft out-of-focus lights drifting.
|
|
580
|
-
uniform vec3 uBackground; // @control color default=#0B1026 label="Background"
|
|
581
|
-
uniform float uBackgroundAlpha; // @control min=0 max=1 default=1 label="Background opacity"
|
|
582
|
-
uniform vec3 uWarm; // @control color default=#FFC56E label="Warm light"
|
|
583
|
-
uniform vec3 uCool; // @control color default=#6EC8FF label="Cool light"
|
|
584
|
-
uniform int uCount; // @control min=4 max=60 default=30 label="Lights"
|
|
585
|
-
uniform float uSize; // @control min=0.02 max=0.25 default=0.08 label="Size"
|
|
586
|
-
uniform float uSpeed; // @control min=0 max=1 default=0.15 label="Drift"`,
|
|
587
|
-
HASH,
|
|
588
|
-
`void mainImage(out vec4 fragColor, in vec2 fragCoord) {
|
|
589
|
-
vec2 p = fragCoord / iResolution.y;
|
|
590
|
-
float aspect = iResolution.x / iResolution.y;
|
|
591
|
-
vec3 col = uBackground * (0.7 + 0.3 * fragCoord.y / iResolution.y) * uBackgroundAlpha;
|
|
592
|
-
float lightAlpha = 0.0;
|
|
593
|
-
for (int i = 0; i < 60; i++) {
|
|
594
|
-
if (i >= uCount) break;
|
|
595
|
-
float fi = float(i);
|
|
596
|
-
vec2 h = hash22(vec2(fi, 3.1));
|
|
597
|
-
float depth = hash12(vec2(fi, 9.7));
|
|
598
|
-
vec2 c = vec2(fract(h.x + iTime * uSpeed * (0.2 + depth) * 0.1) * (aspect + 0.4) - 0.2, h.y);
|
|
599
|
-
c.y += 0.03 * sin(iTime * 0.5 + fi);
|
|
600
|
-
float r = uSize * (0.5 + depth);
|
|
601
|
-
float d = length(p - c);
|
|
602
|
-
float disc = smoothstep(r, r * 0.85, d) * (0.6 + 0.4 * smoothstep(r * 0.4, r, d));
|
|
603
|
-
vec3 light = mix(uWarm, uCool, step(0.5, hash12(vec2(fi, 1.3))));
|
|
604
|
-
float weight = disc * (0.15 + 0.25 * (1.0 - depth));
|
|
605
|
-
col += light * weight;
|
|
606
|
-
lightAlpha += weight;
|
|
607
|
-
}
|
|
608
|
-
float a = clamp(uBackgroundAlpha + lightAlpha * (1.0 - uBackgroundAlpha), 0.0, 1.0);
|
|
609
|
-
fragColor = vec4(col, a);
|
|
610
|
-
}`
|
|
611
|
-
)
|
|
612
|
-
};
|
|
613
|
-
var godRays = {
|
|
614
|
-
kind: "shader",
|
|
615
|
-
id: "god-rays",
|
|
616
|
-
name: "God rays",
|
|
617
|
-
description: "Shafts of light fanning down through haze.",
|
|
618
|
-
category: "light",
|
|
619
|
-
tags: ["background", "hero", "heavenly", "spotlight"],
|
|
620
|
-
source: glsl(
|
|
621
|
-
`// God rays: shafts of light fanning through haze.
|
|
622
|
-
uniform vec3 uBackground; // @control color default=#0C0A09 label="Background"
|
|
623
|
-
uniform vec3 uLight; // @control color default=#FDE68A label="Light"
|
|
624
|
-
uniform vec2 uSource; // @control min=-1 max=1 default=0,0.6 label="Source"
|
|
625
|
-
uniform float uRays; // @control min=4 max=60 default=18 label="Rays"
|
|
626
|
-
uniform float uSpeed; // @control min=0 max=2 default=0.3 label="Speed"
|
|
627
|
-
uniform float uStrength; // @control min=0 max=2 default=1 label="Strength"`,
|
|
628
|
-
HASH,
|
|
629
|
-
NOISE,
|
|
630
|
-
`void mainImage(out vec4 fragColor, in vec2 fragCoord) {
|
|
631
|
-
vec2 p = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;
|
|
632
|
-
vec2 d = p - uSource * vec2(iResolution.x / iResolution.y, 1.0) * 0.5;
|
|
633
|
-
float a = atan(d.x, -d.y);
|
|
634
|
-
float r = length(d);
|
|
635
|
-
float t = iTime * uSpeed;
|
|
636
|
-
float rays = vnoise(vec2(a * uRays, t)) * 0.6 + vnoise(vec2(a * uRays * 2.3, -t * 1.3)) * 0.4;
|
|
637
|
-
rays = pow(rays, 2.0) * smoothstep(1.6, 0.0, r);
|
|
638
|
-
float glow = exp(-r * 4.0);
|
|
639
|
-
vec3 col = uBackground + uLight * (rays * 0.9 + glow * 0.8) * uStrength;
|
|
640
|
-
fragColor = vec4(col, 1.0);
|
|
641
|
-
}`
|
|
642
|
-
)
|
|
643
|
-
};
|
|
644
|
-
var sparkles = {
|
|
645
|
-
kind: "shader",
|
|
646
|
-
id: "sparkles",
|
|
647
|
-
name: "Sparkles",
|
|
648
|
-
description: "Four-pointed glints twinkling on and off across the frame.",
|
|
649
|
-
category: "light",
|
|
650
|
-
tags: ["overlay", "magic", "celebration", "premium"],
|
|
651
|
-
source: glsl(
|
|
652
|
-
`// Sparkles: four-pointed glints twinkling across the frame.
|
|
653
|
-
uniform vec3 uBackground; // @control color default=#1E1B4B label="Background"
|
|
654
|
-
uniform float uBackgroundAlpha; // @control min=0 max=1 default=1 label="Background opacity"
|
|
655
|
-
uniform vec3 uSparkle; // @control color default=#FFFFFF label="Sparkle"
|
|
656
|
-
uniform float uDensity; // @control min=2 max=30 default=10 label="Density"
|
|
657
|
-
uniform float uChance; // @control min=0.02 max=1 default=1 label="Sparsity"
|
|
658
|
-
uniform float uSize; // @control min=0.05 max=0.5 default=0.35 label="Size"
|
|
659
|
-
uniform float uSpeed; // @control min=0 max=4 default=1 label="Twinkle speed"`,
|
|
660
|
-
HASH,
|
|
661
|
-
`void mainImage(out vec4 fragColor, in vec2 fragCoord) {
|
|
662
|
-
vec2 p = fragCoord / iResolution.y * uDensity;
|
|
663
|
-
float glint = 0.0;
|
|
664
|
-
for (int layer = 0; layer < 2; layer++) {
|
|
665
|
-
vec2 q = p * (1.0 + float(layer) * 0.6) + float(layer) * 7.3;
|
|
666
|
-
vec2 id = floor(q);
|
|
667
|
-
float keep = step(1.0 - uChance, hash12(id + 19.0 + float(layer) * 31.0));
|
|
668
|
-
vec2 f = fract(q) - 0.5;
|
|
669
|
-
vec2 o = hash22(id) - 0.5;
|
|
670
|
-
vec2 d = f - o * 0.6;
|
|
671
|
-
float phase = hash12(id + 4.0) * 6.28318;
|
|
672
|
-
float tw = pow(max(0.0, sin(iTime * uSpeed * 2.0 + phase)), 4.0);
|
|
673
|
-
float r = length(d);
|
|
674
|
-
float rays = max(0.0, 1.0 - abs(d.x * d.y) * 3000.0);
|
|
675
|
-
float core = 0.01 / (r + 0.01);
|
|
676
|
-
float star = (rays * 0.8 + core) * smoothstep(uSize, 0.0, r) * tw * keep;
|
|
677
|
-
glint += star;
|
|
678
|
-
}
|
|
679
|
-
vec3 col = uBackground * uBackgroundAlpha + uSparkle * glint;
|
|
680
|
-
float a = clamp(uBackgroundAlpha + glint * (1.0 - uBackgroundAlpha), 0.0, 1.0);
|
|
681
|
-
fragColor = vec4(col, a);
|
|
682
|
-
}`
|
|
683
|
-
)
|
|
684
|
-
};
|
|
685
|
-
var neonRipple = {
|
|
686
|
-
kind: "shader",
|
|
687
|
-
id: "neon-ripple",
|
|
688
|
-
name: "Neon ripple",
|
|
689
|
-
description: "Glowing neon rings radiating out from the pointer or the centre.",
|
|
690
|
-
category: "light",
|
|
691
|
-
tags: ["interactive", "glow", "music", "pulse"],
|
|
692
|
-
loop: 2,
|
|
693
|
-
source: glsl(
|
|
694
|
-
`// Neon ripple: glowing rings radiating from the pointer (or the centre).
|
|
695
|
-
uniform vec3 uBackground; // @control color default=#09090B label="Background"
|
|
696
|
-
uniform vec3 uNeon; // @control color default=#FF2BD6 label="Neon"
|
|
697
|
-
uniform float uRings; // @control min=1 max=10 default=4 label="Rings"
|
|
698
|
-
uniform float uWidth; // @control min=0.002 max=0.05 default=0.01 label="Width"
|
|
699
|
-
uniform float uGlow; // @control min=0 max=3 default=1 label="Glow"`,
|
|
700
|
-
`void mainImage(out vec4 fragColor, in vec2 fragCoord) {
|
|
701
|
-
vec2 centre = iMouse.z > 0.0 ? iMouse.xy : 0.5 * iResolution.xy;
|
|
702
|
-
float r = length(fragCoord - centre) / iResolution.y;
|
|
703
|
-
float phase = fract(r * uRings - iTime * 0.5);
|
|
704
|
-
float ringDist = min(phase, 1.0 - phase) / uRings;
|
|
705
|
-
float fade = smoothstep(1.2, 0.0, r);
|
|
706
|
-
float core = smoothstep(uWidth, 0.0, ringDist);
|
|
707
|
-
float glow = uWidth * 2.0 / (ringDist + uWidth) * 0.25 * uGlow;
|
|
708
|
-
vec3 col = uBackground + uNeon * (core + glow) * fade;
|
|
709
|
-
fragColor = vec4(col, 1.0);
|
|
710
|
-
}`
|
|
711
|
-
)
|
|
712
|
-
};
|
|
713
|
-
var starfield = {
|
|
714
|
-
kind: "shader",
|
|
715
|
-
id: "starfield",
|
|
716
|
-
name: "Warp starfield",
|
|
717
|
-
description: "Flying through a field of stars, from cruising to warp speed.",
|
|
718
|
-
category: "space",
|
|
719
|
-
tags: ["background", "space", "speed", "sci-fi"],
|
|
720
|
-
source: glsl(
|
|
721
|
-
`// Warp starfield: flying through layers of stars.
|
|
722
|
-
uniform vec3 uBackground; // @control color default=#000005 label="Background"
|
|
723
|
-
uniform vec3 uStar; // @control color default=#E0E7FF label="Star"
|
|
724
|
-
uniform float uSpeed; // @control min=0 max=4 default=0.6 label="Speed"
|
|
725
|
-
uniform float uStreak; // @control min=0 max=1 default=0.3 label="Streak"
|
|
726
|
-
uniform int uLayers; // @control min=1 max=8 default=5 label="Layers"`,
|
|
727
|
-
HASH,
|
|
728
|
-
ROTATE,
|
|
729
|
-
`void mainImage(out vec4 fragColor, in vec2 fragCoord) {
|
|
730
|
-
vec2 p = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;
|
|
731
|
-
vec3 col = uBackground;
|
|
732
|
-
float n = float(uLayers);
|
|
733
|
-
for (int i = 0; i < 8; i++) {
|
|
734
|
-
if (i >= uLayers) break;
|
|
735
|
-
float depth = fract(float(i) / n + iTime * uSpeed * 0.1);
|
|
736
|
-
float scale = mix(20.0, 0.5, depth);
|
|
737
|
-
float fade = depth * smoothstep(1.0, 0.85, depth);
|
|
738
|
-
vec2 q = rot(float(i) * 1.7) * p * scale + float(i) * 13.1;
|
|
739
|
-
vec2 id = floor(q);
|
|
740
|
-
vec2 f = fract(q) - 0.5;
|
|
741
|
-
vec2 o = hash22(id) - 0.5;
|
|
742
|
-
vec2 d = f - o * 0.8;
|
|
743
|
-
vec2 dir = normalize(p + 1e-4);
|
|
744
|
-
float along = dot(d, dir);
|
|
745
|
-
float across = length(d - dir * along);
|
|
746
|
-
float len = 0.02 + uStreak * depth * 0.4;
|
|
747
|
-
float star = smoothstep(0.03, 0.0, across) * smoothstep(len, 0.0, abs(along));
|
|
748
|
-
star *= step(0.6, hash12(id + 2.0));
|
|
749
|
-
col += uStar * star * fade * 1.5;
|
|
750
|
-
}
|
|
751
|
-
fragColor = vec4(col, 1.0);
|
|
752
|
-
}`
|
|
753
|
-
)
|
|
754
|
-
};
|
|
755
|
-
var nebula = {
|
|
756
|
-
kind: "shader",
|
|
757
|
-
id: "nebula",
|
|
758
|
-
name: "Nebula",
|
|
759
|
-
description: "Glowing gas clouds and stars drifting in deep space.",
|
|
760
|
-
category: "space",
|
|
761
|
-
tags: ["background", "space", "cosmic", "hero"],
|
|
762
|
-
source: glsl(
|
|
763
|
-
`// Nebula: glowing gas clouds over a field of stars.
|
|
764
|
-
uniform vec3 uGasA; // @control color default=#7C3AED label="Gas A"
|
|
765
|
-
uniform vec3 uGasB; // @control color default=#EC4899 label="Gas B"
|
|
766
|
-
uniform vec3 uDeep; // @control color default=#020010 label="Deep space"
|
|
767
|
-
uniform float uSpeed; // @control min=0 max=1 default=0.05 label="Drift"
|
|
768
|
-
uniform float uDensity; // @control min=0 max=2 default=1 label="Density"`,
|
|
769
|
-
HASH,
|
|
770
|
-
NOISE,
|
|
771
|
-
FBM,
|
|
772
|
-
`void mainImage(out vec4 fragColor, in vec2 fragCoord) {
|
|
773
|
-
vec2 p = fragCoord / iResolution.y * 2.0;
|
|
774
|
-
float t = iTime * uSpeed;
|
|
775
|
-
vec2 q = vec2(fbm(p + t, 5), fbm(p - t + 3.3, 5));
|
|
776
|
-
float gas = fbm(p * 1.3 + 2.5 * q, 6);
|
|
777
|
-
float dust = smoothstep(0.45, 0.75, fbm(p * 3.0 - q, 5));
|
|
778
|
-
vec3 col = uDeep;
|
|
779
|
-
col += uGasA * smoothstep(0.35, 0.8, gas) * uDensity;
|
|
780
|
-
col += uGasB * smoothstep(0.5, 0.9, gas * q.x * 1.6) * uDensity;
|
|
781
|
-
col *= 1.0 - 0.6 * dust;
|
|
782
|
-
float star = step(0.996, hash12(floor(fragCoord)));
|
|
783
|
-
float bright = step(0.9993, hash12(floor(fragCoord * 0.5) + 7.0));
|
|
784
|
-
col += vec3(star * 0.7 + bright * (0.8 + 0.2 * sin(iTime * 4.0 + fragCoord.x)));
|
|
785
|
-
fragColor = vec4(col, 1.0);
|
|
786
|
-
}`
|
|
787
|
-
)
|
|
788
|
-
};
|
|
789
|
-
var blackHole = {
|
|
790
|
-
kind: "shader",
|
|
791
|
-
id: "event-horizon",
|
|
792
|
-
name: "Event horizon",
|
|
793
|
-
description: "A glowing accretion disc swirling into a black hole.",
|
|
794
|
-
category: "space",
|
|
795
|
-
tags: ["space", "dramatic", "vortex", "hero"],
|
|
796
|
-
source: glsl(
|
|
797
|
-
`// Event horizon: an accretion disc swirling into a black hole.
|
|
798
|
-
uniform vec3 uHot; // @control color default=#FFE4B5 label="Hot"
|
|
799
|
-
uniform vec3 uDisc; // @control color default=#FF6A00 label="Disc"
|
|
800
|
-
uniform float uRadius; // @control min=0.05 max=0.4 default=0.15 label="Horizon"
|
|
801
|
-
uniform float uSpin; // @control min=0 max=4 default=1 label="Spin"`,
|
|
802
|
-
HASH,
|
|
803
|
-
NOISE,
|
|
804
|
-
FBM,
|
|
805
|
-
`void mainImage(out vec4 fragColor, in vec2 fragCoord) {
|
|
806
|
-
vec2 p = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;
|
|
807
|
-
p.y *= 2.2;
|
|
808
|
-
float r = length(p);
|
|
809
|
-
float a = atan(p.y, p.x);
|
|
810
|
-
float swirl = a + uSpin * iTime * 0.5 + 1.5 / (r + 0.05);
|
|
811
|
-
float streaks = fbm(vec2(swirl * 3.0, r * 8.0), 5);
|
|
812
|
-
float disc = smoothstep(uRadius, uRadius * 1.6, r) * smoothstep(1.0, uRadius * 1.6, r);
|
|
813
|
-
vec3 col = mix(uDisc, uHot, smoothstep(0.5, 0.9, streaks) * smoothstep(0.6, 0.1, r));
|
|
814
|
-
col *= disc * (0.4 + 1.2 * streaks);
|
|
815
|
-
col += uHot * 0.6 * exp(-abs(r - uRadius * 1.05) * 60.0);
|
|
816
|
-
col *= smoothstep(uRadius * 0.95, uRadius * 1.05, r);
|
|
817
|
-
fragColor = vec4(col, 1.0);
|
|
818
|
-
}`
|
|
819
|
-
)
|
|
820
|
-
};
|
|
821
|
-
var lightRibbons = {
|
|
822
|
-
kind: "shader",
|
|
823
|
-
id: "light-ribbons",
|
|
824
|
-
name: "Light ribbons",
|
|
825
|
-
description: "A bundle of thin iridescent light strands weaving across a dark frame.",
|
|
826
|
-
category: "light",
|
|
827
|
-
tags: ["background", "hero", "tech", "iridescent", "flow"],
|
|
828
|
-
source: glsl(
|
|
829
|
-
`// Light ribbons: thin glowing strands weaving across the frame, shading from one colour to the next.
|
|
830
|
-
uniform vec3 uBackground; // @control color default=#05060D label="Background"
|
|
831
|
-
uniform vec3 uColorA; // @control color default=#4FD1FF label="Colour A"
|
|
832
|
-
uniform vec3 uColorB; // @control color default=#B06BFF label="Colour B"
|
|
833
|
-
uniform vec3 uColorC; // @control color default=#FF7AC6 label="Colour C"
|
|
834
|
-
uniform float uCount; // @control min=2 max=24 default=12 step=1 label="Strands"
|
|
835
|
-
uniform float uSpread; // @control min=0.02 max=0.6 default=0.18 label="Spread"
|
|
836
|
-
uniform float uWave; // @control min=0 max=0.5 default=0.22 label="Wave"
|
|
837
|
-
uniform float uTilt; // @control min=-1 max=1 default=0.3 label="Tilt"
|
|
838
|
-
uniform float uSpeed; // @control min=0 max=2 default=0.5 label="Speed"
|
|
839
|
-
uniform float uGlow; // @control min=0 max=2 default=1 label="Glow"`,
|
|
840
|
-
`// Three colours round a loop, so the shading drifts without a seam.
|
|
841
|
-
vec3 shade(float g) {
|
|
842
|
-
g = fract(g) * 3.0;
|
|
843
|
-
return g < 1.0 ? mix(uColorA, uColorB, g) : g < 2.0 ? mix(uColorB, uColorC, g - 1.0) : mix(uColorC, uColorA, g - 2.0);
|
|
844
|
-
}
|
|
845
|
-
|
|
846
|
-
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
|
|
847
|
-
vec2 p = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;
|
|
848
|
-
float px = 1.0 / iResolution.y;
|
|
849
|
-
float t = iTime * uSpeed;
|
|
850
|
-
float halfWidth = 0.5 * iResolution.x / iResolution.y;
|
|
851
|
-
float x = p.x;
|
|
852
|
-
float y = p.y - x * uTilt;
|
|
853
|
-
// The bundle pinches and opens along its length, like ribbons twisting.
|
|
854
|
-
float pinch = 0.35 + 0.65 * abs(sin(x * 1.3 - t * 0.7));
|
|
855
|
-
vec3 light = vec3(0.0);
|
|
856
|
-
for (int i = 0; i < 24; i++) {
|
|
857
|
-
if (float(i) >= uCount) break;
|
|
858
|
-
float f = float(i) / max(uCount - 1.0, 1.0) - 0.5;
|
|
859
|
-
float centre = f * uSpread * pinch
|
|
860
|
-
+ uWave * (0.6 * sin(x * 1.7 + t + f * 2.0) + 0.3 * sin(x * 3.3 - t * 1.3 + f * 5.0));
|
|
861
|
-
float d = abs(y - centre);
|
|
862
|
-
float core = 1.0 - smoothstep(0.0, px * 1.5, d);
|
|
863
|
-
float glow = uGlow * 0.006 / (d + 0.006);
|
|
864
|
-
light += shade(f * 0.6 + x * 0.12 + t * 0.05) * (core * 0.9 + glow * 0.4);
|
|
865
|
-
}
|
|
866
|
-
light *= 1.0 - 0.75 * pow(min(abs(x) / halfWidth, 1.0), 2.0);
|
|
867
|
-
fragColor = vec4(uBackground + (1.0 - exp(-light)), 1.0);
|
|
868
|
-
}`
|
|
869
|
-
),
|
|
870
|
-
presets: [
|
|
871
|
-
{ name: "Gold", values: { uBackground: [0.03, 0.03, 0.04], uColorA: [1, 0.78, 0.4], uColorB: [0.75, 0.5, 0.2], uColorC: [1, 0.93, 0.75], uCount: 8, uSpread: 0.4 } },
|
|
872
|
-
{ name: "Aurora", values: { uColorA: [0.2, 1, 0.7], uColorB: [0.2, 0.6, 1], uColorC: [0.6, 0.3, 1], uTilt: -0.2, uWave: 0.3 } }
|
|
873
|
-
]
|
|
874
|
-
};
|
|
875
|
-
|
|
876
|
-
// src/catalog/shaders/organic.ts
|
|
877
|
-
var marble = {
|
|
878
|
-
kind: "shader",
|
|
879
|
-
id: "marble",
|
|
880
|
-
name: "Liquid marble",
|
|
881
|
-
description: "Veined marble swirling like ink in water (domain-warped noise).",
|
|
882
|
-
category: "organic",
|
|
883
|
-
tags: ["background", "ink", "liquid", "luxury"],
|
|
884
|
-
source: glsl(
|
|
885
|
-
`// Liquid marble: domain-warped noise swirling like ink in water.
|
|
886
|
-
uniform vec3 uStone; // @control color default=#F5F1E8 label="Stone"
|
|
887
|
-
uniform vec3 uVein; // @control color default=#1F2937 label="Vein"
|
|
888
|
-
uniform vec3 uAccent; // @control color default=#B08D57 label="Accent"
|
|
889
|
-
uniform float uSpeed; // @control min=0 max=1 default=0.15 label="Speed"
|
|
890
|
-
uniform float uScale; // @control min=0.5 max=6 default=2.5 label="Scale"
|
|
891
|
-
uniform float uWarp; // @control min=0 max=8 default=4 label="Warp"`,
|
|
892
|
-
HASH,
|
|
893
|
-
NOISE,
|
|
894
|
-
FBM,
|
|
895
|
-
`void mainImage(out vec4 fragColor, in vec2 fragCoord) {
|
|
896
|
-
vec2 p = fragCoord / iResolution.y * uScale;
|
|
897
|
-
float t = iTime * uSpeed;
|
|
898
|
-
vec2 q = vec2(fbm(p + vec2(0.0, t), 5), fbm(p + vec2(5.2, 1.3 - t), 5));
|
|
899
|
-
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));
|
|
900
|
-
float f = fbm(p + uWarp * r, 5);
|
|
901
|
-
float vein = 1.0 - smoothstep(0.0, 0.06, abs(sin(f * 12.0 + r.x * 6.0)));
|
|
902
|
-
vec3 col = mix(uStone, uStone * 0.85, length(q) * 0.6);
|
|
903
|
-
col = mix(col, uAccent, smoothstep(0.55, 0.9, r.y) * 0.5);
|
|
904
|
-
col = mix(col, uVein, vein * 0.85);
|
|
905
|
-
fragColor = vec4(col, 1.0);
|
|
906
|
-
}`
|
|
907
|
-
),
|
|
908
|
-
presets: [
|
|
909
|
-
{ name: "Nero", values: { uStone: [0.08, 0.08, 0.09], uVein: [0.9, 0.88, 0.82], uAccent: [0.7, 0.55, 0.3] } },
|
|
910
|
-
{ name: "Jade", values: { uStone: [0.18, 0.45, 0.35], uVein: [0.85, 0.95, 0.88], uAccent: [0.05, 0.2, 0.15] } }
|
|
911
|
-
]
|
|
912
|
-
};
|
|
913
|
-
var smoke = {
|
|
914
|
-
kind: "shader",
|
|
915
|
-
id: "smoke",
|
|
916
|
-
name: "Smoke",
|
|
917
|
-
description: "Billowing smoke or cloud drifting upwards.",
|
|
918
|
-
category: "organic",
|
|
919
|
-
tags: ["background", "atmosphere", "cloud", "mist"],
|
|
920
|
-
source: glsl(
|
|
921
|
-
`// Smoke: billowing fractal noise drifting upwards.
|
|
922
|
-
uniform vec3 uBackground; // @control color default=#0B0F19 label="Background"
|
|
923
|
-
uniform vec3 uSmoke; // @control color default=#CBD5E1 label="Smoke"
|
|
924
|
-
uniform float uSpeed; // @control min=0 max=2 default=0.3 label="Speed"
|
|
925
|
-
uniform float uDensity; // @control min=0 max=2 default=1 label="Density"
|
|
926
|
-
uniform int uDetail; // @control min=1 max=8 default=6 label="Detail"`,
|
|
927
|
-
HASH,
|
|
928
|
-
NOISE,
|
|
929
|
-
FBM,
|
|
930
|
-
`void mainImage(out vec4 fragColor, in vec2 fragCoord) {
|
|
931
|
-
vec2 p = fragCoord / iResolution.y * 3.0;
|
|
932
|
-
float t = iTime * uSpeed;
|
|
933
|
-
p.y -= t;
|
|
934
|
-
float n = fbm(p + fbm(p + vec2(t * 0.3, 0.0), uDetail), uDetail);
|
|
935
|
-
float body = smoothstep(0.35, 0.85, n) * uDensity;
|
|
936
|
-
float fade = smoothstep(0.0, 0.7, fragCoord.y / iResolution.y + 0.2);
|
|
937
|
-
vec3 col = mix(uBackground, uSmoke, clamp(body * fade, 0.0, 1.0));
|
|
938
|
-
fragColor = vec4(col, 1.0);
|
|
939
|
-
}`
|
|
940
|
-
)
|
|
941
|
-
};
|
|
942
|
-
var lavaLamp = {
|
|
943
|
-
kind: "shader",
|
|
944
|
-
id: "lava-lamp",
|
|
945
|
-
name: "Lava lamp",
|
|
946
|
-
description: "Soft metaballs merging and parting as they rise and fall.",
|
|
947
|
-
category: "organic",
|
|
948
|
-
tags: ["background", "playful", "blobs", "retro"],
|
|
949
|
-
source: glsl(
|
|
950
|
-
`// Lava lamp: metaballs merging as they rise and fall.
|
|
951
|
-
uniform vec3 uBackground; // @control color default=#1A0B2E label="Background"
|
|
952
|
-
uniform vec3 uBlobA; // @control color default=#FF5E8A label="Blob A"
|
|
953
|
-
uniform vec3 uBlobB; // @control color default=#FFB347 label="Blob B"
|
|
954
|
-
uniform int uCount; // @control min=2 max=12 default=7 label="Blobs"
|
|
955
|
-
uniform float uSpeed; // @control min=0 max=2 default=0.5 label="Speed"
|
|
956
|
-
uniform float uSize; // @control min=0.02 max=0.3 default=0.12 label="Size"`,
|
|
957
|
-
HASH,
|
|
958
|
-
`void mainImage(out vec4 fragColor, in vec2 fragCoord) {
|
|
959
|
-
vec2 p = fragCoord / iResolution.y;
|
|
960
|
-
float aspect = iResolution.x / iResolution.y;
|
|
961
|
-
float t = iTime * uSpeed;
|
|
962
|
-
float field = 0.0;
|
|
963
|
-
float tint = 0.0;
|
|
964
|
-
for (int i = 0; i < 12; i++) {
|
|
965
|
-
if (i >= uCount) break;
|
|
966
|
-
float fi = float(i);
|
|
967
|
-
vec2 seed = hash22(vec2(fi, 7.0));
|
|
968
|
-
vec2 c = vec2(seed.x * aspect, 0.5 + 0.45 * sin(t * (0.4 + seed.y) + fi * 2.1));
|
|
969
|
-
c.x += 0.1 * sin(t * 0.7 + fi);
|
|
970
|
-
float r = uSize * (0.7 + 0.6 * seed.y);
|
|
971
|
-
float k = r * r / dot(p - c, p - c);
|
|
972
|
-
field += k;
|
|
973
|
-
tint += k * seed.x;
|
|
974
|
-
}
|
|
975
|
-
float inside = smoothstep(0.9, 1.1, field);
|
|
976
|
-
vec3 blob = mix(uBlobA, uBlobB, clamp(tint / max(field, 1e-3), 0.0, 1.0));
|
|
977
|
-
blob *= 0.85 + 0.15 * smoothstep(1.0, 3.0, field);
|
|
978
|
-
vec3 col = mix(uBackground, blob, inside);
|
|
979
|
-
col += blob * 0.15 * smoothstep(0.3, 0.9, field) * (1.0 - inside);
|
|
980
|
-
fragColor = vec4(col, 1.0);
|
|
981
|
-
}`
|
|
982
|
-
)
|
|
983
|
-
};
|
|
984
|
-
var plasma = {
|
|
985
|
-
kind: "shader",
|
|
986
|
-
id: "plasma",
|
|
987
|
-
name: "Plasma",
|
|
988
|
-
description: "The demoscene classic: interfering sine waves through a colour palette.",
|
|
989
|
-
category: "organic",
|
|
990
|
-
tags: ["background", "retro", "psychedelic", "loop"],
|
|
991
|
-
loop: 6.28318,
|
|
992
|
-
source: glsl(
|
|
993
|
-
`// Plasma: interfering sine waves through a cosine palette.
|
|
994
|
-
uniform float uScale; // @control min=1 max=20 default=8 label="Scale"
|
|
995
|
-
uniform float uShift; // @control min=0 max=1 default=0 label="Hue shift"
|
|
996
|
-
uniform vec3 uPhase; // @control min=0 max=1 default=0,0.33,0.67 label="Palette phase"`,
|
|
997
|
-
PALETTE,
|
|
998
|
-
`void mainImage(out vec4 fragColor, in vec2 fragCoord) {
|
|
999
|
-
vec2 p = (fragCoord - 0.5 * iResolution.xy) / iResolution.y * uScale;
|
|
1000
|
-
float t = iTime;
|
|
1001
|
-
float v = sin(p.x + t);
|
|
1002
|
-
v += sin((p.y + t) * 0.5);
|
|
1003
|
-
v += sin((p.x + p.y + t) * 0.5);
|
|
1004
|
-
vec2 c = p + vec2(sin(t * 0.5), cos(t * 0.5)) * uScale * 0.3;
|
|
1005
|
-
v += sin(sqrt(dot(c, c) + 1.0) + t);
|
|
1006
|
-
vec3 col = palette(v * 0.25 + uShift, vec3(0.5), vec3(0.5), vec3(1.0), uPhase);
|
|
1007
|
-
fragColor = vec4(col, 1.0);
|
|
1008
|
-
}`
|
|
1009
|
-
)
|
|
1010
|
-
};
|
|
1011
|
-
var caustics = {
|
|
1012
|
-
kind: "shader",
|
|
1013
|
-
id: "caustics",
|
|
1014
|
-
name: "Caustics",
|
|
1015
|
-
description: "Light dancing on the floor of a swimming pool.",
|
|
1016
|
-
category: "organic",
|
|
1017
|
-
tags: ["background", "water", "summer", "calm"],
|
|
1018
|
-
source: glsl(
|
|
1019
|
-
`// Caustics: light dancing on the floor of a pool.
|
|
1020
|
-
uniform vec3 uWater; // @control color default=#0369A1 label="Water"
|
|
1021
|
-
uniform vec3 uLight; // @control color default=#E0F2FE label="Light"
|
|
1022
|
-
uniform float uSpeed; // @control min=0 max=2 default=0.5 label="Speed"
|
|
1023
|
-
uniform float uScale; // @control min=1 max=10 default=4 label="Scale"
|
|
1024
|
-
uniform float uBright; // @control min=0 max=2 default=1 label="Brightness"`,
|
|
1025
|
-
`void mainImage(out vec4 fragColor, in vec2 fragCoord) {
|
|
1026
|
-
// After Dave Hoskins' "Tileable water caustic".
|
|
1027
|
-
vec2 uv = fragCoord / iResolution.y * uScale * 0.25;
|
|
1028
|
-
float t = iTime * uSpeed * 0.5 + 23.0;
|
|
1029
|
-
vec2 p = mod(uv * 6.28318, 6.28318) - 250.0;
|
|
1030
|
-
vec2 i = p;
|
|
1031
|
-
float c = 1.0;
|
|
1032
|
-
float inten = 0.005;
|
|
1033
|
-
for (int n = 0; n < 5; n++) {
|
|
1034
|
-
float tn = t * (1.0 - 3.5 / float(n + 1));
|
|
1035
|
-
i = p + vec2(cos(tn - i.x) + sin(tn + i.y), sin(tn - i.y) + cos(tn + i.x));
|
|
1036
|
-
c += 1.0 / length(vec2(p.x / (sin(i.x + tn) / inten), p.y / (cos(i.y + tn) / inten)));
|
|
1037
|
-
}
|
|
1038
|
-
c /= 5.0;
|
|
1039
|
-
c = 1.17 - pow(c, 1.4);
|
|
1040
|
-
float light = pow(abs(c), 8.0) * uBright;
|
|
1041
|
-
vec3 col = uWater + uLight * clamp(light, 0.0, 1.5);
|
|
1042
|
-
fragColor = vec4(col, 1.0);
|
|
1043
|
-
}`
|
|
1044
|
-
)
|
|
1045
|
-
};
|
|
1046
|
-
var flames = {
|
|
1047
|
-
kind: "shader",
|
|
1048
|
-
id: "flames",
|
|
1049
|
-
name: "Flames",
|
|
1050
|
-
description: "A wall of fire licking upwards from the bottom edge.",
|
|
1051
|
-
category: "organic",
|
|
1052
|
-
tags: ["fire", "energy", "hot", "overlay"],
|
|
1053
|
-
source: glsl(
|
|
1054
|
-
`// Flames: a wall of fire licking upwards.
|
|
1055
|
-
uniform vec3 uHot; // @control color default=#FFF3B0 label="Hot"
|
|
1056
|
-
uniform vec3 uMid; // @control color default=#FF7A00 label="Mid"
|
|
1057
|
-
uniform vec3 uCool; // @control color default=#7A0000 label="Cool"
|
|
1058
|
-
uniform float uSpeed; // @control min=0 max=3 default=1.2 label="Speed"
|
|
1059
|
-
uniform float uHeight; // @control min=0.1 max=1.5 default=0.7 label="Height"`,
|
|
1060
|
-
HASH,
|
|
1061
|
-
NOISE,
|
|
1062
|
-
FBM,
|
|
1063
|
-
`void mainImage(out vec4 fragColor, in vec2 fragCoord) {
|
|
1064
|
-
vec2 uv = fragCoord / iResolution.xy;
|
|
1065
|
-
vec2 p = vec2(fragCoord.x / iResolution.y, uv.y);
|
|
1066
|
-
float t = iTime * uSpeed;
|
|
1067
|
-
float n = fbm(vec2(p.x * 4.0, p.y * 3.0 - t * 1.5), 5);
|
|
1068
|
-
float heat = (1.0 - uv.y / uHeight) + (n - 0.5) * 1.1;
|
|
1069
|
-
heat = clamp(heat, 0.0, 1.0);
|
|
1070
|
-
vec3 col = mix(vec3(0.0), uCool, smoothstep(0.05, 0.3, heat));
|
|
1071
|
-
col = mix(col, uMid, smoothstep(0.3, 0.6, heat));
|
|
1072
|
-
col = mix(col, uHot, smoothstep(0.6, 0.95, heat));
|
|
1073
|
-
fragColor = vec4(col, 1.0);
|
|
1074
|
-
}`
|
|
1075
|
-
),
|
|
1076
|
-
presets: [
|
|
1077
|
-
{ name: "Spirit", values: { uHot: [0.85, 1, 1], uMid: [0.2, 0.6, 1], uCool: [0.05, 0.05, 0.4] } },
|
|
1078
|
-
{ name: "Toxic", values: { uHot: [0.95, 1, 0.7], uMid: [0.4, 0.95, 0.2], uCool: [0.05, 0.25, 0.05] } }
|
|
1079
|
-
]
|
|
1080
|
-
};
|
|
1081
|
-
|
|
1082
|
-
// src/catalog/shaders/patterns.ts
|
|
1083
|
-
var dotWave = {
|
|
1084
|
-
kind: "shader",
|
|
1085
|
-
id: "dot-wave",
|
|
1086
|
-
name: "Dot wave",
|
|
1087
|
-
description: "A halftone grid of dots swelling in a travelling wave.",
|
|
1088
|
-
category: "pattern",
|
|
1089
|
-
tags: ["background", "halftone", "tech", "minimal"],
|
|
1090
|
-
loop: 4,
|
|
1091
|
-
source: glsl(
|
|
1092
|
-
`// Dot wave: a grid of dots swelling in a travelling wave.
|
|
1093
|
-
uniform vec3 uBackground; // @control color default=#0F172A label="Background"
|
|
1094
|
-
uniform vec3 uDot; // @control color default=#38BDF8 label="Dot"
|
|
1095
|
-
uniform float uSpacing; // @control min=8 max=64 default=22 label="Spacing (px)"
|
|
1096
|
-
uniform float uWaves; // @control min=0.5 max=6 default=2 label="Waves"
|
|
1097
|
-
uniform bool uRadial; // @control default=true label="Radial"`,
|
|
1098
|
-
`void mainImage(out vec4 fragColor, in vec2 fragCoord) {
|
|
1099
|
-
vec2 cell = floor(fragCoord / uSpacing);
|
|
1100
|
-
vec2 local = fract(fragCoord / uSpacing) - 0.5;
|
|
1101
|
-
vec2 centre = (cell + 0.5) * uSpacing;
|
|
1102
|
-
vec2 p = (centre - 0.5 * iResolution.xy) / iResolution.y;
|
|
1103
|
-
float d = uRadial ? length(p) : p.x + p.y * 0.3;
|
|
1104
|
-
float phase = d * uWaves * 6.28318 - iTime * 6.28318 / 4.0;
|
|
1105
|
-
float size = 0.1 + 0.32 * (0.5 + 0.5 * sin(phase));
|
|
1106
|
-
float aa = 1.5 / uSpacing;
|
|
1107
|
-
float disc = 1.0 - smoothstep(size - aa, size + aa, length(local));
|
|
1108
|
-
vec3 col = mix(uBackground, uDot * (0.6 + 0.4 * size / 0.42), disc);
|
|
1109
|
-
fragColor = vec4(col, 1.0);
|
|
1110
|
-
}`
|
|
1111
|
-
)
|
|
1112
|
-
};
|
|
1113
|
-
var truchet = {
|
|
1114
|
-
kind: "shader",
|
|
1115
|
-
id: "truchet",
|
|
1116
|
-
name: "Truchet flow",
|
|
1117
|
-
description: "Quarter-circle Truchet tiles forming endless winding paths, with light flowing along them.",
|
|
1118
|
-
category: "pattern",
|
|
1119
|
-
tags: ["background", "maze", "geometric", "tech"],
|
|
1120
|
-
source: glsl(
|
|
1121
|
-
`// Truchet flow: winding quarter-circle tiles with light flowing along them.
|
|
1122
|
-
uniform vec3 uBackground; // @control color default=#111827 label="Background"
|
|
1123
|
-
uniform vec3 uLine; // @control color default=#F472B6 label="Line"
|
|
1124
|
-
uniform float uTiles; // @control min=3 max=30 default=10 label="Tiles"
|
|
1125
|
-
uniform float uWidth; // @control min=0.02 max=0.3 default=0.1 label="Line width"
|
|
1126
|
-
uniform float uSpeed; // @control min=0 max=3 default=1 label="Flow speed"`,
|
|
1127
|
-
HASH,
|
|
1128
|
-
`void mainImage(out vec4 fragColor, in vec2 fragCoord) {
|
|
1129
|
-
vec2 p = fragCoord / iResolution.y * uTiles;
|
|
1130
|
-
vec2 id = floor(p);
|
|
1131
|
-
vec2 f = fract(p) - 0.5;
|
|
1132
|
-
float flip = hash12(id) > 0.5 ? 1.0 : -1.0;
|
|
1133
|
-
f.x *= flip;
|
|
1134
|
-
vec2 c = f.x > -f.y ? vec2(0.5) : vec2(-0.5);
|
|
1135
|
-
vec2 q = f - c;
|
|
1136
|
-
float d = abs(length(q) - 0.5);
|
|
1137
|
-
float angle = atan(q.y, q.x);
|
|
1138
|
-
float checker = mod(id.x + id.y, 2.0) * 2.0 - 1.0;
|
|
1139
|
-
float flow = 0.5 + 0.5 * sin(angle * 4.0 * checker * flip + iTime * uSpeed * 4.0);
|
|
1140
|
-
float line = 1.0 - smoothstep(uWidth - 0.02, uWidth, d);
|
|
1141
|
-
vec3 col = mix(uBackground, uLine * (0.45 + 0.75 * flow), line);
|
|
1142
|
-
fragColor = vec4(col, 1.0);
|
|
1143
|
-
}`
|
|
1144
|
-
)
|
|
1145
|
-
};
|
|
1146
|
-
var hexPulse = {
|
|
1147
|
-
kind: "shader",
|
|
1148
|
-
id: "hex-pulse",
|
|
1149
|
-
name: "Hex pulse",
|
|
1150
|
-
description: "A honeycomb grid lighting up cell by cell in ripples.",
|
|
1151
|
-
category: "pattern",
|
|
1152
|
-
tags: ["background", "tech", "honeycomb", "sci-fi"],
|
|
1153
|
-
source: glsl(
|
|
1154
|
-
`// Hex pulse: a honeycomb grid lighting up in ripples.
|
|
1155
|
-
uniform vec3 uBackground; // @control color default=#020617 label="Background"
|
|
1156
|
-
uniform vec3 uEdge; // @control color default=#22D3EE label="Edge"
|
|
1157
|
-
uniform vec3 uFill; // @control color default=#0E7490 label="Fill"
|
|
1158
|
-
uniform float uCells; // @control min=3 max=40 default=12 label="Cells"
|
|
1159
|
-
uniform float uSpeed; // @control min=0 max=3 default=1 label="Speed"`,
|
|
1160
|
-
HASH,
|
|
1161
|
-
`vec4 hexCoords(vec2 p) {
|
|
1162
|
-
vec2 r = vec2(1.0, 1.7320508);
|
|
1163
|
-
vec2 h = r * 0.5;
|
|
1164
|
-
vec2 a = mod(p, r) - h;
|
|
1165
|
-
vec2 b = mod(p - h, r) - h;
|
|
1166
|
-
vec2 gv = dot(a, a) < dot(b, b) ? a : b;
|
|
1167
|
-
return vec4(gv, p - gv);
|
|
1168
|
-
}
|
|
1169
|
-
|
|
1170
|
-
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
|
|
1171
|
-
vec2 p = (fragCoord - 0.5 * iResolution.xy) / iResolution.y * uCells;
|
|
1172
|
-
vec4 h = hexCoords(p);
|
|
1173
|
-
vec2 gv = abs(h.xy);
|
|
1174
|
-
float edgeDist = 0.5 - max(dot(gv, normalize(vec2(1.0, 1.7320508))), gv.x);
|
|
1175
|
-
float ripple = 0.5 + 0.5 * sin(length(h.zw) * 0.6 - iTime * uSpeed * 3.0);
|
|
1176
|
-
float spark = step(0.92, hash12(h.zw + floor(iTime * uSpeed * 2.0)));
|
|
1177
|
-
float lit = clamp(pow(ripple, 6.0) + spark * 0.6, 0.0, 1.0);
|
|
1178
|
-
vec3 col = mix(uBackground, uFill, lit * smoothstep(0.0, 0.1, edgeDist));
|
|
1179
|
-
col = mix(col, uEdge, (1.0 - smoothstep(0.0, 0.04, edgeDist)) * (0.3 + 0.7 * ripple));
|
|
1180
|
-
fragColor = vec4(col, 1.0);
|
|
1181
|
-
}`
|
|
1182
|
-
)
|
|
1183
|
-
};
|
|
1184
|
-
var moire = {
|
|
1185
|
-
kind: "shader",
|
|
1186
|
-
id: "moire",
|
|
1187
|
-
name: "Moir\xE9 rings",
|
|
1188
|
-
description: "Two sets of concentric rings sliding over each other into shimmering interference.",
|
|
1189
|
-
category: "pattern",
|
|
1190
|
-
tags: ["background", "op-art", "hypnotic", "minimal"],
|
|
1191
|
-
source: glsl(
|
|
1192
|
-
`// Moir\xE9 rings: two ring sets interfering as they slide.
|
|
1193
|
-
uniform vec3 uInk; // @control color default=#111111 label="Ink"
|
|
1194
|
-
uniform vec3 uPaper; // @control color default=#F5F5F0 label="Paper"
|
|
1195
|
-
uniform float uRings; // @control min=10 max=200 default=80 label="Rings"
|
|
1196
|
-
uniform float uDrift; // @control min=0 max=0.5 default=0.15 label="Drift"`,
|
|
1197
|
-
`void mainImage(out vec4 fragColor, in vec2 fragCoord) {
|
|
1198
|
-
vec2 p = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;
|
|
1199
|
-
vec2 o = uDrift * vec2(sin(iTime * 0.4), cos(iTime * 0.3));
|
|
1200
|
-
float a = sin(length(p - o) * uRings);
|
|
1201
|
-
float b = sin(length(p + o) * uRings);
|
|
1202
|
-
float v = smoothstep(-0.2, 0.2, a * b);
|
|
1203
|
-
fragColor = vec4(mix(uInk, uPaper, v), 1.0);
|
|
1204
|
-
}`
|
|
1205
|
-
)
|
|
1206
|
-
};
|
|
1207
|
-
var barberStripes = {
|
|
1208
|
-
kind: "shader",
|
|
1209
|
-
id: "stripes",
|
|
1210
|
-
name: "Stripes",
|
|
1211
|
-
description: "Diagonal stripes marching across, for progress bars, banners and warnings.",
|
|
1212
|
-
category: "pattern",
|
|
1213
|
-
tags: ["progress", "banner", "warning", "loop"],
|
|
1214
|
-
loop: 1,
|
|
1215
|
-
source: glsl(
|
|
1216
|
-
`// Stripes: diagonal stripes marching across.
|
|
1217
|
-
uniform vec3 uColorA; // @control color default=#FACC15 label="Colour A"
|
|
1218
|
-
uniform vec3 uColorB; // @control color default=#18181B label="Colour B"
|
|
1219
|
-
uniform float uWidth; // @control min=4 max=120 default=28 label="Stripe width (px)"
|
|
1220
|
-
uniform float uAngle; // @control min=-90 max=90 default=45 label="Angle (\xB0)"
|
|
1221
|
-
uniform float uSoft; // @control min=0 max=1 default=0.05 label="Softness"`,
|
|
1222
|
-
`void mainImage(out vec4 fragColor, in vec2 fragCoord) {
|
|
1223
|
-
float a = radians(uAngle);
|
|
1224
|
-
float d = dot(fragCoord, vec2(cos(a), sin(a))) / (uWidth * 2.0);
|
|
1225
|
-
float s = fract(d - iTime);
|
|
1226
|
-
float edge = max(uSoft * 0.5, 0.5 / uWidth);
|
|
1227
|
-
float m = smoothstep(0.5 - edge, 0.5 + edge, s) - smoothstep(1.0 - edge, 1.0, s) + (1.0 - smoothstep(0.0, edge, s));
|
|
1228
|
-
fragColor = vec4(mix(uColorA, uColorB, clamp(m, 0.0, 1.0)), 1.0);
|
|
1229
|
-
}`
|
|
1230
|
-
)
|
|
1231
|
-
};
|
|
1232
|
-
var voronoi = {
|
|
1233
|
-
kind: "shader",
|
|
1234
|
-
id: "voronoi",
|
|
1235
|
-
name: "Living cells",
|
|
1236
|
-
description: "Voronoi cells drifting and dividing, like a microscope slide.",
|
|
1237
|
-
category: "pattern",
|
|
1238
|
-
tags: ["background", "biology", "cells", "organic"],
|
|
1239
|
-
source: glsl(
|
|
1240
|
-
`// Living cells: drifting Voronoi cells with glowing walls.
|
|
1241
|
-
uniform vec3 uBackground; // @control color default=#052E16 label="Background"
|
|
1242
|
-
uniform vec3 uCell; // @control color default=#4ADE80 label="Cell"
|
|
1243
|
-
uniform vec3 uWall; // @control color default=#DCFCE7 label="Wall"
|
|
1244
|
-
uniform float uScale; // @control min=2 max=20 default=6 label="Scale"
|
|
1245
|
-
uniform float uSpeed; // @control min=0 max=2 default=0.5 label="Speed"`,
|
|
1246
|
-
HASH,
|
|
1247
|
-
`void mainImage(out vec4 fragColor, in vec2 fragCoord) {
|
|
1248
|
-
vec2 p = fragCoord / iResolution.y * uScale;
|
|
1249
|
-
vec2 i = floor(p);
|
|
1250
|
-
vec2 f = fract(p);
|
|
1251
|
-
float d1 = 8.0;
|
|
1252
|
-
float d2 = 8.0;
|
|
1253
|
-
float shade = 0.0;
|
|
1254
|
-
for (int y = -1; y <= 1; y++) {
|
|
1255
|
-
for (int x = -1; x <= 1; x++) {
|
|
1256
|
-
vec2 g = vec2(float(x), float(y));
|
|
1257
|
-
vec2 o = hash22(i + g);
|
|
1258
|
-
o = 0.5 + 0.4 * sin(iTime * uSpeed + 6.28318 * o);
|
|
1259
|
-
float d = length(g + o - f);
|
|
1260
|
-
if (d < d1) { d2 = d1; d1 = d; shade = hash12(i + g); }
|
|
1261
|
-
else if (d < d2) { d2 = d; }
|
|
1262
|
-
}
|
|
1263
|
-
}
|
|
1264
|
-
float wall = 1.0 - smoothstep(0.0, 0.08, d2 - d1);
|
|
1265
|
-
vec3 col = mix(uBackground, uCell * (0.4 + 0.6 * shade), smoothstep(0.9, 0.1, d1));
|
|
1266
|
-
col = mix(col, uWall, wall);
|
|
1267
|
-
fragColor = vec4(col, 1.0);
|
|
1268
|
-
}`
|
|
1269
|
-
)
|
|
1270
|
-
};
|
|
1271
|
-
var kaleidoscope = {
|
|
1272
|
-
kind: "shader",
|
|
1273
|
-
id: "kaleidoscope",
|
|
1274
|
-
name: "Kaleidoscope",
|
|
1275
|
-
description: "Mirrored wedges of shifting colour turning around the centre.",
|
|
1276
|
-
category: "pattern",
|
|
1277
|
-
tags: ["psychedelic", "music", "symmetry"],
|
|
1278
|
-
source: glsl(
|
|
1279
|
-
`// Kaleidoscope: mirrored wedges of shifting colour.
|
|
1280
|
-
uniform int uSegments; // @control min=2 max=16 default=8 label="Segments"
|
|
1281
|
-
uniform float uZoom; // @control min=0.5 max=8 default=3 label="Zoom"
|
|
1282
|
-
uniform float uSpeed; // @control min=0 max=2 default=0.4 label="Speed"
|
|
1283
|
-
uniform vec3 uTint; // @control color default=#FFFFFF label="Tint"`,
|
|
1284
|
-
ROTATE,
|
|
1285
|
-
`void mainImage(out vec4 fragColor, in vec2 fragCoord) {
|
|
1286
|
-
vec2 p = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;
|
|
1287
|
-
float t = iTime * uSpeed;
|
|
1288
|
-
float n = float(uSegments);
|
|
1289
|
-
float a = atan(p.y, p.x) + t * 0.3;
|
|
1290
|
-
float r = length(p);
|
|
1291
|
-
float wedge = 6.28318 / n;
|
|
1292
|
-
a = mod(a, wedge);
|
|
1293
|
-
a = abs(a - wedge * 0.5);
|
|
1294
|
-
vec2 q = vec2(cos(a), sin(a)) * r * uZoom;
|
|
1295
|
-
q = rot(t * 0.2) * q + t;
|
|
1296
|
-
vec3 col = 0.5 + 0.5 * cos(vec3(0.0, 2.0, 4.0) + q.x * 2.0 + sin(q.y * 3.0) + t);
|
|
1297
|
-
col *= 0.6 + 0.4 * sin(q.x * 5.0) * sin(q.y * 5.0);
|
|
1298
|
-
col *= smoothstep(1.2, 0.2, r);
|
|
1299
|
-
fragColor = vec4(col * uTint, 1.0);
|
|
1300
|
-
}`
|
|
1301
|
-
)
|
|
1302
|
-
};
|
|
1303
|
-
var neonCircuit = {
|
|
1304
|
-
kind: "shader",
|
|
1305
|
-
id: "neon-circuit",
|
|
1306
|
-
name: "Neon circuit",
|
|
1307
|
-
description: "Glowing neon traces routed up a circuit board, with pulses of light racing along them.",
|
|
1308
|
-
category: "pattern",
|
|
1309
|
-
tags: ["background", "neon", "circuit", "tech", "80s", "vaporwave"],
|
|
1310
|
-
source: glsl(
|
|
1311
|
-
`// Neon circuit: glowing traces climbing a board in rounded jogs, with pulses running along them.
|
|
1312
|
-
uniform vec3 uBackground; // @control color default=#170A2E label="Background"
|
|
1313
|
-
uniform vec3 uColorA; // @control color default=#FF5FA2 label="Trace A"
|
|
1314
|
-
uniform vec3 uColorB; // @control color default=#43E5FF label="Trace B"
|
|
1315
|
-
uniform vec3 uColorC; // @control color default=#FFD23F label="Trace C"
|
|
1316
|
-
uniform vec3 uColorD; // @control color default=#9D7BFF label="Trace D"
|
|
1317
|
-
uniform float uLanes; // @control min=4 max=40 default=14 label="Lanes"
|
|
1318
|
-
uniform float uDensity; // @control min=0.1 max=1 default=0.7 label="Density"
|
|
1319
|
-
uniform float uWidth; // @control min=0.04 max=0.4 default=0.16 label="Line width"
|
|
1320
|
-
uniform float uGlow; // @control min=0 max=2 default=1 label="Glow"
|
|
1321
|
-
uniform float uSpeed; // @control min=0 max=3 default=1 label="Pulse speed"
|
|
1322
|
-
uniform float uPulses; // @control min=0 max=1 default=0.5 label="Pulses"
|
|
1323
|
-
uniform float uScroll; // @control min=-2 max=2 default=0.4 label="Scroll"
|
|
1324
|
-
uniform float uGrid; // @control min=0 max=1 default=0.3 label="Grid"`,
|
|
1325
|
-
HASH,
|
|
1326
|
-
`// Each trace k owns lanes 3k..3k+2 and climbs in runs of RUN lanes, jogging
|
|
1327
|
-
// sideways between them on rounded corners, so traces never cross.
|
|
1328
|
-
const float RUN = 2.5;
|
|
1329
|
-
const float RADIUS = 0.35;
|
|
1330
|
-
|
|
1331
|
-
float laneOf(float k, float j) {
|
|
1332
|
-
return 3.0 * k + floor(hash12(vec2(k, j) + 0.37) * 3.0) + 0.5;
|
|
1333
|
-
}
|
|
1334
|
-
|
|
1335
|
-
vec3 traceColor(float k) {
|
|
1336
|
-
float h = hash12(vec2(k, 7.3));
|
|
1337
|
-
return h < 0.25 ? uColorA : h < 0.5 ? uColorB : h < 0.75 ? uColorC : uColorD;
|
|
1338
|
-
}
|
|
1339
|
-
|
|
1340
|
-
// Distance to a quarter circle of RADIUS round c, in the quadrant whose signs are q.
|
|
1341
|
-
float arcDist(vec2 p, vec2 c, vec2 q) {
|
|
1342
|
-
vec2 d = p - c;
|
|
1343
|
-
return d.x * q.x >= 0.0 && d.y * q.y >= 0.0 ? abs(length(d) - RADIUS) : 1e5;
|
|
1344
|
-
}
|
|
1345
|
-
|
|
1346
|
-
// Trace k's run j: distance from p to its climb (x) and how far up it p is (y), then
|
|
1347
|
-
// the same for its jog into run j + 1 (z, w), measured along the run.
|
|
1348
|
-
vec4 run(vec2 p, float k, float j) {
|
|
1349
|
-
float x0 = laneOf(k, j - 1.0);
|
|
1350
|
-
float x1 = laneOf(k, j);
|
|
1351
|
-
float x2 = laneOf(k, j + 1.0);
|
|
1352
|
-
float y0 = j * RUN;
|
|
1353
|
-
float y1 = y0 + RUN;
|
|
1354
|
-
float lo = y0 + (x0 == x1 ? 0.0 : RADIUS);
|
|
1355
|
-
float hi = y1 - (x2 == x1 ? 0.0 : RADIUS);
|
|
1356
|
-
vec4 r = vec4(length(p - vec2(x1, clamp(p.y, lo, hi))), p.y - y0, 1e5, 0.0);
|
|
1357
|
-
if (x2 != x1) {
|
|
1358
|
-
float sx = sign(x2 - x1);
|
|
1359
|
-
float h = length(p - vec2(clamp(p.x, min(x1, x2) + RADIUS, max(x1, x2) - RADIUS), y1));
|
|
1360
|
-
h = min(h, arcDist(p, vec2(x1 + sx * RADIUS, y1 - RADIUS), vec2(-sx, 1.0)));
|
|
1361
|
-
h = min(h, arcDist(p, vec2(x2 - sx * RADIUS, y1 + RADIUS), vec2(sx, -1.0)));
|
|
1362
|
-
r.zw = vec2(h, RUN + abs(p.x - x1));
|
|
1363
|
-
}
|
|
1364
|
-
return r;
|
|
1365
|
-
}
|
|
1366
|
-
|
|
1367
|
-
// How bright a pulse is at s along run j of trace k: a bright head with a fading tail.
|
|
1368
|
-
// A pulse crosses one run per cycle and carries on into the next with the next cycle.
|
|
1369
|
-
float pulseAt(float k, float j, float s) {
|
|
1370
|
-
float cycle = iTime * uSpeed * 1.5 + hash12(vec2(k, 8.8));
|
|
1371
|
-
float behind = fract(cycle) * (RUN + abs(laneOf(k, j + 1.0) - laneOf(k, j))) - s;
|
|
1372
|
-
float lit = step(hash12(vec2(k, floor(cycle) - j)), uPulses);
|
|
1373
|
-
return lit * (behind >= 0.0 ? exp(-behind * 0.8) : exp(behind * 30.0));
|
|
1374
|
-
}
|
|
1375
|
-
|
|
1376
|
-
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
|
|
1377
|
-
float px = uLanes / iResolution.y;
|
|
1378
|
-
vec2 p = fragCoord * px;
|
|
1379
|
-
p.y += iTime * uScroll;
|
|
1380
|
-
|
|
1381
|
-
vec2 g = abs(fract(p / 1.5) - 0.5);
|
|
1382
|
-
float grid = 1.0 - smoothstep(0.0, px * 1.5, (0.5 - max(g.x, g.y)) * 1.5);
|
|
1383
|
-
vec3 light = uColorD * 0.12 * uGrid * grid;
|
|
1384
|
-
|
|
1385
|
-
float home = floor(p.x / 3.0);
|
|
1386
|
-
for (int i = -1; i <= 1; i++) {
|
|
1387
|
-
float k = home + float(i);
|
|
1388
|
-
if (hash12(vec2(k, 1.9)) > uDensity) continue;
|
|
1389
|
-
vec2 q = vec2(p.x, p.y + hash12(vec2(k, 4.1)) * RUN);
|
|
1390
|
-
float jq = floor(q.y / RUN);
|
|
1391
|
-
float w = uWidth * 0.5;
|
|
1392
|
-
// The trace's nearest point lights it; each piece's pulse lights only round that piece.
|
|
1393
|
-
float d = 1e5;
|
|
1394
|
-
float pulse = 0.0;
|
|
1395
|
-
for (int b = -1; b <= 1; b++) {
|
|
1396
|
-
float j = jq + float(b);
|
|
1397
|
-
vec4 r = run(q, k, j);
|
|
1398
|
-
d = min(d, min(r.x, r.z));
|
|
1399
|
-
pulse = max(pulse, pulseAt(k, j, r.y) * exp(-r.x / uWidth));
|
|
1400
|
-
if (r.z < 1e4) pulse = max(pulse, pulseAt(k, j, r.w) * exp(-r.z / uWidth));
|
|
1401
|
-
}
|
|
1402
|
-
float core = 1.0 - smoothstep(w - px, w + px, d);
|
|
1403
|
-
float glow = uGlow * (0.5 * exp(-d / uWidth) + 0.2 * exp(-d / (uWidth * 5.0)));
|
|
1404
|
-
vec3 c = traceColor(k);
|
|
1405
|
-
light += c * (core * 0.8 + glow + uGlow * pulse) + mix(c, vec3(1.0), 0.7) * core * pulse * 3.0;
|
|
1406
|
-
}
|
|
1407
|
-
fragColor = vec4(uBackground + (1.0 - exp(-light * 1.4)), 1.0);
|
|
1408
|
-
}`
|
|
1409
|
-
),
|
|
1410
|
-
presets: [
|
|
1411
|
-
{ name: "Mainframe", values: { uBackground: [0, 0.04, 0.02], uColorA: [0.2, 1, 0.45], uColorB: [0.1, 0.75, 0.35], uColorC: [0.6, 1, 0.7], uColorD: [0.1, 0.5, 0.25] } },
|
|
1412
|
-
{ name: "Blueprint", values: { uBackground: [0.02, 0.06, 0.16], uColorA: [0.35, 0.75, 1], uColorB: [0.6, 0.9, 1], uColorC: [1, 1, 1], uColorD: [0.25, 0.45, 0.9], uGlow: 0.6, uPulses: 0.3 } },
|
|
1413
|
-
{ name: "Dense", values: { uLanes: 26, uDensity: 1, uWidth: 0.12, uPulses: 0.8 } }
|
|
1414
|
-
]
|
|
1415
|
-
};
|
|
1416
|
-
|
|
1417
|
-
// src/catalog/shaders/retro.ts
|
|
1418
|
-
var synthwaveGrid = {
|
|
1419
|
-
kind: "shader",
|
|
1420
|
-
id: "synthwave",
|
|
1421
|
-
name: "Synthwave",
|
|
1422
|
-
description: "A neon perspective grid racing towards a striped retro sun.",
|
|
1423
|
-
category: "retro",
|
|
1424
|
-
tags: ["background", "80s", "neon", "music", "loop"],
|
|
1425
|
-
loop: 1,
|
|
1426
|
-
source: glsl(
|
|
1427
|
-
`// Synthwave: a neon grid racing towards a striped sun.
|
|
1428
|
-
uniform vec3 uSkyTop; // @control color default=#12002B label="Sky top"
|
|
1429
|
-
uniform vec3 uSkyBottom; // @control color default=#7A1A6B label="Sky bottom"
|
|
1430
|
-
uniform vec3 uSunTop; // @control color default=#FFE45E label="Sun top"
|
|
1431
|
-
uniform vec3 uSunBottom; // @control color default=#FF3D7F label="Sun bottom"
|
|
1432
|
-
uniform vec3 uGrid; // @control color default=#00F0FF label="Grid"
|
|
1433
|
-
uniform float uHorizon; // @control min=0.2 max=0.7 default=0.42 label="Horizon"`,
|
|
1434
|
-
`void mainImage(out vec4 fragColor, in vec2 fragCoord) {
|
|
1435
|
-
vec2 uv = fragCoord / iResolution.xy;
|
|
1436
|
-
vec2 p = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;
|
|
1437
|
-
float horizon = uHorizon - 0.5;
|
|
1438
|
-
vec3 col;
|
|
1439
|
-
if (p.y > horizon) {
|
|
1440
|
-
float h = (p.y - horizon) / (0.5 - horizon);
|
|
1441
|
-
col = mix(uSkyBottom, uSkyTop, h);
|
|
1442
|
-
vec2 s = p - vec2(0.0, horizon + 0.18);
|
|
1443
|
-
float sun = smoothstep(0.2, 0.195, length(s));
|
|
1444
|
-
float bands = step(0.0, sin((s.y + 0.2) * 60.0 - 1.5)) + step(0.06, s.y);
|
|
1445
|
-
sun *= clamp(bands, 0.0, 1.0);
|
|
1446
|
-
col = mix(col, mix(uSunBottom, uSunTop, clamp(s.y / 0.2 * 0.5 + 0.5, 0.0, 1.0)), sun);
|
|
1447
|
-
col += uSunBottom * 0.25 * exp(-length(s) * 4.0);
|
|
1448
|
-
} else {
|
|
1449
|
-
float depth = (horizon - p.y);
|
|
1450
|
-
vec2 g = vec2(p.x / depth, 1.0 / depth);
|
|
1451
|
-
g.y += iTime * 2.0;
|
|
1452
|
-
vec2 grid = abs(fract(g * 0.5) - 0.5) / fwidth(g * 0.5);
|
|
1453
|
-
float line = 1.0 - clamp(min(grid.x, grid.y), 0.0, 1.0);
|
|
1454
|
-
float fade = smoothstep(0.0, 0.25, depth);
|
|
1455
|
-
col = mix(uSkyTop * 0.4, uSkyBottom * 0.3, uv.y);
|
|
1456
|
-
col += uGrid * line * fade + uGrid * 0.25 * exp(-depth * 20.0);
|
|
1457
|
-
}
|
|
1458
|
-
fragColor = vec4(col, 1.0);
|
|
1459
|
-
}`
|
|
1460
|
-
)
|
|
1461
|
-
};
|
|
1462
|
-
var tvStatic = {
|
|
1463
|
-
kind: "shader",
|
|
1464
|
-
id: "tv-static",
|
|
1465
|
-
name: "TV static",
|
|
1466
|
-
description: "Analogue snow with rolling bars and scanlines.",
|
|
1467
|
-
category: "retro",
|
|
1468
|
-
tags: ["glitch", "analogue", "transition", "noise"],
|
|
1469
|
-
source: glsl(
|
|
1470
|
-
`// TV static: analogue snow with a rolling bar and scanlines.
|
|
1471
|
-
uniform float uAmount; // @control min=0 max=1 default=0.9 label="Snow"
|
|
1472
|
-
uniform float uScanlines; // @control min=0 max=1 default=0.35 label="Scanlines"
|
|
1473
|
-
uniform float uRoll; // @control min=0 max=1 default=0.3 label="Rolling bar"
|
|
1474
|
-
uniform vec3 uTint; // @control color default=#E6E6E6 label="Tint"`,
|
|
1475
|
-
HASH,
|
|
1476
|
-
`void mainImage(out vec4 fragColor, in vec2 fragCoord) {
|
|
1477
|
-
vec2 uv = fragCoord / iResolution.xy;
|
|
1478
|
-
float n = hash12(floor(fragCoord * 0.5) + floor(iTime * 30.0) * 17.31);
|
|
1479
|
-
float scan = 1.0 - uScanlines * 0.5 * (1.0 + sin(fragCoord.y * 3.14159));
|
|
1480
|
-
float bar = 1.0 + uRoll * 0.6 * smoothstep(0.15, 0.0, abs(fract(uv.y - iTime * 0.2) - 0.5) - 0.3);
|
|
1481
|
-
vec3 col = uTint * mix(0.5, n, uAmount) * scan * bar;
|
|
1482
|
-
col *= smoothstep(1.2, 0.4, length(uv - 0.5) * 1.4);
|
|
1483
|
-
fragColor = vec4(col, 1.0);
|
|
1484
|
-
}`
|
|
1485
|
-
)
|
|
1486
|
-
};
|
|
1487
|
-
var digitalRain = {
|
|
1488
|
-
kind: "shader",
|
|
1489
|
-
id: "digital-rain",
|
|
1490
|
-
name: "Digital rain",
|
|
1491
|
-
description: "Columns of glyph-like characters raining down a terminal.",
|
|
1492
|
-
category: "retro",
|
|
1493
|
-
tags: ["hacker", "code", "tech", "cyber"],
|
|
1494
|
-
source: glsl(
|
|
1495
|
-
`// Digital rain: glyph-like cells raining down in columns.
|
|
1496
|
-
uniform vec3 uBackground; // @control color default=#000000 label="Background"
|
|
1497
|
-
uniform vec3 uGlyph; // @control color default=#22FF66 label="Glyph"
|
|
1498
|
-
uniform float uCell; // @control min=6 max=40 default=14 label="Cell size (px)"
|
|
1499
|
-
uniform float uSpeed; // @control min=0 max=4 default=1 label="Speed"`,
|
|
1500
|
-
HASH,
|
|
1501
|
-
`float glyph(vec2 cell, vec2 f) {
|
|
1502
|
-
vec2 g = floor(f * vec2(3.0, 5.0));
|
|
1503
|
-
float on = step(0.45, hash12(g + cell * 13.7 + floor(iTime * 4.0 * hash12(cell))));
|
|
1504
|
-
vec2 inset = abs(fract(f * vec2(3.0, 5.0)) - 0.5);
|
|
1505
|
-
return on * step(max(inset.x, inset.y), 0.38);
|
|
1506
|
-
}
|
|
1507
|
-
|
|
1508
|
-
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
|
|
1509
|
-
vec2 q = fragCoord / uCell;
|
|
1510
|
-
vec2 cell = floor(q);
|
|
1511
|
-
vec2 f = fract(q);
|
|
1512
|
-
float col = cell.x;
|
|
1513
|
-
float speed = (0.5 + hash12(vec2(col, 1.0))) * uSpeed * 8.0;
|
|
1514
|
-
float offset = hash12(vec2(col, 2.0)) * 100.0;
|
|
1515
|
-
float head = fract((iTime * speed + offset) / 40.0) * (iResolution.y / uCell + 30.0);
|
|
1516
|
-
float y = iResolution.y / uCell - cell.y;
|
|
1517
|
-
float trail = head - y;
|
|
1518
|
-
float lit = trail >= 0.0 ? exp(-trail * 0.15) : 0.0;
|
|
1519
|
-
float isHead = step(abs(trail), 0.5);
|
|
1520
|
-
float g = glyph(cell, f);
|
|
1521
|
-
vec3 c = uBackground + uGlyph * g * lit + vec3(0.8) * g * isHead;
|
|
1522
|
-
fragColor = vec4(c, 1.0);
|
|
1523
|
-
}`
|
|
1524
|
-
)
|
|
1525
|
-
};
|
|
1526
|
-
var pixelSunset = {
|
|
1527
|
-
kind: "shader",
|
|
1528
|
-
id: "dither-gradient",
|
|
1529
|
-
name: "Dithered gradient",
|
|
1530
|
-
description: "A 1-bit Bayer-dithered gradient wave, like an old Mac or Game Boy screen.",
|
|
1531
|
-
category: "retro",
|
|
1532
|
-
tags: ["pixel", "1-bit", "lo-fi", "background"],
|
|
1533
|
-
source: glsl(
|
|
1534
|
-
`// Dithered gradient: a Bayer-dithered gradient wave in two colours.
|
|
1535
|
-
uniform vec3 uDark; // @control color default=#0F380F label="Dark"
|
|
1536
|
-
uniform vec3 uLight; // @control color default=#9BBC0F label="Light"
|
|
1537
|
-
uniform float uPixel; // @control min=1 max=12 default=4 step=1 label="Pixel size"
|
|
1538
|
-
uniform float uSpeed; // @control min=0 max=2 default=0.5 label="Speed"`,
|
|
1539
|
-
`float bayer4(vec2 p) {
|
|
1540
|
-
vec2 q = mod(floor(p), 4.0);
|
|
1541
|
-
int i = int(q.x) + int(q.y) * 4;
|
|
1542
|
-
int m[16] = int[16](0, 8, 2, 10, 12, 4, 14, 6, 3, 11, 1, 9, 15, 7, 13, 5);
|
|
1543
|
-
return (float(m[i]) + 0.5) / 16.0;
|
|
1544
|
-
}
|
|
1545
|
-
|
|
1546
|
-
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
|
|
1547
|
-
vec2 px = floor(fragCoord / uPixel);
|
|
1548
|
-
vec2 uv = px * uPixel / iResolution.xy;
|
|
1549
|
-
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);
|
|
1550
|
-
v = clamp(v, 0.0, 1.0);
|
|
1551
|
-
float on = step(bayer4(px), v);
|
|
1552
|
-
fragColor = vec4(mix(uDark, uLight, on), 1.0);
|
|
1553
|
-
}`
|
|
1554
|
-
),
|
|
1555
|
-
presets: [
|
|
1556
|
-
{ name: "Mac", values: { uDark: [0.05, 0.05, 0.05], uLight: [0.93, 0.93, 0.9] } },
|
|
1557
|
-
{ name: "Amber", values: { uDark: [0.1, 0.04, 0], uLight: [1, 0.69, 0] } }
|
|
1558
|
-
]
|
|
1559
|
-
};
|
|
1560
|
-
|
|
1561
|
-
// src/catalog/types.ts
|
|
1562
|
-
var EFFECT_CATEGORIES = [
|
|
1563
|
-
"gradient",
|
|
1564
|
-
"organic",
|
|
1565
|
-
"pattern",
|
|
1566
|
-
"light",
|
|
1567
|
-
"space",
|
|
1568
|
-
"retro",
|
|
1569
|
-
"image",
|
|
1570
|
-
"transition",
|
|
1571
|
-
"loader",
|
|
1572
|
-
"data",
|
|
1573
|
-
"icon"
|
|
1574
|
-
];
|
|
1575
|
-
var VECTOR_CONTROL_TYPES = ["float", "int", "bool", "color", "text"];
|
|
1576
|
-
var PLACEHOLDER = /\{\{\s*([A-Za-z_]\w*)\s*(?::\s*([A-Za-z0-9]*)([^}]*))?\}\}/g;
|
|
1577
|
-
function motionStrings(motion) {
|
|
1578
|
-
const out = [];
|
|
1579
|
-
const walk = (v) => {
|
|
1580
|
-
if (typeof v === "string") out.push(v);
|
|
1581
|
-
else if (Array.isArray(v)) v.forEach(walk);
|
|
1582
|
-
else if (v && typeof v === "object") Object.values(v).forEach(walk);
|
|
1583
|
-
};
|
|
1584
|
-
walk(motion);
|
|
1585
|
-
return out;
|
|
1586
|
-
}
|
|
1587
|
-
function vectorControls(motion) {
|
|
1588
|
-
const controls = [];
|
|
1589
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1590
|
-
for (const text of motionStrings(motion)) {
|
|
1591
|
-
for (const [, name, type, attributes] of text.matchAll(PLACEHOLDER)) {
|
|
1592
|
-
if (type === void 0 || seen.has(name) || !VECTOR_CONTROL_TYPES.includes(type)) continue;
|
|
1593
|
-
seen.add(name);
|
|
1594
|
-
const colour = type === "color";
|
|
1595
|
-
controls.push(controlFromDeclaration(name, colour ? "vec3" : type, `${attributes}${colour ? " color" : ""}`, controls.length + 1));
|
|
1596
|
-
}
|
|
1597
|
-
}
|
|
1598
|
-
return controls;
|
|
1599
|
-
}
|
|
1600
|
-
function vectorEffectControls(effect) {
|
|
1601
|
-
const declared = vectorControls(effect.motion);
|
|
1602
|
-
const names = new Set(declared.map((c) => c.name));
|
|
1603
|
-
const listed = (effect.controls ?? []).filter((c) => !names.has(c.name));
|
|
1604
|
-
return [...declared, ...listed.map((c, i) => vectorControlDef(c, declared.length + i + 1))];
|
|
1605
|
-
}
|
|
1606
|
-
function vectorControlIssues(motion) {
|
|
1607
|
-
const issues = [];
|
|
1608
|
-
const declarations = /* @__PURE__ */ new Map();
|
|
1609
|
-
for (const text of motionStrings(motion)) {
|
|
1610
|
-
for (const [whole, name, type] of text.matchAll(PLACEHOLDER)) {
|
|
1611
|
-
if (type === void 0) continue;
|
|
1612
|
-
if (!VECTOR_CONTROL_TYPES.includes(type))
|
|
1613
|
-
issues.push(`${whole}: a control's type is one of ${VECTOR_CONTROL_TYPES.join(", ")}`);
|
|
1614
|
-
else if (declarations.has(name) && declarations.get(name) !== whole)
|
|
1615
|
-
issues.push(`"${name}" is declared twice; declare it once and write {{${name}}} everywhere else`);
|
|
1616
|
-
else declarations.set(name, whole);
|
|
1617
|
-
}
|
|
1618
|
-
}
|
|
1619
|
-
return issues;
|
|
1620
|
-
}
|
|
1621
|
-
function written(control, value) {
|
|
1622
|
-
if (control.color && Array.isArray(value)) return rgbToHex(value);
|
|
1623
|
-
if (Array.isArray(value)) return value.join(" ");
|
|
1624
|
-
return value;
|
|
1625
|
-
}
|
|
1626
|
-
var escapeMarkup = (text) => text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
1627
|
-
function vectorControlDef(control, line = 0) {
|
|
1628
|
-
const min = control.min ?? 0;
|
|
1629
|
-
return {
|
|
1630
|
-
name: control.name,
|
|
1631
|
-
type: control.type,
|
|
1632
|
-
label: control.label ?? control.name,
|
|
1633
|
-
color: control.color ?? false,
|
|
1634
|
-
min,
|
|
1635
|
-
max: control.max ?? Math.max(min + 1, 1),
|
|
1636
|
-
step: control.step ?? (control.type === "int" ? 1 : 0.01),
|
|
1637
|
-
default: control.default,
|
|
1638
|
-
line
|
|
1639
|
-
};
|
|
1640
|
-
}
|
|
1641
|
-
function renderVectorEffect(effect, values = {}) {
|
|
1642
|
-
const filled = {};
|
|
1643
|
-
const placeholders = /* @__PURE__ */ new Map();
|
|
1644
|
-
for (const control of vectorEffectControls(effect)) {
|
|
1645
|
-
const given = values[control.name];
|
|
1646
|
-
const check = given === void 0 ? void 0 : checkControlValue(control, given);
|
|
1647
|
-
filled[control.name] = check?.ok ? check.value : control.default;
|
|
1648
|
-
placeholders.set(control.name, written(control, filled[control.name]));
|
|
1649
|
-
}
|
|
1650
|
-
for (const [name, value] of Object.entries(effect.derived?.(filled) ?? {})) placeholders.set(name, value);
|
|
1651
|
-
const fill = (text, markup) => {
|
|
1652
|
-
const whole = /^\{\{\s*([A-Za-z_]\w*)\s*(?::[^}]*)?\}\}$/.exec(text);
|
|
1653
|
-
if (!markup && whole && placeholders.has(whole[1])) return placeholders.get(whole[1]);
|
|
1654
|
-
return text.replace(PLACEHOLDER, (match, name) => {
|
|
1655
|
-
if (!placeholders.has(name)) return match;
|
|
1656
|
-
const value = String(placeholders.get(name));
|
|
1657
|
-
return markup ? escapeMarkup(value) : value;
|
|
1658
|
-
});
|
|
1659
|
-
};
|
|
1660
|
-
const walk = (v) => {
|
|
1661
|
-
if (typeof v === "string") return fill(v, false);
|
|
1662
|
-
if (Array.isArray(v)) return v.map(walk);
|
|
1663
|
-
if (v && typeof v === "object") return Object.fromEntries(Object.entries(v).map(([k, x]) => [k, walk(x)]));
|
|
1664
|
-
return v;
|
|
1665
|
-
};
|
|
1666
|
-
const { svg, ...rest } = effect.motion;
|
|
1667
|
-
return { svg: fill(svg.replace(/<!--[\s\S]*?-->/g, ""), true), ...walk(rest) };
|
|
1668
|
-
}
|
|
1669
|
-
function colorControl(name, hex, label) {
|
|
1670
|
-
return { name, type: "vec3", color: true, default: hexToRgb(hex) ?? [1, 1, 1], label: label ?? name };
|
|
1671
|
-
}
|
|
1672
|
-
function numberControl(name, value, min, max, label, step) {
|
|
1673
|
-
return { name, type: "float", default: value, min, max, label: label ?? name, step };
|
|
1674
|
-
}
|
|
1675
|
-
|
|
1676
|
-
// src/catalog/vector.ts
|
|
1677
|
-
var spinner = {
|
|
1678
|
-
kind: "vector",
|
|
1679
|
-
id: "spinner",
|
|
1680
|
-
name: "Spinner",
|
|
1681
|
-
description: "An arc chasing its tail: the classic indeterminate loader.",
|
|
1682
|
-
category: "loader",
|
|
1683
|
-
tags: ["loader", "ui", "minimal"],
|
|
1684
|
-
hold: 0,
|
|
1685
|
-
motion: {
|
|
1686
|
-
svg: `<svg viewBox="0 0 100 100"><!-- {{accent: color default=#6366F1 label='Arc'}} {{track: color default=#E5E7EB label='Track'}} {{width: float min=2 max=20 default=8 step=1 label='Stroke width'}} --><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>`,
|
|
1687
|
-
animate: [
|
|
1688
|
-
{ id: "spin", attrs: { transform: ["rotate(0 50 50)", "rotate(720 50 50)"] }, ms: 1600, ease: "linear" },
|
|
1689
|
-
{ id: "arc", attrs: { "stroke-dasharray": ["0.08 1", "0.6 1"] }, ms: 800, ease: "in-out" },
|
|
1690
|
-
{ id: "arc", attrs: { "stroke-dasharray": ["0.6 1", "0.08 1"] }, delay: 800, ms: 800, ease: "in-out" }
|
|
1691
|
-
]
|
|
1692
|
-
}
|
|
1693
|
-
};
|
|
1694
|
-
var bouncingDots = {
|
|
1695
|
-
kind: "vector",
|
|
1696
|
-
id: "bouncing-dots",
|
|
1697
|
-
name: "Bouncing dots",
|
|
1698
|
-
description: "Three dots hopping in turn, for typing indicators and loaders.",
|
|
1699
|
-
category: "loader",
|
|
1700
|
-
tags: ["loader", "chat", "typing", "ui"],
|
|
1701
|
-
hold: 0,
|
|
1702
|
-
motion: {
|
|
1703
|
-
svg: `<svg viewBox="0 0 120 60"><!-- {{accent: color default=#0EA5E9 label='Dots'}} {{height: float min=4 max=30 default=18 step=1 label='Bounce height'}} --><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>`,
|
|
1704
|
-
animate: ["a", "b", "c"].flatMap((id, i) => [
|
|
1705
|
-
{ id, attrs: { transform: ["translate(0 0)", "translate(0 -{{height}})"] }, delay: i * 150, ms: 250, ease: "out" },
|
|
1706
|
-
{ id, attrs: { transform: ["translate(0 -{{height}})", "translate(0 0)"] }, delay: i * 150 + 250, ms: 250, ease: "in-out" },
|
|
1707
|
-
{ id, attrs: { opacity: [0.5, 1] }, delay: i * 150, ms: 250 },
|
|
1708
|
-
{ id, attrs: { opacity: [1, 0.5] }, delay: i * 150 + 250, ms: 250 }
|
|
1709
|
-
])
|
|
1710
|
-
}
|
|
1711
|
-
};
|
|
1712
|
-
var progressRing = {
|
|
1713
|
-
kind: "vector",
|
|
1714
|
-
id: "progress-ring",
|
|
1715
|
-
name: "Progress ring",
|
|
1716
|
-
description: "A ring filling up to a percentage while the number counts with it.",
|
|
1717
|
-
category: "data",
|
|
1718
|
-
tags: ["progress", "kpi", "dashboard", "loader"],
|
|
1719
|
-
derived: (v) => ({ fraction: Math.min(1, Math.max(0, Number(v.value) / 100)) }),
|
|
1720
|
-
motion: {
|
|
1721
|
-
svg: `<svg viewBox="0 0 120 120" font-family="system-ui, sans-serif"><!-- {{accent: color default=#22C55E label='Ring'}} {{track: color default=#E5E7EB label='Track'}} {{text: color default=#111827 label='Text'}} {{value: float min=0 max=100 default=72 step=1 label='Percent'}} --><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>`,
|
|
1722
|
-
animate: [
|
|
1723
|
-
{ id: "ring", draw: [0, "{{fraction}}"], ms: 1600, ease: "out" },
|
|
1724
|
-
{ id: "label", count: [0, "{{value}}"], format: "{n}%", ms: 1600, ease: "out" }
|
|
1725
|
-
]
|
|
1726
|
-
}
|
|
1727
|
-
};
|
|
1728
|
-
var pulsePing = {
|
|
1729
|
-
kind: "vector",
|
|
1730
|
-
id: "pulse-ping",
|
|
1731
|
-
name: "Pulse ping",
|
|
1732
|
-
description: "Rings radiating from a dot, like a live indicator or radar ping.",
|
|
1733
|
-
category: "loader",
|
|
1734
|
-
tags: ["live", "notification", "location", "status"],
|
|
1735
|
-
hold: 0,
|
|
1736
|
-
motion: {
|
|
1737
|
-
svg: `<svg viewBox="0 0 100 100"><!-- {{accent: color default=#EF4444 label='Colour'}} --><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>`,
|
|
1738
|
-
animate: [
|
|
1739
|
-
{ id: "r1", attrs: { r: [8, 44], opacity: [0.9, 0], "stroke-width": [4, 1] }, ms: 1400, ease: "out" },
|
|
1740
|
-
{ id: "r2", attrs: { r: [8, 44], opacity: [0.9, 0], "stroke-width": [4, 1] }, delay: 700, ms: 1400, ease: "out" }
|
|
1741
|
-
]
|
|
1742
|
-
}
|
|
1743
|
-
};
|
|
1744
|
-
var successCheck = {
|
|
1745
|
-
kind: "vector",
|
|
1746
|
-
id: "success-check",
|
|
1747
|
-
name: "Success check",
|
|
1748
|
-
description: "A circle drawing itself closed, then a tick, then a little pop.",
|
|
1749
|
-
category: "icon",
|
|
1750
|
-
tags: ["success", "confirmation", "ui", "feedback"],
|
|
1751
|
-
hold: 1200,
|
|
1752
|
-
motion: {
|
|
1753
|
-
svg: `<svg viewBox="0 0 100 100"><!-- {{accent: color default=#16A34A label='Colour'}} {{width: float min=2 max=12 default=6 step=1 label='Stroke width'}} --><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>`,
|
|
1754
|
-
animate: [
|
|
1755
|
-
{ id: "circle", draw: true, ms: 600, ease: "in-out" },
|
|
1756
|
-
{ id: "tick", draw: true, delay: 500, ms: 400, ease: "out" },
|
|
1757
|
-
{ 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" },
|
|
1758
|
-
{ id: "badge", attrs: { transform: ["translate(50 50) scale(1.12) translate(-50 -50)", "translate(50 50) scale(1) translate(-50 -50)"] }, delay: 1e3, ms: 250, ease: "in-out" }
|
|
1759
|
-
]
|
|
1760
|
-
}
|
|
1761
|
-
};
|
|
1762
|
-
var errorCross = {
|
|
1763
|
-
kind: "vector",
|
|
1764
|
-
id: "error-cross",
|
|
1765
|
-
name: "Error cross",
|
|
1766
|
-
description: "A circle and a cross drawing on, with a short shake.",
|
|
1767
|
-
category: "icon",
|
|
1768
|
-
tags: ["error", "failure", "ui", "feedback"],
|
|
1769
|
-
hold: 1200,
|
|
1770
|
-
motion: {
|
|
1771
|
-
svg: `<svg viewBox="0 0 100 100"><!-- {{accent: color default=#DC2626 label='Colour'}} {{width: float min=2 max=12 default=6 step=1 label='Stroke width'}} --><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>`,
|
|
1772
|
-
animate: [
|
|
1773
|
-
{ id: "circle", draw: true, ms: 600, ease: "in-out" },
|
|
1774
|
-
{ id: "a", draw: true, delay: 500, ms: 250, ease: "out" },
|
|
1775
|
-
{ id: "b", draw: true, delay: 700, ms: 250, ease: "out" },
|
|
1776
|
-
{ id: "badge", attrs: { transform: ["translate(0 0)", "translate(-6 0)"] }, delay: 950, ms: 60, ease: "linear" },
|
|
1777
|
-
{ id: "badge", attrs: { transform: ["translate(-6 0)", "translate(6 0)"] }, delay: 1010, ms: 100, ease: "linear" },
|
|
1778
|
-
{ id: "badge", attrs: { transform: ["translate(6 0)", "translate(0 0)"] }, delay: 1110, ms: 80, ease: "out" }
|
|
1779
|
-
]
|
|
1780
|
-
}
|
|
1781
|
-
};
|
|
1782
|
-
var heartbeat = {
|
|
1783
|
-
kind: "vector",
|
|
1784
|
-
id: "heartbeat",
|
|
1785
|
-
name: "Heartbeat",
|
|
1786
|
-
description: "A heart beating twice, with an ECG line tracing beneath it.",
|
|
1787
|
-
category: "icon",
|
|
1788
|
-
tags: ["health", "like", "love", "pulse"],
|
|
1789
|
-
hold: 300,
|
|
1790
|
-
motion: {
|
|
1791
|
-
svg: `<svg viewBox="0 0 200 120"><!-- {{accent: color default=#F43F5E label='Heart'}} {{line: color default=#94A3B8 label='Line'}} --><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>`,
|
|
1792
|
-
animate: [
|
|
1793
|
-
{ id: "heart", attrs: { transform: ["translate(100 46) scale(1)", "translate(100 46) scale(1.18)"] }, ms: 120, ease: "out" },
|
|
1794
|
-
{ id: "heart", attrs: { transform: ["translate(100 46) scale(1.18)", "translate(100 46) scale(1)"] }, delay: 120, ms: 160, ease: "in-out" },
|
|
1795
|
-
{ id: "heart", attrs: { transform: ["translate(100 46) scale(1)", "translate(100 46) scale(1.12)"] }, delay: 300, ms: 120, ease: "out" },
|
|
1796
|
-
{ id: "heart", attrs: { transform: ["translate(100 46) scale(1.12)", "translate(100 46) scale(1)"] }, delay: 420, ms: 300, ease: "in-out" },
|
|
1797
|
-
{ id: "ecg", draw: true, ms: 900, ease: "linear" }
|
|
1798
|
-
]
|
|
1799
|
-
}
|
|
1800
|
-
};
|
|
1801
|
-
var barChart = {
|
|
1802
|
-
kind: "vector",
|
|
1803
|
-
id: "bar-chart",
|
|
1804
|
-
name: "Bar chart",
|
|
1805
|
-
description: "Bars growing into place one after another, values counting above them.",
|
|
1806
|
-
category: "data",
|
|
1807
|
-
tags: ["chart", "dashboard", "report", "explainer"],
|
|
1808
|
-
hold: 1500,
|
|
1809
|
-
derived: (v) => Object.fromEntries(
|
|
1810
|
-
["a", "b", "c", "d"].flatMap((k) => {
|
|
1811
|
-
const h = Math.min(100, Math.max(0, Number(v[k]))) * 1.1;
|
|
1812
|
-
return [
|
|
1813
|
-
[`${k}_height`, +h.toFixed(2)],
|
|
1814
|
-
[`${k}_label_y`, +(134 - h).toFixed(2)]
|
|
1815
|
-
];
|
|
1816
|
-
})
|
|
1817
|
-
),
|
|
1818
|
-
motion: {
|
|
1819
|
-
svg: `<svg viewBox="0 0 240 160" font-family="system-ui, sans-serif"><!-- {{accent: color default=#6366F1 label='Bars'}} {{text: color default=#334155 label='Text'}} {{a: float min=0 max=100 default=40 step=1 label='Bar 1'}} {{b: float min=0 max=100 default=65 step=1 label='Bar 2'}} {{c: float min=0 max=100 default=50 step=1 label='Bar 3'}} {{d: float min=0 max=100 default=90 step=1 label='Bar 4'}} --><line x1="20" y1="140" x2="220" y2="140" stroke="{{text}}" stroke-opacity="0.3" stroke-width="2"/>${["a", "b", "c", "d"].map(
|
|
1820
|
-
(k, i) => `<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>`
|
|
1821
|
-
).join("")}</svg>`,
|
|
1822
|
-
animate: ["a", "b", "c", "d"].flatMap((k, i) => [
|
|
1823
|
-
{ 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" },
|
|
1824
|
-
{ id: `label-${k}`, attrs: { y: [132, `{{${k}_label_y}}`], opacity: [0, 1] }, delay: i * 180, ms: 700, ease: "out" },
|
|
1825
|
-
{ id: `label-${k}`, count: [0, `{{${k}}}`], delay: i * 180, ms: 700, ease: "out" }
|
|
1826
|
-
])
|
|
1827
|
-
}
|
|
1828
|
-
};
|
|
1829
|
-
var lineChart = {
|
|
1830
|
-
kind: "vector",
|
|
1831
|
-
id: "line-chart",
|
|
1832
|
-
name: "Line chart",
|
|
1833
|
-
description: "A trend line drawing across with a marker riding its tip and the area fading in below.",
|
|
1834
|
-
category: "data",
|
|
1835
|
-
tags: ["chart", "trend", "growth", "dashboard"],
|
|
1836
|
-
hold: 1500,
|
|
1837
|
-
motion: {
|
|
1838
|
-
svg: `<svg viewBox="0 0 240 140"><!-- {{accent: color default=#0EA5E9 label='Line'}} {{grid: color default=#CBD5E1 label='Grid'}} --><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>`,
|
|
1839
|
-
animate: [
|
|
1840
|
-
{ id: "trend", draw: true, ms: 1400, ease: "in-out" },
|
|
1841
|
-
{ id: "marker", along: "trend", ms: 1400, ease: "in-out" },
|
|
1842
|
-
{ id: "area", attrs: { opacity: [0, 1] }, delay: 900, ms: 600 }
|
|
1843
|
-
]
|
|
1844
|
-
}
|
|
1845
|
-
};
|
|
1846
|
-
var statCounter = {
|
|
1847
|
-
kind: "vector",
|
|
1848
|
-
id: "stat-counter",
|
|
1849
|
-
name: "Stat counter",
|
|
1850
|
-
description: "A headline number counting up, with its caption and underline sweeping in.",
|
|
1851
|
-
category: "data",
|
|
1852
|
-
tags: ["kpi", "number", "headline", "explainer"],
|
|
1853
|
-
hold: 1500,
|
|
1854
|
-
motion: {
|
|
1855
|
-
svg: `<svg viewBox="0 0 240 120" font-family="system-ui, sans-serif"><!-- {{accent: color default=#F59E0B label='Accent'}} {{text: color default=#0F172A label='Text'}} {{value: float min=0 max=100000 default=1250 step=1 label='Value'}} {{caption: text default='happy customers' label='Caption'}} --><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">{{caption}}</text></svg>`,
|
|
1856
|
-
animate: [
|
|
1857
|
-
{ id: "number", count: [0, "{{value}}"], ms: 1500, ease: "out" },
|
|
1858
|
-
{ id: "underline", draw: true, delay: 300, ms: 900, ease: "in-out" },
|
|
1859
|
-
{ id: "caption", attrs: { opacity: [0, 1], y: [112, 104] }, delay: 800, ms: 500, ease: "out" }
|
|
1860
|
-
]
|
|
1861
|
-
}
|
|
1862
|
-
};
|
|
1863
|
-
var timelineTrack = {
|
|
1864
|
-
kind: "vector",
|
|
1865
|
-
id: "timeline-track",
|
|
1866
|
-
name: "Timeline track",
|
|
1867
|
-
description: "A vertical line drawing down with nodes popping along milestones.",
|
|
1868
|
-
category: "data",
|
|
1869
|
-
tags: ["timeline", "roadmap", "milestones", "history"],
|
|
1870
|
-
hold: 1200,
|
|
1871
|
-
motion: {
|
|
1872
|
-
svg: `<svg viewBox="0 0 100 400"><!-- {{track: color default=#E5E7EB label='Track'}} {{accent: color default=#F97316 label='Accent'}} {{width: float min=1 max=8 default=3 step=0.5 label='Line width'}} --><line x1="50" y1="20" x2="50" y2="380" stroke="{{track}}" stroke-width="{{width}}" stroke-linecap="round"/><path id="line" d="M50 20 L50 380" fill="none" stroke="{{accent}}" stroke-width="{{width}}" stroke-linecap="round"/><g id="n1" transform="translate(50 60) scale(0)"><circle r="8" fill="{{accent}}"/><circle r="4" fill="#ffffff"/></g><g id="n2" transform="translate(50 160) scale(0)"><circle r="8" fill="{{accent}}"/><circle r="4" fill="#ffffff"/></g><g id="n3" transform="translate(50 260) scale(0)"><circle r="8" fill="{{accent}}"/><circle r="4" fill="#ffffff"/></g><g id="n4" transform="translate(50 360) scale(0)"><circle r="8" fill="{{accent}}"/><circle r="4" fill="#ffffff"/></g></svg>`,
|
|
1873
|
-
animate: [
|
|
1874
|
-
{ id: "line", draw: true, ms: 1400, ease: "in-out" },
|
|
1875
|
-
{ id: "n1", attrs: { transform: ["translate(50 60) scale(0)", "translate(50 60) scale(1)"] }, delay: 200, ms: 250, ease: "out" },
|
|
1876
|
-
{ id: "n2", attrs: { transform: ["translate(50 160) scale(0)", "translate(50 160) scale(1)"] }, delay: 550, ms: 250, ease: "out" },
|
|
1877
|
-
{ id: "n3", attrs: { transform: ["translate(50 260) scale(0)", "translate(50 260) scale(1)"] }, delay: 900, ms: 250, ease: "out" },
|
|
1878
|
-
{ id: "n4", attrs: { transform: ["translate(50 360) scale(0)", "translate(50 360) scale(1)"] }, delay: 1300, ms: 250, ease: "out" }
|
|
1879
|
-
]
|
|
1880
|
-
}
|
|
1881
|
-
};
|
|
1882
|
-
var route = {
|
|
1883
|
-
kind: "vector",
|
|
1884
|
-
id: "route",
|
|
1885
|
-
name: "Route",
|
|
1886
|
-
description: "A dashed journey drawing between two pins, with a traveller following it.",
|
|
1887
|
-
category: "data",
|
|
1888
|
-
tags: ["map", "travel", "delivery", "journey"],
|
|
1889
|
-
hold: 1200,
|
|
1890
|
-
motion: {
|
|
1891
|
-
svg: `<svg viewBox="0 0 240 150"><!-- {{accent: color default=#8B5CF6 label='Route'}} {{pin: color default=#0F172A label='Pins'}} --><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>`,
|
|
1892
|
-
animate: [
|
|
1893
|
-
{ id: "path", draw: true, ms: 1800, ease: "in-out" },
|
|
1894
|
-
{ id: "traveller", along: "path", ms: 1800, ease: "in-out" },
|
|
1895
|
-
{ id: "end", attrs: { opacity: [0, 1] }, delay: 1600, ms: 300 }
|
|
1896
|
-
]
|
|
1897
|
-
}
|
|
1898
|
-
};
|
|
1899
|
-
var blobMorph = {
|
|
1900
|
-
kind: "vector",
|
|
1901
|
-
id: "blob-morph",
|
|
1902
|
-
name: "Blob morph",
|
|
1903
|
-
description: "An organic blob shape-shifting through three forms.",
|
|
1904
|
-
category: "organic",
|
|
1905
|
-
tags: ["shape", "background", "playful", "brand"],
|
|
1906
|
-
hold: 0,
|
|
1907
|
-
motion: (() => {
|
|
1908
|
-
const shapes = [
|
|
1909
|
-
"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",
|
|
1910
|
-
"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",
|
|
1911
|
-
"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"
|
|
1912
|
-
];
|
|
1913
|
-
const step = 1200;
|
|
1914
|
-
return {
|
|
1915
|
-
svg: `<svg viewBox="0 0 200 200"><!-- {{colorA: color default=#F472B6 label='Colour A'}} {{colorB: color default=#818CF8 label='Colour B'}} --><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>`,
|
|
1916
|
-
animate: shapes.map((d, i) => ({
|
|
1917
|
-
id: "blob",
|
|
1918
|
-
attrs: { d: [d, shapes[(i + 1) % shapes.length]] },
|
|
1919
|
-
delay: i * step,
|
|
1920
|
-
ms: step,
|
|
1921
|
-
ease: "in-out"
|
|
1922
|
-
}))
|
|
1923
|
-
};
|
|
1924
|
-
})()
|
|
1925
|
-
};
|
|
1926
|
-
var signature = {
|
|
1927
|
-
kind: "vector",
|
|
1928
|
-
id: "signature",
|
|
1929
|
-
name: "Signature",
|
|
1930
|
-
description: "A handwritten flourish drawing itself as if by pen.",
|
|
1931
|
-
category: "icon",
|
|
1932
|
-
tags: ["handwriting", "draw-on", "personal", "reveal"],
|
|
1933
|
-
hold: 1500,
|
|
1934
|
-
motion: {
|
|
1935
|
-
svg: `<svg viewBox="0 0 260 120"><!-- {{ink: color default=#1E293B label='Ink'}} {{width: float min=1 max=8 default=3 step=0.5 label='Pen width'}} --><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>`,
|
|
1936
|
-
animate: [
|
|
1937
|
-
{ id: "sig", draw: true, ms: 1800, ease: "in-out" },
|
|
1938
|
-
{ id: "dash", draw: true, delay: 1700, ms: 400, ease: "out" }
|
|
1939
|
-
]
|
|
1940
|
-
}
|
|
1941
|
-
};
|
|
1942
|
-
var equalizer = {
|
|
1943
|
-
kind: "vector",
|
|
1944
|
-
id: "equalizer",
|
|
1945
|
-
name: "Equalizer",
|
|
1946
|
-
description: "Audio level bars dancing, for now-playing badges and voice states.",
|
|
1947
|
-
category: "loader",
|
|
1948
|
-
tags: ["music", "audio", "voice", "now-playing"],
|
|
1949
|
-
hold: 0,
|
|
1950
|
-
motion: (() => {
|
|
1951
|
-
const heights = [
|
|
1952
|
-
[0.3, 0.9, 0.5, 0.3],
|
|
1953
|
-
[0.7, 0.4, 1, 0.7],
|
|
1954
|
-
[0.5, 1, 0.3, 0.5],
|
|
1955
|
-
[0.9, 0.5, 0.7, 0.9],
|
|
1956
|
-
[0.4, 0.8, 0.6, 0.4]
|
|
1957
|
-
];
|
|
1958
|
-
const beat = 250;
|
|
1959
|
-
return {
|
|
1960
|
-
svg: `<svg viewBox="0 0 100 100"><!-- {{accent: color default=#10B981 label='Bars'}} -->${heights.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>`).join("")}</svg>`,
|
|
1961
|
-
animate: heights.flatMap(
|
|
1962
|
-
(h, i) => [0, 1, 2].map((k) => ({
|
|
1963
|
-
id: `bar${i}`,
|
|
1964
|
-
attrs: { transform: [`translate(${14 + i * 16} 85) scale(1 ${h[k]})`, `translate(${14 + i * 16} 85) scale(1 ${h[k + 1]})`] },
|
|
1965
|
-
delay: k * beat,
|
|
1966
|
-
ms: beat,
|
|
1967
|
-
ease: "in-out"
|
|
1968
|
-
}))
|
|
1969
|
-
)
|
|
1970
|
-
};
|
|
1971
|
-
})()
|
|
1972
|
-
};
|
|
1973
|
-
var confetti = {
|
|
1974
|
-
kind: "vector",
|
|
1975
|
-
id: "confetti",
|
|
1976
|
-
name: "Confetti burst",
|
|
1977
|
-
description: "A burst of confetti flying out and fluttering down.",
|
|
1978
|
-
category: "icon",
|
|
1979
|
-
tags: ["celebration", "success", "party", "reward"],
|
|
1980
|
-
hold: 400,
|
|
1981
|
-
motion: (() => {
|
|
1982
|
-
const pieces = Array.from({ length: 18 }, (_, i) => {
|
|
1983
|
-
const angle = i / 18 * Math.PI * 2 + i % 3 * 0.2;
|
|
1984
|
-
const dist = 55 + i % 4 * 12;
|
|
1985
|
-
const x = Math.round(Math.cos(angle) * dist);
|
|
1986
|
-
const y = Math.round(Math.sin(angle) * dist * 0.8 - 20);
|
|
1987
|
-
const spin = (i % 2 ? 1 : -1) * (180 + i % 5 * 60);
|
|
1988
|
-
return { i, x, y, spin, fill: ["{{a}}", "{{b}}", "{{c}}"][i % 3] };
|
|
1989
|
-
});
|
|
1990
|
-
return {
|
|
1991
|
-
svg: `<svg viewBox="0 0 200 200"><!-- {{a: color default=#F43F5E label='Colour A'}} {{b: color default=#FACC15 label='Colour B'}} {{c: color default=#22D3EE label='Colour C'}} -->${pieces.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"/>`).join("")}</svg>`,
|
|
1992
|
-
animate: pieces.flatMap((p) => [
|
|
1993
|
-
{ id: `p${p.i}`, attrs: { opacity: [0, 1] }, ms: 50 },
|
|
1994
|
-
{
|
|
1995
|
-
id: `p${p.i}`,
|
|
1996
|
-
attrs: { transform: ["translate(100 110) rotate(0)", `translate(${100 + p.x} ${110 + p.y}) rotate(${p.spin})`] },
|
|
1997
|
-
ms: 600,
|
|
1998
|
-
ease: "out"
|
|
1999
|
-
},
|
|
2000
|
-
{
|
|
2001
|
-
id: `p${p.i}`,
|
|
2002
|
-
attrs: {
|
|
2003
|
-
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})`]
|
|
2004
|
-
},
|
|
2005
|
-
delay: 600,
|
|
2006
|
-
ms: 1e3,
|
|
2007
|
-
ease: "in-out"
|
|
2008
|
-
},
|
|
2009
|
-
{ id: `p${p.i}`, attrs: { opacity: [1, 0] }, delay: 1300, ms: 300 }
|
|
2010
|
-
])
|
|
2011
|
-
};
|
|
2012
|
-
})()
|
|
2013
|
-
};
|
|
2014
|
-
var NODES = {
|
|
2015
|
-
s: [40, 120],
|
|
2016
|
-
a: [120, 60],
|
|
2017
|
-
b: [120, 180],
|
|
2018
|
-
c: [280, 50],
|
|
2019
|
-
d: [290, 130],
|
|
2020
|
-
e: [280, 200],
|
|
2021
|
-
t: [360, 120]
|
|
2022
|
-
};
|
|
2023
|
-
var HUB = [200, 120];
|
|
2024
|
-
var link = ([x1, y1], [x2, y2]) => {
|
|
2025
|
-
const mid = (x1 + x2) / 2;
|
|
2026
|
-
return `M${x1} ${y1} C${mid} ${y1} ${mid} ${y2} ${x2} ${y2}`;
|
|
2027
|
-
};
|
|
2028
|
-
var LINKS = [
|
|
2029
|
-
{ from: NODES.s, to: NODES.a, column: 0 },
|
|
2030
|
-
{ from: NODES.s, to: NODES.b, column: 0 },
|
|
2031
|
-
{ from: NODES.a, to: HUB, column: 1 },
|
|
2032
|
-
{ from: NODES.b, to: HUB, column: 1 },
|
|
2033
|
-
{ from: HUB, to: NODES.c, column: 2 },
|
|
2034
|
-
{ from: HUB, to: NODES.d, column: 2 },
|
|
2035
|
-
{ from: HUB, to: NODES.e, column: 2 },
|
|
2036
|
-
{ from: NODES.c, to: NODES.t, column: 3 },
|
|
2037
|
-
{ from: NODES.d, to: NODES.t, column: 3 },
|
|
2038
|
-
{ from: NODES.e, to: NODES.t, column: 3 }
|
|
2039
|
-
];
|
|
2040
|
-
var CRITICAL = [NODES.s, NODES.b, HUB, NODES.c, NODES.t].map((p, i, all) => i === 0 ? "" : link(all[i - 1], p).replace(/^M[\d.]+ [\d.]+ /, i === 1 ? `M${all[0][0]} ${all[0][1]} ` : "")).filter(Boolean).join(" ");
|
|
2041
|
-
var NODE_COLUMN = { s: 0, a: 1, b: 1, c: 3, d: 3, e: 3, t: 4 };
|
|
2042
|
-
var COLUMN_MS = 280;
|
|
2043
|
-
var PATH_START = 1500;
|
|
2044
|
-
var PATH_MS = 1800;
|
|
2045
|
-
var flowNetwork = {
|
|
2046
|
-
kind: "vector",
|
|
2047
|
-
id: "flow-network",
|
|
2048
|
-
name: "Flow network",
|
|
2049
|
-
description: "A node graph linking up, then a glowing critical path racing through it to a pulsing hub.",
|
|
2050
|
-
category: "data",
|
|
2051
|
-
tags: ["network", "graph", "dag", "workflow", "tech", "explainer"],
|
|
2052
|
-
hold: 1200,
|
|
2053
|
-
presets: [
|
|
2054
|
-
{ name: "Light", values: { background: [0.93, 0.95, 0.96], link: [0.37, 0.72, 0.69], path: [0.96, 0.62, 0.04], ink: [0.2, 0.25, 0.33] } },
|
|
2055
|
-
{ name: "Neon", values: { background: [0.04, 0.02, 0.1], link: [0.13, 0.83, 0.93], path: [0.96, 0.45, 0.71], ink: [0.85, 0.85, 1] } }
|
|
2056
|
-
],
|
|
2057
|
-
motion: {
|
|
2058
|
-
svg: `<svg viewBox="0 0 400 240" font-family="system-ui, sans-serif"><!-- {{label: text default='Critical path' label='Hub label'}} {{background: color default=#0B1220 label='Background'}} {{link: color default=#4FD1C5 label='Links'}} {{path: color default=#F5A524 label='Critical path'}} {{ink: color default=#CBD5E1 label='Label'}} {{glow: float min=0 max=6 default=2.5 step=0.5 label='Glow'}} --><defs><filter id="glow" x="-50%" y="-50%" width="200%" height="200%"><feGaussianBlur stdDeviation="{{glow}}" result="blur"/><feMerge><feMergeNode in="blur"/><feMergeNode in="SourceGraphic"/></feMerge></filter></defs><rect width="400" height="240" fill="{{background}}"/>${LINKS.map(
|
|
2059
|
-
(l, i) => `<path id="l${i}" d="${link(l.from, l.to)}" fill="none" stroke="{{link}}" stroke-width="1.6" stroke-opacity="0.75" filter="url(#glow)"/>`
|
|
2060
|
-
).join("")}<path id="crit" d="${CRITICAL}" fill="none" stroke="{{path}}" stroke-width="3" stroke-linecap="round" filter="url(#glow)"/>${[0, 1].map((i) => `<circle id="ring${i}" cx="${HUB[0]}" cy="${HUB[1]}" r="10" fill="none" stroke="{{link}}" stroke-width="2" opacity="0"/>`).join("")}${Object.entries(NODES).map(([id, [x, y]]) => `<circle id="n${id}" cx="${x}" cy="${y}" r="6" fill="{{background}}" stroke="{{link}}" stroke-width="2.5" filter="url(#glow)" opacity="0"/>`).join("")}<g id="hub" opacity="0"><circle cx="${HUB[0]}" cy="${HUB[1]}" r="13" fill="{{link}}" fill-opacity="0.25" stroke="{{link}}" stroke-width="2" filter="url(#glow)"/><circle cx="${HUB[0]}" cy="${HUB[1]}" r="6" fill="{{link}}"/></g><text id="caption" x="${HUB[0]}" y="${HUB[1] + 34}" text-anchor="middle" font-size="12" font-weight="600" fill="{{ink}}" opacity="0">{{label}}</text>${[0, 1, 2].map((i) => `<circle id="pkt${i}" r="2.5" fill="{{link}}" filter="url(#glow)" opacity="0"/>`).join("")}<circle id="comet" r="5" fill="#FFFFFF" stroke="{{path}}" stroke-width="2" filter="url(#glow)" opacity="0"/></svg>`,
|
|
2061
|
-
animate: [
|
|
2062
|
-
...LINKS.map((l, i) => ({ id: `l${i}`, draw: true, delay: l.column * COLUMN_MS, ms: 500, ease: "out" })),
|
|
2063
|
-
...Object.keys(NODES).map((id) => ({ id: `n${id}`, attrs: { opacity: [0, 1] }, delay: Math.max(0, NODE_COLUMN[id] * COLUMN_MS - 120), ms: 250 })),
|
|
2064
|
-
{ id: "hub", attrs: { opacity: [0, 1] }, delay: 2 * COLUMN_MS - 120, ms: 300 },
|
|
2065
|
-
{ id: "caption", attrs: { opacity: [0, 1], y: [HUB[1] + 40, HUB[1] + 34] }, delay: 2 * COLUMN_MS, ms: 400, ease: "out" },
|
|
2066
|
-
// Data packets drift along a few links while the path waits.
|
|
2067
|
-
...[3, 5, 8].map((link2, i) => [
|
|
2068
|
-
{ id: `pkt${i}`, attrs: { opacity: [0, 1] }, delay: 1e3 + i * 150, ms: 150 },
|
|
2069
|
-
{ id: `pkt${i}`, along: `l${link2}`, delay: 1e3 + i * 150, ms: 1100, ease: "in-out" },
|
|
2070
|
-
{ id: `pkt${i}`, attrs: { opacity: [1, 0] }, delay: 2e3 + i * 150, ms: 200 }
|
|
2071
|
-
]).flat(),
|
|
2072
|
-
{ id: "crit", draw: true, delay: PATH_START, ms: PATH_MS, ease: "in-out" },
|
|
2073
|
-
{ id: "comet", attrs: { opacity: [0, 1] }, delay: PATH_START, ms: 150 },
|
|
2074
|
-
{ id: "comet", along: "crit", delay: PATH_START, ms: PATH_MS, ease: "in-out" },
|
|
2075
|
-
{ id: "comet", attrs: { opacity: [1, 0] }, delay: PATH_START + PATH_MS, ms: 300 },
|
|
2076
|
-
// Rings pulse out from the hub as the comet passes it, halfway along.
|
|
2077
|
-
...[0, 1].map((i) => ({
|
|
2078
|
-
id: `ring${i}`,
|
|
2079
|
-
attrs: { r: [10, 52], opacity: [0.9, 0], "stroke-width": [3, 0.5] },
|
|
2080
|
-
delay: PATH_START + PATH_MS / 2 + i * 350,
|
|
2081
|
-
ms: 1200,
|
|
2082
|
-
ease: "out"
|
|
2083
|
-
}))
|
|
2084
|
-
]
|
|
2085
|
-
}
|
|
2086
|
-
};
|
|
2087
|
-
var DRIP_TOP = 106;
|
|
2088
|
-
var DRIPS = [
|
|
2089
|
-
{ at: 0.067, width: 11, length: 34, delay: 0, ms: 1500 },
|
|
2090
|
-
{ at: 0.18, width: 8, length: 18, delay: 260, ms: 1100 },
|
|
2091
|
-
{ at: 0.3, width: 13, length: 50, delay: 120, ms: 1800 },
|
|
2092
|
-
{ at: 0.453, width: 9, length: 22, delay: 380, ms: 1200 },
|
|
2093
|
-
{ at: 0.587, width: 10, length: 28, delay: 200, ms: 1400 },
|
|
2094
|
-
{ at: 0.693, width: 12, length: 42, delay: 60, ms: 1700 },
|
|
2095
|
-
{ at: 0.793, width: 11, length: 32, delay: 300, ms: 1500 }
|
|
2096
|
-
];
|
|
2097
|
-
var dripText = {
|
|
2098
|
-
kind: "vector",
|
|
2099
|
-
id: "drip-text",
|
|
2100
|
-
name: "Drip text",
|
|
2101
|
-
description: "A chunky outlined title popping in, then oozing slime drips from its letters.",
|
|
2102
|
-
category: "icon",
|
|
2103
|
-
tags: ["title", "text", "drip", "slime", "y2k", "headline"],
|
|
2104
|
-
hold: 1200,
|
|
2105
|
-
presets: [
|
|
2106
|
-
{ name: "Slime", values: { fill: [0.55, 0.93, 0.25], outline: [0.06, 0.2, 0.04], shine: [0.85, 1, 0.7] } },
|
|
2107
|
-
{ name: "Molten gold", values: { fill: [1, 0.78, 0.2], outline: [0.35, 0.16, 0], shine: [1, 0.96, 0.75], drip: 1.5 } }
|
|
2108
|
-
],
|
|
2109
|
-
// The word is stretched to a width set by its length (textLength), so the drips land under its letters in any font.
|
|
2110
|
-
derived: (v) => {
|
|
2111
|
-
const letters = Math.max(1, Array.from(String(v.word).trim()).length);
|
|
2112
|
-
const width = Math.min(380, letters * 75);
|
|
2113
|
-
const left = 200 - width / 2;
|
|
2114
|
-
return {
|
|
2115
|
-
width,
|
|
2116
|
-
...Object.fromEntries(
|
|
2117
|
-
DRIPS.flatMap((d, i) => {
|
|
2118
|
-
const x = Math.round((left + d.at * width) * 10) / 10;
|
|
2119
|
-
const length = Math.round(d.length * Number(v.drip) * 10) / 10;
|
|
2120
|
-
return [
|
|
2121
|
-
[`x${i}`, x],
|
|
2122
|
-
[`left${i}`, x - d.width / 2],
|
|
2123
|
-
[`shine${i}`, x - d.width * 0.25],
|
|
2124
|
-
[`len${i}`, length],
|
|
2125
|
-
[`tip${i}`, DRIP_TOP + length]
|
|
2126
|
-
];
|
|
2127
|
-
})
|
|
2128
|
-
)
|
|
2129
|
-
};
|
|
2130
|
-
},
|
|
2131
|
-
motion: (() => {
|
|
2132
|
-
const word = `<text x="200" y="120" text-anchor="middle" textLength="{{width}}" lengthAdjust="spacingAndGlyphs" font-family="'Arial Black', Impact, system-ui, sans-serif" font-size="104" font-weight="900" stroke-linejoin="round"`;
|
|
2133
|
-
const drips = (layer, paint) => DRIPS.map(
|
|
2134
|
-
(d, i) => `<rect id="${layer}r${i}" x="{{left${i}}}" y="${DRIP_TOP}" width="${d.width}" height="0" rx="${d.width / 2}" ${paint}/><circle id="${layer}c${i}" cx="{{x${i}}}" cy="${DRIP_TOP}" r="${d.width * 0.72}" ${paint}/>`
|
|
2135
|
-
).join("");
|
|
2136
|
-
const outline = `fill="{{outline}}" stroke="{{outline}}" stroke-width="{{stroke}}"`;
|
|
2137
|
-
const pop = (from, to) => [`translate(200 100) scale(${from}) translate(-200 -100)`, `translate(200 100) scale(${to}) translate(-200 -100)`];
|
|
2138
|
-
return {
|
|
2139
|
-
svg: `<svg viewBox="0 0 400 200"><!-- {{word: text default=SLOP label='Word'}} {{fill: color default=#FF7EB6 label='Fill'}} {{outline: color default=#3A0A2C label='Outline'}} {{shine: color default=#FFD1E6 label='Shine'}} {{drip: float min=0.2 max=2 default=1 label='Drip length'}} {{stroke: float min=0 max=14 default=7 step=0.5 label='Outline width'}} --><g id="word" transform="${pop(0.6, 1)[0]}" opacity="0">${drips("o", outline)}${word} fill="none" stroke="{{outline}}" stroke-width="{{stroke}}">{{word}}</text>${drips("f", `fill="{{fill}}"`)}${word} fill="{{fill}}">{{word}}</text>${DRIPS.map(
|
|
2140
|
-
(d, i) => `<circle id="s${i}" cx="{{shine${i}}}" cy="${DRIP_TOP}" r="${d.width * 0.2}" fill="{{shine}}" opacity="0"/>`
|
|
2141
|
-
).join("")}</g></svg>`,
|
|
2142
|
-
animate: [
|
|
2143
|
-
{ id: "word", attrs: { transform: pop(0.6, 1.08), opacity: [0, 1] }, ms: 260, ease: "out" },
|
|
2144
|
-
{ id: "word", attrs: { transform: pop(1.08, 1) }, delay: 260, ms: 200, ease: "in-out" },
|
|
2145
|
-
...DRIPS.flatMap((d, i) => {
|
|
2146
|
-
const at = { delay: 400 + d.delay, ms: d.ms, ease: "in-out" };
|
|
2147
|
-
const tip = `{{tip${i}}}`;
|
|
2148
|
-
return [
|
|
2149
|
-
...["o", "f"].flatMap((layer) => [
|
|
2150
|
-
{ id: `${layer}r${i}`, attrs: { height: [0, `{{len${i}}}`] }, ...at },
|
|
2151
|
-
{ id: `${layer}c${i}`, attrs: { cy: [DRIP_TOP, tip] }, ...at }
|
|
2152
|
-
]),
|
|
2153
|
-
{ id: `s${i}`, attrs: { cy: [DRIP_TOP, tip], opacity: [0, 0.9] }, ...at }
|
|
2154
|
-
];
|
|
2155
|
-
})
|
|
2156
|
-
]
|
|
2157
|
-
};
|
|
2158
|
-
})()
|
|
2159
|
-
};
|
|
2160
|
-
var vectorEffects = [
|
|
2161
|
-
spinner,
|
|
2162
|
-
bouncingDots,
|
|
2163
|
-
pulsePing,
|
|
2164
|
-
equalizer,
|
|
2165
|
-
progressRing,
|
|
2166
|
-
barChart,
|
|
2167
|
-
lineChart,
|
|
2168
|
-
statCounter,
|
|
2169
|
-
route,
|
|
2170
|
-
timelineTrack,
|
|
2171
|
-
successCheck,
|
|
2172
|
-
errorCross,
|
|
2173
|
-
heartbeat,
|
|
2174
|
-
signature,
|
|
2175
|
-
confetti,
|
|
2176
|
-
blobMorph,
|
|
2177
|
-
dripText,
|
|
2178
|
-
flowNetwork
|
|
2179
|
-
];
|
|
2180
|
-
|
|
2181
|
-
// src/catalog/index.ts
|
|
2182
|
-
var SHADER_EFFECTS = [
|
|
2183
|
-
meshGradient,
|
|
2184
|
-
aurora,
|
|
2185
|
-
duotoneFlow,
|
|
2186
|
-
conicSweep,
|
|
2187
|
-
silk,
|
|
2188
|
-
marble,
|
|
2189
|
-
smoke,
|
|
2190
|
-
lavaLamp,
|
|
2191
|
-
plasma,
|
|
2192
|
-
caustics,
|
|
2193
|
-
flames,
|
|
2194
|
-
dotWave,
|
|
2195
|
-
truchet,
|
|
2196
|
-
hexPulse,
|
|
2197
|
-
voronoi,
|
|
2198
|
-
moire,
|
|
2199
|
-
kaleidoscope,
|
|
2200
|
-
barberStripes,
|
|
2201
|
-
neonCircuit,
|
|
2202
|
-
bokeh,
|
|
2203
|
-
godRays,
|
|
2204
|
-
sparkles,
|
|
2205
|
-
neonRipple,
|
|
2206
|
-
lightRibbons,
|
|
2207
|
-
starfield,
|
|
2208
|
-
nebula,
|
|
2209
|
-
blackHole,
|
|
2210
|
-
synthwaveGrid,
|
|
2211
|
-
digitalRain,
|
|
2212
|
-
pixelSunset,
|
|
2213
|
-
tvStatic,
|
|
2214
|
-
imageRipple,
|
|
2215
|
-
imageGlitch,
|
|
2216
|
-
imageDuotone,
|
|
2217
|
-
imageHalftone,
|
|
2218
|
-
imagePixelate,
|
|
2219
|
-
crt,
|
|
2220
|
-
filmGrain,
|
|
2221
|
-
...transitions
|
|
2222
|
-
];
|
|
2223
|
-
var EFFECTS = [...SHADER_EFFECTS, ...vectorEffects].sort(
|
|
2224
|
-
(a, b) => EFFECT_CATEGORIES.indexOf(a.category) - EFFECT_CATEGORIES.indexOf(b.category)
|
|
2225
|
-
);
|
|
2226
|
-
var byId = new Map(EFFECTS.map((e) => [e.id, e]));
|
|
2227
|
-
function getEffect(id) {
|
|
2228
|
-
return byId.get(id);
|
|
2229
|
-
}
|
|
2230
|
-
function findEffects(query = {}) {
|
|
2231
|
-
const words = (query.text ?? "").toLowerCase().split(/\s+/).filter(Boolean);
|
|
2232
|
-
return EFFECTS.filter((e) => {
|
|
2233
|
-
if (query.category && e.category !== query.category) return false;
|
|
2234
|
-
if (query.kind && e.kind !== query.kind) return false;
|
|
2235
|
-
if (query.tags && !query.tags.every((t) => e.tags.includes(t))) return false;
|
|
2236
|
-
const haystack = [e.id, e.name, e.description, e.category, ...e.tags].join(" ").toLowerCase();
|
|
2237
|
-
return words.every((w) => haystack.includes(w));
|
|
2238
|
-
});
|
|
2239
|
-
}
|
|
2240
|
-
function effectControls(effect) {
|
|
2241
|
-
return effect.kind === "shader" ? parseControls(effect.source) : vectorEffectControls(effect);
|
|
2242
|
-
}
|
|
2243
|
-
function effectDefaults(effect, options = {}) {
|
|
2244
|
-
const preset = options.preset ? effect.presets?.find((p) => p.name === options.preset)?.values : void 0;
|
|
2245
|
-
const merged = { ...preset, ...options.values };
|
|
2246
|
-
return Object.fromEntries(effectControls(effect).map((c) => [c.name, controlValue(c, merged)]));
|
|
2247
|
-
}
|
|
2248
|
-
|
|
2249
|
-
export {
|
|
2250
|
-
HASH,
|
|
2251
|
-
NOISE,
|
|
2252
|
-
FBM,
|
|
2253
|
-
PALETTE,
|
|
2254
|
-
OKLAB_MIX,
|
|
2255
|
-
ROTATE,
|
|
2256
|
-
glsl,
|
|
2257
|
-
svgDataUrl,
|
|
2258
|
-
SAMPLE_IMAGES,
|
|
2259
|
-
EFFECT_CATEGORIES,
|
|
2260
|
-
VECTOR_CONTROL_TYPES,
|
|
2261
|
-
vectorControls,
|
|
2262
|
-
vectorEffectControls,
|
|
2263
|
-
vectorControlIssues,
|
|
2264
|
-
vectorControlDef,
|
|
2265
|
-
renderVectorEffect,
|
|
2266
|
-
colorControl,
|
|
2267
|
-
numberControl,
|
|
2268
|
-
SHADER_EFFECTS,
|
|
2269
|
-
EFFECTS,
|
|
2270
|
-
getEffect,
|
|
2271
|
-
findEffects,
|
|
2272
|
-
effectControls,
|
|
2273
|
-
effectDefaults
|
|
2274
|
-
};
|
|
2275
|
-
//# sourceMappingURL=chunk-J2WIWT4J.js.map
|