@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/README.md +171 -60
- package/dist/errors/base.d.ts +6 -0
- package/dist/errors/context.d.ts +7 -0
- package/dist/errors/index.d.ts +6 -0
- package/dist/errors/module.d.ts +42 -0
- package/dist/errors/program.d.ts +5 -0
- package/dist/errors/shader.d.ts +34 -0
- package/dist/errors/unknown.d.ts +7 -0
- package/dist/globals.d.ts +17 -0
- package/dist/index.cjs.js +834 -8
- package/dist/index.d.ts +56 -4
- package/dist/index.es.js +2323 -378
- package/dist/tools/clock.d.ts +7 -0
- package/dist/tools/compilable.d.ts +79 -0
- package/dist/tools/module.d.ts +46 -0
- package/dist/tools/module_registry.d.ts +63 -0
- package/dist/tools/parser.d.ts +32 -0
- package/dist/tools/program.d.ts +1 -12
- package/dist/tools/shader.d.ts +4 -0
- package/dist/tools/uniforms.d.ts +0 -2
- package/dist/tools/web_gl.d.ts +7 -1
- package/dist/types.d.ts +96 -1
- package/package.json +8 -5
- package/dist/errors.d.ts +0 -32
package/dist/index.cjs.js
CHANGED
|
@@ -1,8 +1,834 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var te=Object.defineProperty;var ie=(u,e,n)=>e in u?te(u,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):u[e]=n;var a=(u,e,n)=>ie(u,typeof e!="symbol"?e+"":e,n);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});class x{constructor(e,n,t,i){this.target=e,this.type=n,this.listener=t,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,n,t,i){return e.addEventListener(n,t,i),()=>e.removeEventListener(n,t,i)}}class h extends Error{constructor(n,t){super(n);a(this,"name","SandboxError");this.code=t}}class V extends h{constructor(){super("WebGL is not supported in this browser.","CONTEXT_ERROR")}}class re extends h{constructor(){super("Failed to create WebGL context. The GPU may be unavailable.","CONTEXT_ERROR")}}class $ extends h{constructor(e,n){super(`Vertex and fragment shader WebGL versions do not match (${e} vs ${n})`,"VALIDATION_ERROR"),this.vertexVersion=e,this.fragmentVersion=n}}class w extends h{constructor(n,t,i){const r=w.parseErrorLines(i),o=r.length>0?` at line(s): ${r.join(", ")}`:"";super(`${n} shader compilation failed${o}
|
|
2
2
|
|
|
3
|
-
${
|
|
3
|
+
${i}`,"SHADER_ERROR");a(this,"lines");this.shaderType=n,this.source=t,this.infoLog=i,this.lines=r}static parseErrorLines(n){const t=[/ERROR:\s*\d*:(\d+)/g,/(\d+):(\d+)\(\d+\):/g,/^(\d+):/gm],i=new Set;for(const r of t){let o;for(;(o=r.exec(n))!==null;){const s=parseInt(o[1],10);s>0&&i.add(s)}}return[...i].sort((r,o)=>r-o)}}class A extends h{constructor(e,n,t,i){super(`The shader ${e} "${n}" has type "${i}" but expected "${t}"`,"SHADER_ERROR"),this.requirement=e,this.name=n,this.expectedType=t,this.actualType=i}}class T extends h{constructor(){super("Shader source does not contain any function.","SHADER_ERROR")}}class I extends h{constructor(e,n){super(`Syntax error in shader import statement at line ${e}: ${n}`,"SHADER_ERROR"),this.line=e,this.details=n}}class P extends h{constructor(e,n){super(`Duplicate import name "${e}" found at line ${n}. Each import must have a unique name.`,"SHADER_ERROR"),this.name=e,this.line=n}}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 D extends h{constructor(e,n){super(`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.`,"MODULE_ERROR"),this.moduleName=e,this.methodName=n}}class N extends h{constructor(e){super(`Importing 'main' function from module '${e}' is forbidden.`,"MODULE_ERROR"),this.moduleName=e}}class z 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 G 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 W 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,n,t){super(`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.`,"MODULE_ERROR"),this.moduleName=e,this.functionName=n,this.uniformName=t}}class H extends h{constructor(e,n){super(`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.`,"MODULE_ERROR"),this.functionName=e,this.uniformName=n}}class j extends h{constructor(e,n){super(`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.`,"MODULE_ERROR"),this.mentionName=e,this.calledInFunction=n}}class E extends h{constructor(e){super(`Shader program linking failed
|
|
4
4
|
|
|
5
|
-
${t}`,"PROGRAM_LINK_FAILED"),this.infoLog=t,this.name="SandboxProgramError"}}class R{constructor(){i(this,"time",0);i(this,"delta",0);i(this,"frame",0);i(this,"running",!1);i(this,"startTime",0);i(this,"lastTime",0);i(this,"rafId",null);i(this,"callback",null);this.loop=this.loop.bind(this)}start(t){if(this.running)return this;this.callback=t,this.running=!0;const e=performance.now();return this.frame===0?this.startTime=e:this.startTime=e-this.time*1e3,this.lastTime=e,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}getState(){return{time:this.time,delta:this.delta,frame:this.frame}}tick(t=0){return this.delta=t,this.time+=t,this.frame++,this.callback&&this.callback(this.getState()),this}setTime(t){return this.time=t,this}destroy(){this.reset(),this.callback=null}loop(t){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))}}class p{constructor(t){i(this,"gl");i(this,"vao",null);i(this,"vbo",null);i(this,"ibo",null);i(this,"vertexCount",0);i(this,"indexCount",0);i(this,"useIndices",!1);i(this,"vaoExt",null);i(this,"isWebGL2");this.gl=t,this.isWebGL2=t instanceof WebGL2RenderingContext,this.isWebGL2||(this.vaoExt=t.getExtension("OES_vertex_array_object"))}static fullscreenQuad(t){const e=new p(t),r=new Float32Array([-1,-1,0,0,1,-1,1,0,-1,1,0,1,1,1,1,1]),s=new Uint16Array([0,1,2,2,1,3]);return e.setup(r,s),e}setup(t,e){const r=this.gl;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}linkAttributes(t){const e=this.gl;this.bindVAO(),e.bindBuffer(e.ARRAY_BUFFER,this.vbo);const r=4*Float32Array.BYTES_PER_ELEMENT,s=this.getPositionLocation(t);s>=0&&(e.enableVertexAttribArray(s),e.vertexAttribPointer(s,2,e.FLOAT,!1,r,0));const n=this.getTexcoordLocation(t);return n>=0&&(e.enableVertexAttribArray(n),e.vertexAttribPointer(n,2,e.FLOAT,!1,r,2*Float32Array.BYTES_PER_ELEMENT)),this.useIndices&&e.bindBuffer(e.ELEMENT_ARRAY_BUFFER,this.ibo),this.unbindVAO(),this}bind(){return this.bindVAO(),this}unbind(){return this.unbindVAO(),this}draw(){const t=this.gl;return this.bindVAO(),this.useIndices?t.drawElements(t.TRIANGLES,this.indexCount,t.UNSIGNED_SHORT,0):t.drawArrays(t.TRIANGLE_STRIP,0,this.vertexCount),this}destroy(){const t=this.gl;this.deleteVAO(),this.vbo&&(t.deleteBuffer(this.vbo),this.vbo=null),this.ibo&&(t.deleteBuffer(this.ibo),this.ibo=null)}getPositionLocation(t){let e=t.getAttribLocation("a_position");return e>=0||(e=t.getAttribLocation("aPosition"),e>=0)||(e=t.getAttribLocation("position"),e>=0)?e:-1}getTexcoordLocation(t){let e=t.getAttribLocation("a_texcoord");return e>=0||(e=t.getAttribLocation("aTexCoord"),e>=0)||(e=t.getAttribLocation("texcoord"),e>=0)||(e=t.getAttribLocation("a_uv"),e>=0)?e:-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 h{constructor(t){i(this,"gl");i(this,"program",null);i(this,"vertexShader",null);i(this,"fragmentShader",null);i(this,"version",1);this.gl=t}static detectVersion(t){return/^\s*#version\s+300\s+es/m.test(t)?2:1}compile(t,e){this.destroy();const r=h.detectVersion(t),s=h.detectVersion(e);if(r!=s)throw new L(r,s);return this.version=Math.max(r,s),this.vertexShader=this.compileShader("vertex",t),this.fragmentShader=this.compileShader("fragment",e),this.linkProgram(),this}use(){return this.program&&this.gl.useProgram(this.program),this}getProgram(){return this.program}getVersion(){return this.version}getAttribLocation(t){return this.program?this.gl.getAttribLocation(this.program,t):-1}getUniformLocation(t){return this.program?this.gl.getUniformLocation(this.program,t):null}destroy(){const t=this.gl;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)}compileShader(t,e){const r=this.gl,s=t==="vertex"?r.VERTEX_SHADER:r.FRAGMENT_SHADER,n=r.createShader(s);if(!n)throw new f(t,e,"Failed to create shader object");if(r.shaderSource(n,e),r.compileShader(n),!r.getShaderParameter(n,r.COMPILE_STATUS)){const c=r.getShaderInfoLog(n)||"Unknown error";throw r.deleteShader(n),new f(t,e,c)}return n}linkProgram(){const t=this.gl;if(!this.vertexShader||!this.fragmentShader)throw new d("Shaders not compiled");const e=t.createProgram();if(!e)throw new d("Failed to create program object");if(t.attachShader(e,this.vertexShader),t.attachShader(e,this.fragmentShader),t.linkProgram(e),!t.getProgramParameter(e,t.LINK_STATUS)){const s=t.getProgramInfoLog(e)||"Unknown error";throw t.deleteProgram(e),new d(s)}this.program=e}}class x{constructor(t,e){i(this,"name");i(this,"method");i(this,"isArray");i(this,"isMatrix");i(this,"location",null);i(this,"locationResolved",!1);i(this,"value");this.name=t,this.value=e;const r=x.inferMethodInfo(e);this.method=r.method,this.isArray=r.isArray,this.isMatrix=r.isMatrix}static inferMethodInfo(t){if(typeof t=="boolean")return{method:"uniform1i",isArray:!1,isMatrix:!1};if(typeof t=="number")return{method:"uniform1f",isArray:!1,isMatrix:!1};if(!Array.isArray(t))return{method:"uniform1f",isArray:!1,isMatrix:!1};const e=t.length,r=t[0];if(Array.isArray(r))switch(r.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(e){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(t,e){return this.locationResolved||(this.location=t.getUniformLocation(e,this.name),this.locationResolved=!0),this.location}invalidateLocation(){this.location=null,this.locationResolved=!1}setValue(t){this.value=t}getValue(){return this.value}upload(t,e){const r=this.resolveLocation(t,e);if(r===null)return;const s=this.value;let n;switch(typeof s=="boolean"?n=s?1:0:typeof s=="number"?n=s:this.isArray&&Array.isArray(s[0])?n=new Float32Array(s.flat()):n=new Float32Array(s),this.method){case"uniform1f":t.uniform1f(r,n);break;case"uniform1i":t.uniform1i(r,n);break;case"uniform1fv":t.uniform1fv(r,n);break;case"uniform2fv":t.uniform2fv(r,n);break;case"uniform3fv":t.uniform3fv(r,n);break;case"uniform4fv":t.uniform4fv(r,n);break;case"uniformMatrix2fv":t.uniformMatrix2fv(r,!1,n);break;case"uniformMatrix3fv":t.uniformMatrix3fv(r,!1,n);break;case"uniformMatrix4fv":t.uniformMatrix4fv(r,!1,n);break}}}const m=class m{constructor(t){i(this,"gl");i(this,"program",null);i(this,"uniforms",new Map);this.gl=t}attachProgram(t){this.program=t;for(const e of this.uniforms.values())e.invalidateLocation();return this}set(t,e){const r=this.uniforms.get(t);return r?r.setValue(e):this.uniforms.set(t,new x(t,e)),this}setMany(t){for(const[e,r]of Object.entries(t))this.set(e,r);return this}get(t){var e;return(e=this.uniforms.get(t))==null?void 0:e.getValue()}has(t){return this.uniforms.has(t)}delete(t){return this.uniforms.delete(t)}uploadAll(){if(!this.program)return this;for(const t of this.uniforms.values())t.upload(this.gl,this.program);return this}uploadBuiltIns(t,e,r){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)return this;for(const s of m.BUILT_INS){const n=this.uniforms.get(s);n&&n.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}};i(m,"BUILT_INS",new Set(["u_resolution","u_time","u_delta","u_mouse","u_frame"]));let v=m;class A{constructor(){i(this,"hooks",new Map)}id(){return Math.random().toString(36).substring(2,10)}add(t){const e=this.id();return this.hooks.set(e,t),()=>this.remove(e)}remove(t){this.hooks.delete(t)}run(t){for(const[e,r]of this.hooks)r(t)===!1&&this.remove(e)}destroy(){this.hooks.clear()}}class b{constructor(t,e){i(this,"canvas");i(this,"gl");i(this,"options");i(this,"onBeforeHooks",new A);i(this,"onAfterHooks",new A);i(this,"_program");i(this,"_geometry");i(this,"_uniforms");i(this,"_clock");i(this,"_resolution",[1,1]);i(this,"_mouse",[0,0]);i(this,"_version",1);i(this,"playing",!1);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)}static setup(t,e){const r=new b(t,e);return e.vertex&&e.fragment&&r.shader(e.vertex,e.fragment),e.uniforms&&r._uniforms.setMany(e.uniforms),r}initContext(){const t={antialias:this.options.antialias,preserveDrawingBuffer:this.options.preserveDrawingBuffer,alpha:!0,depth:!1,stencil:!1},e=this.canvas.getContext("webgl2",t);if(e)return this._version=2,e;const r=this.canvas.getContext("webgl",t);if(r)return this._version=1,r;const s=new S("not_supported");throw this.options.onError(s),s}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(t,e,r,s){return this.canvas.width=r,this.canvas.height=s,this.gl.viewport(t,e,r,s),this._resolution=[r,s],this}clock(t){return this._clock.setTime(t),this}mouse(t,e){return this._mouse=[t,e],this}uniform(t,e){return this._uniforms.set(t,e),this}uniforms(t){return this._uniforms.setMany(t),this}getUniform(t){return this._uniforms.get(t)}shader(t,e){try{this._program.compile(t,e),this._version=this._program.getVersion(),this._geometry.linkAttributes(this._program);const r=this._program.getProgram();r&&this._uniforms.attachProgram(r)}catch(r){r instanceof l&&this.options.onError(r)}return this}play(){return this.playing?this:(this.playing=!0,this._clock.start(this.onRender),this)}pause(){if(!this.playing)return this;const t=this._clock.getState();return this.onBeforeHooks.run(t),this.playing=!1,this._clock.stop(),this.onAfterHooks.run(t),this}render(){return this.onRender(this._clock.getState()),this}getContext(){return this.gl}getVersion(){return this._version}destroy(){this.pause(),this._clock.destroy(),this._geometry.destroy(),this._program.destroy(),this._uniforms.destroy(),this.onAfterHooks.destroy(),this.onBeforeHooks.destroy()}onRender(t){const e=this.gl;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)}}const g=`#ifdef GL_ES
|
|
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,n){super(`Error in onBefore/onAfter hook callback with ID ${e}: ${n}`,"UNKNOWN_ERROR")}}class oe{constructor(){a(this,"time",0);a(this,"delta",0);a(this,"frame",0);a(this,"running",!1);a(this,"fps",0);a(this,"startTime",0);a(this,"lastTime",0);a(this,"rafId",null);a(this,"callback",null);a(this,"maxFps",0);this.loop=this.loop.bind(this)}start(e){if(this.running)return this;this.callback=e,this.running=!0;const n=performance.now();return this.frame===0?this.startTime=n:this.startTime=n-this.time*1e3,this.lastTime=n,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 t=1e3/this.maxFps;if(e-this.lastTime<t){this.rafId=requestAnimationFrame(this.loop);return}}this.delta=(e-this.lastTime)/1e3,this.lastTime=e;const n=this.delta>0?1/this.delta:0;this.fps=this.fps*.95+n*.05,this.time=(e-this.startTime)/1e3,this.frame++,this.callback&&this.callback(this.getState()),this.rafId=requestAnimationFrame(this.loop)}}class k{constructor(e){a(this,"gl");a(this,"vao",null);a(this,"vbo",null);a(this,"ibo",null);a(this,"vertexCount",0);a(this,"indexCount",0);a(this,"useIndices",!1);a(this,"vaoExt",null);a(this,"isWebGL2");this.gl=e,this.isWebGL2=e instanceof WebGL2RenderingContext,this.isWebGL2||(this.vaoExt=e.getExtension("OES_vertex_array_object"))}static fullscreenQuad(e){const n=new k(e),t=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 n.setup(t,i),n}setup(e,n){const t=this.gl;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}linkAttributes(e){const n=this.gl;this.bindVAO(),n.bindBuffer(n.ARRAY_BUFFER,this.vbo);const t=4*Float32Array.BYTES_PER_ELEMENT,i=this.getPositionLocation(e);i>=0&&(n.enableVertexAttribArray(i),n.vertexAttribPointer(i,2,n.FLOAT,!1,t,0));const r=this.getTexcoordLocation(e);return r>=0&&(n.enableVertexAttribArray(r),n.vertexAttribPointer(r,2,n.FLOAT,!1,t,2*Float32Array.BYTES_PER_ELEMENT)),this.useIndices&&n.bindBuffer(n.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 n=e.getAttribLocation("a_position");return n>=0||(n=e.getAttribLocation("aPosition"),n>=0)||(n=e.getAttribLocation("position"),n>=0)?n:-1}getTexcoordLocation(e){let n=e.getAttribLocation("a_texcoord");return n>=0||(n=e.getAttribLocation("aTexCoord"),n>=0)||(n=e.getAttribLocation("texcoord"),n>=0)||(n=e.getAttribLocation("a_uv"),n>=0)?n:-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 se{constructor(e){a(this,"gl");a(this,"program",null);a(this,"vertexShader",null);a(this,"fragmentShader",null);this.gl=e}compile(e,n){return this.destroy(),this.vertexShader=this.compileShader("vertex",e),this.fragmentShader=this.compileShader("fragment",n),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,n){const t=this.gl,i=e==="vertex"?t.VERTEX_SHADER:t.FRAGMENT_SHADER,r=t.createShader(i);if(!r)throw new w(e,n,"Failed to create shader object");if(t.shaderSource(r,n),t.compileShader(r),!t.getShaderParameter(r,t.COMPILE_STATUS)){const s=t.getShaderInfoLog(r)||"Unknown error";throw t.deleteShader(r),new w(e,n,s)}return r}linkProgram(){const e=this.gl;if(!this.vertexShader||!this.fragmentShader)throw new E("Shaders not compiled");const n=e.createProgram();if(!n)throw new E("Failed to create program object");if(e.attachShader(n,this.vertexShader),e.attachShader(n,this.fragmentShader),e.linkProgram(n),!e.getProgramParameter(n,e.LINK_STATUS)){const i=e.getProgramInfoLog(n)||"Unknown error";throw e.deleteProgram(n),new E(i)}this.program=n}}class M{constructor(e,n){a(this,"name");a(this,"method");a(this,"isArray");a(this,"isMatrix");a(this,"location",null);a(this,"locationResolved",!1);a(this,"value");this.name=e,this.value=n;const t=M.inferMethodInfo(n);this.method=t.method,this.isArray=t.isArray,this.isMatrix=t.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 n=e.length,t=e[0];if(Array.isArray(t))switch(t.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(n){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,n){return this.locationResolved||(this.location=e.getUniformLocation(n,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,n){const t=this.resolveLocation(e,n);if(t===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(t,r);break;case"uniform1i":e.uniform1i(t,r);break;case"uniform1fv":e.uniform1fv(t,r);break;case"uniform2fv":e.uniform2fv(t,r);break;case"uniform3fv":e.uniform3fv(t,r);break;case"uniform4fv":e.uniform4fv(t,r);break;case"uniformMatrix2fv":e.uniformMatrix2fv(t,!1,r);break;case"uniformMatrix3fv":e.uniformMatrix3fv(t,!1,r);break;case"uniformMatrix4fv":e.uniformMatrix4fv(t,!1,r);break}}}class _{constructor(e){a(this,"parsed",null);this.source=e}parse(){if(this.parsed)return this.parsed;const e=this.detectVersion(),n=this.detectImports(),t=this.detectUniforms(),i=this.detectFunctions(t);return this.parsed={version:e,imports:n,uniforms:t,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,n=/^[ \t]*[^\w\s]?import\b/gm,t=[],i=new Set;let r,o=1,s=0;for(;(r=e.exec(this.source))!==null;){o+=(this.source.substring(s,r.index).match(/\n/g)||[]).length,s=r.index,i.add(o);const f=r[1],c=r[2]||r[1],d=r[3];if(t.some(p=>p.alias===c))throw new P(c,o);t.push({name:f,alias:c,module:d,line:o})}let l;for(;(l=n.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 I(f,this.diagnoseImport(c))}return t}diagnoseImport(e){const n=e.match(/^([^\w\s])import\b/);if(n&&n[1]!=="#")return`Invalid prefix '${n[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 t=e.split(/\s+/);return`Missing 'from' clause. Expected: #import ${t[1]} as ${t[3]} from '<module>'`}if(/^#import\s+\w+(?:\s+as\s+\w+)?\s+from\s+\w+/.test(e)){const t=e.match(/from\s+(\S+)/);return`Module name must be quoted. Expected: from '${t==null?void 0:t[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,n=[];let t,i=1,r=0;for(;(t=e.exec(this.source))!==null;){i+=(this.source.substring(r,t.index).match(/\n/g)||[]).length,r=t.index;const o=t[1],s=t[2],l=t[3]?parseInt(t[3],10):void 0;n.push({name:s,type:o,line:i,arrayNum:l})}return n}detectFunctions(e){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(`^[ \\t]*(${t})\\s+(\\w+)\\s*\\(([^)]*)\\)\\s*\\{`,"gm");let r;for(;(r=i.exec(this.source))!==null;){const o=r[1],s=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),J=this.parseParams(l),Z=this.findFunctionCalls(g),ee=this.findUniformCalls(g,e),ne=this.findMentionCalls(g);n.push({name:s,type:o,params:J,body:g,dependencies:[...Z,...ee,...ne],line:c})}return n}parseParams(e){if(!e.trim())return[];const n=[],t=e.split(",");for(const i of t){const r=i.trim();if(!r)continue;const s=r.replace(/\b(in|out|inout|const|highp|mediump|lowp)\b\s*/g,"").trim().match(/^(\w+)\s+(\w+)(?:\[\d*\])?$/);s&&n.push({type:s[1],name:s[2]})}return n}findClosingBrace(e,n){let t=0,i=!1,r=!1,o=!1,s=!1;for(let l=n;l<e.length;l++){const f=e[l],c=e[l+1],d=e[l-1];if(!r&&!s&&f==="/"&&c==="/"){o=!0;continue}if(o&&f===`
|
|
7
|
+
`){o=!1;continue}if(!r&&!o&&f==="/"&&c==="*"){s=!0,l++;continue}if(s&&f==="*"&&c==="/"){s=!1,l++;continue}if(!(o||s)){if(f==='"'&&d!=="\\"){r=!r;continue}if(!r){if(f==="{")t++,i=!0;else if(f==="}"&&(t--,i&&t===0))return l}}}return-1}findFunctionCalls(e){const n=[],t=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 o=r[1];t.has(o)||n.push({name:o,type:"function",index:r.index})}return n}findUniformCalls(e,n){const t=[];for(const i of n){const r=new RegExp(`\\b${i.name}\\b`,"g");let o;for(;(o=r.exec(e))!==null;)t.push({name:i.name,type:"uniform",index:o.index})}return t}findMentionCalls(e){const n=[],t=/@(\w+)\.([a-zA-Z_]\w*)/g;let i;for(;(i=t.exec(e))!==null;){const r=i[1],o=i[2];n.push({name:`${r}.${o}`,type:"mention",index:i.index})}return n}}class X{constructor(e){a(this,"isCompiled",!1);a(this,"original");a(this,"compiled");a(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(),n=e.functions.flatMap(t=>t.dependencies.filter(i=>i.type==="mention").map(i=>({name:i.name.split(".")[0],uniform:i.name.split(".")[1]})));for(const t of e.imports){const i=b.resolve(t.module),r=i.extract(t.name);let o=n.filter(l=>l.name===r.function.name);if(o.length>0){const l=i.getDefinition().uniforms;if(o=o.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=n.indexOf(f);return d>-1&&n.splice(d,1),!1}return!0}),o.length>0)throw new q(t.module,r.function.name,o[0].uniform)}const s=i.copy();this.processExtraction(r,t.alias,s.options),y.merge(t.module,s)}if(n.length>0)throw new H(n[0].name,n[0].uniform)}processExtraction(e,n,t={}){const i=e.function,r=Math.random().toString(36).substring(2,8),o=`${n}_${r}`;for(let l=e.dependencies.functions.length-1;l>=0;l--){const f=e.dependencies.functions[l],c=this.rewriteFunction(f,n,{uniforms:e.dependencies.uniforms,functions:e.dependencies.functions,unique:o});this.requirements.functions.set(c.name,c)}const s=this.rewriteFunction(i,n,{uniforms:e.dependencies.uniforms,functions:e.dependencies.functions,rename:!0,unique:o});this.requirements.functions.set(s.name,s);for(const l of e.dependencies.uniforms){if(S.has(l.name))continue;const f={...l,name:`${o}_${l.name}${l.arrayNum?`[${l.arrayNum}]`:""}`};if(t[i.name]){const c=Object.entries(t[i.name]).find(([d,p])=>p.uniform===l.name);c&&(c[1].uniform=`${o}_${l.name}`)}this.requirements.uniforms.set(f.name,f)}t[i.name]&&n!==i.name&&(t[n]=t[i.name],delete t[i.name])}rewriteFunction(e,n,t={rename:!1,uniforms:[],functions:[],unique:""}){const i=new Set(t.uniforms.map(c=>c.name)),r=new Set(t.functions.map(c=>c.name)),o=[],s=t.unique?t.unique:n;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;o.push({index:c.index,oldText:c.name,newText:`${s}_${c.name}`})}else c.type==="function"&&r.has(c.name)&&o.push({index:c.index,oldText:c.name,newText:`${s}_${c.name}`});const l=this.applyRewrites(e.body,o),f=t.rename?n:`${s}_${e.name}`;return{...e,name:f,body:l}}applyRewrites(e,n){const t=[...n].sort((r,o)=>o.index-r.index);let i=e;for(const r of t)i=i.slice(0,r.index)+r.newText+i.slice(r.index+r.oldText.length);return i}build(){const e=this.original.parse();let n=this.original.source;n=this.removeImportLines(n,e);const t=this.findInsertionPointForUniforms(n),i=this.generateUniformsCode();i&&(n=n.slice(0,t)+i+`
|
|
8
|
+
`+n.slice(t));const r=this.findInsertionPointForFunctions(n),o=this.generateFunctionCode();return o&&(n=n.slice(0,r)+o+n.slice(r)),n=n.replace(/\n{3,}/g,`
|
|
9
|
+
|
|
10
|
+
`),n=this.replaceMentions(n,e),n}replaceMentions(e,n){let t=e;const i=n.functions.filter(r=>r.dependencies.some(o=>o.type==="mention"));for(const r of i){const o=r.dependencies.filter(s=>s.type==="mention");for(const s of o){const l=s.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 j(s.name,r.name);const d=new RegExp(`@\\b${s.name}\\b`,"g");t=t.replace(d,c)}}return t}removeImportLines(e,n){const t=e.split(`
|
|
11
|
+
`),i=new Set(n.imports.map(r=>r.line));return t.filter((r,o)=>{const s=i.has(o+1);if(!s&&r.trim()===""&&o>0){const l=o-1;if(i.has(l+1))return!1}return!s}).join(`
|
|
12
|
+
`)}findInsertionPointForUniforms(e){const n=new _(e).parse(),t=n.uniforms.find(s=>s.line===Math.max(...n.uniforms.map(l=>l.line??0))),i=e.split(`
|
|
13
|
+
`);let r=0;if(t&&t.line)r=t.line;else for(let s=0;s<i.length;s++){const l=i[s].trim();if(l.startsWith("#version")){r=s+1;continue}if(l.startsWith("precision ")){r=s+1;continue}if(l===""||l.startsWith("//")){r===s&&(r=s+1);continue}break}let o=0;for(let s=0;s<r;s++)o+=i[s].length+1;return o}findInsertionPointForFunctions(e){const n=new _(e).parse(),t=n.functions.find(s=>s.line===Math.min(...n.functions.map(l=>l.line??1/0))),i=e.split(`
|
|
14
|
+
`);let r=0;if(t&&t.line)r=t.line-2;else throw new T;let o=0;for(let s=0;s<r;s++)o+=i[s].length+1;return o}generateUniformsCode(){const e=[];if(this.requirements.uniforms.size>0)for(const n of this.checkUniformsPresence())e.push(`uniform ${n.type} ${n.name};`);return e.length===0?"":e.join(`
|
|
15
|
+
`)+`
|
|
16
|
+
`}generateFunctionCode(){const e=[];if(this.requirements.functions.size>0)for(const n of this.checkFunctionsPresence()){const t=n.params.map(i=>`${i.type} ${i.name}`).join(", ");e.push(`
|
|
17
|
+
${n.type} ${n.name}(${t}) ${n.body}`)}return e.length===0?"":e.join(`
|
|
18
|
+
`)+`
|
|
19
|
+
`}checkUniformsPresence(){const e=this.original.parse(),n=[],t=this.requirements.uniforms;for(const[i,r]of t){const o=e.uniforms.find(s=>s.name===i);if(!o){n.push(r);continue}if(o.type!==r.type)throw new A("uniform",i,r.type,o.type)}return n}checkFunctionsPresence(){const e=this.original.parse(),n=[],t=this.requirements.functions;for(const[i,r]of t){const o=e.functions.find(s=>s.name===i);if(!o){n.push(r);continue}if(o.type!==r.type)throw new A("function",i,r.type,o.type)}return n}}class v extends X{constructor(n,t,i={}){super(t);a(this,"name");a(this,"options",{});this.name=n,this.options=this.resolveOptions(i)}resolveOptions(n){if(!(n!=null&&n.default))return n||{};const t=this.original.parse(),i=n.default;for(const r of t.functions)if(!(r.name==="main"||r.name==="default"))if(n[r.name]){const o=n[r.name];for(const s in i)s in o||(o[s]=i[s])}else n[r.name]={...i};return delete n.default,n||{}}static define(n){const{name:t,source:i,options:r}=n;if(t==="sandbox"||t.startsWith("sandbox/"))throw new G(t);const o=new v(t,i,r);if(b.has(t))throw new W(t);return b.register(t,o),o}static resolve(n){return b.resolve(n)}copy(n="original"){return new v(this.name,this[n].source,JSON.parse(JSON.stringify(this.options)))}merge(n){this.options=this.options||{};const t=this.getDefinition().uniforms.map(i=>i.name);for(const[i,r]of Object.entries(n.options??{}))if(!this.options[i])this.options[i]=r;else for(const[o,s]of Object.entries(r))t.includes(s.uniform)||(this.options[i][o]=s)}getDefinition(){return this.compile(),{name:this.name,methods:this.compiled.parse().functions.map(n=>n.name).filter(n=>n!=="main"&&n!=="default"),uniforms:this.compiled.parse().uniforms.map(n=>({name:n.name,type:n.type})),options:this.options}}extract(n){if(this.compile(),n==="main")throw new N(this.name);if(n==="default")throw new z(this.name);const t=this.compiled.parse(),i=t.functions.find(s=>s.name===n);if(!i)throw new D(this.name,n);const r=new Map,o=new Map;return this.collectDependencies({current:i,scope:{functions:t.functions,uniforms:t.uniforms},collected:{functions:r,uniforms:o},visited:new Set([n])}),{function:i,dependencies:{functions:Array.from(r.values()),uniforms:Array.from(o.values())}}}collectDependencies(n){for(const t of n.current.dependencies)if(t.type==="function"){if(n.visited.has(t.name))continue;const i=n.scope.functions.find(r=>r.name===t.name);i&&(n.visited.add(t.name),n.collected.functions.set(t.name,i),this.collectDependencies({current:i,scope:{functions:n.scope.functions,uniforms:n.scope.uniforms},collected:{functions:n.collected.functions,uniforms:n.collected.uniforms},visited:n.visited}))}else if(t.type==="uniform"){const i=n.scope.uniforms.find(r=>r.name===t.name);i&&!n.collected.uniforms.has(t.name)&&n.collected.uniforms.set(t.name,i)}}}class Q{constructor(e=[]){a(this,"modules",new Map);e.forEach(n=>{this.register(n.name,n)})}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(n=>{const t=n.getDefinition();if(t.options)for(const i in t.options){const r=t.options[i];for(const o in r){const s=r[o];s.default!==void 0&&!t.uniforms.map(l=>l.name).includes(s.uniform)&&(e[s.uniform]=s.default)}}}),e}resolveOptions(e){for(const n of this.modules.values())if(n.options&&n.options[e])return n.options[e];return null}register(e,n){this.modules.set(e,n)}merge(e,n){if(!this.modules.has(e))return this.register(e,n);const t=this.modules.get(e);t.merge(n),this.modules.set(e,t)}resolve(e){const n=this.modules.get(e);if(!n)throw new B(e);return n}has(e){return this.modules.has(e)}isEmpty(){return this.modules.size===0}remove(e){this.modules.delete(e)}load(e){e.forEach(n=>{this.register(n.name,n)})}clear(){this.modules.clear()}}const ae=`// ─── Constants ──────────────────────────────────────────────
|
|
20
|
+
|
|
21
|
+
const float PI = 3.14159265359;
|
|
22
|
+
const float TAU = 6.28318530718;
|
|
23
|
+
|
|
24
|
+
// ─── UV Transforms ──────────────────────────────────────────
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Center UV coordinates — remap to range where (0,0) is canvas center.
|
|
28
|
+
* Aspect-ratio corrected using resolution length.
|
|
29
|
+
* @uv-modifier
|
|
30
|
+
*/
|
|
31
|
+
vec2 center(vec2 uv) {
|
|
32
|
+
return (uv - 0.5 * u_resolution) / length(u_resolution);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Translate UV coordinates by offset.
|
|
37
|
+
* @uv-modifier
|
|
38
|
+
*/
|
|
39
|
+
vec2 translate(vec2 uv, vec2 offset) {
|
|
40
|
+
return uv - offset;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Scale UV coordinates by factor around origin.
|
|
45
|
+
* @uv-modifier
|
|
46
|
+
*/
|
|
47
|
+
vec2 scale(vec2 uv, float factor) {
|
|
48
|
+
return uv * factor;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Zoom — scale UV with aspect ratio correction.
|
|
53
|
+
* Expects centered UV (use center() first).
|
|
54
|
+
* Pushes UV outward/inward from origin.
|
|
55
|
+
* factor = zoom factor (>1 = zoom in, <1 = zoom out)
|
|
56
|
+
* @uv-modifier
|
|
57
|
+
*/
|
|
58
|
+
vec2 zoom(vec2 uv, float factor) {
|
|
59
|
+
return uv / max(factor, 0.001);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Normalize UV to range -1 to 1 with aspect ratio correction.
|
|
64
|
+
* @uv-modifier
|
|
65
|
+
*/
|
|
66
|
+
vec2 norm(vec2 uv) {
|
|
67
|
+
uv *= min(u_resolution.x, u_resolution.y) / length(u_resolution);
|
|
68
|
+
return uv;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Rotate UV coordinates around origin by angle (radians).
|
|
73
|
+
* @uv-modifier
|
|
74
|
+
*/
|
|
75
|
+
vec2 rotate(vec2 uv, float angle) {
|
|
76
|
+
float c = cos(angle);
|
|
77
|
+
float s = sin(angle);
|
|
78
|
+
return vec2(uv.x * c - uv.y * s, uv.x * s + uv.y * c);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Tile — repeat coordinates in a grid of given size.
|
|
83
|
+
* @uv-modifier
|
|
84
|
+
*/
|
|
85
|
+
vec2 tile(vec2 uv, float size) {
|
|
86
|
+
return fract(uv / size) * size;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Cartesian to polar coordinates.
|
|
91
|
+
* Returns vec2(angle, radius) where angle is -PI to PI.
|
|
92
|
+
*/
|
|
93
|
+
vec2 polar(vec2 uv) {
|
|
94
|
+
return vec2(atan(uv.y, uv.x), length(uv));
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Aspect ratio correction — makes UV square regardless of canvas shape.
|
|
99
|
+
* @uv-modifier
|
|
100
|
+
*/
|
|
101
|
+
vec2 aspect(vec2 uv) {
|
|
102
|
+
return uv * vec2(u_resolution.x / u_resolution.y, 1.0);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// ─── Math Utilities ─────────────────────────────────────────
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Remap a value from one range to another.
|
|
109
|
+
* map(0.5, 0.0, 1.0, -1.0, 1.0) → 0.0
|
|
110
|
+
*/
|
|
111
|
+
float map(float value, float inMin, float inMax, float outMin, float outMax) {
|
|
112
|
+
return outMin + (value - inMin) * (outMax - outMin) / (inMax - inMin);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// ─── Noise & Random ─────────────────────────────────────────
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Pseudo-random hash from 2D coordinates.
|
|
119
|
+
* Returns float in 0–1 range.
|
|
120
|
+
*/
|
|
121
|
+
float hash(vec2 p) {
|
|
122
|
+
return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453123);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* 2D pseudo-random hash.
|
|
127
|
+
* Returns vec2 in 0–1 range — used for point scattering (worley etc).
|
|
128
|
+
*/
|
|
129
|
+
vec2 hash2(vec2 p) {
|
|
130
|
+
p = vec2(dot(p, vec2(127.1, 311.7)), dot(p, vec2(269.5, 183.3)));
|
|
131
|
+
return fract(sin(p) * 43758.5453123);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Smooth value noise — interpolated hash grid.
|
|
136
|
+
*/
|
|
137
|
+
float noise(vec2 p) {
|
|
138
|
+
vec2 i = floor(p);
|
|
139
|
+
vec2 f = fract(p);
|
|
140
|
+
vec2 u = f * f * (3.0 - 2.0 * f);
|
|
141
|
+
|
|
142
|
+
return mix(
|
|
143
|
+
mix(hash(i), hash(i + vec2(1.0, 0.0)), u.x),
|
|
144
|
+
mix(hash(i + vec2(0.0, 1.0)), hash(i + vec2(1.0, 1.0)), u.x),
|
|
145
|
+
u.y
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Fractal Brownian Motion — 6 octaves of layered noise.
|
|
151
|
+
* Returns ~0–1 range. Great for clouds, terrain, organic textures.
|
|
152
|
+
*/
|
|
153
|
+
float fbm(vec2 p) {
|
|
154
|
+
float value = 0.0;
|
|
155
|
+
float amplitude = 0.5;
|
|
156
|
+
|
|
157
|
+
for (int i = 0; i < 6; i++) {
|
|
158
|
+
value += amplitude * noise(p);
|
|
159
|
+
p *= 2.0;
|
|
160
|
+
amplitude *= 0.5;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return value;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Worley (cellular / voronoi) noise.
|
|
168
|
+
* Returns distance to nearest random cell point.
|
|
169
|
+
* Great for cells, cracks, organic patterns.
|
|
170
|
+
*/
|
|
171
|
+
float worley(vec2 p) {
|
|
172
|
+
vec2 i = floor(p);
|
|
173
|
+
vec2 f = fract(p);
|
|
174
|
+
float minDist = 1.0;
|
|
175
|
+
|
|
176
|
+
for (int y = -1; y <= 1; y++) {
|
|
177
|
+
for (int x = -1; x <= 1; x++) {
|
|
178
|
+
vec2 neighbor = vec2(float(x), float(y));
|
|
179
|
+
vec2 point = hash2(i + neighbor);
|
|
180
|
+
float dist = length(neighbor + point - f);
|
|
181
|
+
minDist = min(minDist, dist);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return minDist;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Waves — layered sine wave interference pattern.
|
|
190
|
+
* Creates clean, regular wave lines like fabric or moiré.
|
|
191
|
+
* frequency = wave density (5.0 = fabric, 10.0 = fine lines)
|
|
192
|
+
* Returns 0–1 range.
|
|
193
|
+
*/
|
|
194
|
+
float waves(vec2 p, float frequency) {
|
|
195
|
+
return 0.6 + 0.4 * sin(
|
|
196
|
+
frequency * (p.x + p.y + cos(3.0 * p.x + 5.0 * p.y)) +
|
|
197
|
+
sin(frequency * 4.0 * (p.x + p.y))
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Voronoi cell lookup — returns the position of the nearest cell point.
|
|
203
|
+
* Same algorithm as worley, but returns the point instead of distance.
|
|
204
|
+
* Great for UV snapping, cell-based effects.
|
|
205
|
+
*/
|
|
206
|
+
vec2 voronoi(vec2 p) {
|
|
207
|
+
vec2 i = floor(p);
|
|
208
|
+
vec2 f = fract(p);
|
|
209
|
+
float minDist = 10.0;
|
|
210
|
+
vec2 nearest = vec2(0.0);
|
|
211
|
+
|
|
212
|
+
for (int y = -1; y <= 1; y++) {
|
|
213
|
+
for (int x = -1; x <= 1; x++) {
|
|
214
|
+
vec2 neighbor = vec2(float(x), float(y));
|
|
215
|
+
vec2 point = hash2(i + neighbor);
|
|
216
|
+
float dist = length(neighbor + point - f);
|
|
217
|
+
if (dist < minDist) {
|
|
218
|
+
minDist = dist;
|
|
219
|
+
nearest = i + neighbor + point;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return nearest;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
void main() {}
|
|
228
|
+
`,le=`/**
|
|
229
|
+
* Hex integer to RGB.
|
|
230
|
+
* Usage: hex(0xFF6600) → orange
|
|
231
|
+
*/
|
|
232
|
+
vec3 hex(int value) {
|
|
233
|
+
float v = float(value);
|
|
234
|
+
float r = floor(v / 65536.0);
|
|
235
|
+
float g = floor((v - r * 65536.0) / 256.0);
|
|
236
|
+
float b = v - r * 65536.0 - g * 256.0;
|
|
237
|
+
return vec3(r, g, b) / 255.0;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* RGB 0–255 to normalized RGB 0–1.
|
|
242
|
+
* Usage: rgb255(255.0, 128.0, 0.0) → orange
|
|
243
|
+
*/
|
|
244
|
+
vec3 rgb255(float r, float g, float b) {
|
|
245
|
+
return vec3(r, g, b) / 255.0;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* HSV to RGB.
|
|
250
|
+
* Input: vec3(hue 0–1, saturation 0–1, value 0–1)
|
|
251
|
+
*/
|
|
252
|
+
vec3 hsv(vec3 c) {
|
|
253
|
+
vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
|
|
254
|
+
vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
|
|
255
|
+
return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* HSL to RGB.
|
|
260
|
+
* Input: vec3(hue 0–1, saturation 0–1, lightness 0–1)
|
|
261
|
+
*/
|
|
262
|
+
vec3 hsl(vec3 c) {
|
|
263
|
+
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);
|
|
264
|
+
return c.z + c.y * (rgb - 0.5) * (1.0 - abs(2.0 * c.z - 1.0));
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Linear gradient between two colors.
|
|
269
|
+
* t is clamped to 0–1.
|
|
270
|
+
*/
|
|
271
|
+
vec3 gradient(vec3 a, vec3 b, float t) {
|
|
272
|
+
return mix(a, b, clamp(t, 0.0, 1.0));
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* 3-stop gradient.
|
|
277
|
+
* t=0 → a, t=0.5 → b, t=1 → c
|
|
278
|
+
*/
|
|
279
|
+
vec3 gradient3(vec3 a, vec3 b, vec3 c, float t) {
|
|
280
|
+
t = clamp(t, 0.0, 1.0);
|
|
281
|
+
return t < 0.5
|
|
282
|
+
? mix(a, b, t * 2.0)
|
|
283
|
+
: mix(b, c, (t - 0.5) * 2.0);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Cosine palette — Inigo Quilez formula.
|
|
288
|
+
* color = a + b * cos(2π(c·t + d))
|
|
289
|
+
* Generates infinite smooth color ramps from 4 vec3 params.
|
|
290
|
+
*/
|
|
291
|
+
vec3 palette(vec3 a, vec3 b, vec3 c, vec3 d, float t) {
|
|
292
|
+
return a + b * cos(6.28318 * (c * t + d));
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Banded gradient — 3-zone color mapping with sharp transitions.
|
|
297
|
+
* Uses tent functions instead of linear interpolation.
|
|
298
|
+
* t=0 → b (center), t=1 → a (middle), t=2 → c (outer).
|
|
299
|
+
* sharpness = transition width (2.0 = sharp, 1.0 = soft).
|
|
300
|
+
*/
|
|
301
|
+
vec3 bands(vec3 a, vec3 b, vec3 c, float t, float sharpness) {
|
|
302
|
+
float w1 = max(0.0, 1.0 - sharpness * abs(1.0 - t));
|
|
303
|
+
float w2 = max(0.0, 1.0 - sharpness * abs(t));
|
|
304
|
+
float w3 = 1.0 - min(1.0, w1 + w2);
|
|
305
|
+
return a * w1 + b * w2 + c * w3;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Iridescent — iterative interference color pattern.
|
|
310
|
+
* Generates rainbow-like colors from UV through trigonometric iteration.
|
|
311
|
+
* Creates shimmering, oil-slick-like color fields.
|
|
312
|
+
* time = animation time (pass u_time), speed = animation speed
|
|
313
|
+
*/
|
|
314
|
+
vec3 iridescent(vec2 uv, float time, float speed) {
|
|
315
|
+
float d = -time * 0.5 * speed;
|
|
316
|
+
float a = 0.0;
|
|
317
|
+
for (int i = 0; i < 8; i++) {
|
|
318
|
+
a += cos(float(i) - d - a * uv.x);
|
|
319
|
+
d += sin(uv.y * float(i) + a);
|
|
320
|
+
}
|
|
321
|
+
d += time * 0.5 * speed;
|
|
322
|
+
vec3 col = vec3(cos(uv * vec2(d, a)) * 0.6 + 0.4, cos(a + d) * 0.5 + 0.5);
|
|
323
|
+
return cos(col * cos(vec3(d, a, 2.5)) * 0.5 + 0.5);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
void main() {}
|
|
327
|
+
`,ce=`// ─── Time Shapers ──────────────────────────────────────────
|
|
328
|
+
// Convert raw time (u_time) to normalized 0–1 range.
|
|
329
|
+
// Output feeds directly into easing functions.
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Loop — repeating sawtooth 0→1→0→1...
|
|
333
|
+
* duration = cycle length in seconds
|
|
334
|
+
*/
|
|
335
|
+
float loop(float t, float duration) {
|
|
336
|
+
return fract(t / duration);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Pingpong — triangle wave 0→1→0→1...
|
|
341
|
+
* duration = half-cycle length (0→1 takes \`duration\` seconds)
|
|
342
|
+
*/
|
|
343
|
+
float pingpong(float t, float duration) {
|
|
344
|
+
float m = mod(t, duration * 2.0);
|
|
345
|
+
return m < duration ? m / duration : 2.0 - m / duration;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Once — single play 0→1, then stays at 1.
|
|
350
|
+
* duration = animation length in seconds
|
|
351
|
+
* Use with delay: once(u_time - 2.0, 3.0) starts at 2s, takes 3s.
|
|
352
|
+
*/
|
|
353
|
+
float once(float t, float duration) {
|
|
354
|
+
return clamp(t / duration, 0.0, 1.0);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Reverse — flip animation direction.
|
|
359
|
+
* Turns 0→1 into 1→0. Works with any easing or shaper.
|
|
360
|
+
* Usage: reverse(spring(loop(u_time, 2.0))) for spring zoom-in.
|
|
361
|
+
*/
|
|
362
|
+
float reverse(float t) {
|
|
363
|
+
return 1.0 - t;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// ─── Easing Curves ─────────────────────────────────────────
|
|
367
|
+
// All take t (0–1) and return shaped t (0–1).
|
|
368
|
+
// Compose with time shapers: ease_in(loop(u_time, 2.0))
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* Ease in — cubic acceleration. Slow start, fast end.
|
|
372
|
+
*/
|
|
373
|
+
float ease_in(float t) {
|
|
374
|
+
return t * t * t;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* Ease out — cubic deceleration. Fast start, slow end.
|
|
379
|
+
*/
|
|
380
|
+
float ease_out(float t) {
|
|
381
|
+
float f = 1.0 - t;
|
|
382
|
+
return 1.0 - f * f * f;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Ease in-out — cubic smooth both ends.
|
|
387
|
+
*/
|
|
388
|
+
float ease_in_out(float t) {
|
|
389
|
+
return t < 0.5
|
|
390
|
+
? 4.0 * t * t * t
|
|
391
|
+
: 1.0 - pow(-2.0 * t + 2.0, 3.0) * 0.5;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* Spring — damped oscillation. Overshoots then settles at 1.
|
|
396
|
+
*/
|
|
397
|
+
float spring(float t) {
|
|
398
|
+
return 1.0 - exp(-6.0 * t) * cos(12.0 * t);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Bounce — bouncing ball landing. Multiple bounces settling at 1.
|
|
403
|
+
*/
|
|
404
|
+
float bounce(float t) {
|
|
405
|
+
if (t < 1.0 / 2.75) {
|
|
406
|
+
return 7.5625 * t * t;
|
|
407
|
+
} else if (t < 2.0 / 2.75) {
|
|
408
|
+
t -= 1.5 / 2.75;
|
|
409
|
+
return 7.5625 * t * t + 0.75;
|
|
410
|
+
} else if (t < 2.5 / 2.75) {
|
|
411
|
+
t -= 2.25 / 2.75;
|
|
412
|
+
return 7.5625 * t * t + 0.9375;
|
|
413
|
+
} else {
|
|
414
|
+
t -= 2.625 / 2.75;
|
|
415
|
+
return 7.5625 * t * t + 0.984375;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* Elastic — springy overshoot with oscillation.
|
|
421
|
+
*/
|
|
422
|
+
float elastic(float t) {
|
|
423
|
+
return sin(13.0 * 3.14159 * 0.5 * t) * pow(2.0, -10.0 * t) + 1.0;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* Overshoot — goes past 1, then pulls back. Slight rubber-band feel.
|
|
428
|
+
*/
|
|
429
|
+
float overshoot(float t) {
|
|
430
|
+
float s = 1.70158;
|
|
431
|
+
float f = 1.0 - t;
|
|
432
|
+
return 1.0 - f * f * (f * (s + 1.0) - s);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* Teleport — barely creeps, then near-instant jump with spring settle.
|
|
437
|
+
* Duration mostly = wait time. Jump happens at ~85% of t.
|
|
438
|
+
* Tiny slow drift → snap → spring overshoot → settle at 1.
|
|
439
|
+
*/
|
|
440
|
+
float teleport(float t) {
|
|
441
|
+
float jump = smoothstep(0.8, 0.85, t);
|
|
442
|
+
float creep = pow(t / 0.8, 4.0) * 0.1 * (1.0 - jump);
|
|
443
|
+
float s = max(t - 0.85, 0.0) * 6.667;
|
|
444
|
+
float settle = exp(-5.0 * s) * sin(s * 15.0) * 0.15;
|
|
445
|
+
return creep + jump + settle;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
void main() {}
|
|
449
|
+
`,ue=`#import hash from 'sandbox'
|
|
450
|
+
#import noise from 'sandbox'
|
|
451
|
+
#import fbm from 'sandbox'
|
|
452
|
+
#import voronoi from 'sandbox'
|
|
453
|
+
#import worley from 'sandbox'
|
|
454
|
+
|
|
455
|
+
uniform float u_intensity;
|
|
456
|
+
|
|
457
|
+
// ─── UV Effects ─────────────────────────────────────────────
|
|
458
|
+
// Each function takes UV and returns modified UV.
|
|
459
|
+
// All use u_intensity uniform for configuration.
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* Pixelate — blocky mosaic effect.
|
|
463
|
+
* Snaps UV to a grid. Higher intensity = larger pixels.
|
|
464
|
+
* intensity = pixel count along shortest axis (default: 20)
|
|
465
|
+
* @uv-modifier
|
|
466
|
+
*/
|
|
467
|
+
vec2 pixelate(vec2 uv, float intensity) {
|
|
468
|
+
float size = length(u_resolution) / (max(intensity, 1.0) * 10.0);
|
|
469
|
+
return floor(uv / size) * size;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
/**
|
|
473
|
+
* Twist — spiral distortion from center outward.
|
|
474
|
+
* Expects centered UV (use center() first).
|
|
475
|
+
* intensity = twist strength (0.4 = subtle, 1.0 = full spiral)
|
|
476
|
+
* @uv-modifier
|
|
477
|
+
*/
|
|
478
|
+
vec2 twist(vec2 uv, float intensity) {
|
|
479
|
+
float dist = length(uv);
|
|
480
|
+
float angle = atan(uv.y, uv.x) - intensity * 20.0 * dist;
|
|
481
|
+
return vec2(cos(angle), sin(angle)) * dist;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
/**
|
|
485
|
+
* Ripple — concentric wave distortion from center.
|
|
486
|
+
* Expects centered UV (use center() first).
|
|
487
|
+
* Creates water-drop-like rings.
|
|
488
|
+
* intensity = wave amplitude (0.5 = gentle, 2.0 = strong)
|
|
489
|
+
* @uv-modifier
|
|
490
|
+
*/
|
|
491
|
+
vec2 ripple(vec2 uv, float intensity) {
|
|
492
|
+
float dist = length(uv);
|
|
493
|
+
float wave = sin(dist * 40.0 - u_time * 3.0) * intensity * 0.02;
|
|
494
|
+
return uv + normalize(uv + 0.001) * wave;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
/**
|
|
498
|
+
* Fisheye — barrel distortion from center.
|
|
499
|
+
* Expects centered UV (use center() first).
|
|
500
|
+
* Bends space outward like a fisheye lens.
|
|
501
|
+
* intensity = distortion power (0.5 = subtle, 2.0 = extreme)
|
|
502
|
+
* @uv-modifier
|
|
503
|
+
*/
|
|
504
|
+
vec2 fisheye(vec2 uv, float intensity) {
|
|
505
|
+
float dist = length(uv);
|
|
506
|
+
float power = pow(dist, intensity) / dist;
|
|
507
|
+
return uv * power;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
/**
|
|
511
|
+
* Wobble — animated sine wave displacement.
|
|
512
|
+
* Creates a jelly-like horizontal wobble.
|
|
513
|
+
* intensity = wobble amount (1.0 = gentle, 5.0 = wild)
|
|
514
|
+
* @uv-modifier
|
|
515
|
+
*/
|
|
516
|
+
vec2 wobble(vec2 uv, float intensity) {
|
|
517
|
+
uv.x += sin(uv.y * 10.0 + u_time * 2.0) * intensity * 0.01;
|
|
518
|
+
uv.y += cos(uv.x * 10.0 + u_time * 2.0) * intensity * 0.01;
|
|
519
|
+
return uv;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
/**
|
|
523
|
+
* Organic — iterative fluid morph distortion.
|
|
524
|
+
* Creates marble-like flowing patterns.
|
|
525
|
+
* intensity = animation speed (3.0 = default)
|
|
526
|
+
* @uv-modifier
|
|
527
|
+
*/
|
|
528
|
+
vec2 organic(vec2 uv, float intensity) {
|
|
529
|
+
float speed = u_time * intensity;
|
|
530
|
+
vec2 acc = vec2(uv.x + uv.y);
|
|
531
|
+
|
|
532
|
+
for (int i = 0; i < 5; i++) {
|
|
533
|
+
acc += sin(max(uv.x, uv.y)) + uv;
|
|
534
|
+
uv += 0.5 * vec2(
|
|
535
|
+
cos(5.1123314 + 0.353 * acc.y + speed * 0.131121),
|
|
536
|
+
sin(acc.x - 0.113 * speed)
|
|
537
|
+
);
|
|
538
|
+
uv -= cos(uv.x + uv.y) - sin(uv.x * 0.711 - uv.y);
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
return uv;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
/**
|
|
545
|
+
* Glitch — random horizontal line displacement.
|
|
546
|
+
* Creates digital glitch artifact bands.
|
|
547
|
+
* intensity = glitch strength (0.5 = subtle, 3.0 = heavy)
|
|
548
|
+
* @uv-modifier
|
|
549
|
+
*/
|
|
550
|
+
vec2 glitch(vec2 uv, float intensity) {
|
|
551
|
+
float line = floor(uv.y * 50.0);
|
|
552
|
+
float shift = hash(vec2(line, floor(u_time * 8.0)));
|
|
553
|
+
shift = step(0.9, shift) * (shift - 0.9) * 10.0;
|
|
554
|
+
uv.x += shift * intensity * 0.1 * u_resolution.x;
|
|
555
|
+
return uv;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
/**
|
|
559
|
+
* Mirror — reflect UV across a chosen axis.
|
|
560
|
+
* Creates bilateral symmetry.
|
|
561
|
+
* intensity = 0 for horizontal mirror, 1 for vertical mirror
|
|
562
|
+
* @uv-modifier
|
|
563
|
+
*/
|
|
564
|
+
vec2 mirror(vec2 uv, float intensity) {
|
|
565
|
+
if (intensity < 0.5) {
|
|
566
|
+
uv.x = abs(uv.x);
|
|
567
|
+
} else {
|
|
568
|
+
uv.y = abs(uv.y);
|
|
569
|
+
}
|
|
570
|
+
return uv;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
/**
|
|
574
|
+
* Kaleidoscope — repeating angular symmetry.
|
|
575
|
+
* Expects centered UV (use center() first).
|
|
576
|
+
* Creates mandala-like radial repeats.
|
|
577
|
+
* intensity = number of segments (4 = quad, 6 = hex, 8 = octa)
|
|
578
|
+
* @uv-modifier
|
|
579
|
+
*/
|
|
580
|
+
vec2 kaleidoscope(vec2 uv, float intensity) {
|
|
581
|
+
float segments = max(intensity, 1.0);
|
|
582
|
+
float angle = atan(uv.y, uv.x);
|
|
583
|
+
float segment_angle = 6.28318 / segments;
|
|
584
|
+
angle = mod(angle, segment_angle);
|
|
585
|
+
angle = abs(angle - segment_angle * 0.5);
|
|
586
|
+
float r = length(uv);
|
|
587
|
+
return vec2(cos(angle), sin(angle)) * r;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
/**
|
|
591
|
+
* Warp — domain warping using fractal noise.
|
|
592
|
+
* Displaces UV by layered fbm noise for smoke/cloud-like distortion.
|
|
593
|
+
* Animated over time.
|
|
594
|
+
* intensity = warp strength (0.5 = subtle haze, 3.0 = heavy distortion)
|
|
595
|
+
* @uv-modifier
|
|
596
|
+
*/
|
|
597
|
+
vec2 warp(vec2 uv, float intensity) {
|
|
598
|
+
vec2 offset = vec2(
|
|
599
|
+
fbm(uv + u_time * 0.3),
|
|
600
|
+
fbm(uv + vec2(5.2, 1.3) + u_time * 0.3)
|
|
601
|
+
);
|
|
602
|
+
return uv + offset * intensity * 0.1;
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
/**
|
|
606
|
+
* Displace — noise-based UV displacement.
|
|
607
|
+
* Shifts UV using smooth value noise for a wavy, heat-haze look.
|
|
608
|
+
* Animated over time.
|
|
609
|
+
* intensity = displacement amount (1.0 = gentle, 5.0 = strong)
|
|
610
|
+
* @uv-modifier
|
|
611
|
+
*/
|
|
612
|
+
vec2 displace(vec2 uv, float intensity) {
|
|
613
|
+
float nx = noise(uv * 5.0 + u_time);
|
|
614
|
+
float ny = noise(uv * 5.0 + vec2(3.7, 7.1) + u_time);
|
|
615
|
+
return uv + (vec2(nx, ny) - 0.5) * intensity * 0.05;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
/**
|
|
619
|
+
* Shatter — voronoi cell-based UV snapping.
|
|
620
|
+
* Snaps UV to nearest cell point, creating a broken-glass look.
|
|
621
|
+
* intensity = cell density (5.0 = large shards, 20.0 = fine fragments)
|
|
622
|
+
* @uv-modifier
|
|
623
|
+
*/
|
|
624
|
+
vec2 shatter(vec2 uv, float intensity) {
|
|
625
|
+
return voronoi(uv * intensity) / intensity;
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
/**
|
|
629
|
+
* Cells — cellular distortion using Worley distance.
|
|
630
|
+
* Displaces UV outward from cell edges, like looking through textured glass.
|
|
631
|
+
* intensity = cell scale (3.0 = large bubbles, 15.0 = fine texture)
|
|
632
|
+
* @uv-modifier
|
|
633
|
+
*/
|
|
634
|
+
vec2 cells(vec2 uv, float intensity) {
|
|
635
|
+
float dist = worley(uv * intensity);
|
|
636
|
+
vec2 grad = vec2(
|
|
637
|
+
worley(uv * intensity + vec2(0.01, 0.0)) - dist,
|
|
638
|
+
worley(uv * intensity + vec2(0.0, 0.01)) - dist
|
|
639
|
+
);
|
|
640
|
+
return uv + grad * dist * 2.0;
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
void main() {}
|
|
644
|
+
`,fe=`#import hash from 'sandbox'
|
|
645
|
+
|
|
646
|
+
uniform float u_intensity;
|
|
647
|
+
|
|
648
|
+
// ─── Color Filters ──────────────────────────────────────────
|
|
649
|
+
// Each function takes a color and returns modified color.
|
|
650
|
+
// All use u_intensity uniform for configuration.
|
|
651
|
+
|
|
652
|
+
/**
|
|
653
|
+
* Contrast — adjust contrast around mid-gray.
|
|
654
|
+
* intensity: 1.0 = original, >1 = more contrast, <1 = flatter
|
|
655
|
+
* @color-modifier
|
|
656
|
+
*/
|
|
657
|
+
vec3 contrast(vec3 color, float intensity) {
|
|
658
|
+
return (color - 0.5) * intensity + 0.5;
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
/**
|
|
662
|
+
* Brightness — multiply overall brightness.
|
|
663
|
+
* intensity: 1.0 = original, >1 = brighter, <1 = darker
|
|
664
|
+
* @color-modifier
|
|
665
|
+
*/
|
|
666
|
+
vec3 brightness(vec3 color, float intensity) {
|
|
667
|
+
return color * intensity;
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
/**
|
|
671
|
+
* Saturate — adjust color saturation.
|
|
672
|
+
* intensity: 0 = grayscale, 1.0 = original, >1 = oversaturated
|
|
673
|
+
* @color-modifier
|
|
674
|
+
*/
|
|
675
|
+
vec3 saturate(vec3 color, float intensity) {
|
|
676
|
+
float gray = dot(color, vec3(0.299, 0.587, 0.114));
|
|
677
|
+
return mix(vec3(gray), color, intensity);
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
/**
|
|
681
|
+
* Posterize — reduce color to N discrete levels per channel.
|
|
682
|
+
* intensity = number of levels (4 = retro, 16 = subtle, 256 = nearly smooth)
|
|
683
|
+
* @color-modifier
|
|
684
|
+
*/
|
|
685
|
+
vec3 posterize(vec3 color, float intensity) {
|
|
686
|
+
float levels = max(intensity, 1.0);
|
|
687
|
+
return floor(color * levels + 0.5) / levels;
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
/**
|
|
691
|
+
* Threshold — convert to black and white based on cutoff.
|
|
692
|
+
* intensity = cutoff point (0.5 = balanced, lower = more white)
|
|
693
|
+
* @color-modifier
|
|
694
|
+
*/
|
|
695
|
+
vec3 threshold(vec3 color, float intensity) {
|
|
696
|
+
float avg = dot(color, vec3(0.299, 0.587, 0.114));
|
|
697
|
+
return avg > intensity ? vec3(1.0) : vec3(0.0);
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
/**
|
|
701
|
+
* Invert — flip all color channels.
|
|
702
|
+
* intensity: 0.0 = original, 1.0 = fully inverted, 0.5 = mid-gray
|
|
703
|
+
* @color-modifier
|
|
704
|
+
*/
|
|
705
|
+
vec3 invert(vec3 color, float intensity) {
|
|
706
|
+
return mix(color, 1.0 - color, intensity);
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
/**
|
|
710
|
+
* Glow — luminance-based bloom, bright areas get amplified.
|
|
711
|
+
* intensity = glow strength (0.5 = subtle, 2.0 = intense)
|
|
712
|
+
* @color-modifier
|
|
713
|
+
*/
|
|
714
|
+
vec3 glow(vec3 color, float intensity) {
|
|
715
|
+
float luminance = dot(color, vec3(0.299, 0.587, 0.114));
|
|
716
|
+
return color + color * luminance * intensity;
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
/**
|
|
720
|
+
* Grain — animated film noise overlay.
|
|
721
|
+
* intensity = noise strength (0.1 = subtle, 0.5 = heavy)
|
|
722
|
+
* @color-modifier
|
|
723
|
+
*/
|
|
724
|
+
vec3 grain(vec3 color, vec2 uv, float intensity) {
|
|
725
|
+
float n = (hash(uv * 1000.0 + u_time) - 0.5) * intensity;
|
|
726
|
+
return color + n;
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
/**
|
|
730
|
+
* Vignette — darken canvas edges.
|
|
731
|
+
* Applies circular falloff from center.
|
|
732
|
+
* intensity = darkness strength (1.0 = subtle, 2.0 = strong)
|
|
733
|
+
* @color-modifier
|
|
734
|
+
*/
|
|
735
|
+
vec3 vignette(vec3 color, vec2 uv, float intensity) {
|
|
736
|
+
vec2 centered = uv / u_resolution - 0.5;
|
|
737
|
+
float dist = length(centered);
|
|
738
|
+
float falloff = 1.0 - smoothstep(0.3, 0.7, dist * intensity);
|
|
739
|
+
return color * falloff;
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
/**
|
|
743
|
+
* Sepia — warm brownish tint like old photographs.
|
|
744
|
+
* intensity: 0.0 = original, 1.0 = full sepia
|
|
745
|
+
* @color-modifier
|
|
746
|
+
*/
|
|
747
|
+
vec3 sepia(vec3 color, float intensity) {
|
|
748
|
+
vec3 s;
|
|
749
|
+
s.r = dot(color, vec3(0.393, 0.769, 0.189));
|
|
750
|
+
s.g = dot(color, vec3(0.349, 0.686, 0.168));
|
|
751
|
+
s.b = dot(color, vec3(0.272, 0.534, 0.131));
|
|
752
|
+
return mix(color, s, intensity);
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
/**
|
|
756
|
+
* Gamma — apply gamma correction curve.
|
|
757
|
+
* intensity = gamma value (1.0 = linear, 2.2 = standard sRGB, <1 = brighten darks)
|
|
758
|
+
* @color-modifier
|
|
759
|
+
*/
|
|
760
|
+
vec3 gamma(vec3 color, float intensity) {
|
|
761
|
+
return pow(max(color, 0.0), vec3(1.0 / max(intensity, 0.001)));
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
/**
|
|
765
|
+
* Tint — blend color toward a target tint.
|
|
766
|
+
* intensity: 0.0 = original, 1.0 = fully tinted
|
|
767
|
+
* @color-modifier
|
|
768
|
+
*/
|
|
769
|
+
vec3 tint(vec3 color, vec3 tintColor, float intensity) {
|
|
770
|
+
return mix(color, color * tintColor, intensity);
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
/**
|
|
774
|
+
* Highlights — add white light to bright areas.
|
|
775
|
+
* Uses max channel brightness so even saturated colors trigger highlights.
|
|
776
|
+
* intensity = highlight strength (0.2 = subtle sheen, 1.0 = strong gloss)
|
|
777
|
+
* @color-modifier
|
|
778
|
+
*/
|
|
779
|
+
vec3 highlights(vec3 color, float intensity) {
|
|
780
|
+
float bright = max(color.r, max(color.g, color.b + 0.3));
|
|
781
|
+
return color + max(bright * 5.5 - 4.0, 0.0) * intensity;
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
/**
|
|
785
|
+
* Bayer 8×8 threshold matrix — computed mathematically.
|
|
786
|
+
* Returns threshold value 0–1 for ordered dithering.
|
|
787
|
+
*/
|
|
788
|
+
float bayer8(vec2 p) {
|
|
789
|
+
p = mod(floor(p), 8.0);
|
|
790
|
+
float value = 0.0;
|
|
791
|
+
float divisor = 1.0;
|
|
792
|
+
float multiplier = 16.0;
|
|
793
|
+
|
|
794
|
+
for (int i = 0; i < 3; i++) {
|
|
795
|
+
vec2 q = mod(floor(p / divisor), 2.0);
|
|
796
|
+
value += (q.x * 2.0 + q.y * 3.0 - q.x * q.y * 4.0) * multiplier;
|
|
797
|
+
divisor *= 2.0;
|
|
798
|
+
multiplier *= 0.25;
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
return value / 64.0;
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
/**
|
|
805
|
+
* Dither — ordered Bayer 8×8 dithering.
|
|
806
|
+
* Reduces color to discrete levels with a threshold pattern.
|
|
807
|
+
* Use pixelate() on UV before coloring for blocky dither cells.
|
|
808
|
+
* intensity = number of color levels (2 = 1-bit, 4 = retro, 8 = subtle)
|
|
809
|
+
* @color-modifier
|
|
810
|
+
*/
|
|
811
|
+
vec3 dither(vec3 color, vec2 uv, float intensity) {
|
|
812
|
+
float levels = max(intensity, 2.0);
|
|
813
|
+
float threshold = bayer8(uv) - 0.5;
|
|
814
|
+
float stepSize = 1.0 / (levels - 1.0);
|
|
815
|
+
color += threshold * stepSize;
|
|
816
|
+
return clamp(floor(color * (levels - 1.0) + 0.5) / (levels - 1.0), 0.0, 1.0);
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
/**
|
|
820
|
+
* Arcade — pixelated Bayer dithering.
|
|
821
|
+
* Combines pixelation and ordered dither for retro 8-bit look.
|
|
822
|
+
* intensity = number of color levels (2 = 1-bit, 4 = retro, 8 = subtle)
|
|
823
|
+
* @color-modifier
|
|
824
|
+
*/
|
|
825
|
+
vec3 arcade(vec3 color, vec2 uv, float intensity) {
|
|
826
|
+
vec2 grid = uv * u_resolution * 0.5;
|
|
827
|
+
return dither(color, grid, intensity);
|
|
828
|
+
}
|
|
829
|
+
|
|
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
|
|
6
832
|
precision mediump float;
|
|
7
833
|
#endif
|
|
8
834
|
|
|
@@ -15,7 +841,7 @@ void main() {
|
|
|
15
841
|
v_texcoord = a_texcoord;
|
|
16
842
|
gl_Position = vec4(a_position, 0.0, 1.0);
|
|
17
843
|
}
|
|
18
|
-
`,
|
|
844
|
+
`,F=`#ifdef GL_ES
|
|
19
845
|
precision mediump float;
|
|
20
846
|
#endif
|
|
21
847
|
|
|
@@ -29,7 +855,7 @@ void main() {
|
|
|
29
855
|
vec3 color = vec3(uv.x, uv.y, 0.5 + 0.5 * sin(u_time));
|
|
30
856
|
gl_FragColor = vec4(color, 1.0);
|
|
31
857
|
}
|
|
32
|
-
`,
|
|
858
|
+
`,U=`#version 300 es
|
|
33
859
|
|
|
34
860
|
in vec2 a_position;
|
|
35
861
|
in vec2 a_texcoord;
|
|
@@ -39,7 +865,7 @@ out vec2 v_texcoord;
|
|
|
39
865
|
void main() {
|
|
40
866
|
v_texcoord = a_texcoord;
|
|
41
867
|
gl_Position = vec4(a_position, 0.0, 1.0);
|
|
42
|
-
}`,
|
|
868
|
+
}`,de=`#version 300 es
|
|
43
869
|
precision highp float;
|
|
44
870
|
|
|
45
871
|
uniform vec2 u_resolution;
|
|
@@ -53,5 +879,5 @@ void main() {
|
|
|
53
879
|
vec2 uv = gl_FragCoord.xy / u_resolution;
|
|
54
880
|
vec3 color = vec3(uv.x, uv.y, 0.5 + 0.5 * sin(u_time));
|
|
55
881
|
fragColor = vec4(color, 1.0);
|
|
56
|
-
}`;class
|
|
57
|
-
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:{}};if(
|
|
882
|
+
}`;class O{constructor(e,n){a(this,"listeners",[]);a(this,"canvasEl");a(this,"options");a(this,"engine");a(this,"usingCustomVertex",!1);if(this.canvasEl=e,this.options=this.resolveOptions(n),this.engine=C.setup(this.canvasEl,this.options),this.setupListeners(),this.setViewport(),this.options.modules)for(const[t,i]of Object.entries(this.options.modules))this.module(t,i);this.options.autoplay&&this.play()}static create(e,n){return new O(e,n)}static defineModule(e,n,t={}){v.define({name:e,source:n,options:t})}static availableModules(){return b.available()}static compile(e){return new m(e).compile()}resolveOptions(e){const n={vertex:new m(R),fragment:new m(F),autoplay:!0,pauseWhenHidden:!0,dpr:"auto",fps:0,preserveDrawingBuffer:!1,antialias:!0,onError:o=>{console.error("Oops!",o,`
|
|
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)){n.vertex=new m(e.vertex);const o=n.vertex.version();n.fragment=new m(o===2?de:F)}if(e!=null&&e.fragment&&!(e!=null&&e.vertex)){n.fragment=new m(e.fragment);const o=n.fragment.version();n.vertex=new m(o===2?U:R)}e!=null&&e.vertex&&(e!=null&&e.fragment)&&(n.vertex=new m(e.vertex),n.fragment=new m(e.fragment));const{vertex:t,fragment:i,...r}=e||{};return{...n,...r}}setupListeners(){this.listeners.push(x.on(window,"resize",()=>{this.setViewport()}),x.on(this.canvasEl,"resize",()=>{this.setViewport()}),(()=>{let e=!1;return x.on(document,"scroll",n=>{this.options.pauseWhenHidden&&(this.isInViewport()?e&&!this.isPlaying()&&(this.play(),e=!1):this.isPlaying()&&(this.pause(),e=!0))})})(),x.on(document,"mousemove",e=>{this.setMouse(e.clientX||e.pageX,e.clientY||e.pageY)}),x.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,n=this.canvasEl.clientWidth||this.canvasEl.width||1,t=this.canvasEl.clientHeight||this.canvasEl.height||1;this.engine.viewport(0,0,Math.max(1,Math.floor(n*e)),Math.max(1,Math.floor(t*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,n){const t=this.canvasEl.getBoundingClientRect();e>=t.left&&e<=t.right&&n>=t.top&&n<=t.bottom&&this.engine.mouse(e-t.left,n-t.top)}setUniform(e,n){return this.engine.uniform(e,n),this}setUniforms(e){return this.engine.uniforms(e),this}getUniform(e){return this.engine.getUniform(e)}setShader(e,n){return this.options.vertex=new m(e),this.options.fragment=new m(n),this.usingCustomVertex=!0,this.engine.shader(this.options.vertex,this.options.fragment),this}setFragment(e){const n=new m(e),t=n.version(),i=this.options.vertex.version();return this.options.fragment=n,t!==i&&(this.usingCustomVertex||(this.options.vertex=new m(t===2?U:R))),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,n="before"){return n==="before"?this.engine.onBeforeHooks.add(e):this.engine.onAfterHooks.add(e)}module(e,n){const t=y.resolveOptions(e);if(!t)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(n)){const o=t[i];if(!o){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(o.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 n=this.hook(t=>{t.time>=e&&(n(),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}destroy(){this.destroyListeners(),this.engine.destroy()}}exports.Sandbox=O;exports.SandboxAttemptedToImportDefaultFunctionError=z;exports.SandboxAttemptedToImportMainFunctionError=N;exports.SandboxContextCreationError=re;exports.SandboxError=h;exports.SandboxForbiddenModuleNameError=G;exports.SandboxGLSLShaderCompilationError=w;exports.SandboxMentionCouldNotBeReplacedError=j;exports.SandboxMentionFunctionNotFoundError=H;exports.SandboxMentionUniformNotFoundError=q;exports.SandboxModuleMethodNotFoundError=D;exports.SandboxModuleNotFoundError=B;exports.SandboxOnHookCallbackError=K;exports.SandboxOnLoadCallbackError=Y;exports.SandboxOverwriteModuleError=W;exports.SandboxProgramError=E;exports.SandboxShaderDuplicateImportNameError=P;exports.SandboxShaderImportSyntaxError=I;exports.SandboxShaderRequirementMismatchError=A;exports.SandboxShaderVersionMismatchError=$;exports.SandboxShaderWithoutFunctionError=T;exports.SandboxWebGLNotSupportedError=V;
|