chroma-noise 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,11 +2,15 @@
2
2
 
3
3
  Gradient noise for Svelte 5.
4
4
 
5
- At an early stage of development.
5
+ ### New in 0.0.2
6
+ - types
7
+ - new modes: swirl and radial waves
8
+ - removed unused dependencies
6
9
 
7
- ### Roadmap
8
- - [] docs
9
- - [] optimization
10
10
 
11
11
  ### Site & playground
12
- https://chromanoise.netlify.app
12
+ https://chromanoise.netlify.app
13
+
14
+ ### Roadmap
15
+ - [] docs
16
+ - [] fix grain size option
@@ -1,221 +1,177 @@
1
- <script lang="ts">
2
- import vert from './gradient.vert.glsl?raw';
3
- import frag from './gradient.frag.glsl?raw';
4
- import { onMount, onDestroy } from 'svelte';
5
- import { hexToRgb } from 'ventoui-utils';
6
- import { browser } from '$app/environment';
7
-
8
- interface Props {
9
- points?: { color: string; x: number; y: number }[];
10
- radius?: number;
11
- timeAmount?: number;
12
- intensity?: number;
13
- warpMode?: number;
14
- warpSize?: number;
15
- warpAmount?: number;
16
- grainAmount?: number;
17
- grainSize?: number;
18
- seed?: number;
19
- }
20
-
21
- let {
22
- points = [],
23
- radius = 0.6,
24
- timeAmount = 1.0,
25
- intensity = 1.0,
26
- warpMode = 0,
27
- warpSize = 1.0,
28
- warpAmount = 0.0,
29
- grainAmount = 0.0,
30
- grainSize = 1.0,
31
- seed = Math.random()
32
- }: Props = $props();
33
-
34
- export const MAX_POINTS = 12;
35
-
36
- let canvas: HTMLCanvasElement | undefined = $state();
37
- let gl: WebGLRenderingContext | undefined = $state();
38
- let program: WebGLProgram | undefined = $state();
39
-
40
- let u_resolution: WebGLUniformLocation;
41
- let u_time: WebGLUniformLocation;
42
- let u_count: WebGLUniformLocation;
43
- let u_colors: WebGLUniformLocation;
44
- let u_positions: WebGLUniformLocation;
45
- let u_radius: WebGLUniformLocation;
46
- let u_intensity: WebGLUniformLocation;
47
- let u_warpMode: WebGLUniformLocation;
48
- let u_warpSize: WebGLUniformLocation;
49
- let u_warpAmount: WebGLUniformLocation;
50
- let u_grainAmount : WebGLUniformLocation;
51
- let u_grainSize : WebGLUniformLocation;
52
- let u_grainAnimate : WebGLUniformLocation;
53
- let u_seed: WebGLUniformLocation;
54
-
55
- let frameId: number;
56
- let running = true;
57
-
58
- let lastTime = 0;
59
- let animTime = 0;
60
-
61
- function createShader(gl: WebGLRenderingContext, type: number, src: string) {
62
- const s = gl.createShader(type)!;
63
- gl.shaderSource(s, src);
64
- gl.compileShader(s);
65
- if (!gl.getShaderParameter(s, gl.COMPILE_STATUS)) {
66
- console.error(gl.getShaderInfoLog(s));
67
- gl.deleteShader(s);
68
- throw new Error('Shader compile error');
69
- }
70
- return s;
71
- }
72
- function createProgram(gl: WebGLRenderingContext, vsSrc: string, fsSrc: string) {
73
- const vs = createShader(gl, gl.VERTEX_SHADER, vsSrc);
74
- const fs = createShader(gl, gl.FRAGMENT_SHADER, fsSrc);
75
- const p = gl.createProgram()!;
76
- gl.attachShader(p, vs);
77
- gl.attachShader(p, fs);
78
- gl.linkProgram(p);
79
- if (!gl.getProgramParameter(p, gl.LINK_STATUS)) {
80
- console.error(gl.getProgramInfoLog(p));
81
- gl.deleteProgram(p);
82
- throw new Error('Program link error');
83
- }
84
- return p;
85
- }
86
-
87
- function resizeCanvasToDisplaySize() {
88
- if (!canvas) return false;
89
- const dpr = Math.max(1, window.devicePixelRatio || 1);
90
- const width = Math.floor(canvas.clientWidth * dpr);
91
- const height = Math.floor(canvas.clientHeight * dpr);
92
- if (canvas.width !== width || canvas.height !== height) {
93
- canvas.width = width;
94
- canvas.height = height;
95
- return true;
96
- }
97
- return false;
98
- }
99
-
100
- function updateUniforms() {
101
- if (!gl || !program || !canvas) return;
102
- gl.useProgram(program);
103
- gl.uniform2f(u_resolution, canvas.width, canvas.height);
104
-
105
- const colorsArr = new Float32Array(MAX_POINTS * 3);
106
- const posArr = new Float32Array(MAX_POINTS * 2);
107
- for (let i = 0; i < points.length && i < MAX_POINTS; i++) {
108
- const [r, g, b] = hexToRgb(points[i].color);
109
- colorsArr[i * 3] = r;
110
- colorsArr[i * 3 + 1] = g;
111
- colorsArr[i * 3 + 2] = b;
112
- posArr[i * 2] = points[i].x;
113
- posArr[i * 2 + 1] = points[i].y;
114
- }
115
-
116
- gl.uniform3fv(u_colors, colorsArr);
117
- gl.uniform2fv(u_positions, posArr);
118
- gl.uniform1i(u_count, Math.min(points.length, MAX_POINTS));
119
- gl.uniform1f(u_radius, radius);
120
- gl.uniform1f(u_intensity, intensity);
121
- gl.uniform1i(u_warpMode, warpMode);
122
- gl.uniform1f(u_warpSize, warpSize);
123
- gl.uniform1f(u_warpAmount, warpAmount);
124
- gl.uniform1f(u_grainAmount, grainAmount);
125
- gl.uniform1f(u_grainSize, grainSize);
126
- gl.uniform1f(u_seed, seed);
127
- }
128
-
129
- function render(time: number) {
130
- if (!gl || !program || !canvas) return;
131
- if (lastTime === 0) lastTime = time;
132
- const dt = (time - lastTime) * 0.001;
133
- lastTime = time;
134
- animTime += dt * timeAmount;
135
-
136
- resizeCanvasToDisplaySize();
137
- gl.viewport(0, 0, canvas.width, canvas.height);
138
- gl.clearColor(0, 0, 0, 0);
139
- gl.clear(gl.COLOR_BUFFER_BIT);
140
-
141
- gl.useProgram(program);
142
- if (u_time) gl.uniform1f(u_time, animTime);
143
-
144
- updateUniforms();
145
- gl.drawArrays(gl.TRIANGLES, 0, 6);
146
-
147
- if (running) frameId = requestAnimationFrame(render);
148
- }
149
-
150
- onMount(() => {
151
- if (!canvas || !browser) return;
152
- gl = canvas.getContext('webgl', { antialias: true })!;
153
- if (!gl) {
154
- console.warn('WebGL not supported');
155
- return;
156
- }
157
-
158
- program = createProgram(gl, vert, frag);
159
- gl.useProgram(program);
160
-
161
- const verts = new Float32Array([-1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1]);
162
- const vbo = gl.createBuffer();
163
- gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
164
- gl.bufferData(gl.ARRAY_BUFFER, verts, gl.STATIC_DRAW);
165
- const a_pos = gl.getAttribLocation(program, 'a_pos');
166
- gl.enableVertexAttribArray(a_pos);
167
- gl.vertexAttribPointer(a_pos, 2, gl.FLOAT, false, 0, 0);
168
-
169
- // @ts-ignore
170
- u_resolution = gl.getUniformLocation(program, 'u_resolution');
171
- // @ts-ignore
172
- u_time = gl.getUniformLocation(program, 'u_time');
173
- // @ts-ignore
174
- u_count = gl.getUniformLocation(program, 'u_count');
175
- // @ts-ignore
176
- u_colors = gl.getUniformLocation(program, 'u_colors');
177
- // @ts-ignore
178
- u_positions = gl.getUniformLocation(program, 'u_positions');
179
- // @ts-ignore
180
- u_radius = gl.getUniformLocation(program, 'u_radius');
181
- // @ts-ignore
182
- u_intensity = gl.getUniformLocation(program, 'u_intensity');
183
- // @ts-ignore
184
- u_warpMode = gl.getUniformLocation(program, 'u_warpMode');
185
- // @ts-ignore
186
- u_warpSize = gl.getUniformLocation(program, 'u_warpSize');
187
- // @ts-ignore
188
- u_warpAmount = gl.getUniformLocation(program, 'u_warpAmount');
189
- // @ts-ignore
190
- u_grainAmount = gl.getUniformLocation(program, 'u_grainAmount');
191
- // @ts-ignore
192
- u_grainSize = gl.getUniformLocation(program, 'u_grainSize');
193
- // @ts-ignore
194
- u_grainAnimate = gl.getUniformLocation(program, 'u_grainAnimate');
195
- // @ts-ignore
196
- u_seed = gl.getUniformLocation(program, 'u_seed');
197
-
198
- frameId = requestAnimationFrame(render);
199
- window.addEventListener('resize', updateUniforms);
200
- });
201
-
202
- onDestroy(() => {
203
- if (!browser) return;
204
-
205
- running = false;
206
- if (frameId) cancelAnimationFrame(frameId);
207
- window.removeEventListener('resize', updateUniforms);
208
- });
209
-
210
- $effect(() => {
211
- if (gl && program) {
212
- updateUniforms();
213
- }
214
- });
215
- </script>
216
-
217
- <canvas bind:this={canvas}></canvas>
218
-
219
- <style>
220
- canvas { width: 100%; height: 100%; display: block; }
221
- </style>
1
+ <script lang="ts">
2
+ import vert from './gradient.vert.glsl?raw';
3
+ import frag from './gradient.frag.glsl?raw';
4
+ import { hexToRgb } from 'ventoui-utils';
5
+ import { onMount, onDestroy } from 'svelte';
6
+ import type { Warp, Grain, GradientOptions } from './index.js';
7
+
8
+ let {
9
+ points = [],
10
+ radius = 0.6,
11
+ intensity = 1.0,
12
+ warp: initialWarp = {},
13
+ speed = 1.0,
14
+ grain: initialGrain = {},
15
+ maxPoints = 12,
16
+ seed = Math.random(),
17
+ ...rest
18
+ }: GradientOptions = $props();
19
+
20
+ const defaultWarp: Required<Warp> = { mode: 0, amount: 0, size: 1 };
21
+ const defaultGrain: Required<Grain> = { amount: 0, size: 1 };
22
+
23
+ const warp: Required<Warp> = $derived({ ...defaultWarp, ...initialWarp });
24
+ const grain: Required<Grain> = $derived({ ...defaultGrain, ...initialGrain });
25
+ const browser = () => typeof window !== 'undefined' && typeof document !== 'undefined';
26
+
27
+ let canvas: HTMLCanvasElement | undefined = $state();
28
+ let gl: WebGLRenderingContext | undefined = $state();
29
+ let program: WebGLProgram | undefined = $state();
30
+
31
+ let uniforms: Record<string, WebGLUniformLocation> = {} as Record<string, WebGLUniformLocation>;
32
+ let frameId: number;
33
+ let running = true;
34
+ let lastTime = 0;
35
+ let animTime = 0;
36
+
37
+ const shaderUniforms = ['u_resolution',
38
+ 'u_time', 'u_count', 'u_colors',
39
+ 'u_positions', 'u_radius', 'u_intensity',
40
+ 'u_warpMode', 'u_warpSize', 'u_warpAmount',
41
+ 'u_grainAmount', 'u_grainSize', 'u_seed'
42
+ ];
43
+
44
+ function createShader(gl: WebGLRenderingContext, type: number, src: string) {
45
+ const s = gl.createShader(type)!;
46
+ gl.shaderSource(s, src);
47
+ gl.compileShader(s);
48
+ if (!gl.getShaderParameter(s, gl.COMPILE_STATUS)) {
49
+ console.error(gl.getShaderInfoLog(s));
50
+ gl.deleteShader(s);
51
+ throw new Error('Shader compile error');
52
+ }
53
+ return s;
54
+ }
55
+
56
+ function createProgram(gl: WebGLRenderingContext, vsSrc: string, fsSrc: string) {
57
+ const vs = createShader(gl, gl.VERTEX_SHADER, vsSrc);
58
+ const fs = createShader(gl, gl.FRAGMENT_SHADER, fsSrc);
59
+ const p = gl.createProgram()!;
60
+ gl.attachShader(p, vs);
61
+ gl.attachShader(p, fs);
62
+ gl.linkProgram(p);
63
+ if (!gl.getProgramParameter(p, gl.LINK_STATUS)) {
64
+ console.error(gl.getProgramInfoLog(p));
65
+ gl.deleteProgram(p);
66
+ throw new Error('Program link error');
67
+ }
68
+ return p;
69
+ }
70
+
71
+ function resizeCanvasToDisplaySize() {
72
+ if (!canvas) return false;
73
+ const dpr = Math.max(1, window.devicePixelRatio || 1);
74
+ const width = Math.floor(canvas.clientWidth * dpr);
75
+ const height = Math.floor(canvas.clientHeight * dpr);
76
+ if (canvas.width !== width || canvas.height !== height) {
77
+ canvas.width = width;
78
+ canvas.height = height;
79
+ return true;
80
+ }
81
+ return false;
82
+ }
83
+
84
+ function updateUniforms() {
85
+ if (!gl || !program || !canvas) return;
86
+ gl.useProgram(program);
87
+ gl.uniform2f(uniforms.u_resolution, canvas.width, canvas.height);
88
+
89
+ const colorsArr = new Float32Array(maxPoints * 3);
90
+ const posArr = new Float32Array(maxPoints * 2);
91
+
92
+ for (let i = 0; i < points.length && i < maxPoints; i++) {
93
+ const [r, g, b] = hexToRgb(points[i].color);
94
+ colorsArr.set([r, g, b], i * 3);
95
+ posArr.set([points[i].x, points[i].y], i * 2);
96
+ }
97
+
98
+ gl.uniform3fv(uniforms.u_colors, colorsArr);
99
+ gl.uniform2fv(uniforms.u_positions, posArr);
100
+ gl.uniform1i(uniforms.u_count, Math.min(points.length, maxPoints));
101
+ gl.uniform1f(uniforms.u_radius, radius);
102
+ gl.uniform1f(uniforms.u_intensity, intensity);
103
+ gl.uniform1i(uniforms.u_warpMode, warp.mode);
104
+ gl.uniform1f(uniforms.u_warpSize, warp.size);
105
+ gl.uniform1f(uniforms.u_warpAmount, warp.amount);
106
+ gl.uniform1f(uniforms.u_grainAmount, grain.amount);
107
+ gl.uniform1f(uniforms.u_grainSize, grain.size);
108
+ gl.uniform1f(uniforms.u_seed, seed);
109
+ }
110
+
111
+ function render(time: number) {
112
+ if (!gl || !program || !canvas) return;
113
+ if (lastTime === 0) lastTime = time;
114
+ const dt = (time - lastTime) * 0.001;
115
+ lastTime = time;
116
+ animTime += dt * speed;
117
+
118
+ resizeCanvasToDisplaySize();
119
+ gl.viewport(0, 0, canvas.width, canvas.height);
120
+ gl.clearColor(0, 0, 0, 0);
121
+ gl.clear(gl.COLOR_BUFFER_BIT);
122
+
123
+ gl.useProgram(program);
124
+ gl.uniform1f(uniforms.u_time, animTime);
125
+ updateUniforms();
126
+ gl.drawArrays(gl.TRIANGLES, 0, 6);
127
+
128
+ if (running) frameId = requestAnimationFrame(render);
129
+ }
130
+
131
+ function getUniform(gl: WebGLRenderingContext, program: WebGLProgram, name: string): WebGLUniformLocation {
132
+ const loc = gl.getUniformLocation(program, name);
133
+ if (!loc) throw new Error(`Uniform ${name} not found`);
134
+ return loc;
135
+ }
136
+
137
+ onMount(() => {
138
+ if (!canvas || !browser) return;
139
+ gl = canvas.getContext('webgl', { antialias: true })!;
140
+ if (!gl) return console.warn('WebGL not supported');
141
+
142
+ program = createProgram(gl, vert, frag);
143
+ gl.useProgram(program);
144
+
145
+ const verts = new Float32Array([-1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1]);
146
+ const vbo = gl.createBuffer();
147
+ gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
148
+ gl.bufferData(gl.ARRAY_BUFFER, verts, gl.STATIC_DRAW);
149
+ const a_pos = gl.getAttribLocation(program, 'a_pos');
150
+ gl.enableVertexAttribArray(a_pos);
151
+ gl.vertexAttribPointer(a_pos, 2, gl.FLOAT, false, 0, 0);
152
+
153
+ for (const name of shaderUniforms) {
154
+ uniforms[name] = getUniform(gl, program, name);
155
+ }
156
+
157
+ frameId = requestAnimationFrame(render);
158
+ window.addEventListener('resize', updateUniforms);
159
+ });
160
+
161
+ onDestroy(() => {
162
+ running = false;
163
+ if (!canvas || !browser) return;
164
+ if (frameId) cancelAnimationFrame(frameId);
165
+ window.removeEventListener('resize', updateUniforms);
166
+ });
167
+
168
+ $effect(() => {
169
+ if (gl && program) updateUniforms();
170
+ });
171
+ </script>
172
+
173
+ <canvas bind:this={canvas} {...rest}></canvas>
174
+
175
+ <style>
176
+ canvas { width: 100%; height: 100%; display: block; }
177
+ </style>
@@ -1,21 +1,4 @@
1
- interface Props {
2
- points?: {
3
- color: string;
4
- x: number;
5
- y: number;
6
- }[];
7
- radius?: number;
8
- timeAmount?: number;
9
- intensity?: number;
10
- warpMode?: number;
11
- warpSize?: number;
12
- warpAmount?: number;
13
- grainAmount?: number;
14
- grainSize?: number;
15
- seed?: number;
16
- }
17
- declare const Gradient: import("svelte").Component<Props, {
18
- MAX_POINTS: 12;
19
- }, "">;
1
+ import type { GradientOptions } from './index.js';
2
+ declare const Gradient: import("svelte").Component<GradientOptions, {}, "">;
20
3
  type Gradient = ReturnType<typeof Gradient>;
