@rosalana/sandbox 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -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
@@ -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=c;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);
@@ -365,14 +365,14 @@ class h {
365
365
  compileShader(t, e) {
366
366
  const r = this.gl, s = t === "vertex" ? r.VERTEX_SHADER : r.FRAGMENT_SHADER, n = r.createShader(s);
367
367
  if (!n)
368
- throw new f(
368
+ throw new c(
369
369
  t,
370
370
  e,
371
371
  "Failed to create shader object"
372
372
  );
373
373
  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);
374
+ const f = r.getShaderInfoLog(n) || "Unknown error";
375
+ throw r.deleteShader(n), new c(t, e, f);
376
376
  }
377
377
  return n;
378
378
  }
@@ -924,9 +924,12 @@ You can handle errors programmatically by providing an onError callback to suppr
924
924
  this.setViewport();
925
925
  }),
926
926
  // Visibility check on scroll
927
- l.on(document, "scroll", () => {
928
- this.options.pauseWhenHidden && (this.isInViewport() ? this.play() : this.pause());
929
- }),
927
+ (() => {
928
+ let t = !1;
929
+ return l.on(document, "scroll", (e) => {
930
+ this.options.pauseWhenHidden && (this.isInViewport() ? t && !this.isPlaying() && (this.play(), t = !1) : this.isPlaying() && (this.pause(), t = !0));
931
+ });
932
+ })(),
930
933
  // Mouse tracking
931
934
  l.on(document, "mousemove", (t) => {
932
935
  this.setMouse(t.clientX || t.pageX, t.clientY || t.pageY);
@@ -1102,6 +1105,6 @@ export {
1102
1105
  w as SandboxContextError,
1103
1106
  u as SandboxError,
1104
1107
  m as SandboxProgramError,
1105
- f as SandboxShaderCompilationError,
1108
+ c as SandboxShaderCompilationError,
1106
1109
  k as SandboxShaderVersionMismatchError
1107
1110
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rosalana/sandbox",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "description": "Lightweight WebGL wrapper for simple, beautiful shader effects",
5
5
  "keywords": [
6
6
  "webgl",