@rosalana/sandbox 0.0.4 → 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.
package/dist/index.es.js CHANGED
@@ -1,9 +1,9 @@
1
- var S = Object.defineProperty;
2
- var L = (o, t, e) => t in o ? S(o, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : o[t] = e;
3
- var i = (o, t, e) => L(o, typeof t != "symbol" ? t + "" : t, e);
4
- class l {
5
- constructor(t, e, r, s) {
6
- this.target = t, this.type = e, this.listener = r, this.options = s, this.target.addEventListener(
1
+ var D = Object.defineProperty;
2
+ var N = (u, e, n) => e in u ? D(u, e, { enumerable: !0, configurable: !0, writable: !0, value: n }) : u[e] = n;
3
+ var a = (u, e, n) => N(u, typeof e != "symbol" ? e + "" : e, n);
4
+ class x {
5
+ constructor(e, n, t, i) {
6
+ this.target = e, this.type = n, this.listener = t, this.options = i, this.target.addEventListener(
7
7
  this.type,
8
8
  this.listener,
9
9
  this.options
@@ -16,96 +16,211 @@ class l {
16
16
  this.options
17
17
  );
18
18
  }
19
- static on(t, e, r, s) {
20
- return t.addEventListener(e, r, s), () => t.removeEventListener(e, r, s);
19
+ static on(e, n, t, i) {
20
+ return e.addEventListener(n, t, i), () => e.removeEventListener(n, t, i);
21
+ }
22
+ }
23
+ class h extends Error {
24
+ constructor(n, t) {
25
+ super(n);
26
+ a(this, "name", "SandboxError");
27
+ this.code = t;
21
28
  }
22
29
  }
23
- class u extends Error {
24
- constructor(t, e) {
25
- super(t), this.code = e, this.name = "SandboxError";
30
+ class z extends h {
31
+ constructor() {
32
+ super("WebGL is not supported in this browser.", "CONTEXT_ERROR");
26
33
  }
27
34
  }
28
- class w extends u {
29
- constructor(t) {
30
- const e = t === "not_supported" ? "WebGL is not supported in this browser." : "Failed to create WebGL context. The GPU may be unavailable.";
35
+ class de extends h {
36
+ constructor() {
31
37
  super(
32
- e,
33
- t === "not_supported" ? "WEBGL_NOT_SUPPORTED" : "CONTEXT_CREATION_FAILED"
34
- ), this.name = "SandboxContextError";
38
+ "Failed to create WebGL context. The GPU may be unavailable.",
39
+ "CONTEXT_ERROR"
40
+ );
35
41
  }
36
42
  }
37
- class k extends u {
38
- constructor(t, e) {
43
+ class G extends h {
44
+ constructor(e, n) {
39
45
  super(
40
- `Vertex and fragment shader WebGL versions do not match (${t} vs ${e})`,
41
- "SHADER_VERSION_MISMATCH"
42
- ), this.vertexVersion = t, this.fragmentVersion = e, this.name = "SandboxShaderVersionMismatchError";
46
+ `Vertex and fragment shader WebGL versions do not match (${e} vs ${n})`,
47
+ "VALIDATION_ERROR"
48
+ ), this.vertexVersion = e, this.fragmentVersion = n;
43
49
  }
44
50
  }
45
- class c extends u {
46
- constructor(e, r, s) {
47
- const n = c.parseErrorLines(s), a = n.length > 0 ? ` at line(s): ${n.join(", ")}` : "";
51
+ class _ extends h {
52
+ constructor(n, t, i) {
53
+ const r = _.parseErrorLines(i), s = r.length > 0 ? ` at line(s): ${r.join(", ")}` : "";
48
54
  super(
49
- `${e} shader compilation failed${a}
55
+ `${n} shader compilation failed${s}
50
56
 
51
- ${s}`,
52
- "SHADER_COMPILATION_FAILED"
57
+ ${i}`,
58
+ "SHADER_ERROR"
53
59
  );
54
- /** Line numbers where errors occurred */
55
- i(this, "lines");
56
- this.shaderType = e, this.source = r, this.infoLog = s, this.name = "SandboxShaderCompilationError", this.lines = n;
60
+ a(this, "lines");
61
+ this.shaderType = n, this.source = t, this.infoLog = i, this.lines = r;
57
62
  }
58
- /** Parse error log to extract line numbers */
59
- static parseErrorLines(e) {
60
- const r = [
63
+ static parseErrorLines(n) {
64
+ const t = [
61
65
  /ERROR:\s*\d*:(\d+)/g,
62
- // Chrome/ANGLE: ERROR: 0:15
63
66
  /(\d+):(\d+)\(\d+\):/g,
64
- // Mesa: 0:15(0):
65
67
  /^(\d+):/gm
66
- // Simple: 15:
67
- ], s = /* @__PURE__ */ new Set();
68
- for (const n of r) {
69
- let a;
70
- for (; (a = n.exec(e)) !== null; ) {
71
- const f = parseInt(a[1], 10);
72
- f > 0 && s.add(f);
68
+ ], i = /* @__PURE__ */ new Set();
69
+ for (const r of t) {
70
+ let s;
71
+ for (; (s = r.exec(n)) !== null; ) {
72
+ const o = parseInt(s[1], 10);
73
+ o > 0 && i.add(o);
73
74
  }
74
75
  }
75
- return [...s].sort((n, a) => n - a);
76
+ return [...i].sort((r, s) => r - s);
77
+ }
78
+ }
79
+ class C extends h {
80
+ constructor(e, n, t, i) {
81
+ super(
82
+ `The shader ${e} "${n}" has type "${i}" but expected "${t}"`,
83
+ "SHADER_ERROR"
84
+ ), this.requirement = e, this.name = n, this.expectedType = t, this.actualType = i;
85
+ }
86
+ }
87
+ class W extends h {
88
+ constructor() {
89
+ super("Shader source does not contain any function.", "SHADER_ERROR");
90
+ }
91
+ }
92
+ class q extends h {
93
+ constructor(e, n) {
94
+ super(
95
+ `Syntax error in shader import statement at line ${e}: ${n}`,
96
+ "SHADER_ERROR"
97
+ ), this.line = e, this.details = n;
98
+ }
99
+ }
100
+ class H extends h {
101
+ constructor(e, n) {
102
+ super(
103
+ `Duplicate import name "${e}" found at line ${n}. Each import must have a unique name.`,
104
+ "SHADER_ERROR"
105
+ ), this.name = e, this.line = n;
106
+ }
107
+ }
108
+ class j extends h {
109
+ constructor(e) {
110
+ super(
111
+ `Can not find module '${e}'. Check if it is defined before usage or if the name is correct.`,
112
+ "MODULE_ERROR"
113
+ ), this.moduleName = e;
114
+ }
115
+ }
116
+ class Y extends h {
117
+ constructor(e, n) {
118
+ super(
119
+ `Method '${n}' not found in shader module '${e}'. Check if the method is defined in the module source code or if the name is correct.`,
120
+ "MODULE_ERROR"
121
+ ), this.moduleName = e, this.methodName = n;
122
+ }
123
+ }
124
+ class K extends h {
125
+ constructor(e) {
126
+ super(
127
+ `Importing 'main' function from module '${e}' is forbidden.`,
128
+ "MODULE_ERROR"
129
+ ), this.moduleName = e;
130
+ }
131
+ }
132
+ class X extends h {
133
+ constructor(e) {
134
+ super(
135
+ `Name 'default' is reserved and cannot be used as a function name in module '${e}'.`,
136
+ "MODULE_ERROR"
137
+ ), this.moduleName = e;
76
138
  }
77
139
  }
78
- class m extends u {
79
- constructor(t) {
140
+ class Q extends h {
141
+ constructor(e) {
142
+ super(
143
+ `Module name '${e}' is not allowed. Module names cannot be 'sandbox' or start with 'sandbox/'.`,
144
+ "MODULE_ERROR"
145
+ ), this.moduleName = e;
146
+ }
147
+ }
148
+ class J extends h {
149
+ constructor(e) {
150
+ super(
151
+ `Module '${e}' is already defined. Overwriting existing modules is not allowed.`,
152
+ "MODULE_ERROR"
153
+ ), this.moduleName = e;
154
+ }
155
+ }
156
+ class Z extends h {
157
+ constructor(e, n, t) {
158
+ super(
159
+ `Uniform '${t}' mentioned for function '${n}' of module '${e}' was not found among the module's declared uniforms. Check if the uniform is declared in the module source code or if the name is correct.`,
160
+ "MODULE_ERROR"
161
+ ), this.moduleName = e, this.functionName = n, this.uniformName = t;
162
+ }
163
+ }
164
+ class ee extends h {
165
+ constructor(e, n) {
166
+ super(
167
+ `Uniform '${n}' mentioned for function '${e}' was not imported from any module. Check if the function is imported from the correct module and if the uniform is declared in that module's source code with the correct name.`,
168
+ "MODULE_ERROR"
169
+ ), this.functionName = e, this.uniformName = n;
170
+ }
171
+ }
172
+ class ne extends h {
173
+ constructor(e, n) {
174
+ super(
175
+ `Mention '${e}' called in function '${n}' could not be replaced with the corresponding uniform reference. There might be an issue with the compilation process because the referenced uniform was not found among the shader requirements. Try use a different name for uniforms you want to mention in functions or check if the uniform is properly declared and mentioned in the module source code.`,
176
+ "MODULE_ERROR"
177
+ ), this.mentionName = e, this.calledInFunction = n;
178
+ }
179
+ }
180
+ class R extends h {
181
+ constructor(e) {
80
182
  super(`Shader program linking failed
81
183
 
82
- ${t}`, "PROGRAM_LINK_FAILED"), this.infoLog = t, this.name = "SandboxProgramError";
184
+ ${e}`, "PROGRAM_ERROR"), this.infoLog = e;
83
185
  }
84
186
  }
85
- class R {
187
+ class te extends h {
188
+ constructor(e) {
189
+ super(`Error in onLoad callback: ${e}`, "UNKNOWN_ERROR");
190
+ }
191
+ }
192
+ class ie extends h {
193
+ constructor(e, n) {
194
+ super(`Error in onBefore/onAfter hook callback with ID ${e}: ${n}`, "UNKNOWN_ERROR");
195
+ }
196
+ }
197
+ class re {
86
198
  constructor() {
87
199
  /** Total elapsed time in seconds */
88
- i(this, "time", 0);
200
+ a(this, "time", 0);
89
201
  /** Delta time since last frame in seconds */
90
- i(this, "delta", 0);
202
+ a(this, "delta", 0);
91
203
  /** Frame counter */
92
- i(this, "frame", 0);
204
+ a(this, "frame", 0);
93
205
  /** Is clock running */
94
- i(this, "running", !1);
95
- i(this, "startTime", 0);
96
- i(this, "lastTime", 0);
97
- i(this, "rafId", null);
98
- i(this, "callback", null);
206
+ a(this, "running", !1);
207
+ /** Smoothed frames per second */
208
+ a(this, "fps", 0);
209
+ a(this, "startTime", 0);
210
+ a(this, "lastTime", 0);
211
+ a(this, "rafId", null);
212
+ a(this, "callback", null);
213
+ a(this, "maxFps", 0);
99
214
  this.loop = this.loop.bind(this);
100
215
  }
101
216
  /**
102
217
  * Start the animation loop with a render callback.
103
218
  */
104
- start(t) {
219
+ start(e) {
105
220
  if (this.running) return this;
106
- this.callback = t, this.running = !0;
107
- const e = performance.now();
108
- return this.frame === 0 ? this.startTime = e : this.startTime = e - this.time * 1e3, this.lastTime = e, this.rafId = requestAnimationFrame(this.loop), this;
221
+ this.callback = e, this.running = !0;
222
+ const n = performance.now();
223
+ return this.frame === 0 ? this.startTime = n : this.startTime = n - this.time * 1e3, this.lastTime = n, this.rafId = requestAnimationFrame(this.loop), this;
109
224
  }
110
225
  /**
111
226
  * Stop the animation loop.
@@ -118,7 +233,7 @@ class R {
118
233
  * Reset clock to initial state.
119
234
  */
120
235
  reset() {
121
- return this.stop(), this.time = 0, this.delta = 0, this.frame = 0, this;
236
+ return this.stop(), this.time = 0, this.delta = 0, this.frame = 0, this.fps = 0, this;
122
237
  }
123
238
  /**
124
239
  * Get current clock state snapshot.
@@ -127,21 +242,23 @@ class R {
127
242
  return {
128
243
  time: this.time,
129
244
  delta: this.delta,
130
- frame: this.frame
245
+ frame: this.frame,
246
+ running: this.running,
247
+ fps: Math.round(this.fps)
131
248
  };
132
249
  }
133
250
  /**
134
251
  * Advance clock by one tick (for single-shot rendering).
135
252
  * Useful when autoplay is disabled.
136
253
  */
137
- tick(t = 0) {
138
- return this.delta = t, this.time += t, this.frame++, this.callback && this.callback(this.getState()), this;
254
+ tick(e = 0) {
255
+ return this.delta = e, this.time += e, this.frame++, this.callback && this.callback(this.getState()), this;
139
256
  }
140
257
  /**
141
258
  * Set time directly (for deterministic rendering).
142
259
  */
143
- setTime(t) {
144
- return this.time = t, this;
260
+ setTime(e) {
261
+ return this.time = e, this;
145
262
  }
146
263
  /**
147
264
  * Cleanup.
@@ -149,33 +266,49 @@ class R {
149
266
  destroy() {
150
267
  this.reset(), this.callback = null;
151
268
  }
269
+ /**
270
+ * Set maximum frames per second.
271
+ */
272
+ setMaxFps(e) {
273
+ return this.maxFps = e, this;
274
+ }
152
275
  /**
153
276
  * Internal animation frame handler.
154
277
  */
155
- loop(t) {
156
- this.running && (this.delta = (t - this.lastTime) / 1e3, this.lastTime = t, this.time = (t - this.startTime) / 1e3, this.frame++, this.callback && this.callback(this.getState()), this.rafId = requestAnimationFrame(this.loop));
278
+ loop(e) {
279
+ if (!this.running) return;
280
+ if (this.maxFps > 0) {
281
+ const t = 1e3 / this.maxFps;
282
+ if (e - this.lastTime < t) {
283
+ this.rafId = requestAnimationFrame(this.loop);
284
+ return;
285
+ }
286
+ }
287
+ this.delta = (e - this.lastTime) / 1e3, this.lastTime = e;
288
+ const n = this.delta > 0 ? 1 / this.delta : 0;
289
+ this.fps = this.fps * 0.95 + n * 0.05, this.time = (e - this.startTime) / 1e3, this.frame++, this.callback && this.callback(this.getState()), this.rafId = requestAnimationFrame(this.loop);
157
290
  }
158
291
  }
159
- class p {
160
- constructor(t) {
161
- i(this, "gl");
162
- i(this, "vao", null);
163
- i(this, "vbo", null);
164
- i(this, "ibo", null);
165
- i(this, "vertexCount", 0);
166
- i(this, "indexCount", 0);
167
- i(this, "useIndices", !1);
292
+ class A {
293
+ constructor(e) {
294
+ a(this, "gl");
295
+ a(this, "vao", null);
296
+ a(this, "vbo", null);
297
+ a(this, "ibo", null);
298
+ a(this, "vertexCount", 0);
299
+ a(this, "indexCount", 0);
300
+ a(this, "useIndices", !1);
168
301
  // WebGL1 VAO extension (if available)
169
- i(this, "vaoExt", null);
170
- i(this, "isWebGL2");
171
- this.gl = t, this.isWebGL2 = t instanceof WebGL2RenderingContext, this.isWebGL2 || (this.vaoExt = t.getExtension("OES_vertex_array_object"));
302
+ a(this, "vaoExt", null);
303
+ a(this, "isWebGL2");
304
+ this.gl = e, this.isWebGL2 = e instanceof WebGL2RenderingContext, this.isWebGL2 || (this.vaoExt = e.getExtension("OES_vertex_array_object"));
172
305
  }
173
306
  /**
174
307
  * Create a fullscreen quad geometry.
175
308
  * This is the most common use case for shader effects.
176
309
  */
177
- static fullscreenQuad(t) {
178
- const e = new p(t), r = new Float32Array([
310
+ static fullscreenQuad(e) {
311
+ const n = new A(e), t = new Float32Array([
179
312
  // position texcoord
180
313
  -1,
181
314
  -1,
@@ -197,7 +330,7 @@ class p {
197
330
  1,
198
331
  1
199
332
  // top-right
200
- ]), s = new Uint16Array([
333
+ ]), i = new Uint16Array([
201
334
  0,
202
335
  1,
203
336
  2,
@@ -207,33 +340,33 @@ class p {
207
340
  3
208
341
  // second triangle
209
342
  ]);
210
- return e.setup(r, s), e;
343
+ return n.setup(t, i), n;
211
344
  }
212
345
  /**
213
346
  * Setup geometry from vertex and index data.
214
347
  */
215
- setup(t, e) {
216
- const r = this.gl;
217
- return this.createVAO(), this.bindVAO(), this.vbo = r.createBuffer(), r.bindBuffer(r.ARRAY_BUFFER, this.vbo), r.bufferData(r.ARRAY_BUFFER, t, r.STATIC_DRAW), this.vertexCount = t.length / 4, e && (this.ibo = r.createBuffer(), r.bindBuffer(r.ELEMENT_ARRAY_BUFFER, this.ibo), r.bufferData(r.ELEMENT_ARRAY_BUFFER, e, r.STATIC_DRAW), this.indexCount = e.length, this.useIndices = !0), this.unbindVAO(), this;
348
+ setup(e, n) {
349
+ const t = this.gl;
350
+ return this.createVAO(), this.bindVAO(), this.vbo = t.createBuffer(), t.bindBuffer(t.ARRAY_BUFFER, this.vbo), t.bufferData(t.ARRAY_BUFFER, e, t.STATIC_DRAW), this.vertexCount = e.length / 4, n && (this.ibo = t.createBuffer(), t.bindBuffer(t.ELEMENT_ARRAY_BUFFER, this.ibo), t.bufferData(t.ELEMENT_ARRAY_BUFFER, n, t.STATIC_DRAW), this.indexCount = n.length, this.useIndices = !0), this.unbindVAO(), this;
218
351
  }
219
352
  /**
220
353
  * Link vertex attributes to shader program.
221
354
  * Call this after compiling shaders.
222
355
  */
223
- linkAttributes(t) {
224
- const e = this.gl;
225
- this.bindVAO(), e.bindBuffer(e.ARRAY_BUFFER, this.vbo);
226
- const r = 4 * Float32Array.BYTES_PER_ELEMENT, s = this.getPositionLocation(t);
227
- s >= 0 && (e.enableVertexAttribArray(s), e.vertexAttribPointer(s, 2, e.FLOAT, !1, r, 0));
228
- const n = this.getTexcoordLocation(t);
229
- return n >= 0 && (e.enableVertexAttribArray(n), e.vertexAttribPointer(
230
- n,
356
+ linkAttributes(e) {
357
+ const n = this.gl;
358
+ this.bindVAO(), n.bindBuffer(n.ARRAY_BUFFER, this.vbo);
359
+ const t = 4 * Float32Array.BYTES_PER_ELEMENT, i = this.getPositionLocation(e);
360
+ i >= 0 && (n.enableVertexAttribArray(i), n.vertexAttribPointer(i, 2, n.FLOAT, !1, t, 0));
361
+ const r = this.getTexcoordLocation(e);
362
+ return r >= 0 && (n.enableVertexAttribArray(r), n.vertexAttribPointer(
363
+ r,
231
364
  2,
232
- e.FLOAT,
365
+ n.FLOAT,
233
366
  !1,
234
- r,
367
+ t,
235
368
  2 * Float32Array.BYTES_PER_ELEMENT
236
- )), this.useIndices && e.bindBuffer(e.ELEMENT_ARRAY_BUFFER, this.ibo), this.unbindVAO(), this;
369
+ )), this.useIndices && n.bindBuffer(n.ELEMENT_ARRAY_BUFFER, this.ibo), this.unbindVAO(), this;
237
370
  }
238
371
  /**
239
372
  * Bind geometry for rendering.
@@ -251,31 +384,31 @@ class p {
251
384
  * Draw the geometry.
252
385
  */
253
386
  draw() {
254
- const t = this.gl;
255
- return this.bindVAO(), this.useIndices ? t.drawElements(t.TRIANGLES, this.indexCount, t.UNSIGNED_SHORT, 0) : t.drawArrays(t.TRIANGLE_STRIP, 0, this.vertexCount), this;
387
+ const e = this.gl;
388
+ return this.bindVAO(), this.useIndices ? e.drawElements(e.TRIANGLES, this.indexCount, e.UNSIGNED_SHORT, 0) : e.drawArrays(e.TRIANGLE_STRIP, 0, this.vertexCount), this;
256
389
  }
257
390
  /**
258
391
  * Cleanup all GPU resources.
259
392
  */
260
393
  destroy() {
261
- const t = this.gl;
262
- this.deleteVAO(), this.vbo && (t.deleteBuffer(this.vbo), this.vbo = null), this.ibo && (t.deleteBuffer(this.ibo), this.ibo = null);
394
+ const e = this.gl;
395
+ this.deleteVAO(), this.vbo && (e.deleteBuffer(this.vbo), this.vbo = null), this.ibo && (e.deleteBuffer(this.ibo), this.ibo = null);
263
396
  }
264
397
  /**
265
398
  * Get position attribute location.
266
399
  * Tries common naming conventions.
267
400
  */
268
- getPositionLocation(t) {
269
- let e = t.getAttribLocation("a_position");
270
- return e >= 0 || (e = t.getAttribLocation("aPosition"), e >= 0) || (e = t.getAttribLocation("position"), e >= 0) ? e : -1;
401
+ getPositionLocation(e) {
402
+ let n = e.getAttribLocation("a_position");
403
+ return n >= 0 || (n = e.getAttribLocation("aPosition"), n >= 0) || (n = e.getAttribLocation("position"), n >= 0) ? n : -1;
271
404
  }
272
405
  /**
273
406
  * Get texcoord attribute location.
274
407
  * Tries common naming conventions.
275
408
  */
276
- getTexcoordLocation(t) {
277
- let e = t.getAttribLocation("a_texcoord");
278
- return e >= 0 || (e = t.getAttribLocation("aTexCoord"), e >= 0) || (e = t.getAttribLocation("texcoord"), e >= 0) || (e = t.getAttribLocation("a_uv"), e >= 0) ? e : -1;
409
+ getTexcoordLocation(e) {
410
+ let n = e.getAttribLocation("a_texcoord");
411
+ return n >= 0 || (n = e.getAttribLocation("aTexCoord"), n >= 0) || (n = e.getAttribLocation("texcoord"), n >= 0) || (n = e.getAttribLocation("a_uv"), n >= 0) ? n : -1;
279
412
  }
280
413
  // ============================================================================
281
414
  // VAO helpers (handle WebGL1 vs WebGL2 differences)
@@ -293,33 +426,21 @@ class p {
293
426
  this.vao && (this.isWebGL2 ? this.gl.deleteVertexArray(this.vao) : this.vaoExt && this.vaoExt.deleteVertexArrayOES(this.vao), this.vao = null);
294
427
  }
295
428
  }
296
- class h {
297
- constructor(t) {
298
- i(this, "gl");
299
- i(this, "program", null);
300
- i(this, "vertexShader", null);
301
- i(this, "fragmentShader", null);
302
- i(this, "version", 1);
303
- this.gl = t;
304
- }
305
- /**
306
- * Detect WebGL version from shader source.
307
- * Looks for "#version 300 es" directive.
308
- */
309
- static detectVersion(t) {
310
- return /^\s*#version\s+300\s+es/m.test(t) ? 2 : 1;
429
+ class se {
430
+ constructor(e) {
431
+ a(this, "gl");
432
+ a(this, "program", null);
433
+ a(this, "vertexShader", null);
434
+ a(this, "fragmentShader", null);
435
+ this.gl = e;
311
436
  }
312
437
  /**
313
438
  * Compile shaders and link program.
314
439
  * @throws ShaderCompilationError if compilation fails
315
440
  * @throws ProgramLinkError if linking fails
316
441
  */
317
- compile(t, e) {
318
- this.destroy();
319
- const r = h.detectVersion(t), s = h.detectVersion(e);
320
- if (r != s)
321
- throw new k(r, s);
322
- return this.version = Math.max(r, s), this.vertexShader = this.compileShader("vertex", t), this.fragmentShader = this.compileShader("fragment", e), this.linkProgram(), this;
442
+ compile(e, n) {
443
+ return this.destroy(), this.vertexShader = this.compileShader("vertex", e), this.fragmentShader = this.compileShader("fragment", n), this.linkProgram(), this;
323
444
  }
324
445
  /**
325
446
  * Bind this program for rendering.
@@ -333,93 +454,87 @@ class h {
333
454
  getProgram() {
334
455
  return this.program;
335
456
  }
336
- /**
337
- * Get detected WebGL version.
338
- */
339
- getVersion() {
340
- return this.version;
341
- }
342
457
  /**
343
458
  * Get attribute location.
344
459
  */
345
- getAttribLocation(t) {
346
- return this.program ? this.gl.getAttribLocation(this.program, t) : -1;
460
+ getAttribLocation(e) {
461
+ return this.program ? this.gl.getAttribLocation(this.program, e) : -1;
347
462
  }
348
463
  /**
349
464
  * Get uniform location.
350
465
  */
351
- getUniformLocation(t) {
352
- return this.program ? this.gl.getUniformLocation(this.program, t) : null;
466
+ getUniformLocation(e) {
467
+ return this.program ? this.gl.getUniformLocation(this.program, e) : null;
353
468
  }
354
469
  /**
355
470
  * Cleanup all GPU resources.
356
471
  */
357
472
  destroy() {
358
- const t = this.gl;
359
- this.program && (this.vertexShader && t.detachShader(this.program, this.vertexShader), this.fragmentShader && t.detachShader(this.program, this.fragmentShader), t.deleteProgram(this.program), this.program = null), this.vertexShader && (t.deleteShader(this.vertexShader), this.vertexShader = null), this.fragmentShader && (t.deleteShader(this.fragmentShader), this.fragmentShader = null);
473
+ const e = this.gl;
474
+ this.program && (this.vertexShader && e.detachShader(this.program, this.vertexShader), this.fragmentShader && e.detachShader(this.program, this.fragmentShader), e.deleteProgram(this.program), this.program = null), this.vertexShader && (e.deleteShader(this.vertexShader), this.vertexShader = null), this.fragmentShader && (e.deleteShader(this.fragmentShader), this.fragmentShader = null);
360
475
  }
361
476
  /**
362
477
  * Compile a single shader.
363
478
  * @throws ShaderCompilationError if compilation fails
364
479
  */
365
- compileShader(t, e) {
366
- const r = this.gl, s = t === "vertex" ? r.VERTEX_SHADER : r.FRAGMENT_SHADER, n = r.createShader(s);
367
- if (!n)
368
- throw new c(
369
- t,
480
+ compileShader(e, n) {
481
+ const t = this.gl, i = e === "vertex" ? t.VERTEX_SHADER : t.FRAGMENT_SHADER, r = t.createShader(i);
482
+ if (!r)
483
+ throw new _(
370
484
  e,
485
+ n,
371
486
  "Failed to create shader object"
372
487
  );
373
- if (r.shaderSource(n, e), r.compileShader(n), !r.getShaderParameter(n, r.COMPILE_STATUS)) {
374
- const f = r.getShaderInfoLog(n) || "Unknown error";
375
- throw r.deleteShader(n), new c(t, e, f);
488
+ if (t.shaderSource(r, n), t.compileShader(r), !t.getShaderParameter(r, t.COMPILE_STATUS)) {
489
+ const o = t.getShaderInfoLog(r) || "Unknown error";
490
+ throw t.deleteShader(r), new _(e, n, o);
376
491
  }
377
- return n;
492
+ return r;
378
493
  }
379
494
  /**
380
495
  * Link vertex and fragment shaders into a program.
381
496
  * @throws ProgramLinkError if linking fails
382
497
  */
383
498
  linkProgram() {
384
- const t = this.gl;
499
+ const e = this.gl;
385
500
  if (!this.vertexShader || !this.fragmentShader)
386
- throw new m("Shaders not compiled");
387
- const e = t.createProgram();
388
- if (!e)
389
- throw new m("Failed to create program object");
390
- if (t.attachShader(e, this.vertexShader), t.attachShader(e, this.fragmentShader), t.linkProgram(e), !t.getProgramParameter(e, t.LINK_STATUS)) {
391
- const s = t.getProgramInfoLog(e) || "Unknown error";
392
- throw t.deleteProgram(e), new m(s);
501
+ throw new R("Shaders not compiled");
502
+ const n = e.createProgram();
503
+ if (!n)
504
+ throw new R("Failed to create program object");
505
+ if (e.attachShader(n, this.vertexShader), e.attachShader(n, this.fragmentShader), e.linkProgram(n), !e.getProgramParameter(n, e.LINK_STATUS)) {
506
+ const i = e.getProgramInfoLog(n) || "Unknown error";
507
+ throw e.deleteProgram(n), new R(i);
393
508
  }
394
- this.program = e;
509
+ this.program = n;
395
510
  }
396
511
  }
397
- class x {
398
- constructor(t, e) {
399
- i(this, "name");
400
- i(this, "method");
401
- i(this, "isArray");
402
- i(this, "isMatrix");
403
- i(this, "location", null);
404
- i(this, "locationResolved", !1);
405
- i(this, "value");
406
- this.name = t, this.value = e;
407
- const r = x.inferMethodInfo(e);
408
- this.method = r.method, this.isArray = r.isArray, this.isMatrix = r.isMatrix;
512
+ class k {
513
+ constructor(e, n) {
514
+ a(this, "name");
515
+ a(this, "method");
516
+ a(this, "isArray");
517
+ a(this, "isMatrix");
518
+ a(this, "location", null);
519
+ a(this, "locationResolved", !1);
520
+ a(this, "value");
521
+ this.name = e, this.value = n;
522
+ const t = k.inferMethodInfo(n);
523
+ this.method = t.method, this.isArray = t.isArray, this.isMatrix = t.isMatrix;
409
524
  }
410
525
  /**
411
526
  * Infer WebGL method and metadata from value type.
412
527
  */
413
- static inferMethodInfo(t) {
414
- if (typeof t == "boolean")
528
+ static inferMethodInfo(e) {
529
+ if (typeof e == "boolean")
415
530
  return { method: "uniform1i", isArray: !1, isMatrix: !1 };
416
- if (typeof t == "number")
531
+ if (typeof e == "number")
417
532
  return { method: "uniform1f", isArray: !1, isMatrix: !1 };
418
- if (!Array.isArray(t))
533
+ if (!Array.isArray(e))
419
534
  return { method: "uniform1f", isArray: !1, isMatrix: !1 };
420
- const e = t.length, r = t[0];
421
- if (Array.isArray(r))
422
- switch (r.length) {
535
+ const n = e.length, t = e[0];
536
+ if (Array.isArray(t))
537
+ switch (t.length) {
423
538
  case 2:
424
539
  return { method: "uniform2fv", isArray: !0, isMatrix: !1 };
425
540
  case 3:
@@ -429,7 +544,7 @@ class x {
429
544
  default:
430
545
  return { method: "uniform1fv", isArray: !0, isMatrix: !1 };
431
546
  }
432
- switch (e) {
547
+ switch (n) {
433
548
  case 2:
434
549
  return { method: "uniform2fv", isArray: !1, isMatrix: !1 };
435
550
  case 3:
@@ -448,8 +563,8 @@ class x {
448
563
  * Resolve and cache uniform location from program.
449
564
  * Returns null if uniform doesn't exist (optimized out by compiler, etc.)
450
565
  */
451
- resolveLocation(t, e) {
452
- return this.locationResolved || (this.location = t.getUniformLocation(e, this.name), this.locationResolved = !0), this.location;
566
+ resolveLocation(e, n) {
567
+ return this.locationResolved || (this.location = e.getUniformLocation(n, this.name), this.locationResolved = !0), this.location;
453
568
  }
454
569
  /**
455
570
  * Invalidate cached location (call when program changes).
@@ -460,8 +575,8 @@ class x {
460
575
  /**
461
576
  * Update value (doesn't upload to GPU until upload() is called).
462
577
  */
463
- setValue(t) {
464
- this.value = t;
578
+ setValue(e) {
579
+ this.value = e;
465
580
  }
466
581
  /**
467
582
  * Get current value.
@@ -474,96 +589,1782 @@ class x {
474
589
  * @param gl - WebGL context
475
590
  * @param program - Current WebGL program (for location resolution)
476
591
  */
477
- upload(t, e) {
478
- const r = this.resolveLocation(t, e);
479
- if (r === null)
592
+ upload(e, n) {
593
+ const t = this.resolveLocation(e, n);
594
+ if (t === null)
480
595
  return;
481
- const s = this.value;
482
- let n;
483
- switch (typeof s == "boolean" ? n = s ? 1 : 0 : typeof s == "number" ? n = s : this.isArray && Array.isArray(s[0]) ? n = new Float32Array(
484
- s.flat()
485
- ) : n = new Float32Array(s), this.method) {
596
+ const i = this.value;
597
+ let r;
598
+ switch (typeof i == "boolean" ? r = i ? 1 : 0 : typeof i == "number" ? r = i : this.isArray && Array.isArray(i[0]) ? r = new Float32Array(
599
+ i.flat()
600
+ ) : r = new Float32Array(i), this.method) {
486
601
  case "uniform1f":
487
- t.uniform1f(r, n);
602
+ e.uniform1f(t, r);
488
603
  break;
489
604
  case "uniform1i":
490
- t.uniform1i(r, n);
605
+ e.uniform1i(t, r);
491
606
  break;
492
607
  case "uniform1fv":
493
- t.uniform1fv(r, n);
608
+ e.uniform1fv(t, r);
494
609
  break;
495
610
  case "uniform2fv":
496
- t.uniform2fv(r, n);
611
+ e.uniform2fv(t, r);
497
612
  break;
498
613
  case "uniform3fv":
499
- t.uniform3fv(r, n);
614
+ e.uniform3fv(t, r);
500
615
  break;
501
616
  case "uniform4fv":
502
- t.uniform4fv(r, n);
617
+ e.uniform4fv(t, r);
503
618
  break;
504
619
  case "uniformMatrix2fv":
505
- t.uniformMatrix2fv(r, !1, n);
620
+ e.uniformMatrix2fv(t, !1, r);
506
621
  break;
507
622
  case "uniformMatrix3fv":
508
- t.uniformMatrix3fv(r, !1, n);
623
+ e.uniformMatrix3fv(t, !1, r);
509
624
  break;
510
625
  case "uniformMatrix4fv":
511
- t.uniformMatrix4fv(r, !1, n);
626
+ e.uniformMatrix4fv(t, !1, r);
627
+ break;
628
+ }
629
+ }
630
+ }
631
+ class w {
632
+ constructor(e) {
633
+ a(this, "parsed", null);
634
+ this.source = e;
635
+ }
636
+ /**
637
+ * Parse the shader source to extract imports, uniforms, functions, and version.
638
+ */
639
+ parse() {
640
+ if (this.parsed) return this.parsed;
641
+ const e = this.detectVersion(), n = this.detectImports(), t = this.detectUniforms(), i = this.detectFunctions(t);
642
+ return this.parsed = {
643
+ version: e,
644
+ imports: n,
645
+ uniforms: t,
646
+ functions: i
647
+ };
648
+ }
649
+ /**
650
+ * Check if the shader source has already been parsed
651
+ */
652
+ isParsed() {
653
+ return this.parsed !== null;
654
+ }
655
+ /**
656
+ * Change the shader source and reset the parsed result
657
+ */
658
+ setSource(e) {
659
+ this.source = e, this.parsed = null;
660
+ }
661
+ /**
662
+ * Get the detected GLSL version from the shader source without parsing the entire shader
663
+ */
664
+ version() {
665
+ return this.detectVersion();
666
+ }
667
+ detectVersion() {
668
+ return /^\s*#version\s+300\s+es/m.test(this.source) ? 2 : 1;
669
+ }
670
+ detectImports() {
671
+ const e = /^[ \t]*#import\s+(\w+)(?:\s+as\s+(\w+))?\s+from\s+["'](.+)["']/gm, n = /^[ \t]*[^\w\s]?import\b/gm, t = [], i = /* @__PURE__ */ new Set();
672
+ let r, s = 1, o = 0;
673
+ for (; (r = e.exec(this.source)) !== null; ) {
674
+ s += (this.source.substring(o, r.index).match(/\n/g) || []).length, o = r.index, i.add(s);
675
+ const f = r[1], c = r[2] || r[1], m = r[3];
676
+ if (t.some((p) => p.alias === c))
677
+ throw new H(c, s);
678
+ t.push({ name: f, alias: c, module: m, line: s });
679
+ }
680
+ let l;
681
+ for (; (l = n.exec(this.source)) !== null; ) {
682
+ const f = (this.source.substring(0, l.index).match(/\n/g) || []).length + 1;
683
+ if (i.has(f)) continue;
684
+ const c = this.source.split(`
685
+ `)[f - 1].trim();
686
+ throw new q(
687
+ f,
688
+ this.diagnoseImport(c)
689
+ );
690
+ }
691
+ return t;
692
+ }
693
+ diagnoseImport(e) {
694
+ const n = e.match(/^([^\w\s])import\b/);
695
+ if (n && n[1] !== "#")
696
+ return `Invalid prefix '${n[1]}'. Expected: #import <function> from '<module>'`;
697
+ if (/^import\b/.test(e))
698
+ return "Missing '#' prefix. Expected: #import <function> from '<module>'";
699
+ if (/^#import\s+from\b/.test(e))
700
+ return "Missing function name. Expected: #import <function> from '<module>'";
701
+ if (/^#import\s+\w+\s*$/.test(e))
702
+ return `Missing 'from' clause. Expected: #import ${e.split(/\s+/)[1]} from '<module>'`;
703
+ if (/^#import\s+\w+\s+as\s*$/.test(e) || /^#import\s+\w+\s+as\s+from\b/.test(e))
704
+ return `Missing alias name after 'as'. Expected: #import ${e.split(/\s+/)[1]} as <alias> from '<module>'`;
705
+ if (/^#import\s+\w+\s+as\s+\w+\s*$/.test(e)) {
706
+ const t = e.split(/\s+/);
707
+ return `Missing 'from' clause. Expected: #import ${t[1]} as ${t[3]} from '<module>'`;
708
+ }
709
+ if (/^#import\s+\w+(?:\s+as\s+\w+)?\s+from\s+\w+/.test(e)) {
710
+ const t = e.match(/from\s+(\S+)/);
711
+ return `Module name must be quoted. Expected: from '${t == null ? void 0 : t[1]}'`;
712
+ }
713
+ return "Invalid syntax. Expected: #import <function> from '<module>'";
714
+ }
715
+ detectUniforms() {
716
+ const e = /^[ \t]*uniform\s+(?:(?:highp|mediump|lowp)\s+)?(\w+)\s+(\w+)(?:\[(\d+)\])?\s*;/gm, n = [];
717
+ let t, i = 1, r = 0;
718
+ for (; (t = e.exec(this.source)) !== null; ) {
719
+ i += (this.source.substring(r, t.index).match(/\n/g) || []).length, r = t.index;
720
+ const s = t[1], o = t[2], l = t[3] ? parseInt(t[3], 10) : void 0;
721
+ n.push({ name: o, type: s, line: i, arrayNum: l });
722
+ }
723
+ return n;
724
+ }
725
+ detectFunctions(e) {
726
+ const n = [], t = "void|float|int|uint|bool|vec[234]|ivec[234]|uvec[234]|bvec[234]|mat[234](?:x[234])?|sampler2D|samplerCube|sampler3D|sampler2DArray", i = new RegExp(
727
+ `^[ \\t]*(${t})\\s+(\\w+)\\s*\\(([^)]*)\\)\\s*\\{`,
728
+ "gm"
729
+ );
730
+ let r;
731
+ for (; (r = i.exec(this.source)) !== null; ) {
732
+ const s = r[1], o = r[2], l = r[3].trim(), f = r.index, c = (this.source.substring(0, f).match(/\n/g) || []).length + 1, m = this.source.indexOf("{", f), p = this.findClosingBrace(this.source, m);
733
+ if (p === -1) continue;
734
+ const g = this.source.slice(m, p + 1), T = this.parseParams(l), I = this.findFunctionCalls(g), P = this.findUniformCalls(g, e), B = this.findMentionCalls(g);
735
+ n.push({
736
+ name: o,
737
+ type: s,
738
+ params: T,
739
+ body: g,
740
+ dependencies: [...I, ...P, ...B],
741
+ line: c
742
+ });
743
+ }
744
+ return n;
745
+ }
746
+ parseParams(e) {
747
+ if (!e.trim()) return [];
748
+ const n = [], t = e.split(",");
749
+ for (const i of t) {
750
+ const r = i.trim();
751
+ if (!r) continue;
752
+ const o = r.replace(/\b(in|out|inout|const|highp|mediump|lowp)\b\s*/g, "").trim().match(/^(\w+)\s+(\w+)(?:\[\d*\])?$/);
753
+ o && n.push({
754
+ type: o[1],
755
+ name: o[2]
756
+ });
757
+ }
758
+ return n;
759
+ }
760
+ findClosingBrace(e, n) {
761
+ let t = 0, i = !1, r = !1, s = !1, o = !1;
762
+ for (let l = n; l < e.length; l++) {
763
+ const f = e[l], c = e[l + 1], m = e[l - 1];
764
+ if (!r && !o && f === "/" && c === "/") {
765
+ s = !0;
766
+ continue;
767
+ }
768
+ if (s && f === `
769
+ `) {
770
+ s = !1;
771
+ continue;
772
+ }
773
+ if (!r && !s && f === "/" && c === "*") {
774
+ o = !0, l++;
775
+ continue;
776
+ }
777
+ if (o && f === "*" && c === "/") {
778
+ o = !1, l++;
779
+ continue;
780
+ }
781
+ if (!(s || o)) {
782
+ if (f === '"' && m !== "\\") {
783
+ r = !r;
784
+ continue;
785
+ }
786
+ if (!r) {
787
+ if (f === "{")
788
+ t++, i = !0;
789
+ else if (f === "}" && (t--, i && t === 0))
790
+ return l;
791
+ }
792
+ }
793
+ }
794
+ return -1;
795
+ }
796
+ findFunctionCalls(e) {
797
+ const n = [], t = /* @__PURE__ */ new Set([
798
+ "if",
799
+ "else",
800
+ "for",
801
+ "while",
802
+ "do",
803
+ "switch",
804
+ "case",
805
+ "return",
806
+ "break",
807
+ "continue",
808
+ "discard"
809
+ ]), i = /\b([a-zA-Z_]\w*)\s*\(/g;
810
+ let r;
811
+ for (; (r = i.exec(e)) !== null; ) {
812
+ const s = r[1];
813
+ t.has(s) || n.push({
814
+ name: s,
815
+ type: "function",
816
+ index: r.index
817
+ });
818
+ }
819
+ return n;
820
+ }
821
+ findUniformCalls(e, n) {
822
+ const t = [];
823
+ for (const i of n) {
824
+ const r = new RegExp(`\\b${i.name}\\b`, "g");
825
+ let s;
826
+ for (; (s = r.exec(e)) !== null; )
827
+ t.push({
828
+ name: i.name,
829
+ type: "uniform",
830
+ index: s.index
831
+ });
832
+ }
833
+ return t;
834
+ }
835
+ findMentionCalls(e) {
836
+ const n = [], t = /@(\w+)\.([a-zA-Z_]\w*)/g;
837
+ let i;
838
+ for (; (i = t.exec(e)) !== null; ) {
839
+ const r = i[1], s = i[2];
840
+ n.push({
841
+ name: `${r}.${s}`,
842
+ type: "mention",
843
+ index: i.index
844
+ });
845
+ }
846
+ return n;
847
+ }
848
+ }
849
+ class F {
850
+ constructor(e) {
851
+ /** Flag to track if the shader has been compiled */
852
+ a(this, "isCompiled", !1);
853
+ /** Original and compiled shader parsers */
854
+ a(this, "original");
855
+ /** Compiled parser will be updated with rewritten source after processing imports */
856
+ a(this, "compiled");
857
+ /** Collected requirements from imports */
858
+ a(this, "requirements", {
859
+ uniforms: /* @__PURE__ */ new Map(),
860
+ functions: /* @__PURE__ */ new Map()
861
+ });
862
+ this.original = new w(e), this.compiled = new w(e);
863
+ }
864
+ /**
865
+ * Detect WebGL version from shader source
866
+ */
867
+ version() {
868
+ return this.original.version();
869
+ }
870
+ /**
871
+ * Get the original source code of the shader
872
+ */
873
+ source() {
874
+ return this.original.source;
875
+ }
876
+ /**
877
+ * Force recompilation of the shader, reprocessing all imports and rewrites
878
+ * It's not necessary to call this manually because the state gets lost whenever the shader is switched out and back in.
879
+ */
880
+ recompile() {
881
+ return this.isCompiled = !1, this.compile();
882
+ }
883
+ /**
884
+ * Compile the shader source, resolving all imports
885
+ */
886
+ compile() {
887
+ return this.isCompiled ? this.compiled.source : (this.original.parse().imports.length > 0 && this.processImports(), this.compiled.setSource(this.build()), this.isCompiled = !0, this.compiled.source);
888
+ }
889
+ /**
890
+ * Process all #import directives
891
+ */
892
+ processImports() {
893
+ const e = this.original.parse(), n = e.functions.flatMap((t) => t.dependencies.filter((i) => i.type === "mention").map((i) => ({
894
+ name: i.name.split(".")[0],
895
+ uniform: i.name.split(".")[1]
896
+ })));
897
+ for (const t of e.imports) {
898
+ const i = b.resolve(t.module), r = i.extract(t.name);
899
+ let s = n.filter(
900
+ (l) => l.name === r.function.name
901
+ );
902
+ if (s.length > 0) {
903
+ const l = i.getDefinition().uniforms;
904
+ if (s = s.filter((f) => {
905
+ const c = l.find(
906
+ (m) => m.name === `u_${f.uniform}` || m.name === f.uniform
907
+ );
908
+ if (c) {
909
+ r.dependencies.uniforms.some(
910
+ (p) => p.name === c.name
911
+ ) || r.dependencies.uniforms.push(c);
912
+ const m = n.indexOf(f);
913
+ return m > -1 && n.splice(m, 1), !1;
914
+ }
915
+ return !0;
916
+ }), s.length > 0)
917
+ throw new Z(
918
+ t.module,
919
+ r.function.name,
920
+ s[0].uniform
921
+ );
922
+ }
923
+ const o = i.copy();
924
+ this.processExtraction(r, t.alias, o.options), y.merge(t.module, o);
925
+ }
926
+ if (n.length > 0)
927
+ throw new ee(
928
+ n[0].name,
929
+ n[0].uniform
930
+ );
931
+ }
932
+ /**
933
+ * Process an extraction: rewrite names and collect as requirements
934
+ */
935
+ processExtraction(e, n, t = {}) {
936
+ const i = e.function, r = Math.random().toString(36).substring(2, 8), s = `${n}_${r}`;
937
+ for (let l = e.dependencies.functions.length - 1; l >= 0; l--) {
938
+ const f = e.dependencies.functions[l], c = this.rewriteFunction(f, n, {
939
+ uniforms: e.dependencies.uniforms,
940
+ functions: e.dependencies.functions,
941
+ unique: s
942
+ });
943
+ this.requirements.functions.set(c.name, c);
944
+ }
945
+ const o = this.rewriteFunction(i, n, {
946
+ uniforms: e.dependencies.uniforms,
947
+ functions: e.dependencies.functions,
948
+ rename: !0,
949
+ unique: s
950
+ });
951
+ this.requirements.functions.set(o.name, o);
952
+ for (const l of e.dependencies.uniforms) {
953
+ if (E.has(l.name)) continue;
954
+ const f = {
955
+ ...l,
956
+ name: `${s}_${l.name}${l.arrayNum ? `[${l.arrayNum}]` : ""}`
957
+ };
958
+ if (t[i.name]) {
959
+ const c = Object.entries(t[i.name]).find(
960
+ ([m, p]) => p.uniform === l.name
961
+ );
962
+ c && (c[1].uniform = `${s}_${l.name}`);
963
+ }
964
+ this.requirements.uniforms.set(f.name, f);
965
+ }
966
+ t[i.name] && n !== i.name && (t[n] = t[i.name], delete t[i.name]);
967
+ }
968
+ /**
969
+ * Rewrite a function: namespace all uniform and helper function references
970
+ */
971
+ rewriteFunction(e, n, t = { rename: !1, uniforms: [], functions: [], unique: "" }) {
972
+ const i = new Set(t.uniforms.map((c) => c.name)), r = new Set(t.functions.map((c) => c.name)), s = [], o = t.unique ? t.unique : n;
973
+ for (const c of e.dependencies)
974
+ if (c.index !== void 0)
975
+ if (c.type === "uniform" && i.has(c.name)) {
976
+ if (E.has(c.name)) continue;
977
+ s.push({
978
+ index: c.index,
979
+ oldText: c.name,
980
+ newText: `${o}_${c.name}`
981
+ });
982
+ } else c.type === "function" && r.has(c.name) && s.push({
983
+ index: c.index,
984
+ oldText: c.name,
985
+ newText: `${o}_${c.name}`
986
+ });
987
+ const l = this.applyRewrites(e.body, s), f = t.rename ? n : `${o}_${e.name}`;
988
+ return {
989
+ ...e,
990
+ name: f,
991
+ body: l
992
+ };
993
+ }
994
+ /**
995
+ * Apply rewrite operations to a string, processing from end to start
996
+ */
997
+ applyRewrites(e, n) {
998
+ const t = [...n].sort((r, s) => s.index - r.index);
999
+ let i = e;
1000
+ for (const r of t)
1001
+ i = i.slice(0, r.index) + r.newText + i.slice(r.index + r.oldText.length);
1002
+ return i;
1003
+ }
1004
+ /**
1005
+ * Build the final compiled shader
1006
+ */
1007
+ build() {
1008
+ const e = this.original.parse();
1009
+ let n = this.original.source;
1010
+ n = this.removeImportLines(n, e);
1011
+ const t = this.findInsertionPointForUniforms(n), i = this.generateUniformsCode();
1012
+ i && (n = n.slice(0, t) + i + `
1013
+ ` + n.slice(t));
1014
+ const r = this.findInsertionPointForFunctions(n), s = this.generateFunctionCode();
1015
+ return s && (n = n.slice(0, r) + s + n.slice(r)), n = n.replace(/\n{3,}/g, `
1016
+
1017
+ `), n = this.replaceMentions(n, e), n;
1018
+ }
1019
+ replaceMentions(e, n) {
1020
+ let t = e;
1021
+ const i = n.functions.filter(
1022
+ (r) => r.dependencies.some((s) => s.type === "mention")
1023
+ );
1024
+ for (const r of i) {
1025
+ const s = r.dependencies.filter((o) => o.type === "mention");
1026
+ for (const o of s) {
1027
+ const l = o.name.split("."), f = new RegExp(`\\b${l[0]}_(\\w+)_${l[1]}\\b`, "g"), c = this.requirements.uniforms.keys().find((p) => {
1028
+ var g;
1029
+ return ((g = p.match(f)) == null ? void 0 : g[0]) === p;
1030
+ });
1031
+ if (!c)
1032
+ throw new ne(
1033
+ o.name,
1034
+ r.name
1035
+ );
1036
+ const m = new RegExp(`@\\b${o.name}\\b`, "g");
1037
+ t = t.replace(m, c);
1038
+ }
1039
+ }
1040
+ return t;
1041
+ }
1042
+ /**
1043
+ * Remove #import lines from shader source
1044
+ */
1045
+ removeImportLines(e, n) {
1046
+ const t = e.split(`
1047
+ `), i = new Set(n.imports.map((r) => r.line));
1048
+ return t.filter((r, s) => {
1049
+ const o = i.has(s + 1);
1050
+ if (!o && r.trim() === "" && s > 0) {
1051
+ const l = s - 1;
1052
+ if (i.has(l + 1))
1053
+ return !1;
1054
+ }
1055
+ return !o;
1056
+ }).join(`
1057
+ `);
1058
+ }
1059
+ /**
1060
+ * Find insertion point for uniforms (after existing uniforms)
1061
+ */
1062
+ findInsertionPointForUniforms(e) {
1063
+ const n = new w(e).parse(), t = n.uniforms.find(
1064
+ (o) => o.line === Math.max(...n.uniforms.map((l) => l.line ?? 0))
1065
+ ), i = e.split(`
1066
+ `);
1067
+ let r = 0;
1068
+ if (t && t.line)
1069
+ r = t.line;
1070
+ else
1071
+ for (let o = 0; o < i.length; o++) {
1072
+ const l = i[o].trim();
1073
+ if (l.startsWith("#version")) {
1074
+ r = o + 1;
1075
+ continue;
1076
+ }
1077
+ if (l.startsWith("precision ")) {
1078
+ r = o + 1;
1079
+ continue;
1080
+ }
1081
+ if (l === "" || l.startsWith("//")) {
1082
+ r === o && (r = o + 1);
1083
+ continue;
1084
+ }
512
1085
  break;
1086
+ }
1087
+ let s = 0;
1088
+ for (let o = 0; o < r; o++)
1089
+ s += i[o].length + 1;
1090
+ return s;
1091
+ }
1092
+ findInsertionPointForFunctions(e) {
1093
+ const n = new w(e).parse(), t = n.functions.find(
1094
+ (o) => o.line === Math.min(...n.functions.map((l) => l.line ?? 1 / 0))
1095
+ ), i = e.split(`
1096
+ `);
1097
+ let r = 0;
1098
+ if (t && t.line)
1099
+ r = t.line - 2;
1100
+ else
1101
+ throw new W();
1102
+ let s = 0;
1103
+ for (let o = 0; o < r; o++)
1104
+ s += i[o].length + 1;
1105
+ return s;
1106
+ }
1107
+ /**
1108
+ * Generate GLSL code for uniforms
1109
+ */
1110
+ generateUniformsCode() {
1111
+ const e = [];
1112
+ if (this.requirements.uniforms.size > 0)
1113
+ for (const n of this.checkUniformsPresence())
1114
+ e.push(`uniform ${n.type} ${n.name};`);
1115
+ return e.length === 0 ? "" : e.join(`
1116
+ `) + `
1117
+ `;
1118
+ }
1119
+ /**
1120
+ * Generate GLSL code for functions
1121
+ */
1122
+ generateFunctionCode() {
1123
+ const e = [];
1124
+ if (this.requirements.functions.size > 0)
1125
+ for (const n of this.checkFunctionsPresence()) {
1126
+ const t = n.params.map((i) => `${i.type} ${i.name}`).join(", ");
1127
+ e.push(`
1128
+ ${n.type} ${n.name}(${t}) ${n.body}`);
1129
+ }
1130
+ return e.length === 0 ? "" : e.join(`
1131
+ `) + `
1132
+ `;
1133
+ }
1134
+ /**
1135
+ * Check which required uniforms are missing from the original shader
1136
+ */
1137
+ checkUniformsPresence() {
1138
+ const e = this.original.parse(), n = [], t = this.requirements.uniforms;
1139
+ for (const [i, r] of t) {
1140
+ const s = e.uniforms.find((o) => o.name === i);
1141
+ if (!s) {
1142
+ n.push(r);
1143
+ continue;
1144
+ }
1145
+ if (s.type !== r.type)
1146
+ throw new C(
1147
+ "uniform",
1148
+ i,
1149
+ r.type,
1150
+ s.type
1151
+ );
513
1152
  }
1153
+ return n;
1154
+ }
1155
+ /**
1156
+ * Check which required functions are missing from the original shader
1157
+ */
1158
+ checkFunctionsPresence() {
1159
+ const e = this.original.parse(), n = [], t = this.requirements.functions;
1160
+ for (const [i, r] of t) {
1161
+ const s = e.functions.find((o) => o.name === i);
1162
+ if (!s) {
1163
+ n.push(r);
1164
+ continue;
1165
+ }
1166
+ if (s.type !== r.type)
1167
+ throw new C(
1168
+ "function",
1169
+ i,
1170
+ r.type,
1171
+ s.type
1172
+ );
1173
+ }
1174
+ return n;
1175
+ }
1176
+ }
1177
+ class v extends F {
1178
+ constructor(n, t, i = {}) {
1179
+ super(t);
1180
+ a(this, "name");
1181
+ a(this, "options", {});
1182
+ this.name = n, this.options = this.resolveOptions(i);
1183
+ }
1184
+ resolveOptions(n) {
1185
+ if (!(n != null && n.default)) return n || {};
1186
+ const t = this.original.parse(), i = n.default;
1187
+ for (const r of t.functions)
1188
+ if (!(r.name === "main" || r.name === "default"))
1189
+ if (n[r.name]) {
1190
+ const s = n[r.name];
1191
+ for (const o in i)
1192
+ o in s || (s[o] = i[o]);
1193
+ } else
1194
+ n[r.name] = { ...i };
1195
+ return delete n.default, n || {};
1196
+ }
1197
+ /**
1198
+ * Define a module with given name and source, and register it
1199
+ */
1200
+ static define(n) {
1201
+ const { name: t, source: i, options: r } = n;
1202
+ if (t === "sandbox" || t.startsWith("sandbox/"))
1203
+ throw new Q(t);
1204
+ const s = new v(t, i, r);
1205
+ if (b.has(t))
1206
+ throw new J(t);
1207
+ return b.register(t, s), s;
1208
+ }
1209
+ /**
1210
+ * Resolve a module by name from the registry, throwing an error if not found
1211
+ */
1212
+ static resolve(n) {
1213
+ return b.resolve(n);
1214
+ }
1215
+ /**
1216
+ * Create a copy of the module. To unplug references to the original object.
1217
+ * Used when copying module to the runtime registry to allow independent runtime changes to options without affecting the original module definition.
1218
+ */
1219
+ copy(n = "original") {
1220
+ return new v(
1221
+ this.name,
1222
+ this[n].source,
1223
+ JSON.parse(JSON.stringify(this.options))
1224
+ );
1225
+ }
1226
+ /**
1227
+ * Merge options from another module into this one, without affecting the original module definition.
1228
+ * Used when merging imported modules into the runtime registry.
1229
+ */
1230
+ merge(n) {
1231
+ this.options = this.options || {};
1232
+ const t = this.getDefinition().uniforms.map((i) => i.name);
1233
+ for (const [i, r] of Object.entries(n.options ?? {}))
1234
+ if (!this.options[i]) this.options[i] = r;
1235
+ else
1236
+ for (const [s, o] of Object.entries(r))
1237
+ t.includes(o.uniform) || (this.options[i][s] = o);
1238
+ }
1239
+ /**
1240
+ * Get the module definition
1241
+ */
1242
+ getDefinition() {
1243
+ return this.compile(), {
1244
+ name: this.name,
1245
+ methods: this.compiled.parse().functions.map((n) => n.name).filter((n) => n !== "main" && n !== "default"),
1246
+ uniforms: this.compiled.parse().uniforms.map((n) => ({ name: n.name, type: n.type })),
1247
+ options: this.options
1248
+ };
1249
+ }
1250
+ /**
1251
+ * Extract a method with all its dependencies
1252
+ */
1253
+ extract(n) {
1254
+ if (this.compile(), n === "main")
1255
+ throw new K(this.name);
1256
+ if (n === "default")
1257
+ throw new X(this.name);
1258
+ const t = this.compiled.parse(), i = t.functions.find((o) => o.name === n);
1259
+ if (!i)
1260
+ throw new Y(this.name, n);
1261
+ const r = /* @__PURE__ */ new Map(), s = /* @__PURE__ */ new Map();
1262
+ return this.collectDependencies({
1263
+ current: i,
1264
+ scope: {
1265
+ functions: t.functions,
1266
+ uniforms: t.uniforms
1267
+ },
1268
+ collected: {
1269
+ functions: r,
1270
+ uniforms: s
1271
+ },
1272
+ visited: /* @__PURE__ */ new Set([n])
1273
+ }), {
1274
+ function: i,
1275
+ dependencies: {
1276
+ functions: Array.from(r.values()),
1277
+ uniforms: Array.from(s.values())
1278
+ }
1279
+ };
1280
+ }
1281
+ /**
1282
+ * Recursively collect all function and uniform dependencies
1283
+ */
1284
+ collectDependencies(n) {
1285
+ for (const t of n.current.dependencies)
1286
+ if (t.type === "function") {
1287
+ if (n.visited.has(t.name)) continue;
1288
+ const i = n.scope.functions.find((r) => r.name === t.name);
1289
+ i && (n.visited.add(t.name), n.collected.functions.set(t.name, i), this.collectDependencies({
1290
+ current: i,
1291
+ scope: {
1292
+ functions: n.scope.functions,
1293
+ uniforms: n.scope.uniforms
1294
+ },
1295
+ collected: {
1296
+ functions: n.collected.functions,
1297
+ uniforms: n.collected.uniforms
1298
+ },
1299
+ visited: n.visited
1300
+ }));
1301
+ } else if (t.type === "uniform") {
1302
+ const i = n.scope.uniforms.find(
1303
+ (r) => r.name === t.name
1304
+ );
1305
+ i && !n.collected.uniforms.has(t.name) && n.collected.uniforms.set(t.name, i);
1306
+ }
514
1307
  }
515
1308
  }
516
- const d = class d {
517
- constructor(t) {
518
- i(this, "gl");
519
- i(this, "program", null);
520
- i(this, "uniforms", /* @__PURE__ */ new Map());
521
- this.gl = t;
1309
+ class V {
1310
+ constructor(e = []) {
1311
+ a(this, "modules", /* @__PURE__ */ new Map());
1312
+ e.forEach((n) => {
1313
+ this.register(n.name, n);
1314
+ });
1315
+ }
1316
+ /**
1317
+ * Compile all registered modules.
1318
+ */
1319
+ compile() {
1320
+ this.modules.forEach((e) => {
1321
+ e.compile();
1322
+ });
1323
+ }
1324
+ /**
1325
+ * Get the list of available shader modules.
1326
+ */
1327
+ available() {
1328
+ return Array.from(this.modules.values()).map(
1329
+ (e) => e.getDefinition()
1330
+ );
1331
+ }
1332
+ /**
1333
+ * Get the list of uniforms required by the currently registered modules.
1334
+ * This is used to automatically set up the uniforms in the shader based on the modules in use.
1335
+ */
1336
+ defaults() {
1337
+ const e = {};
1338
+ return this.modules.forEach((n) => {
1339
+ const t = n.getDefinition();
1340
+ if (t.options)
1341
+ for (const i in t.options) {
1342
+ const r = t.options[i];
1343
+ for (const s in r) {
1344
+ const o = r[s];
1345
+ o.default !== void 0 && !t.uniforms.map((l) => l.name).includes(o.uniform) && (e[o.uniform] = o.default);
1346
+ }
1347
+ }
1348
+ }), e;
1349
+ }
1350
+ /**
1351
+ * Resolve the options from the module definitions for a given function name.
1352
+ */
1353
+ resolveOptions(e) {
1354
+ for (const n of this.modules.values())
1355
+ if (n.options && n.options[e])
1356
+ return n.options[e];
1357
+ return null;
1358
+ }
1359
+ /**
1360
+ * Register a new module in the registry.
1361
+ */
1362
+ register(e, n) {
1363
+ this.modules.set(e, n);
1364
+ }
1365
+ /**
1366
+ * Merge a module into the registry. If a module with the same name already exists, options will be merged together.
1367
+ */
1368
+ merge(e, n) {
1369
+ if (!this.modules.has(e)) return this.register(e, n);
1370
+ const t = this.modules.get(e);
1371
+ t.merge(n), this.modules.set(e, t);
1372
+ }
1373
+ /**
1374
+ * Resolve a module by name. Throws an error if the module is not found.
1375
+ */
1376
+ resolve(e) {
1377
+ const n = this.modules.get(e);
1378
+ if (!n)
1379
+ throw new j(e);
1380
+ return n;
1381
+ }
1382
+ /**
1383
+ * Check if a module exists in the registry by name.
1384
+ */
1385
+ has(e) {
1386
+ return this.modules.has(e);
1387
+ }
1388
+ /**
1389
+ * Check if the registry is empty (no modules registered).
1390
+ */
1391
+ isEmpty() {
1392
+ return this.modules.size === 0;
1393
+ }
1394
+ /**
1395
+ * Remove a module from the registry by name.
1396
+ */
1397
+ remove(e) {
1398
+ this.modules.delete(e);
1399
+ }
1400
+ /**
1401
+ * Load multiple modules into the registry at once.
1402
+ */
1403
+ load(e) {
1404
+ e.forEach((n) => {
1405
+ this.register(n.name, n);
1406
+ });
1407
+ }
1408
+ /**
1409
+ * Clear all registered modules from the registry.
1410
+ */
1411
+ clear() {
1412
+ this.modules.clear();
1413
+ }
1414
+ }
1415
+ const oe = `// ─── Constants ──────────────────────────────────────────────
1416
+
1417
+ const float PI = 3.14159265359;
1418
+ const float TAU = 6.28318530718;
1419
+
1420
+ // ─── UV Transforms ──────────────────────────────────────────
1421
+
1422
+ /**
1423
+ * Center UV coordinates — remap to range where (0,0) is canvas center.
1424
+ * Aspect-ratio corrected using resolution length.
1425
+ * @uv-modifier
1426
+ */
1427
+ vec2 center(vec2 uv) {
1428
+ return (uv - 0.5 * u_resolution) / length(u_resolution);
1429
+ }
1430
+
1431
+ /**
1432
+ * Translate UV coordinates by offset.
1433
+ * @uv-modifier
1434
+ */
1435
+ vec2 translate(vec2 uv, vec2 offset) {
1436
+ return uv - offset;
1437
+ }
1438
+
1439
+ /**
1440
+ * Scale UV coordinates by factor around origin.
1441
+ * @uv-modifier
1442
+ */
1443
+ vec2 scale(vec2 uv, float factor) {
1444
+ return uv * factor;
1445
+ }
1446
+
1447
+ /**
1448
+ * Zoom — scale UV with aspect ratio correction.
1449
+ * Expects centered UV (use center() first).
1450
+ * Pushes UV outward/inward from origin.
1451
+ * factor = zoom factor (>1 = zoom in, <1 = zoom out)
1452
+ * @uv-modifier
1453
+ */
1454
+ vec2 zoom(vec2 uv, float factor) {
1455
+ return uv / max(factor, 0.001);
1456
+ }
1457
+
1458
+ /**
1459
+ * Normalize UV to range -1 to 1 with aspect ratio correction.
1460
+ * @uv-modifier
1461
+ */
1462
+ vec2 norm(vec2 uv) {
1463
+ uv *= min(u_resolution.x, u_resolution.y) / length(u_resolution);
1464
+ return uv;
1465
+ }
1466
+
1467
+ /**
1468
+ * Rotate UV coordinates around origin by angle (radians).
1469
+ * @uv-modifier
1470
+ */
1471
+ vec2 rotate(vec2 uv, float angle) {
1472
+ float c = cos(angle);
1473
+ float s = sin(angle);
1474
+ return vec2(uv.x * c - uv.y * s, uv.x * s + uv.y * c);
1475
+ }
1476
+
1477
+ /**
1478
+ * Tile — repeat coordinates in a grid of given size.
1479
+ * @uv-modifier
1480
+ */
1481
+ vec2 tile(vec2 uv, float size) {
1482
+ return fract(uv / size) * size;
1483
+ }
1484
+
1485
+ /**
1486
+ * Cartesian to polar coordinates.
1487
+ * Returns vec2(angle, radius) where angle is -PI to PI.
1488
+ */
1489
+ vec2 polar(vec2 uv) {
1490
+ return vec2(atan(uv.y, uv.x), length(uv));
1491
+ }
1492
+
1493
+ /**
1494
+ * Aspect ratio correction — makes UV square regardless of canvas shape.
1495
+ * @uv-modifier
1496
+ */
1497
+ vec2 aspect(vec2 uv) {
1498
+ return uv * vec2(u_resolution.x / u_resolution.y, 1.0);
1499
+ }
1500
+
1501
+ // ─── Math Utilities ─────────────────────────────────────────
1502
+
1503
+ /**
1504
+ * Remap a value from one range to another.
1505
+ * map(0.5, 0.0, 1.0, -1.0, 1.0) → 0.0
1506
+ */
1507
+ float map(float value, float inMin, float inMax, float outMin, float outMax) {
1508
+ return outMin + (value - inMin) * (outMax - outMin) / (inMax - inMin);
1509
+ }
1510
+
1511
+ // ─── Noise & Random ─────────────────────────────────────────
1512
+
1513
+ /**
1514
+ * Pseudo-random hash from 2D coordinates.
1515
+ * Returns float in 0–1 range.
1516
+ */
1517
+ float hash(vec2 p) {
1518
+ return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453123);
1519
+ }
1520
+
1521
+ /**
1522
+ * 2D pseudo-random hash.
1523
+ * Returns vec2 in 0–1 range — used for point scattering (worley etc).
1524
+ */
1525
+ vec2 hash2(vec2 p) {
1526
+ p = vec2(dot(p, vec2(127.1, 311.7)), dot(p, vec2(269.5, 183.3)));
1527
+ return fract(sin(p) * 43758.5453123);
1528
+ }
1529
+
1530
+ /**
1531
+ * Smooth value noise — interpolated hash grid.
1532
+ */
1533
+ float noise(vec2 p) {
1534
+ vec2 i = floor(p);
1535
+ vec2 f = fract(p);
1536
+ vec2 u = f * f * (3.0 - 2.0 * f);
1537
+
1538
+ return mix(
1539
+ mix(hash(i), hash(i + vec2(1.0, 0.0)), u.x),
1540
+ mix(hash(i + vec2(0.0, 1.0)), hash(i + vec2(1.0, 1.0)), u.x),
1541
+ u.y
1542
+ );
1543
+ }
1544
+
1545
+ /**
1546
+ * Fractal Brownian Motion — 6 octaves of layered noise.
1547
+ * Returns ~0–1 range. Great for clouds, terrain, organic textures.
1548
+ */
1549
+ float fbm(vec2 p) {
1550
+ float value = 0.0;
1551
+ float amplitude = 0.5;
1552
+
1553
+ for (int i = 0; i < 6; i++) {
1554
+ value += amplitude * noise(p);
1555
+ p *= 2.0;
1556
+ amplitude *= 0.5;
1557
+ }
1558
+
1559
+ return value;
1560
+ }
1561
+
1562
+ /**
1563
+ * Worley (cellular / voronoi) noise.
1564
+ * Returns distance to nearest random cell point.
1565
+ * Great for cells, cracks, organic patterns.
1566
+ */
1567
+ float worley(vec2 p) {
1568
+ vec2 i = floor(p);
1569
+ vec2 f = fract(p);
1570
+ float minDist = 1.0;
1571
+
1572
+ for (int y = -1; y <= 1; y++) {
1573
+ for (int x = -1; x <= 1; x++) {
1574
+ vec2 neighbor = vec2(float(x), float(y));
1575
+ vec2 point = hash2(i + neighbor);
1576
+ float dist = length(neighbor + point - f);
1577
+ minDist = min(minDist, dist);
1578
+ }
1579
+ }
1580
+
1581
+ return minDist;
1582
+ }
1583
+
1584
+ /**
1585
+ * Waves — layered sine wave interference pattern.
1586
+ * Creates clean, regular wave lines like fabric or moiré.
1587
+ * frequency = wave density (5.0 = fabric, 10.0 = fine lines)
1588
+ * Returns 0–1 range.
1589
+ */
1590
+ float waves(vec2 p, float frequency) {
1591
+ return 0.6 + 0.4 * sin(
1592
+ frequency * (p.x + p.y + cos(3.0 * p.x + 5.0 * p.y)) +
1593
+ sin(frequency * 4.0 * (p.x + p.y))
1594
+ );
1595
+ }
1596
+
1597
+ /**
1598
+ * Voronoi cell lookup — returns the position of the nearest cell point.
1599
+ * Same algorithm as worley, but returns the point instead of distance.
1600
+ * Great for UV snapping, cell-based effects.
1601
+ */
1602
+ vec2 voronoi(vec2 p) {
1603
+ vec2 i = floor(p);
1604
+ vec2 f = fract(p);
1605
+ float minDist = 10.0;
1606
+ vec2 nearest = vec2(0.0);
1607
+
1608
+ for (int y = -1; y <= 1; y++) {
1609
+ for (int x = -1; x <= 1; x++) {
1610
+ vec2 neighbor = vec2(float(x), float(y));
1611
+ vec2 point = hash2(i + neighbor);
1612
+ float dist = length(neighbor + point - f);
1613
+ if (dist < minDist) {
1614
+ minDist = dist;
1615
+ nearest = i + neighbor + point;
1616
+ }
1617
+ }
1618
+ }
1619
+
1620
+ return nearest;
1621
+ }
1622
+
1623
+ void main() {}
1624
+ `, ae = `/**
1625
+ * Hex integer to RGB.
1626
+ * Usage: hex(0xFF6600) → orange
1627
+ */
1628
+ vec3 hex(int value) {
1629
+ float v = float(value);
1630
+ float r = floor(v / 65536.0);
1631
+ float g = floor((v - r * 65536.0) / 256.0);
1632
+ float b = v - r * 65536.0 - g * 256.0;
1633
+ return vec3(r, g, b) / 255.0;
1634
+ }
1635
+
1636
+ /**
1637
+ * RGB 0–255 to normalized RGB 0–1.
1638
+ * Usage: rgb255(255.0, 128.0, 0.0) → orange
1639
+ */
1640
+ vec3 rgb255(float r, float g, float b) {
1641
+ return vec3(r, g, b) / 255.0;
1642
+ }
1643
+
1644
+ /**
1645
+ * HSV to RGB.
1646
+ * Input: vec3(hue 0–1, saturation 0–1, value 0–1)
1647
+ */
1648
+ vec3 hsv(vec3 c) {
1649
+ vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
1650
+ vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
1651
+ return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
1652
+ }
1653
+
1654
+ /**
1655
+ * HSL to RGB.
1656
+ * Input: vec3(hue 0–1, saturation 0–1, lightness 0–1)
1657
+ */
1658
+ vec3 hsl(vec3 c) {
1659
+ vec3 rgb = clamp(abs(mod(c.x * 6.0 + vec3(0.0, 4.0, 2.0), 6.0) - 3.0) - 1.0, 0.0, 1.0);
1660
+ return c.z + c.y * (rgb - 0.5) * (1.0 - abs(2.0 * c.z - 1.0));
1661
+ }
1662
+
1663
+ /**
1664
+ * Linear gradient between two colors.
1665
+ * t is clamped to 0–1.
1666
+ */
1667
+ vec3 gradient(vec3 a, vec3 b, float t) {
1668
+ return mix(a, b, clamp(t, 0.0, 1.0));
1669
+ }
1670
+
1671
+ /**
1672
+ * 3-stop gradient.
1673
+ * t=0 → a, t=0.5 → b, t=1 → c
1674
+ */
1675
+ vec3 gradient3(vec3 a, vec3 b, vec3 c, float t) {
1676
+ t = clamp(t, 0.0, 1.0);
1677
+ return t < 0.5
1678
+ ? mix(a, b, t * 2.0)
1679
+ : mix(b, c, (t - 0.5) * 2.0);
1680
+ }
1681
+
1682
+ /**
1683
+ * Cosine palette — Inigo Quilez formula.
1684
+ * color = a + b * cos(2π(c·t + d))
1685
+ * Generates infinite smooth color ramps from 4 vec3 params.
1686
+ */
1687
+ vec3 palette(vec3 a, vec3 b, vec3 c, vec3 d, float t) {
1688
+ return a + b * cos(6.28318 * (c * t + d));
1689
+ }
1690
+
1691
+ /**
1692
+ * Banded gradient — 3-zone color mapping with sharp transitions.
1693
+ * Uses tent functions instead of linear interpolation.
1694
+ * t=0 → b (center), t=1 → a (middle), t=2 → c (outer).
1695
+ * sharpness = transition width (2.0 = sharp, 1.0 = soft).
1696
+ */
1697
+ vec3 bands(vec3 a, vec3 b, vec3 c, float t, float sharpness) {
1698
+ float w1 = max(0.0, 1.0 - sharpness * abs(1.0 - t));
1699
+ float w2 = max(0.0, 1.0 - sharpness * abs(t));
1700
+ float w3 = 1.0 - min(1.0, w1 + w2);
1701
+ return a * w1 + b * w2 + c * w3;
1702
+ }
1703
+
1704
+ /**
1705
+ * Iridescent — iterative interference color pattern.
1706
+ * Generates rainbow-like colors from UV through trigonometric iteration.
1707
+ * Creates shimmering, oil-slick-like color fields.
1708
+ * time = animation time (pass u_time), speed = animation speed
1709
+ */
1710
+ vec3 iridescent(vec2 uv, float time, float speed) {
1711
+ float d = -time * 0.5 * speed;
1712
+ float a = 0.0;
1713
+ for (int i = 0; i < 8; i++) {
1714
+ a += cos(float(i) - d - a * uv.x);
1715
+ d += sin(uv.y * float(i) + a);
1716
+ }
1717
+ d += time * 0.5 * speed;
1718
+ vec3 col = vec3(cos(uv * vec2(d, a)) * 0.6 + 0.4, cos(a + d) * 0.5 + 0.5);
1719
+ return cos(col * cos(vec3(d, a, 2.5)) * 0.5 + 0.5);
1720
+ }
1721
+
1722
+ void main() {}
1723
+ `, le = `// ─── Time Shapers ──────────────────────────────────────────
1724
+ // Convert raw time (u_time) to normalized 0–1 range.
1725
+ // Output feeds directly into easing functions.
1726
+
1727
+ /**
1728
+ * Loop — repeating sawtooth 0→1→0→1...
1729
+ * duration = cycle length in seconds
1730
+ */
1731
+ float loop(float t, float duration) {
1732
+ return fract(t / duration);
1733
+ }
1734
+
1735
+ /**
1736
+ * Pingpong — triangle wave 0→1→0→1...
1737
+ * duration = half-cycle length (0→1 takes \`duration\` seconds)
1738
+ */
1739
+ float pingpong(float t, float duration) {
1740
+ float m = mod(t, duration * 2.0);
1741
+ return m < duration ? m / duration : 2.0 - m / duration;
1742
+ }
1743
+
1744
+ /**
1745
+ * Once — single play 0→1, then stays at 1.
1746
+ * duration = animation length in seconds
1747
+ * Use with delay: once(u_time - 2.0, 3.0) starts at 2s, takes 3s.
1748
+ */
1749
+ float once(float t, float duration) {
1750
+ return clamp(t / duration, 0.0, 1.0);
1751
+ }
1752
+
1753
+ /**
1754
+ * Reverse — flip animation direction.
1755
+ * Turns 0→1 into 1→0. Works with any easing or shaper.
1756
+ * Usage: reverse(spring(loop(u_time, 2.0))) for spring zoom-in.
1757
+ */
1758
+ float reverse(float t) {
1759
+ return 1.0 - t;
1760
+ }
1761
+
1762
+ // ─── Easing Curves ─────────────────────────────────────────
1763
+ // All take t (0–1) and return shaped t (0–1).
1764
+ // Compose with time shapers: ease_in(loop(u_time, 2.0))
1765
+
1766
+ /**
1767
+ * Ease in — cubic acceleration. Slow start, fast end.
1768
+ */
1769
+ float ease_in(float t) {
1770
+ return t * t * t;
1771
+ }
1772
+
1773
+ /**
1774
+ * Ease out — cubic deceleration. Fast start, slow end.
1775
+ */
1776
+ float ease_out(float t) {
1777
+ float f = 1.0 - t;
1778
+ return 1.0 - f * f * f;
1779
+ }
1780
+
1781
+ /**
1782
+ * Ease in-out — cubic smooth both ends.
1783
+ */
1784
+ float ease_in_out(float t) {
1785
+ return t < 0.5
1786
+ ? 4.0 * t * t * t
1787
+ : 1.0 - pow(-2.0 * t + 2.0, 3.0) * 0.5;
1788
+ }
1789
+
1790
+ /**
1791
+ * Spring — damped oscillation. Overshoots then settles at 1.
1792
+ */
1793
+ float spring(float t) {
1794
+ return 1.0 - exp(-6.0 * t) * cos(12.0 * t);
1795
+ }
1796
+
1797
+ /**
1798
+ * Bounce — bouncing ball landing. Multiple bounces settling at 1.
1799
+ */
1800
+ float bounce(float t) {
1801
+ if (t < 1.0 / 2.75) {
1802
+ return 7.5625 * t * t;
1803
+ } else if (t < 2.0 / 2.75) {
1804
+ t -= 1.5 / 2.75;
1805
+ return 7.5625 * t * t + 0.75;
1806
+ } else if (t < 2.5 / 2.75) {
1807
+ t -= 2.25 / 2.75;
1808
+ return 7.5625 * t * t + 0.9375;
1809
+ } else {
1810
+ t -= 2.625 / 2.75;
1811
+ return 7.5625 * t * t + 0.984375;
1812
+ }
1813
+ }
1814
+
1815
+ /**
1816
+ * Elastic — springy overshoot with oscillation.
1817
+ */
1818
+ float elastic(float t) {
1819
+ return sin(13.0 * 3.14159 * 0.5 * t) * pow(2.0, -10.0 * t) + 1.0;
1820
+ }
1821
+
1822
+ /**
1823
+ * Overshoot — goes past 1, then pulls back. Slight rubber-band feel.
1824
+ */
1825
+ float overshoot(float t) {
1826
+ float s = 1.70158;
1827
+ float f = 1.0 - t;
1828
+ return 1.0 - f * f * (f * (s + 1.0) - s);
1829
+ }
1830
+
1831
+ /**
1832
+ * Teleport — barely creeps, then near-instant jump with spring settle.
1833
+ * Duration mostly = wait time. Jump happens at ~85% of t.
1834
+ * Tiny slow drift → snap → spring overshoot → settle at 1.
1835
+ */
1836
+ float teleport(float t) {
1837
+ float jump = smoothstep(0.8, 0.85, t);
1838
+ float creep = pow(t / 0.8, 4.0) * 0.1 * (1.0 - jump);
1839
+ float s = max(t - 0.85, 0.0) * 6.667;
1840
+ float settle = exp(-5.0 * s) * sin(s * 15.0) * 0.15;
1841
+ return creep + jump + settle;
1842
+ }
1843
+
1844
+ void main() {}
1845
+ `, ce = `#import hash from 'sandbox'
1846
+ #import noise from 'sandbox'
1847
+ #import fbm from 'sandbox'
1848
+ #import voronoi from 'sandbox'
1849
+ #import worley from 'sandbox'
1850
+
1851
+ uniform float u_intensity;
1852
+
1853
+ // ─── UV Effects ─────────────────────────────────────────────
1854
+ // Each function takes UV and returns modified UV.
1855
+ // All use u_intensity uniform for configuration.
1856
+
1857
+ /**
1858
+ * Pixelate — blocky mosaic effect.
1859
+ * Snaps UV to a grid. Higher intensity = larger pixels.
1860
+ * intensity = pixel count along shortest axis (default: 20)
1861
+ * @uv-modifier
1862
+ */
1863
+ vec2 pixelate(vec2 uv, float intensity) {
1864
+ float size = length(u_resolution) / (max(intensity, 1.0) * 10.0);
1865
+ return floor(uv / size) * size;
1866
+ }
1867
+
1868
+ /**
1869
+ * Twist — spiral distortion from center outward.
1870
+ * Expects centered UV (use center() first).
1871
+ * intensity = twist strength (0.4 = subtle, 1.0 = full spiral)
1872
+ * @uv-modifier
1873
+ */
1874
+ vec2 twist(vec2 uv, float intensity) {
1875
+ float dist = length(uv);
1876
+ float angle = atan(uv.y, uv.x) - intensity * 20.0 * dist;
1877
+ return vec2(cos(angle), sin(angle)) * dist;
1878
+ }
1879
+
1880
+ /**
1881
+ * Ripple — concentric wave distortion from center.
1882
+ * Expects centered UV (use center() first).
1883
+ * Creates water-drop-like rings.
1884
+ * intensity = wave amplitude (0.5 = gentle, 2.0 = strong)
1885
+ * @uv-modifier
1886
+ */
1887
+ vec2 ripple(vec2 uv, float intensity) {
1888
+ float dist = length(uv);
1889
+ float wave = sin(dist * 40.0 - u_time * 3.0) * intensity * 0.02;
1890
+ return uv + normalize(uv + 0.001) * wave;
1891
+ }
1892
+
1893
+ /**
1894
+ * Fisheye — barrel distortion from center.
1895
+ * Expects centered UV (use center() first).
1896
+ * Bends space outward like a fisheye lens.
1897
+ * intensity = distortion power (0.5 = subtle, 2.0 = extreme)
1898
+ * @uv-modifier
1899
+ */
1900
+ vec2 fisheye(vec2 uv, float intensity) {
1901
+ float dist = length(uv);
1902
+ float power = pow(dist, intensity) / dist;
1903
+ return uv * power;
1904
+ }
1905
+
1906
+ /**
1907
+ * Wobble — animated sine wave displacement.
1908
+ * Creates a jelly-like horizontal wobble.
1909
+ * intensity = wobble amount (1.0 = gentle, 5.0 = wild)
1910
+ * @uv-modifier
1911
+ */
1912
+ vec2 wobble(vec2 uv, float intensity) {
1913
+ uv.x += sin(uv.y * 10.0 + u_time * 2.0) * intensity * 0.01;
1914
+ uv.y += cos(uv.x * 10.0 + u_time * 2.0) * intensity * 0.01;
1915
+ return uv;
1916
+ }
1917
+
1918
+ /**
1919
+ * Organic — iterative fluid morph distortion.
1920
+ * Creates marble-like flowing patterns.
1921
+ * intensity = animation speed (3.0 = default)
1922
+ * @uv-modifier
1923
+ */
1924
+ vec2 organic(vec2 uv, float intensity) {
1925
+ float speed = u_time * intensity;
1926
+ vec2 acc = vec2(uv.x + uv.y);
1927
+
1928
+ for (int i = 0; i < 5; i++) {
1929
+ acc += sin(max(uv.x, uv.y)) + uv;
1930
+ uv += 0.5 * vec2(
1931
+ cos(5.1123314 + 0.353 * acc.y + speed * 0.131121),
1932
+ sin(acc.x - 0.113 * speed)
1933
+ );
1934
+ uv -= cos(uv.x + uv.y) - sin(uv.x * 0.711 - uv.y);
1935
+ }
1936
+
1937
+ return uv;
1938
+ }
1939
+
1940
+ /**
1941
+ * Glitch — random horizontal line displacement.
1942
+ * Creates digital glitch artifact bands.
1943
+ * intensity = glitch strength (0.5 = subtle, 3.0 = heavy)
1944
+ * @uv-modifier
1945
+ */
1946
+ vec2 glitch(vec2 uv, float intensity) {
1947
+ float line = floor(uv.y * 50.0);
1948
+ float shift = hash(vec2(line, floor(u_time * 8.0)));
1949
+ shift = step(0.9, shift) * (shift - 0.9) * 10.0;
1950
+ uv.x += shift * intensity * 0.1 * u_resolution.x;
1951
+ return uv;
1952
+ }
1953
+
1954
+ /**
1955
+ * Mirror — reflect UV across a chosen axis.
1956
+ * Creates bilateral symmetry.
1957
+ * intensity = 0 for horizontal mirror, 1 for vertical mirror
1958
+ * @uv-modifier
1959
+ */
1960
+ vec2 mirror(vec2 uv, float intensity) {
1961
+ if (intensity < 0.5) {
1962
+ uv.x = abs(uv.x);
1963
+ } else {
1964
+ uv.y = abs(uv.y);
1965
+ }
1966
+ return uv;
1967
+ }
1968
+
1969
+ /**
1970
+ * Kaleidoscope — repeating angular symmetry.
1971
+ * Expects centered UV (use center() first).
1972
+ * Creates mandala-like radial repeats.
1973
+ * intensity = number of segments (4 = quad, 6 = hex, 8 = octa)
1974
+ * @uv-modifier
1975
+ */
1976
+ vec2 kaleidoscope(vec2 uv, float intensity) {
1977
+ float segments = max(intensity, 1.0);
1978
+ float angle = atan(uv.y, uv.x);
1979
+ float segment_angle = 6.28318 / segments;
1980
+ angle = mod(angle, segment_angle);
1981
+ angle = abs(angle - segment_angle * 0.5);
1982
+ float r = length(uv);
1983
+ return vec2(cos(angle), sin(angle)) * r;
1984
+ }
1985
+
1986
+ /**
1987
+ * Warp — domain warping using fractal noise.
1988
+ * Displaces UV by layered fbm noise for smoke/cloud-like distortion.
1989
+ * Animated over time.
1990
+ * intensity = warp strength (0.5 = subtle haze, 3.0 = heavy distortion)
1991
+ * @uv-modifier
1992
+ */
1993
+ vec2 warp(vec2 uv, float intensity) {
1994
+ vec2 offset = vec2(
1995
+ fbm(uv + u_time * 0.3),
1996
+ fbm(uv + vec2(5.2, 1.3) + u_time * 0.3)
1997
+ );
1998
+ return uv + offset * intensity * 0.1;
1999
+ }
2000
+
2001
+ /**
2002
+ * Displace — noise-based UV displacement.
2003
+ * Shifts UV using smooth value noise for a wavy, heat-haze look.
2004
+ * Animated over time.
2005
+ * intensity = displacement amount (1.0 = gentle, 5.0 = strong)
2006
+ * @uv-modifier
2007
+ */
2008
+ vec2 displace(vec2 uv, float intensity) {
2009
+ float nx = noise(uv * 5.0 + u_time);
2010
+ float ny = noise(uv * 5.0 + vec2(3.7, 7.1) + u_time);
2011
+ return uv + (vec2(nx, ny) - 0.5) * intensity * 0.05;
2012
+ }
2013
+
2014
+ /**
2015
+ * Shatter — voronoi cell-based UV snapping.
2016
+ * Snaps UV to nearest cell point, creating a broken-glass look.
2017
+ * intensity = cell density (5.0 = large shards, 20.0 = fine fragments)
2018
+ * @uv-modifier
2019
+ */
2020
+ vec2 shatter(vec2 uv, float intensity) {
2021
+ return voronoi(uv * intensity) / intensity;
2022
+ }
2023
+
2024
+ /**
2025
+ * Cells — cellular distortion using Worley distance.
2026
+ * Displaces UV outward from cell edges, like looking through textured glass.
2027
+ * intensity = cell scale (3.0 = large bubbles, 15.0 = fine texture)
2028
+ * @uv-modifier
2029
+ */
2030
+ vec2 cells(vec2 uv, float intensity) {
2031
+ float dist = worley(uv * intensity);
2032
+ vec2 grad = vec2(
2033
+ worley(uv * intensity + vec2(0.01, 0.0)) - dist,
2034
+ worley(uv * intensity + vec2(0.0, 0.01)) - dist
2035
+ );
2036
+ return uv + grad * dist * 2.0;
2037
+ }
2038
+
2039
+ void main() {}
2040
+ `, ue = `#import hash from 'sandbox'
2041
+
2042
+ uniform float u_intensity;
2043
+
2044
+ // ─── Color Filters ──────────────────────────────────────────
2045
+ // Each function takes a color and returns modified color.
2046
+ // All use u_intensity uniform for configuration.
2047
+
2048
+ /**
2049
+ * Contrast — adjust contrast around mid-gray.
2050
+ * intensity: 1.0 = original, >1 = more contrast, <1 = flatter
2051
+ * @color-modifier
2052
+ */
2053
+ vec3 contrast(vec3 color, float intensity) {
2054
+ return (color - 0.5) * intensity + 0.5;
2055
+ }
2056
+
2057
+ /**
2058
+ * Brightness — multiply overall brightness.
2059
+ * intensity: 1.0 = original, >1 = brighter, <1 = darker
2060
+ * @color-modifier
2061
+ */
2062
+ vec3 brightness(vec3 color, float intensity) {
2063
+ return color * intensity;
2064
+ }
2065
+
2066
+ /**
2067
+ * Saturate — adjust color saturation.
2068
+ * intensity: 0 = grayscale, 1.0 = original, >1 = oversaturated
2069
+ * @color-modifier
2070
+ */
2071
+ vec3 saturate(vec3 color, float intensity) {
2072
+ float gray = dot(color, vec3(0.299, 0.587, 0.114));
2073
+ return mix(vec3(gray), color, intensity);
2074
+ }
2075
+
2076
+ /**
2077
+ * Posterize — reduce color to N discrete levels per channel.
2078
+ * intensity = number of levels (4 = retro, 16 = subtle, 256 = nearly smooth)
2079
+ * @color-modifier
2080
+ */
2081
+ vec3 posterize(vec3 color, float intensity) {
2082
+ float levels = max(intensity, 1.0);
2083
+ return floor(color * levels + 0.5) / levels;
2084
+ }
2085
+
2086
+ /**
2087
+ * Threshold — convert to black and white based on cutoff.
2088
+ * intensity = cutoff point (0.5 = balanced, lower = more white)
2089
+ * @color-modifier
2090
+ */
2091
+ vec3 threshold(vec3 color, float intensity) {
2092
+ float avg = dot(color, vec3(0.299, 0.587, 0.114));
2093
+ return avg > intensity ? vec3(1.0) : vec3(0.0);
2094
+ }
2095
+
2096
+ /**
2097
+ * Invert — flip all color channels.
2098
+ * intensity: 0.0 = original, 1.0 = fully inverted, 0.5 = mid-gray
2099
+ * @color-modifier
2100
+ */
2101
+ vec3 invert(vec3 color, float intensity) {
2102
+ return mix(color, 1.0 - color, intensity);
2103
+ }
2104
+
2105
+ /**
2106
+ * Glow — luminance-based bloom, bright areas get amplified.
2107
+ * intensity = glow strength (0.5 = subtle, 2.0 = intense)
2108
+ * @color-modifier
2109
+ */
2110
+ vec3 glow(vec3 color, float intensity) {
2111
+ float luminance = dot(color, vec3(0.299, 0.587, 0.114));
2112
+ return color + color * luminance * intensity;
2113
+ }
2114
+
2115
+ /**
2116
+ * Grain — animated film noise overlay.
2117
+ * intensity = noise strength (0.1 = subtle, 0.5 = heavy)
2118
+ * @color-modifier
2119
+ */
2120
+ vec3 grain(vec3 color, vec2 uv, float intensity) {
2121
+ float n = (hash(uv * 1000.0 + u_time) - 0.5) * intensity;
2122
+ return color + n;
2123
+ }
2124
+
2125
+ /**
2126
+ * Vignette — darken canvas edges.
2127
+ * Applies circular falloff from center.
2128
+ * intensity = darkness strength (1.0 = subtle, 2.0 = strong)
2129
+ * @color-modifier
2130
+ */
2131
+ vec3 vignette(vec3 color, vec2 uv, float intensity) {
2132
+ vec2 centered = uv / u_resolution - 0.5;
2133
+ float dist = length(centered);
2134
+ float falloff = 1.0 - smoothstep(0.3, 0.7, dist * intensity);
2135
+ return color * falloff;
2136
+ }
2137
+
2138
+ /**
2139
+ * Sepia — warm brownish tint like old photographs.
2140
+ * intensity: 0.0 = original, 1.0 = full sepia
2141
+ * @color-modifier
2142
+ */
2143
+ vec3 sepia(vec3 color, float intensity) {
2144
+ vec3 s;
2145
+ s.r = dot(color, vec3(0.393, 0.769, 0.189));
2146
+ s.g = dot(color, vec3(0.349, 0.686, 0.168));
2147
+ s.b = dot(color, vec3(0.272, 0.534, 0.131));
2148
+ return mix(color, s, intensity);
2149
+ }
2150
+
2151
+ /**
2152
+ * Gamma — apply gamma correction curve.
2153
+ * intensity = gamma value (1.0 = linear, 2.2 = standard sRGB, <1 = brighten darks)
2154
+ * @color-modifier
2155
+ */
2156
+ vec3 gamma(vec3 color, float intensity) {
2157
+ return pow(max(color, 0.0), vec3(1.0 / max(intensity, 0.001)));
2158
+ }
2159
+
2160
+ /**
2161
+ * Tint — blend color toward a target tint.
2162
+ * intensity: 0.0 = original, 1.0 = fully tinted
2163
+ * @color-modifier
2164
+ */
2165
+ vec3 tint(vec3 color, vec3 tintColor, float intensity) {
2166
+ return mix(color, color * tintColor, intensity);
2167
+ }
2168
+
2169
+ /**
2170
+ * Highlights — add white light to bright areas.
2171
+ * Uses max channel brightness so even saturated colors trigger highlights.
2172
+ * intensity = highlight strength (0.2 = subtle sheen, 1.0 = strong gloss)
2173
+ * @color-modifier
2174
+ */
2175
+ vec3 highlights(vec3 color, float intensity) {
2176
+ float bright = max(color.r, max(color.g, color.b + 0.3));
2177
+ return color + max(bright * 5.5 - 4.0, 0.0) * intensity;
2178
+ }
2179
+
2180
+ /**
2181
+ * Bayer 8×8 threshold matrix — computed mathematically.
2182
+ * Returns threshold value 0–1 for ordered dithering.
2183
+ */
2184
+ float bayer8(vec2 p) {
2185
+ p = mod(floor(p), 8.0);
2186
+ float value = 0.0;
2187
+ float divisor = 1.0;
2188
+ float multiplier = 16.0;
2189
+
2190
+ for (int i = 0; i < 3; i++) {
2191
+ vec2 q = mod(floor(p / divisor), 2.0);
2192
+ value += (q.x * 2.0 + q.y * 3.0 - q.x * q.y * 4.0) * multiplier;
2193
+ divisor *= 2.0;
2194
+ multiplier *= 0.25;
2195
+ }
2196
+
2197
+ return value / 64.0;
2198
+ }
2199
+
2200
+ /**
2201
+ * Dither — ordered Bayer 8×8 dithering.
2202
+ * Reduces color to discrete levels with a threshold pattern.
2203
+ * Use pixelate() on UV before coloring for blocky dither cells.
2204
+ * intensity = number of color levels (2 = 1-bit, 4 = retro, 8 = subtle)
2205
+ * @color-modifier
2206
+ */
2207
+ vec3 dither(vec3 color, vec2 uv, float intensity) {
2208
+ float levels = max(intensity, 2.0);
2209
+ float threshold = bayer8(uv) - 0.5;
2210
+ float stepSize = 1.0 / (levels - 1.0);
2211
+ color += threshold * stepSize;
2212
+ return clamp(floor(color * (levels - 1.0) + 0.5) / (levels - 1.0), 0.0, 1.0);
2213
+ }
2214
+
2215
+ /**
2216
+ * Arcade — pixelated Bayer dithering.
2217
+ * Combines pixelation and ordered dither for retro 8-bit look.
2218
+ * intensity = number of color levels (2 = 1-bit, 4 = retro, 8 = subtle)
2219
+ * @color-modifier
2220
+ */
2221
+ vec3 arcade(vec3 color, vec2 uv, float intensity) {
2222
+ vec2 grid = uv * u_resolution * 0.5;
2223
+ return dither(color, grid, intensity);
2224
+ }
2225
+
2226
+ void main() {}
2227
+ `, b = new V([
2228
+ new v("sandbox", oe),
2229
+ new v("sandbox/colors", ae),
2230
+ new v("sandbox/time", le),
2231
+ new v("sandbox/effects", ce, {
2232
+ default: {
2233
+ intensity: { uniform: "u_intensity", default: 1 }
2234
+ },
2235
+ pixelate: {
2236
+ intensity: { uniform: "u_intensity", default: 20 }
2237
+ },
2238
+ twist: {
2239
+ intensity: { uniform: "u_intensity", default: 1 }
2240
+ },
2241
+ ripple: {
2242
+ intensity: { uniform: "u_intensity", default: 1 }
2243
+ },
2244
+ fisheye: {
2245
+ intensity: { uniform: "u_intensity", default: 1 }
2246
+ },
2247
+ wobble: {
2248
+ intensity: { uniform: "u_intensity", default: 1 }
2249
+ },
2250
+ organic: {
2251
+ intensity: { uniform: "u_intensity", default: 3 }
2252
+ },
2253
+ glitch: {
2254
+ intensity: { uniform: "u_intensity", default: 1 }
2255
+ },
2256
+ mirror: {
2257
+ intensity: { uniform: "u_intensity", default: 0 }
2258
+ },
2259
+ kaleidoscope: {
2260
+ intensity: { uniform: "u_intensity", default: 6 }
2261
+ },
2262
+ zoom: {
2263
+ intensity: { uniform: "u_intensity", default: 1 }
2264
+ },
2265
+ warp: {
2266
+ intensity: { uniform: "u_intensity", default: 1 }
2267
+ },
2268
+ displace: {
2269
+ intensity: { uniform: "u_intensity", default: 1 }
2270
+ },
2271
+ shatter: {
2272
+ intensity: { uniform: "u_intensity", default: 10 }
2273
+ },
2274
+ cells: {
2275
+ intensity: { uniform: "u_intensity", default: 8 }
2276
+ },
2277
+ glass: {
2278
+ intensity: { uniform: "u_intensity", default: 1 }
2279
+ }
2280
+ }),
2281
+ new v("sandbox/filters", ue, {
2282
+ default: {
2283
+ intensity: { uniform: "u_intensity", default: 1 }
2284
+ },
2285
+ posterize: {
2286
+ intensity: { uniform: "u_intensity", default: 8 }
2287
+ },
2288
+ threshold: {
2289
+ intensity: { uniform: "u_intensity", default: 0.5 }
2290
+ },
2291
+ grain: {
2292
+ intensity: { uniform: "u_intensity", default: 0.1 }
2293
+ },
2294
+ vignette: {
2295
+ intensity: { uniform: "u_intensity", default: 1.4 }
2296
+ },
2297
+ glow: {
2298
+ intensity: { uniform: "u_intensity", default: 0.5 }
2299
+ },
2300
+ gamma: {
2301
+ intensity: { uniform: "u_intensity", default: 2.2 }
2302
+ },
2303
+ dither: {
2304
+ intensity: { uniform: "u_intensity", default: 4 }
2305
+ },
2306
+ highlights: {
2307
+ intensity: { uniform: "u_intensity", default: 0.5 }
2308
+ }
2309
+ })
2310
+ ]), y = new V(), E = /* @__PURE__ */ new Map([
2311
+ ["u_resolution", "vec2"],
2312
+ ["u_time", "float"],
2313
+ ["u_delta", "float"],
2314
+ ["u_mouse", "vec2"],
2315
+ ["u_frame", "int"]
2316
+ ]);
2317
+ class fe {
2318
+ constructor(e) {
2319
+ a(this, "gl");
2320
+ a(this, "program", null);
2321
+ a(this, "uniforms", /* @__PURE__ */ new Map());
2322
+ this.gl = e;
522
2323
  }
523
2324
  /**
524
2325
  * Attach to a WebGL program.
525
2326
  * Invalidates all cached locations since they're program-specific.
526
2327
  */
527
- attachProgram(t) {
528
- this.program = t;
529
- for (const e of this.uniforms.values())
530
- e.invalidateLocation();
2328
+ attachProgram(e) {
2329
+ this.program = e;
2330
+ for (const n of this.uniforms.values())
2331
+ n.invalidateLocation();
531
2332
  return this;
532
2333
  }
533
2334
  /**
534
2335
  * Set a uniform value.
535
2336
  * Creates the uniform if it doesn't exist, updates if it does.
536
2337
  */
537
- set(t, e) {
538
- const r = this.uniforms.get(t);
539
- return r ? r.setValue(e) : this.uniforms.set(t, new x(t, e)), this;
2338
+ set(e, n) {
2339
+ const t = this.uniforms.get(e);
2340
+ return t ? t.setValue(n) : this.uniforms.set(e, new k(e, n)), this;
540
2341
  }
541
2342
  /**
542
2343
  * Set multiple uniforms at once.
543
2344
  */
544
- setMany(t) {
545
- for (const [e, r] of Object.entries(t))
546
- this.set(e, r);
2345
+ setMany(e) {
2346
+ for (const [n, t] of Object.entries(e))
2347
+ this.set(n, t);
547
2348
  return this;
548
2349
  }
549
2350
  /**
550
2351
  * Get current uniform value.
551
2352
  */
552
- get(t) {
553
- var e;
554
- return (e = this.uniforms.get(t)) == null ? void 0 : e.getValue();
2353
+ get(e) {
2354
+ var n;
2355
+ return (n = this.uniforms.get(e)) == null ? void 0 : n.getValue();
555
2356
  }
556
2357
  /**
557
2358
  * Check if uniform exists.
558
2359
  */
559
- has(t) {
560
- return this.uniforms.has(t);
2360
+ has(e) {
2361
+ return this.uniforms.has(e);
561
2362
  }
562
2363
  /**
563
2364
  * Remove a uniform.
564
2365
  */
565
- delete(t) {
566
- return this.uniforms.delete(t);
2366
+ delete(e) {
2367
+ return this.uniforms.delete(e);
567
2368
  }
568
2369
  /**
569
2370
  * Upload all uniforms to GPU.
@@ -572,20 +2373,20 @@ const d = class d {
572
2373
  uploadAll() {
573
2374
  if (!this.program)
574
2375
  return this;
575
- for (const t of this.uniforms.values())
576
- t.upload(this.gl, this.program);
2376
+ for (const e of this.uniforms.values())
2377
+ e.upload(this.gl, this.program);
577
2378
  return this;
578
2379
  }
579
2380
  /**
580
2381
  * Upload only built-in uniforms (u_resolution, u_time, u_delta, u_mouse, u_frame).
581
2382
  * Call this every frame with current values.
582
2383
  */
583
- uploadBuiltIns(t, e, r) {
584
- if (this.set("u_resolution", e), this.set("u_time", t.time), this.set("u_delta", t.delta), this.set("u_mouse", r), this.set("u_frame", t.frame), !this.program)
2384
+ uploadBuiltIns(e, n, t) {
2385
+ if (this.set("u_resolution", n), this.set("u_time", e.time), this.set("u_delta", e.delta), this.set("u_mouse", t), this.set("u_frame", e.frame), !this.program)
585
2386
  return this;
586
- for (const s of d.BUILT_INS) {
587
- const n = this.uniforms.get(s);
588
- n && n.upload(this.gl, this.program);
2387
+ for (const i of E.keys()) {
2388
+ const r = this.uniforms.get(i);
2389
+ r && r.upload(this.gl, this.program);
589
2390
  }
590
2391
  return this;
591
2392
  }
@@ -613,64 +2414,62 @@ const d = class d {
613
2414
  get size() {
614
2415
  return this.uniforms.size;
615
2416
  }
616
- };
617
- /** Built-in uniform names that are handled automatically */
618
- i(d, "BUILT_INS", /* @__PURE__ */ new Set([
619
- "u_resolution",
620
- "u_time",
621
- "u_delta",
622
- "u_mouse",
623
- "u_frame"
624
- ]));
625
- let v = d;
626
- class b {
2417
+ }
2418
+ class O {
627
2419
  constructor() {
628
- i(this, "hooks", /* @__PURE__ */ new Map());
2420
+ a(this, "hooks", /* @__PURE__ */ new Map());
629
2421
  }
630
2422
  id() {
631
2423
  return Math.random().toString(36).substring(2, 10);
632
2424
  }
633
2425
  /** Add a new hook, returns a removal function */
634
- add(t) {
635
- const e = this.id();
636
- return this.hooks.set(e, t), () => this.remove(e);
2426
+ add(e) {
2427
+ const n = this.id();
2428
+ return this.hooks.set(n, e), () => this.remove(n);
637
2429
  }
638
2430
  /** Remove a hook by its ID */
639
- remove(t) {
640
- this.hooks.delete(t);
2431
+ remove(e) {
2432
+ this.hooks.delete(e);
641
2433
  }
642
2434
  /** Run all hooks with the given state */
643
- run(t) {
644
- for (const [e, r] of this.hooks)
645
- r(t) === !1 && this.remove(e);
2435
+ run(e) {
2436
+ for (const [n, t] of this.hooks)
2437
+ try {
2438
+ t(e) === !1 && this.remove(n);
2439
+ } catch (i) {
2440
+ throw new ie(
2441
+ n,
2442
+ i instanceof Error ? i.message : String(i)
2443
+ );
2444
+ }
646
2445
  }
647
2446
  destroy() {
648
2447
  this.hooks.clear();
649
2448
  }
650
2449
  }
651
- class _ {
652
- constructor(t, e) {
653
- i(this, "canvas");
654
- i(this, "gl");
655
- i(this, "options");
656
- i(this, "onBeforeHooks", new b());
657
- i(this, "onAfterHooks", new b());
658
- i(this, "_program");
659
- i(this, "_geometry");
660
- i(this, "_uniforms");
661
- i(this, "_clock");
662
- i(this, "_resolution", [1, 1]);
663
- i(this, "_mouse", [0, 0]);
664
- i(this, "_version", 1);
665
- i(this, "playing", !1);
666
- this.canvas = t, this.options = e, this.gl = this.initContext(), this.enableExtensions(), this._program = new h(this.gl), this._geometry = p.fullscreenQuad(this.gl), this._uniforms = new v(this.gl), this._clock = new R(), this.options.onBeforeRender && this.onBeforeHooks.add(this.options.onBeforeRender), this.options.onAfterRender && this.onAfterHooks.add(this.options.onAfterRender), this.onRender = this.onRender.bind(this);
2450
+ class M {
2451
+ constructor(e, n) {
2452
+ a(this, "canvas");
2453
+ a(this, "gl");
2454
+ a(this, "options");
2455
+ a(this, "onBeforeHooks", new O());
2456
+ a(this, "onAfterHooks", new O());
2457
+ a(this, "_program");
2458
+ a(this, "_geometry");
2459
+ a(this, "_uniforms");
2460
+ a(this, "_clock");
2461
+ a(this, "_resolution", [1, 1]);
2462
+ a(this, "_mouse", [0, 0]);
2463
+ a(this, "_version", 1);
2464
+ a(this, "playing", !1);
2465
+ this.canvas = e, this.options = n, this.gl = this.initContext(), this.enableExtensions(), this._program = new se(this.gl), this._geometry = A.fullscreenQuad(this.gl), this._uniforms = new fe(this.gl), this._clock = new re(), this.options.fps && this._clock.setMaxFps(this.options.fps), this.options.onBeforeRender && this.onBeforeHooks.add(this.options.onBeforeRender), this.options.onAfterRender && this.onAfterHooks.add(this.options.onAfterRender), this.onRender = this.onRender.bind(this);
667
2466
  }
668
2467
  /**
669
2468
  * Factory method to create and setup WebGL instance.
670
2469
  */
671
- static setup(t, e) {
672
- const r = new _(t, e);
673
- return e.vertex && e.fragment && r.shader(e.vertex, e.fragment), e.uniforms && r._uniforms.setMany(e.uniforms), r;
2470
+ static setup(e, n) {
2471
+ const t = new M(e, n);
2472
+ return n.vertex && n.fragment && t.shader(n.vertex, n.fragment), n.uniforms && t._uniforms.setMany(n.uniforms), y.isEmpty() || t._uniforms.setMany(y.defaults()), t;
674
2473
  }
675
2474
  /**
676
2475
  * Initialize WebGL context.
@@ -678,20 +2477,20 @@ class _ {
678
2477
  * Context errors are fatal but still reported via onError.
679
2478
  */
680
2479
  initContext() {
681
- const t = {
2480
+ const e = {
682
2481
  antialias: this.options.antialias,
683
2482
  preserveDrawingBuffer: this.options.preserveDrawingBuffer,
684
2483
  alpha: !0,
685
2484
  depth: !1,
686
2485
  stencil: !1
687
- }, e = this.canvas.getContext("webgl2", t);
688
- if (e)
689
- return this._version = 2, e;
690
- const r = this.canvas.getContext("webgl", t);
691
- if (r)
692
- return this._version = 1, r;
693
- const s = new w("not_supported");
694
- throw this.options.onError(s), s;
2486
+ }, n = this.canvas.getContext("webgl2", e);
2487
+ if (n)
2488
+ return this._version = 2, n;
2489
+ const t = this.canvas.getContext("webgl", e);
2490
+ if (t)
2491
+ return this._version = 1, t;
2492
+ const i = new z();
2493
+ throw this.options.onError(i), i;
695
2494
  }
696
2495
  /**
697
2496
  * Enable useful WebGL extensions.
@@ -702,50 +2501,62 @@ class _ {
702
2501
  /**
703
2502
  * Set viewport dimensions.
704
2503
  */
705
- viewport(t, e, r, s) {
706
- return this.canvas.width = r, this.canvas.height = s, this.gl.viewport(t, e, r, s), this._resolution = [r, s], this;
2504
+ viewport(e, n, t, i) {
2505
+ return this.canvas.width = t, this.canvas.height = i, this.gl.viewport(e, n, t, i), this._resolution = [t, i], this;
707
2506
  }
708
2507
  /**
709
2508
  * Set the clock time
710
2509
  */
711
- clock(t) {
712
- return this._clock.setTime(t), this;
2510
+ clock(e) {
2511
+ return this._clock.setTime(e), this;
713
2512
  }
714
2513
  /**
715
2514
  * Update mouse position.
716
2515
  */
717
- mouse(t, e) {
718
- return this._mouse = [t, e], this;
2516
+ mouse(e, n) {
2517
+ return this._mouse = [e, n], this;
719
2518
  }
720
2519
  /**
721
2520
  * Set a uniform value.
722
2521
  */
723
- uniform(t, e) {
724
- return this._uniforms.set(t, e), this;
2522
+ uniform(e, n) {
2523
+ return this._uniforms.set(e, n), this;
725
2524
  }
726
2525
  /**
727
2526
  * Set multiple uniforms.
728
2527
  */
729
- uniforms(t) {
730
- return this._uniforms.setMany(t), this;
2528
+ uniforms(e) {
2529
+ return this._uniforms.setMany(e), this;
731
2530
  }
732
2531
  /**
733
2532
  * Get current uniform value.
734
2533
  */
735
- getUniform(t) {
736
- return this._uniforms.get(t);
2534
+ getUniform(e) {
2535
+ return this._uniforms.get(e);
737
2536
  }
738
2537
  /**
739
2538
  * Compile and link shaders.
740
2539
  * Errors are handled via onError callback, never thrown.
741
2540
  */
742
- shader(t, e) {
2541
+ shader(e, n) {
743
2542
  try {
744
- this._program.compile(t, e), this._version = this._program.getVersion(), this._geometry.linkAttributes(this._program);
745
- const r = this._program.getProgram();
746
- r && this._uniforms.attachProgram(r);
747
- } catch (r) {
748
- r instanceof u && this.options.onError(r);
2543
+ if (y.clear(), e.version() !== n.version())
2544
+ throw new G(
2545
+ e.version(),
2546
+ n.version()
2547
+ );
2548
+ this._program.compile(e.source(), n.compile()), this._version = n.version(), this._geometry.linkAttributes(this._program);
2549
+ const t = this._program.getProgram();
2550
+ t && this._uniforms.attachProgram(t);
2551
+ try {
2552
+ this.options.onLoad();
2553
+ } catch (i) {
2554
+ throw new te(
2555
+ i instanceof Error ? i.message : String(i)
2556
+ );
2557
+ }
2558
+ } catch (t) {
2559
+ t instanceof h && this.options.onError(t);
749
2560
  }
750
2561
  return this;
751
2562
  }
@@ -760,8 +2571,19 @@ class _ {
760
2571
  */
761
2572
  pause() {
762
2573
  if (!this.playing) return this;
763
- const t = this._clock.getState();
764
- return this.onBeforeHooks.run(t), this.playing = !1, this._clock.stop(), this.onAfterHooks.run(t), this;
2574
+ const e = this._clock.getState();
2575
+ try {
2576
+ this.onBeforeHooks.run(e);
2577
+ } catch (n) {
2578
+ n instanceof h && this.options.onError(n);
2579
+ }
2580
+ this.playing = !1, this._clock.stop();
2581
+ try {
2582
+ this.onAfterHooks.run(e);
2583
+ } catch (n) {
2584
+ n instanceof h && this.options.onError(n);
2585
+ }
2586
+ return this;
765
2587
  }
766
2588
  /**
767
2589
  * Render a single frame.
@@ -781,21 +2603,44 @@ class _ {
781
2603
  getVersion() {
782
2604
  return this._version;
783
2605
  }
2606
+ /**
2607
+ * Get the clock instance
2608
+ */
2609
+ getClock() {
2610
+ return this._clock;
2611
+ }
784
2612
  /**
785
2613
  * Cleanup all resources.
786
2614
  */
787
2615
  destroy() {
788
- this.pause(), this._clock.destroy(), this._geometry.destroy(), this._program.destroy(), this._uniforms.destroy(), this.onAfterHooks.destroy(), this.onBeforeHooks.destroy();
2616
+ this.pause(), this._clock.destroy(), this._geometry.destroy(), this._program.destroy(), this._uniforms.destroy(), this.onAfterHooks.destroy(), this.onBeforeHooks.destroy(), y.clear();
789
2617
  }
790
2618
  /**
791
2619
  * Internal render callback.
792
2620
  */
793
- onRender(t) {
794
- const e = this.gl;
795
- this.onBeforeHooks.run(t), e.clearColor(0, 0, 0, 0), e.clear(e.COLOR_BUFFER_BIT), this._program.use(), this._uniforms.uploadBuiltIns(t, this._resolution, this._mouse), this._uniforms.uploadAll(), this._geometry.bind(), this._geometry.draw(), this.onAfterHooks.run(t);
2621
+ onRender(e) {
2622
+ const n = this.gl;
2623
+ try {
2624
+ this.onBeforeHooks.run(e);
2625
+ } catch (t) {
2626
+ t instanceof h && this.options.onError(t);
2627
+ }
2628
+ n.clearColor(0, 0, 0, 0), n.clear(n.COLOR_BUFFER_BIT), this._program.use(), this._uniforms.uploadBuiltIns(e, this._resolution, this._mouse), this._uniforms.uploadAll(), this._geometry.bind(), this._geometry.draw();
2629
+ try {
2630
+ this.onAfterHooks.run(e);
2631
+ } catch (t) {
2632
+ t instanceof h && this.options.onError(t);
2633
+ }
2634
+ }
2635
+ }
2636
+ class d extends F {
2637
+ constructor(e) {
2638
+ super(e), E.forEach((n, t) => {
2639
+ this.requirements.uniforms.set(t, { name: t, type: n, line: 0 });
2640
+ });
796
2641
  }
797
2642
  }
798
- const g = `#ifdef GL_ES
2643
+ const S = `#ifdef GL_ES
799
2644
  precision mediump float;
800
2645
  #endif
801
2646
 
@@ -808,7 +2653,7 @@ void main() {
808
2653
  v_texcoord = a_texcoord;
809
2654
  gl_Position = vec4(a_position, 0.0, 1.0);
810
2655
  }
811
- `, A = `#ifdef GL_ES
2656
+ `, L = `#ifdef GL_ES
812
2657
  precision mediump float;
813
2658
  #endif
814
2659
 
@@ -822,7 +2667,7 @@ void main() {
822
2667
  vec3 color = vec3(uv.x, uv.y, 0.5 + 0.5 * sin(u_time));
823
2668
  gl_FragColor = vec4(color, 1.0);
824
2669
  }
825
- `, y = `#version 300 es
2670
+ `, U = `#version 300 es
826
2671
 
827
2672
  in vec2 a_position;
828
2673
  in vec2 a_texcoord;
@@ -832,7 +2677,7 @@ out vec2 v_texcoord;
832
2677
  void main() {
833
2678
  v_texcoord = a_texcoord;
834
2679
  gl_Position = vec4(a_position, 0.0, 1.0);
835
- }`, M = `#version 300 es
2680
+ }`, he = `#version 300 es
836
2681
  precision highp float;
837
2682
 
838
2683
  uniform vec2 u_resolution;
@@ -847,17 +2692,22 @@ void main() {
847
2692
  vec3 color = vec3(uv.x, uv.y, 0.5 + 0.5 * sin(u_time));
848
2693
  fragColor = vec4(color, 1.0);
849
2694
  }`;
850
- class E {
851
- constructor(t, e) {
2695
+ class $ {
2696
+ constructor(e, n) {
852
2697
  /** Active event listeners */
853
- i(this, "listeners", []);
2698
+ a(this, "listeners", []);
854
2699
  /** HTML canvas element */
855
- i(this, "canvas");
2700
+ a(this, "canvasEl");
856
2701
  /** Resolved options */
857
- i(this, "options");
2702
+ a(this, "options");
858
2703
  /** WebGL engine */
859
- i(this, "engine");
860
- this.canvas = t, this.options = this.resolveOptions(e), this.engine = _.setup(this.canvas, this.options), this.setupListeners(), this.setViewport(), this.options.onLoad(), this.options.autoplay && this.play();
2704
+ a(this, "engine");
2705
+ /** User sets custom vertex shader */
2706
+ a(this, "usingCustomVertex", !1);
2707
+ if (this.canvasEl = e, this.options = this.resolveOptions(n), this.engine = M.setup(this.canvasEl, this.options), this.setupListeners(), this.setViewport(), this.options.modules)
2708
+ for (const [t, i] of Object.entries(this.options.modules))
2709
+ this.module(t, i);
2710
+ this.options.autoplay && this.play();
861
2711
  }
862
2712
  /**
863
2713
  * Sandbox - A lightweight WebGL wrapper for shader effects.
@@ -877,22 +2727,49 @@ class E {
877
2727
  * autoplay: true,
878
2728
  * });
879
2729
  */
880
- static create(t, e) {
881
- return new E(t, e);
2730
+ static create(e, n) {
2731
+ return new $(e, n);
882
2732
  }
883
- resolveOptions(t) {
884
- const e = {
885
- vertex: g,
886
- fragment: A,
2733
+ /**
2734
+ * Define a shader module that can be imported in shader source with `#import <function> from "module_name"`.
2735
+ * @example
2736
+ * Sandbox.defineModule("my_module", source, { options });
2737
+ * // Then in shader:
2738
+ * // #import myFunc from "my_module"
2739
+ * // void main() {
2740
+ * // myFunc();
2741
+ * // }
2742
+ */
2743
+ static defineModule(e, n, t = {}) {
2744
+ v.define({ name: e, source: n, options: t });
2745
+ }
2746
+ /**
2747
+ * Get the list of available shader modules that can be used with `#import` in shader source.
2748
+ */
2749
+ static availableModules() {
2750
+ return b.available();
2751
+ }
2752
+ /**
2753
+ * Compile a shader source with Sandbox's shader preprocessor and return the final GLSL code.
2754
+ * This is useful for debugging shader code or precompiling shaders for production use.
2755
+ */
2756
+ static compile(e) {
2757
+ return new d(e).compile();
2758
+ }
2759
+ resolveOptions(e) {
2760
+ const n = {
2761
+ vertex: new d(S),
2762
+ fragment: new d(L),
887
2763
  autoplay: !0,
888
2764
  pauseWhenHidden: !0,
889
2765
  dpr: "auto",
2766
+ fps: 0,
890
2767
  preserveDrawingBuffer: !1,
891
2768
  antialias: !0,
892
- onError: (r) => {
2769
+ onError: (s) => {
893
2770
  console.error(
894
2771
  "Oops!",
895
- r,
2772
+ s,
896
2773
  `
897
2774
  You can handle errors programmatically by providing an onError callback to suppress this log and implement custom fallback behavior.`
898
2775
  );
@@ -901,64 +2778,69 @@ You can handle errors programmatically by providing an onError callback to suppr
901
2778
  },
902
2779
  onBeforeRender: null,
903
2780
  onAfterRender: null,
904
- uniforms: {}
2781
+ uniforms: {},
2782
+ modules: {}
905
2783
  };
906
- if (t != null && t.vertex && !(t != null && t.fragment)) {
907
- const r = h.detectVersion(t.vertex);
908
- e.vertex = t.vertex, e.fragment = r === 2 ? M : A;
2784
+ if (e != null && e.vertex && (this.usingCustomVertex = !0), e != null && e.vertex && !(e != null && e.fragment)) {
2785
+ n.vertex = new d(e.vertex);
2786
+ const s = n.vertex.version();
2787
+ n.fragment = new d(s === 2 ? he : L);
909
2788
  }
910
- if (t != null && t.fragment && !(t != null && t.vertex)) {
911
- const r = h.detectVersion(t.fragment);
912
- e.fragment = t.fragment, e.vertex = r === 2 ? y : g;
2789
+ if (e != null && e.fragment && !(e != null && e.vertex)) {
2790
+ n.fragment = new d(e.fragment);
2791
+ const s = n.fragment.version();
2792
+ n.vertex = new d(s === 2 ? U : S);
913
2793
  }
914
- return { ...e, ...t };
2794
+ e != null && e.vertex && (e != null && e.fragment) && (n.vertex = new d(e.vertex), n.fragment = new d(e.fragment));
2795
+ const { vertex: t, fragment: i, ...r } = e || {};
2796
+ return { ...n, ...r };
915
2797
  }
916
2798
  setupListeners() {
917
2799
  this.listeners.push(
918
2800
  // Window resize
919
- l.on(window, "resize", () => {
2801
+ x.on(window, "resize", () => {
920
2802
  this.setViewport();
921
2803
  }),
922
2804
  // Canvas resize
923
- l.on(this.canvas, "resize", () => {
2805
+ x.on(this.canvasEl, "resize", () => {
924
2806
  this.setViewport();
925
2807
  }),
926
2808
  // Visibility check on scroll
927
2809
  (() => {
928
- let t = !1;
929
- return l.on(document, "scroll", (e) => {
930
- this.options.pauseWhenHidden && (this.isInViewport() ? t && !this.isPlaying() && (this.play(), t = !1) : this.isPlaying() && (this.pause(), t = !0));
2810
+ let e = !1;
2811
+ return x.on(document, "scroll", (n) => {
2812
+ this.options.pauseWhenHidden && (this.isInViewport() ? e && !this.isPlaying() && (this.play(), e = !1) : this.isPlaying() && (this.pause(), e = !0));
931
2813
  });
932
2814
  })(),
933
2815
  // Mouse tracking
934
- l.on(document, "mousemove", (t) => {
935
- this.setMouse(t.clientX || t.pageX, t.clientY || t.pageY);
2816
+ x.on(document, "mousemove", (e) => {
2817
+ this.setMouse(e.clientX || e.pageX, e.clientY || e.pageY);
936
2818
  }),
937
2819
  // Touch tracking
938
- l.on(document, "touchmove", (t) => {
939
- t.touches.length > 0 && this.setMouse(t.touches[0].clientX, t.touches[0].clientY);
2820
+ x.on(document, "touchmove", (e) => {
2821
+ e.touches.length > 0 && this.setMouse(e.touches[0].clientX, e.touches[0].clientY);
940
2822
  })
941
2823
  );
942
2824
  }
943
2825
  destroyListeners() {
944
- this.listeners.forEach((t) => t()), this.listeners = [];
2826
+ this.listeners.forEach((e) => e()), this.listeners = [];
945
2827
  }
946
2828
  setViewport() {
947
- const t = this.options.dpr === "auto" ? Math.min(2, window.devicePixelRatio || 1) : this.options.dpr, e = this.canvas.clientWidth || this.canvas.width || 1, r = this.canvas.clientHeight || this.canvas.height || 1;
2829
+ const e = this.options.dpr === "auto" ? Math.min(2, window.devicePixelRatio || 1) : this.options.dpr, n = this.canvasEl.clientWidth || this.canvasEl.width || 1, t = this.canvasEl.clientHeight || this.canvasEl.height || 1;
948
2830
  this.engine.viewport(
949
2831
  0,
950
2832
  0,
951
- Math.max(1, Math.floor(e * t)),
952
- Math.max(1, Math.floor(r * t))
2833
+ Math.max(1, Math.floor(n * e)),
2834
+ Math.max(1, Math.floor(t * e))
953
2835
  );
954
2836
  }
955
2837
  isInViewport() {
956
- const t = this.canvas.getBoundingClientRect();
957
- return t.bottom >= 0 && t.right >= 0 && t.top <= (window.innerHeight || document.documentElement.clientHeight) && t.left <= (window.innerWidth || document.documentElement.clientWidth);
2838
+ const e = this.canvasEl.getBoundingClientRect();
2839
+ return e.bottom >= 0 && e.right >= 0 && e.top <= (window.innerHeight || document.documentElement.clientHeight) && e.left <= (window.innerWidth || document.documentElement.clientWidth);
958
2840
  }
959
- setMouse(t, e) {
960
- const r = this.canvas.getBoundingClientRect();
961
- t >= r.left && t <= r.right && e >= r.top && e <= r.bottom && this.engine.mouse(t - r.left, e - r.top);
2841
+ setMouse(e, n) {
2842
+ const t = this.canvasEl.getBoundingClientRect();
2843
+ e >= t.left && e <= t.right && n >= t.top && n <= t.bottom && this.engine.mouse(e - t.left, n - t.top);
962
2844
  }
963
2845
  /**
964
2846
  * Set a single uniform value with type checking.
@@ -967,8 +2849,8 @@ You can handle errors programmatically by providing an onError callback to suppr
967
2849
  * sandbox.setUniform<number>("u_time", 1.5);
968
2850
  * sandbox.setUniform<Vec3[]>("u_colors", [[1, 0, 0], [0, 1, 0]]);
969
2851
  */
970
- setUniform(t, e) {
971
- return this.engine.uniform(t, e), this;
2852
+ setUniform(e, n) {
2853
+ return this.engine.uniform(e, n), this;
972
2854
  }
973
2855
  /**
974
2856
  * Set multiple uniforms at once with type checking.
@@ -984,37 +2866,84 @@ You can handle errors programmatically by providing an onError callback to suppr
984
2866
  * u_colors: [[1, 0, 0], [0, 1, 0]],
985
2867
  * });
986
2868
  */
987
- setUniforms(t) {
988
- return this.engine.uniforms(t), this;
2869
+ setUniforms(e) {
2870
+ return this.engine.uniforms(e), this;
989
2871
  }
990
2872
  /**
991
2873
  * Get current uniform value.
992
2874
  */
993
- getUniform(t) {
994
- return this.engine.getUniform(t);
2875
+ getUniform(e) {
2876
+ return this.engine.getUniform(e);
995
2877
  }
996
2878
  /**
997
2879
  * Update shaders.
998
2880
  * @example
999
2881
  * sandbox.setShader(vertexSource, fragmentSource);
1000
2882
  */
1001
- setShader(t, e) {
1002
- return this.engine.shader(t, e), this;
2883
+ setShader(e, n) {
2884
+ return this.options.vertex = new d(e), this.options.fragment = new d(n), this.usingCustomVertex = !0, this.engine.shader(this.options.vertex, this.options.fragment), this;
1003
2885
  }
1004
2886
  /**
1005
2887
  * Update only fragment shader (uses default vertex).
1006
2888
  * @example
1007
2889
  * sandbox.setFragment(fragmentSource);
1008
2890
  */
1009
- setFragment(t) {
1010
- const r = this.webglVersion() === 1 ? g : y;
1011
- return this.engine.shader(r, t), this;
2891
+ setFragment(e) {
2892
+ const n = new d(e), t = n.version(), i = this.options.vertex.version();
2893
+ return this.options.fragment = n, t !== i && (this.usingCustomVertex || (this.options.vertex = new d(
2894
+ t === 2 ? U : S
2895
+ ))), this.engine.shader(this.options.vertex, this.options.fragment), this;
2896
+ }
2897
+ /**
2898
+ * Get current fragment shader source.
2899
+ */
2900
+ getFragment() {
2901
+ return this.options.fragment.source();
2902
+ }
2903
+ /**
2904
+ * Get current vertex shader source.
2905
+ */
2906
+ getVertex() {
2907
+ return this.options.vertex.source();
2908
+ }
2909
+ /**
2910
+ * Set the max frame rate runtime
2911
+ *
2912
+ * @example
2913
+ * sandbox.setFps(30); // Limit to 30 FPS
2914
+ * sandbox.setFps(0); // Unlimited FPS
2915
+ */
2916
+ setFps(e) {
2917
+ return this.engine.getClock().setMaxFps(e), this;
1012
2918
  }
1013
2919
  /**
1014
2920
  * Add a runtime render hook.
1015
2921
  */
1016
- hook(t, e = "before") {
1017
- return e === "before" ? this.engine.onBeforeHooks.add(t) : this.engine.onAfterHooks.add(t);
2922
+ hook(e, n = "before") {
2923
+ return n === "before" ? this.engine.onBeforeHooks.add(e) : this.engine.onAfterHooks.add(e);
2924
+ }
2925
+ /**
2926
+ * Runtime configure the module behavior
2927
+ * @example
2928
+ * sandbox.module("my_module", { intensity: 0.5 });
2929
+ */
2930
+ module(e, n) {
2931
+ const t = y.resolveOptions(e);
2932
+ if (!t)
2933
+ return console.warn(
2934
+ `Sandbox: Counld not find options for '${e}' function. Make sure you used the correct imported name and the module is currently in use by the shader.`
2935
+ ), this;
2936
+ for (const [i, r] of Object.entries(n)) {
2937
+ const s = t[i];
2938
+ if (!s) {
2939
+ console.warn(
2940
+ `Sandbox: Option '${i}' not found for function '${e}'. Make sure to check available options with Sandbox.availableModules() and provide the correct option name.`
2941
+ );
2942
+ continue;
2943
+ }
2944
+ this.setUniform(s.uniform, r);
2945
+ }
2946
+ return this;
1018
2947
  }
1019
2948
  /**
1020
2949
  * Start animation loop.
@@ -1025,8 +2954,8 @@ You can handle errors programmatically by providing an onError callback to suppr
1025
2954
  /**
1026
2955
  * Start animation loop at specific time (in seconds).
1027
2956
  */
1028
- playAt(t) {
1029
- return this.engine.clock(t), this.engine.play(), this;
2957
+ playAt(e) {
2958
+ return this.engine.clock(e), this.engine.play(), this;
1030
2959
  }
1031
2960
  /**
1032
2961
  * Stop animation loop.
@@ -1037,9 +2966,9 @@ You can handle errors programmatically by providing an onError callback to suppr
1037
2966
  /**
1038
2967
  * Pause animation loop at specific time (in seconds).
1039
2968
  */
1040
- pauseAt(t) {
1041
- const e = this.hook((r) => {
1042
- r.time >= t && (e(), this.pause());
2969
+ pauseAt(e) {
2970
+ const n = this.hook((t) => {
2971
+ t.time >= e && (n(), this.pause());
1043
2972
  }, "after");
1044
2973
  return this;
1045
2974
  }
@@ -1052,8 +2981,8 @@ You can handle errors programmatically by providing an onError callback to suppr
1052
2981
  /**
1053
2982
  * Set current time (in seconds).
1054
2983
  */
1055
- time(t) {
1056
- return this.engine.clock(t), this;
2984
+ time(e) {
2985
+ return this.engine.clock(e), this;
1057
2986
  }
1058
2987
  /**
1059
2988
  * Render a single frame (for static rendering).
@@ -1068,8 +2997,8 @@ You can handle errors programmatically by providing an onError callback to suppr
1068
2997
  * @example
1069
2998
  * sandbox.renderAt(2.5); // Render as if 2.5 seconds elapsed
1070
2999
  */
1071
- renderAt(t) {
1072
- return this.engine.clock(t), this.engine.render(), this;
3000
+ renderAt(e) {
3001
+ return this.engine.clock(e), this.engine.render(), this;
1073
3002
  }
1074
3003
  /**
1075
3004
  * Check if currently playing.
@@ -1080,14 +3009,14 @@ You can handle errors programmatically by providing an onError callback to suppr
1080
3009
  /**
1081
3010
  * Get WebGL version using (1 or 2).
1082
3011
  */
1083
- webglVersion() {
3012
+ get version() {
1084
3013
  return this.engine.getVersion();
1085
3014
  }
1086
3015
  /**
1087
3016
  * Get canvas element.
1088
3017
  */
1089
- canvasElement() {
1090
- return this.canvas;
3018
+ get canvas() {
3019
+ return this.canvasEl;
1091
3020
  }
1092
3021
  /**
1093
3022
  * Destroy sandbox and release all resources.
@@ -1101,10 +3030,26 @@ You can handle errors programmatically by providing an onError callback to suppr
1101
3030
  }
1102
3031
  }
1103
3032
  export {
1104
- E as Sandbox,
1105
- w as SandboxContextError,
1106
- u as SandboxError,
1107
- m as SandboxProgramError,
1108
- c as SandboxShaderCompilationError,
1109
- k as SandboxShaderVersionMismatchError
3033
+ $ as Sandbox,
3034
+ X as SandboxAttemptedToImportDefaultFunctionError,
3035
+ K as SandboxAttemptedToImportMainFunctionError,
3036
+ de as SandboxContextCreationError,
3037
+ h as SandboxError,
3038
+ Q as SandboxForbiddenModuleNameError,
3039
+ _ as SandboxGLSLShaderCompilationError,
3040
+ ne as SandboxMentionCouldNotBeReplacedError,
3041
+ ee as SandboxMentionFunctionNotFoundError,
3042
+ Z as SandboxMentionUniformNotFoundError,
3043
+ Y as SandboxModuleMethodNotFoundError,
3044
+ j as SandboxModuleNotFoundError,
3045
+ ie as SandboxOnHookCallbackError,
3046
+ te as SandboxOnLoadCallbackError,
3047
+ J as SandboxOverwriteModuleError,
3048
+ R as SandboxProgramError,
3049
+ H as SandboxShaderDuplicateImportNameError,
3050
+ q as SandboxShaderImportSyntaxError,
3051
+ C as SandboxShaderRequirementMismatchError,
3052
+ G as SandboxShaderVersionMismatchError,
3053
+ W as SandboxShaderWithoutFunctionError,
3054
+ z as SandboxWebGLNotSupportedError
1110
3055
  };