@paper-design/shaders 0.0.39 → 0.0.40

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/README.md +3 -24
  2. package/dist/index.d.ts +2 -0
  3. package/dist/index.js +12 -0
  4. package/dist/index.js.map +2 -2
  5. package/dist/shader-mount.d.ts +2 -2
  6. package/dist/shader-mount.js +25 -10
  7. package/dist/shader-mount.js.map +2 -2
  8. package/dist/shader-utils.d.ts +1 -0
  9. package/dist/shader-utils.js +38 -0
  10. package/dist/shader-utils.js.map +2 -2
  11. package/dist/shaders/color-panels.d.ts +3 -0
  12. package/dist/shaders/color-panels.js +29 -16
  13. package/dist/shaders/color-panels.js.map +2 -2
  14. package/dist/shaders/dot-grid.js +3 -2
  15. package/dist/shaders/dot-grid.js.map +2 -2
  16. package/dist/shaders/dot-orbit.js +1 -2
  17. package/dist/shaders/dot-orbit.js.map +2 -2
  18. package/dist/shaders/grain-gradient.js +5 -5
  19. package/dist/shaders/grain-gradient.js.map +1 -1
  20. package/dist/shaders/liquid-metal.js +11 -11
  21. package/dist/shaders/liquid-metal.js.map +1 -1
  22. package/dist/shaders/mesh-gradient.js +1 -6
  23. package/dist/shaders/mesh-gradient.js.map +2 -2
  24. package/dist/shaders/neuro-noise.js +1 -2
  25. package/dist/shaders/neuro-noise.js.map +2 -2
  26. package/dist/shaders/perlin-noise.js +2 -2
  27. package/dist/shaders/perlin-noise.js.map +1 -1
  28. package/dist/shaders/pulsing-border.d.ts +8 -7
  29. package/dist/shaders/pulsing-border.js +119 -103
  30. package/dist/shaders/pulsing-border.js.map +2 -2
  31. package/dist/shaders/simplex-noise.js +13 -17
  32. package/dist/shaders/simplex-noise.js.map +2 -2
  33. package/dist/shaders/spiral.d.ts +3 -3
  34. package/dist/shaders/spiral.js +27 -30
  35. package/dist/shaders/spiral.js.map +2 -2
  36. package/dist/shaders/static-mesh-gradient.d.ts +43 -0
  37. package/dist/shaders/static-mesh-gradient.js +114 -0
  38. package/dist/shaders/static-mesh-gradient.js.map +7 -0
  39. package/dist/shaders/static-radial-gradient.d.ts +51 -0
  40. package/dist/shaders/static-radial-gradient.js +155 -0
  41. package/dist/shaders/static-radial-gradient.js.map +7 -0
  42. package/dist/shaders/swirl.d.ts +3 -3
  43. package/dist/shaders/swirl.js +18 -17
  44. package/dist/shaders/swirl.js.map +2 -2
  45. package/dist/shaders/voronoi.js +2 -2
  46. package/dist/shaders/voronoi.js.map +1 -1
  47. package/dist/shaders/warp.js +16 -11
  48. package/dist/shaders/warp.js.map +2 -2
  49. package/dist/shaders/waves.js +6 -10
  50. package/dist/shaders/waves.js.map +2 -2
  51. package/package.json +1 -1