21
4
  export default Gradient;
@@ -5,7 +5,7 @@ precision mediump float;
5
5
  #define MAX_POINTS 12
6
6
 
7
7
  uniform vec2 u_resolution;
8
- uniform float u_time; // теперь это интегрированное animTime из компонента
8
+ uniform float u_time;
9
9
  uniform int u_count;
10
10
  uniform vec3 u_colors[MAX_POINTS];
11
11
  uniform vec2 u_positions[MAX_POINTS];
@@ -13,53 +13,37 @@ uniform float u_radius;
13
13
  uniform float u_intensity;
14
14
  uniform float u_grainAmount;
15
15
  uniform float u_grainSize;
16
-
17
- // warp uniforms
18
- uniform int u_warpMode; // 0 = none, 1 = waves, 2 = simplex, 3 = fbm, 4 = ridged
16
+ uniform int u_warpMode;
19
17
  uniform float u_warpSize;
20
18
  uniform float u_warpAmount;
21
- uniform float u_seed; // новый
19
+ uniform float u_seed;
22
20
 
23
- // --- Simplex (from Ashima / webgl-noise) ---
24
21
  vec3 permute(vec3 x) {
25
22
  return mod(((x * 34.0) + 1.0) * x, 289.0);
26
23
  }
24
+
27
25
  float snoise(vec2 v) {
28
- const vec4 C = vec4(0.211324865405187, /* (3.0-sqrt(3.0))/6.0 */
29
- 0.366025403784439, /* (sqrt(3.0)-1.0)/2.0 */
30
- -0.577350269189626, /* -1.0 + 2.0 * C.x */
31
- 0.024390243902439); /* 1.0 / 41.0 */
32
- vec2 i = floor(v + dot(v, C.yy) );
33
- vec2 x0 = v - i + dot(i, C.xx);
34
-
35
- vec2 i1;
36
- i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
26
+ const vec4 C = vec4(0.211324865405187, 0.366025403784439, -0.577350269189626, 0.024390243902439);
27
+ vec2 i = floor(v + dot(v, C.yy));
28
+ vec2 x0 = v - i + dot(i, C.xx);
29
+ vec2 i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
37
30
  vec4 x12 = x0.xyxy + C.xxzz;
38
31
  x12.xy -= i1;
39
-
40
32
  i = mod(i, 289.0);
41
- vec3 p = permute( permute( i.y + vec3(0.0, i1.y, 1.0) )
42
- + i.x + vec3(0.0, i1.x, 1.0) );
43
-
44
- vec3 m = max(0.5 - vec3(dot(x0,x0), dot(x12.xy,x12.xy), dot(x12.zw,x12.zw)), 0.0);
45
- m = m * m;
46
- m = m * m;
47
-
33
+ vec3 p = permute(permute(i.y + vec3(0.0, i1.y, 1.0)) + i.x + vec3(0.0, i1.x, 1.0));
34
+ vec3 m = max(0.5 - vec3(dot(x0, x0), dot(x12.xy, x12.xy), dot(x12.zw, x12.zw)), 0.0);
35
+ m = m * m * m * m;
48
36
  vec3 x = 2.0 * fract(p * C.www) - 1.0;
49
37
  vec3 h = abs(x) - 0.5;
50
38
  vec3 ox = floor(x + 0.5);
51
39
  vec3 a0 = x - ox;
52
-
53
40
  m *= 1.79284291400159 - 0.85373472095314 * (a0*a0 + h*h);
54
-
55
41
  vec3 g;
56
- g.x = a0.x * x0.x + h.x * x0.y;
42
+ g.x = a0.x * x0.x + h.x * x0.y;
57
43
  g.yz = a0.yz * x12.xz + h.yz * x12.yw;
58
-
59
44
  return 130.0 * dot(m, g);
60
45
  }
