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,225 @@
1
+ import { PixelRatio, StyleSheet } from 'react-native';
2
+ import { Canvas } from 'react-native-wgpu';
3
+ import { useEffect, useRef } from 'react';
4
+ import { createSynchronizable, scheduleOnRuntime } from 'react-native-worklets';
5
+ import { colorToVec4 } from '../../utils/colors';
6
+ import { useWGPUSetup } from '../../hooks/useWGPUSetup';
7
+ import { TRIANGLE_VERTEX_SHADER } from '../../shaders/TRIANGLE_VERTEX_SHADER';
8
+ import {
9
+ UNIFORM_BUFFER_SIZE,
10
+ UNIFORM_FLOAT_COUNT,
11
+ } from '../../shaders/uniforms';
12
+ import type { ShaderViewProps } from './types';
13
+
14
+ // Synchronizable layout: [c0r,c0g,c0b,c0a, c1r,c1g,c1b,c1a, speed, p0..p7, alive]
15
+ // Total: 4 + 4 + 1 + 8 + 1 = 18 floats
16
+ const SYNC_SIZE = 18;
17
+ const IDX_SPEED = 8;
18
+ const IDX_PARAMS = 9; // 9..16
19
+ const IDX_ALIVE = 17;
20
+
21
+ export default function ShaderView({
22
+ fragmentShader,
23
+ colors = [],
24
+ speed = 1.0,
25
+ params = [],
26
+ isStatic = false,
27
+ style,
28
+ ...viewProps
29
+ }: ShaderViewProps) {
30
+ const { canvasRef, runtime, resources } = useWGPUSetup();
31
+
32
+ const propsSync = useRef(
33
+ createSynchronizable<Float64Array>(new Float64Array(SYNC_SIZE))
34
+ ).current;
35
+
36
+ // Convert props to flat floats and push to synchronizable
37
+ useEffect(() => {
38
+ const data = new Float64Array(SYNC_SIZE);
39
+
40
+ // color0 (indices 0-3)
41
+ if (colors[0] !== undefined) {
42
+ const c0 = colorToVec4(colors[0]);
43
+ data[0] = c0.r;
44
+ data[1] = c0.g;
45
+ data[2] = c0.b;
46
+ data[3] = c0.a;
47
+ }
48
+
49
+ // color1 (indices 4-7)
50
+ if (colors[1] !== undefined) {
51
+ const c1 = colorToVec4(colors[1]);
52
+ data[4] = c1.r;
53
+ data[5] = c1.g;
54
+ data[6] = c1.b;
55
+ data[7] = c1.a;
56
+ }
57
+
58
+ // speed
59
+ data[IDX_SPEED] = speed;
60
+
61
+ // params (indices 9-16)
62
+ for (let i = 0; i < 8; i++) {
63
+ data[IDX_PARAMS + i] = params[i] ?? 0;
64
+ }
65
+
66
+ // alive
67
+ data[IDX_ALIVE] = 1;
68
+
69
+ propsSync.setBlocking(() => data);
70
+ }, [colors, speed, params, propsSync]);
71
+
72
+ // Signal cleanup on unmount
73
+ useEffect(() => {
74
+ return () => {
75
+ propsSync.setBlocking((prev) => {
76
+ prev[IDX_ALIVE] = 0;
77
+ return prev;
78
+ });
79
+ };
80
+ }, [propsSync]);
81
+
82
+ // Start render loop when GPU resources are ready
83
+ useEffect(() => {
84
+ if (!resources) {
85
+ return;
86
+ }
87
+
88
+ const { device, context, presentationFormat } = resources;
89
+ const dpr = PixelRatio.get();
90
+
91
+ scheduleOnRuntime(runtime, () => {
92
+ 'worklet';
93
+
94
+ // Create pipeline once
95
+ const pipeline = device.createRenderPipeline({
96
+ layout: 'auto',
97
+ vertex: {
98
+ module: device.createShaderModule({ code: TRIANGLE_VERTEX_SHADER }),
99
+ entryPoint: 'main',
100
+ },
101
+ fragment: {
102
+ module: device.createShaderModule({ code: fragmentShader }),
103
+ entryPoint: 'main',
104
+ targets: [{ format: presentationFormat }],
105
+ },
106
+ primitive: { topology: 'triangle-list' },
107
+ });
108
+
109
+ // Create uniform buffer + bind group once, reuse via writeBuffer
110
+ const uniformBuffer = device.createBuffer({
111
+ size: UNIFORM_BUFFER_SIZE,
112
+ usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
113
+ });
114
+
115
+ const bindGroup = device.createBindGroup({
116
+ layout: pipeline.getBindGroupLayout(0),
117
+ entries: [{ binding: 0, resource: { buffer: uniformBuffer } }],
118
+ });
119
+
120
+ const uniformData = new Float32Array(UNIFORM_FLOAT_COUNT);
121
+ let accumulatedTime = 0;
122
+ let lastTimestamp = 0;
123
+
124
+ function render(timestamp: number) {
125
+ const props = propsSync.getDirty();
126
+ if (props[IDX_ALIVE] === 0) {
127
+ return;
128
+ }
129
+
130
+ // Compute dt
131
+ const dt = lastTimestamp === 0 ? 0 : (timestamp - lastTimestamp) / 1000;
132
+ lastTimestamp = timestamp;
133
+
134
+ // Accumulate time with speed
135
+ const currentSpeed = props[IDX_SPEED]!;
136
+ accumulatedTime += dt * currentSpeed;
137
+
138
+ // Resolution
139
+ const canvas = context.canvas as typeof context.canvas & {
140
+ width: number;
141
+ height: number;
142
+ };
143
+ const width = canvas.width || 1;
144
+ const height = canvas.height || 1;
145
+ const aspect = width / height;
146
+
147
+ // Fill uniform data (6 × vec4 = 24 floats)
148
+ // resolution: vec4<f32>
149
+ uniformData[0] = width;
150
+ uniformData[1] = height;
151
+ uniformData[2] = aspect;
152
+ uniformData[3] = dpr;
153
+
154
+ // time: vec4<f32>
155
+ uniformData[4] = accumulatedTime;
156
+ uniformData[5] = dt;
157
+ uniformData[6] = 0;
158
+ uniformData[7] = 0;
159
+
160
+ // color0: vec4<f32>
161
+ uniformData[8] = props[0]!;
162
+ uniformData[9] = props[1]!;
163
+ uniformData[10] = props[2]!;
164
+ uniformData[11] = props[3]!;
165
+
166
+ // color1: vec4<f32>
167
+ uniformData[12] = props[4]!;
168
+ uniformData[13] = props[5]!;
169
+ uniformData[14] = props[6]!;
170
+ uniformData[15] = props[7]!;
171
+
172
+ // params0: vec4<f32>
173
+ uniformData[16] = props[IDX_PARAMS]!;
174
+ uniformData[17] = props[IDX_PARAMS + 1]!;
175
+ uniformData[18] = props[IDX_PARAMS + 2]!;
176
+ uniformData[19] = props[IDX_PARAMS + 3]!;
177
+
178
+ // params1: vec4<f32>
179
+ uniformData[20] = props[IDX_PARAMS + 4]!;
180
+ uniformData[21] = props[IDX_PARAMS + 5]!;
181
+ uniformData[22] = props[IDX_PARAMS + 6]!;
182
+ uniformData[23] = props[IDX_PARAMS + 7]!;
183
+
184
+ device.queue.writeBuffer(uniformBuffer, 0, uniformData);
185
+
186
+ const commandEncoder = device.createCommandEncoder();
187
+ const textureView = context.getCurrentTexture().createView();
188
+ const passEncoder = commandEncoder.beginRenderPass({
189
+ colorAttachments: [
190
+ {
191
+ view: textureView,
192
+ clearValue: [0, 0, 0, 1],
193
+ loadOp: 'clear',
194
+ storeOp: 'store',
195
+ },
196
+ ],
197
+ });
198
+
199
+ passEncoder.setPipeline(pipeline);
200
+ passEncoder.setBindGroup(0, bindGroup);
201
+ passEncoder.draw(3);
202
+ passEncoder.end();
203
+
204
+ device.queue.submit([commandEncoder.finish()]);
205
+ context.present();
206
+
207
+ if (!isStatic) {
208
+ requestAnimationFrame(render);
209
+ }
210
+ }
211
+
212
+ requestAnimationFrame(render);
213
+ });
214
+ }, [resources, runtime, propsSync, fragmentShader, isStatic]);
215
+
216
+ return (
217
+ <Canvas ref={canvasRef} style={[styles.canvas, style]} {...viewProps} />
218
+ );
219
+ }
220
+
221
+ const styles = StyleSheet.create({
222
+ canvas: {
223
+ flex: 1,
224
+ },
225
+ });
@@ -0,0 +1,15 @@
1
+ import type { ViewProps } from 'react-native';
2
+ import type { ColorInput } from '../../utils/colors';
3
+
4
+ export type ShaderViewProps = ViewProps & {
5
+ /** WGSL fragment shader source (must declare the Uniforms struct) */
6
+ fragmentShader: string;
7
+ /** Array of colors mapped to u.color0, u.color1 (max 2). Default: [] */
8
+ colors?: ColorInput[];
9
+ /** Time multiplier — controls animation speed. Default: 1.0 */
10
+ speed?: number;
11
+ /** Up to 8 shader-specific floats mapped to u.params0.xyzw and u.params1.xyzw */
12
+ params?: number[];
13
+ /** Render once then stop the RAF loop. Default: false */
14
+ isStatic?: boolean;
15
+ };
@@ -0,0 +1,102 @@
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 silk effect. */
8
+ color?: ColorInput;
9
+ /** Animation speed multiplier. Default: 1.0 */
10
+ speed?: number;
11
+ /** Scale of the pattern. Default: 1.0 */
12
+ scale?: number;
13
+ /** Rotation angle in radians. Default: 0.0 */
14
+ rotation?: number;
15
+ /** Intensity of the noise grain. Default: 1.5 */
16
+ noiseIntensity?: number;
17
+ };
18
+
19
+ export default function Silk({
20
+ color = '#7B7481',
21
+ speed = 1.0,
22
+ scale = 1.0,
23
+ rotation = 0.0,
24
+ noiseIntensity = 1.5,
25
+ ...viewProps
26
+ }: Props) {
27
+ const colors = useMemo(() => [color], [color]);
28
+ const params = useMemo(
29
+ () => [scale, rotation, noiseIntensity],
30
+ [scale, rotation, noiseIntensity]
31
+ );
32
+
33
+ return (
34
+ <ShaderView
35
+ fragmentShader={SILK_SHADER}
36
+ colors={colors}
37
+ params={params}
38
+ speed={speed}
39
+ isStatic={false}
40
+ {...viewProps}
41
+ />
42
+ );
43
+ }
44
+
45
+ const SILK_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
+ const e = 2.71828182845904523536;
57
+
58
+ fn noise(texCoord: vec2<f32>) -> f32 {
59
+ let G = e;
60
+ let r = (G * sin(G * texCoord));
61
+ return fract(r.x * r.y * (1.0 + texCoord.x));
62
+ }
63
+
64
+ fn rotateUvs(uv: vec2<f32>, angle: f32) -> vec2<f32> {
65
+ let c = cos(angle);
66
+ let s = sin(angle);
67
+ let rot = mat2x2<f32>(c, -s, s, c);
68
+ return rot * uv;
69
+ }
70
+
71
+ @fragment
72
+ fn main(@location(0) ndc: vec2<f32>) -> @location(0) vec4<f32> {
73
+ let time = u.time.x;
74
+ let scale = u.params0.x;
75
+ let rotation = u.params0.y;
76
+ let noiseIntensity = u.params0.z;
77
+
78
+ let resolution2D = u.resolution.xy;
79
+ let vUv = ndc * 0.5 + vec2<f32>(0.5, 0.5);
80
+ let fragCoord = vUv * resolution2D;
81
+
82
+ let rnd = noise(fragCoord);
83
+ let uv = rotateUvs(vUv * scale, rotation);
84
+ var tex = uv * scale;
85
+ let tOffset = time;
86
+
87
+ tex.y = tex.y + 0.03 * sin(8.0 * tex.x - tOffset);
88
+
89
+ let pattern = 0.6 +
90
+ 0.4 * sin(5.0 * (tex.x + tex.y +
91
+ cos(3.0 * tex.x + 5.0 * tex.y) +
92
+ 0.02 * tOffset) +
93
+ sin(20.0 * (tex.x + tex.y - 0.1 * tOffset)));
94
+
95
+ var col = u.color0 * vec4<f32>(pattern, pattern, pattern, 1.0) -
96
+ vec4<f32>(rnd / 15.0 * noiseIntensity, rnd / 15.0 * noiseIntensity,
97
+ rnd / 15.0 * noiseIntensity, 0.0);
98
+ col.a = u.color0.a;
99
+
100
+ return clamp(col, vec4<f32>(0.0), vec4<f32>(1.0));
101
+ }
102
+ `;
package/src/consts.ts ADDED
@@ -0,0 +1,152 @@
1
+ export const NAMED_COLORS: Record<string, number> = {
2
+ // Basic colors
3
+ black: 0x000000,
4
+ white: 0xffffff,
5
+ red: 0xff0000,
6
+ green: 0x008000,
7
+ blue: 0x0000ff,
8
+ yellow: 0xffff00,
9
+ cyan: 0x00ffff,
10
+ magenta: 0xff00ff,
11
+ silver: 0xc0c0c0,
12
+ gray: 0x808080,
13
+ grey: 0x808080,
14
+ maroon: 0x800000,
15
+ olive: 0x808000,
16
+ lime: 0x00ff00,
17
+ aqua: 0x00ffff,
18
+ teal: 0x008080,
19
+ navy: 0x000080,
20
+ fuchsia: 0xff00ff,
21
+ purple: 0x800080,
22
+
23
+ // Extended colors
24
+ aliceblue: 0xf0f8ff,
25
+ antiquewhite: 0xfaebd7,
26
+ aquamarine: 0x7fffd4,
27
+ azure: 0xf0ffff,
28
+ beige: 0xf5f5dc,
29
+ bisque: 0xffe4c4,
30
+ blanchedalmond: 0xffebcd,
31
+ blueviolet: 0x8a2be2,
32
+ brown: 0xa52a2a,
33
+ burlywood: 0xdeb887,
34
+ cadetblue: 0x5f9ea0,
35
+ chartreuse: 0x7fff00,
36
+ chocolate: 0xd2691e,
37
+ coral: 0xff7f50,
38
+ cornflowerblue: 0x6495ed,
39
+ cornsilk: 0xfff8dc,
40
+ crimson: 0xdc143c,
41
+ darkblue: 0x00008b,
42
+ darkcyan: 0x008b8b,
43
+ darkgoldenrod: 0xb8860b,
44
+ darkgray: 0xa9a9a9,
45
+ darkgrey: 0xa9a9a9,
46
+ darkgreen: 0x006400,
47
+ darkkhaki: 0xbdb76b,
48
+ darkmagenta: 0x8b008b,
49
+ darkolivegreen: 0x556b2f,
50
+ darkorange: 0xff8c00,
51
+ darkorchid: 0x9932cc,
52
+ darkred: 0x8b0000,
53
+ darksalmon: 0xe9967a,
54
+ darkseagreen: 0x8fbc8f,
55
+ darkslateblue: 0x483d8b,
56
+ darkslategray: 0x2f4f4f,
57
+ darkslategrey: 0x2f4f4f,
58
+ darkturquoise: 0x00ced1,
59
+ darkviolet: 0x9400d3,
60
+ deeppink: 0xff1493,
61
+ deepskyblue: 0x00bfff,
62
+ dimgray: 0x696969,
63
+ dimgrey: 0x696969,
64
+ dodgerblue: 0x1e90ff,
65
+ firebrick: 0xb22222,
66
+ floralwhite: 0xfffaf0,
67
+ forestgreen: 0x228b22,
68
+ gainsboro: 0xdcdcdc,
69
+ ghostwhite: 0xf8f8ff,
70
+ gold: 0xffd700,
71
+ goldenrod: 0xdaa520,
72
+ greenyellow: 0xadff2f,
73
+ honeydew: 0xf0fff0,
74
+ hotpink: 0xff69b4,
75
+ indianred: 0xcd5c5c,
76
+ indigo: 0x4b0082,
77
+ ivory: 0xfffff0,
78
+ khaki: 0xf0e68c,
79
+ lavender: 0xe6e6fa,
80
+ lavenderblush: 0xfff0f5,
81
+ lawngreen: 0x7cfc00,
82
+ lemonchiffon: 0xfffacd,
83
+ lightblue: 0xadd8e6,
84
+ lightcoral: 0xf08080,
85
+ lightcyan: 0xe0ffff,
86
+ lightgoldenrodyellow: 0xfafad2,
87
+ lightgray: 0xd3d3d3,
88
+ lightgrey: 0xd3d3d3,
89
+ lightgreen: 0x90ee90,
90
+ lightpink: 0xffb6c1,
91
+ lightsalmon: 0xffa07a,
92
+ lightseagreen: 0x20b2aa,
93
+ lightskyblue: 0x87cefa,
94
+ lightslategray: 0x778899,
95
+ lightslategrey: 0x778899,
96
+ lightsteelblue: 0xb0c4de,
97
+ lightyellow: 0xffffe0,
98
+ limegreen: 0x32cd32,
99
+ linen: 0xfaf0e6,
100
+ mediumaquamarine: 0x66cdaa,
101
+ mediumblue: 0x0000cd,
102
+ mediumorchid: 0xba55d3,
103
+ mediumpurple: 0x9370db,
104
+ mediumseagreen: 0x3cb371,
105
+ mediumslateblue: 0x7b68ee,
106
+ mediumspringgreen: 0x00fa9a,
107
+ mediumturquoise: 0x48d1cc,
108
+ mediumvioletred: 0xc71585,
109
+ midnightblue: 0x191970,
110
+ mintcream: 0xf5fffa,
111
+ mistyrose: 0xffe4e1,
112
+ moccasin: 0xffe4b5,
113
+ navajowhite: 0xffdead,
114
+ oldlace: 0xfdf5e6,
115
+ olivedrab: 0x6b8e23,
116
+ orange: 0xffa500,
117
+ orangered: 0xff4500,
118
+ orchid: 0xda70d6,
119
+ palegoldenrod: 0xeee8aa,
120
+ palegreen: 0x98fb98,
121
+ paleturquoise: 0xafeeee,
122
+ palevioletred: 0xdb7093,
123
+ papayawhip: 0xffefd5,
124
+ peachpuff: 0xffdab9,
125
+ peru: 0xcd853f,
126
+ pink: 0xffc0cb,
127
+ plum: 0xdda0dd,
128
+ powderblue: 0xb0e0e6,
129
+ rosybrown: 0xbc8f8f,
130
+ royalblue: 0x4169e1,
131
+ saddlebrown: 0x8b4513,
132
+ salmon: 0xfa8072,
133
+ sandybrown: 0xf4a460,
134
+ seagreen: 0x2e8b57,
135
+ seashell: 0xfff5ee,
136
+ sienna: 0xa0522d,
137
+ skyblue: 0x87ceeb,
138
+ slateblue: 0x6a5acd,
139
+ slategray: 0x708090,
140
+ slategrey: 0x708090,
141
+ snow: 0xfffafa,
142
+ springgreen: 0x00ff7f,
143
+ steelblue: 0x4682b4,
144
+ tan: 0xd2b48c,
145
+ thistle: 0xd8bfd8,
146
+ tomato: 0xff6347,
147
+ turquoise: 0x40e0d0,
148
+ violet: 0xee82ee,
149
+ wheat: 0xf5deb3,
150
+ whitesmoke: 0xf5f5f5,
151
+ yellowgreen: 0x9acd32,
152
+ };
@@ -0,0 +1,20 @@
1
+ import { useCallback } from 'react';
2
+ import {
3
+ useFrameCallback,
4
+ useSharedValue,
5
+ type FrameInfo,
6
+ type SharedValue,
7
+ } from 'react-native-reanimated';
8
+
9
+ export function useClock(): SharedValue<number> {
10
+ const clock = useSharedValue(0);
11
+ const callback = useCallback(
12
+ (info: FrameInfo) => {
13
+ 'worklet';
14
+ clock.value = info.timeSinceFirstFrame;
15
+ },
16
+ [clock]
17
+ );
18
+ useFrameCallback(callback);
19
+ return clock;
20
+ }
@@ -0,0 +1,73 @@
1
+ import { PixelRatio } from 'react-native';
2
+ import {
3
+ useCanvasRef,
4
+ type CanvasRef,
5
+ type RNCanvasContext,
6
+ } from 'react-native-wgpu';
7
+ import { useEffect, useState } from 'react';
8
+ import type { WorkletRuntime } from 'react-native-worklets';
9
+ import { initWebGPU } from '../utils/initWebGPU';
10
+ import { BackgroundRuntime } from '../utils/backgroundRuntime';
11
+
12
+ type GPUResources = {
13
+ device: GPUDevice;
14
+ context: RNCanvasContext;
15
+ presentationFormat: GPUTextureFormat;
16
+ };
17
+
18
+ type WGPUSetupResult = {
19
+ canvasRef: React.RefObject<CanvasRef>;
20
+ runtime: WorkletRuntime;
21
+ resources: GPUResources | null;
22
+ };
23
+
24
+ export function useWGPUSetup(): WGPUSetupResult {
25
+ const canvasRef = useCanvasRef();
26
+ const [resources, setResources] = useState<GPUResources | null>(null);
27
+ const runtime = BackgroundRuntime;
28
+
29
+ useEffect(() => {
30
+ let cancelled = false;
31
+
32
+ (async () => {
33
+ const adapter = await navigator.gpu.requestAdapter();
34
+ if (!adapter || cancelled) {
35
+ return;
36
+ }
37
+
38
+ const device = await adapter.requestDevice();
39
+ if (cancelled) {
40
+ return;
41
+ }
42
+
43
+ const context = canvasRef.current!.getContext('webgpu')!;
44
+ const canvas = context.canvas as typeof context.canvas & {
45
+ width: number;
46
+ height: number;
47
+ };
48
+ const dpr = PixelRatio.get();
49
+ canvas.width = canvas.width * dpr;
50
+ canvas.height = canvas.height * dpr;
51
+
52
+ const presentationFormat = navigator.gpu.getPreferredCanvasFormat();
53
+ context.configure({
54
+ device,
55
+ format: presentationFormat,
56
+ alphaMode: 'premultiplied',
57
+ });
58
+
59
+ initWebGPU(runtime);
60
+
61
+ if (!cancelled) {
62
+ setResources({ device, context, presentationFormat });
63
+ }
64
+ })();
65
+
66
+ return () => {
67
+ cancelled = true;
68
+ };
69
+ // eslint-disable-next-line react-hooks/exhaustive-deps
70
+ }, []);
71
+
72
+ return { canvasRef, runtime, resources };
73
+ }
package/src/index.tsx ADDED
@@ -0,0 +1,23 @@
1
+ import CircularGradient from './components/CircularGradient';
2
+ import LinearGradient from './components/LinearGradient';
3
+ import ShaderView from './components/ShaderView';
4
+ import Iridescence from './components/Iridescence';
5
+ import LiquidChrome from './components/LiquidChrome';
6
+ import Silk from './components/Silk';
7
+ import Campfire from './components/Campfire';
8
+ import CalicoSwirl from './components/CalicoSwirl';
9
+ import Aurora from './components/Aurora';
10
+
11
+ export type { ShaderViewProps } from './components/ShaderView/types';
12
+
13
+ export {
14
+ CircularGradient,
15
+ LinearGradient,
16
+ ShaderView,
17
+ Iridescence,
18
+ LiquidChrome,
19
+ Silk,
20
+ Campfire,
21
+ CalicoSwirl,
22
+ Aurora,
23
+ };
@@ -0,0 +1,17 @@
1
+ export const TRIANGLE_VERTEX_SHADER = /* wgsl */ `
2
+ struct VSOut {
3
+ @builtin(position) pos: vec4<f32>,
4
+ @location(0) ndc: vec2<f32>,
5
+ };
6
+
7
+ @vertex
8
+ fn main(@builtin(vertex_index) vid: u32) -> VSOut {
9
+ var p = array<vec2<f32>,3>(
10
+ vec2<f32>(-1.0,-3.0), vec2<f32>(-1.0,1.0), vec2<f32>(3.0,1.0)
11
+ );
12
+ var o: VSOut;
13
+ o.pos = vec4<f32>(p[vid], 0, 1.0);
14
+ o.ndc = p[vid];
15
+ return o;
16
+ }
17
+ `;
@@ -0,0 +1,17 @@
1
+ /** 96 bytes = 6 × vec4<f32> */
2
+ export const UNIFORM_BUFFER_SIZE = 96;
3
+
4
+ /** Number of float32 values in the uniform buffer */
5
+ export const UNIFORM_FLOAT_COUNT = UNIFORM_BUFFER_SIZE / 4; // 24
6
+
7
+ export const UNIFORMS_WGSL = /* wgsl */ `
8
+ struct Uniforms {
9
+ resolution: vec4<f32>, // (width, height, aspect, pixelRatio)
10
+ time: vec4<f32>, // (seconds, dt, 0, 0)
11
+ color0: vec4<f32>, // colors[0] RGBA
12
+ color1: vec4<f32>, // colors[1] RGBA
13
+ params0: vec4<f32>, // params[0..3]
14
+ params1: vec4<f32>, // params[4..7]
15
+ };
16
+ @group(0) @binding(0) var<uniform> u: Uniforms;
17
+ `;
@@ -0,0 +1,10 @@
1
+ import { createWorkletRuntime, scheduleOnRuntime } from 'react-native-worklets';
2
+
3
+ export const BackgroundRuntime = createWorkletRuntime({
4
+ name: 'react-native-effects',
5
+ });
6
+
7
+ export function runOnBackground(callback: (...args: any[]) => void) {
8
+ 'worklet';
9
+ return scheduleOnRuntime(BackgroundRuntime, callback);
10
+ }