react-native-effects 0.0.1 → 0.1.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.
Files changed (100) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +251 -0
  3. package/lib/module/components/Aurora.js +184 -0
  4. package/lib/module/components/Aurora.js.map +1 -0
  5. package/lib/module/components/CalicoSwirl.js +155 -0
  6. package/lib/module/components/CalicoSwirl.js.map +1 -0
  7. package/lib/module/components/Campfire.js +225 -0
  8. package/lib/module/components/Campfire.js.map +1 -0
  9. package/lib/module/components/CircularGradient.js +52 -0
  10. package/lib/module/components/CircularGradient.js.map +1 -0
  11. package/lib/module/components/Iridescence.js +57 -0
  12. package/lib/module/components/Iridescence.js.map +1 -0
  13. package/lib/module/components/LinearGradient.js +48 -0
  14. package/lib/module/components/LinearGradient.js.map +1 -0
  15. package/lib/module/components/LiquidChrome.js +75 -0
  16. package/lib/module/components/LiquidChrome.js.map +1 -0
  17. package/lib/module/components/ShaderView/index.js +224 -0
  18. package/lib/module/components/ShaderView/index.js.map +1 -0
  19. package/lib/module/components/ShaderView/types.js +4 -0
  20. package/lib/module/components/ShaderView/types.js.map +1 -0
  21. package/lib/module/components/Silk.js +83 -0
  22. package/lib/module/components/Silk.js.map +1 -0
  23. package/lib/module/consts.js +154 -0
  24. package/lib/module/consts.js.map +1 -0
  25. package/lib/module/hooks/useClock.js +15 -0
  26. package/lib/module/hooks/useClock.js.map +1 -0
  27. package/lib/module/hooks/useWGPUSetup.js +54 -0
  28. package/lib/module/hooks/useWGPUSetup.js.map +1 -0
  29. package/lib/module/index.js +13 -0
  30. package/lib/module/index.js.map +1 -0
  31. package/lib/module/package.json +1 -0
  32. package/lib/module/shaders/TRIANGLE_VERTEX_SHADER.js +20 -0
  33. package/lib/module/shaders/TRIANGLE_VERTEX_SHADER.js.map +1 -0
  34. package/lib/module/shaders/uniforms.js +20 -0
  35. package/lib/module/shaders/uniforms.js.map +1 -0
  36. package/lib/module/utils/backgroundRuntime.js +12 -0
  37. package/lib/module/utils/backgroundRuntime.js.map +1 -0
  38. package/lib/module/utils/colors.js +94 -0
  39. package/lib/module/utils/colors.js.map +1 -0
  40. package/lib/module/utils/initWebGPU.js +40 -0
  41. package/lib/module/utils/initWebGPU.js.map +1 -0
  42. package/lib/typescript/package.json +1 -0
  43. package/lib/typescript/src/components/Aurora.d.ts +17 -0
  44. package/lib/typescript/src/components/Aurora.d.ts.map +1 -0
  45. package/lib/typescript/src/components/CalicoSwirl.d.ts +13 -0
  46. package/lib/typescript/src/components/CalicoSwirl.d.ts.map +1 -0
  47. package/lib/typescript/src/components/Campfire.d.ts +17 -0
  48. package/lib/typescript/src/components/Campfire.d.ts.map +1 -0
  49. package/lib/typescript/src/components/CircularGradient.d.ts +19 -0
  50. package/lib/typescript/src/components/CircularGradient.d.ts.map +1 -0
  51. package/lib/typescript/src/components/Iridescence.d.ts +11 -0
  52. package/lib/typescript/src/components/Iridescence.d.ts.map +1 -0
  53. package/lib/typescript/src/components/LinearGradient.d.ts +15 -0
  54. package/lib/typescript/src/components/LinearGradient.d.ts.map +1 -0
  55. package/lib/typescript/src/components/LiquidChrome.d.ts +17 -0
  56. package/lib/typescript/src/components/LiquidChrome.d.ts.map +1 -0
  57. package/lib/typescript/src/components/ShaderView/index.d.ts +3 -0
  58. package/lib/typescript/src/components/ShaderView/index.d.ts.map +1 -0
  59. package/lib/typescript/src/components/ShaderView/types.d.ts +15 -0
  60. package/lib/typescript/src/components/ShaderView/types.d.ts.map +1 -0
  61. package/lib/typescript/src/components/Silk.d.ts +17 -0
  62. package/lib/typescript/src/components/Silk.d.ts.map +1 -0
  63. package/lib/typescript/src/consts.d.ts +2 -0
  64. package/lib/typescript/src/consts.d.ts.map +1 -0
  65. package/lib/typescript/src/hooks/useClock.d.ts +3 -0
  66. package/lib/typescript/src/hooks/useClock.d.ts.map +1 -0
  67. package/lib/typescript/src/hooks/useWGPUSetup.d.ts +15 -0
  68. package/lib/typescript/src/hooks/useWGPUSetup.d.ts.map +1 -0
  69. package/lib/typescript/src/index.d.ts +12 -0
  70. package/lib/typescript/src/index.d.ts.map +1 -0
  71. package/lib/typescript/src/shaders/TRIANGLE_VERTEX_SHADER.d.ts +2 -0
  72. package/lib/typescript/src/shaders/TRIANGLE_VERTEX_SHADER.d.ts.map +1 -0
  73. package/lib/typescript/src/shaders/uniforms.d.ts +6 -0
  74. package/lib/typescript/src/shaders/uniforms.d.ts.map +1 -0
  75. package/lib/typescript/src/utils/backgroundRuntime.d.ts +3 -0
  76. package/lib/typescript/src/utils/backgroundRuntime.d.ts.map +1 -0
  77. package/lib/typescript/src/utils/colors.d.ts +22 -0
  78. package/lib/typescript/src/utils/colors.d.ts.map +1 -0
  79. package/lib/typescript/src/utils/initWebGPU.d.ts +23 -0
  80. package/lib/typescript/src/utils/initWebGPU.d.ts.map +1 -0
  81. package/package.json +174 -7
  82. package/src/components/Aurora.tsx +203 -0
  83. package/src/components/CalicoSwirl.tsx +167 -0
  84. package/src/components/Campfire.tsx +244 -0
  85. package/src/components/CircularGradient.tsx +76 -0
  86. package/src/components/Iridescence.tsx +67 -0
  87. package/src/components/LinearGradient.tsx +62 -0
  88. package/src/components/LiquidChrome.tsx +94 -0
  89. package/src/components/ShaderView/index.tsx +225 -0
  90. package/src/components/ShaderView/types.ts +15 -0
  91. package/src/components/Silk.tsx +102 -0
  92. package/src/consts.ts +152 -0
  93. package/src/hooks/useClock.ts +20 -0
  94. package/src/hooks/useWGPUSetup.tsx +73 -0
  95. package/src/index.tsx +23 -0
  96. package/src/shaders/TRIANGLE_VERTEX_SHADER.ts +17 -0
  97. package/src/shaders/uniforms.ts +17 -0
  98. package/src/utils/backgroundRuntime.ts +10 -0
  99. package/src/utils/colors.ts +117 -0
  100. package/src/utils/initWebGPU.ts +47 -0