@@ -3,7 +3,7 @@ import type { ShaderMotionParams } from '../shader-mount.js';
3
3
  import { type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js';
4
4
  export declare const pulsingBorderMeta: {
5
5
  readonly maxColorCount: 5;
6
- readonly maxSpotsPerColor: 5;
6
+ readonly maxSpots: 4;
7
7
  };
8
8
  /**
9
9
  * Color spots traveling around rectangular stroke (border)
@@ -12,13 +12,13 @@ export declare const pulsingBorderMeta: {
12
12
  * - u_colorBack (RGBA)
13
13
  * - u_colors (vec4[]), u_colorsCount (float used as integer)
14
14
  * - u_roundness, u_thickness, u_softness: border parameters
15
- * - u_intensity: global multiplier for spots shape & color
15
+ * - u_intensity: thickness of individual spots
16
+ * - u_bloom: normal / additive color blending
16
17
  * - u_spotSize: angular size of spots
17
- * - u_spotsPerColor (float used as int): number of spots rendered per color (not all visible at the same time)
18
+ * - u_spots (float used as int): number of spots rendered per color
18
19
  * - u_pulse: optional pulsing animation
19
20
  * - u_smoke, u_smokeSize: optional noisy shapes around the border
20
21
  *
21
- * - u_pulseTexture (sampler2D): pulsing signal source
22
22
  * - u_noiseTexture (sampler2D): pre-computed randomizer source
23
23
  *
24
24
  */
@@ -31,12 +31,12 @@ export interface PulsingBorderUniforms extends ShaderSizingUniforms {
31
31
  u_thickness: number;
32
32
  u_softness: number;
33
33
  u_intensity: number;
34
- u_spotsPerColor: number;
34
+ u_bloom: number;
35
+ u_spots: number;
35
36
  u_spotSize: number;
36
37
  u_pulse: number;
37
38
  u_smoke: number;
38
39
  u_smokeSize: number;
39
- u_pulseTexture?: HTMLImageElement;
40
40
  }
41
41
  export interface PulsingBorderParams extends ShaderSizingParams, ShaderMotionParams {
42
42
  colorBack?: string;
@@ -45,7 +45,8 @@ export interface PulsingBorderParams extends ShaderSizingParams, ShaderMotionPar
45
45
  thickness?: number;
46
46
  softness?: number;
47
47
  intensity?: number;
48
- spotsPerColor?: number;
48
+ bloom?: number;
49
+ spots?: number;
49
50
  spotSize?: number;
50
51
  pulse?: number;
51
52
  smoke?: number;
@@ -7,7 +7,7 @@ import { sizingVariablesDeclaration } from "../shader-sizing.js";
7
7
  import { declarePI, declareValueNoise, colorBandingFix } from "../shader-utils.js";
8
8
  const pulsingBorderMeta = {
9
9
  maxColorCount: 5,
10
- maxSpotsPerColor: 5
10
+ maxSpots: 4
11
11
  };
12
12
  const pulsingBorderFragmentShader = `#version 300 es
13
13
  precision lowp float;
@@ -21,13 +21,13 @@ uniform float u_roundness;
21
21
  uniform float u_thickness;
22
22
  uniform float u_softness;
23
23
  uniform float u_intensity;
24
+ uniform float u_bloom;
24
25
  uniform float u_spotSize;
25
- uniform float u_spotsPerColor;
26
+ uniform float u_spots;
26
27
  uniform float u_pulse;
27
28
  uniform float u_smoke;
28
29
  uniform float u_smokeSize;
29
30
 
30
- uniform sampler2D u_pulseTexture;
31
31
  uniform sampler2D u_noiseTexture;
32
32
 
33
33
  ${sizingVariablesDeclaration}
@@ -36,60 +36,27 @@ out vec4 fragColor;
36
36
 
37
37
  ${declarePI}
38
38
 
39
- float roundedBox(vec2 uv, vec2 halfSize, float radius, float distance, float edgeSoftness) {
40
-
41
- float borderDistance = abs(distance) - .5 * u_thickness;
42
- float border = 1. - smoothstep(-.5 * edgeSoftness, .5 * edgeSoftness, borderDistance);
43
- border *= border;
44
-
45
- vec2 v0 = uv + halfSize;
46
- vec2 v1 = uv - vec2(-halfSize.x, halfSize.y);
47
- vec2 v2 = uv - vec2(halfSize.x, -halfSize.y);
48
- vec2 v3 = uv - halfSize;
49
-
50
- float mult = (.07 - .25 * radius);
51
- float m0 = mult * clamp(pow(1. - abs(v0.x - v0.y), 20.), 0., 1.);
52
- float m1 = mult * clamp(pow(1. - abs(v1.x + v1.y), 20.), 0., 1.);
53
- float m2 = mult * clamp(pow(1. - abs(v2.x + v2.y), 20.), 0., 1.);
54
- float m3 = mult * clamp(pow(1. - abs(v3.x - v3.y), 20.), 0., 1.);
55
-
56
- float l = edgeSoftness * .5 + .75 * u_thickness;
57
- float fade0 = 1. - clamp(length(v0) / l, 0., 1.);
58
- float fade1 = 1. - clamp(length(v1) / l, 0., 1.);
59
- float fade2 = 1. - clamp(length(v2) / l, 0., 1.);
60
- float fade3 = 1. - clamp(length(v3) / l, 0., 1.);
61
-
62
- m0 *= fade0;
63
- m1 *= fade1;
64
- m2 *= fade2;
65
- m3 *= fade3;
66
-
67
- float fillFix = m0 + m1 + m2 + m3;
68
- fillFix *= step(distance, 0.);
69
- fillFix *= (1. + 3. * u_thickness);
70
- fillFix *= (1.5 - .5 * smoothstep(0., .5, edgeSoftness));
71
- fillFix = clamp(fillFix, 0., 1.);
72
-
73
- return border + fillFix;
74
- }
39
+ float beat(float time) {
40
+ float first = pow(sin(time * TWO_PI), 10.);
41
+ float second = pow(sin((time - 0.15) * TWO_PI), 10.);
75
42
 
76
- float roundedBoxSmoke(vec2 uv, vec2 halfSize, float radius, float distance, float size) {
77
- float borderDistance = abs(distance);
78
- float border = 1. - smoothstep(-.75 * size, .75 * size, borderDistance);
79
- border *= border;
43
+ return clamp(first + 0.6 * second, 0.0, 1.0);
44
+ }
80
45
 
81
- vec2 v0 = uv + halfSize;
82
- vec2 v1 = uv - vec2(-halfSize.x, halfSize.y);
83
- vec2 v2 = uv - vec2(halfSize.x, -halfSize.y);
84
- vec2 v3 = uv - halfSize;
46
+ float roundedBox(vec2 uv, float distance) {
47
+ float thickness = .5 * u_thickness;
48
+ float borderDistance = abs(distance);
49
+ float border = 1. - smoothstep(-u_softness * thickness - 2. * fwidth(borderDistance), .5 * u_softness * thickness, borderDistance - .5 * thickness);
50
+ border = pow(border, 2.);
85
51
 
86
- float l_mask = .5;
87
- float mask = smoothstep(0., 1., length(v0) / l_mask);
88
- mask *= smoothstep(0., 1., length(v1) / l_mask);
89
- mask *= smoothstep(0., 1., length(v2) / l_mask);
90
- mask *= smoothstep(0., 1., length(v3) / l_mask);
52
+ return border;
53
+ }
91
54
 
92
- return border * mask;
55
+ float roundedBoxSmoke(vec2 uv, float distance, float size) {
56
+ float borderDistance = abs(distance);
57
+ float border = 1. - smoothstep(-.75 * size, .75 * size, borderDistance);
58
+ border *= border;
59
+ return border;
93
60
  }
94
61
 
95
62
  float random(vec2 p) {
@@ -103,22 +70,19 @@ vec2 rand2(vec2 p) {
103
70
 
104
71
  ${declareValueNoise}
105
72
 
106
- float getWaveformValue(float time) {
107
- float dur = 5.;
108
- float wrappedTime = mod(time, dur);
109
- float normalizedTime = wrappedTime / dur;
110
- float value = texture(u_pulseTexture, vec2(normalizedTime, 0.5)).r;
111
- return value * 2. - 1.;
73
+ float linearstep(float edge0, float edge1, float x) {
74
+ return clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);
112
75
  }
113
76
 
114
77
  void main() {
115
78
 
116
- float t = .5 * u_time + 20.;
79
+ float t = 1.2 * u_time;
117
80
 
118
81
  vec2 borderUV = v_responsiveUV;
119
82
 
120
83
  float angle = atan(borderUV.y, borderUV.x) / TWO_PI;
121
84
 
85
+ float pulse = u_pulse * beat(.18 * u_time);
122
86
 
123
87
  float borderRatio = v_responsiveBoxGivenSize.x / v_responsiveBoxGivenSize.y;
124
88
  borderUV.x *= borderRatio;
@@ -130,80 +94,132 @@ void main() {
130
94
  float insideDistance = min(max(d.x, d.y), 0.0);
131
95
  float distance = outsideDistance + insideDistance;
132
96
 
133
- float border = roundedBox(borderUV, halfSize, radius, distance, .5 * u_softness);
134
-
135
- float pulse = u_pulse * getWaveformValue(.18 * u_time);
97
+ float border = roundedBox(borderUV, distance);
98
+
99
+ vec2 v0 = borderUV + halfSize;
100
+ vec2 v1 = borderUV - vec2(-halfSize.x, halfSize.y);
101
+ vec2 v2 = borderUV - vec2(halfSize.x, -halfSize.y);
102
+ vec2 v3 = borderUV - halfSize;
103
+
104
+ float cornerFade = 1. - abs(v0.x - v0.y);
105
+ cornerFade = max(cornerFade, 1. - abs(v1.x + v1.y));
106
+ cornerFade = max(cornerFade, 1. - abs(v2.x + v2.y));
107
+ cornerFade = max(cornerFade, 1. - abs(v3.x - v3.y));
108
+ cornerFade = .75 * pow(cornerFade, 20.);
109
+
110
+ float cornerFadeMask = 0.;
111
+ float maskR = (.35 * u_thickness - .25 * radius);
112
+ float maskHL = linearstep(halfSize.x - .25 * u_thickness, halfSize.x, borderUV.x);
113
+ float maskHR = linearstep(halfSize.x - .25 * u_thickness, halfSize.x, -borderUV.x);
114
+ float maskVT = linearstep(halfSize.y - .25 * u_thickness, halfSize.y, -borderUV.y);
115
+ float maskVB = linearstep(halfSize.y - .25 * u_thickness, halfSize.y, borderUV.y);
116
+ float maskOffset = .25 * (u_thickness + radius);
117
+ {
118
+ float m = maskHR;
119
+ m *= maskVT;
120
+ m *= (1. - clamp(length((v0 - maskOffset) / maskR), 0., 1.));
121
+ cornerFadeMask += m;
122
+ }
123
+ {
124
+ float m = maskHR;
125
+ m *= maskVB;
126
+ m *= (1. - clamp(length((v1 - vec2(1., -1.) * maskOffset) / maskR), 0., 1.));
127
+ cornerFadeMask += m;
128
+ }
129
+ {
130
+ float m = maskHL;
131
+ m *= maskVT;
132
+ m *= (1. - clamp(length((v2 - vec2(-1., 1.) * maskOffset) / maskR), 0., 1.));
133
+ cornerFadeMask += m;
134
+ }
135
+ {
136
+ float m = maskHL;
137
+ m *= maskVB;
138
+ m *= (1. - clamp(length((v3 + maskOffset) / maskR), 0., 1.));
139
+ cornerFadeMask += m;
140
+ }
136
141
 
137
- border *= (1. + .1 * pulse);
138
- border *= (1. + u_intensity);
142
+ cornerFade *= cornerFadeMask;
143
+ border += cornerFade;
139
144
 
140
- vec2 smokeUV = .001 * u_smokeSize * v_patternUV;
145
+ vec2 smokeUV = .2 * u_smokeSize * v_patternUV;
141
146
  float smoke = clamp(3. * valueNoise(2.7 * smokeUV + .5 * t), 0., 1.);
142
147
  smoke -= valueNoise(3.4 * smokeUV - .5 * t);
143
- smoke *= roundedBoxSmoke(borderUV, halfSize, radius, distance, u_smoke);
144
- smoke = 50. * pow(smoke, 2.);
148
+ smoke *= roundedBoxSmoke(borderUV, distance, u_smoke);
149
+ smoke = 30. * pow(smoke, 2.);
150
+ smoke += cornerFadeMask;
145
151
  smoke *= u_smoke;
146
- smoke *= (.8 + .4 * pulse);
152
+ smoke *= mix(1., pulse, u_pulse);
147
153
  smoke = clamp(smoke, 0., 1.);
148
154
 
149
155
  border += smoke;
156
+ border = clamp(border, 0., 1.);
157
+
158
+ vec3 blendColor = vec3(0.);
159
+ float blendAlpha = 0.0;
160
+ vec3 addColor = vec3(0.);
161
+ float addAlpha = 0.0;
162
+
163
+ float bloom = 4. * u_bloom;
164
+ float intensity = 1. + 4. * u_intensity;
150
165
 
151
- float sectorsTotal = 0.;
152
-
153
- vec3 color = vec3(0.);
154
- float opacity = 0.;
166
+ for (int colorIdx = 0; colorIdx < ${pulsingBorderMeta.maxColorCount}; colorIdx++) {
167
+ if (colorIdx >= int(u_colorsCount)) break;
168
+ float colorIdxF = float(colorIdx);
155
169
 
156
- vec3 accumColor = vec3(0.);
157
- float accumAlpha = 0.;
170
+ vec3 c = u_colors[colorIdx].rgb * u_colors[colorIdx].a;
171
+ float a = u_colors[colorIdx].a;
158
172
 
159
- for (int i = 0; i < ${pulsingBorderMeta.maxSpotsPerColor}; i++) {
160
- if (i >= int(u_spotsPerColor)) break;
161
- float idx = float(i);
173
+ for (int spotIdx = 0; spotIdx < ${pulsingBorderMeta.maxSpots}; spotIdx++) {
174
+ if (spotIdx >= int(u_spots)) break;
175
+ float spotIdxF = float(spotIdx);
162
176
 
163
- for (int j = 0; j < ${pulsingBorderMeta.maxColorCount}; j++) {
164
- if (j >= int(u_colorsCount)) break;
165
- float colorIdx = float(j);
177
+ vec2 randVal = rand2(vec2(spotIdxF * 10. + 2., 40. + colorIdxF));
166
178
 
167
- vec2 randVal = rand2(vec2(idx * 10. + 2., 40. + colorIdx));
168
-
169
- float time = (.1 + .15 * abs(sin(idx * (2. + colorIdx)) * cos(idx * (2. + 2.5 * colorIdx)))) * t + randVal.x * 3.;
179
+ float time = (.1 + .15 * abs(sin(spotIdxF * (2. + colorIdxF)) * cos(spotIdxF * (2. + 2.5 * colorIdxF)))) * t + randVal.x * 3.;
170
180
  time *= mix(1., -1., step(.5, randVal.y));
171
181
 
172
- float mask = .2 + mix(
173
- sin(t + idx * (5. - 1.5 * colorIdx)),
174
- cos(t + idx * (3. + 1.3 * colorIdx)),
175
- step(mod(colorIdx, 2.), .5)
182
+ float mask = .5 + .5 * mix(
183
+ sin(t + spotIdxF * (5. - 1.5 * colorIdxF)),
184
+ cos(t + spotIdxF * (3. + 1.3 * colorIdxF)),
185
+ step(mod(colorIdxF, 2.), .5)
176
186
  );
177
187
 
178
- mask += pulse;
179
- if (mask < 0.) continue;
188
+ float p = clamp(2. * u_pulse - randVal.x, 0., 1.);
189
+ mask = mix(mask, pulse, p);
180
190
 
181
191
  float atg1 = fract(angle + time);
182
- float sector = smoothstep(.5 - u_spotSize, .5, atg1) * smoothstep(.5 + u_spotSize, .5, atg1);
183
- sector *= border;
192
+ float spotSize = .05 + .6 * pow(u_spotSize, 2.) + .05 * randVal.x;
193
+ spotSize = mix(spotSize, .1, p);
194
+ float sector = smoothstep(.5 - spotSize, .5, atg1) * smoothstep(.5 + spotSize, .5, atg1);
195
+
184
196
  sector *= mask;
197
+ sector *= border;
198
+ sector *= intensity;
185
199
  sector = clamp(sector, 0., 1.);
186
200
 
187
- sectorsTotal += sector;
201
+ vec3 srcColor = c * sector;
202
+ float srcAlpha = a * sector;
188
203
 
189
- float alpha = sector * u_colors[j].a;
190
- accumColor += u_colors[j].rgb * alpha;
191
- accumAlpha += alpha;
204
+ blendColor += ((1. - blendAlpha) * srcColor);
205
+ blendAlpha = blendAlpha + (1. - blendAlpha) * srcAlpha;
206
+ addColor += srcColor;
207
+ addAlpha += srcAlpha;
192
208
  }
193
209
  }
194
210
 
195
- color = accumColor;
196
- opacity = clamp(accumAlpha, 0., 1.);
211
+ vec3 accumColor = mix(blendColor, addColor, bloom);
212
+ float accumAlpha = mix(blendAlpha, addAlpha, bloom);
213
+ accumAlpha = clamp(accumAlpha, 0., 1.);
197
214
 
198
215
  vec3 bgColor = u_colorBack.rgb * u_colorBack.a;
199
- color = color + bgColor * (1. - opacity);
200
- opacity = opacity + u_colorBack.a * (1. - opacity);
201
-
216
+ vec3 color = accumColor + (1. - accumAlpha) * bgColor;
217
+ float opacity = accumAlpha + (1. - accumAlpha) * u_colorBack.a;
218
+
202
219
  ${colorBandingFix}
203
220
 
204
221
  fragColor = vec4(color, opacity);
205
- }
206
- `;
222
+ }`;
207
223
  export {
208
224
  pulsingBorderFragmentShader,
209
225
  pulsingBorderMeta
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/shaders/pulsing-border.ts"],
4
- "sourcesContent": ["import type { vec4 } from '../types.js';\nimport type { ShaderMotionParams } from '../shader-mount.js';\nimport { sizingVariablesDeclaration, type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js';\nimport { declarePI, declareValueNoise, colorBandingFix } from '../shader-utils.js';\n\nexport const pulsingBorderMeta = {\n maxColorCount: 5,\n maxSpotsPerColor: 5,\n} as const;\n\n/**\n * Color spots traveling around rectangular stroke (border)\n *\n * Uniforms:\n * - u_colorBack (RGBA)\n * - u_colors (vec4[]), u_colorsCount (float used as integer)\n * - u_roundness, u_thickness, u_softness: border parameters\n * - u_intensity: global multiplier for spots shape & color\n * - u_spotSize: angular size of spots\n * - u_spotsPerColor (float used as int): number of spots rendered per color (not all visible at the same time)\n * - u_pulse: optional pulsing animation\n * - u_smoke, u_smokeSize: optional noisy shapes around the border\n *\n * - u_pulseTexture (sampler2D): pulsing signal source\n * - u_noiseTexture (sampler2D): pre-computed randomizer source\n *\n */\n\n// language=GLSL\nexport const pulsingBorderFragmentShader: string = `#version 300 es\nprecision lowp float;\n\nuniform float u_time;\n\nuniform vec4 u_colorBack;\nuniform vec4 u_colors[${pulsingBorderMeta.maxColorCount}];\nuniform float u_colorsCount;\nuniform float u_roundness;\nuniform float u_thickness;\nuniform float u_softness;\nuniform float u_intensity;\nuniform float u_spotSize;\nuniform float u_spotsPerColor;\nuniform float u_pulse;\nuniform float u_smoke;\nuniform float u_smokeSize;\n\nuniform sampler2D u_pulseTexture;\nuniform sampler2D u_noiseTexture;\n\n${sizingVariablesDeclaration}\n\nout vec4 fragColor;\n\n${declarePI}\n\nfloat roundedBox(vec2 uv, vec2 halfSize, float radius, float distance, float edgeSoftness) {\n \n float borderDistance = abs(distance) - .5 * u_thickness;\n float border = 1. - smoothstep(-.5 * edgeSoftness, .5 * edgeSoftness, borderDistance);\n border *= border;\n\n vec2 v0 = uv + halfSize;\n vec2 v1 = uv - vec2(-halfSize.x, halfSize.y);\n vec2 v2 = uv - vec2(halfSize.x, -halfSize.y);\n vec2 v3 = uv - halfSize;\n\n float mult = (.07 - .25 * radius);\n float m0 = mult * clamp(pow(1. - abs(v0.x - v0.y), 20.), 0., 1.);\n float m1 = mult * clamp(pow(1. - abs(v1.x + v1.y), 20.), 0., 1.);\n float m2 = mult * clamp(pow(1. - abs(v2.x + v2.y), 20.), 0., 1.);\n float m3 = mult * clamp(pow(1. - abs(v3.x - v3.y), 20.), 0., 1.);\n\n float l = edgeSoftness * .5 + .75 * u_thickness;\n float fade0 = 1. - clamp(length(v0) / l, 0., 1.);\n float fade1 = 1. - clamp(length(v1) / l, 0., 1.);\n float fade2 = 1. - clamp(length(v2) / l, 0., 1.);\n float fade3 = 1. - clamp(length(v3) / l, 0., 1.);\n\n m0 *= fade0;\n m1 *= fade1;\n m2 *= fade2;\n m3 *= fade3;\n\n float fillFix = m0 + m1 + m2 + m3;\n fillFix *= step(distance, 0.);\n fillFix *= (1. + 3. * u_thickness);\n fillFix *= (1.5 - .5 * smoothstep(0., .5, edgeSoftness));\n fillFix = clamp(fillFix, 0., 1.);\n\n return border + fillFix;\n}\n\nfloat roundedBoxSmoke(vec2 uv, vec2 halfSize, float radius, float distance, float size) {\n float borderDistance = abs(distance);\n float border = 1. - smoothstep(-.75 * size, .75 * size, borderDistance);\n border *= border;\n\n vec2 v0 = uv + halfSize;\n vec2 v1 = uv - vec2(-halfSize.x, halfSize.y);\n vec2 v2 = uv - vec2(halfSize.x, -halfSize.y);\n vec2 v3 = uv - halfSize;\n\n float l_mask = .5;\n float mask = smoothstep(0., 1., length(v0) / l_mask);\n mask *= smoothstep(0., 1., length(v1) / l_mask);\n mask *= smoothstep(0., 1., length(v2) / l_mask);\n mask *= smoothstep(0., 1., length(v3) / l_mask);\n\n return border * mask;\n}\n\nfloat random(vec2 p) {\n vec2 uv = floor(p) / 100. + .5;\n return texture(u_noiseTexture, uv).g;\n}\nvec2 rand2(vec2 p) {\n vec2 uv = floor(p) / 100. + .5;\n return texture(u_noiseTexture, uv).gb;\n}\n\n${declareValueNoise}\n\nfloat getWaveformValue(float time) {\n float dur = 5.;\n float wrappedTime = mod(time, dur);\n float normalizedTime = wrappedTime / dur;\n float value = texture(u_pulseTexture, vec2(normalizedTime, 0.5)).r;\n return value * 2. - 1.;\n}\n\nvoid main() {\n\n float t = .5 * u_time + 20.;\n\n vec2 borderUV = v_responsiveUV;\n\n float angle = atan(borderUV.y, borderUV.x) / TWO_PI;\n\n\n float borderRatio = v_responsiveBoxGivenSize.x / v_responsiveBoxGivenSize.y;\n borderUV.x *= borderRatio;\n vec2 halfSize = vec2(.5);\n halfSize.x *= borderRatio;\n float radius = min(.5 * u_roundness, halfSize.x);\n vec2 d = abs(borderUV) - halfSize + radius;\n float outsideDistance = length(max(d, 0.)) - radius;\n float insideDistance = min(max(d.x, d.y), 0.0);\n float distance = outsideDistance + insideDistance;\n\n float border = roundedBox(borderUV, halfSize, radius, distance, .5 * u_softness);\n\n float pulse = u_pulse * getWaveformValue(.18 * u_time);\n\n border *= (1. + .1 * pulse);\n border *= (1. + u_intensity);\n\n vec2 smokeUV = .001 * u_smokeSize * v_patternUV;\n float smoke = clamp(3. * valueNoise(2.7 * smokeUV + .5 * t), 0., 1.);\n smoke -= valueNoise(3.4 * smokeUV - .5 * t);\n smoke *= roundedBoxSmoke(borderUV, halfSize, radius, distance, u_smoke);\n smoke = 50. * pow(smoke, 2.);\n smoke *= u_smoke;\n smoke *= (.8 + .4 * pulse);\n smoke = clamp(smoke, 0., 1.);\n\n border += smoke;\n\n float sectorsTotal = 0.;\n\n vec3 color = vec3(0.);\n float opacity = 0.;\n\n vec3 accumColor = vec3(0.);\n float accumAlpha = 0.;\n\n for (int i = 0; i < ${pulsingBorderMeta.maxSpotsPerColor}; i++) {\n if (i >= int(u_spotsPerColor)) break;\n float idx = float(i);\n\n for (int j = 0; j < ${pulsingBorderMeta.maxColorCount}; j++) {\n if (j >= int(u_colorsCount)) break;\n float colorIdx = float(j);\n\n vec2 randVal = rand2(vec2(idx * 10. + 2., 40. + colorIdx));\n \n float time = (.1 + .15 * abs(sin(idx * (2. + colorIdx)) * cos(idx * (2. + 2.5 * colorIdx)))) * t + randVal.x * 3.;\n time *= mix(1., -1., step(.5, randVal.y));\n\n float mask = .2 + mix(\n sin(t + idx * (5. - 1.5 * colorIdx)),\n cos(t + idx * (3. + 1.3 * colorIdx)),\n step(mod(colorIdx, 2.), .5)\n );\n\n mask += pulse;\n if (mask < 0.) continue;\n\n float atg1 = fract(angle + time);\n float sector = smoothstep(.5 - u_spotSize, .5, atg1) * smoothstep(.5 + u_spotSize, .5, atg1);\n sector *= border;\n sector *= mask;\n sector = clamp(sector, 0., 1.);\n\n sectorsTotal += sector;\n\n float alpha = sector * u_colors[j].a;\n accumColor += u_colors[j].rgb * alpha;\n accumAlpha += alpha;\n }\n }\n\n color = accumColor;\n opacity = clamp(accumAlpha, 0., 1.);\n\n vec3 bgColor = u_colorBack.rgb * u_colorBack.a;\n color = color + bgColor * (1. - opacity);\n opacity = opacity + u_colorBack.a * (1. - opacity);\n\n ${colorBandingFix}\n\n fragColor = vec4(color, opacity);\n}\n`;\n\nexport interface PulsingBorderUniforms extends ShaderSizingUniforms {\n u_colorBack: [number, number, number, number];\n u_colors: vec4[];\n u_colorsCount: number;\n u_roundness: number;\n u_thickness: number;\n u_softness: number;\n u_intensity: number;\n u_spotsPerColor: number;\n u_spotSize: number;\n u_pulse: number;\n u_smoke: number;\n u_smokeSize: number;\n u_pulseTexture?: HTMLImageElement;\n}\n\nexport interface PulsingBorderParams extends ShaderSizingParams, ShaderMotionParams {\n colorBack?: string;\n colors?: string[];\n roundness?: number;\n thickness?: number;\n softness?: number;\n intensity?: number;\n spotsPerColor?: number;\n spotSize?: number;\n pulse?: number;\n smoke?: number;\n smokeSize?: number;\n}\n"],
5
- "mappings": ";;;;;AAEA,SAAS,kCAAsF;AAC/F,SAAS,WAAW,mBAAmB,uBAAuB;AAEvD,MAAM,oBAAoB;AAAA,EAC/B,eAAe;AAAA,EACf,kBAAkB;AACpB;AAqBO,MAAM,8BAAsC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAM3B,kBAAkB,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAerD,0BAA0B;AAAA;AAAA;AAAA;AAAA,EAI1B,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAmET,iBAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAuDK,kBAAkB,gBAAgB;AAAA;AAAA;AAAA;AAAA,0BAIhC,kBAAkB,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAuCrD,eAAe;AAAA;AAAA;AAAA;AAAA;",
4
+ "sourcesContent": ["import type { vec4 } from '../types.js';\nimport type { ShaderMotionParams } from '../shader-mount.js';\nimport { sizingVariablesDeclaration, type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js';\nimport { declarePI, declareValueNoise, colorBandingFix } from '../shader-utils.js';\n\nexport const pulsingBorderMeta = {\n maxColorCount: 5,\n maxSpots: 4,\n} as const;\n\n/**\n * Color spots traveling around rectangular stroke (border)\n *\n * Uniforms:\n * - u_colorBack (RGBA)\n * - u_colors (vec4[]), u_colorsCount (float used as integer)\n * - u_roundness, u_thickness, u_softness: border parameters\n * - u_intensity: thickness of individual spots\n * - u_bloom: normal / additive color blending\n * - u_spotSize: angular size of spots\n * - u_spots (float used as int): number of spots rendered per color\n * - u_pulse: optional pulsing animation\n * - u_smoke, u_smokeSize: optional noisy shapes around the border\n *\n * - u_noiseTexture (sampler2D): pre-computed randomizer source\n *\n */\n\n// language=GLSL\nexport const pulsingBorderFragmentShader: string = `#version 300 es\nprecision lowp float;\n\nuniform float u_time;\n\nuniform vec4 u_colorBack;\nuniform vec4 u_colors[${pulsingBorderMeta.maxColorCount}];\nuniform float u_colorsCount;\nuniform float u_roundness;\nuniform float u_thickness;\nuniform float u_softness;\nuniform float u_intensity;\nuniform float u_bloom;\nuniform float u_spotSize;\nuniform float u_spots;\nuniform float u_pulse;\nuniform float u_smoke;\nuniform float u_smokeSize;\n\nuniform sampler2D u_noiseTexture;\n\n${sizingVariablesDeclaration}\n\nout vec4 fragColor;\n\n${declarePI}\n\nfloat beat(float time) {\n float first = pow(sin(time * TWO_PI), 10.);\n float second = pow(sin((time - 0.15) * TWO_PI), 10.);\n\n return clamp(first + 0.6 * second, 0.0, 1.0);\n}\n\nfloat roundedBox(vec2 uv, float distance) {\n float thickness = .5 * u_thickness;\n float borderDistance = abs(distance);\n float border = 1. - smoothstep(-u_softness * thickness - 2. * fwidth(borderDistance), .5 * u_softness * thickness, borderDistance - .5 * thickness);\n border = pow(border, 2.);\n\n return border;\n}\n\nfloat roundedBoxSmoke(vec2 uv, float distance, float size) {\n float borderDistance = abs(distance);\n float border = 1. - smoothstep(-.75 * size, .75 * size, borderDistance);\n border *= border;\n return border;\n}\n\nfloat random(vec2 p) {\n vec2 uv = floor(p) / 100. + .5;\n return texture(u_noiseTexture, uv).g;\n}\nvec2 rand2(vec2 p) {\n vec2 uv = floor(p) / 100. + .5;\n return texture(u_noiseTexture, uv).gb;\n}\n\n${declareValueNoise}\n\nfloat linearstep(float edge0, float edge1, float x) {\n return clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);\n}\n\nvoid main() {\n\n float t = 1.2 * u_time;\n\n vec2 borderUV = v_responsiveUV;\n\n float angle = atan(borderUV.y, borderUV.x) / TWO_PI;\n\n float pulse = u_pulse * beat(.18 * u_time);\n\n float borderRatio = v_responsiveBoxGivenSize.x / v_responsiveBoxGivenSize.y;\n borderUV.x *= borderRatio;\n vec2 halfSize = vec2(.5);\n halfSize.x *= borderRatio;\n float radius = min(.5 * u_roundness, halfSize.x);\n vec2 d = abs(borderUV) - halfSize + radius;\n float outsideDistance = length(max(d, 0.)) - radius;\n float insideDistance = min(max(d.x, d.y), 0.0);\n float distance = outsideDistance + insideDistance;\n\n float border = roundedBox(borderUV, distance);\n \n vec2 v0 = borderUV + halfSize;\n vec2 v1 = borderUV - vec2(-halfSize.x, halfSize.y);\n vec2 v2 = borderUV - vec2(halfSize.x, -halfSize.y);\n vec2 v3 = borderUV - halfSize;\n \n float cornerFade = 1. - abs(v0.x - v0.y);\n cornerFade = max(cornerFade, 1. - abs(v1.x + v1.y));\n cornerFade = max(cornerFade, 1. - abs(v2.x + v2.y));\n cornerFade = max(cornerFade, 1. - abs(v3.x - v3.y));\n cornerFade = .75 * pow(cornerFade, 20.);\n\n float cornerFadeMask = 0.;\n float maskR = (.35 * u_thickness - .25 * radius);\n float maskHL = linearstep(halfSize.x - .25 * u_thickness, halfSize.x, borderUV.x);\n float maskHR = linearstep(halfSize.x - .25 * u_thickness, halfSize.x, -borderUV.x);\n float maskVT = linearstep(halfSize.y - .25 * u_thickness, halfSize.y, -borderUV.y);\n float maskVB = linearstep(halfSize.y - .25 * u_thickness, halfSize.y, borderUV.y);\n float maskOffset = .25 * (u_thickness + radius);\n {\n float m = maskHR;\n m *= maskVT;\n m *= (1. - clamp(length((v0 - maskOffset) / maskR), 0., 1.));\n cornerFadeMask += m;\n }\n {\n float m = maskHR;\n m *= maskVB;\n m *= (1. - clamp(length((v1 - vec2(1., -1.) * maskOffset) / maskR), 0., 1.));\n cornerFadeMask += m;\n }\n {\n float m = maskHL;\n m *= maskVT;\n m *= (1. - clamp(length((v2 - vec2(-1., 1.) * maskOffset) / maskR), 0., 1.));\n cornerFadeMask += m;\n }\n {\n float m = maskHL;\n m *= maskVB;\n m *= (1. - clamp(length((v3 + maskOffset) / maskR), 0., 1.));\n cornerFadeMask += m;\n }\n\n cornerFade *= cornerFadeMask;\n border += cornerFade;\n\n vec2 smokeUV = .2 * u_smokeSize * v_patternUV;\n float smoke = clamp(3. * valueNoise(2.7 * smokeUV + .5 * t), 0., 1.);\n smoke -= valueNoise(3.4 * smokeUV - .5 * t);\n smoke *= roundedBoxSmoke(borderUV, distance, u_smoke);\n smoke = 30. * pow(smoke, 2.);\n smoke += cornerFadeMask;\n smoke *= u_smoke;\n smoke *= mix(1., pulse, u_pulse);\n smoke = clamp(smoke, 0., 1.);\n\n border += smoke;\n border = clamp(border, 0., 1.);\n \n vec3 blendColor = vec3(0.);\n float blendAlpha = 0.0;\n vec3 addColor = vec3(0.);\n float addAlpha = 0.0;\n \n float bloom = 4. * u_bloom;\n float intensity = 1. + 4. * u_intensity;\n\n for (int colorIdx = 0; colorIdx < ${pulsingBorderMeta.maxColorCount}; colorIdx++) {\n if (colorIdx >= int(u_colorsCount)) break;\n float colorIdxF = float(colorIdx);\n\n vec3 c = u_colors[colorIdx].rgb * u_colors[colorIdx].a;\n float a = u_colors[colorIdx].a;\n\n for (int spotIdx = 0; spotIdx < ${pulsingBorderMeta.maxSpots}; spotIdx++) {\n if (spotIdx >= int(u_spots)) break;\n float spotIdxF = float(spotIdx);\n\n vec2 randVal = rand2(vec2(spotIdxF * 10. + 2., 40. + colorIdxF));\n\n float time = (.1 + .15 * abs(sin(spotIdxF * (2. + colorIdxF)) * cos(spotIdxF * (2. + 2.5 * colorIdxF)))) * t + randVal.x * 3.;\n time *= mix(1., -1., step(.5, randVal.y));\n\n float mask = .5 + .5 * mix(\n sin(t + spotIdxF * (5. - 1.5 * colorIdxF)),\n cos(t + spotIdxF * (3. + 1.3 * colorIdxF)),\n step(mod(colorIdxF, 2.), .5)\n );\n\n float p = clamp(2. * u_pulse - randVal.x, 0., 1.);\n mask = mix(mask, pulse, p);\n\n float atg1 = fract(angle + time);\n float spotSize = .05 + .6 * pow(u_spotSize, 2.) + .05 * randVal.x;\n spotSize = mix(spotSize, .1, p);\n float sector = smoothstep(.5 - spotSize, .5, atg1) * smoothstep(.5 + spotSize, .5, atg1);\n \n sector *= mask;\n sector *= border;\n sector *= intensity;\n sector = clamp(sector, 0., 1.);\n\n vec3 srcColor = c * sector;\n float srcAlpha = a * sector;\n\n blendColor += ((1. - blendAlpha) * srcColor);\n blendAlpha = blendAlpha + (1. - blendAlpha) * srcAlpha;\n addColor += srcColor;\n addAlpha += srcAlpha;\n }\n }\n\n vec3 accumColor = mix(blendColor, addColor, bloom);\n float accumAlpha = mix(blendAlpha, addAlpha, bloom);\n accumAlpha = clamp(accumAlpha, 0., 1.);\n\n vec3 bgColor = u_colorBack.rgb * u_colorBack.a;\n vec3 color = accumColor + (1. - accumAlpha) * bgColor;\n float opacity = accumAlpha + (1. - accumAlpha) * u_colorBack.a;\n \n ${colorBandingFix}\n\n fragColor = vec4(color, opacity);\n}`;\n\nexport interface PulsingBorderUniforms extends ShaderSizingUniforms {\n u_colorBack: [number, number, number, number];\n u_colors: vec4[];\n u_colorsCount: number;\n u_roundness: number;\n u_thickness: number;\n u_softness: number;\n u_intensity: number;\n u_bloom: number;\n u_spots: number;\n u_spotSize: number;\n u_pulse: number;\n u_smoke: number;\n u_smokeSize: number;\n}\n\nexport interface PulsingBorderParams extends ShaderSizingParams, ShaderMotionParams {\n colorBack?: string;\n colors?: string[];\n roundness?: number;\n thickness?: number;\n softness?: number;\n intensity?: number;\n bloom?: number;\n spots?: number;\n spotSize?: number;\n pulse?: number;\n smoke?: number;\n smokeSize?: number;\n}\n"],
5
+ "mappings": ";;;;;AAEA,SAAS,kCAAsF;AAC/F,SAAS,WAAW,mBAAmB,uBAAuB;AAEvD,MAAM,oBAAoB;AAAA,EAC/B,eAAe;AAAA,EACf,UAAU;AACZ;AAqBO,MAAM,8BAAsC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAM3B,kBAAkB,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAerD,0BAA0B;AAAA;AAAA;AAAA;AAAA,EAI1B,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkCT,iBAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,sCA+FmB,kBAAkB,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,sCAO/B,kBAAkB,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IA8C5D,eAAe;AAAA;AAAA;AAAA;",
6
6
  "names": []
7
7
  }
