@rosalana/sandbox 0.1.0 → 0.2.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/README.md +135 -3
- package/dist/errors/base.d.ts +1 -1
- package/dist/errors/index.d.ts +1 -0
- package/dist/errors/texture.d.ts +12 -0
- package/dist/index.cjs.js +23 -23
- package/dist/index.d.ts +57 -1
- package/dist/index.es.js +899 -571
- package/dist/tools/texture.d.ts +64 -0
- package/dist/tools/textures.d.ts +48 -0
- package/dist/tools/web_gl.d.ts +14 -1
- package/dist/types.d.ts +33 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -38,6 +38,11 @@ It works in both **WebGL1 and WebGL2** contexts, with automatic fallback and det
|
|
|
38
38
|
- [Sandbox Shaders](#sandbox-shaders)
|
|
39
39
|
- [Hooks](#hooks)
|
|
40
40
|
- [Self-removing hooks](#self-removing-hooks)
|
|
41
|
+
- [Textures](#textures)
|
|
42
|
+
- [Texture options](#texture-options)
|
|
43
|
+
- [Dynamic textures](#dynamic-textures)
|
|
44
|
+
- [Export](#export)
|
|
45
|
+
- [Streaming](#streaming)
|
|
41
46
|
- [Chaining](#chaining)
|
|
42
47
|
- [Error handling](#error-handling)
|
|
43
48
|
- [Vue integration](#vue-integration)
|
|
@@ -260,7 +265,10 @@ Sandbox.defineModule("my_gradient", gradientSource, {
|
|
|
260
265
|
myFunc: {
|
|
261
266
|
colors: {
|
|
262
267
|
uniform: "u_colors",
|
|
263
|
-
default: [
|
|
268
|
+
default: [
|
|
269
|
+
[1, 0, 0],
|
|
270
|
+
[0, 0, 1],
|
|
271
|
+
],
|
|
264
272
|
},
|
|
265
273
|
speed: { uniform: "u_speed", default: 1.0 },
|
|
266
274
|
},
|
|
@@ -276,7 +284,10 @@ Sandbox.defineModule("my_module", source, {
|
|
|
276
284
|
default: {
|
|
277
285
|
colors: {
|
|
278
286
|
uniform: "u_colors",
|
|
279
|
-
default: [
|
|
287
|
+
default: [
|
|
288
|
+
[1, 0, 0],
|
|
289
|
+
[0, 0, 1],
|
|
290
|
+
],
|
|
280
291
|
},
|
|
281
292
|
speed: { uniform: "u_speed", default: 1.0 },
|
|
282
293
|
},
|
|
@@ -336,6 +347,124 @@ sandbox.hook(({ time }) => {
|
|
|
336
347
|
|
|
337
348
|
This is how `pauseAt()` works internally — it's hooks all the way down.
|
|
338
349
|
|
|
350
|
+
## Textures
|
|
351
|
+
|
|
352
|
+
Sandbox supports textures as `sampler2D` uniforms. Pass any image, canvas, or video element and Sandbox takes care of the WebGL plumbing — creating the texture, binding it to a texture unit, and setting the sampler uniform.
|
|
353
|
+
|
|
354
|
+
```ts
|
|
355
|
+
const img = new Image();
|
|
356
|
+
img.src = "photo.jpg";
|
|
357
|
+
img.onload = () => {
|
|
358
|
+
sandbox.setTexture("u_texture", img);
|
|
359
|
+
};
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
Then sample it in your shader:
|
|
363
|
+
|
|
364
|
+
```glsl
|
|
365
|
+
uniform sampler2D u_texture;
|
|
366
|
+
|
|
367
|
+
void main() {
|
|
368
|
+
vec4 color = texture2D(u_texture, v_texcoord);
|
|
369
|
+
gl_FragColor = color;
|
|
370
|
+
}
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
Multiple textures work the same way — each gets its own texture unit automatically:
|
|
374
|
+
|
|
375
|
+
```ts
|
|
376
|
+
sandbox.setTexture("u_photo", photoImg);
|
|
377
|
+
sandbox.setTexture("u_mask", maskImg);
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
You can also set textures upfront via options:
|
|
381
|
+
|
|
382
|
+
```ts
|
|
383
|
+
Sandbox.create(canvas, {
|
|
384
|
+
fragment: shader,
|
|
385
|
+
textures: {
|
|
386
|
+
u_photo: photoImg,
|
|
387
|
+
u_mask: { source: maskImg, wrap: "repeat" },
|
|
388
|
+
},
|
|
389
|
+
});
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
### Texture options
|
|
393
|
+
|
|
394
|
+
Each texture accepts optional configuration for wrapping, filtering, and orientation:
|
|
395
|
+
|
|
396
|
+
```ts
|
|
397
|
+
sandbox.setTexture("u_texture", img, {
|
|
398
|
+
wrap: "repeat", // both axes (default: "clamp")
|
|
399
|
+
minFilter: "nearest", // pixelated look (default: "linear")
|
|
400
|
+
flipY: false, // disable vertical flip (default: true)
|
|
401
|
+
});
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
| Option | Values | Default |
|
|
405
|
+
| ----------- | ---------------------------------- | ---------- |
|
|
406
|
+
| `wrap` | `"clamp"`, `"repeat"`, `"mirror"` | `"clamp"` |
|
|
407
|
+
| `wrapS` | same (overrides `wrap` for S axis) | `wrap` |
|
|
408
|
+
| `wrapT` | same (overrides `wrap` for T axis) | `wrap` |
|
|
409
|
+
| `minFilter` | `"nearest"`, `"linear"` | `"linear"` |
|
|
410
|
+
| `magFilter` | `"nearest"`, `"linear"` | `"linear"` |
|
|
411
|
+
| `flipY` | `boolean` | `true` |
|
|
412
|
+
| `dynamic` | `boolean` | auto |
|
|
413
|
+
|
|
414
|
+
### Dynamic textures
|
|
415
|
+
|
|
416
|
+
When you pass a video element, Sandbox automatically re-uploads pixels every frame so the texture stays in sync with playback. This also works for animated canvases — just set `dynamic: true`:
|
|
417
|
+
|
|
418
|
+
```ts
|
|
419
|
+
// Video — dynamic by default
|
|
420
|
+
sandbox.setTexture("u_video", videoElement);
|
|
421
|
+
|
|
422
|
+
// Animated canvas — opt in
|
|
423
|
+
sandbox.setTexture("u_canvas", canvasElement, { dynamic: true });
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
To remove a texture and free its GPU memory:
|
|
427
|
+
|
|
428
|
+
```ts
|
|
429
|
+
sandbox.removeTexture("u_texture");
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
## Export
|
|
433
|
+
|
|
434
|
+
Sandbox can export the current frame as an image or blob — useful for saving screenshots, generating thumbnails, or uploading processed images to a server.
|
|
435
|
+
|
|
436
|
+
```ts
|
|
437
|
+
// Data URL (synchronous)
|
|
438
|
+
const url = sandbox.renderAt(1.5).exportAsURL("image/png");
|
|
439
|
+
|
|
440
|
+
// Blob (async) — perfect for server uploads
|
|
441
|
+
const blob = await sandbox.renderAt(1.5).exportAsBlob("image/jpeg", 0.9);
|
|
442
|
+
await fetch("/upload", { method: "POST", body: blob });
|
|
443
|
+
|
|
444
|
+
// HTMLImageElement
|
|
445
|
+
const img = sandbox.renderAt(1.5).exportAsImage("image/png");
|
|
446
|
+
document.body.appendChild(img);
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
> [!NOTE]
|
|
450
|
+
> Export methods work reliably after `render()` or `renderAt()`. If you need to capture frames during an active render loop, set `preserveDrawingBuffer: true` in options.
|
|
451
|
+
|
|
452
|
+
### Streaming
|
|
453
|
+
|
|
454
|
+
For real-time use cases like video calls or recording, Sandbox can expose the canvas as a `MediaStream`:
|
|
455
|
+
|
|
456
|
+
```ts
|
|
457
|
+
// WebRTC — send shader output to a video call
|
|
458
|
+
const stream = sandbox.stream(30);
|
|
459
|
+
peerConnection.addTrack(stream.getVideoTracks()[0], stream);
|
|
460
|
+
|
|
461
|
+
// Recording — save as video file
|
|
462
|
+
const recorder = new MediaRecorder(sandbox.stream(30));
|
|
463
|
+
recorder.start();
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
This opens up workflows like **webcam → texture → shader effect → video call** with just a few lines of code.
|
|
467
|
+
|
|
339
468
|
## Chaining
|
|
340
469
|
|
|
341
470
|
Every method returns `this`, so you can chain calls for clean, expressive code:
|
|
@@ -373,6 +502,7 @@ The error object includes useful details:
|
|
|
373
502
|
| `PROGRAM_ERROR` | Shader program linking failed |
|
|
374
503
|
| `VALIDATION_ERROR` | Vertex/fragment shader version mismatch |
|
|
375
504
|
| `MODULE_ERROR` | Module not found, method not found, forbidden name, or duplicate definition |
|
|
505
|
+
| `TEXTURE_ERROR` | Texture creation failed or texture unit limit exceeded |
|
|
376
506
|
| `UNKNOWN_ERROR` | Unexpected error in callbacks (onLoad, hooks) |
|
|
377
507
|
|
|
378
508
|
## Vue integration
|
|
@@ -443,6 +573,7 @@ interface SandboxOptions {
|
|
|
443
573
|
onAfterRender?: HookCallback | null;
|
|
444
574
|
uniforms?: UniformSchema;
|
|
445
575
|
modules?: Record<string, Record<string, AnyUniformValue>>;
|
|
576
|
+
textures?: TextureSchema;
|
|
446
577
|
}
|
|
447
578
|
```
|
|
448
579
|
|
|
@@ -462,12 +593,13 @@ interface SandboxOptions {
|
|
|
462
593
|
| `onAfterRender` | — | Hook after each frame |
|
|
463
594
|
| `uniforms` | — | Initial uniform values |
|
|
464
595
|
| `modules` | — | Configure module options per imported function |
|
|
596
|
+
| `textures` | — | Initial textures to bind to sampler uniforms |
|
|
465
597
|
|
|
466
598
|
## Limitations (by design)
|
|
467
599
|
|
|
468
|
-
- No textures (planned for future)
|
|
469
600
|
- No multi‑pass rendering
|
|
470
601
|
- No 3D scene graph
|
|
602
|
+
- No custom geometry (fullscreen quad only)
|
|
471
603
|
|
|
472
604
|
If you need a full engine, reach for three.js. For clean shader‑only effects, Sandbox is a joy to use.
|
|
473
605
|
|
package/dist/errors/base.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type SandboxErrorCode = "CONTEXT_ERROR" | "VALIDATION_ERROR" | "PROGRAM_ERROR" | "SHADER_ERROR" | "MODULE_ERROR" | "UNKNOWN_ERROR";
|
|
1
|
+
export type SandboxErrorCode = "CONTEXT_ERROR" | "VALIDATION_ERROR" | "PROGRAM_ERROR" | "SHADER_ERROR" | "MODULE_ERROR" | "TEXTURE_ERROR" | "UNKNOWN_ERROR";
|
|
2
2
|
export declare class SandboxError extends Error {
|
|
3
3
|
readonly code: SandboxErrorCode;
|
|
4
4
|
readonly name: string;
|
package/dist/errors/index.d.ts
CHANGED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { SandboxError } from "./base";
|
|
2
|
+
export declare class SandboxTextureCreationError extends SandboxError {
|
|
3
|
+
readonly textureName: string;
|
|
4
|
+
readonly name = "SandboxTextureCreationError";
|
|
5
|
+
constructor(textureName: string);
|
|
6
|
+
}
|
|
7
|
+
export declare class SandboxTextureUnitLimitError extends SandboxError {
|
|
8
|
+
readonly textureName: string;
|
|
9
|
+
readonly maxUnits: number;
|
|
10
|
+
readonly name = "SandboxTextureUnitLimitError";
|
|
11
|
+
constructor(textureName: string, maxUnits: number);
|
|
12
|
+
}
|
package/dist/index.cjs.js
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var ie=Object.defineProperty;var re=(u,e,t)=>e in u?ie(u,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):u[e]=t;var o=(u,e,t)=>re(u,typeof e!="symbol"?e+"":e,t);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});class b{constructor(e,t,n,i){this.target=e,this.type=t,this.listener=n,this.options=i,this.target.addEventListener(this.type,this.listener,this.options)}remove(){this.target.removeEventListener(this.type,this.listener,this.options)}static on(e,t,n,i){return e.addEventListener(t,n,i),()=>e.removeEventListener(t,n,i)}}class h extends Error{constructor(t,n){super(t);o(this,"name","SandboxError");this.code=n}}class V extends h{constructor(){super("WebGL is not supported in this browser.","CONTEXT_ERROR")}}class se extends h{constructor(){super("Failed to create WebGL context. The GPU may be unavailable.","CONTEXT_ERROR")}}class I extends h{constructor(e,t){super(`Vertex and fragment shader WebGL versions do not match (${e} vs ${t})`,"VALIDATION_ERROR"),this.vertexVersion=e,this.fragmentVersion=t}}class E extends h{constructor(t,n,i){const r=E.parseErrorLines(i),s=r.length>0?` at line(s): ${r.join(", ")}`:"";super(`${t} shader compilation failed${s}
|
|
2
2
|
|
|
3
|
-
${i}`,"SHADER_ERROR");
|
|
3
|
+
${i}`,"SHADER_ERROR");o(this,"lines");this.shaderType=t,this.source=n,this.infoLog=i,this.lines=r}static parseErrorLines(t){const n=[/ERROR:\s*\d*:(\d+)/g,/(\d+):(\d+)\(\d+\):/g,/^(\d+):/gm],i=new Set;for(const r of n){let s;for(;(s=r.exec(t))!==null;){const a=parseInt(s[1],10);a>0&&i.add(a)}}return[...i].sort((r,s)=>r-s)}}class M extends h{constructor(e,t,n,i){super(`The shader ${e} "${t}" has type "${i}" but expected "${n}"`,"SHADER_ERROR"),this.requirement=e,this.name=t,this.expectedType=n,this.actualType=i}}class P extends h{constructor(){super("Shader source does not contain any function.","SHADER_ERROR")}}class $ extends h{constructor(e,t){super(`Syntax error in shader import statement at line ${e}: ${t}`,"SHADER_ERROR"),this.line=e,this.details=t}}class D extends h{constructor(e,t){super(`Duplicate import name "${e}" found at line ${t}. Each import must have a unique name.`,"SHADER_ERROR"),this.name=e,this.line=t}}class B extends h{constructor(e){super(`Can not find module '${e}'. Check if it is defined before usage or if the name is correct.`,"MODULE_ERROR"),this.moduleName=e}}class N extends h{constructor(e,t){super(`Method '${t}' not found in shader module '${e}'. Check if the method is defined in the module source code or if the name is correct.`,"MODULE_ERROR"),this.moduleName=e,this.methodName=t}}class G extends h{constructor(e){super(`Importing 'main' function from module '${e}' is forbidden.`,"MODULE_ERROR"),this.moduleName=e}}class W extends h{constructor(e){super(`Name 'default' is reserved and cannot be used as a function name in module '${e}'.`,"MODULE_ERROR"),this.moduleName=e}}class z extends h{constructor(e){super(`Module name '${e}' is not allowed. Module names cannot be 'sandbox' or start with 'sandbox/'.`,"MODULE_ERROR"),this.moduleName=e}}class H extends h{constructor(e){super(`Module '${e}' is already defined. Overwriting existing modules is not allowed.`,"MODULE_ERROR"),this.moduleName=e}}class q extends h{constructor(e,t,n){super(`Uniform '${n}' mentioned for function '${t}' 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.`,"MODULE_ERROR"),this.moduleName=e,this.functionName=t,this.uniformName=n}}class j extends h{constructor(e,t){super(`Uniform '${t}' 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.`,"MODULE_ERROR"),this.functionName=e,this.uniformName=t}}class X extends h{constructor(e,t){super(`Mention '${e}' called in function '${t}' 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.`,"MODULE_ERROR"),this.mentionName=e,this.calledInFunction=t}}class R extends h{constructor(e){super(`Shader program linking failed
|
|
4
4
|
|
|
5
|
-
${e}`,"PROGRAM_ERROR"),this.infoLog=e}}class Y extends h{constructor(e){super(`Error in onLoad callback: ${e}`,"UNKNOWN_ERROR")}}class K extends h{constructor(e,
|
|
6
|
-
`)[f-1].trim();throw new
|
|
7
|
-
`){
|
|
8
|
-
`+
|
|
5
|
+
${e}`,"PROGRAM_ERROR"),this.infoLog=e}}class oe extends h{constructor(t){super(`Failed to create WebGL texture for "${t}".`,"TEXTURE_ERROR");o(this,"name","SandboxTextureCreationError");this.textureName=t}}class ae extends h{constructor(t,n){super(`Cannot bind texture "${t}": all ${n} texture units are in use.`,"TEXTURE_ERROR");o(this,"name","SandboxTextureUnitLimitError");this.textureName=t,this.maxUnits=n}}class Y extends h{constructor(e){super(`Error in onLoad callback: ${e}`,"UNKNOWN_ERROR")}}class K extends h{constructor(e,t){super(`Error in onBefore/onAfter hook callback with ID ${e}: ${t}`,"UNKNOWN_ERROR")}}class le{constructor(){o(this,"time",0);o(this,"delta",0);o(this,"frame",0);o(this,"running",!1);o(this,"fps",0);o(this,"startTime",0);o(this,"lastTime",0);o(this,"rafId",null);o(this,"callback",null);o(this,"maxFps",0);this.loop=this.loop.bind(this)}start(e){if(this.running)return this;this.callback=e,this.running=!0;const t=performance.now();return this.frame===0?this.startTime=t:this.startTime=t-this.time*1e3,this.lastTime=t,this.rafId=requestAnimationFrame(this.loop),this}stop(){return this.running?(this.running=!1,this.rafId!==null&&(cancelAnimationFrame(this.rafId),this.rafId=null),this):this}reset(){return this.stop(),this.time=0,this.delta=0,this.frame=0,this.fps=0,this}getState(){return{time:this.time,delta:this.delta,frame:this.frame,running:this.running,fps:Math.round(this.fps)}}tick(e=0){return this.delta=e,this.time+=e,this.frame++,this.callback&&this.callback(this.getState()),this}setTime(e){return this.time=e,this}destroy(){this.reset(),this.callback=null}setMaxFps(e){return this.maxFps=e,this}loop(e){if(!this.running)return;if(this.maxFps>0){const n=1e3/this.maxFps;if(e-this.lastTime<n){this.rafId=requestAnimationFrame(this.loop);return}}this.delta=(e-this.lastTime)/1e3,this.lastTime=e;const t=this.delta>0?1/this.delta:0;this.fps=this.fps*.95+t*.05,this.time=(e-this.startTime)/1e3,this.frame++,this.callback&&this.callback(this.getState()),this.rafId=requestAnimationFrame(this.loop)}}class k{constructor(e){o(this,"gl");o(this,"vao",null);o(this,"vbo",null);o(this,"ibo",null);o(this,"vertexCount",0);o(this,"indexCount",0);o(this,"useIndices",!1);o(this,"vaoExt",null);o(this,"isWebGL2");this.gl=e,this.isWebGL2=e instanceof WebGL2RenderingContext,this.isWebGL2||(this.vaoExt=e.getExtension("OES_vertex_array_object"))}static fullscreenQuad(e){const t=new k(e),n=new Float32Array([-1,-1,0,0,1,-1,1,0,-1,1,0,1,1,1,1,1]),i=new Uint16Array([0,1,2,2,1,3]);return t.setup(n,i),t}setup(e,t){const n=this.gl;return this.createVAO(),this.bindVAO(),this.vbo=n.createBuffer(),n.bindBuffer(n.ARRAY_BUFFER,this.vbo),n.bufferData(n.ARRAY_BUFFER,e,n.STATIC_DRAW),this.vertexCount=e.length/4,t&&(this.ibo=n.createBuffer(),n.bindBuffer(n.ELEMENT_ARRAY_BUFFER,this.ibo),n.bufferData(n.ELEMENT_ARRAY_BUFFER,t,n.STATIC_DRAW),this.indexCount=t.length,this.useIndices=!0),this.unbindVAO(),this}linkAttributes(e){const t=this.gl;this.bindVAO(),t.bindBuffer(t.ARRAY_BUFFER,this.vbo);const n=4*Float32Array.BYTES_PER_ELEMENT,i=this.getPositionLocation(e);i>=0&&(t.enableVertexAttribArray(i),t.vertexAttribPointer(i,2,t.FLOAT,!1,n,0));const r=this.getTexcoordLocation(e);return r>=0&&(t.enableVertexAttribArray(r),t.vertexAttribPointer(r,2,t.FLOAT,!1,n,2*Float32Array.BYTES_PER_ELEMENT)),this.useIndices&&t.bindBuffer(t.ELEMENT_ARRAY_BUFFER,this.ibo),this.unbindVAO(),this}bind(){return this.bindVAO(),this}unbind(){return this.unbindVAO(),this}draw(){const e=this.gl;return this.bindVAO(),this.useIndices?e.drawElements(e.TRIANGLES,this.indexCount,e.UNSIGNED_SHORT,0):e.drawArrays(e.TRIANGLE_STRIP,0,this.vertexCount),this}destroy(){const e=this.gl;this.deleteVAO(),this.vbo&&(e.deleteBuffer(this.vbo),this.vbo=null),this.ibo&&(e.deleteBuffer(this.ibo),this.ibo=null)}getPositionLocation(e){let t=e.getAttribLocation("a_position");return t>=0||(t=e.getAttribLocation("aPosition"),t>=0)||(t=e.getAttribLocation("position"),t>=0)?t:-1}getTexcoordLocation(e){let t=e.getAttribLocation("a_texcoord");return t>=0||(t=e.getAttribLocation("aTexCoord"),t>=0)||(t=e.getAttribLocation("texcoord"),t>=0)||(t=e.getAttribLocation("a_uv"),t>=0)?t:-1}createVAO(){this.isWebGL2?this.vao=this.gl.createVertexArray():this.vaoExt&&(this.vao=this.vaoExt.createVertexArrayOES())}bindVAO(){this.vao&&(this.isWebGL2?this.gl.bindVertexArray(this.vao):this.vaoExt&&this.vaoExt.bindVertexArrayOES(this.vao))}unbindVAO(){this.isWebGL2?this.gl.bindVertexArray(null):this.vaoExt&&this.vaoExt.bindVertexArrayOES(null)}deleteVAO(){this.vao&&(this.isWebGL2?this.gl.deleteVertexArray(this.vao):this.vaoExt&&this.vaoExt.deleteVertexArrayOES(this.vao),this.vao=null)}}class ce{constructor(e){o(this,"gl");o(this,"program",null);o(this,"vertexShader",null);o(this,"fragmentShader",null);this.gl=e}compile(e,t){return this.destroy(),this.vertexShader=this.compileShader("vertex",e),this.fragmentShader=this.compileShader("fragment",t),this.linkProgram(),this}use(){return this.program&&this.gl.useProgram(this.program),this}getProgram(){return this.program}getAttribLocation(e){return this.program?this.gl.getAttribLocation(this.program,e):-1}getUniformLocation(e){return this.program?this.gl.getUniformLocation(this.program,e):null}destroy(){const e=this.gl;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)}compileShader(e,t){const n=this.gl,i=e==="vertex"?n.VERTEX_SHADER:n.FRAGMENT_SHADER,r=n.createShader(i);if(!r)throw new E(e,t,"Failed to create shader object");if(n.shaderSource(r,t),n.compileShader(r),!n.getShaderParameter(r,n.COMPILE_STATUS)){const a=n.getShaderInfoLog(r)||"Unknown error";throw n.deleteShader(r),new E(e,t,a)}return r}linkProgram(){const e=this.gl;if(!this.vertexShader||!this.fragmentShader)throw new R("Shaders not compiled");const t=e.createProgram();if(!t)throw new R("Failed to create program object");if(e.attachShader(t,this.vertexShader),e.attachShader(t,this.fragmentShader),e.linkProgram(t),!e.getProgramParameter(t,e.LINK_STATUS)){const i=e.getProgramInfoLog(t)||"Unknown error";throw e.deleteProgram(t),new R(i)}this.program=t}}class T{constructor(e,t){o(this,"name");o(this,"method");o(this,"isArray");o(this,"isMatrix");o(this,"location",null);o(this,"locationResolved",!1);o(this,"value");this.name=e,this.value=t;const n=T.inferMethodInfo(t);this.method=n.method,this.isArray=n.isArray,this.isMatrix=n.isMatrix}static inferMethodInfo(e){if(typeof e=="boolean")return{method:"uniform1i",isArray:!1,isMatrix:!1};if(typeof e=="number")return{method:"uniform1f",isArray:!1,isMatrix:!1};if(!Array.isArray(e))return{method:"uniform1f",isArray:!1,isMatrix:!1};const t=e.length,n=e[0];if(Array.isArray(n))switch(n.length){case 2:return{method:"uniform2fv",isArray:!0,isMatrix:!1};case 3:return{method:"uniform3fv",isArray:!0,isMatrix:!1};case 4:return{method:"uniform4fv",isArray:!0,isMatrix:!1};default:return{method:"uniform1fv",isArray:!0,isMatrix:!1}}switch(t){case 2:return{method:"uniform2fv",isArray:!1,isMatrix:!1};case 3:return{method:"uniform3fv",isArray:!1,isMatrix:!1};case 4:return{method:"uniform4fv",isArray:!1,isMatrix:!1};case 9:return{method:"uniformMatrix3fv",isArray:!1,isMatrix:!0};case 16:return{method:"uniformMatrix4fv",isArray:!1,isMatrix:!0};default:return{method:"uniform1fv",isArray:!0,isMatrix:!1}}}resolveLocation(e,t){return this.locationResolved||(this.location=e.getUniformLocation(t,this.name),this.locationResolved=!0),this.location}invalidateLocation(){this.location=null,this.locationResolved=!1}setValue(e){this.value=e}getValue(){return this.value}upload(e,t){const n=this.resolveLocation(e,t);if(n===null)return;const i=this.value;let r;switch(typeof i=="boolean"?r=i?1:0:typeof i=="number"?r=i:this.isArray&&Array.isArray(i[0])?r=new Float32Array(i.flat()):r=new Float32Array(i),this.method){case"uniform1f":e.uniform1f(n,r);break;case"uniform1i":e.uniform1i(n,r);break;case"uniform1fv":e.uniform1fv(n,r);break;case"uniform2fv":e.uniform2fv(n,r);break;case"uniform3fv":e.uniform3fv(n,r);break;case"uniform4fv":e.uniform4fv(n,r);break;case"uniformMatrix2fv":e.uniformMatrix2fv(n,!1,r);break;case"uniformMatrix3fv":e.uniformMatrix3fv(n,!1,r);break;case"uniformMatrix4fv":e.uniformMatrix4fv(n,!1,r);break}}}class _{constructor(e){o(this,"parsed",null);this.source=e}parse(){if(this.parsed)return this.parsed;const e=this.detectVersion(),t=this.detectImports(),n=this.detectUniforms(),i=this.detectFunctions(n);return this.parsed={version:e,imports:t,uniforms:n,functions:i}}isParsed(){return this.parsed!==null}setSource(e){this.source=e,this.parsed=null}version(){return this.detectVersion()}detectVersion(){return/^\s*#version\s+300\s+es/m.test(this.source)?2:1}detectImports(){const e=/^[ \t]*#import\s+(\w+)(?:\s+as\s+(\w+))?\s+from\s+["'](.+)["']/gm,t=/^[ \t]*[^\w\s]?import\b/gm,n=[],i=new Set;let r,s=1,a=0;for(;(r=e.exec(this.source))!==null;){s+=(this.source.substring(a,r.index).match(/\n/g)||[]).length,a=r.index,i.add(s);const f=r[1],c=r[2]||r[1],d=r[3];if(n.some(p=>p.alias===c))throw new D(c,s);n.push({name:f,alias:c,module:d,line:s})}let l;for(;(l=t.exec(this.source))!==null;){const f=(this.source.substring(0,l.index).match(/\n/g)||[]).length+1;if(i.has(f))continue;const c=this.source.split(`
|
|
6
|
+
`)[f-1].trim();throw new $(f,this.diagnoseImport(c))}return n}diagnoseImport(e){const t=e.match(/^([^\w\s])import\b/);if(t&&t[1]!=="#")return`Invalid prefix '${t[1]}'. Expected: #import <function> from '<module>'`;if(/^import\b/.test(e))return"Missing '#' prefix. Expected: #import <function> from '<module>'";if(/^#import\s+from\b/.test(e))return"Missing function name. Expected: #import <function> from '<module>'";if(/^#import\s+\w+\s*$/.test(e))return`Missing 'from' clause. Expected: #import ${e.split(/\s+/)[1]} from '<module>'`;if(/^#import\s+\w+\s+as\s*$/.test(e)||/^#import\s+\w+\s+as\s+from\b/.test(e))return`Missing alias name after 'as'. Expected: #import ${e.split(/\s+/)[1]} as <alias> from '<module>'`;if(/^#import\s+\w+\s+as\s+\w+\s*$/.test(e)){const n=e.split(/\s+/);return`Missing 'from' clause. Expected: #import ${n[1]} as ${n[3]} from '<module>'`}if(/^#import\s+\w+(?:\s+as\s+\w+)?\s+from\s+\w+/.test(e)){const n=e.match(/from\s+(\S+)/);return`Module name must be quoted. Expected: from '${n==null?void 0:n[1]}'`}return"Invalid syntax. Expected: #import <function> from '<module>'"}detectUniforms(){const e=/^[ \t]*uniform\s+(?:(?:highp|mediump|lowp)\s+)?(\w+)\s+(\w+)(?:\[(\d+)\])?\s*;/gm,t=[];let n,i=1,r=0;for(;(n=e.exec(this.source))!==null;){i+=(this.source.substring(r,n.index).match(/\n/g)||[]).length,r=n.index;const s=n[1],a=n[2],l=n[3]?parseInt(n[3],10):void 0;t.push({name:a,type:s,line:i,arrayNum:l})}return t}detectFunctions(e){const t=[],n="void|float|int|uint|bool|vec[234]|ivec[234]|uvec[234]|bvec[234]|mat[234](?:x[234])?|sampler2D|samplerCube|sampler3D|sampler2DArray",i=new RegExp(`^[ \\t]*(${n})\\s+(\\w+)\\s*\\(([^)]*)\\)\\s*\\{`,"gm");let r;for(;(r=i.exec(this.source))!==null;){const s=r[1],a=r[2],l=r[3].trim(),f=r.index,c=(this.source.substring(0,f).match(/\n/g)||[]).length+1,d=this.source.indexOf("{",f),p=this.findClosingBrace(this.source,d);if(p===-1)continue;const g=this.source.slice(d,p+1),Z=this.parseParams(l),ee=this.findFunctionCalls(g),te=this.findUniformCalls(g,e),ne=this.findMentionCalls(g);t.push({name:a,type:s,params:Z,body:g,dependencies:[...ee,...te,...ne],line:c})}return t}parseParams(e){if(!e.trim())return[];const t=[],n=e.split(",");for(const i of n){const r=i.trim();if(!r)continue;const a=r.replace(/\b(in|out|inout|const|highp|mediump|lowp)\b\s*/g,"").trim().match(/^(\w+)\s+(\w+)(?:\[\d*\])?$/);a&&t.push({type:a[1],name:a[2]})}return t}findClosingBrace(e,t){let n=0,i=!1,r=!1,s=!1,a=!1;for(let l=t;l<e.length;l++){const f=e[l],c=e[l+1],d=e[l-1];if(!r&&!a&&f==="/"&&c==="/"){s=!0;continue}if(s&&f===`
|
|
7
|
+
`){s=!1;continue}if(!r&&!s&&f==="/"&&c==="*"){a=!0,l++;continue}if(a&&f==="*"&&c==="/"){a=!1,l++;continue}if(!(s||a)){if(f==='"'&&d!=="\\"){r=!r;continue}if(!r){if(f==="{")n++,i=!0;else if(f==="}"&&(n--,i&&n===0))return l}}}return-1}findFunctionCalls(e){const t=[],n=new Set(["if","else","for","while","do","switch","case","return","break","continue","discard"]),i=/\b([a-zA-Z_]\w*)\s*\(/g;let r;for(;(r=i.exec(e))!==null;){const s=r[1];n.has(s)||t.push({name:s,type:"function",index:r.index})}return t}findUniformCalls(e,t){const n=[];for(const i of t){const r=new RegExp(`\\b${i.name}\\b`,"g");let s;for(;(s=r.exec(e))!==null;)n.push({name:i.name,type:"uniform",index:s.index})}return n}findMentionCalls(e){const t=[],n=/@(\w+)\.([a-zA-Z_]\w*)/g;let i;for(;(i=n.exec(e))!==null;){const r=i[1],s=i[2];t.push({name:`${r}.${s}`,type:"mention",index:i.index})}return t}}class Q{constructor(e){o(this,"isCompiled",!1);o(this,"original");o(this,"compiled");o(this,"requirements",{uniforms:new Map,functions:new Map});this.original=new _(e),this.compiled=new _(e)}version(){return this.original.version()}source(){return this.original.source}recompile(){return this.isCompiled=!1,this.compile()}compile(){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)}processImports(){const e=this.original.parse(),t=e.functions.flatMap(n=>n.dependencies.filter(i=>i.type==="mention").map(i=>({name:i.name.split(".")[0],uniform:i.name.split(".")[1]})));for(const n of e.imports){const i=w.resolve(n.module),r=i.extract(n.name);let s=t.filter(l=>l.name===r.function.name);if(s.length>0){const l=i.getDefinition().uniforms;if(s=s.filter(f=>{const c=l.find(d=>d.name===`u_${f.uniform}`||d.name===f.uniform);if(c){r.dependencies.uniforms.some(p=>p.name===c.name)||r.dependencies.uniforms.push(c);const d=t.indexOf(f);return d>-1&&t.splice(d,1),!1}return!0}),s.length>0)throw new q(n.module,r.function.name,s[0].uniform)}const a=i.copy();this.processExtraction(r,n.alias,a.options),y.merge(n.module,a)}if(t.length>0)throw new j(t[0].name,t[0].uniform)}processExtraction(e,t,n={}){const i=e.function,r=Math.random().toString(36).substring(2,8),s=`${t}_${r}`;for(let l=e.dependencies.functions.length-1;l>=0;l--){const f=e.dependencies.functions[l],c=this.rewriteFunction(f,t,{uniforms:e.dependencies.uniforms,functions:e.dependencies.functions,unique:s});this.requirements.functions.set(c.name,c)}const a=this.rewriteFunction(i,t,{uniforms:e.dependencies.uniforms,functions:e.dependencies.functions,rename:!0,unique:s});this.requirements.functions.set(a.name,a);for(const l of e.dependencies.uniforms){if(S.has(l.name))continue;const f={...l,name:`${s}_${l.name}${l.arrayNum?`[${l.arrayNum}]`:""}`};if(n[i.name]){const c=Object.entries(n[i.name]).find(([d,p])=>p.uniform===l.name);c&&(c[1].uniform=`${s}_${l.name}`)}this.requirements.uniforms.set(f.name,f)}n[i.name]&&t!==i.name&&(n[t]=n[i.name],delete n[i.name])}rewriteFunction(e,t,n={rename:!1,uniforms:[],functions:[],unique:""}){const i=new Set(n.uniforms.map(c=>c.name)),r=new Set(n.functions.map(c=>c.name)),s=[],a=n.unique?n.unique:t;for(const c of e.dependencies)if(c.index!==void 0)if(c.type==="uniform"&&i.has(c.name)){if(S.has(c.name))continue;s.push({index:c.index,oldText:c.name,newText:`${a}_${c.name}`})}else c.type==="function"&&r.has(c.name)&&s.push({index:c.index,oldText:c.name,newText:`${a}_${c.name}`});const l=this.applyRewrites(e.body,s),f=n.rename?t:`${a}_${e.name}`;return{...e,name:f,body:l}}applyRewrites(e,t){const n=[...t].sort((r,s)=>s.index-r.index);let i=e;for(const r of n)i=i.slice(0,r.index)+r.newText+i.slice(r.index+r.oldText.length);return i}build(){const e=this.original.parse();let t=this.original.source;t=this.removeImportLines(t,e);const n=this.findInsertionPointForUniforms(t),i=this.generateUniformsCode();i&&(t=t.slice(0,n)+i+`
|
|
8
|
+
`+t.slice(n));const r=this.findInsertionPointForFunctions(t),s=this.generateFunctionCode();return s&&(t=t.slice(0,r)+s+t.slice(r)),t=t.replace(/\n{3,}/g,`
|
|
9
9
|
|
|
10
|
-
`),
|
|
11
|
-
`),i=new Set(
|
|
12
|
-
`)}findInsertionPointForUniforms(e){const
|
|
13
|
-
`);let r=0;if(
|
|
14
|
-
`);let r=0;if(
|
|
10
|
+
`),t=this.replaceMentions(t,e),t}replaceMentions(e,t){let n=e;const i=t.functions.filter(r=>r.dependencies.some(s=>s.type==="mention"));for(const r of i){const s=r.dependencies.filter(a=>a.type==="mention");for(const a of s){const l=a.name.split("."),f=new RegExp(`\\b${l[0]}_(\\w+)_${l[1]}\\b`,"g"),c=this.requirements.uniforms.keys().find(p=>{var g;return((g=p.match(f))==null?void 0:g[0])===p});if(!c)throw new X(a.name,r.name);const d=new RegExp(`@\\b${a.name}\\b`,"g");n=n.replace(d,c)}}return n}removeImportLines(e,t){const n=e.split(`
|
|
11
|
+
`),i=new Set(t.imports.map(r=>r.line));return n.filter((r,s)=>{const a=i.has(s+1);if(!a&&r.trim()===""&&s>0){const l=s-1;if(i.has(l+1))return!1}return!a}).join(`
|
|
12
|
+
`)}findInsertionPointForUniforms(e){const t=new _(e).parse(),n=t.uniforms.find(a=>a.line===Math.max(...t.uniforms.map(l=>l.line??0))),i=e.split(`
|
|
13
|
+
`);let r=0;if(n&&n.line)r=n.line;else for(let a=0;a<i.length;a++){const l=i[a].trim();if(l.startsWith("#version")){r=a+1;continue}if(l.startsWith("precision ")){r=a+1;continue}if(l===""||l.startsWith("//")){r===a&&(r=a+1);continue}break}let s=0;for(let a=0;a<r;a++)s+=i[a].length+1;return s}findInsertionPointForFunctions(e){const t=new _(e).parse(),n=t.functions.find(a=>a.line===Math.min(...t.functions.map(l=>l.line??1/0))),i=e.split(`
|
|
14
|
+
`);let r=0;if(n&&n.line)r=n.line-2;else throw new P;let s=0;for(let a=0;a<r;a++)s+=i[a].length+1;return s}generateUniformsCode(){const e=[];if(this.requirements.uniforms.size>0)for(const t of this.checkUniformsPresence())e.push(`uniform ${t.type} ${t.name};`);return e.length===0?"":e.join(`
|
|
15
15
|
`)+`
|
|
16
|
-
`}generateFunctionCode(){const e=[];if(this.requirements.functions.size>0)for(const
|
|
17
|
-
${
|
|
16
|
+
`}generateFunctionCode(){const e=[];if(this.requirements.functions.size>0)for(const t of this.checkFunctionsPresence()){const n=t.params.map(i=>`${i.type} ${i.name}`).join(", ");e.push(`
|
|
17
|
+
${t.type} ${t.name}(${n}) ${t.body}`)}return e.length===0?"":e.join(`
|
|
18
18
|
`)+`
|
|
19
|
-
`}checkUniformsPresence(){const e=this.original.parse(),
|
|
19
|
+
`}checkUniformsPresence(){const e=this.original.parse(),t=[],n=this.requirements.uniforms;for(const[i,r]of n){const s=e.uniforms.find(a=>a.name===i);if(!s){t.push(r);continue}if(s.type!==r.type)throw new M("uniform",i,r.type,s.type)}return t}checkFunctionsPresence(){const e=this.original.parse(),t=[],n=this.requirements.functions;for(const[i,r]of n){const s=e.functions.find(a=>a.name===i);if(!s){t.push(r);continue}if(s.type!==r.type)throw new M("function",i,r.type,s.type)}return t}}class v extends Q{constructor(t,n,i={}){super(n);o(this,"name");o(this,"options",{});this.name=t,this.options=this.resolveOptions(i)}resolveOptions(t){if(!(t!=null&&t.default))return t||{};const n=this.original.parse(),i=t.default;for(const r of n.functions)if(!(r.name==="main"||r.name==="default"))if(t[r.name]){const s=t[r.name];for(const a in i)a in s||(s[a]=i[a])}else t[r.name]={...i};return delete t.default,t||{}}static define(t){const{name:n,source:i,options:r}=t;if(n==="sandbox"||n.startsWith("sandbox/"))throw new z(n);const s=new v(n,i,r);if(w.has(n))throw new H(n);return w.register(n,s),s}static resolve(t){return w.resolve(t)}copy(t="original"){return new v(this.name,this[t].source,JSON.parse(JSON.stringify(this.options)))}merge(t){this.options=this.options||{};const n=this.getDefinition().uniforms.map(i=>i.name);for(const[i,r]of Object.entries(t.options??{}))if(!this.options[i])this.options[i]=r;else for(const[s,a]of Object.entries(r))n.includes(a.uniform)||(this.options[i][s]=a)}getDefinition(){return this.compile(),{name:this.name,methods:this.compiled.parse().functions.map(t=>t.name).filter(t=>t!=="main"&&t!=="default"),uniforms:this.compiled.parse().uniforms.map(t=>({name:t.name,type:t.type})),options:this.options}}extract(t){if(this.compile(),t==="main")throw new G(this.name);if(t==="default")throw new W(this.name);const n=this.compiled.parse(),i=n.functions.find(a=>a.name===t);if(!i)throw new N(this.name,t);const r=new Map,s=new Map;return this.collectDependencies({current:i,scope:{functions:n.functions,uniforms:n.uniforms},collected:{functions:r,uniforms:s},visited:new Set([t])}),{function:i,dependencies:{functions:Array.from(r.values()),uniforms:Array.from(s.values())}}}collectDependencies(t){for(const n of t.current.dependencies)if(n.type==="function"){if(t.visited.has(n.name))continue;const i=t.scope.functions.find(r=>r.name===n.name);i&&(t.visited.add(n.name),t.collected.functions.set(n.name,i),this.collectDependencies({current:i,scope:{functions:t.scope.functions,uniforms:t.scope.uniforms},collected:{functions:t.collected.functions,uniforms:t.collected.uniforms},visited:t.visited}))}else if(n.type==="uniform"){const i=t.scope.uniforms.find(r=>r.name===n.name);i&&!t.collected.uniforms.has(n.name)&&t.collected.uniforms.set(n.name,i)}}}class J{constructor(e=[]){o(this,"modules",new Map);e.forEach(t=>{this.register(t.name,t)})}compile(){this.modules.forEach(e=>{e.compile()})}available(){return Array.from(this.modules.values()).map(e=>e.getDefinition())}defaults(){const e={};return this.modules.forEach(t=>{const n=t.getDefinition();if(n.options)for(const i in n.options){const r=n.options[i];for(const s in r){const a=r[s];a.default!==void 0&&!n.uniforms.map(l=>l.name).includes(a.uniform)&&(e[a.uniform]=a.default)}}}),e}resolveOptions(e){for(const t of this.modules.values())if(t.options&&t.options[e])return t.options[e];return null}register(e,t){this.modules.set(e,t)}merge(e,t){if(!this.modules.has(e))return this.register(e,t);const n=this.modules.get(e);n.merge(t),this.modules.set(e,n)}resolve(e){const t=this.modules.get(e);if(!t)throw new B(e);return t}has(e){return this.modules.has(e)}isEmpty(){return this.modules.size===0}remove(e){this.modules.delete(e)}load(e){e.forEach(t=>{this.register(t.name,t)})}clear(){this.modules.clear()}}const ue=`// ─── Constants ──────────────────────────────────────────────
|
|
20
20
|
|
|
21
21
|
const float PI = 3.14159265359;
|
|
22
22
|
const float TAU = 6.28318530718;
|
|
@@ -225,7 +225,7 @@ vec2 voronoi(vec2 p) {
|
|
|
225
225
|
}
|
|
226
226
|
|
|
227
227
|
void main() {}
|
|
228
|
-
`,
|
|
228
|
+
`,fe=`/**
|
|
229
229
|
* Hex integer to RGB.
|
|
230
230
|
* Usage: hex(0xFF6600) → orange
|
|
231
231
|
*/
|
|
@@ -324,7 +324,7 @@ vec3 iridescent(vec2 uv, float time, float speed) {
|
|
|
324
324
|
}
|
|
325
325
|
|
|
326
326
|
void main() {}
|
|
327
|
-
`,
|
|
327
|
+
`,he=`// ─── Time Shapers ──────────────────────────────────────────
|
|
328
328
|
// Convert raw time (u_time) to normalized 0–1 range.
|
|
329
329
|
// Output feeds directly into easing functions.
|
|
330
330
|
|
|
@@ -446,7 +446,7 @@ float teleport(float t) {
|
|
|
446
446
|
}
|
|
447
447
|
|
|
448
448
|
void main() {}
|
|
449
|
-
`,
|
|
449
|
+
`,de=`#import hash from 'sandbox'
|
|
450
450
|
#import noise from 'sandbox'
|
|
451
451
|
#import fbm from 'sandbox'
|
|
452
452
|
#import voronoi from 'sandbox'
|
|
@@ -641,7 +641,7 @@ vec2 cells(vec2 uv, float intensity) {
|
|
|
641
641
|
}
|
|
642
642
|
|
|
643
643
|
void main() {}
|
|
644
|
-
`,
|
|
644
|
+
`,me=`#import hash from 'sandbox'
|
|
645
645
|
|
|
646
646
|
uniform float u_intensity;
|
|
647
647
|
|
|
@@ -828,7 +828,7 @@ vec3 arcade(vec3 color, vec2 uv, float intensity) {
|
|
|
828
828
|
}
|
|
829
829
|
|
|
830
830
|
void main() {}
|
|
831
|
-
`,b=new Q([new v("sandbox",ae),new v("sandbox/colors",le),new v("sandbox/time",ce),new v("sandbox/effects",ue,{default:{intensity:{uniform:"u_intensity",default:1}},pixelate:{intensity:{uniform:"u_intensity",default:20}},twist:{intensity:{uniform:"u_intensity",default:1}},ripple:{intensity:{uniform:"u_intensity",default:1}},fisheye:{intensity:{uniform:"u_intensity",default:1}},wobble:{intensity:{uniform:"u_intensity",default:1}},organic:{intensity:{uniform:"u_intensity",default:3}},glitch:{intensity:{uniform:"u_intensity",default:1}},mirror:{intensity:{uniform:"u_intensity",default:0}},kaleidoscope:{intensity:{uniform:"u_intensity",default:6}},zoom:{intensity:{uniform:"u_intensity",default:1}},warp:{intensity:{uniform:"u_intensity",default:1}},displace:{intensity:{uniform:"u_intensity",default:1}},shatter:{intensity:{uniform:"u_intensity",default:10}},cells:{intensity:{uniform:"u_intensity",default:8}},glass:{intensity:{uniform:"u_intensity",default:1}}}),new v("sandbox/filters",fe,{default:{intensity:{uniform:"u_intensity",default:1}},posterize:{intensity:{uniform:"u_intensity",default:8}},threshold:{intensity:{uniform:"u_intensity",default:.5}},grain:{intensity:{uniform:"u_intensity",default:.1}},vignette:{intensity:{uniform:"u_intensity",default:1.4}},glow:{intensity:{uniform:"u_intensity",default:.5}},gamma:{intensity:{uniform:"u_intensity",default:2.2}},dither:{intensity:{uniform:"u_intensity",default:4}},highlights:{intensity:{uniform:"u_intensity",default:.5}}})]),y=new Q,S=new Map([["u_resolution","vec2"],["u_time","float"],["u_delta","float"],["u_mouse","vec2"],["u_frame","int"]]);class he{constructor(e){a(this,"gl");a(this,"program",null);a(this,"uniforms",new Map);this.gl=e}attachProgram(e){this.program=e;for(const n of this.uniforms.values())n.invalidateLocation();return this}set(e,n){const t=this.uniforms.get(e);return t?t.setValue(n):this.uniforms.set(e,new M(e,n)),this}setMany(e){for(const[n,t]of Object.entries(e))this.set(n,t);return this}get(e){var n;return(n=this.uniforms.get(e))==null?void 0:n.getValue()}has(e){return this.uniforms.has(e)}delete(e){return this.uniforms.delete(e)}uploadAll(){if(!this.program)return this;for(const e of this.uniforms.values())e.upload(this.gl,this.program);return this}uploadBuiltIns(e,n,t){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)return this;for(const i of S.keys()){const r=this.uniforms.get(i);r&&r.upload(this.gl,this.program)}return this}clear(){this.uniforms.clear()}destroy(){this.uniforms.clear(),this.program=null}keys(){return this.uniforms.keys()}get size(){return this.uniforms.size}}class L{constructor(){a(this,"hooks",new Map)}id(){return Math.random().toString(36).substring(2,10)}add(e){const n=this.id();return this.hooks.set(n,e),()=>this.remove(n)}remove(e){this.hooks.delete(e)}run(e){for(const[n,t]of this.hooks)try{t(e)===!1&&this.remove(n)}catch(i){throw new K(n,i instanceof Error?i.message:String(i))}}destroy(){this.hooks.clear()}}class C{constructor(e,n){a(this,"canvas");a(this,"gl");a(this,"options");a(this,"onBeforeHooks",new L);a(this,"onAfterHooks",new L);a(this,"_program");a(this,"_geometry");a(this,"_uniforms");a(this,"_clock");a(this,"_resolution",[1,1]);a(this,"_mouse",[0,0]);a(this,"_version",1);a(this,"playing",!1);this.canvas=e,this.options=n,this.gl=this.initContext(),this.enableExtensions(),this._program=new se(this.gl),this._geometry=k.fullscreenQuad(this.gl),this._uniforms=new he(this.gl),this._clock=new oe,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)}static setup(e,n){const t=new C(e,n);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}initContext(){const e={antialias:this.options.antialias,preserveDrawingBuffer:this.options.preserveDrawingBuffer,alpha:!0,depth:!1,stencil:!1},n=this.canvas.getContext("webgl2",e);if(n)return this._version=2,n;const t=this.canvas.getContext("webgl",e);if(t)return this._version=1,t;const i=new V;throw this.options.onError(i),i}enableExtensions(){this.gl.getExtension("OES_standard_derivatives"),this.gl.getExtension("OES_texture_float"),this.gl.getExtension("OES_texture_float_linear"),this._version===1&&this.gl.getExtension("OES_vertex_array_object")}viewport(e,n,t,i){return this.canvas.width=t,this.canvas.height=i,this.gl.viewport(e,n,t,i),this._resolution=[t,i],this}clock(e){return this._clock.setTime(e),this}mouse(e,n){return this._mouse=[e,n],this}uniform(e,n){return this._uniforms.set(e,n),this}uniforms(e){return this._uniforms.setMany(e),this}getUniform(e){return this._uniforms.get(e)}shader(e,n){try{if(y.clear(),e.version()!==n.version())throw new $(e.version(),n.version());this._program.compile(e.source(),n.compile()),this._version=n.version(),this._geometry.linkAttributes(this._program);const t=this._program.getProgram();t&&this._uniforms.attachProgram(t);try{this.options.onLoad()}catch(i){throw new Y(i instanceof Error?i.message:String(i))}}catch(t){t instanceof h&&this.options.onError(t)}return this}play(){return this.playing?this:(this.playing=!0,this._clock.start(this.onRender),this)}pause(){if(!this.playing)return this;const e=this._clock.getState();try{this.onBeforeHooks.run(e)}catch(n){n instanceof h&&this.options.onError(n)}this.playing=!1,this._clock.stop();try{this.onAfterHooks.run(e)}catch(n){n instanceof h&&this.options.onError(n)}return this}render(){return this.onRender(this._clock.getState()),this}getContext(){return this.gl}getVersion(){return this._version}getClock(){return this._clock}destroy(){this.pause(),this._clock.destroy(),this._geometry.destroy(),this._program.destroy(),this._uniforms.destroy(),this.onAfterHooks.destroy(),this.onBeforeHooks.destroy(),y.clear()}onRender(e){const n=this.gl;try{this.onBeforeHooks.run(e)}catch(t){t instanceof h&&this.options.onError(t)}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();try{this.onAfterHooks.run(e)}catch(t){t instanceof h&&this.options.onError(t)}}}class m extends X{constructor(e){super(e),S.forEach((n,t)=>{this.requirements.uniforms.set(t,{name:t,type:n,line:0})})}}const R=`#ifdef GL_ES
|
|
831
|
+
`,w=new J([new v("sandbox",ue),new v("sandbox/colors",fe),new v("sandbox/time",he),new v("sandbox/effects",de,{default:{intensity:{uniform:"u_intensity",default:1}},pixelate:{intensity:{uniform:"u_intensity",default:20}},twist:{intensity:{uniform:"u_intensity",default:1}},ripple:{intensity:{uniform:"u_intensity",default:1}},fisheye:{intensity:{uniform:"u_intensity",default:1}},wobble:{intensity:{uniform:"u_intensity",default:1}},organic:{intensity:{uniform:"u_intensity",default:3}},glitch:{intensity:{uniform:"u_intensity",default:1}},mirror:{intensity:{uniform:"u_intensity",default:0}},kaleidoscope:{intensity:{uniform:"u_intensity",default:6}},zoom:{intensity:{uniform:"u_intensity",default:1}},warp:{intensity:{uniform:"u_intensity",default:1}},displace:{intensity:{uniform:"u_intensity",default:1}},shatter:{intensity:{uniform:"u_intensity",default:10}},cells:{intensity:{uniform:"u_intensity",default:8}},glass:{intensity:{uniform:"u_intensity",default:1}}}),new v("sandbox/filters",me,{default:{intensity:{uniform:"u_intensity",default:1}},posterize:{intensity:{uniform:"u_intensity",default:8}},threshold:{intensity:{uniform:"u_intensity",default:.5}},grain:{intensity:{uniform:"u_intensity",default:.1}},vignette:{intensity:{uniform:"u_intensity",default:1.4}},glow:{intensity:{uniform:"u_intensity",default:.5}},gamma:{intensity:{uniform:"u_intensity",default:2.2}},dither:{intensity:{uniform:"u_intensity",default:4}},highlights:{intensity:{uniform:"u_intensity",default:.5}}})]),y=new J,S=new Map([["u_resolution","vec2"],["u_time","float"],["u_delta","float"],["u_mouse","vec2"],["u_frame","int"]]);class pe{constructor(e){o(this,"gl");o(this,"program",null);o(this,"uniforms",new Map);this.gl=e}attachProgram(e){this.program=e;for(const t of this.uniforms.values())t.invalidateLocation();return this}set(e,t){const n=this.uniforms.get(e);return n?n.setValue(t):this.uniforms.set(e,new T(e,t)),this}setMany(e){for(const[t,n]of Object.entries(e))this.set(t,n);return this}get(e){var t;return(t=this.uniforms.get(e))==null?void 0:t.getValue()}has(e){return this.uniforms.has(e)}delete(e){return this.uniforms.delete(e)}uploadAll(){if(!this.program)return this;for(const e of this.uniforms.values())e.upload(this.gl,this.program);return this}uploadBuiltIns(e,t,n){if(this.set("u_resolution",t),this.set("u_time",e.time),this.set("u_delta",e.delta),this.set("u_mouse",n),this.set("u_frame",e.frame),!this.program)return this;for(const i of S.keys()){const r=this.uniforms.get(i);r&&r.upload(this.gl,this.program)}return this}clear(){this.uniforms.clear()}destroy(){this.uniforms.clear(),this.program=null}keys(){return this.uniforms.keys()}get size(){return this.uniforms.size}}class x{constructor(e,t,n,i){o(this,"name");o(this,"gl");o(this,"texture",null);o(this,"location",null);o(this,"locationResolved",!1);o(this,"source");o(this,"options");o(this,"dynamicOverride");o(this,"needsUpload",!0);o(this,"needsReupload",!1);this.gl=e,this.name=t,this.source=n,this.dynamicOverride=i==null?void 0:i.dynamic,this.options=x.resolveOptions(n,i),this.needsReupload=this.options.dynamic}setSource(e){this.source=e,this.needsUpload=!0,this.needsReupload=this.dynamicOverride??x.isDynamicSource(e)}resolveLocation(e,t){return this.locationResolved||(this.location=e.getUniformLocation(t,this.name),this.locationResolved=!0),this.location}invalidateLocation(){this.location=null,this.locationResolved=!1}upload(e,t){const n=this.gl,i=this.resolveLocation(n,e);if(i!==null){if(!this.texture){if(this.texture=n.createTexture(),!this.texture)return;this.needsUpload=!0}n.activeTexture(n.TEXTURE0+t),n.bindTexture(n.TEXTURE_2D,this.texture),(this.needsUpload||this.needsReupload)&&(this.uploadPixels(),this.needsUpload=!1),n.uniform1i(i,t)}}destroy(){this.texture&&(this.gl.deleteTexture(this.texture),this.texture=null),this.location=null,this.locationResolved=!1}uploadPixels(){const e=this.gl;e.pixelStorei(e.UNPACK_FLIP_Y_WEBGL,this.options.flipY),e.texImage2D(e.TEXTURE_2D,0,e.RGBA,e.RGBA,e.UNSIGNED_BYTE,this.source),this.applyParameters()}applyParameters(){const e=this.gl;e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_S,x.resolveWrap(e,this.options.wrapS)),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_T,x.resolveWrap(e,this.options.wrapT)),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MIN_FILTER,this.options.minFilter==="nearest"?e.NEAREST:e.LINEAR),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MAG_FILTER,this.options.magFilter==="nearest"?e.NEAREST:e.LINEAR)}static resolveWrap(e,t){switch(t){case"repeat":return e.REPEAT;case"mirror":return e.MIRRORED_REPEAT;case"clamp":default:return e.CLAMP_TO_EDGE}}static resolveOptions(e,t){const n=(t==null?void 0:t.wrap)??"clamp";return{wrap:n,wrapS:(t==null?void 0:t.wrapS)??n,wrapT:(t==null?void 0:t.wrapT)??n,minFilter:(t==null?void 0:t.minFilter)??"linear",magFilter:(t==null?void 0:t.magFilter)??"linear",flipY:(t==null?void 0:t.flipY)??!0,dynamic:(t==null?void 0:t.dynamic)??x.isDynamicSource(e)}}static isDynamicSource(e){return e instanceof HTMLVideoElement}}class ve{constructor(e){o(this,"gl");o(this,"program",null);o(this,"textures",new Map);this.gl=e}attachProgram(e){this.program=e;for(const t of this.textures.values())t.invalidateLocation();return this}set(e,t,n){const i=this.textures.get(e);return i?i.setSource(t):this.textures.set(e,new x(this.gl,e,t,n)),this}get(e){return this.textures.get(e)}has(e){return this.textures.has(e)}delete(e){const t=this.textures.get(e);return t?(t.destroy(),this.textures.delete(e)):!1}uploadAll(){if(!this.program)return this;let e=0;for(const t of this.textures.values())t.upload(this.program,e),e++;return this}get size(){return this.textures.size}destroy(){for(const e of this.textures.values())e.destroy();this.textures.clear(),this.program=null}}class C{constructor(){o(this,"hooks",new Map)}id(){return Math.random().toString(36).substring(2,10)}add(e){const t=this.id();return this.hooks.set(t,e),()=>this.remove(t)}remove(e){this.hooks.delete(e)}run(e){for(const[t,n]of this.hooks)try{n(e)===!1&&this.remove(t)}catch(i){throw new K(t,i instanceof Error?i.message:String(i))}}destroy(){this.hooks.clear()}}class L{constructor(e,t){o(this,"canvas");o(this,"gl");o(this,"options");o(this,"onBeforeHooks",new C);o(this,"onAfterHooks",new C);o(this,"_program");o(this,"_geometry");o(this,"_uniforms");o(this,"_textures");o(this,"_clock");o(this,"_resolution",[1,1]);o(this,"_mouse",[0,0]);o(this,"_version",1);o(this,"playing",!1);this.canvas=e,this.options=t,this.gl=this.initContext(),this.enableExtensions(),this._program=new ce(this.gl),this._geometry=k.fullscreenQuad(this.gl),this._uniforms=new pe(this.gl),this._textures=new ve(this.gl),this._clock=new le,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)}static setup(e,t){const n=new L(e,t);return t.vertex&&t.fragment&&n.shader(t.vertex,t.fragment),t.uniforms&&n._uniforms.setMany(t.uniforms),t.textures&&n.texturesFromSchema(t.textures),y.isEmpty()||n._uniforms.setMany(y.defaults()),n}initContext(){const e={antialias:this.options.antialias,preserveDrawingBuffer:this.options.preserveDrawingBuffer,alpha:!0,depth:!1,stencil:!1},t=this.canvas.getContext("webgl2",e);if(t)return this._version=2,t;const n=this.canvas.getContext("webgl",e);if(n)return this._version=1,n;const i=new V;throw this.options.onError(i),i}enableExtensions(){this.gl.getExtension("OES_standard_derivatives"),this.gl.getExtension("OES_texture_float"),this.gl.getExtension("OES_texture_float_linear"),this._version===1&&this.gl.getExtension("OES_vertex_array_object")}viewport(e,t,n,i){return this.canvas.width=n,this.canvas.height=i,this.gl.viewport(e,t,n,i),this._resolution=[n,i],this}clock(e){return this._clock.setTime(e),this}mouse(e,t){return this._mouse=[e,t],this}uniform(e,t){return this._uniforms.set(e,t),this}uniforms(e){return this._uniforms.setMany(e),this}getUniform(e){return this._uniforms.get(e)}texture(e,t,n){return this._textures.set(e,t,n),this}texturesFromSchema(e){for(const[t,n]of Object.entries(e))if(n instanceof HTMLImageElement||n instanceof HTMLCanvasElement||n instanceof HTMLVideoElement||n instanceof ImageBitmap||n instanceof ImageData||n instanceof OffscreenCanvas)this._textures.set(t,n);else{const{source:i,...r}=n;this._textures.set(t,i,r)}return this}removeTexture(e){return this._textures.delete(e),this}shader(e,t){try{if(y.clear(),e.version()!==t.version())throw new I(e.version(),t.version());this._program.compile(e.source(),t.compile()),this._version=t.version(),this._geometry.linkAttributes(this._program);const n=this._program.getProgram();n&&(this._uniforms.attachProgram(n),this._textures.attachProgram(n));try{this.options.onLoad()}catch(i){throw new Y(i instanceof Error?i.message:String(i))}}catch(n){n instanceof h&&this.options.onError(n)}return this}play(){return this.playing?this:(this.playing=!0,this._clock.start(this.onRender),this)}pause(){if(!this.playing)return this;const e=this._clock.getState();try{this.onBeforeHooks.run(e)}catch(t){t instanceof h&&this.options.onError(t)}this.playing=!1,this._clock.stop();try{this.onAfterHooks.run(e)}catch(t){t instanceof h&&this.options.onError(t)}return this}render(){return this.onRender(this._clock.getState()),this}getContext(){return this.gl}getVersion(){return this._version}getClock(){return this._clock}destroy(){this.pause(),this._clock.destroy(),this._geometry.destroy(),this._program.destroy(),this._uniforms.destroy(),this._textures.destroy(),this.onAfterHooks.destroy(),this.onBeforeHooks.destroy(),y.clear()}onRender(e){const t=this.gl;try{this.onBeforeHooks.run(e)}catch(n){n instanceof h&&this.options.onError(n)}t.clearColor(0,0,0,0),t.clear(t.COLOR_BUFFER_BIT),this._program.use(),this._uniforms.uploadBuiltIns(e,this._resolution,this._mouse),this._uniforms.uploadAll(),this._textures.uploadAll(),this._geometry.bind(),this._geometry.draw();try{this.onAfterHooks.run(e)}catch(n){n instanceof h&&this.options.onError(n)}}}class m extends Q{constructor(e){super(e),S.forEach((t,n)=>{this.requirements.uniforms.set(n,{name:n,type:t,line:0})})}}const A=`#ifdef GL_ES
|
|
832
832
|
precision mediump float;
|
|
833
833
|
#endif
|
|
834
834
|
|
|
@@ -855,7 +855,7 @@ void main() {
|
|
|
855
855
|
vec3 color = vec3(uv.x, uv.y, 0.5 + 0.5 * sin(u_time));
|
|
856
856
|
gl_FragColor = vec4(color, 1.0);
|
|
857
857
|
}
|
|
858
|
-
`,
|
|
858
|
+
`,O=`#version 300 es
|
|
859
859
|
|
|
860
860
|
in vec2 a_position;
|
|
861
861
|
in vec2 a_texcoord;
|
|
@@ -865,7 +865,7 @@ out vec2 v_texcoord;
|
|
|
865
865
|
void main() {
|
|
866
866
|
v_texcoord = a_texcoord;
|
|
867
867
|
gl_Position = vec4(a_position, 0.0, 1.0);
|
|
868
|
-
}`,
|
|
868
|
+
}`,ge=`#version 300 es
|
|
869
869
|
precision highp float;
|
|
870
870
|
|
|
871
871
|
uniform vec2 u_resolution;
|
|
@@ -879,5 +879,5 @@ void main() {
|
|
|
879
879
|
vec2 uv = gl_FragCoord.xy / u_resolution;
|
|
880
880
|
vec3 color = vec3(uv.x, uv.y, 0.5 + 0.5 * sin(u_time));
|
|
881
881
|
fragColor = vec4(color, 1.0);
|
|
882
|
-
}`;class
|
|
883
|
-
You can handle errors programmatically by providing an onError callback to suppress this log and implement custom fallback behavior.`)},onLoad:()=>{},onBeforeRender:null,onAfterRender:null,uniforms:{},modules:{}};if(e!=null&&e.vertex&&(this.usingCustomVertex=!0),e!=null&&e.vertex&&!(e!=null&&e.fragment)){
|
|
882
|
+
}`;class U{constructor(e,t){o(this,"listeners",[]);o(this,"canvasEl");o(this,"options");o(this,"engine");o(this,"usingCustomVertex",!1);if(this.canvasEl=e,this.options=this.resolveOptions(t),this.engine=L.setup(this.canvasEl,this.options),this.setupListeners(),this.setViewport(),this.options.modules)for(const[n,i]of Object.entries(this.options.modules))this.module(n,i);this.options.autoplay&&this.play()}static create(e,t){return new U(e,t)}static defineModule(e,t,n={}){v.define({name:e,source:t,options:n})}static availableModules(){return w.available()}static compile(e){return new m(e).compile()}resolveOptions(e){const t={vertex:new m(A),fragment:new m(F),autoplay:!0,pauseWhenHidden:!0,dpr:"auto",fps:0,preserveDrawingBuffer:!1,antialias:!0,onError:s=>{console.error("Oops!",s,`
|
|
883
|
+
You can handle errors programmatically by providing an onError callback to suppress this log and implement custom fallback behavior.`)},onLoad:()=>{},onBeforeRender:null,onAfterRender:null,uniforms:{},modules:{},textures:{}};if(e!=null&&e.vertex&&(this.usingCustomVertex=!0),e!=null&&e.vertex&&!(e!=null&&e.fragment)){t.vertex=new m(e.vertex);const s=t.vertex.version();t.fragment=new m(s===2?ge:F)}if(e!=null&&e.fragment&&!(e!=null&&e.vertex)){t.fragment=new m(e.fragment);const s=t.fragment.version();t.vertex=new m(s===2?O:A)}e!=null&&e.vertex&&(e!=null&&e.fragment)&&(t.vertex=new m(e.vertex),t.fragment=new m(e.fragment));const{vertex:n,fragment:i,...r}=e||{};return{...t,...r}}setupListeners(){this.listeners.push(b.on(window,"resize",()=>{this.setViewport()}),b.on(this.canvasEl,"resize",()=>{this.setViewport()}),(()=>{let e=!1;return b.on(document,"scroll",t=>{this.options.pauseWhenHidden&&(this.isInViewport()?e&&!this.isPlaying()&&(this.play(),e=!1):this.isPlaying()&&(this.pause(),e=!0))})})(),b.on(document,"mousemove",e=>{this.setMouse(e.clientX||e.pageX,e.clientY||e.pageY)}),b.on(document,"touchmove",e=>{e.touches.length>0&&this.setMouse(e.touches[0].clientX,e.touches[0].clientY)}))}destroyListeners(){this.listeners.forEach(e=>e()),this.listeners=[]}setViewport(){const e=this.options.dpr==="auto"?Math.min(2,window.devicePixelRatio||1):this.options.dpr,t=this.canvasEl.clientWidth||this.canvasEl.width||1,n=this.canvasEl.clientHeight||this.canvasEl.height||1;this.engine.viewport(0,0,Math.max(1,Math.floor(t*e)),Math.max(1,Math.floor(n*e)))}isInViewport(){const e=this.canvasEl.getBoundingClientRect();return e.bottom>=0&&e.right>=0&&e.top<=(window.innerHeight||document.documentElement.clientHeight)&&e.left<=(window.innerWidth||document.documentElement.clientWidth)}setMouse(e,t){const n=this.canvasEl.getBoundingClientRect();e>=n.left&&e<=n.right&&t>=n.top&&t<=n.bottom&&this.engine.mouse(e-n.left,t-n.top)}setUniform(e,t){return this.engine.uniform(e,t),this}setUniforms(e){return this.engine.uniforms(e),this}getUniform(e){return this.engine.getUniform(e)}setTexture(e,t,n){return this.engine.texture(e,t,n),this}setTextures(e){return this.engine.texturesFromSchema(e),this}removeTexture(e){return this.engine.removeTexture(e),this}setShader(e,t){return this.options.vertex=new m(e),this.options.fragment=new m(t),this.usingCustomVertex=!0,this.engine.shader(this.options.vertex,this.options.fragment),this}setFragment(e){const t=new m(e),n=t.version(),i=this.options.vertex.version();return this.options.fragment=t,n!==i&&(this.usingCustomVertex||(this.options.vertex=new m(n===2?O:A))),this.engine.shader(this.options.vertex,this.options.fragment),this}getFragment(){return this.options.fragment.source()}getVertex(){return this.options.vertex.source()}setFps(e){return this.engine.getClock().setMaxFps(e),this}hook(e,t="before"){return t==="before"?this.engine.onBeforeHooks.add(e):this.engine.onAfterHooks.add(e)}module(e,t){const n=y.resolveOptions(e);if(!n)return console.warn(`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.`),this;for(const[i,r]of Object.entries(t)){const s=n[i];if(!s){console.warn(`Sandbox: Option '${i}' not found for function '${e}'. Make sure to check available options with Sandbox.availableModules() and provide the correct option name.`);continue}this.setUniform(s.uniform,r)}return this}play(){return this.engine.play(),this}playAt(e){return this.engine.clock(e),this.engine.play(),this}pause(){return this.engine.pause(),this}pauseAt(e){const t=this.hook(n=>{n.time>=e&&(t(),this.pause())},"after");return this}toggle(){return this.engine.playing?this.pause():this.play(),this}time(e){return this.engine.clock(e),this}render(){return this.engine.render(),this}renderAt(e){return this.engine.clock(e),this.engine.render(),this}isPlaying(){return this.engine.playing}get version(){return this.engine.getVersion()}get canvas(){return this.canvasEl}exportAsURL(e="image/png",t){return this.canvas.toDataURL(e,t)}exportAsBlob(e="image/png",t){return new Promise((n,i)=>{this.canvas.toBlob(r=>{r?n(r):i(new Error("Failed to create blob from canvas."))},e,t)})}exportAsImage(e="image/png",t){const n=new Image;return n.src=this.exportAsURL(e,t),n}stream(e){return this.canvasEl.captureStream(e)}destroy(){this.destroyListeners(),this.engine.destroy()}}exports.Sandbox=U;exports.SandboxAttemptedToImportDefaultFunctionError=W;exports.SandboxAttemptedToImportMainFunctionError=G;exports.SandboxContextCreationError=se;exports.SandboxError=h;exports.SandboxForbiddenModuleNameError=z;exports.SandboxGLSLShaderCompilationError=E;exports.SandboxMentionCouldNotBeReplacedError=X;exports.SandboxMentionFunctionNotFoundError=j;exports.SandboxMentionUniformNotFoundError=q;exports.SandboxModuleMethodNotFoundError=N;exports.SandboxModuleNotFoundError=B;exports.SandboxOnHookCallbackError=K;exports.SandboxOnLoadCallbackError=Y;exports.SandboxOverwriteModuleError=H;exports.SandboxProgramError=R;exports.SandboxShaderDuplicateImportNameError=D;exports.SandboxShaderImportSyntaxError=$;exports.SandboxShaderRequirementMismatchError=M;exports.SandboxShaderVersionMismatchError=I;exports.SandboxShaderWithoutFunctionError=P;exports.SandboxTextureCreationError=oe;exports.SandboxTextureUnitLimitError=ae;exports.SandboxWebGLNotSupportedError=V;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AnyUniformValue, HookCallback, ModuleDefinition, SandboxOptions, UniformSchema, WebGLVersion } from "./types";
|
|
1
|
+
import type { AnyUniformValue, HookCallback, ModuleDefinition, SandboxOptions, TextureOptions, TextureSchema, TextureSource, UniformSchema, WebGLVersion } from "./types";
|
|
2
2
|
export * from "./types";
|
|
3
3
|
export * from "./errors";
|
|
4
4
|
/**
|
|
@@ -111,6 +111,28 @@ export declare class Sandbox {
|
|
|
111
111
|
* Get current uniform value.
|
|
112
112
|
*/
|
|
113
113
|
getUniform<T extends AnyUniformValue>(name: string): T | undefined;
|
|
114
|
+
/**
|
|
115
|
+
* Set a texture for a sampler2D uniform.
|
|
116
|
+
* @example
|
|
117
|
+
* sandbox.setTexture("u_texture", imageElement);
|
|
118
|
+
* sandbox.setTexture("u_texture", imageElement, { wrap: "repeat" });
|
|
119
|
+
*/
|
|
120
|
+
setTexture(name: string, source: TextureSource, options?: TextureOptions): this;
|
|
121
|
+
/**
|
|
122
|
+
* Set multiple textures at once.
|
|
123
|
+
* @example
|
|
124
|
+
* sandbox.setTextures({
|
|
125
|
+
* u_texture: imageElement,
|
|
126
|
+
* u_detail: { source: detailImg, wrap: "repeat" },
|
|
127
|
+
* });
|
|
128
|
+
*/
|
|
129
|
+
setTextures(textures: TextureSchema): this;
|
|
130
|
+
/**
|
|
131
|
+
* Remove a texture and free its GPU resources.
|
|
132
|
+
* @example
|
|
133
|
+
* sandbox.removeTexture("u_texture");
|
|
134
|
+
*/
|
|
135
|
+
removeTexture(name: string): this;
|
|
114
136
|
/**
|
|
115
137
|
* Update shaders.
|
|
116
138
|
* @example
|
|
@@ -197,6 +219,40 @@ export declare class Sandbox {
|
|
|
197
219
|
* Get canvas element.
|
|
198
220
|
*/
|
|
199
221
|
get canvas(): HTMLCanvasElement;
|
|
222
|
+
/**
|
|
223
|
+
* Export current frame as a data URL string.
|
|
224
|
+
* Requires `preserveDrawingBuffer: true` if called while playing.
|
|
225
|
+
* @example
|
|
226
|
+
* const url = sandbox.renderAt(1.5).exportAsURL("image/png");
|
|
227
|
+
*/
|
|
228
|
+
exportAsURL(type?: "image/png" | "image/jpeg", quality?: number): string;
|
|
229
|
+
/**
|
|
230
|
+
* Export current frame as a Blob.
|
|
231
|
+
* Requires `preserveDrawingBuffer: true` if called while playing.
|
|
232
|
+
* @example
|
|
233
|
+
* const blob = await sandbox.renderAt(1.5).exportAsBlob("image/png");
|
|
234
|
+
*/
|
|
235
|
+
exportAsBlob(type?: "image/png" | "image/jpeg", quality?: number): Promise<Blob>;
|
|
236
|
+
/**
|
|
237
|
+
* Export current frame as an HTMLImageElement.
|
|
238
|
+
* Requires `preserveDrawingBuffer: true` if called while playing.
|
|
239
|
+
* @example
|
|
240
|
+
* const img = sandbox.renderAt(1.5).exportAsImage("image/png");
|
|
241
|
+
* img.onload = () => document.body.appendChild(img);
|
|
242
|
+
*/
|
|
243
|
+
exportAsImage(type?: "image/png" | "image/jpeg", quality?: number): HTMLImageElement;
|
|
244
|
+
/**
|
|
245
|
+
* Capture the canvas as a MediaStream for video calls or recording.
|
|
246
|
+
* @example
|
|
247
|
+
* // WebRTC video call
|
|
248
|
+
* const stream = sandbox.stream(30);
|
|
249
|
+
* peerConnection.addTrack(stream.getVideoTracks()[0], stream);
|
|
250
|
+
*
|
|
251
|
+
* @example
|
|
252
|
+
* // Record to video file
|
|
253
|
+
* const recorder = new MediaRecorder(sandbox.stream(30));
|
|
254
|
+
*/
|
|
255
|
+
stream(fps?: number): MediaStream;
|
|
200
256
|
/**
|
|
201
257
|
* Destroy sandbox and release all resources.
|
|
202
258
|
* @example
|