shaderpad 1.0.0-beta.31 → 1.0.0-beta.33

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
@@ -399,27 +399,44 @@ const shader = new ShaderPad(fragmentShaderSrc, {
399
399
 
400
400
  **Uniforms:**
401
401
 
402
- | Uniform | Type | Description |
403
- | -------------------- | --------- | ---------------------------------------------------------- |
404
- | `u_maxFaces` | int | Maximum number of faces to detect |
405
- | `u_nFaces` | int | Current number of detected faces |
406
- | `u_faceLandmarksTex` | sampler2D | Raw landmark data texture (use `faceLandmark()` to access) |
407
- | `u_faceMask` | sampler2D | Face mask texture (R: mouth, G: face, B: eyes) |
402
+ | Uniform | Type | Description |
403
+ | -------------------- | --------- | --------------------------------------------------------------------------- |
404
+ | `u_maxFaces` | int | Maximum number of faces to detect |
405
+ | `u_nFaces` | int | Current number of detected faces |
406
+ | `u_faceLandmarksTex` | sampler2D | Raw landmark data texture (use `faceLandmark()` to access) |
407
+ | `u_faceMask` | sampler2D | Face mask texture (R: region type, G: confidence, B: normalized face index) |
408
408
 
409
409
  **Helper functions:**
410
410
 
411
- - `faceLandmark(int faceIndex, int landmarkIndex) -> vec4` - Returns landmark data as `vec4(x, y, z, visibility)`. Use `vec2(faceLandmark(...))` to get just the screen position.
412
- - `inFace(vec2 pos) -> float` - Returns face mask value at position (green channel)
413
- - `inEye(vec2 pos) -> float` - Returns eye mask value at position (blue channel)
414
- - `inMouth(vec2 pos) -> float` - Returns mouth mask value at position (red channel)
411
+ All region functions return `vec2(confidence, faceIndex)`. faceIndex is 0-indexed (-1 = no face).
415
412
 
416
- **Constants:**
413
+ - `faceLandmark(int faceIndex, int landmarkIndex) -> vec4` - Returns landmark data as `vec4(x, y, z, visibility)`. Use `vec2(faceLandmark(...))` to get just the screen position.
414
+ - `leftEyebrowAt(vec2 pos) -> vec2` - Returns `vec2(1.0, faceIndex)` if position is in left eyebrow, `vec2(0.0, -1.0)` otherwise.
415
+ - `rightEyebrowAt(vec2 pos) -> vec2` - Returns `vec2(1.0, faceIndex)` if position is in right eyebrow, `vec2(0.0, -1.0)` otherwise.
416
+ - `leftEyeAt(vec2 pos) -> vec2` - Returns `vec2(1.0, faceIndex)` if position is in left eye, `vec2(0.0, -1.0)` otherwise.
417
+ - `rightEyeAt(vec2 pos) -> vec2` - Returns `vec2(1.0, faceIndex)` if position is in right eye, `vec2(0.0, -1.0)` otherwise.
418
+ - `outerMouthAt(vec2 pos) -> vec2` - Returns `vec2(1.0, faceIndex)` if position is in outer mouth/lip region, `vec2(0.0, -1.0)` otherwise.
419
+ - `innerMouthAt(vec2 pos) -> vec2` - Returns `vec2(1.0, faceIndex)` if position is in inner mouth region, `vec2(0.0, -1.0)` otherwise.
420
+ - `faceOvalAt(vec2 pos) -> vec2` - Returns `vec2(1.0, faceIndex)` if position is in face oval contour, `vec2(0.0, -1.0)` otherwise.
421
+ - `faceAt(vec2 pos) -> vec2` - Returns `vec2(1.0, faceIndex)` if position is in face mesh or oval contour, `vec2(0.0, -1.0)` otherwise.
422
+ - `eyeAt(vec2 pos) -> vec2` - Returns `vec2(1.0, faceIndex)` if position is in either eye, `vec2(0.0, -1.0)` otherwise.
423
+ - `eyebrowAt(vec2 pos) -> vec2` - Returns `vec2(1.0, faceIndex)` if position is in either eyebrow, `vec2(0.0, -1.0)` otherwise.
424
+
425
+ **Convenience functions** (return `1.0` if true, `0.0` if false):
426
+
427
+ - `inFace(vec2 pos) -> float` - Returns `1.0` if position is in face mesh, `0.0` otherwise.
428
+ - `inEye(vec2 pos) -> float` - Returns `1.0` if position is in either eye, `0.0` otherwise.
429
+ - `inEyebrow(vec2 pos) -> float` - Returns `1.0` if position is in either eyebrow, `0.0` otherwise.
430
+ - `inMouth(vec2 pos) -> float` - Returns `1.0` if position is in inner mouth, `0.0` otherwise.
431
+ - `inLips(vec2 pos) -> float` - Returns `1.0` if position is in lips (outer mouth excluding inner), `0.0` otherwise.
432
+
433
+ **Landmark Constants:**
417
434
 
418
435
  - `FACE_LANDMARK_L_EYE_CENTER` - Left eye center landmark index
419
436
  - `FACE_LANDMARK_R_EYE_CENTER` - Right eye center landmark index
420
437
  - `FACE_LANDMARK_NOSE_TIP` - Nose tip landmark index
421
438
  - `FACE_LANDMARK_FACE_CENTER` - Face center landmark index (custom, calculated from all landmarks)
422
- - `FACE_LANDMARK_MOUTH_CENTER` - Mouth center landmark index (custom, calculated from inner lip landmarks)
439
+ - `FACE_LANDMARK_MOUTH_CENTER` - Mouth center landmark index (custom, calculated from inner mouth landmarks)
423
440
 
424
441
  **Example usage:**
425
442
 
@@ -427,14 +444,17 @@ const shader = new ShaderPad(fragmentShaderSrc, {
427
444
  // Get a specific landmark position.
428
445
  vec2 nosePos = vec2(faceLandmark(0, FACE_LANDMARK_NOSE_TIP));
429
446
 
430
- if (inMouth(v_uv) > 0.0) {
431
- // Position is inside a mouth.
432
- }
447
+ // Use in* convenience functions for simple boolean checks.
448
+ float eyeMask = inEye(v_uv);
433
449
 
434
- // Iterate through all faces and landmarks.
450
+ // Use faceLandmark or *At functions when you need to check a specific face index.
451
+ vec2 leftEye = leftEyeAt(v_uv);
435
452
  for (int i = 0; i < u_nFaces; ++i) {
436
- vec4 leftEye = faceLandmark(i, FACE_LANDMARK_L_EYE_CENTER);
437
- vec4 rightEye = faceLandmark(i, FACE_LANDMARK_R_EYE_CENTER);
453
+ vec4 leftEyeCenter = faceLandmark(i, FACE_LANDMARK_L_EYE_CENTER);
454
+ vec4 rightEyeCenter = faceLandmark(i, FACE_LANDMARK_R_EYE_CENTER);
455
+ if (leftEye.x > 0.0 && int(leftEye.y) == i) {
456
+ // Position is inside the left eye of face i.
457
+ }
438
458
  // ...
439
459
  }
440
460
  ```
@@ -458,17 +478,18 @@ const shader = new ShaderPad(fragmentShaderSrc, {
458
478
 
459
479
  **Uniforms:**
460
480
 
461
- | Uniform | Type | Description |
462
- | -------------------- | --------- | ----------------------------------------------------- |
463
- | `u_maxPoses` | int | Maximum number of poses to track |
464
- | `u_nPoses` | int | Current number of detected poses |
465
- | `u_poseLandmarksTex` | sampler2D | Raw landmark data texture (RGBA: x, y, z, visibility) |
466
- | `u_poseMask` | sampler2D | Pose mask texture (G: body) |
481
+ | Uniform | Type | Description |
482
+ | -------------------- | --------- | ----------------------------------------------------------------------------- |
483
+ | `u_maxPoses` | int | Maximum number of poses to track |
484
+ | `u_nPoses` | int | Current number of detected poses |
485
+ | `u_poseLandmarksTex` | sampler2D | Raw landmark data texture (RGBA: x, y, z, visibility) |
486
+ | `u_poseMask` | sampler2D | Pose mask texture (R: body detected, G: confidence, B: normalized pose index) |
467
487
 
468
488
  **Helper functions:**
469
489
 
470
490
  - `poseLandmark(int poseIndex, int landmarkIndex) -> vec4` - Returns landmark data as `vec4(x, y, z, visibility)`. Use `vec2(poseLandmark(...))` to get just the screen position.
471
- - `inBody(vec2 pos) -> float` - Returns body mask value at position (green channel)
491
+ - `poseAt(vec2 pos) -> vec2` - Returns `vec2(confidence, poseIndex)`. poseIndex is 0-indexed (-1 = no pose), confidence is the segmentation confidence.
492
+ - `inPose(vec2 pos) -> float` - Returns `1.0` if position is in any pose, `0.0` otherwise.
472
493
 
473
494
  **Constants:**
474
495
 
@@ -550,13 +571,15 @@ const shader = new ShaderPad(fragmentShaderSrc, {
550
571
  | -------------------- | --------- | ----------------------------------------------------- |
551
572
  | `u_maxHands` | int | Maximum number of hands to track |
552
573
  | `u_nHands` | int | Current number of detected hands |
553
- | `u_handLandmarksTex` | sampler2D | Raw landmark data texture (RGBA: x, y, z, visibility) |
574
+ | `u_handLandmarksTex` | sampler2D | Raw landmark data texture (RGBA: x, y, z, handedness) |
554
575
 
555
576
  **Helper functions:**
556
577
 
557
- - `handLandmark(int handIndex, int landmarkIndex) -> vec4` - Returns landmark data as `vec4(x, y, z, visibility)`. Use `vec2(handLandmark(...))` to get just the screen position.
578
+ - `handLandmark(int handIndex, int landmarkIndex) -> vec4` - Returns landmark data as `vec4(x, y, z, handedness)`. Use `vec2(handLandmark(...))` to get just the screen position. Handedness: 0.0 = left hand, 1.0 = right hand.
579
+ - `isRightHand(int handIndex) -> float` - Returns 1.0 if the hand is a right hand, 0.0 if left.
580
+ - `isLeftHand(int handIndex) -> float` - Returns 1.0 if the hand is a left hand, 0.0 if right.
558
581
 
559
- Use `handLandmark(int handIndex, int landmarkIndex)` in GLSL to retrieve a specific point. Landmark indices are:
582
+ **Landmark Indices:**
560
583
 
561
584
  | Index | Landmark | Index | Landmark |
562
585
  | ----- | ----------------- | ----- | ----------------- |
@@ -577,29 +600,81 @@ Use `handLandmark(int handIndex, int landmarkIndex)` in GLSL to retrieve a speci
577
600
  A minimal fragment shader loop looks like:
578
601
 
579
602
  ```glsl
580
- #define HAND_LANDMARK_WRIST 0
581
- #define HAND_LANDMARK_THUMB_TIP 4
582
- #define HAND_LANDMARK_INDEX_TIP 8
583
- #define HAND_LANDMARK_HAND_CENTER 21
603
+ #define WRIST 0
604
+ #define THUMB_TIP 4
605
+ #define INDEX_TIP 8
606
+ #define HAND_CENTER 21
607
+
584
608
  for (int i = 0; i < u_maxHands; ++i) {
585
609
  if (i >= u_nHands) break;
586
- vec2 wrist = vec2(handLandmark(i, HAND_LANDMARK_WRIST));
587
- vec2 thumbTip = vec2(handLandmark(i, HAND_LANDMARK_THUMB_TIP));
588
- vec2 indexTip = vec2(handLandmark(i, HAND_LANDMARK_INDEX_TIP));
589
- vec2 handCenter = vec2(handLandmark(i, HAND_LANDMARK_HAND_CENTER));
610
+ vec2 wrist = vec2(handLandmark(i, WRIST));
611
+ vec2 thumbTip = vec2(handLandmark(i, THUMB_TIP));
612
+ vec2 indexTip = vec2(handLandmark(i, INDEX_TIP));
613
+ vec2 handCenter = vec2(handLandmark(i, HAND_CENTER));
614
+
615
+ // Use handedness for coloring (0.0 = left/black, 1.0 = right/white).
616
+ vec3 handColor = vec3(isRightHand(i));
590
617
  // …
591
618
  }
592
619
  ```
593
620
 
594
621
  **Note:** The hands plugin requires `@mediapipe/tasks-vision` as a peer dependency.
595
622
 
623
+ #### segmenter
624
+
625
+ The `segmenter` plugin uses [MediaPipe Image Segmenter](https://ai.google.dev/edge/mediapipe/solutions/vision/image_segmenter) to segment objects in video or image textures. It supports models with multiple categories (e.g., background, hair, chair, dog…). By default, it uses the [hair segmentation model](https://ai.google.dev/edge/mediapipe/solutions/vision/image_segmenter#hair-model).
626
+
627
+ ```typescript
628
+ import ShaderPad from 'shaderpad';
629
+ import segmenter from 'shaderpad/plugins/segmenter';
630
+
631
+ const shader = new ShaderPad(fragmentShaderSrc, {
632
+ plugins: [
633
+ segmenter({
634
+ textureName: 'u_webcam',
635
+ options: {
636
+ modelPath:
637
+ 'https://storage.googleapis.com/mediapipe-models/image_segmenter/selfie_multiclass_256x256/float32/latest/selfie_multiclass_256x256.tflite',
638
+ outputCategoryMask: true,
639
+ },
640
+ }),
641
+ ],
642
+ });
643
+ ```
644
+
645
+ **Uniforms:**
646
+
647
+ | Uniform | Type | Description |
648
+ | ----------------- | --------- | ----------------------------------------------------------------------- |
649
+ | `u_segmentMask` | sampler2D | Segment mask texture (R: normalized category, G: confidence, B: unused) |
650
+ | `u_numCategories` | int | Number of segmentation categories (including background) |
651
+
652
+ **Helper functions:**
653
+
654
+ - `segmentAt(vec2 pos) -> vec2` - Returns `vec2(confidence, categoryIndex)`. categoryIndex is 0-indexed (-1 = background). confidence is the segmentation confidence (0-1).
655
+
656
+ **Example usage:**
657
+
658
+ ```glsl
659
+ vec2 segment = segmentAt(v_uv);
660
+ float confidence = segment.x; // Segmentation confidence
661
+ float category = segment.y; // Category index (0-indexed, -1 = background)
662
+
663
+ if (category >= 0.0) {
664
+ // Apply effect based on confidence.
665
+ color = mix(color, vec3(1.0, 0.0, 1.0), confidence);
666
+ }
667
+ ```
668
+
669
+ **Note:** The segmenter plugin requires `@mediapipe/tasks-vision` as a peer dependency.
670
+
596
671
  ## Contributing
597
672
 
598
673
  ### Running an example
599
674
 
600
675
  ```bash
601
676
  # Clone the repository.
602
- git clone https://github.com/your-username/shaderpad.git
677
+ git clone https://github.com/rileyjshaw/shaderpad.git
603
678
  cd shaderpad
604
679
 
605
680
  # Install dependencies and start the development server.
@@ -0,0 +1,11 @@
1
+ var T=`#version 300 es
2
+ in vec2 aPosition;
3
+ out vec2 v_uv;
4
+ void main() {
5
+ v_uv = aPosition * 0.5 + 0.5;
6
+ gl_Position = vec4(aPosition, 0.0, 1.0);
7
+ }
8
+ `,x=33.333333333333336,f=Symbol("u_history");function v(h,t){if(!t?.length)return h;let e=h.split(`
9
+ `),i=e.findLastIndex(s=>{let r=s.trimStart();return r.startsWith("precision ")||r.startsWith("#version ")})+1;return e.splice(i,0,...t),e.join(`
10
+ `)}function p(h){return h instanceof WebGLTexture?{width:0,height:0}:h instanceof HTMLVideoElement?{width:h.videoWidth,height:h.videoHeight}:h instanceof HTMLImageElement?{width:h.naturalWidth??h.width,height:h.naturalHeight??h.height}:{width:h.width,height:h.height}}function m(h){return typeof h=="symbol"?h.description??"":h}var d=class{isInternalCanvas=!1;isTouchDevice=!1;gl;fragmentShaderSrc;uniforms=new Map;textures=new Map;textureUnitPool;buffer=null;program=null;aPositionLocation=0;animationFrameId;resolutionObserver;resizeObserver;resizeTimeout=null;lastResizeTime=-1/0;eventListeners=new Map;frame=0;startTime=0;cursorPosition=[.5,.5];clickPosition=[.5,.5];isMouseDown=!1;canvas;onResize;hooks=new Map;historyDepth;debug;constructor(t,e={}){if(this.canvas=e.canvas||document.createElement("canvas"),!e.canvas){this.isInternalCanvas=!0;let s=this.canvas;s.style.position="fixed",s.style.inset="0",s.style.height="100dvh",s.style.width="100dvw",document.body.appendChild(s)}if(this.gl=this.canvas.getContext("webgl2",{antialias:!1}),!this.gl)throw new Error("WebGL2 not supported. Please use a browser that supports WebGL2.");this.textureUnitPool={free:[],next:0,max:this.gl.getParameter(this.gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS)},this.historyDepth=e.history??0,this.debug=e.debug??(typeof process<"u"&&!1),this.animationFrameId=null,this.resolutionObserver=new MutationObserver(()=>this.updateResolution()),this.resizeObserver=new ResizeObserver(()=>this.throttledHandleResize());let i=[];if(e.plugins){let s={gl:this.gl,uniforms:this.uniforms,textures:this.textures,canvas:this.canvas,reserveTextureUnit:this.reserveTextureUnit.bind(this),releaseTextureUnit:this.releaseTextureUnit.bind(this),injectGLSL:r=>{i.push(r)}};Object.defineProperty(s,"program",{get:()=>this.program,enumerable:!0,configurable:!0}),e.plugins.forEach(r=>r(this,s))}this.fragmentShaderSrc=v(t,i),this.init(),this.canvas instanceof HTMLCanvasElement&&this.addEventListeners()}registerHook(t,e){this.hooks.has(t)||this.hooks.set(t,[]),this.hooks.get(t).push(e)}init(){let t=T;if(this.program=this.gl.createProgram(),!this.program)throw new Error("Failed to create WebGL program");let e=this.createShader(this.gl.VERTEX_SHADER,t),i=this.createShader(this.gl.FRAGMENT_SHADER,this.fragmentShaderSrc);if(this.gl.attachShader(this.program,e),this.gl.attachShader(this.program,i),this.gl.linkProgram(this.program),this.gl.deleteShader(e),this.gl.deleteShader(i),!this.gl.getProgramParameter(this.program,this.gl.LINK_STATUS))throw console.error("Program link error:",this.gl.getProgramInfoLog(this.program)),this.gl.deleteProgram(this.program),new Error("Failed to link WebGL program");this.aPositionLocation=this.gl.getAttribLocation(this.program,"aPosition"),this.setupBuffer(),this.gl.useProgram(this.program),this.canvas instanceof HTMLCanvasElement&&(this.resolutionObserver.observe(this.canvas,{attributes:!0,attributeFilter:["width","height"]}),this.resizeObserver.observe(this.canvas)),this.isInternalCanvas||this.updateResolution(),this.initializeUniform("u_cursor","float",this.cursorPosition),this.initializeUniform("u_click","float",[...this.clickPosition,this.isMouseDown?1:0]),this.initializeUniform("u_time","float",0),this.initializeUniform("u_frame","int",0),this.historyDepth>0&&this._initializeTexture(f,this.canvas,{history:this.historyDepth}),this.hooks.get("init")?.forEach(s=>s.call(this))}createShader(t,e){let i=this.gl.createShader(t);if(this.gl.shaderSource(i,e),this.gl.compileShader(i),!this.gl.getShaderParameter(i,this.gl.COMPILE_STATUS))throw console.error("Shader compilation failed:",e),console.error(this.gl.getShaderInfoLog(i)),this.gl.deleteShader(i),new Error("Shader compilation failed");return i}setupBuffer(){let t=new Float32Array([-1,-1,1,-1,-1,1,-1,1,1,-1,1,1]);this.buffer=this.gl.createBuffer(),this.gl.bindBuffer(this.gl.ARRAY_BUFFER,this.buffer),this.gl.bufferData(this.gl.ARRAY_BUFFER,t,this.gl.STATIC_DRAW),this.gl.viewport(0,0,this.gl.drawingBufferWidth,this.gl.drawingBufferHeight),this.gl.enableVertexAttribArray(this.aPositionLocation),this.gl.vertexAttribPointer(this.aPositionLocation,2,this.gl.FLOAT,!1,0,0)}throttledHandleResize(){clearTimeout(this.resizeTimeout);let t=performance.now(),e=this.lastResizeTime+x-t;e<=0?(this.lastResizeTime=t,this.handleResize()):this.resizeTimeout=setTimeout(()=>this.throttledHandleResize(),e)}handleResize(){if(!(this.canvas instanceof HTMLCanvasElement))return;let t=window.devicePixelRatio||1,e=this.canvas.clientWidth*t,i=this.canvas.clientHeight*t;this.isInternalCanvas&&(this.canvas.width!==e||this.canvas.height!==i)&&(this.canvas.width=e,this.canvas.height=i),this.onResize?.(e,i)}addEventListeners(){let t=this.canvas,e=(s,r)=>{if(!this.uniforms.has("u_cursor"))return;let n=t.getBoundingClientRect();this.cursorPosition[0]=(s-n.left)/n.width,this.cursorPosition[1]=1-(r-n.top)/n.height,this.updateUniforms({u_cursor:this.cursorPosition})},i=(s,r,n)=>{if(this.uniforms.has("u_click")){if(this.isMouseDown=s,s){let a=t.getBoundingClientRect(),l=r,u=n;this.clickPosition[0]=(l-a.left)/a.width,this.clickPosition[1]=1-(u-a.top)/a.height}this.updateUniforms({u_click:[...this.clickPosition,this.isMouseDown?1:0]})}};this.eventListeners.set("mousemove",s=>{let r=s;this.isTouchDevice||e(r.clientX,r.clientY)}),this.eventListeners.set("mousedown",s=>{let r=s;this.isTouchDevice||r.button===0&&(this.isMouseDown=!0,i(!0,r.clientX,r.clientY))}),this.eventListeners.set("mouseup",s=>{let r=s;this.isTouchDevice||r.button===0&&i(!1)}),this.eventListeners.set("touchmove",s=>{let r=s;r.touches.length>0&&e(r.touches[0].clientX,r.touches[0].clientY)}),this.eventListeners.set("touchstart",s=>{let r=s;this.isTouchDevice=!0,r.touches.length>0&&(e(r.touches[0].clientX,r.touches[0].clientY),i(!0,r.touches[0].clientX,r.touches[0].clientY))}),this.eventListeners.set("touchend",s=>{s.touches.length===0&&i(!1)}),this.eventListeners.forEach((s,r)=>{t.addEventListener(r,s)})}updateResolution(){let t=[this.gl.drawingBufferWidth,this.gl.drawingBufferHeight];this.gl.viewport(0,0,...t),this.uniforms.has("u_resolution")?this.updateUniforms({u_resolution:t}):this.initializeUniform("u_resolution","float",t),this.hooks.get("updateResolution")?.forEach(e=>e.call(this))}reserveTextureUnit(t){let e=this.textures.get(t);if(e)return e.unitIndex;if(this.textureUnitPool.free.length>0)return this.textureUnitPool.free.pop();if(this.textureUnitPool.next>=this.textureUnitPool.max)throw new Error("Exceeded the available texture units for this device.");return this.textureUnitPool.next++}releaseTextureUnit(t){let e=this.textures.get(t);e&&this.textureUnitPool.free.push(e.unitIndex)}clearHistoryTextureLayers(t){if(!t.history)return;let e=t.options?.type??this.gl.UNSIGNED_BYTE,i=e===this.gl.FLOAT?new Float32Array(t.width*t.height*4):new Uint8Array(t.width*t.height*4);this.gl.activeTexture(this.gl.TEXTURE0+t.unitIndex),this.gl.bindTexture(this.gl.TEXTURE_2D_ARRAY,t.texture);for(let s=0;s<t.history.depth;++s)this.gl.texSubImage3D(this.gl.TEXTURE_2D_ARRAY,0,0,0,s,t.width,t.height,1,t.options?.format??this.gl.RGBA,e,i)}initializeUniform(t,e,i,s){let r=s?.arrayLength;if(this.uniforms.has(t))throw new Error(`${t} is already initialized.`);if(e!=="float"&&e!=="int")throw new Error(`Invalid uniform type: ${e}. Expected 'float' or 'int'.`);if(r&&!(Array.isArray(i)&&i.length===r))throw new Error(`${t} array length mismatch: must initialize with ${r} elements.`);let n=this.gl.getUniformLocation(this.program,t);if(!n&&r&&(n=this.gl.getUniformLocation(this.program,`${t}[0]`)),!n){this.log(`${t} not found in fragment shader. Skipping initialization.`);return}let a=r?i[0]:i,l=Array.isArray(a)?a.length:1;this.uniforms.set(t,{type:e,length:l,location:n,arrayLength:r});try{this.updateUniforms({[t]:i})}catch(u){throw this.uniforms.delete(t),u}this.hooks.get("initializeUniform")?.forEach(u=>u.call(this,...arguments))}log(...t){this.debug&&console.debug(...t)}updateUniforms(t,e){this.gl.useProgram(this.program),Object.entries(t).forEach(([i,s])=>{let r=this.uniforms.get(i);if(!r){this.log(`${i} not found in fragment shader. Skipping update.`);return}let n=`uniform${r.length}${r.type.charAt(0)}`;if(r.arrayLength){if(!Array.isArray(s))throw new Error(`${i} is an array, but the value passed to updateUniforms is not an array.`);let a=s.length;if(!a)return;if(a>r.arrayLength)throw new Error(`${i} received ${a} values, but maximum length is ${r.arrayLength}.`);if(s.some(o=>(Array.isArray(o)?o.length:1)!==r.length))throw new Error(`Tried to update ${i} with some elements that are not length ${r.length}.`);let l=new(r.type==="float"?Float32Array:Int32Array)(s.flat()),u=r.location;if(e?.startIndex){let o=this.gl.getUniformLocation(this.program,`${i}[${e.startIndex}]`);if(!o)throw new Error(`${i}[${e.startIndex}] not found in fragment shader. Did you pass an invalid startIndex?`);u=o}this.gl[n+"v"](r.location,l)}else{if(Array.isArray(s)||(s=[s]),s.length!==r.length)throw new Error(`Invalid uniform value length: ${s.length}. Expected ${r.length}.`);this.gl[n](r.location,...s)}}),this.hooks.get("updateUniforms")?.forEach(i=>i.call(this,...arguments))}createTexture(t,e,i){let{width:s,height:r}=e,n=e.history?.depth??0,a=this.gl.createTexture();if(!a)throw new Error("Failed to create texture");let l=i?.unitIndex;if(typeof l!="number")try{l=this.reserveTextureUnit(t)}catch(g){throw this.gl.deleteTexture(a),g}let u=n>0,o=u?this.gl.TEXTURE_2D_ARRAY:this.gl.TEXTURE_2D;if(this.gl.activeTexture(this.gl.TEXTURE0+l),this.gl.bindTexture(o,a),this.gl.texParameteri(o,this.gl.TEXTURE_WRAP_S,i?.wrapS??this.gl.CLAMP_TO_EDGE),this.gl.texParameteri(o,this.gl.TEXTURE_WRAP_T,i?.wrapT??this.gl.CLAMP_TO_EDGE),this.gl.texParameteri(o,this.gl.TEXTURE_MIN_FILTER,i?.minFilter??this.gl.LINEAR),this.gl.texParameteri(o,this.gl.TEXTURE_MAG_FILTER,i?.magFilter??this.gl.LINEAR),u){let g=i?.type??this.gl.UNSIGNED_BYTE,c=i?.internalFormat??(g===this.gl.FLOAT?this.gl.RGBA32F:this.gl.RGBA8);this.gl.texStorage3D(o,1,c,s,r,n)}return{texture:a,unitIndex:l}}_initializeTexture(t,e,i){if(this.textures.has(t))throw new Error(`Texture '${m(t)}' is already initialized.`);let{history:s=0,...r}=i??{},{width:n,height:a}=p(e);if(!n||!a)throw new Error("Texture source must have valid dimensions");let l={width:n,height:a};s>0&&(l.history={depth:s,writeIndex:0});let{texture:u,unitIndex:o}=this.createTexture(t,l,r),g={texture:u,unitIndex:o,...l,options:r};s>0&&(this.initializeUniform(`${m(t)}FrameOffset`,"int",0),this.clearHistoryTextureLayers(g)),this.textures.set(t,g),this.updateTexture(t,e);let c=this.gl.getUniformLocation(this.program,m(t));c&&this.gl.uniform1i(c,o)}initializeTexture(t,e,i){this._initializeTexture(t,e,i),this.hooks.get("initializeTexture")?.forEach(s=>s.call(this,...arguments))}updateTextures(t){this.hooks.get("updateTextures")?.forEach(e=>e.call(this,...arguments)),Object.entries(t).forEach(([e,i])=>{this.updateTexture(e,i)})}updateTexture(t,e){let i=this.textures.get(t);if(!i)throw new Error(`Texture '${m(t)}' is not initialized.`);if(e instanceof WebGLTexture){this.gl.activeTexture(this.gl.TEXTURE0+i.unitIndex),this.gl.bindTexture(this.gl.TEXTURE_2D,e);return}let{width:s,height:r}=p(e);if(!s||!r)return;let n="isPartial"in e&&e.isPartial;if(!n&&(i.width!==s||i.height!==r)){this.gl.deleteTexture(i.texture),i.width=s,i.height=r;let{texture:o}=this.createTexture(t,i,{...i.options,unitIndex:i.unitIndex});i.texture=o,i.history&&(i.history.writeIndex=0,this.clearHistoryTextureLayers(i))}let a="data"in e&&e.data,l=!a&&!i.options?.preserveY,u=this.gl.getParameter(this.gl.UNPACK_FLIP_Y_WEBGL);if(i.history){let o=t===f;this.gl.activeTexture(this.gl.TEXTURE0+i.unitIndex),this.gl.bindTexture(this.gl.TEXTURE_2D_ARRAY,i.texture),o?this.gl.copyTexSubImage3D(this.gl.TEXTURE_2D_ARRAY,0,0,0,i.history.writeIndex,0,0,s,r):(this.gl.pixelStorei(this.gl.UNPACK_FLIP_Y_WEBGL,l),this.gl.texSubImage3D(this.gl.TEXTURE_2D_ARRAY,0,0,0,i.history.writeIndex,s,r,1,i.options?.format??this.gl.RGBA,i.options?.type??this.gl.UNSIGNED_BYTE,e.data??e),this.gl.pixelStorei(this.gl.UNPACK_FLIP_Y_WEBGL,u));let g=`${m(t)}FrameOffset`;this.updateUniforms({[g]:i.history.writeIndex}),i.history.writeIndex=(i.history.writeIndex+1)%i.history.depth}else{this.gl.activeTexture(this.gl.TEXTURE0+i.unitIndex),this.gl.bindTexture(this.gl.TEXTURE_2D,i.texture),this.gl.pixelStorei(this.gl.UNPACK_FLIP_Y_WEBGL,l);let o=i.options?.format??this.gl.RGBA,g=i.options?.type??this.gl.UNSIGNED_BYTE;if(n)this.gl.texSubImage2D(this.gl.TEXTURE_2D,0,e.x??0,e.y??0,s,r,o,g,e.data);else{let c=i.options?.internalFormat??(a?g===this.gl.FLOAT?this.gl.RGBA32F:this.gl.RGBA8:this.gl.RGBA);this.gl.texImage2D(this.gl.TEXTURE_2D,0,c,s,r,0,o,g,e.data??e)}this.gl.pixelStorei(this.gl.UNPACK_FLIP_Y_WEBGL,u)}}draw(t=!0){let e=this.gl;e.useProgram(this.program),e.bindBuffer(e.ARRAY_BUFFER,this.buffer),e.vertexAttribPointer(this.aPositionLocation,2,e.FLOAT,!1,0,0),e.enableVertexAttribArray(this.aPositionLocation),e.viewport(0,0,e.drawingBufferWidth,e.drawingBufferHeight),t&&e.clear(e.COLOR_BUFFER_BIT),e.drawArrays(e.TRIANGLES,0,6)}step(t){this.uniforms.has("u_time")&&this.updateUniforms({u_time:t}),this.uniforms.has("u_frame")&&this.updateUniforms({u_frame:this.frame}),this.draw(),this.textures.get(f)&&this.updateTexture(f,this.canvas),this.hooks.get("step")?.forEach(e=>e.call(this,t,this.frame)),++this.frame}play(t){this.pause();let e=i=>{i=(i-this.startTime)/1e3,this.step(i),this.animationFrameId=requestAnimationFrame(e),t&&t(i,this.frame)};this.animationFrameId=requestAnimationFrame(e)}pause(){this.animationFrameId&&(cancelAnimationFrame(this.animationFrameId),this.animationFrameId=null)}reset(){this.frame=0,this.startTime=performance.now(),this.textures.forEach(t=>{t.history&&(t.history.writeIndex=0,this.clearHistoryTextureLayers(t))}),this.hooks.get("reset")?.forEach(t=>t.call(this))}destroy(){this.animationFrameId&&(cancelAnimationFrame(this.animationFrameId),this.animationFrameId=null),this.resolutionObserver.disconnect(),this.resizeObserver.disconnect(),this.canvas instanceof HTMLCanvasElement&&this.eventListeners.forEach((t,e)=>{this.canvas.removeEventListener(e,t)}),this.program&&this.gl.deleteProgram(this.program),this.textures.forEach(t=>{this.gl.deleteTexture(t.texture)}),this.textureUnitPool.free=[],this.textureUnitPool.next=0,this.buffer&&(this.gl.deleteBuffer(this.buffer),this.buffer=null),this.hooks.get("destroy")?.forEach(t=>t.call(this)),this.isInternalCanvas&&this.canvas instanceof HTMLCanvasElement&&this.canvas.remove()}},E=d;export{E as a};
11
+ //# sourceMappingURL=chunk-RKULNJXI.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["const DEFAULT_VERTEX_SHADER_SRC = `#version 300 es\nin vec2 aPosition;\nout vec2 v_uv;\nvoid main() {\n v_uv = aPosition * 0.5 + 0.5;\n gl_Position = vec4(aPosition, 0.0, 1.0);\n}\n`;\nconst RESIZE_THROTTLE_INTERVAL = 1000 / 30;\n\ninterface Uniform {\n\ttype: 'float' | 'int';\n\tlength: 1 | 2 | 3 | 4;\n\tlocation: WebGLUniformLocation;\n\tarrayLength?: number;\n}\n\nexport interface TextureOptions {\n\tinternalFormat?: number;\n\tformat?: number;\n\ttype?: number;\n\tminFilter?: number;\n\tmagFilter?: number;\n\twrapS?: number;\n\twrapT?: number;\n\tpreserveY?: boolean;\n}\n\ninterface Texture {\n\ttexture: WebGLTexture;\n\tunitIndex: number;\n\twidth: number;\n\theight: number;\n\thistory?: {\n\t\tdepth: number;\n\t\twriteIndex: number;\n\t};\n\toptions?: TextureOptions;\n}\n\nexport interface CustomTexture {\n\tdata: ArrayBufferView | null;\n\twidth: number;\n\theight: number;\n}\n\nexport interface PartialCustomTexture extends CustomTexture {\n\tisPartial?: boolean;\n\tx?: number;\n\ty?: number;\n}\n\nexport type TextureSource =\n\t| HTMLImageElement\n\t| HTMLVideoElement\n\t| HTMLCanvasElement\n\t| OffscreenCanvas\n\t| ImageBitmap\n\t| WebGLTexture\n\t| CustomTexture;\n\n// Custom textures allow partial updates starting from (x, y).\ntype UpdateTextureSource = Exclude<TextureSource, CustomTexture> | PartialCustomTexture;\n\nexport interface PluginContext {\n\tgl: WebGL2RenderingContext;\n\tuniforms: Map<string, Uniform>;\n\ttextures: Map<string | symbol, Texture>;\n\tget program(): WebGLProgram | null;\n\tcanvas: HTMLCanvasElement | OffscreenCanvas;\n\treserveTextureUnit: (name: string | symbol) => number;\n\treleaseTextureUnit: (name: string | symbol) => void;\n\tinjectGLSL: (code: string) => void;\n}\n\ntype Plugin = (shaderPad: ShaderPad, context: PluginContext) => void;\n\ntype LifecycleMethod =\n\t| 'init'\n\t| 'step'\n\t| 'destroy'\n\t| 'updateResolution'\n\t| 'reset'\n\t| 'initializeTexture'\n\t| 'updateTextures'\n\t| 'initializeUniform'\n\t| 'updateUniforms';\n\nexport interface Options {\n\tcanvas?: HTMLCanvasElement | OffscreenCanvas | null;\n\tplugins?: Plugin[];\n\thistory?: number;\n\tdebug?: boolean;\n}\n\ntype TextureUnitPool = {\n\tfree: number[];\n\tnext: number;\n\tmax: number;\n};\n\nconst HISTORY_TEXTURE_KEY = Symbol('u_history');\n\nfunction combineShaderCode(shader: string, injections: string[]): string {\n\tif (!injections?.length) return shader;\n\tconst lines = shader.split('\\n');\n\tconst insertAt =\n\t\tlines.findLastIndex(line => {\n\t\t\tconst trimmed = line.trimStart();\n\t\t\treturn trimmed.startsWith('precision ') || trimmed.startsWith('#version ');\n\t\t}) + 1;\n\tlines.splice(insertAt, 0, ...injections);\n\treturn lines.join('\\n');\n}\n\nfunction getSourceDimensions(source: TextureSource): { width: number; height: number } {\n\tif (source instanceof WebGLTexture) {\n\t\treturn { width: 0, height: 0 }; // Invalid - dimensions not readable.\n\t}\n\tif (source instanceof HTMLVideoElement) {\n\t\treturn { width: source.videoWidth, height: source.videoHeight };\n\t}\n\tif (source instanceof HTMLImageElement) {\n\t\treturn { width: source.naturalWidth ?? source.width, height: source.naturalHeight ?? source.height };\n\t}\n\t// CustomTexture, HTMLCanvasElement, OffscreenCanvas, ImageBitmap.\n\treturn { width: source.width, height: source.height };\n}\n\nfunction stringFrom(name: string | symbol) {\n\treturn typeof name === 'symbol' ? name.description ?? '' : name;\n}\n\nclass ShaderPad {\n\tprivate isInternalCanvas = false;\n\tprivate isTouchDevice = false;\n\tprivate gl: WebGL2RenderingContext;\n\tprivate fragmentShaderSrc: string;\n\tprivate uniforms: Map<string, Uniform> = new Map();\n\tprivate textures: Map<string | symbol, Texture> = new Map();\n\tprivate textureUnitPool: TextureUnitPool;\n\tprivate buffer: WebGLBuffer | null = null;\n\tprivate program: WebGLProgram | null = null;\n\tprivate aPositionLocation = 0;\n\tprivate animationFrameId: number | null;\n\tprivate resolutionObserver: MutationObserver;\n\tprivate resizeObserver: ResizeObserver;\n\tprivate resizeTimeout: ReturnType<typeof setTimeout> = null as unknown as ReturnType<typeof setTimeout>;\n\tprivate lastResizeTime = -Infinity;\n\tprivate eventListeners: Map<string, EventListener> = new Map();\n\tprivate frame = 0;\n\tprivate startTime = 0;\n\tprivate cursorPosition = [0.5, 0.5];\n\tprivate clickPosition = [0.5, 0.5];\n\tprivate isMouseDown = false;\n\tpublic canvas: HTMLCanvasElement | OffscreenCanvas;\n\tpublic onResize?: (width: number, height: number) => void;\n\tprivate hooks: Map<LifecycleMethod, Function[]> = new Map();\n\tprivate historyDepth: number;\n\tprivate debug: boolean;\n\n\tconstructor(fragmentShaderSrc: string, options: Options = {}) {\n\t\tthis.canvas = options.canvas || document.createElement('canvas');\n\t\tif (!options.canvas) {\n\t\t\tthis.isInternalCanvas = true;\n\t\t\tconst htmlCanvas = this.canvas as HTMLCanvasElement;\n\t\t\thtmlCanvas.style.position = 'fixed';\n\t\t\thtmlCanvas.style.inset = '0';\n\t\t\thtmlCanvas.style.height = '100dvh';\n\t\t\thtmlCanvas.style.width = '100dvw';\n\t\t\tdocument.body.appendChild(htmlCanvas);\n\t\t}\n\n\t\tthis.gl = this.canvas.getContext('webgl2', { antialias: false }) as WebGL2RenderingContext;\n\t\tif (!this.gl) {\n\t\t\tthrow new Error('WebGL2 not supported. Please use a browser that supports WebGL2.');\n\t\t}\n\n\t\tthis.textureUnitPool = {\n\t\t\tfree: [],\n\t\t\tnext: 0,\n\t\t\tmax: this.gl.getParameter(this.gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS),\n\t\t};\n\t\tthis.historyDepth = options.history ?? 0;\n\t\tthis.debug = options.debug ?? (typeof process !== 'undefined' && process.env.NODE_ENV !== 'production');\n\t\tthis.animationFrameId = null;\n\t\tthis.resolutionObserver = new MutationObserver(() => this.updateResolution());\n\t\tthis.resizeObserver = new ResizeObserver(() => this.throttledHandleResize());\n\n\t\tconst glslInjections: string[] = [];\n\t\tif (options.plugins) {\n\t\t\tconst context: PluginContext = {\n\t\t\t\tgl: this.gl,\n\t\t\t\tuniforms: this.uniforms,\n\t\t\t\ttextures: this.textures,\n\t\t\t\tcanvas: this.canvas,\n\t\t\t\treserveTextureUnit: this.reserveTextureUnit.bind(this),\n\t\t\t\treleaseTextureUnit: this.releaseTextureUnit.bind(this),\n\t\t\t\tinjectGLSL: (code: string) => {\n\t\t\t\t\tglslInjections.push(code);\n\t\t\t\t},\n\t\t\t} as PluginContext;\n\t\t\t// Define program as a getter so it always returns the current program.\n\t\t\tObject.defineProperty(context, 'program', {\n\t\t\t\tget: () => this.program,\n\t\t\t\tenumerable: true,\n\t\t\t\tconfigurable: true,\n\t\t\t});\n\t\t\toptions.plugins.forEach(plugin => plugin(this, context));\n\t\t}\n\n\t\tthis.fragmentShaderSrc = combineShaderCode(fragmentShaderSrc, glslInjections);\n\t\tthis.init();\n\t\tif (this.canvas instanceof HTMLCanvasElement) {\n\t\t\tthis.addEventListeners();\n\t\t}\n\t}\n\n\tregisterHook(name: LifecycleMethod, fn: Function) {\n\t\tif (!this.hooks.has(name)) {\n\t\t\tthis.hooks.set(name, []);\n\t\t}\n\t\tthis.hooks.get(name)!.push(fn);\n\t}\n\n\tprivate init() {\n\t\tconst vertexShaderSrc = DEFAULT_VERTEX_SHADER_SRC;\n\n\t\tthis.program = this.gl.createProgram();\n\t\tif (!this.program) {\n\t\t\tthrow new Error('Failed to create WebGL program');\n\t\t}\n\t\tconst vertexShader = this.createShader(this.gl.VERTEX_SHADER, vertexShaderSrc);\n\t\tconst fragmentShader = this.createShader(this.gl.FRAGMENT_SHADER, this.fragmentShaderSrc);\n\n\t\tthis.gl.attachShader(this.program, vertexShader);\n\t\tthis.gl.attachShader(this.program, fragmentShader);\n\t\tthis.gl.linkProgram(this.program);\n\t\tthis.gl.deleteShader(vertexShader);\n\t\tthis.gl.deleteShader(fragmentShader);\n\n\t\tif (!this.gl.getProgramParameter(this.program, this.gl.LINK_STATUS)) {\n\t\t\tconsole.error('Program link error:', this.gl.getProgramInfoLog(this.program));\n\t\t\tthis.gl.deleteProgram(this.program);\n\t\t\tthrow new Error('Failed to link WebGL program');\n\t\t}\n\n\t\tthis.aPositionLocation = this.gl.getAttribLocation(this.program, 'aPosition');\n\t\tthis.setupBuffer();\n\n\t\tthis.gl.useProgram(this.program);\n\n\t\tif (this.canvas instanceof HTMLCanvasElement) {\n\t\t\tthis.resolutionObserver.observe(this.canvas, { attributes: true, attributeFilter: ['width', 'height'] });\n\t\t\tthis.resizeObserver.observe(this.canvas);\n\t\t}\n\n\t\tif (!this.isInternalCanvas) {\n\t\t\tthis.updateResolution();\n\t\t}\n\t\tthis.initializeUniform('u_cursor', 'float', this.cursorPosition);\n\t\tthis.initializeUniform('u_click', 'float', [...this.clickPosition, this.isMouseDown ? 1.0 : 0.0]);\n\t\tthis.initializeUniform('u_time', 'float', 0);\n\t\tthis.initializeUniform('u_frame', 'int', 0);\n\t\tif (this.historyDepth > 0) {\n\t\t\tthis._initializeTexture(HISTORY_TEXTURE_KEY, this.canvas, { history: this.historyDepth });\n\t\t}\n\n\t\tthis.hooks.get('init')?.forEach(hook => hook.call(this));\n\t}\n\n\tprivate createShader(type: number, source: string): WebGLShader {\n\t\tconst shader = this.gl.createShader(type)!;\n\t\tthis.gl.shaderSource(shader, source);\n\t\tthis.gl.compileShader(shader);\n\t\tif (!this.gl.getShaderParameter(shader, this.gl.COMPILE_STATUS)) {\n\t\t\tconsole.error('Shader compilation failed:', source);\n\t\t\tconsole.error(this.gl.getShaderInfoLog(shader));\n\t\t\tthis.gl.deleteShader(shader);\n\t\t\tthrow new Error('Shader compilation failed');\n\t\t}\n\t\treturn shader;\n\t}\n\n\tprivate setupBuffer() {\n\t\tconst quadVertices = new Float32Array([-1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1]);\n\n\t\tthis.buffer = this.gl.createBuffer();\n\t\tthis.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.buffer);\n\t\tthis.gl.bufferData(this.gl.ARRAY_BUFFER, quadVertices, this.gl.STATIC_DRAW);\n\t\tthis.gl.viewport(0, 0, this.gl.drawingBufferWidth, this.gl.drawingBufferHeight);\n\t\tthis.gl.enableVertexAttribArray(this.aPositionLocation);\n\t\tthis.gl.vertexAttribPointer(this.aPositionLocation, 2, this.gl.FLOAT, false, 0, 0);\n\t}\n\n\tprivate throttledHandleResize() {\n\t\tclearTimeout(this.resizeTimeout);\n\t\tconst now = performance.now();\n\t\tconst timeUntilNextResize = this.lastResizeTime + RESIZE_THROTTLE_INTERVAL - now;\n\t\tif (timeUntilNextResize <= 0) {\n\t\t\tthis.lastResizeTime = now;\n\t\t\tthis.handleResize();\n\t\t} else {\n\t\t\tthis.resizeTimeout = setTimeout(() => this.throttledHandleResize(), timeUntilNextResize);\n\t\t}\n\t}\n\n\tprivate handleResize() {\n\t\tif (!(this.canvas instanceof HTMLCanvasElement)) return;\n\t\tconst pixelRatio = window.devicePixelRatio || 1;\n\t\tconst width = this.canvas.clientWidth * pixelRatio;\n\t\tconst height = this.canvas.clientHeight * pixelRatio;\n\t\tif (this.isInternalCanvas && (this.canvas.width !== width || this.canvas.height !== height)) {\n\t\t\tthis.canvas.width = width;\n\t\t\tthis.canvas.height = height;\n\t\t}\n\t\tthis.onResize?.(width, height);\n\t}\n\n\tprivate addEventListeners() {\n\t\tconst htmlCanvas = this.canvas as HTMLCanvasElement;\n\t\tconst updateCursor = (x: number, y: number) => {\n\t\t\tif (!this.uniforms.has('u_cursor')) return;\n\t\t\tconst rect = htmlCanvas.getBoundingClientRect();\n\t\t\tthis.cursorPosition[0] = (x - rect.left) / rect.width;\n\t\t\tthis.cursorPosition[1] = 1 - (y - rect.top) / rect.height; // Flip Y for WebGL\n\t\t\tthis.updateUniforms({ u_cursor: this.cursorPosition });\n\t\t};\n\n\t\tconst updateClick = (isMouseDown: boolean, x?: number, y?: number) => {\n\t\t\tif (!this.uniforms.has('u_click')) return;\n\t\t\tthis.isMouseDown = isMouseDown;\n\t\t\tif (isMouseDown) {\n\t\t\t\tconst rect = htmlCanvas.getBoundingClientRect();\n\t\t\t\tconst xVal = x as number;\n\t\t\t\tconst yVal = y as number;\n\t\t\t\tthis.clickPosition[0] = (xVal - rect.left) / rect.width;\n\t\t\t\tthis.clickPosition[1] = 1 - (yVal - rect.top) / rect.height; // Flip Y for WebGL\n\t\t\t}\n\t\t\tthis.updateUniforms({ u_click: [...this.clickPosition, this.isMouseDown ? 1.0 : 0.0] });\n\t\t};\n\n\t\tthis.eventListeners.set('mousemove', event => {\n\t\t\tconst mouseEvent = event as MouseEvent;\n\t\t\tif (!this.isTouchDevice) {\n\t\t\t\tupdateCursor(mouseEvent.clientX, mouseEvent.clientY);\n\t\t\t}\n\t\t});\n\n\t\tthis.eventListeners.set('mousedown', event => {\n\t\t\tconst mouseEvent = event as MouseEvent;\n\t\t\tif (!this.isTouchDevice) {\n\t\t\t\tif (mouseEvent.button === 0) {\n\t\t\t\t\tthis.isMouseDown = true;\n\t\t\t\t\tupdateClick(true, mouseEvent.clientX, mouseEvent.clientY);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\tthis.eventListeners.set('mouseup', event => {\n\t\t\tconst mouseEvent = event as MouseEvent;\n\t\t\tif (!this.isTouchDevice) {\n\t\t\t\tif (mouseEvent.button === 0) {\n\t\t\t\t\tupdateClick(false);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\tthis.eventListeners.set('touchmove', event => {\n\t\t\tconst touchEvent = event as TouchEvent;\n\t\t\tif (touchEvent.touches.length > 0) {\n\t\t\t\tupdateCursor(touchEvent.touches[0].clientX, touchEvent.touches[0].clientY);\n\t\t\t}\n\t\t});\n\n\t\tthis.eventListeners.set('touchstart', event => {\n\t\t\tconst touchEvent = event as TouchEvent;\n\t\t\tthis.isTouchDevice = true;\n\t\t\tif (touchEvent.touches.length > 0) {\n\t\t\t\tupdateCursor(touchEvent.touches[0].clientX, touchEvent.touches[0].clientY);\n\t\t\t\tupdateClick(true, touchEvent.touches[0].clientX, touchEvent.touches[0].clientY);\n\t\t\t}\n\t\t});\n\n\t\tthis.eventListeners.set('touchend', event => {\n\t\t\tconst touchEvent = event as TouchEvent;\n\t\t\tif (touchEvent.touches.length === 0) {\n\t\t\t\tupdateClick(false);\n\t\t\t}\n\t\t});\n\n\t\tthis.eventListeners.forEach((listener, event) => {\n\t\t\thtmlCanvas.addEventListener(event, listener);\n\t\t});\n\t}\n\n\tprivate updateResolution() {\n\t\tconst resolution: [number, number] = [this.gl.drawingBufferWidth, this.gl.drawingBufferHeight];\n\t\tthis.gl.viewport(0, 0, ...resolution);\n\t\tif (this.uniforms.has('u_resolution')) {\n\t\t\tthis.updateUniforms({ u_resolution: resolution });\n\t\t} else {\n\t\t\tthis.initializeUniform('u_resolution', 'float', resolution);\n\t\t}\n\t\tthis.hooks.get('updateResolution')?.forEach(hook => hook.call(this));\n\t}\n\n\tprivate reserveTextureUnit(name: string | symbol) {\n\t\tconst existing = this.textures.get(name);\n\t\tif (existing) return existing.unitIndex;\n\t\tif (this.textureUnitPool.free.length > 0) return this.textureUnitPool.free.pop()!;\n\t\tif (this.textureUnitPool.next >= this.textureUnitPool.max) {\n\t\t\tthrow new Error('Exceeded the available texture units for this device.');\n\t\t}\n\t\treturn this.textureUnitPool.next++;\n\t}\n\n\tprivate releaseTextureUnit(name: string | symbol) {\n\t\tconst existing = this.textures.get(name);\n\t\tif (existing) {\n\t\t\tthis.textureUnitPool.free.push(existing.unitIndex);\n\t\t}\n\t}\n\n\tprivate clearHistoryTextureLayers(textureInfo: Texture): void {\n\t\tif (!textureInfo.history) return;\n\n\t\tconst type = textureInfo.options?.type ?? this.gl.UNSIGNED_BYTE;\n\t\tconst transparent =\n\t\t\ttype === this.gl.FLOAT\n\t\t\t\t? new Float32Array(textureInfo.width * textureInfo.height * 4)\n\t\t\t\t: new Uint8Array(textureInfo.width * textureInfo.height * 4);\n\t\tthis.gl.activeTexture(this.gl.TEXTURE0 + textureInfo.unitIndex);\n\t\tthis.gl.bindTexture(this.gl.TEXTURE_2D_ARRAY, textureInfo.texture);\n\t\tfor (let layer = 0; layer < textureInfo.history.depth; ++layer) {\n\t\t\tthis.gl.texSubImage3D(\n\t\t\t\tthis.gl.TEXTURE_2D_ARRAY,\n\t\t\t\t0,\n\t\t\t\t0,\n\t\t\t\t0,\n\t\t\t\tlayer,\n\t\t\t\ttextureInfo.width,\n\t\t\t\ttextureInfo.height,\n\t\t\t\t1,\n\t\t\t\ttextureInfo.options?.format ?? this.gl.RGBA,\n\t\t\t\ttype,\n\t\t\t\ttransparent\n\t\t\t);\n\t\t}\n\t}\n\n\tinitializeUniform(\n\t\tname: string,\n\t\ttype: 'float' | 'int',\n\t\tvalue: number | number[] | (number | number[])[],\n\t\toptions?: { arrayLength?: number }\n\t) {\n\t\tconst arrayLength = options?.arrayLength;\n\t\tif (this.uniforms.has(name)) {\n\t\t\tthrow new Error(`${name} is already initialized.`);\n\t\t}\n\t\tif (type !== 'float' && type !== 'int') {\n\t\t\tthrow new Error(`Invalid uniform type: ${type}. Expected 'float' or 'int'.`);\n\t\t}\n\t\tif (arrayLength && !(Array.isArray(value) && value.length === arrayLength)) {\n\t\t\tthrow new Error(`${name} array length mismatch: must initialize with ${arrayLength} elements.`);\n\t\t}\n\n\t\tlet location = this.gl.getUniformLocation(this.program!, name);\n\t\tif (!location && arrayLength) {\n\t\t\tlocation = this.gl.getUniformLocation(this.program!, `${name}[0]`);\n\t\t}\n\t\tif (!location) {\n\t\t\tthis.log(`${name} not found in fragment shader. Skipping initialization.`);\n\t\t\treturn;\n\t\t}\n\n\t\tconst probeValue = arrayLength ? (value as number[] | number[][])[0] : value;\n\t\tconst length = Array.isArray(probeValue) ? (probeValue.length as 1 | 2 | 3 | 4) : 1;\n\t\tthis.uniforms.set(name, { type, length, location, arrayLength });\n\n\t\ttry {\n\t\t\tthis.updateUniforms({ [name]: value });\n\t\t} catch (error) {\n\t\t\tthis.uniforms.delete(name);\n\t\t\tthrow error;\n\t\t}\n\t\tthis.hooks.get('initializeUniform')?.forEach(hook => hook.call(this, ...arguments));\n\t}\n\n\tprivate log(...args: any[]) {\n\t\tif (this.debug) console.debug(...args);\n\t}\n\n\tupdateUniforms(\n\t\tupdates: Record<string, number | number[] | (number | number[])[]>,\n\t\toptions?: { startIndex?: number }\n\t) {\n\t\tthis.gl.useProgram(this.program);\n\t\tObject.entries(updates).forEach(([name, value]) => {\n\t\t\tconst uniform = this.uniforms.get(name);\n\t\t\tif (!uniform) {\n\t\t\t\tthis.log(`${name} not found in fragment shader. Skipping update.`);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tlet glFunctionName = `uniform${uniform.length}${uniform.type.charAt(0)}`; // e.g. uniform1f, uniform3i…\n\t\t\tif (uniform.arrayLength) {\n\t\t\t\tif (!Array.isArray(value)) {\n\t\t\t\t\tthrow new Error(`${name} is an array, but the value passed to updateUniforms is not an array.`);\n\t\t\t\t}\n\t\t\t\tconst nValues = value.length;\n\t\t\t\tif (!nValues) return;\n\t\t\t\tif (nValues > uniform.arrayLength) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`${name} received ${nValues} values, but maximum length is ${uniform.arrayLength}.`\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tif (value.some(item => (Array.isArray(item) ? item.length : 1) !== uniform.length)) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`Tried to update ${name} with some elements that are not length ${uniform.length}.`\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tconst typedArray = new (uniform.type === 'float' ? Float32Array : Int32Array)(value.flat());\n\t\t\t\tlet location = uniform.location;\n\t\t\t\tif (options?.startIndex) {\n\t\t\t\t\tconst newLocation = this.gl.getUniformLocation(this.program!, `${name}[${options.startIndex}]`);\n\t\t\t\t\tif (!newLocation) {\n\t\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\t`${name}[${options.startIndex}] not found in fragment shader. Did you pass an invalid startIndex?`\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tlocation = newLocation;\n\t\t\t\t}\n\t\t\t\t(this.gl as any)[glFunctionName + 'v'](uniform.location, typedArray);\n\t\t\t} else {\n\t\t\t\tif (!Array.isArray(value)) value = [value];\n\t\t\t\tif (value.length !== uniform.length) {\n\t\t\t\t\tthrow new Error(`Invalid uniform value length: ${value.length}. Expected ${uniform.length}.`);\n\t\t\t\t}\n\t\t\t\t(this.gl as any)[glFunctionName](uniform.location, ...value);\n\t\t\t}\n\t\t});\n\t\tthis.hooks.get('updateUniforms')?.forEach(hook => hook.call(this, ...arguments));\n\t}\n\n\tprivate createTexture(\n\t\tname: string | symbol,\n\t\ttextureInfo: Pick<Texture, 'width' | 'height' | 'history'>,\n\t\toptions?: TextureOptions & { unitIndex?: number }\n\t) {\n\t\tconst { width, height } = textureInfo;\n\t\tconst historyDepth = textureInfo.history?.depth ?? 0;\n\n\t\tconst texture = this.gl.createTexture();\n\t\tif (!texture) {\n\t\t\tthrow new Error('Failed to create texture');\n\t\t}\n\n\t\tlet unitIndex = options?.unitIndex;\n\t\tif (typeof unitIndex !== 'number') {\n\t\t\ttry {\n\t\t\t\tunitIndex = this.reserveTextureUnit(name);\n\t\t\t} catch (error) {\n\t\t\t\tthis.gl.deleteTexture(texture);\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}\n\n\t\tconst hasHistory = historyDepth > 0;\n\t\tconst textureTarget = hasHistory ? this.gl.TEXTURE_2D_ARRAY : this.gl.TEXTURE_2D;\n\n\t\tthis.gl.activeTexture(this.gl.TEXTURE0 + unitIndex);\n\t\tthis.gl.bindTexture(textureTarget, texture);\n\t\tthis.gl.texParameteri(textureTarget, this.gl.TEXTURE_WRAP_S, options?.wrapS ?? this.gl.CLAMP_TO_EDGE);\n\t\tthis.gl.texParameteri(textureTarget, this.gl.TEXTURE_WRAP_T, options?.wrapT ?? this.gl.CLAMP_TO_EDGE);\n\t\tthis.gl.texParameteri(textureTarget, this.gl.TEXTURE_MIN_FILTER, options?.minFilter ?? this.gl.LINEAR);\n\t\tthis.gl.texParameteri(textureTarget, this.gl.TEXTURE_MAG_FILTER, options?.magFilter ?? this.gl.LINEAR);\n\t\tif (hasHistory) {\n\t\t\tconst type = options?.type ?? this.gl.UNSIGNED_BYTE;\n\t\t\tconst internalFormat =\n\t\t\t\toptions?.internalFormat ?? (type === this.gl.FLOAT ? this.gl.RGBA32F : this.gl.RGBA8);\n\t\t\tthis.gl.texStorage3D(textureTarget, 1, internalFormat, width, height, historyDepth);\n\t\t}\n\t\treturn { texture, unitIndex };\n\t}\n\n\tprivate _initializeTexture(\n\t\tname: string | symbol,\n\t\tsource: TextureSource,\n\t\toptions?: TextureOptions & { history?: number }\n\t) {\n\t\tif (this.textures.has(name)) {\n\t\t\tthrow new Error(`Texture '${stringFrom(name)}' is already initialized.`);\n\t\t}\n\n\t\tconst { history: historyDepth = 0, ...textureOptions } = options ?? {};\n\t\tconst { width, height } = getSourceDimensions(source);\n\t\tif (!width || !height) {\n\t\t\tthrow new Error(`Texture source must have valid dimensions`);\n\t\t}\n\t\tconst textureInfo: Pick<Texture, 'width' | 'height' | 'history'> = { width, height };\n\t\tif (historyDepth > 0) {\n\t\t\ttextureInfo.history = { depth: historyDepth, writeIndex: 0 };\n\t\t}\n\t\tconst { texture, unitIndex } = this.createTexture(name, textureInfo, textureOptions);\n\t\tconst completeTextureInfo: Texture = { texture, unitIndex, ...textureInfo, options: textureOptions };\n\t\tif (historyDepth > 0) {\n\t\t\tthis.initializeUniform(`${stringFrom(name)}FrameOffset`, 'int', 0);\n\t\t\tthis.clearHistoryTextureLayers(completeTextureInfo);\n\t\t}\n\t\tthis.textures.set(name, completeTextureInfo);\n\t\tthis.updateTexture(name, source);\n\n\t\t// Set a uniform to access the texture in the fragment shader.\n\t\tconst uSampler = this.gl.getUniformLocation(this.program!, stringFrom(name));\n\t\tif (uSampler) {\n\t\t\tthis.gl.uniform1i(uSampler, unitIndex);\n\t\t}\n\t}\n\n\tinitializeTexture(name: string, source: TextureSource, options?: TextureOptions & { history?: number }) {\n\t\tthis._initializeTexture(name, source, options);\n\t\tthis.hooks.get('initializeTexture')?.forEach(hook => hook.call(this, ...arguments));\n\t}\n\n\tupdateTextures(updates: Record<string, UpdateTextureSource>) {\n\t\tthis.hooks.get('updateTextures')?.forEach(hook => hook.call(this, ...arguments));\n\t\tObject.entries(updates).forEach(([name, source]) => {\n\t\t\tthis.updateTexture(name, source);\n\t\t});\n\t}\n\n\tprivate updateTexture(name: string | symbol, source: UpdateTextureSource) {\n\t\tconst info = this.textures.get(name);\n\t\tif (!info) throw new Error(`Texture '${stringFrom(name)}' is not initialized.`);\n\n\t\tif (source instanceof WebGLTexture) {\n\t\t\tthis.gl.activeTexture(this.gl.TEXTURE0 + info.unitIndex);\n\t\t\tthis.gl.bindTexture(this.gl.TEXTURE_2D, source);\n\t\t\treturn;\n\t\t}\n\n\t\t// If dimensions changed, recreate the texture with new dimensions.\n\t\tconst { width, height } = getSourceDimensions(source);\n\t\tif (!width || !height) return;\n\n\t\tconst isPartial = 'isPartial' in source && source.isPartial;\n\t\tif (!isPartial && (info.width !== width || info.height !== height)) {\n\t\t\tthis.gl.deleteTexture(info.texture);\n\t\t\tinfo.width = width;\n\t\t\tinfo.height = height;\n\t\t\tconst { texture } = this.createTexture(name, info, { ...info.options, unitIndex: info.unitIndex });\n\t\t\tinfo.texture = texture;\n\t\t\tif (info.history) {\n\t\t\t\tinfo.history.writeIndex = 0;\n\t\t\t\tthis.clearHistoryTextureLayers(info);\n\t\t\t}\n\t\t}\n\n\t\t// UNPACK_FLIP_Y_WEBGL only works for DOM element sources, not typed arrays.\n\t\tconst isTypedArray = 'data' in source && source.data;\n\t\tconst shouldFlipY = !isTypedArray && !info.options?.preserveY;\n\t\tconst previousFlipY = this.gl.getParameter(this.gl.UNPACK_FLIP_Y_WEBGL);\n\n\t\tif (info.history) {\n\t\t\tconst isFramebufferHistory = name === HISTORY_TEXTURE_KEY;\n\n\t\t\tthis.gl.activeTexture(this.gl.TEXTURE0 + info.unitIndex);\n\t\t\tthis.gl.bindTexture(this.gl.TEXTURE_2D_ARRAY, info.texture);\n\t\t\tif (isFramebufferHistory) {\n\t\t\t\tthis.gl.copyTexSubImage3D(\n\t\t\t\t\tthis.gl.TEXTURE_2D_ARRAY,\n\t\t\t\t\t0,\n\t\t\t\t\t0,\n\t\t\t\t\t0,\n\t\t\t\t\tinfo.history.writeIndex,\n\t\t\t\t\t0,\n\t\t\t\t\t0,\n\t\t\t\t\twidth,\n\t\t\t\t\theight\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tthis.gl.pixelStorei(this.gl.UNPACK_FLIP_Y_WEBGL, shouldFlipY);\n\t\t\t\tthis.gl.texSubImage3D(\n\t\t\t\t\tthis.gl.TEXTURE_2D_ARRAY,\n\t\t\t\t\t0,\n\t\t\t\t\t0,\n\t\t\t\t\t0,\n\t\t\t\t\tinfo.history.writeIndex,\n\t\t\t\t\twidth,\n\t\t\t\t\theight,\n\t\t\t\t\t1,\n\t\t\t\t\tinfo.options?.format ?? this.gl.RGBA,\n\t\t\t\t\tinfo.options?.type ?? this.gl.UNSIGNED_BYTE,\n\t\t\t\t\t((source as PartialCustomTexture).data ?? (source as Exclude<TextureSource, CustomTexture>)) as any\n\t\t\t\t);\n\t\t\t\tthis.gl.pixelStorei(this.gl.UNPACK_FLIP_Y_WEBGL, previousFlipY);\n\t\t\t}\n\t\t\tconst frameOffsetUniformName = `${stringFrom(name)}FrameOffset`;\n\t\t\tthis.updateUniforms({ [frameOffsetUniformName]: info.history.writeIndex });\n\t\t\tinfo.history.writeIndex = (info.history.writeIndex + 1) % info.history.depth;\n\t\t} else {\n\t\t\tthis.gl.activeTexture(this.gl.TEXTURE0 + info.unitIndex);\n\t\t\tthis.gl.bindTexture(this.gl.TEXTURE_2D, info.texture);\n\t\t\tthis.gl.pixelStorei(this.gl.UNPACK_FLIP_Y_WEBGL, shouldFlipY);\n\n\t\t\tconst format = info.options?.format ?? this.gl.RGBA;\n\t\t\tconst type = info.options?.type ?? this.gl.UNSIGNED_BYTE;\n\n\t\t\tif (isPartial) {\n\t\t\t\tthis.gl.texSubImage2D(\n\t\t\t\t\tthis.gl.TEXTURE_2D,\n\t\t\t\t\t0,\n\t\t\t\t\tsource.x ?? 0,\n\t\t\t\t\tsource.y ?? 0,\n\t\t\t\t\twidth,\n\t\t\t\t\theight,\n\t\t\t\t\tformat,\n\t\t\t\t\ttype,\n\t\t\t\t\tsource.data\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tconst internalFormat =\n\t\t\t\t\tinfo.options?.internalFormat ??\n\t\t\t\t\t(isTypedArray ? (type === this.gl.FLOAT ? this.gl.RGBA32F : this.gl.RGBA8) : this.gl.RGBA);\n\t\t\t\tthis.gl.texImage2D(\n\t\t\t\t\tthis.gl.TEXTURE_2D,\n\t\t\t\t\t0,\n\t\t\t\t\tinternalFormat,\n\t\t\t\t\twidth,\n\t\t\t\t\theight,\n\t\t\t\t\t0,\n\t\t\t\t\tformat,\n\t\t\t\t\ttype,\n\t\t\t\t\t((source as PartialCustomTexture).data ?? (source as Exclude<TextureSource, CustomTexture>)) as any\n\t\t\t\t);\n\t\t\t}\n\t\t\tthis.gl.pixelStorei(this.gl.UNPACK_FLIP_Y_WEBGL, previousFlipY);\n\t\t}\n\t}\n\n\tdraw(clear = true) {\n\t\tconst gl = this.gl;\n\t\tgl.useProgram(this.program);\n\t\tgl.bindBuffer(gl.ARRAY_BUFFER, this.buffer);\n\t\tgl.vertexAttribPointer(this.aPositionLocation, 2, gl.FLOAT, false, 0, 0);\n\t\tgl.enableVertexAttribArray(this.aPositionLocation);\n\t\tgl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);\n\t\tif (clear) gl.clear(gl.COLOR_BUFFER_BIT);\n\t\tgl.drawArrays(gl.TRIANGLES, 0, 6);\n\t}\n\n\tstep(time: number) {\n\t\tif (this.uniforms.has('u_time')) {\n\t\t\tthis.updateUniforms({ u_time: time });\n\t\t}\n\t\tif (this.uniforms.has('u_frame')) {\n\t\t\tthis.updateUniforms({ u_frame: this.frame });\n\t\t}\n\n\t\tthis.draw();\n\n\t\tif (this.textures.get(HISTORY_TEXTURE_KEY)) {\n\t\t\tthis.updateTexture(HISTORY_TEXTURE_KEY, this.canvas);\n\t\t}\n\t\tthis.hooks.get('step')?.forEach(hook => hook.call(this, time, this.frame));\n\t\t++this.frame;\n\t}\n\n\tplay(callback?: (time: number, frame: number) => void) {\n\t\tthis.pause(); // Prevent double play.\n\t\tconst loop = (time: number) => {\n\t\t\ttime = (time - this.startTime) / 1000; // Convert from milliseconds to seconds.\n\t\t\tthis.step(time);\n\t\t\tthis.animationFrameId = requestAnimationFrame(loop);\n\t\t\tif (callback) callback(time, this.frame);\n\t\t};\n\t\tthis.animationFrameId = requestAnimationFrame(loop);\n\t}\n\n\tpause() {\n\t\tif (this.animationFrameId) {\n\t\t\tcancelAnimationFrame(this.animationFrameId);\n\t\t\tthis.animationFrameId = null;\n\t\t}\n\t}\n\n\treset() {\n\t\tthis.frame = 0;\n\t\tthis.startTime = performance.now();\n\t\tthis.textures.forEach(texture => {\n\t\t\tif (texture.history) {\n\t\t\t\ttexture.history.writeIndex = 0;\n\t\t\t\tthis.clearHistoryTextureLayers(texture);\n\t\t\t}\n\t\t});\n\t\tthis.hooks.get('reset')?.forEach(hook => hook.call(this));\n\t}\n\n\tdestroy() {\n\t\tif (this.animationFrameId) {\n\t\t\tcancelAnimationFrame(this.animationFrameId);\n\t\t\tthis.animationFrameId = null;\n\t\t}\n\n\t\tthis.resolutionObserver.disconnect();\n\t\tthis.resizeObserver.disconnect();\n\t\tif (this.canvas instanceof HTMLCanvasElement) {\n\t\t\tthis.eventListeners.forEach((listener, event) => {\n\t\t\t\tthis.canvas.removeEventListener(event, listener);\n\t\t\t});\n\t\t}\n\n\t\tif (this.program) {\n\t\t\tthis.gl.deleteProgram(this.program);\n\t\t}\n\n\t\tthis.textures.forEach(texture => {\n\t\t\tthis.gl.deleteTexture(texture.texture);\n\t\t});\n\t\tthis.textureUnitPool.free = [];\n\t\tthis.textureUnitPool.next = 0;\n\n\t\tif (this.buffer) {\n\t\t\tthis.gl.deleteBuffer(this.buffer);\n\t\t\tthis.buffer = null;\n\t\t}\n\n\t\tthis.hooks.get('destroy')?.forEach(hook => hook.call(this));\n\n\t\tif (this.isInternalCanvas && this.canvas instanceof HTMLCanvasElement) {\n\t\t\tthis.canvas.remove();\n\t\t}\n\t}\n}\n\nexport default ShaderPad;\n"],"mappings":"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","names":["DEFAULT_VERTEX_SHADER_SRC","RESIZE_THROTTLE_INTERVAL","HISTORY_TEXTURE_KEY","combineShaderCode","shader","injections","lines","insertAt","line","trimmed","getSourceDimensions","source","stringFrom","name","ShaderPad","fragmentShaderSrc","options","htmlCanvas","glslInjections","context","code","plugin","fn","vertexShaderSrc","vertexShader","fragmentShader","hook","type","quadVertices","now","timeUntilNextResize","pixelRatio","width","height","updateCursor","x","y","rect","updateClick","isMouseDown","xVal","yVal","event","mouseEvent","touchEvent","listener","resolution","existing","textureInfo","transparent","layer","value","arrayLength","location","probeValue","length","error","args","updates","uniform","glFunctionName","nValues","item","typedArray","newLocation","historyDepth","texture","unitIndex","hasHistory","textureTarget","internalFormat","textureOptions","completeTextureInfo","uSampler","info","isPartial","isTypedArray","shouldFlipY","previousFlipY","isFramebufferHistory","frameOffsetUniformName","format","clear","gl","time","callback","loop","index_default"]}
package/dist/index.d.mts CHANGED
@@ -35,14 +35,14 @@ interface PartialCustomTexture extends CustomTexture {
35
35
  x?: number;
36
36
  y?: number;
37
37
  }
38
- type TextureSource = HTMLImageElement | HTMLVideoElement | HTMLCanvasElement | CustomTexture;
38
+ type TextureSource = HTMLImageElement | HTMLVideoElement | HTMLCanvasElement | OffscreenCanvas | ImageBitmap | WebGLTexture | CustomTexture;
39
39
  type UpdateTextureSource = Exclude<TextureSource, CustomTexture> | PartialCustomTexture;
40
40
  interface PluginContext {
41
41
  gl: WebGL2RenderingContext;
42
42
  uniforms: Map<string, Uniform>;
43
43
  textures: Map<string | symbol, Texture>;
44
44
  get program(): WebGLProgram | null;
45
- canvas: HTMLCanvasElement;
45
+ canvas: HTMLCanvasElement | OffscreenCanvas;
46
46
  reserveTextureUnit: (name: string | symbol) => number;
47
47
  releaseTextureUnit: (name: string | symbol) => void;
48
48
  injectGLSL: (code: string) => void;
@@ -50,7 +50,7 @@ interface PluginContext {
50
50
  type Plugin = (shaderPad: ShaderPad, context: PluginContext) => void;
51
51
  type LifecycleMethod = 'init' | 'step' | 'destroy' | 'updateResolution' | 'reset' | 'initializeTexture' | 'updateTextures' | 'initializeUniform' | 'updateUniforms';
52
52
  interface Options {
53
- canvas?: HTMLCanvasElement | null;
53
+ canvas?: HTMLCanvasElement | OffscreenCanvas | null;
54
54
  plugins?: Plugin[];
55
55
  history?: number;
56
56
  debug?: boolean;
@@ -65,6 +65,7 @@ declare class ShaderPad {
65
65
  private textureUnitPool;
66
66
  private buffer;
67
67
  private program;
68
+ private aPositionLocation;
68
69
  private animationFrameId;
69
70
  private resolutionObserver;
70
71
  private resizeObserver;
@@ -76,7 +77,7 @@ declare class ShaderPad {
76
77
  private cursorPosition;
77
78
  private clickPosition;
78
79
  private isMouseDown;
79
- canvas: HTMLCanvasElement;
80
+ canvas: HTMLCanvasElement | OffscreenCanvas;
80
81
  onResize?: (width: number, height: number) => void;
81
82
  private hooks;
82
83
  private historyDepth;
@@ -107,7 +108,7 @@ declare class ShaderPad {
107
108
  }): void;
108
109
  updateTextures(updates: Record<string, UpdateTextureSource>): void;
109
110
  private updateTexture;
110
- draw(): void;
111
+ draw(clear?: boolean): void;
111
112
  step(time: number): void;
112
113
  play(callback?: (time: number, frame: number) => void): void;
113
114
  pause(): void;
package/dist/index.d.ts CHANGED
@@ -35,14 +35,14 @@ interface PartialCustomTexture extends CustomTexture {
35
35
  x?: number;
36
36
  y?: number;
37
37
  }
38
- type TextureSource = HTMLImageElement | HTMLVideoElement | HTMLCanvasElement | CustomTexture;
38
+ type TextureSource = HTMLImageElement | HTMLVideoElement | HTMLCanvasElement | OffscreenCanvas | ImageBitmap | WebGLTexture | CustomTexture;
39
39
  type UpdateTextureSource = Exclude<TextureSource, CustomTexture> | PartialCustomTexture;
40
40
  interface PluginContext {
41
41
  gl: WebGL2RenderingContext;
42
42
  uniforms: Map<string, Uniform>;
43
43
  textures: Map<string | symbol, Texture>;
44
44
  get program(): WebGLProgram | null;
45
- canvas: HTMLCanvasElement;
45
+ canvas: HTMLCanvasElement | OffscreenCanvas;
46
46
  reserveTextureUnit: (name: string | symbol) => number;
47
47
  releaseTextureUnit: (name: string | symbol) => void;
48
48
  injectGLSL: (code: string) => void;
@@ -50,7 +50,7 @@ interface PluginContext {
50
50
  type Plugin = (shaderPad: ShaderPad, context: PluginContext) => void;
51
51
  type LifecycleMethod = 'init' | 'step' | 'destroy' | 'updateResolution' | 'reset' | 'initializeTexture' | 'updateTextures' | 'initializeUniform' | 'updateUniforms';
52
52
  interface Options {
53
- canvas?: HTMLCanvasElement | null;
53
+ canvas?: HTMLCanvasElement | OffscreenCanvas | null;
54
54
  plugins?: Plugin[];
55
55
  history?: number;
56
56
  debug?: boolean;
@@ -65,6 +65,7 @@ declare class ShaderPad {
65
65
  private textureUnitPool;
66
66
  private buffer;
67
67
  private program;
68
+ private aPositionLocation;
68
69
  private animationFrameId;
69
70
  private resolutionObserver;
70
71
  private resizeObserver;
@@ -76,7 +77,7 @@ declare class ShaderPad {
76
77
  private cursorPosition;
77
78
  private clickPosition;
78
79
  private isMouseDown;
79
- canvas: HTMLCanvasElement;
80
+ canvas: HTMLCanvasElement | OffscreenCanvas;
80
81
  onResize?: (width: number, height: number) => void;
81
82
  private hooks;
82
83
  private historyDepth;
@@ -107,7 +108,7 @@ declare class ShaderPad {
107
108
  }): void;
108
109
  updateTextures(updates: Record<string, UpdateTextureSource>): void;
109
110
  private updateTexture;
110
- draw(): void;
111
+ draw(clear?: boolean): void;
111
112
  step(time: number): void;
112
113
  play(callback?: (time: number, frame: number) => void): void;
113
114
  pause(): void;
package/dist/index.js CHANGED
@@ -1,11 +1,11 @@
1
- "use strict";var f=Object.defineProperty;var T=Object.getOwnPropertyDescriptor;var v=Object.getOwnPropertyNames;var b=Object.prototype.hasOwnProperty;var E=(n,t)=>{for(var i in t)f(n,i,{get:t[i],enumerable:!0})},y=(n,t,i,e)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of v(t))!b.call(n,r)&&r!==i&&f(n,r,{get:()=>t[r],enumerable:!(e=T(t,r))||e.enumerable});return n};var w=n=>y(f({},"__esModule",{value:!0}),n);var A={};E(A,{default:()=>_});module.exports=w(A);var R=`#version 300 es
1
+ "use strict";var d=Object.defineProperty;var x=Object.getOwnPropertyDescriptor;var v=Object.getOwnPropertyNames;var E=Object.prototype.hasOwnProperty;var b=(n,t)=>{for(var e in t)d(n,e,{get:t[e],enumerable:!0})},y=(n,t,e,i)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of v(t))!E.call(n,r)&&r!==e&&d(n,r,{get:()=>t[r],enumerable:!(i=x(t,r))||i.enumerable});return n};var w=n=>y(d({},"__esModule",{value:!0}),n);var P={};b(P,{default:()=>_});module.exports=w(P);var L=`#version 300 es
2
2
  in vec2 aPosition;
3
3
  out vec2 v_uv;
4
4
  void main() {
5
5
  v_uv = aPosition * 0.5 + 0.5;
6
6
  gl_Position = vec4(aPosition, 0.0, 1.0);
7
7
  }
8
- `,U=33.333333333333336,d=Symbol("u_history");function L(n,t){if(!t?.length)return n;let i=n.split(`
9
- `),e=i.findLastIndex(r=>{let s=r.trimStart();return s.startsWith("precision ")||s.startsWith("#version ")})+1;return i.splice(e,0,...t),i.join(`
10
- `)}function x(n){if("data"in n)return{width:n.width,height:n.height};if(n instanceof HTMLVideoElement)return{width:n.videoWidth,height:n.videoHeight};if(n instanceof HTMLCanvasElement){let t=n.getContext("webgl2");return t?{width:t.drawingBufferWidth,height:t.drawingBufferHeight}:{width:n.width,height:n.height}}return{width:n.naturalWidth??n.width,height:n.naturalHeight??n.height}}function c(n){return typeof n=="symbol"?n.description??"":n}var p=class{isInternalCanvas=!1;isTouchDevice=!1;gl;fragmentShaderSrc;uniforms=new Map;textures=new Map;textureUnitPool;buffer=null;program=null;animationFrameId;resolutionObserver;resizeObserver;resizeTimeout=null;lastResizeTime=-1/0;eventListeners=new Map;frame=0;startTime=0;cursorPosition=[.5,.5];clickPosition=[.5,.5];isMouseDown=!1;canvas;onResize;hooks=new Map;historyDepth;debug;constructor(t,i={}){if(this.canvas=i.canvas||document.createElement("canvas"),i.canvas||(this.isInternalCanvas=!0,this.canvas.style.position="fixed",this.canvas.style.inset="0",this.canvas.style.height="100dvh",this.canvas.style.width="100dvw",document.body.appendChild(this.canvas)),this.gl=this.canvas.getContext("webgl2",{antialias:!1}),!this.gl)throw new Error("WebGL2 not supported. Please use a browser that supports WebGL2.");this.textureUnitPool={free:[],next:0,max:this.gl.getParameter(this.gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS)},this.historyDepth=i.history??0,this.debug=i.debug??(typeof process<"u"&&!1),this.animationFrameId=null,this.resolutionObserver=new MutationObserver(()=>this.updateResolution()),this.resizeObserver=new ResizeObserver(()=>this.throttledHandleResize());let e=[];if(i.plugins){let r={gl:this.gl,uniforms:this.uniforms,textures:this.textures,canvas:this.canvas,reserveTextureUnit:this.reserveTextureUnit.bind(this),releaseTextureUnit:this.releaseTextureUnit.bind(this),injectGLSL:s=>{e.push(s)}};Object.defineProperty(r,"program",{get:()=>this.program,enumerable:!0,configurable:!0}),i.plugins.forEach(s=>s(this,r))}this.fragmentShaderSrc=L(t,e),this.init(),this.addEventListeners()}registerHook(t,i){this.hooks.has(t)||this.hooks.set(t,[]),this.hooks.get(t).push(i)}init(){let t=R;if(this.program=this.gl.createProgram(),!this.program)throw new Error("Failed to create WebGL program");let i=this.createShader(this.gl.VERTEX_SHADER,t),e=this.createShader(this.gl.FRAGMENT_SHADER,this.fragmentShaderSrc);if(this.gl.attachShader(this.program,i),this.gl.attachShader(this.program,e),this.gl.linkProgram(this.program),this.gl.deleteShader(i),this.gl.deleteShader(e),!this.gl.getProgramParameter(this.program,this.gl.LINK_STATUS))throw console.error("Program link error:",this.gl.getProgramInfoLog(this.program)),this.gl.deleteProgram(this.program),new Error("Failed to link WebGL program");let r=this.gl.getAttribLocation(this.program,"aPosition");this.setupBuffer(r),this.gl.useProgram(this.program),this.resolutionObserver.observe(this.canvas,{attributes:!0,attributeFilter:["width","height"]}),this.resizeObserver.observe(this.canvas),this.isInternalCanvas||this.updateResolution(),this.initializeUniform("u_cursor","float",this.cursorPosition),this.initializeUniform("u_click","float",[...this.clickPosition,this.isMouseDown?1:0]),this.initializeUniform("u_time","float",0),this.initializeUniform("u_frame","int",0),this.historyDepth>0&&this._initializeTexture(d,this.canvas,{history:this.historyDepth}),this.hooks.get("init")?.forEach(s=>s.call(this))}createShader(t,i){let e=this.gl.createShader(t);if(this.gl.shaderSource(e,i),this.gl.compileShader(e),!this.gl.getShaderParameter(e,this.gl.COMPILE_STATUS))throw console.error("Shader compilation failed:",i),console.error(this.gl.getShaderInfoLog(e)),this.gl.deleteShader(e),new Error("Shader compilation failed");return e}setupBuffer(t){let i=new Float32Array([-1,-1,1,-1,-1,1,-1,1,1,-1,1,1]);this.buffer=this.gl.createBuffer(),this.gl.bindBuffer(this.gl.ARRAY_BUFFER,this.buffer),this.gl.bufferData(this.gl.ARRAY_BUFFER,i,this.gl.STATIC_DRAW),this.gl.viewport(0,0,this.gl.drawingBufferWidth,this.gl.drawingBufferHeight),this.gl.enableVertexAttribArray(t),this.gl.vertexAttribPointer(t,2,this.gl.FLOAT,!1,0,0)}throttledHandleResize(){clearTimeout(this.resizeTimeout);let t=performance.now(),i=this.lastResizeTime+U-t;i<=0?(this.lastResizeTime=t,this.handleResize()):this.resizeTimeout=setTimeout(()=>this.throttledHandleResize(),i)}handleResize(){let t=window.devicePixelRatio||1,i=this.canvas.clientWidth*t,e=this.canvas.clientHeight*t;this.isInternalCanvas&&(this.canvas.width!==i||this.canvas.height!==e)&&(this.canvas.width=i,this.canvas.height=e),this.onResize?.(i,e)}addEventListeners(){let t=(e,r)=>{if(!this.uniforms.has("u_cursor"))return;let s=this.canvas.getBoundingClientRect();this.cursorPosition[0]=(e-s.left)/s.width,this.cursorPosition[1]=1-(r-s.top)/s.height,this.updateUniforms({u_cursor:this.cursorPosition})},i=(e,r,s)=>{if(this.uniforms.has("u_click")){if(this.isMouseDown=e,e){let o=this.canvas.getBoundingClientRect(),h=r,a=s;this.clickPosition[0]=(h-o.left)/o.width,this.clickPosition[1]=1-(a-o.top)/o.height}this.updateUniforms({u_click:[...this.clickPosition,this.isMouseDown?1:0]})}};this.eventListeners.set("mousemove",e=>{let r=e;this.isTouchDevice||t(r.clientX,r.clientY)}),this.eventListeners.set("mousedown",e=>{let r=e;this.isTouchDevice||r.button===0&&(this.isMouseDown=!0,i(!0,r.clientX,r.clientY))}),this.eventListeners.set("mouseup",e=>{let r=e;this.isTouchDevice||r.button===0&&i(!1)}),this.eventListeners.set("touchmove",e=>{let r=e;r.touches.length>0&&t(r.touches[0].clientX,r.touches[0].clientY)}),this.eventListeners.set("touchstart",e=>{let r=e;this.isTouchDevice=!0,r.touches.length>0&&(t(r.touches[0].clientX,r.touches[0].clientY),i(!0,r.touches[0].clientX,r.touches[0].clientY))}),this.eventListeners.set("touchend",e=>{e.touches.length===0&&i(!1)}),this.eventListeners.forEach((e,r)=>{this.canvas.addEventListener(r,e)})}updateResolution(){let t=[this.gl.drawingBufferWidth,this.gl.drawingBufferHeight];this.gl.viewport(0,0,...t),this.uniforms.has("u_resolution")?this.updateUniforms({u_resolution:t}):this.initializeUniform("u_resolution","float",t),this.hooks.get("updateResolution")?.forEach(i=>i.call(this))}reserveTextureUnit(t){let i=this.textures.get(t);if(i)return i.unitIndex;if(this.textureUnitPool.free.length>0)return this.textureUnitPool.free.pop();if(this.textureUnitPool.next>=this.textureUnitPool.max)throw new Error("Exceeded the available texture units for this device.");return this.textureUnitPool.next++}releaseTextureUnit(t){let i=this.textures.get(t);i&&this.textureUnitPool.free.push(i.unitIndex)}clearHistoryTextureLayers(t){if(!t.history)return;let i=t.options?.type??this.gl.UNSIGNED_BYTE,e=i===this.gl.FLOAT?new Float32Array(t.width*t.height*4):new Uint8Array(t.width*t.height*4);this.gl.activeTexture(this.gl.TEXTURE0+t.unitIndex),this.gl.bindTexture(this.gl.TEXTURE_2D_ARRAY,t.texture);for(let r=0;r<t.history.depth;++r)this.gl.texSubImage3D(this.gl.TEXTURE_2D_ARRAY,0,0,0,r,t.width,t.height,1,t.options?.format??this.gl.RGBA,i,e)}initializeUniform(t,i,e,r){let s=r?.arrayLength;if(this.uniforms.has(t))throw new Error(`${t} is already initialized.`);if(i!=="float"&&i!=="int")throw new Error(`Invalid uniform type: ${i}. Expected 'float' or 'int'.`);if(s&&!(Array.isArray(e)&&e.length===s))throw new Error(`${t} array length mismatch: must initialize with ${s} elements.`);let o=this.gl.getUniformLocation(this.program,t);if(!o&&s&&(o=this.gl.getUniformLocation(this.program,`${t}[0]`)),!o){this.log(`${t} not found in fragment shader. Skipping initialization.`);return}let h=s?e[0]:e,a=Array.isArray(h)?h.length:1;this.uniforms.set(t,{type:i,length:a,location:o,arrayLength:s});try{this.updateUniforms({[t]:e})}catch(u){throw this.uniforms.delete(t),u}this.hooks.get("initializeUniform")?.forEach(u=>u.call(this,...arguments))}log(...t){this.debug&&console.debug(...t)}updateUniforms(t,i){Object.entries(t).forEach(([e,r])=>{let s=this.uniforms.get(e);if(!s){this.log(`${e} not found in fragment shader. Skipping update.`);return}let o=`uniform${s.length}${s.type.charAt(0)}`;if(s.arrayLength){if(!Array.isArray(r))throw new Error(`${e} is an array, but the value passed to updateUniforms is not an array.`);let h=r.length;if(!h)return;if(h>s.arrayLength)throw new Error(`${e} received ${h} values, but maximum length is ${s.arrayLength}.`);if(r.some(l=>(Array.isArray(l)?l.length:1)!==s.length))throw new Error(`Tried to update ${e} with some elements that are not length ${s.length}.`);let a=new(s.type==="float"?Float32Array:Int32Array)(r.flat()),u=s.location;if(i?.startIndex){let l=this.gl.getUniformLocation(this.program,`${e}[${i.startIndex}]`);if(!l)throw new Error(`${e}[${i.startIndex}] not found in fragment shader. Did you pass an invalid startIndex?`);u=l}this.gl[o+"v"](s.location,a)}else{if(Array.isArray(r)||(r=[r]),r.length!==s.length)throw new Error(`Invalid uniform value length: ${r.length}. Expected ${s.length}.`);this.gl[o](s.location,...r)}}),this.hooks.get("updateUniforms")?.forEach(e=>e.call(this,...arguments))}createTexture(t,i,e){let{width:r,height:s}=i,o=i.history?.depth??0,h=this.gl.createTexture();if(!h)throw new Error("Failed to create texture");let a=e?.unitIndex;if(typeof a!="number")try{a=this.reserveTextureUnit(t)}catch(g){throw this.gl.deleteTexture(h),g}let u=o>0,l=u?this.gl.TEXTURE_2D_ARRAY:this.gl.TEXTURE_2D;if(this.gl.activeTexture(this.gl.TEXTURE0+a),this.gl.bindTexture(l,h),this.gl.texParameteri(l,this.gl.TEXTURE_WRAP_S,e?.wrapS??this.gl.CLAMP_TO_EDGE),this.gl.texParameteri(l,this.gl.TEXTURE_WRAP_T,e?.wrapT??this.gl.CLAMP_TO_EDGE),this.gl.texParameteri(l,this.gl.TEXTURE_MIN_FILTER,e?.minFilter??this.gl.LINEAR),this.gl.texParameteri(l,this.gl.TEXTURE_MAG_FILTER,e?.magFilter??this.gl.LINEAR),u){let g=e?.type??this.gl.UNSIGNED_BYTE,m=e?.internalFormat??(g===this.gl.FLOAT?this.gl.RGBA32F:this.gl.RGBA8);this.gl.texStorage3D(l,1,m,r,s,o)}return{texture:h,unitIndex:a}}_initializeTexture(t,i,e){if(this.textures.has(t))throw new Error(`Texture '${c(t)}' is already initialized.`);let{history:r=0,...s}=e??{},{width:o,height:h}=x(i);if(!o||!h)throw new Error("Texture source must have valid dimensions");let a={width:o,height:h};r>0&&(a.history={depth:r,writeIndex:0});let{texture:u,unitIndex:l}=this.createTexture(t,a,s),g={texture:u,unitIndex:l,...a,options:s};r>0&&(this.initializeUniform(`${c(t)}FrameOffset`,"int",0),this.clearHistoryTextureLayers(g)),this.textures.set(t,g),this.updateTexture(t,i);let m=this.gl.getUniformLocation(this.program,c(t));m&&this.gl.uniform1i(m,l)}initializeTexture(t,i,e){this._initializeTexture(t,i,e),this.hooks.get("initializeTexture")?.forEach(r=>r.call(this,...arguments))}updateTextures(t){this.hooks.get("updateTextures")?.forEach(i=>i.call(this,...arguments)),Object.entries(t).forEach(([i,e])=>{this.updateTexture(i,e)})}updateTexture(t,i){let e=this.textures.get(t);if(!e)throw new Error(`Texture '${c(t)}' is not initialized.`);let{width:r,height:s}=x(i);if(!r||!s)return;let o="isPartial"in i&&i.isPartial;if(!o&&(e.width!==r||e.height!==s)){this.gl.deleteTexture(e.texture),e.width=r,e.height=s;let{texture:a}=this.createTexture(t,e,{...e.options,unitIndex:e.unitIndex});e.texture=a,e.history&&(e.history.writeIndex=0,this.clearHistoryTextureLayers(e))}let h=!e.options?.preserveY;if(e.history){let a=t===d;this.gl.activeTexture(this.gl.TEXTURE0+e.unitIndex),this.gl.bindTexture(this.gl.TEXTURE_2D_ARRAY,e.texture),a?(this.gl.pixelStorei(this.gl.UNPACK_FLIP_Y_WEBGL,!1),this.gl.copyTexSubImage3D(this.gl.TEXTURE_2D_ARRAY,0,0,0,e.history.writeIndex,0,0,r,s)):(this.gl.pixelStorei(this.gl.UNPACK_FLIP_Y_WEBGL,h),this.gl.texSubImage3D(this.gl.TEXTURE_2D_ARRAY,0,0,0,e.history.writeIndex,r,s,1,e.options?.format??this.gl.RGBA,e.options?.type??this.gl.UNSIGNED_BYTE,i.data??i));let u=`${c(t)}FrameOffset`;this.updateUniforms({[u]:e.history.writeIndex}),e.history.writeIndex=(e.history.writeIndex+1)%e.history.depth}else{this.gl.activeTexture(this.gl.TEXTURE0+e.unitIndex),this.gl.bindTexture(this.gl.TEXTURE_2D,e.texture),this.gl.pixelStorei(this.gl.UNPACK_FLIP_Y_WEBGL,h);let a=e.options?.format??this.gl.RGBA,u=e.options?.type??this.gl.UNSIGNED_BYTE;if(o)this.gl.texSubImage2D(this.gl.TEXTURE_2D,0,i.x??0,i.y??0,r,s,a,u,i.data);else{let l="data"in i&&i.data,g=e.options?.internalFormat??(l?u===this.gl.FLOAT?this.gl.RGBA32F:this.gl.RGBA8:this.gl.RGBA);this.gl.texImage2D(this.gl.TEXTURE_2D,0,g,r,s,0,a,u,i.data??i)}}}draw(){let t=this.gl;t.clear(t.COLOR_BUFFER_BIT),t.drawArrays(t.TRIANGLES,0,6)}step(t){this.uniforms.has("u_time")&&this.updateUniforms({u_time:t}),this.uniforms.has("u_frame")&&this.updateUniforms({u_frame:this.frame}),this.draw(),this.textures.get(d)&&this.updateTexture(d,this.canvas),this.hooks.get("step")?.forEach(i=>i.call(this,t,this.frame)),++this.frame}play(t){this.pause();let i=e=>{e=(e-this.startTime)/1e3,this.step(e),this.animationFrameId=requestAnimationFrame(i),t&&t(e,this.frame)};this.animationFrameId=requestAnimationFrame(i)}pause(){this.animationFrameId&&(cancelAnimationFrame(this.animationFrameId),this.animationFrameId=null)}reset(){this.frame=0,this.startTime=performance.now(),this.textures.forEach(t=>{t.history&&(t.history.writeIndex=0,this.clearHistoryTextureLayers(t))}),this.hooks.get("reset")?.forEach(t=>t.call(this))}destroy(){this.animationFrameId&&(cancelAnimationFrame(this.animationFrameId),this.animationFrameId=null),this.resolutionObserver.disconnect(),this.resizeObserver.disconnect(),this.eventListeners.forEach((t,i)=>{this.canvas.removeEventListener(i,t)}),this.program&&this.gl.deleteProgram(this.program),this.textures.forEach(t=>{this.gl.deleteTexture(t.texture)}),this.textureUnitPool.free=[],this.textureUnitPool.next=0,this.buffer&&(this.gl.deleteBuffer(this.buffer),this.buffer=null),this.hooks.get("destroy")?.forEach(t=>t.call(this)),this.isInternalCanvas&&this.canvas.remove()}},_=p;
8
+ `,R=33.333333333333336,f=Symbol("u_history");function U(n,t){if(!t?.length)return n;let e=n.split(`
9
+ `),i=e.findLastIndex(r=>{let s=r.trimStart();return s.startsWith("precision ")||s.startsWith("#version ")})+1;return e.splice(i,0,...t),e.join(`
10
+ `)}function T(n){return n instanceof WebGLTexture?{width:0,height:0}:n instanceof HTMLVideoElement?{width:n.videoWidth,height:n.videoHeight}:n instanceof HTMLImageElement?{width:n.naturalWidth??n.width,height:n.naturalHeight??n.height}:{width:n.width,height:n.height}}function m(n){return typeof n=="symbol"?n.description??"":n}var p=class{isInternalCanvas=!1;isTouchDevice=!1;gl;fragmentShaderSrc;uniforms=new Map;textures=new Map;textureUnitPool;buffer=null;program=null;aPositionLocation=0;animationFrameId;resolutionObserver;resizeObserver;resizeTimeout=null;lastResizeTime=-1/0;eventListeners=new Map;frame=0;startTime=0;cursorPosition=[.5,.5];clickPosition=[.5,.5];isMouseDown=!1;canvas;onResize;hooks=new Map;historyDepth;debug;constructor(t,e={}){if(this.canvas=e.canvas||document.createElement("canvas"),!e.canvas){this.isInternalCanvas=!0;let r=this.canvas;r.style.position="fixed",r.style.inset="0",r.style.height="100dvh",r.style.width="100dvw",document.body.appendChild(r)}if(this.gl=this.canvas.getContext("webgl2",{antialias:!1}),!this.gl)throw new Error("WebGL2 not supported. Please use a browser that supports WebGL2.");this.textureUnitPool={free:[],next:0,max:this.gl.getParameter(this.gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS)},this.historyDepth=e.history??0,this.debug=e.debug??(typeof process<"u"&&!1),this.animationFrameId=null,this.resolutionObserver=new MutationObserver(()=>this.updateResolution()),this.resizeObserver=new ResizeObserver(()=>this.throttledHandleResize());let i=[];if(e.plugins){let r={gl:this.gl,uniforms:this.uniforms,textures:this.textures,canvas:this.canvas,reserveTextureUnit:this.reserveTextureUnit.bind(this),releaseTextureUnit:this.releaseTextureUnit.bind(this),injectGLSL:s=>{i.push(s)}};Object.defineProperty(r,"program",{get:()=>this.program,enumerable:!0,configurable:!0}),e.plugins.forEach(s=>s(this,r))}this.fragmentShaderSrc=U(t,i),this.init(),this.canvas instanceof HTMLCanvasElement&&this.addEventListeners()}registerHook(t,e){this.hooks.has(t)||this.hooks.set(t,[]),this.hooks.get(t).push(e)}init(){let t=L;if(this.program=this.gl.createProgram(),!this.program)throw new Error("Failed to create WebGL program");let e=this.createShader(this.gl.VERTEX_SHADER,t),i=this.createShader(this.gl.FRAGMENT_SHADER,this.fragmentShaderSrc);if(this.gl.attachShader(this.program,e),this.gl.attachShader(this.program,i),this.gl.linkProgram(this.program),this.gl.deleteShader(e),this.gl.deleteShader(i),!this.gl.getProgramParameter(this.program,this.gl.LINK_STATUS))throw console.error("Program link error:",this.gl.getProgramInfoLog(this.program)),this.gl.deleteProgram(this.program),new Error("Failed to link WebGL program");this.aPositionLocation=this.gl.getAttribLocation(this.program,"aPosition"),this.setupBuffer(),this.gl.useProgram(this.program),this.canvas instanceof HTMLCanvasElement&&(this.resolutionObserver.observe(this.canvas,{attributes:!0,attributeFilter:["width","height"]}),this.resizeObserver.observe(this.canvas)),this.isInternalCanvas||this.updateResolution(),this.initializeUniform("u_cursor","float",this.cursorPosition),this.initializeUniform("u_click","float",[...this.clickPosition,this.isMouseDown?1:0]),this.initializeUniform("u_time","float",0),this.initializeUniform("u_frame","int",0),this.historyDepth>0&&this._initializeTexture(f,this.canvas,{history:this.historyDepth}),this.hooks.get("init")?.forEach(r=>r.call(this))}createShader(t,e){let i=this.gl.createShader(t);if(this.gl.shaderSource(i,e),this.gl.compileShader(i),!this.gl.getShaderParameter(i,this.gl.COMPILE_STATUS))throw console.error("Shader compilation failed:",e),console.error(this.gl.getShaderInfoLog(i)),this.gl.deleteShader(i),new Error("Shader compilation failed");return i}setupBuffer(){let t=new Float32Array([-1,-1,1,-1,-1,1,-1,1,1,-1,1,1]);this.buffer=this.gl.createBuffer(),this.gl.bindBuffer(this.gl.ARRAY_BUFFER,this.buffer),this.gl.bufferData(this.gl.ARRAY_BUFFER,t,this.gl.STATIC_DRAW),this.gl.viewport(0,0,this.gl.drawingBufferWidth,this.gl.drawingBufferHeight),this.gl.enableVertexAttribArray(this.aPositionLocation),this.gl.vertexAttribPointer(this.aPositionLocation,2,this.gl.FLOAT,!1,0,0)}throttledHandleResize(){clearTimeout(this.resizeTimeout);let t=performance.now(),e=this.lastResizeTime+R-t;e<=0?(this.lastResizeTime=t,this.handleResize()):this.resizeTimeout=setTimeout(()=>this.throttledHandleResize(),e)}handleResize(){if(!(this.canvas instanceof HTMLCanvasElement))return;let t=window.devicePixelRatio||1,e=this.canvas.clientWidth*t,i=this.canvas.clientHeight*t;this.isInternalCanvas&&(this.canvas.width!==e||this.canvas.height!==i)&&(this.canvas.width=e,this.canvas.height=i),this.onResize?.(e,i)}addEventListeners(){let t=this.canvas,e=(r,s)=>{if(!this.uniforms.has("u_cursor"))return;let a=t.getBoundingClientRect();this.cursorPosition[0]=(r-a.left)/a.width,this.cursorPosition[1]=1-(s-a.top)/a.height,this.updateUniforms({u_cursor:this.cursorPosition})},i=(r,s,a)=>{if(this.uniforms.has("u_click")){if(this.isMouseDown=r,r){let o=t.getBoundingClientRect(),l=s,u=a;this.clickPosition[0]=(l-o.left)/o.width,this.clickPosition[1]=1-(u-o.top)/o.height}this.updateUniforms({u_click:[...this.clickPosition,this.isMouseDown?1:0]})}};this.eventListeners.set("mousemove",r=>{let s=r;this.isTouchDevice||e(s.clientX,s.clientY)}),this.eventListeners.set("mousedown",r=>{let s=r;this.isTouchDevice||s.button===0&&(this.isMouseDown=!0,i(!0,s.clientX,s.clientY))}),this.eventListeners.set("mouseup",r=>{let s=r;this.isTouchDevice||s.button===0&&i(!1)}),this.eventListeners.set("touchmove",r=>{let s=r;s.touches.length>0&&e(s.touches[0].clientX,s.touches[0].clientY)}),this.eventListeners.set("touchstart",r=>{let s=r;this.isTouchDevice=!0,s.touches.length>0&&(e(s.touches[0].clientX,s.touches[0].clientY),i(!0,s.touches[0].clientX,s.touches[0].clientY))}),this.eventListeners.set("touchend",r=>{r.touches.length===0&&i(!1)}),this.eventListeners.forEach((r,s)=>{t.addEventListener(s,r)})}updateResolution(){let t=[this.gl.drawingBufferWidth,this.gl.drawingBufferHeight];this.gl.viewport(0,0,...t),this.uniforms.has("u_resolution")?this.updateUniforms({u_resolution:t}):this.initializeUniform("u_resolution","float",t),this.hooks.get("updateResolution")?.forEach(e=>e.call(this))}reserveTextureUnit(t){let e=this.textures.get(t);if(e)return e.unitIndex;if(this.textureUnitPool.free.length>0)return this.textureUnitPool.free.pop();if(this.textureUnitPool.next>=this.textureUnitPool.max)throw new Error("Exceeded the available texture units for this device.");return this.textureUnitPool.next++}releaseTextureUnit(t){let e=this.textures.get(t);e&&this.textureUnitPool.free.push(e.unitIndex)}clearHistoryTextureLayers(t){if(!t.history)return;let e=t.options?.type??this.gl.UNSIGNED_BYTE,i=e===this.gl.FLOAT?new Float32Array(t.width*t.height*4):new Uint8Array(t.width*t.height*4);this.gl.activeTexture(this.gl.TEXTURE0+t.unitIndex),this.gl.bindTexture(this.gl.TEXTURE_2D_ARRAY,t.texture);for(let r=0;r<t.history.depth;++r)this.gl.texSubImage3D(this.gl.TEXTURE_2D_ARRAY,0,0,0,r,t.width,t.height,1,t.options?.format??this.gl.RGBA,e,i)}initializeUniform(t,e,i,r){let s=r?.arrayLength;if(this.uniforms.has(t))throw new Error(`${t} is already initialized.`);if(e!=="float"&&e!=="int")throw new Error(`Invalid uniform type: ${e}. Expected 'float' or 'int'.`);if(s&&!(Array.isArray(i)&&i.length===s))throw new Error(`${t} array length mismatch: must initialize with ${s} elements.`);let a=this.gl.getUniformLocation(this.program,t);if(!a&&s&&(a=this.gl.getUniformLocation(this.program,`${t}[0]`)),!a){this.log(`${t} not found in fragment shader. Skipping initialization.`);return}let o=s?i[0]:i,l=Array.isArray(o)?o.length:1;this.uniforms.set(t,{type:e,length:l,location:a,arrayLength:s});try{this.updateUniforms({[t]:i})}catch(u){throw this.uniforms.delete(t),u}this.hooks.get("initializeUniform")?.forEach(u=>u.call(this,...arguments))}log(...t){this.debug&&console.debug(...t)}updateUniforms(t,e){this.gl.useProgram(this.program),Object.entries(t).forEach(([i,r])=>{let s=this.uniforms.get(i);if(!s){this.log(`${i} not found in fragment shader. Skipping update.`);return}let a=`uniform${s.length}${s.type.charAt(0)}`;if(s.arrayLength){if(!Array.isArray(r))throw new Error(`${i} is an array, but the value passed to updateUniforms is not an array.`);let o=r.length;if(!o)return;if(o>s.arrayLength)throw new Error(`${i} received ${o} values, but maximum length is ${s.arrayLength}.`);if(r.some(h=>(Array.isArray(h)?h.length:1)!==s.length))throw new Error(`Tried to update ${i} with some elements that are not length ${s.length}.`);let l=new(s.type==="float"?Float32Array:Int32Array)(r.flat()),u=s.location;if(e?.startIndex){let h=this.gl.getUniformLocation(this.program,`${i}[${e.startIndex}]`);if(!h)throw new Error(`${i}[${e.startIndex}] not found in fragment shader. Did you pass an invalid startIndex?`);u=h}this.gl[a+"v"](s.location,l)}else{if(Array.isArray(r)||(r=[r]),r.length!==s.length)throw new Error(`Invalid uniform value length: ${r.length}. Expected ${s.length}.`);this.gl[a](s.location,...r)}}),this.hooks.get("updateUniforms")?.forEach(i=>i.call(this,...arguments))}createTexture(t,e,i){let{width:r,height:s}=e,a=e.history?.depth??0,o=this.gl.createTexture();if(!o)throw new Error("Failed to create texture");let l=i?.unitIndex;if(typeof l!="number")try{l=this.reserveTextureUnit(t)}catch(g){throw this.gl.deleteTexture(o),g}let u=a>0,h=u?this.gl.TEXTURE_2D_ARRAY:this.gl.TEXTURE_2D;if(this.gl.activeTexture(this.gl.TEXTURE0+l),this.gl.bindTexture(h,o),this.gl.texParameteri(h,this.gl.TEXTURE_WRAP_S,i?.wrapS??this.gl.CLAMP_TO_EDGE),this.gl.texParameteri(h,this.gl.TEXTURE_WRAP_T,i?.wrapT??this.gl.CLAMP_TO_EDGE),this.gl.texParameteri(h,this.gl.TEXTURE_MIN_FILTER,i?.minFilter??this.gl.LINEAR),this.gl.texParameteri(h,this.gl.TEXTURE_MAG_FILTER,i?.magFilter??this.gl.LINEAR),u){let g=i?.type??this.gl.UNSIGNED_BYTE,c=i?.internalFormat??(g===this.gl.FLOAT?this.gl.RGBA32F:this.gl.RGBA8);this.gl.texStorage3D(h,1,c,r,s,a)}return{texture:o,unitIndex:l}}_initializeTexture(t,e,i){if(this.textures.has(t))throw new Error(`Texture '${m(t)}' is already initialized.`);let{history:r=0,...s}=i??{},{width:a,height:o}=T(e);if(!a||!o)throw new Error("Texture source must have valid dimensions");let l={width:a,height:o};r>0&&(l.history={depth:r,writeIndex:0});let{texture:u,unitIndex:h}=this.createTexture(t,l,s),g={texture:u,unitIndex:h,...l,options:s};r>0&&(this.initializeUniform(`${m(t)}FrameOffset`,"int",0),this.clearHistoryTextureLayers(g)),this.textures.set(t,g),this.updateTexture(t,e);let c=this.gl.getUniformLocation(this.program,m(t));c&&this.gl.uniform1i(c,h)}initializeTexture(t,e,i){this._initializeTexture(t,e,i),this.hooks.get("initializeTexture")?.forEach(r=>r.call(this,...arguments))}updateTextures(t){this.hooks.get("updateTextures")?.forEach(e=>e.call(this,...arguments)),Object.entries(t).forEach(([e,i])=>{this.updateTexture(e,i)})}updateTexture(t,e){let i=this.textures.get(t);if(!i)throw new Error(`Texture '${m(t)}' is not initialized.`);if(e instanceof WebGLTexture){this.gl.activeTexture(this.gl.TEXTURE0+i.unitIndex),this.gl.bindTexture(this.gl.TEXTURE_2D,e);return}let{width:r,height:s}=T(e);if(!r||!s)return;let a="isPartial"in e&&e.isPartial;if(!a&&(i.width!==r||i.height!==s)){this.gl.deleteTexture(i.texture),i.width=r,i.height=s;let{texture:h}=this.createTexture(t,i,{...i.options,unitIndex:i.unitIndex});i.texture=h,i.history&&(i.history.writeIndex=0,this.clearHistoryTextureLayers(i))}let o="data"in e&&e.data,l=!o&&!i.options?.preserveY,u=this.gl.getParameter(this.gl.UNPACK_FLIP_Y_WEBGL);if(i.history){let h=t===f;this.gl.activeTexture(this.gl.TEXTURE0+i.unitIndex),this.gl.bindTexture(this.gl.TEXTURE_2D_ARRAY,i.texture),h?this.gl.copyTexSubImage3D(this.gl.TEXTURE_2D_ARRAY,0,0,0,i.history.writeIndex,0,0,r,s):(this.gl.pixelStorei(this.gl.UNPACK_FLIP_Y_WEBGL,l),this.gl.texSubImage3D(this.gl.TEXTURE_2D_ARRAY,0,0,0,i.history.writeIndex,r,s,1,i.options?.format??this.gl.RGBA,i.options?.type??this.gl.UNSIGNED_BYTE,e.data??e),this.gl.pixelStorei(this.gl.UNPACK_FLIP_Y_WEBGL,u));let g=`${m(t)}FrameOffset`;this.updateUniforms({[g]:i.history.writeIndex}),i.history.writeIndex=(i.history.writeIndex+1)%i.history.depth}else{this.gl.activeTexture(this.gl.TEXTURE0+i.unitIndex),this.gl.bindTexture(this.gl.TEXTURE_2D,i.texture),this.gl.pixelStorei(this.gl.UNPACK_FLIP_Y_WEBGL,l);let h=i.options?.format??this.gl.RGBA,g=i.options?.type??this.gl.UNSIGNED_BYTE;if(a)this.gl.texSubImage2D(this.gl.TEXTURE_2D,0,e.x??0,e.y??0,r,s,h,g,e.data);else{let c=i.options?.internalFormat??(o?g===this.gl.FLOAT?this.gl.RGBA32F:this.gl.RGBA8:this.gl.RGBA);this.gl.texImage2D(this.gl.TEXTURE_2D,0,c,r,s,0,h,g,e.data??e)}this.gl.pixelStorei(this.gl.UNPACK_FLIP_Y_WEBGL,u)}}draw(t=!0){let e=this.gl;e.useProgram(this.program),e.bindBuffer(e.ARRAY_BUFFER,this.buffer),e.vertexAttribPointer(this.aPositionLocation,2,e.FLOAT,!1,0,0),e.enableVertexAttribArray(this.aPositionLocation),e.viewport(0,0,e.drawingBufferWidth,e.drawingBufferHeight),t&&e.clear(e.COLOR_BUFFER_BIT),e.drawArrays(e.TRIANGLES,0,6)}step(t){this.uniforms.has("u_time")&&this.updateUniforms({u_time:t}),this.uniforms.has("u_frame")&&this.updateUniforms({u_frame:this.frame}),this.draw(),this.textures.get(f)&&this.updateTexture(f,this.canvas),this.hooks.get("step")?.forEach(e=>e.call(this,t,this.frame)),++this.frame}play(t){this.pause();let e=i=>{i=(i-this.startTime)/1e3,this.step(i),this.animationFrameId=requestAnimationFrame(e),t&&t(i,this.frame)};this.animationFrameId=requestAnimationFrame(e)}pause(){this.animationFrameId&&(cancelAnimationFrame(this.animationFrameId),this.animationFrameId=null)}reset(){this.frame=0,this.startTime=performance.now(),this.textures.forEach(t=>{t.history&&(t.history.writeIndex=0,this.clearHistoryTextureLayers(t))}),this.hooks.get("reset")?.forEach(t=>t.call(this))}destroy(){this.animationFrameId&&(cancelAnimationFrame(this.animationFrameId),this.animationFrameId=null),this.resolutionObserver.disconnect(),this.resizeObserver.disconnect(),this.canvas instanceof HTMLCanvasElement&&this.eventListeners.forEach((t,e)=>{this.canvas.removeEventListener(e,t)}),this.program&&this.gl.deleteProgram(this.program),this.textures.forEach(t=>{this.gl.deleteTexture(t.texture)}),this.textureUnitPool.free=[],this.textureUnitPool.next=0,this.buffer&&(this.gl.deleteBuffer(this.buffer),this.buffer=null),this.hooks.get("destroy")?.forEach(t=>t.call(this)),this.isInternalCanvas&&this.canvas instanceof HTMLCanvasElement&&this.canvas.remove()}},_=p;
11
11
  //# sourceMappingURL=index.js.map