61
46
 
62
- // fbm helper
63
47
  float fbm(vec2 p) {
64
48
  float f = 0.0;
65
49
  float amp = 0.5;
@@ -71,84 +55,101 @@ float fbm(vec2 p) {
71
55
  return f;
72
56
  }
73
57
 
74
- // Wave warp теперь использует seed и animTime
75
- vec2 waveWarp(vec2 uv, float t, float seed) {
76
- // phase field from noise so waves are not uniform / repeatless across seed
58
+ vec2 warpWave(vec2 uv, float t, float seed) {
77
59
  float phase = snoise(uv * (u_warpSize * 1.5 + 0.1) + vec2(seed * 5.0));
78
60
  float w1 = sin((uv.y * u_warpSize * 10.0) + t * 0.6 + phase * 6.28318);
79
61
  float w2 = cos((uv.x * u_warpSize * 8.0) + t * 0.4 - phase * 6.28318 * 0.5);
80
62
  vec2 disp = vec2(w1, w2) * (u_warpAmount * 0.5);
81
- // adjust for aspect
82
63
  disp.x *= u_resolution.x / u_resolution.y;
83
64
  return uv + disp;
84
65
  }
85
66
 
67
+ vec2 warpSimplex(vec2 uv, float t, float seed) {
68
+ vec2 noiseUv = uv * (u_warpSize * 4.0 + 0.001) + vec2(seed * 10.0, seed * 20.0);
69
+ float n = snoise(noiseUv + vec2(t * 0.05));
70
+ vec2 disp = vec2(n, snoise(noiseUv + vec2(12.34))) * u_warpAmount * 0.8;
71
+ disp.x *= u_resolution.x / u_resolution.y;
72
+ return uv + disp;
73
+ }
74
+
75
+ vec2 warpFBM(vec2 uv, float t, float seed) {
76
+ vec2 noiseUv = uv * (u_warpSize * 3.0) + vec2(seed * 7.0);
77
+ float f = fbm(noiseUv + vec2(t * 0.02));
78
+ vec2 disp = vec2(f, fbm(noiseUv + vec2(31.4))) * u_warpAmount;
79
+ disp.x *= u_resolution.x / u_resolution.y;
80
+ return uv + disp;
81
+ }
82
+
83
+ vec2 warpRidged(vec2 uv, float t, float seed) {
84
+ vec2 nUv = uv * (u_warpSize * 2.5) + vec2(seed * 9.0);
85
+ float r = 1.0 - abs(fbm(nUv));
86
+ vec2 disp = vec2(r, r * 0.5) * (u_warpAmount * 1.2);
87
+ disp.x *= u_resolution.x / u_resolution.y;
88
+ return uv + disp;
89
+ }
90
+
91
+ vec2 warpSwirl(vec2 uv, float t) {
92
+ vec2 centered = uv - 0.5;
93
+ float radius = length(centered);
94
+ float angle = radius * u_warpSize * 5.0 - t * 0.5;
95
+ float s = sin(angle) * u_warpAmount;
96
+ float c = cos(angle) * u_warpAmount;
97
+ centered = vec2(c * centered.x - s * centered.y, s * centered.x + c * centered.y);
98
+ centered.x *= u_resolution.x / u_resolution.y;
99
+ return centered + 0.5;
100
+ }
101
+
102
+ vec2 warpRadial(vec2 uv, float t) {
103
+ vec2 centered = uv - 0.5;
104
+ float r = length(centered);
105
+ float theta = atan(centered.y, centered.x);
106
+ float wave = sin(r * u_warpSize * 20.0 - t * 2.0) * u_warpAmount;
107
+ r += wave;
108
+ vec2 p;
109
+ p.x = r * cos(theta);
110
+ p.y = r * sin(theta);
111
+ p.x *= u_resolution.x / u_resolution.y;
112
+ return p + 0.5;
113
+ }
114
+
115
+ vec2 applyWarp(vec2 uv) {
116
+ if (u_warpMode == 1) return warpWave(uv, u_time, u_seed);
117
+ else if (u_warpMode == 2) return warpSimplex(uv, u_time, u_seed);
118
+ else if (u_warpMode == 3) return warpFBM(uv, u_time, u_seed);
119
+ else if (u_warpMode == 4) return warpRidged(uv, u_time, u_seed);
120
+ else if (u_warpMode == 5) return warpSwirl(uv, u_time);
121
+ else if (u_warpMode == 6) return warpRadial(uv, u_time);
122
+ return uv;
123
+ }
124
+
125
+ float grain(vec2 uv) {
126
+ float n = fract(sin(dot(uv * u_grainSize, vec2(12.9898, 78.233))) * 43758.5453);
127
+ return n - 0.5;
128
+ }
129
+
86
130
  void main() {
87
131
  vec2 uv = gl_FragCoord.xy / u_resolution.xy;
88
132
  vec2 centered = uv - 0.5;
89
- // keep aspect-correct uv for distance calculations later
90
- vec2 aspectUV = uv;
91
- aspectUV.x *= u_resolution.x / u_resolution.y;
92
-
93
- // --- Handle warp modes (use u_time as smooth animTime, and u_seed to randomize field) ---
94
- if (u_warpMode == 1) {
95
- uv = waveWarp(uv, u_time, u_seed);
96
- } else if (u_warpMode == 2) {
97
- // Simplex single-octave with seed; animated slowly (u_time influences motion but smoothly)
98
- vec2 noiseUv = uv * (u_warpSize * 4.0 + 0.001) + vec2(u_seed * 10.0, u_seed * 20.0);
99
- float n = snoise(noiseUv + vec2(u_time * 0.05)); // slow drift
100
- vec2 disp = vec2(n, snoise(noiseUv + vec2(12.34))) * u_warpAmount * 0.8;
101
- disp.x *= u_resolution.x / u_resolution.y;
102
- uv += disp;
103
- } else if (u_warpMode == 3) {
104
- // fbm turbulence
105
- vec2 noiseUv = uv * (u_warpSize * 3.0) + vec2(u_seed * 7.0);
106
- float f = fbm(noiseUv + vec2(u_time * 0.02));
107
- vec2 disp = vec2(f, fbm(noiseUv + vec2(31.4))) * u_warpAmount;
108
- disp.x *= u_resolution.x / u_resolution.y;
109
- uv += disp;
110
- } else if (u_warpMode == 4) {
111
- // ridged noise variation
112
- vec2 nUv = uv * (u_warpSize * 2.5) + vec2(u_seed * 9.0);
113
- float r = 1.0 - abs(fbm(nUv));
114
- vec2 disp = vec2(r, r * 0.5) * (u_warpAmount * 1.2);
115
- disp.x *= u_resolution.x / u_resolution.y;
116
- uv += disp;
117
- }
133
+ uv = applyWarp(uv);
118
134
 
119
- // --- Grain calculation (apply animation BEFORE sampling the pseudo-random) ---
120
- vec2 grainUv = gl_FragCoord.xy / u_resolution.xy;
121
- // grainSize: bigger -> larger grains. so invert (1/size)
122
- float grainScale = max(u_grainSize, 0.0001);
123
- grainUv *= 1.0 / grainScale;
124
- // classic hash -> value in [0,1)
125
- float grain = fract(sin(dot(grainUv, vec2(12.9898, 78.233))) * 43758.5453);
126
- grain = grain - 0.5; // center at 0
135
+ float g = grain(gl_FragCoord.xy / u_resolution.xy);
127
136
 
128
- // --- Accumulate colors from points ---
129
137
  vec3 accum = vec3(0.0);
130
138
  float totalWeight = 0.0;
131
139
  for (int i = 0; i < MAX_POINTS; ++i) {
132
140
  if (i >= u_count) break;
133
- vec2 p = u_positions[i];
134
- vec2 diff = (uv - p);
141
+ vec2 diff = uv - u_positions[i];
135
142
  diff.x *= u_resolution.x / u_resolution.y;
136
143
  float d = length(diff);
137
- float w = exp(- (d * d) / (u_radius * u_radius)); // base gaussian weight in (0,1]
138
- // intensity now controls sharpness/contrast via power:
139
- float intensity = max(0.001, u_intensity);
140
- w = pow(w, intensity); // intensity >1 -> sharper peaks; <1 -> broader
144
+ float w = exp(- (d * d) / (u_radius * u_radius));
145
+ w = pow(w, max(0.001, u_intensity));
141
146
  accum += u_colors[i] * w;
142
147
  totalWeight += w;
143
148
  }
144
- vec3 color = totalWeight > 0.0 ? accum / totalWeight : vec3(0.95);
145
149
 
146
- // simple vignette
150
+ vec3 color = totalWeight > 0.0 ? accum / totalWeight : vec3(0.95);
147
151
  float vign = 1.0 - smoothstep(0.6, 0.9, length(centered));
148
152
  color *= mix(0.98, 1.02, vign);
149
-
150
- // add grain
151
- color += grain * u_grainAmount;
152
-
153
+ color += g * u_grainAmount;
153
154
  gl_FragColor = vec4(color, 1.0);
154
155
  }
package/dist/index.d.ts CHANGED
@@ -1 +1,2 @@
1
1
  export { default as Gradient } from './Gradient.svelte';
2
+ export type * from './types.js';
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Represents a color position in 2D space.
3
+ */
4
+ export interface Point {
5
+ /** Color of the point in hex format, e.g. "#FF0000" */
6
+ color: string;
7
+ /** X coordinate of the point (0 to 1) */
8
+ x: number;
9
+ /** Y coordinate of the point (0 to 1) */
10
+ y: number;
11
+ }
12
+ /**
13
+ * Configuration for warp effect.
14
+ */
15
+ export interface Warp {
16
+ /** Warp mode, default: 0 */
17
+ mode?: number;
18
+ /** Warp amount, default: 0 (if 0, warp is not applied) */
19
+ amount?: number;
20
+ /** Warp size, default: 1 */
21
+ size?: number;
22
+ }
23
+ /**
24
+ * Configuration for grain effect.
25
+ */
26
+ export interface Grain {
27
+ /** Grain intensity, default: 0 */
28
+ amount?: number;
29
+ /** Grain size, default: 1 */
30
+ size?: number;
31
+ }
32
+ /**
33
+ * Configuration for the gradient.
34
+ */
35
+ export interface GradientOptions {
36
+ /** Array of color positions */
37
+ points?: Point[];
38
+ /** Maximum number of points */
39
+ maxPoints?: number;
40
+ /** Blend radius, default: 0.6 */
41
+ radius?: number;
42
+ /** Blend intensity, default: 1 */
43
+ intensity?: number;
44
+ /** Warp effect configuration */
45
+ warp?: Warp;
46
+ /** Warp animation speed, default: 1 */
47
+ speed?: number;
48
+ /** Seed for warp effect, default: random */
49
+ seed?: number;
50
+ /** Grain effect configuration */
51
+ grain?: Grain;
52
+ }
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chroma-noise",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "scripts": {
5
5
  "dev": "vite dev",
6
6
  "build": "vite build && npm run prepack",
@@ -31,8 +31,7 @@
31
31
  },
32
32
  "peerDependencies": {
33
33
  "svelte": "^5.0.0",
34
- "ventoui-utils": "^0.0.2",
35
- "@sveltejs/kit": "^2.0.0"
34
+ "ventoui-utils": "^0.0.2"
36
35
  },
37
36
  "devDependencies": {
38
37
  "@sveltejs/adapter-auto": "^6.1.0",
@@ -45,22 +44,25 @@
45
44
  "publint": "^0.3.13",
46
45
  "svelte": "^5.39.5",
47
46
  "svelte-check": "^4.3.2",
47
+ "svelte-fancy-transitions": "^0.0.7",
48
48
  "typescript": "^5.9.2",
49
+ "ventoui-button": "^0.0.2",
50
+ "ventoui-slider": "^0.0.1",
51
+ "ventoui-styles": "^0.0.2",
49
52
  "vite": "^7.1.7"
50
53
  },
51
54
  "keywords": [
52
- "svelte", "simplex", "noise", "gradient", "chroma"
55
+ "svelte",
56
+ "simplex",
57
+ "noise",
58
+ "gradient",
59
+ "chroma",
60
+ "chroma-noise"
53
61
  ],
54
62
  "pnpm": {
55
63
  "onlyBuiltDependencies": [
56
64
  "esbuild"
57
65
  ]
58
66
  },
59
- "packageManager": "pnpm@10.12.1+sha512.f0dda8580f0ee9481c5c79a1d927b9164f2c478e90992ad268bbb2465a736984391d6333d2c327913578b2804af33474ca554ba29c04a8b13060a717675ae3ac",
60
- "dependencies": {
61
- "ventoui-button": "^0.0.2",
62
- "ventoui-slider": "^0.0.1",
63
- "ventoui-styles": "^0.0.2",
64
- "ventoui-utils": "^0.0.2"
65
- }
67
+ "packageManager": "pnpm@10.12.1+sha512.f0dda8580f0ee9481c5c79a1d927b9164f2c478e90992ad268bbb2465a736984391d6333d2c327913578b2804af33474ca554ba29c04a8b13060a717675ae3ac"
66
68
  }