@@ -0,0 +1,244 @@
1
+ import { useMemo } from 'react';
2
+ import type { ViewProps } from 'react-native';
3
+ import type { ColorInput } from '../utils/colors';
4
+ import ShaderView from './ShaderView';
5
+
6
+ type Props = ViewProps & {
7
+ /** The color tint for the fire effect. */
8
+ color?: ColorInput;
9
+ /** Animation speed multiplier. Default: 1.0 */
10
+ speed?: number;
11
+ /** Size of the sparks. Default: 1.0 */
12
+ sparkSize?: number;
13
+ /** Intensity of the fire. Default: 1.0 */
14
+ fireIntensity?: number;
15
+ /** Intensity of the smoke. Default: 1.0 */
16
+ smokeIntensity?: number;
17
+ };
18
+
19
+ export default function Campfire({
20
+ color = '#ffffff',
21
+ speed = 1.0,
22
+ sparkSize = 1.0,
23
+ fireIntensity = 1.0,
24
+ smokeIntensity = 1.0,
25
+ ...viewProps
26
+ }: Props) {
27
+ const colors = useMemo(() => [color], [color]);
28
+ const params = useMemo(
29
+ () => [sparkSize, fireIntensity, smokeIntensity],
30
+ [sparkSize, fireIntensity, smokeIntensity]
31
+ );
32
+
33
+ return (
34
+ <ShaderView
35
+ fragmentShader={CAMPFIRE_SHADER}
36
+ colors={colors}
37
+ params={params}
38
+ speed={speed}
39
+ isStatic={false}
40
+ {...viewProps}
41
+ />
42
+ );
43
+ }
44
+
45
+ const CAMPFIRE_SHADER = /* wgsl */ `
46
+ struct Uniforms {
47
+ resolution: vec4<f32>,
48
+ time: vec4<f32>,
49
+ color0: vec4<f32>,
50
+ color1: vec4<f32>,
51
+ params0: vec4<f32>,
52
+ params1: vec4<f32>,
53
+ };
54
+ @group(0) @binding(0) var<uniform> u: Uniforms;
55
+
56
+ fn mod289_3(x: vec3<f32>) -> vec3<f32> {
57
+ return x - floor(x * (1.0 / 289.0)) * 289.0;
58
+ }
59
+
60
+ fn mod289_4(x: vec4<f32>) -> vec4<f32> {
61
+ return x - floor(x * (1.0 / 289.0)) * 289.0;
62
+ }
63
+
64
+ fn permute(x: vec4<f32>) -> vec4<f32> {
65
+ return mod289_4(((x * 34.0) + 1.0) * x);
66
+ }
67
+
68
+ fn taylorInvSqrt(r: vec4<f32>) -> vec4<f32> {
69
+ return 1.79284291400159 - 0.85373472095314 * r;
70
+ }
71
+
72
+ fn snoise(v: vec3<f32>) -> f32 {
73
+ let C = vec2<f32>(1.0 / 6.0, 1.0 / 3.0);
74
+ let D = vec4<f32>(0.0, 0.5, 1.0, 2.0);
75
+
76
+ var i = floor(v + dot(v, vec3<f32>(C.y, C.y, C.y)));
77
+ let x0 = v - i + dot(i, vec3<f32>(C.x, C.x, C.x));
78
+
79
+ let g = step(vec3<f32>(x0.y, x0.z, x0.x), x0);
80
+ let l = 1.0 - g;
81
+ let i1 = min(g, vec3<f32>(l.z, l.x, l.y));
82
+ let i2 = max(g, vec3<f32>(l.z, l.x, l.y));
83
+
84
+ let x1 = x0 - i1 + vec3<f32>(C.x, C.x, C.x);
85
+ let x2 = x0 - i2 + vec3<f32>(C.y, C.y, C.y);
86
+ let x3 = x0 - vec3<f32>(D.y, D.y, D.y);
87
+
88
+ i = mod289_3(i);
89
+ let p = permute(permute(permute(
90
+ i.z + vec4<f32>(0.0, i1.z, i2.z, 1.0))
91
+ + i.y + vec4<f32>(0.0, i1.y, i2.y, 1.0))
92
+ + i.x + vec4<f32>(0.0, i1.x, i2.x, 1.0));
93
+
94
+ let n_ = 0.142857142857;
95
+ let ns = n_ * vec3<f32>(D.w, D.y, D.z) - vec3<f32>(D.x, D.z, D.x);
96
+
97
+ let j = p - 49.0 * floor(p * ns.z * ns.z);
98
+
99
+ let x_ = floor(j * ns.z);
100
+ let y_ = floor(j - 7.0 * x_);
101
+
102
+ let x = x_ * ns.x + vec4<f32>(ns.y, ns.y, ns.y, ns.y);
103
+ let y = y_ * ns.x + vec4<f32>(ns.y, ns.y, ns.y, ns.y);
104
+ let h = 1.0 - abs(x) - abs(y);
105
+
106
+ let b0 = vec4<f32>(x.x, x.y, y.x, y.y);
107
+ let b1 = vec4<f32>(x.z, x.w, y.z, y.w);
108
+
109
+ let s0 = floor(b0) * 2.0 + 1.0;
110
+ let s1 = floor(b1) * 2.0 + 1.0;
111
+ let sh = -step(h, vec4<f32>(0.0));
112
+
113
+ let a0 = vec4<f32>(b0.x, b0.z, b0.y, b0.w) + vec4<f32>(s0.x, s0.z, s0.y, s0.w) * vec4<f32>(sh.x, sh.x, sh.y, sh.y);
114
+ let a1 = vec4<f32>(b1.x, b1.z, b1.y, b1.w) + vec4<f32>(s1.x, s1.z, s1.y, s1.w) * vec4<f32>(sh.z, sh.z, sh.w, sh.w);
115
+
116
+ let p0 = vec3<f32>(a0.x, a0.y, h.x);
117
+ let p1 = vec3<f32>(a0.z, a0.w, h.y);
118
+ let p2 = vec3<f32>(a1.x, a1.y, h.z);
119
+ let p3 = vec3<f32>(a1.z, a1.w, h.w);
120
+
121
+ let norm = inverseSqrt(vec4<f32>(dot(p0, p0), dot(p1, p1), dot(p2, p2), dot(p3, p3)));
122
+ let p0n = p0 * norm.x;
123
+ let p1n = p1 * norm.y;
124
+ let p2n = p2 * norm.z;
125
+ let p3n = p3 * norm.w;
126
+
127
+ var m = max(0.6 - vec4<f32>(dot(x0, x0), dot(x1, x1), dot(x2, x2), dot(x3, x3)), vec4<f32>(0.0));
128
+ m = m * m;
129
+ return 42.0 * dot(m * m, vec4<f32>(dot(p0n, x0), dot(p1n, x1), dot(p2n, x2), dot(p3n, x3)));
130
+ }
131
+
132
+ fn prng(seed: vec2<f32>) -> f32 {
133
+ var s = fract(seed * vec2<f32>(5.3983, 5.4427));
134
+ s = s + dot(vec2<f32>(s.y, s.x), s + vec2<f32>(21.5351, 14.3137));
135
+ return fract(s.x * s.y * 95.4337);
136
+ }
137
+
138
+ const PI = 3.1415926535897932384626433832795;
139
+
140
+ fn noiseStack(pos: vec3<f32>, octaves: i32, falloff: f32) -> f32 {
141
+ var noise = snoise(pos);
142
+ var off = 1.0;
143
+ var p = pos;
144
+
145
+ if (octaves > 1) {
146
+ p = p * 2.0;
147
+ off = off * falloff;
148
+ noise = (1.0 - off) * noise + off * snoise(p);
149
+ }
150
+ if (octaves > 2) {
151
+ p = p * 2.0;
152
+ off = off * falloff;
153
+ noise = (1.0 - off) * noise + off * snoise(p);
154
+ }
155
+ if (octaves > 3) {
156
+ p = p * 2.0;
157
+ off = off * falloff;
158
+ noise = (1.0 - off) * noise + off * snoise(p);
159
+ }
160
+ return (1.0 + noise) / 2.0;
161
+ }
162
+
163
+ fn noiseStackUV(pos: vec3<f32>, octaves: i32, falloff: f32) -> vec2<f32> {
164
+ let displaceA = noiseStack(pos, octaves, falloff);
165
+ let displaceB = noiseStack(pos + vec3<f32>(3984.293, 423.21, 5235.19), octaves, falloff);
166
+ return vec2<f32>(displaceA, displaceB);
167
+ }
168
+
169
+ @fragment
170
+ fn main(@location(0) ndc: vec2<f32>) -> @location(0) vec4<f32> {
171
+ let time = u.time.x;
172
+ let sparkSize = u.params0.x;
173
+ let fireIntensity = u.params0.y;
174
+ let smokeIntensity = u.params0.z;
175
+
176
+ let resolution = u.resolution.xy;
177
+ let vUv = ndc * 0.5 + vec2<f32>(0.5, 0.5);
178
+ let fragCoord = vUv * resolution;
179
+
180
+ let xpart = fragCoord.x / resolution.x;
181
+ let ypart = fragCoord.y / resolution.y;
182
+
183
+ let clip = 210.0;
184
+ let ypartClip = fragCoord.y / clip;
185
+ let ypartClippedFalloff = clamp(2.0 - ypartClip, 0.0, 1.0);
186
+ let ypartClipped = min(ypartClip, 1.0);
187
+ let ypartClippedn = 1.0 - ypartClipped;
188
+
189
+ let xfuel = 1.0 - abs(2.0 * xpart - 1.0);
190
+
191
+ let timeSpeed = 0.5;
192
+ let realTime = timeSpeed * time;
193
+
194
+ let coordScaled = 0.01 * fragCoord;
195
+ let position = vec3<f32>(coordScaled, 0.0) + vec3<f32>(1223.0, 6434.0, 8425.0);
196
+ let flow = vec3<f32>(4.1 * (0.5 - xpart) * pow(ypartClippedn, 4.0), -2.0 * xfuel * pow(ypartClippedn, 64.0), 0.0);
197
+ let timing = realTime * vec3<f32>(0.0, -1.7, 1.1) + flow;
198
+
199
+ let displacePos = vec3<f32>(1.0, 0.5, 1.0) * 2.4 * position + realTime * vec3<f32>(0.01, -0.7, 1.3);
200
+ let displace3 = vec3<f32>(noiseStackUV(displacePos, 2, 0.4), 0.0);
201
+
202
+ let noiseCoord = (vec3<f32>(2.0, 1.0, 1.0) * position + timing + 0.4 * displace3) / 1.0;
203
+ let noise = noiseStack(noiseCoord, 3, 0.4);
204
+
205
+ let flames = pow(ypartClipped, 0.3 * xfuel) * pow(noise, 0.3 * xfuel);
206
+
207
+ let f = ypartClippedFalloff * pow(1.0 - flames * flames * flames, 8.0);
208
+ let fff = f * f * f;
209
+ var fire = 1.5 * vec3<f32>(f, fff, fff * fff) * fireIntensity;
210
+
211
+ let smokeNoise = 0.5 + snoise(0.4 * position + timing * vec3<f32>(1.0, 1.0, 0.2)) / 2.0;
212
+ let smoke = vec3<f32>(0.3 * pow(xfuel, 3.0) * pow(ypart, 2.0) * (smokeNoise + 0.4 * (1.0 - noise))) * smokeIntensity;
213
+
214
+ let sparkGridSize = 30.0;
215
+ var sparkCoord = fragCoord - vec2<f32>(0.0, 190.0 * realTime);
216
+ sparkCoord = sparkCoord - 30.0 * noiseStackUV(0.01 * vec3<f32>(sparkCoord, 30.0 * time), 1, 0.4);
217
+ sparkCoord = sparkCoord + 100.0 * flow.xy;
218
+
219
+ if (sparkCoord.y / sparkGridSize % 2.0 < 1.0) {
220
+ sparkCoord.x = sparkCoord.x + 0.5 * sparkGridSize;
221
+ }
222
+
223
+ let sparkGridIndex = vec2<f32>(floor(sparkCoord / sparkGridSize));
224
+ let sparkRandom = prng(sparkGridIndex);
225
+ let sparkLife = min(10.0 * (1.0 - min((sparkGridIndex.y + (190.0 * realTime / sparkGridSize)) / (24.0 - 20.0 * sparkRandom), 1.0)), 1.0);
226
+
227
+ var sparks = vec3<f32>(0.0);
228
+ if (sparkLife > 0.0) {
229
+ let sparkSizeScaled = xfuel * xfuel * sparkRandom * 0.08 * sparkSize;
230
+ let sparkRadians = 999.0 * sparkRandom * 2.0 * PI + 2.0 * time;
231
+ let sparkCircular = vec2<f32>(sin(sparkRadians), cos(sparkRadians));
232
+ let sparkOffset = (0.5 - sparkSizeScaled) * sparkGridSize * sparkCircular;
233
+ let sparkModulus = (sparkCoord + sparkOffset) % sparkGridSize - 0.5 * vec2<f32>(sparkGridSize);
234
+ let sparkLength = length(sparkModulus);
235
+ let sparksGray = max(0.0, 1.0 - sparkLength / (sparkSizeScaled * sparkGridSize));
236
+ sparks = sparkLife * sparksGray * vec3<f32>(1.0, 0.3, 0.0);
237
+ }
238
+
239
+ fire = fire * u.color0.rgb;
240
+
241
+ let finalColor = max(fire, sparks) + smoke;
242
+ return vec4<f32>(clamp(finalColor, vec3<f32>(0.0), vec3<f32>(1.0)), u.color0.a);
243
+ }
244
+ `;
@@ -0,0 +1,76 @@
1
+ import { useMemo } from 'react';
2
+ import type { ViewProps } from 'react-native';
3
+ import type { ColorInput } from '../utils/colors';
4
+ import ShaderView from './ShaderView';
5
+
6
+ type Props = ViewProps & {
7
+ /** The color at the center of the gradient. */
8
+ centerColor: ColorInput;
9
+ /** The color at the edge of the gradient. Default: transparent */
10
+ edgeColor?: ColorInput;
11
+ /** Horizontal center position (0-1). Default: 0.5 */
12
+ centerX?: number;
13
+ /** Vertical center position (0-1). Default: 0.5 */
14
+ centerY?: number;
15
+ /** Horizontal radius. Default: 0.5 */
16
+ sizeX?: number;
17
+ /** Vertical radius. Default: 0.5 */
18
+ sizeY?: number;
19
+ };
20
+
21
+ export default function CircularGradient({
22
+ centerColor,
23
+ edgeColor = 'rgba(0,0,0,0)',
24
+ centerX = 0.5,
25
+ centerY = 0.5,
26
+ sizeX = 0.5,
27
+ sizeY = 0.5,
28
+ ...viewProps
29
+ }: Props) {
30
+ const colors = useMemo(
31
+ () => [centerColor, edgeColor],
32
+ [centerColor, edgeColor]
33
+ );
34
+ const params = useMemo(
35
+ () => [centerX, centerY, sizeX, sizeY],
36
+ [centerX, centerY, sizeX, sizeY]
37
+ );
38
+
39
+ return (
40
+ <ShaderView
41
+ fragmentShader={CIRCULAR_GRADIENT_SHADER}
42
+ colors={colors}
43
+ params={params}
44
+ isStatic={true}
45
+ {...viewProps}
46
+ />
47
+ );
48
+ }
49
+
50
+ const CIRCULAR_GRADIENT_SHADER = /* wgsl */ `
51
+ struct Uniforms {
52
+ resolution: vec4<f32>,
53
+ time: vec4<f32>,
54
+ color0: vec4<f32>,
55
+ color1: vec4<f32>,
56
+ params0: vec4<f32>,
57
+ params1: vec4<f32>,
58
+ };
59
+ @group(0) @binding(0) var<uniform> u: Uniforms;
60
+
61
+ @fragment
62
+ fn main(@location(0) ndc: vec2<f32>) -> @location(0) vec4<f32> {
63
+ let uv = (ndc * 0.5) + vec2<f32>(0.5, 0.5);
64
+
65
+ let center = u.params0.xy;
66
+ let size = u.params0.zw;
67
+
68
+ let diff = uv - center;
69
+ let normalizedDiff = diff / size;
70
+ let dist = length(normalizedDiff);
71
+
72
+ let t = smoothstep(0.0, 1.0, dist);
73
+ let color = mix(u.color0, u.color1, t);
74
+ return color;
75
+ }
76
+ `;
@@ -0,0 +1,67 @@
1
+ import { useMemo } from 'react';
2
+ import type { ViewProps } from 'react-native';
3
+ import type { ColorInput } from '../utils/colors';
4
+ import ShaderView from './ShaderView';
5
+
6
+ type Props = ViewProps & {
7
+ /** The color tint for the iridescence effect. */
8
+ color?: ColorInput;
9
+ /** Animation speed multiplier. Default: 1.0 */
10
+ speed?: number;
11
+ };
12
+
13
+ export default function Iridescence({
14
+ color = '#ffffff',
15
+ speed = 1.0,
16
+ ...viewProps
17
+ }: Props) {
18
+ const colors = useMemo(() => [color], [color]);
19
+
20
+ return (
21
+ <ShaderView
22
+ fragmentShader={IRIDESCENCE_SHADER}
23
+ colors={colors}
24
+ params={[]}
25
+ speed={speed}
26
+ isStatic={false}
27
+ {...viewProps}
28
+ />
29
+ );
30
+ }
31
+
32
+ const IRIDESCENCE_SHADER = /* wgsl */ `
33
+ struct Uniforms {
34
+ resolution: vec4<f32>,
35
+ time: vec4<f32>,
36
+ color0: vec4<f32>,
37
+ color1: vec4<f32>,
38
+ params0: vec4<f32>,
39
+ params1: vec4<f32>,
40
+ };
41
+ @group(0) @binding(0) var<uniform> u: Uniforms;
42
+
43
+ @fragment
44
+ fn main(@location(0) ndc: vec2<f32>) -> @location(0) vec4<f32> {
45
+ let time = u.time.x;
46
+
47
+ let vUv = ndc * 0.5 + vec2<f32>(0.5, 0.5);
48
+ let mr = min(u.resolution.x, u.resolution.y);
49
+ var uv = (vUv * 2.0 - vec2<f32>(1.0, 1.0)) * (u.resolution.xy / mr);
50
+
51
+ var d = -time * 0.5;
52
+ var a = 0.0;
53
+ for (var i: f32 = 0.0; i < 8.0; i = i + 1.0) {
54
+ a = a + cos(i - d - a * uv.x);
55
+ d = d + sin(uv.y * i + a);
56
+ }
57
+ d = d + time * 0.5;
58
+
59
+ let c1 = cos(uv * vec2<f32>(d, a)) * 0.6 + 0.4;
60
+ let c2 = cos(a + d) * 0.5 + 0.5;
61
+ let col = vec3<f32>(c1.x, c1.y, c2);
62
+
63
+ let finalCol = cos(col * cos(vec3<f32>(d, a, 2.5)) * 0.5 + 0.5);
64
+ let coloredCol = finalCol * u.color0.rgb;
65
+ return vec4<f32>(clamp(coloredCol, vec3<f32>(0.0), vec3<f32>(1.0)), u.color0.a);
66
+ }
67
+ `;
@@ -0,0 +1,62 @@
1
+ import { useMemo } from 'react';
2
+ import type { ViewProps } from 'react-native';
3
+ import type { ColorInput } from '../utils/colors';
4
+ import ShaderView from './ShaderView';
5
+
6
+ type Props = ViewProps & {
7
+ /** The color of the start of the gradient. */
8
+ startColor: ColorInput;
9
+ /** The color of the end of the gradient. */
10
+ endColor: ColorInput;
11
+ /** The angle of the gradient in degrees (0-360). */
12
+ angle: number;
13
+ /** Rotation speed in degrees/second. 0 = static. Default: 0 */
14
+ speed?: number;
15
+ };
16
+
17
+ export default function LinearGradient({
18
+ startColor,
19
+ endColor,
20
+ angle,
21
+ speed = 0,
22
+ ...viewProps
23
+ }: Props) {
24
+ const colors = useMemo(() => [startColor, endColor], [startColor, endColor]);
25
+ const params = useMemo(() => [angle, speed], [angle, speed]);
26
+
27
+ return (
28
+ <ShaderView
29
+ fragmentShader={LINEAR_GRADIENT_SHADER}
30
+ colors={colors}
31
+ params={params}
32
+ speed={speed === 0 ? 0 : 1}
33
+ isStatic={speed === 0}
34
+ {...viewProps}
35
+ />
36
+ );
37
+ }
38
+
39
+ const LINEAR_GRADIENT_SHADER = /* wgsl */ `
40
+ struct Uniforms {
41
+ resolution: vec4<f32>,
42
+ time: vec4<f32>,
43
+ color0: vec4<f32>,
44
+ color1: vec4<f32>,
45
+ params0: vec4<f32>,
46
+ params1: vec4<f32>,
47
+ };
48
+ @group(0) @binding(0) var<uniform> u: Uniforms;
49
+
50
+ @fragment
51
+ fn main(@location(0) ndc: vec2<f32>) -> @location(0) vec4<f32> {
52
+ let uv = ndc * 0.5 + vec2<f32>(0.5, 0.5);
53
+ let baseAngle = u.params0.x;
54
+ let rotationSpeed = u.params0.y;
55
+ let angle = (baseAngle + u.time.x * rotationSpeed) * 3.14159265359 / 180.0;
56
+ let dir = vec2<f32>(cos(angle), sin(angle));
57
+ let fromCenter = uv - vec2<f32>(0.5, 0.5);
58
+ let corrected = vec2<f32>(fromCenter.x * u.resolution.z, fromCenter.y);
59
+ let t = clamp(dot(corrected, dir) + 0.5, 0.0, 1.0);
60
+ return mix(u.color0, u.color1, t);
61
+ }
62
+ `;
@@ -0,0 +1,94 @@
1
+ import { useMemo } from 'react';
2
+ import type { ViewProps } from 'react-native';
3
+ import type { ColorInput } from '../utils/colors';
4
+ import ShaderView from './ShaderView';
5
+
6
+ type Props = ViewProps & {
7
+ /** The base color for the liquid chrome effect. */
8
+ color?: ColorInput;
9
+ /** Animation speed multiplier. Default: 0.2 */
10
+ speed?: number;
11
+ /** Amplitude of the distortion. Default: 0.3 */
12
+ amplitude?: number;
13
+ /** Horizontal frequency. Default: 3 */
14
+ frequencyX?: number;
15
+ /** Vertical frequency. Default: 3 */
16
+ frequencyY?: number;
17
+ };
18
+
19
+ export default function LiquidChrome({
20
+ color = '#1a1a1a',
21
+ speed = 0.2,
22
+ amplitude = 0.3,
23
+ frequencyX = 3,
24
+ frequencyY = 3,
25
+ ...viewProps
26
+ }: Props) {
27
+ const colors = useMemo(() => [color], [color]);
28
+ const params = useMemo(
29
+ () => [amplitude, frequencyX, frequencyY],
30
+ [amplitude, frequencyX, frequencyY]
31
+ );
32
+
33
+ return (
34
+ <ShaderView
35
+ fragmentShader={LIQUID_CHROME_SHADER}
36
+ colors={colors}
37
+ params={params}
38
+ speed={speed}
39
+ isStatic={false}
40
+ {...viewProps}
41
+ />
42
+ );
43
+ }
44
+
45
+ const LIQUID_CHROME_SHADER = /* wgsl */ `
46
+ struct Uniforms {
47
+ resolution: vec4<f32>,
48
+ time: vec4<f32>,
49
+ color0: vec4<f32>,
50
+ color1: vec4<f32>,
51
+ params0: vec4<f32>,
52
+ params1: vec4<f32>,
53
+ };
54
+ @group(0) @binding(0) var<uniform> u: Uniforms;
55
+
56
+ fn renderImage(uvCoord: vec2<f32>) -> vec4<f32> {
57
+ let resolution2D = u.resolution.xy;
58
+ let time = u.time.x;
59
+ let amplitude = u.params0.x;
60
+ let frequencyX = u.params0.y;
61
+ let frequencyY = u.params0.z;
62
+
63
+ let fragCoord = uvCoord * resolution2D;
64
+ var uv = (2.0 * fragCoord - resolution2D) / min(u.resolution.x, u.resolution.y);
65
+
66
+ for (var i: f32 = 1.0; i < 10.0; i = i + 1.0) {
67
+ uv.x = uv.x + amplitude / i * cos(i * frequencyX * uv.y + time);
68
+ uv.y = uv.y + amplitude / i * cos(i * frequencyY * uv.x + time);
69
+ }
70
+
71
+ let baseColor = u.color0;
72
+ let color = baseColor.rgb / abs(sin(time - uv.y - uv.x));
73
+ return vec4<f32>(color, baseColor.a);
74
+ }
75
+
76
+ @fragment
77
+ fn main(@location(0) ndc: vec2<f32>) -> @location(0) vec4<f32> {
78
+ let vUv = ndc * 0.5 + vec2<f32>(0.5, 0.5);
79
+ let resolution2D = u.resolution.xy;
80
+
81
+ var col = vec4<f32>(0.0);
82
+ var samples = 0;
83
+
84
+ for (var i: i32 = -1; i <= 1; i = i + 1) {
85
+ for (var j: i32 = -1; j <= 1; j = j + 1) {
86
+ let offset = vec2<f32>(f32(i), f32(j)) * (1.0 / min(resolution2D.x, resolution2D.y));
87
+ col = col + renderImage(vUv + offset);
88
+ samples = samples + 1;
89
+ }
90
+ }
91
+
92
+ return col / f32(samples);
93
+ }
94
+ `;