@@ -32,20 +32,16 @@ float getNoise(vec2 uv, float t) {
32
32
  return noise;
33
33
  }
34
34
 
35
- float steppedSmooth(float t, float steps, float softness) {
36
- float stepT = floor(t * steps) / steps;
37
- float f = t * steps - floor(t * steps);
38
-
39
- float fw = 0.005 / u_scale;
40
- float smoothed = smoothstep(.5 - softness * .5 - fw, .5 + softness * .5 + fw, f);
41
-
42
- return stepT + smoothed / steps;
35
+ float steppedSmooth(float m, float steps, float softness) {
36
+ float stepT = floor(m * steps) / steps;
37
+ float f = m * steps - floor(m * steps);
38
+ float smoothed = smoothstep(.5 - softness, .5 + softness, f);
39
+ return stepT + smoothed / steps;
43
40
  }
44
41
 
45
42
  void main() {
46
43
  vec2 shape_uv = v_patternUV;
47
-
48
- shape_uv *= .001;
44
+ shape_uv *= .1;
49
45
 
50
46
  float t = .2 * u_time;
51
47
 
@@ -65,26 +61,26 @@ void main() {
65
61
  for (int i = 1; i < ${simplexNoiseMeta.maxColorCount}; i++) {
66
62
  if (i >= int(u_colorsCount)) break;
67
63
 
68
- float localT = clamp(mixer - float(i - 1), 0., 1.);
69
- localT = steppedSmooth(localT, steps, u_softness);
64
+ float localM = clamp(mixer - float(i - 1), 0., 1.);
65
+ localM = steppedSmooth(localM, steps, .5 * u_softness + steps * fwidth(localM));
70
66
 
71
67
  vec4 c = u_colors[i];
72
68
  c.rgb *= c.a;
73
- gradient = mix(gradient, c, localT);
69
+ gradient = mix(gradient, c, localM);
74
70
  }
75
71
 
76
72
  if (u_extraSides == true) {
77
73
  if ((mixer < 0.) || (mixer > (u_colorsCount - 1.))) {
78
- float localT = mixer + 1.;
74
+ float localM = mixer + 1.;
79
75
  if (mixer > (u_colorsCount - 1.)) {
80
- localT = mixer - (u_colorsCount - 1.);
76
+ localM = mixer - (u_colorsCount - 1.);
81
77
  }
82
- localT = steppedSmooth(localT, steps, u_softness);
78
+ localM = steppedSmooth(localM, steps, .5 * u_softness + steps * fwidth(localM));
83
79
  vec4 cFst = u_colors[0];
84
80
  cFst.rgb *= cFst.a;
85
81
  vec4 cLast = u_colors[int(u_colorsCount - 1.)];
86
82
  cLast.rgb *= cLast.a;
87
- gradient = mix(cLast, cFst, localT);
83
+ gradient = mix(cLast, cFst, localM);
88
84
  }
89
85
  }
90
86
 
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/shaders/simplex-noise.ts"],
4
- "sourcesContent": ["import type { vec4 } from '../types.js';\nimport type { ShaderMotionParams } from '../shader-mount.js';\nimport { sizingVariablesDeclaration, type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js';\nimport { declareSimplexNoise, colorBandingFix } from '../shader-utils.js';\n\nexport const simplexNoiseMeta = {\n maxColorCount: 10,\n} as const;\n\n/**\n * Color Gradient mapped over a combination of 2 Simplex noises\n *\n * Uniforms:\n * - u_colors (vec4[]), u_colorsCount (float used as integer)\n * - u_stepsPerColor (float, used as int): discrete color steps between u_colors\n * - u_softness: color transition sharpness (0 = hard edge, 1 = smooth fade)\n *\n * */\n\n// language=GLSL\nexport const simplexNoiseFragmentShader: string = `#version 300 es\nprecision mediump float;\n\nuniform float u_time;\nuniform float u_scale;\n\nuniform vec4 u_colors[${simplexNoiseMeta.maxColorCount}];\nuniform float u_colorsCount;\nuniform float u_stepsPerColor;\nuniform float u_softness;\n\n${sizingVariablesDeclaration}\n\nout vec4 fragColor;\n\n${declareSimplexNoise}\n\nfloat getNoise(vec2 uv, float t) {\n float noise = .5 * snoise(uv - vec2(0., .3 * t));\n noise += .5 * snoise(2. * uv + vec2(0., .32 * t));\n\n return noise;\n}\n\nfloat steppedSmooth(float t, float steps, float softness) {\n float stepT = floor(t * steps) / steps;\n float f = t * steps - floor(t * steps);\n\n float fw = 0.005 / u_scale;\n float smoothed = smoothstep(.5 - softness * .5 - fw, .5 + softness * .5 + fw, f);\n\n return stepT + smoothed / steps;\n}\n\nvoid main() {\n vec2 shape_uv = v_patternUV;\n\n shape_uv *= .001;\n\n float t = .2 * u_time;\n\n float shape = .5 + .5 * getNoise(shape_uv, t);\n\n bool u_extraSides = true;\n\n float mixer = shape * (u_colorsCount - 1.);\n if (u_extraSides == true) {\n mixer = (shape - .5 / u_colorsCount) * u_colorsCount;\n }\n\n float steps = max(1., u_stepsPerColor);\n\n vec4 gradient = u_colors[0];\n gradient.rgb *= gradient.a;\n for (int i = 1; i < ${simplexNoiseMeta.maxColorCount}; i++) {\n if (i >= int(u_colorsCount)) break;\n\n float localT = clamp(mixer - float(i - 1), 0., 1.);\n localT = steppedSmooth(localT, steps, u_softness);\n\n vec4 c = u_colors[i];\n c.rgb *= c.a;\n gradient = mix(gradient, c, localT);\n }\n\n if (u_extraSides == true) {\n if ((mixer < 0.) || (mixer > (u_colorsCount - 1.))) {\n float localT = mixer + 1.;\n if (mixer > (u_colorsCount - 1.)) {\n localT = mixer - (u_colorsCount - 1.);\n }\n localT = steppedSmooth(localT, steps, u_softness);\n vec4 cFst = u_colors[0];\n cFst.rgb *= cFst.a;\n vec4 cLast = u_colors[int(u_colorsCount - 1.)];\n cLast.rgb *= cLast.a;\n gradient = mix(cLast, cFst, localT);\n }\n }\n\n vec3 color = gradient.rgb;\n float opacity = gradient.a;\n\n ${colorBandingFix}\n\n fragColor = vec4(color, opacity);\n}\n`;\n\nexport interface SimplexNoiseUniforms extends ShaderSizingUniforms {\n u_colors: vec4[];\n u_colorsCount: number;\n u_stepsPerColor: number;\n u_softness: number;\n}\n\nexport interface SimplexNoiseParams extends ShaderSizingParams, ShaderMotionParams {\n colors?: string[];\n stepsPerColor?: number;\n softness?: number;\n}\n"],
5
- "mappings": ";;;;;AAEA,SAAS,kCAAsF;AAC/F,SAAS,qBAAqB,uBAAuB;AAE9C,MAAM,mBAAmB;AAAA,EAC9B,eAAe;AACjB;AAaO,MAAM,6BAAqC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAM1B,iBAAiB,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA,EAKpD,0BAA0B;AAAA;AAAA;AAAA;AAAA,EAI1B,mBAAmB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAuCG,iBAAiB,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IA6BlD,eAAe;AAAA;AAAA;AAAA;AAAA;",
4
+ "sourcesContent": ["import type { vec4 } from '../types.js';\nimport type { ShaderMotionParams } from '../shader-mount.js';\nimport { sizingVariablesDeclaration, type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js';\nimport { declareSimplexNoise, colorBandingFix } from '../shader-utils.js';\n\nexport const simplexNoiseMeta = {\n maxColorCount: 10,\n} as const;\n\n/**\n * Color Gradient mapped over a combination of 2 Simplex noises\n *\n * Uniforms:\n * - u_colors (vec4[]), u_colorsCount (float used as integer)\n * - u_stepsPerColor (float, used as int): discrete color steps between u_colors\n * - u_softness: color transition sharpness (0 = hard edge, 1 = smooth fade)\n *\n * */\n\n// language=GLSL\nexport const simplexNoiseFragmentShader: string = `#version 300 es\nprecision mediump float;\n\nuniform float u_time;\nuniform float u_scale;\n\nuniform vec4 u_colors[${simplexNoiseMeta.maxColorCount}];\nuniform float u_colorsCount;\nuniform float u_stepsPerColor;\nuniform float u_softness;\n\n${sizingVariablesDeclaration}\n\nout vec4 fragColor;\n\n${declareSimplexNoise}\n\nfloat getNoise(vec2 uv, float t) {\n float noise = .5 * snoise(uv - vec2(0., .3 * t));\n noise += .5 * snoise(2. * uv + vec2(0., .32 * t));\n\n return noise;\n}\n\nfloat steppedSmooth(float m, float steps, float softness) { \n float stepT = floor(m * steps) / steps;\n float f = m * steps - floor(m * steps);\n float smoothed = smoothstep(.5 - softness, .5 + softness, f);\n return stepT + smoothed / steps;\n}\n\nvoid main() {\n vec2 shape_uv = v_patternUV;\n shape_uv *= .1;\n\n float t = .2 * u_time;\n\n float shape = .5 + .5 * getNoise(shape_uv, t);\n\n bool u_extraSides = true;\n\n float mixer = shape * (u_colorsCount - 1.);\n if (u_extraSides == true) {\n mixer = (shape - .5 / u_colorsCount) * u_colorsCount;\n }\n\n float steps = max(1., u_stepsPerColor);\n\n vec4 gradient = u_colors[0];\n gradient.rgb *= gradient.a;\n for (int i = 1; i < ${simplexNoiseMeta.maxColorCount}; i++) {\n if (i >= int(u_colorsCount)) break;\n\n float localM = clamp(mixer - float(i - 1), 0., 1.);\n localM = steppedSmooth(localM, steps, .5 * u_softness + steps * fwidth(localM));\n\n vec4 c = u_colors[i];\n c.rgb *= c.a;\n gradient = mix(gradient, c, localM);\n }\n\n if (u_extraSides == true) {\n if ((mixer < 0.) || (mixer > (u_colorsCount - 1.))) {\n float localM = mixer + 1.;\n if (mixer > (u_colorsCount - 1.)) {\n localM = mixer - (u_colorsCount - 1.);\n }\n localM = steppedSmooth(localM, steps, .5 * u_softness + steps * fwidth(localM));\n vec4 cFst = u_colors[0];\n cFst.rgb *= cFst.a;\n vec4 cLast = u_colors[int(u_colorsCount - 1.)];\n cLast.rgb *= cLast.a;\n gradient = mix(cLast, cFst, localM);\n }\n }\n\n vec3 color = gradient.rgb;\n float opacity = gradient.a;\n\n ${colorBandingFix}\n\n fragColor = vec4(color, opacity);\n}\n`;\n\nexport interface SimplexNoiseUniforms extends ShaderSizingUniforms {\n u_colors: vec4[];\n u_colorsCount: number;\n u_stepsPerColor: number;\n u_softness: number;\n}\n\nexport interface SimplexNoiseParams extends ShaderSizingParams, ShaderMotionParams {\n colors?: string[];\n stepsPerColor?: number;\n softness?: number;\n}\n"],
5
+ "mappings": ";;;;;AAEA,SAAS,kCAAsF;AAC/F,SAAS,qBAAqB,uBAAuB;AAE9C,MAAM,mBAAmB;AAAA,EAC9B,eAAe;AACjB;AAaO,MAAM,6BAAqC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAM1B,iBAAiB,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA,EAKpD,0BAA0B;AAAA;AAAA;AAAA;AAAA,EAI1B,mBAAmB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAmCG,iBAAiB,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IA6BlD,eAAe;AAAA;AAAA;AAAA;AAAA;",
6
6
  "names": []
7
7
  }
@@ -10,7 +10,7 @@ import { type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-si
10
10
  * - u_strokeTaper: stroke loosing width further from center (0 for full visibility)
11
11
  * - u_distortion: per-arch shift
12
12
  * - u_strokeCap: extra width at the center (no effect on u_strokeWidth = 0.5)
13
- * - u_noiseFrequency, u_noisePower: simplex noise distortion over the shape
13
+ * - u_noiseFrequency, u_noise: simplex noise distortion over the shape
14
14
  * - u_softness: color transition sharpness (0 = hard edge, 1 = smooth fade)
15
15
  *
16
16
  */
@@ -23,8 +23,8 @@ export interface SpiralUniforms extends ShaderSizingUniforms {
23
23
  u_strokeWidth: number;
24
24
  u_strokeTaper: number;
25
25
  u_strokeCap: number;
26
+ u_noise: number;
26
27
  u_noiseFrequency: number;
27
- u_noisePower: number;
28
28
  u_softness: number;
29
29
  }
30
30
  export interface SpiralParams extends ShaderSizingParams, ShaderMotionParams {
@@ -35,7 +35,7 @@ export interface SpiralParams extends ShaderSizingParams, ShaderMotionParams {
35
35
  strokeWidth?: number;
36
36
  strokeTaper?: number;
37
37
  strokeCap?: number;
38
+ noise?: number;
38
39
  noiseFrequency?: number;
39
- noisePower?: number;
40
40
  softness?: number;
41
41
  }
@@ -17,9 +17,8 @@ uniform float u_distortion;
17
17
  uniform float u_strokeWidth;
18
18
  uniform float u_strokeCap;
19
19
  uniform float u_strokeTaper;
20
-
20
+ uniform float u_noise;
21
21
  uniform float u_noiseFrequency;
22
- uniform float u_noisePower;
23
22
  uniform float u_softness;
24
23
 
25
24
  ${sizingVariablesDeclaration}
@@ -30,48 +29,46 @@ ${declarePI}
30
29
  ${declareSimplexNoise}
31
30
 
32
31
  void main() {
33
- vec2 shape_uv = v_patternUV * .02;
34
-
32
+ vec2 uv = 2. * v_patternUV;
33
+
35
34
  float t = u_time;
35
+ float l = length(uv);
36
+ float density = clamp(u_density, 0., 1.);
37
+ l = pow(l, density);
38
+ float angle = atan(uv.y, uv.x) - t;
39
+ float angleNormalised = angle / TWO_PI;
36
40
 
37
- float l = length(shape_uv);
38
- float angle = atan(shape_uv.y, shape_uv.x) - 2. * t;
39
- float angle_norm = angle / TWO_PI;
40
-
41
- angle_norm += .125 * u_noisePower * snoise(.5 * u_noiseFrequency * shape_uv);
42
-
43
- float offset = pow(l, 1. - clamp(u_density, 0., 1.)) + angle_norm;
44
-
45
- float stripe_map = fract(offset);
46
- stripe_map -= .5 * u_strokeTaper * l;
47
-
48
- stripe_map += .25 * u_noisePower * snoise(u_noiseFrequency * shape_uv);
49
-
50
- float shape = 2. * abs(stripe_map - .5);
51
- float test = step(.5, stripe_map);
41
+ angleNormalised += .125 * u_noise * snoise(16. * pow(u_noiseFrequency, 3.) * uv);
52
42
 
53
- shape *= (1. + u_distortion * sin(4. * l - t) * cos(PI + l + t));
43
+ float offset = l + angleNormalised;
44
+ offset -= u_distortion * (sin(4. * l - .5 * t) * cos(PI + l + .5 * t));
45
+ float stripe = fract(offset);
46
+
47
+ float shape = 2. * abs(stripe - .5);
48
+ float width = clamp(u_strokeWidth, .005 * u_strokeTaper, 1.);
54
49
 
55
- float stroke_width = clamp(u_strokeWidth, fwidth(l), 1. - fwidth(l));
56
50
 
57
- float edge_width = min(fwidth(l), fwidth(offset));
51
+ float wCap = mix(width, (1. - stripe) * (1. - step(.5, stripe)), (1. - clamp(l, 0., 1.)));
52
+ width = mix(width, wCap, u_strokeCap);
53
+ width *= (1. - clamp(u_strokeTaper, 0., 1.) * l);
58
54
 
59
- float mid = 1. - smoothstep(.0, .9, l);
60
- mid = pow(mid, 2.);
61
- shape -= .5 * u_strokeCap * mid;
55
+ float fw = fwidth(offset);
56
+ float fwMult = 4. - 3. * (smoothstep(.05, .4, 2. * u_strokeWidth) * smoothstep(.05, .4, 2. * (1. - u_strokeWidth)));
57
+ float pixelSize = mix(fwMult * fw, fwidth(shape), clamp(fw, 0., 1.));
58
+ pixelSize = mix(pixelSize, .002, u_strokeCap * (1. - clamp(l, 0., 1.)));
62
59
 
63
- float res = smoothstep(stroke_width - edge_width - u_softness, stroke_width + edge_width + u_softness, shape);
60
+ float res = smoothstep(width - pixelSize - u_softness, width + pixelSize + u_softness, shape);
64
61
 
65
62
  vec3 fgColor = u_colorFront.rgb * u_colorFront.a;
66
63
  float fgOpacity = u_colorFront.a;
67
64
  vec3 bgColor = u_colorBack.rgb * u_colorBack.a;
68
65
  float bgOpacity = u_colorBack.a;
69
66
 
70
- vec3 color = bgColor * res;
71
- float opacity = bgOpacity * res;
67
+ vec3 color = fgColor * res;
68
+ float opacity = fgOpacity * res;
72
69
 
73
- color += fgColor * (1. - opacity);
74
- opacity += fgOpacity * (1. - opacity);
70
+ color += bgColor * (1. - opacity);
71
+ opacity += bgOpacity * (1. - opacity);
75
72
 
76
73
  ${colorBandingFix}
77
74