@rosalana/sandbox 0.0.1 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,16 +1,28 @@
1
1
  [![Rosalana](https://raw.githubusercontent.com/rosalana/.github/main/Sandbox_Banner.png)](https://github.com/rosalana)
2
2
 
3
+ <div align="center">
4
+
5
+ [![npm version](https://img.shields.io/npm/v/@rosalana/sandbox?style=flat&colorA=18181B&colorB=28CF8D)](https://www.npmjs.com/package/@rosalana/sandbox)
6
+ [![npm downloads](https://img.shields.io/npm/dm/@rosalana/sandbox?style=flat&colorA=18181B&colorB=28CF8D)](https://www.npmjs.com/package/@rosalana/sandbox)
7
+ [![GitHub stars](https://img.shields.io/github/stars/rosalana/sandbox?style=flat&colorA=18181B&colorB=28CF8D)](https://github.com/rosalana/sandbox/stargazers)
8
+ [![License](https://img.shields.io/npm/l/@rosalana/sandbox?style=flat&colorA=18181B&colorB=28CF8D)](https://github.com/rosalana/sandbox/blob/master/LICENCE)
9
+
10
+ [![Open in CodeSandbox](https://img.shields.io/badge/Open%20in-CodeSandbox-28CF8D?style=for-the-badge&logo=codesandbox&logoColor=white&colorA=18181B)](https://codesandbox.io/p/sandbox/nervous-greider-76wsrk)
11
+ [![Report Issue](https://img.shields.io/badge/Report-Issue-FF6B6B?style=for-the-badge&logo=github&logoColor=white&colorA=18181B)](https://github.com/rosalana/sandbox/issues)
12
+
13
+ </div>
14
+
3
15
  **Rosalana Sandbox** is a lightweight WebGL wrapper for **simple, beautiful shader effects**. It focuses on a clean API, type safety, and fast setup so you can go from idea to a shader in minutes.
4
16
 
5
17
  It's **DX‑friendly**, small, and intentionally minimal — perfect for gradients, ambient backgrounds, and animated GLSL experiments. If you're not building a full 3D engine, Sandbox is a delightful alternative to larger libraries like three.js or p5.js.
6
18
 
7
19
  ### Bundle size comparison
8
20
 
9
- | Library | Minified | Gzipped |
10
- | ------------- | -------- | -------- |
11
- | **Sandbox** | 31 KB | **8 KB** |
12
- | three.js | 694 KB | 175 KB |
13
- | p5.js | 1.1 MB | 351 KB |
21
+ | Library | Minified | Gzipped |
22
+ | ----------- | -------- | -------- |
23
+ | **Sandbox** | 31 KB | **8 KB** |
24
+ | three.js | 694 KB | 175 KB |
25
+ | p5.js | 1.1 MB | 351 KB |
14
26
 
15
27
  Sandbox is **~22x smaller** than three.js and **~44x smaller** than p5.js.
16
28
 
@@ -325,20 +337,20 @@ interface SandboxOptions {
325
337
  }
326
338
  ```
327
339
 
328
- | Option | Default | Description |
329
- | ----------------------- | ------------- | ------------------------------- |
330
- | `vertex` | built-in | Custom vertex shader |
331
- | `fragment` | built-in | Fragment shader |
332
- | `autoplay` | `true` | Start rendering immediately |
333
- | `pauseWhenHidden` | `true` | Pause when scrolled out of view |
334
- | `dpr` | `"auto"` | Device pixel ratio |
335
- | `preserveDrawingBuffer` | `false` | Keep buffer for screenshots |
336
- | `antialias` | `true` | Enable antialiasing |
340
+ | Option | Default | Description |
341
+ | ----------------------- | --------------- | ------------------------------- |
342
+ | `vertex` | built-in | Custom vertex shader |
343
+ | `fragment` | built-in | Fragment shader |
344
+ | `autoplay` | `true` | Start rendering immediately |
345
+ | `pauseWhenHidden` | `true` | Pause when scrolled out of view |
346
+ | `dpr` | `"auto"` | Device pixel ratio |
347
+ | `preserveDrawingBuffer` | `false` | Keep buffer for screenshots |
348
+ | `antialias` | `true` | Enable antialiasing |
337
349
  | `onError` | `console.error` | Error callback |
338
- | `onLoad` | — | Called when ready |
339
- | `onBeforeRender` | — | Hook before each frame |
340
- | `onAfterRender` | — | Hook after each frame |
341
- | `uniforms` | — | Initial uniform values |
350
+ | `onLoad` | — | Called when ready |
351
+ | `onBeforeRender` | — | Hook before each frame |
352
+ | `onAfterRender` | — | Hook after each frame |
353
+ | `uniforms` | — | Initial uniform values |
342
354
 
343
355
  ## Limitations (by design)
344
356
 
package/dist/index.cjs.js CHANGED
@@ -1,8 +1,8 @@
1
- "use strict";var w=Object.defineProperty;var k=(o,t,e)=>t in o?w(o,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):o[t]=e;var i=(o,t,e)=>k(o,typeof t!="symbol"?t+"":t,e);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});class u{constructor(t,e,r,s){this.target=t,this.type=e,this.listener=r,this.options=s,this.target.addEventListener(this.type,this.listener,this.options)}remove(){this.target.removeEventListener(this.type,this.listener,this.options)}static on(t,e,r,s){return t.addEventListener(e,r,s),()=>t.removeEventListener(e,r,s)}}class l extends Error{constructor(t,e){super(t),this.code=e,this.name="SandboxError"}}class S extends l{constructor(t){const e=t==="not_supported"?"WebGL is not supported in this browser.":"Failed to create WebGL context. The GPU may be unavailable.";super(e,t==="not_supported"?"WEBGL_NOT_SUPPORTED":"CONTEXT_CREATION_FAILED"),this.name="SandboxContextError"}}class L extends l{constructor(t,e){super(`Vertex and fragment shader WebGL versions do not match (${t} vs ${e})`,"SHADER_VERSION_MISMATCH"),this.vertexVersion=t,this.fragmentVersion=e,this.name="SandboxShaderVersionMismatchError"}}class c extends l{constructor(e,r,s){const n=c.parseErrorLines(s),a=n.length>0?` at line(s): ${n.join(", ")}`:"";super(`${e} shader compilation failed${a}
1
+ "use strict";var w=Object.defineProperty;var k=(o,t,e)=>t in o?w(o,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):o[t]=e;var i=(o,t,e)=>k(o,typeof t!="symbol"?t+"":t,e);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});class u{constructor(t,e,r,s){this.target=t,this.type=e,this.listener=r,this.options=s,this.target.addEventListener(this.type,this.listener,this.options)}remove(){this.target.removeEventListener(this.type,this.listener,this.options)}static on(t,e,r,s){return t.addEventListener(e,r,s),()=>t.removeEventListener(e,r,s)}}class l extends Error{constructor(t,e){super(t),this.code=e,this.name="SandboxError"}}class S extends l{constructor(t){const e=t==="not_supported"?"WebGL is not supported in this browser.":"Failed to create WebGL context. The GPU may be unavailable.";super(e,t==="not_supported"?"WEBGL_NOT_SUPPORTED":"CONTEXT_CREATION_FAILED"),this.name="SandboxContextError"}}class L extends l{constructor(t,e){super(`Vertex and fragment shader WebGL versions do not match (${t} vs ${e})`,"SHADER_VERSION_MISMATCH"),this.vertexVersion=t,this.fragmentVersion=e,this.name="SandboxShaderVersionMismatchError"}}class f extends l{constructor(e,r,s){const n=f.parseErrorLines(s),a=n.length>0?` at line(s): ${n.join(", ")}`:"";super(`${e} shader compilation failed${a}
2
2
 
3
- ${s}`,"SHADER_COMPILATION_FAILED");i(this,"lines");this.shaderType=e,this.source=r,this.infoLog=s,this.name="SandboxShaderCompilationError",this.lines=n}static parseErrorLines(e){const r=[/ERROR:\s*\d*:(\d+)/g,/(\d+):(\d+)\(\d+\):/g,/^(\d+):/gm],s=new Set;for(const n of r){let a;for(;(a=n.exec(e))!==null;){const f=parseInt(a[1],10);f>0&&s.add(f)}}return[...s].sort((n,a)=>n-a)}}class d extends l{constructor(t){super(`Shader program linking failed
3
+ ${s}`,"SHADER_COMPILATION_FAILED");i(this,"lines");this.shaderType=e,this.source=r,this.infoLog=s,this.name="SandboxShaderCompilationError",this.lines=n}static parseErrorLines(e){const r=[/ERROR:\s*\d*:(\d+)/g,/(\d+):(\d+)\(\d+\):/g,/^(\d+):/gm],s=new Set;for(const n of r){let a;for(;(a=n.exec(e))!==null;){const c=parseInt(a[1],10);c>0&&s.add(c)}}return[...s].sort((n,a)=>n-a)}}class d extends l{constructor(t){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.startTime=e,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.stop(),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 c(t,e,"Failed to create shader object");if(r.shaderSource(n,e),r.compileShader(n),!r.getShaderParameter(n,r.COMPILE_STATUS)){const f=r.getShaderInfoLog(n)||"Unknown error";throw r.deleteShader(n),new c(t,e,f)}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
+ ${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;if(this.callback=t,this.running=!0,this.frame===0){const e=performance.now();this.startTime=e,this.lastTime=e}return 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.stop(),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
6
6
  precision mediump float;
7
7
  #endif
8
8
 
@@ -15,7 +15,7 @@ void main() {
15
15
  v_texcoord = a_texcoord;
16
16
  gl_Position = vec4(a_position, 0.0, 1.0);
17
17
  }
18
- `,E=`#ifdef GL_ES
18
+ `,y=`#ifdef GL_ES
19
19
  precision mediump float;
20
20
  #endif
21
21
 
@@ -29,7 +29,7 @@ void main() {
29
29
  vec3 color = vec3(uv.x, uv.y, 0.5 + 0.5 * sin(u_time));
30
30
  gl_FragColor = vec4(color, 1.0);
31
31
  }
32
- `,y=`#version 300 es
32
+ `,E=`#version 300 es
33
33
 
34
34
  in vec2 a_position;
35
35
  in vec2 a_texcoord;
@@ -53,5 +53,5 @@ void main() {
53
53
  vec2 uv = gl_FragCoord.xy / u_resolution;
54
54
  vec3 color = vec3(uv.x, uv.y, 0.5 + 0.5 * sin(u_time));
55
55
  fragColor = vec4(color, 1.0);
56
- }`;class _{constructor(t,e){i(this,"listeners",[]);i(this,"canvas");i(this,"options");i(this,"engine");this.canvas=t,this.options=this.resolveOptions(e),this.engine=b.setup(this.canvas,this.options),this.setupListeners(),this.setViewport(),this.options.onLoad(),this.options.autoplay&&this.play()}static create(t,e){return new _(t,e)}resolveOptions(t){const e={vertex:g,fragment:E,autoplay:!0,pauseWhenHidden:!0,dpr:"auto",preserveDrawingBuffer:!1,antialias:!0,onError:r=>{console.error("Oops!",r,`
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(t!=null&&t.vertex&&!(t!=null&&t.fragment)){const r=h.detectVersion(t.vertex);e.vertex=t.vertex,e.fragment=r===2?M:E}if(t!=null&&t.fragment&&!(t!=null&&t.vertex)){const r=h.detectVersion(t.fragment);e.fragment=t.fragment,e.vertex=r===2?y:g}return{...e,...t}}setupListeners(){this.listeners.push(u.on(window,"resize",()=>{this.setViewport()}),u.on(this.canvas,"resize",()=>{this.setViewport()}),u.on(document,"scroll",()=>{this.options.pauseWhenHidden&&(this.isInViewport()?this.play():this.pause())}),u.on(document,"mousemove",t=>{this.setMouse(t.clientX||t.pageX,t.clientY||t.pageY)}),u.on(document,"touchmove",t=>{t.touches.length>0&&this.setMouse(t.touches[0].clientX,t.touches[0].clientY)}))}destroyListeners(){this.listeners.forEach(t=>t()),this.listeners=[]}setViewport(){const t=this.options.dpr==="auto"?Math.min(2,window.devicePixelRatio||1):this.options.dpr,e=this.canvas.clientWidth||this.canvas.width||1,r=this.canvas.clientHeight||this.canvas.height||1;this.engine.viewport(0,0,Math.max(1,Math.floor(e*t)),Math.max(1,Math.floor(r*t)))}isInViewport(){const t=this.canvas.getBoundingClientRect();return t.bottom>=0&&t.right>=0&&t.top<=(window.innerHeight||document.documentElement.clientHeight)&&t.left<=(window.innerWidth||document.documentElement.clientWidth)}setMouse(t,e){const r=this.canvas.getBoundingClientRect();t>=r.left&&t<=r.right&&e>=r.top&&e<=r.bottom&&this.engine.mouse(t-r.left,e-r.top)}setUniform(t,e){return this.engine.uniform(t,e),this}setUniforms(t){return this.engine.uniforms(t),this}getUniform(t){return this.engine.getUniform(t)}setShader(t,e){return this.engine.shader(t,e),this}setFragment(t){const r=this.webglVersion()===1?g:y;return this.engine.shader(r,t),this}hook(t,e="before"){return e==="before"?this.engine.onBeforeHooks.add(t):this.engine.onAfterHooks.add(t)}play(){return this.engine.play(),this}playAt(t){return this.engine.clock(t),this.engine.play(),this}pause(){return this.engine.pause(),this}pauseAt(t){const e=this.hook(r=>{r.time>=t&&(e(),this.pause())},"after");return this}toggle(){return this.engine.playing?this.pause():this.play(),this}time(t){return this.engine.clock(t),this}render(){return this.engine.render(),this}renderAt(t){return this.engine.clock(t),this.engine.render(),this}isPlaying(){return this.engine.playing}webglVersion(){return this.engine.getVersion()}canvasElement(){return this.canvas}destroy(){this.destroyListeners(),this.engine.destroy()}}exports.Sandbox=_;exports.SandboxContextError=S;exports.SandboxError=l;exports.SandboxProgramError=d;exports.SandboxShaderCompilationError=c;exports.SandboxShaderVersionMismatchError=L;
56
+ }`;class _{constructor(t,e){i(this,"listeners",[]);i(this,"canvas");i(this,"options");i(this,"engine");this.canvas=t,this.options=this.resolveOptions(e),this.engine=b.setup(this.canvas,this.options),this.setupListeners(),this.setViewport(),this.options.onLoad(),this.options.autoplay&&this.play()}static create(t,e){return new _(t,e)}resolveOptions(t){const e={vertex:g,fragment:y,autoplay:!0,pauseWhenHidden:!0,dpr:"auto",preserveDrawingBuffer:!1,antialias:!0,onError:r=>{console.error("Oops!",r,`
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(t!=null&&t.vertex&&!(t!=null&&t.fragment)){const r=h.detectVersion(t.vertex);e.vertex=t.vertex,e.fragment=r===2?M:y}if(t!=null&&t.fragment&&!(t!=null&&t.vertex)){const r=h.detectVersion(t.fragment);e.fragment=t.fragment,e.vertex=r===2?E:g}return{...e,...t}}setupListeners(){this.listeners.push(u.on(window,"resize",()=>{this.setViewport()}),u.on(this.canvas,"resize",()=>{this.setViewport()}),(()=>{let t=!1;return u.on(document,"scroll",e=>{this.options.pauseWhenHidden&&(this.isInViewport()?t&&!this.isPlaying()&&(this.play(),t=!1):this.isPlaying()&&(this.pause(),t=!0))})})(),u.on(document,"mousemove",t=>{this.setMouse(t.clientX||t.pageX,t.clientY||t.pageY)}),u.on(document,"touchmove",t=>{t.touches.length>0&&this.setMouse(t.touches[0].clientX,t.touches[0].clientY)}))}destroyListeners(){this.listeners.forEach(t=>t()),this.listeners=[]}setViewport(){const t=this.options.dpr==="auto"?Math.min(2,window.devicePixelRatio||1):this.options.dpr,e=this.canvas.clientWidth||this.canvas.width||1,r=this.canvas.clientHeight||this.canvas.height||1;this.engine.viewport(0,0,Math.max(1,Math.floor(e*t)),Math.max(1,Math.floor(r*t)))}isInViewport(){const t=this.canvas.getBoundingClientRect();return t.bottom>=0&&t.right>=0&&t.top<=(window.innerHeight||document.documentElement.clientHeight)&&t.left<=(window.innerWidth||document.documentElement.clientWidth)}setMouse(t,e){const r=this.canvas.getBoundingClientRect();t>=r.left&&t<=r.right&&e>=r.top&&e<=r.bottom&&this.engine.mouse(t-r.left,e-r.top)}setUniform(t,e){return this.engine.uniform(t,e),this}setUniforms(t){return this.engine.uniforms(t),this}getUniform(t){return this.engine.getUniform(t)}setShader(t,e){return this.engine.shader(t,e),this}setFragment(t){const r=this.webglVersion()===1?g:E;return this.engine.shader(r,t),this}hook(t,e="before"){return e==="before"?this.engine.onBeforeHooks.add(t):this.engine.onAfterHooks.add(t)}play(){return this.engine.play(),this}playAt(t){return this.engine.clock(t),this.engine.play(),this}pause(){return this.engine.pause(),this}pauseAt(t){const e=this.hook(r=>{r.time>=t&&(e(),this.pause())},"after");return this}toggle(){return this.engine.playing?this.pause():this.play(),this}time(t){return this.engine.clock(t),this}render(){return this.engine.render(),this}renderAt(t){return this.engine.clock(t),this.engine.render(),this}isPlaying(){return this.engine.playing}webglVersion(){return this.engine.getVersion()}canvasElement(){return this.canvas}destroy(){this.destroyListeners(),this.engine.destroy()}}exports.Sandbox=_;exports.SandboxContextError=S;exports.SandboxError=l;exports.SandboxProgramError=d;exports.SandboxShaderCompilationError=f;exports.SandboxShaderVersionMismatchError=L;
package/dist/index.es.js CHANGED
@@ -42,9 +42,9 @@ class k extends u {
42
42
  ), this.vertexVersion = t, this.fragmentVersion = e, this.name = "SandboxShaderVersionMismatchError";
43
43
  }
44
44
  }
45
- class f extends u {
45
+ class c extends u {
46
46
  constructor(e, r, s) {
47
- const n = f.parseErrorLines(s), a = n.length > 0 ? ` at line(s): ${n.join(", ")}` : "";
47
+ const n = c.parseErrorLines(s), a = n.length > 0 ? ` at line(s): ${n.join(", ")}` : "";
48
48
  super(
49
49
  `${e} shader compilation failed${a}
50
50
 
@@ -68,8 +68,8 @@ ${s}`,
68
68
  for (const n of r) {
69
69
  let a;
70
70
  for (; (a = n.exec(e)) !== null; ) {
71
- const c = parseInt(a[1], 10);
72
- c > 0 && s.add(c);
71
+ const f = parseInt(a[1], 10);
72
+ f > 0 && s.add(f);
73
73
  }
74
74
  }
75
75
  return [...s].sort((n, a) => n - a);
@@ -103,9 +103,11 @@ class R {
103
103
  */
104
104
  start(t) {
105
105
  if (this.running) return this;
106
- this.callback = t, this.running = !0;
107
- const e = performance.now();
108
- return this.startTime = e, this.lastTime = e, this.rafId = requestAnimationFrame(this.loop), this;
106
+ if (this.callback = t, this.running = !0, this.frame === 0) {
107
+ const e = performance.now();
108
+ this.startTime = e, this.lastTime = e;
109
+ }
110
+ return this.rafId = requestAnimationFrame(this.loop), this;
109
111
  }
110
112
  /**
111
113
  * Stop the animation loop.
@@ -365,14 +367,14 @@ class h {
365
367
  compileShader(t, e) {
366
368
  const r = this.gl, s = t === "vertex" ? r.VERTEX_SHADER : r.FRAGMENT_SHADER, n = r.createShader(s);
367
369
  if (!n)
368
- throw new f(
370
+ throw new c(
369
371
  t,
370
372
  e,
371
373
  "Failed to create shader object"
372
374
  );
373
375
  if (r.shaderSource(n, e), r.compileShader(n), !r.getShaderParameter(n, r.COMPILE_STATUS)) {
374
- const c = r.getShaderInfoLog(n) || "Unknown error";
375
- throw r.deleteShader(n), new f(t, e, c);
376
+ const f = r.getShaderInfoLog(n) || "Unknown error";
377
+ throw r.deleteShader(n), new c(t, e, f);
376
378
  }
377
379
  return n;
378
380
  }
@@ -924,9 +926,12 @@ You can handle errors programmatically by providing an onError callback to suppr
924
926
  this.setViewport();
925
927
  }),
926
928
  // Visibility check on scroll
927
- l.on(document, "scroll", () => {
928
- this.options.pauseWhenHidden && (this.isInViewport() ? this.play() : this.pause());
929
- }),
929
+ (() => {
930
+ let t = !1;
931
+ return l.on(document, "scroll", (e) => {
932
+ this.options.pauseWhenHidden && (this.isInViewport() ? t && !this.isPlaying() && (this.play(), t = !1) : this.isPlaying() && (this.pause(), t = !0));
933
+ });
934
+ })(),
930
935
  // Mouse tracking
931
936
  l.on(document, "mousemove", (t) => {
932
937
  this.setMouse(t.clientX || t.pageX, t.clientY || t.pageY);
@@ -1102,6 +1107,6 @@ export {
1102
1107
  w as SandboxContextError,
1103
1108
  u as SandboxError,
1104
1109
  m as SandboxProgramError,
1105
- f as SandboxShaderCompilationError,
1110
+ c as SandboxShaderCompilationError,
1106
1111
  k as SandboxShaderVersionMismatchError
1107
1112
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rosalana/sandbox",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
4
  "description": "Lightweight WebGL wrapper for simple, beautiful shader effects",
5
5
  "keywords": [
6
6
  "webgl",