@paper-design/shaders 0.0.0-canary.390a3d9

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.
Files changed (114) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +7 -0
  3. package/dist/get-empty-pixel.d.ts +1 -0
  4. package/dist/get-empty-pixel.js +19 -0
  5. package/dist/get-empty-pixel.js.map +7 -0
  6. package/dist/get-shader-color-from-string.d.ts +3 -0
  7. package/dist/get-shader-color-from-string.js +93 -0
  8. package/dist/get-shader-color-from-string.js.map +7 -0
  9. package/dist/get-shader-noise-texture.d.ts +1 -0
  10. package/dist/get-shader-noise-texture.js +19 -0
  11. package/dist/get-shader-noise-texture.js.map +7 -0
  12. package/dist/index.d.ts +53 -0
  13. package/dist/index.js +168 -0
  14. package/dist/index.js.map +7 -0
  15. package/dist/shader-color-spaces.d.ts +6 -0
  16. package/dist/shader-color-spaces.js +96 -0
  17. package/dist/shader-color-spaces.js.map +7 -0
  18. package/dist/shader-mount.d.ts +130 -0
  19. package/dist/shader-mount.js +483 -0
  20. package/dist/shader-mount.js.map +7 -0
  21. package/dist/shader-sizing.d.ts +35 -0
  22. package/dist/shader-sizing.js +230 -0
  23. package/dist/shader-sizing.js.map +7 -0
  24. package/dist/shader-utils.d.ts +10 -0
  25. package/dist/shader-utils.js +131 -0
  26. package/dist/shader-utils.js.map +7 -0
  27. package/dist/shaders/color-panels.d.ts +50 -0
  28. package/dist/shaders/color-panels.js +224 -0
  29. package/dist/shaders/color-panels.js.map +7 -0
  30. package/dist/shaders/dithering.d.ts +56 -0
  31. package/dist/shaders/dithering.js +223 -0
  32. package/dist/shaders/dithering.js.map +7 -0
  33. package/dist/shaders/dot-grid.d.ts +51 -0
  34. package/dist/shaders/dot-grid.js +110 -0
  35. package/dist/shaders/dot-grid.js.map +7 -0
  36. package/dist/shaders/dot-orbit.d.ts +39 -0
  37. package/dist/shaders/dot-orbit.js +124 -0
  38. package/dist/shaders/dot-orbit.js.map +7 -0
  39. package/dist/shaders/fluted-glass.d.ts +73 -0
  40. package/dist/shaders/fluted-glass.js +187 -0
  41. package/dist/shaders/fluted-glass.js.map +7 -0
  42. package/dist/shaders/god-rays.d.ts +45 -0
  43. package/dist/shaders/god-rays.js +133 -0
  44. package/dist/shaders/god-rays.js.map +7 -0
  45. package/dist/shaders/grain-gradient.d.ts +58 -0
  46. package/dist/shaders/grain-gradient.js +289 -0
  47. package/dist/shaders/grain-gradient.js.map +7 -0
  48. package/dist/shaders/heatmap.d.ts +31 -0
  49. package/dist/shaders/heatmap.js +303 -0
  50. package/dist/shaders/heatmap.js.map +7 -0
  51. package/dist/shaders/image-dithering.d.ts +49 -0
  52. package/dist/shaders/image-dithering.js +163 -0
  53. package/dist/shaders/image-dithering.js.map +7 -0
  54. package/dist/shaders/image-halftone-dots.d.ts +46 -0
  55. package/dist/shaders/image-halftone-dots.js +337 -0
  56. package/dist/shaders/image-halftone-dots.js.map +7 -0
  57. package/dist/shaders/liquid-metal.d.ts +66 -0
  58. package/dist/shaders/liquid-metal.js +627 -0
  59. package/dist/shaders/liquid-metal.js.map +7 -0
  60. package/dist/shaders/mesh-gradient.d.ts +33 -0
  61. package/dist/shaders/mesh-gradient.js +136 -0
  62. package/dist/shaders/mesh-gradient.js.map +7 -0
  63. package/dist/shaders/metaballs.d.ts +35 -0
  64. package/dist/shaders/metaballs.js +112 -0
  65. package/dist/shaders/metaballs.js.map +7 -0
  66. package/dist/shaders/neuro-noise.d.ts +27 -0
  67. package/dist/shaders/neuro-noise.js +80 -0
  68. package/dist/shaders/neuro-noise.js.map +7 -0
  69. package/dist/shaders/paper-texture.d.ts +53 -0
  70. package/dist/shaders/paper-texture.js +248 -0
  71. package/dist/shaders/paper-texture.js.map +7 -0
  72. package/dist/shaders/perlin-noise.d.ts +33 -0
  73. package/dist/shaders/perlin-noise.js +195 -0
  74. package/dist/shaders/perlin-noise.js.map +7 -0
  75. package/dist/shaders/pulsing-border.d.ts +72 -0
  76. package/dist/shaders/pulsing-border.js +237 -0
  77. package/dist/shaders/pulsing-border.js.map +7 -0
  78. package/dist/shaders/simplex-noise.d.ts +27 -0
  79. package/dist/shaders/simplex-noise.js +100 -0
  80. package/dist/shaders/simplex-noise.js.map +7 -0
  81. package/dist/shaders/smoke-ring.d.ts +39 -0
  82. package/dist/shaders/smoke-ring.js +130 -0
  83. package/dist/shaders/smoke-ring.js.map +7 -0
  84. package/dist/shaders/spiral.d.ts +41 -0
  85. package/dist/shaders/spiral.js +81 -0
  86. package/dist/shaders/spiral.js.map +7 -0
  87. package/dist/shaders/static-mesh-gradient.d.ts +42 -0
  88. package/dist/shaders/static-mesh-gradient.js +139 -0
  89. package/dist/shaders/static-mesh-gradient.js.map +7 -0
  90. package/dist/shaders/static-radial-gradient.d.ts +50 -0
  91. package/dist/shaders/static-radial-gradient.js +195 -0
  92. package/dist/shaders/static-radial-gradient.js.map +7 -0
  93. package/dist/shaders/swirl.d.ts +42 -0
  94. package/dist/shaders/swirl.js +102 -0
  95. package/dist/shaders/swirl.js.map +7 -0
  96. package/dist/shaders/voronoi.d.ts +44 -0
  97. package/dist/shaders/voronoi.js +139 -0
  98. package/dist/shaders/voronoi.js.map +7 -0
  99. package/dist/shaders/warp.d.ts +54 -0
  100. package/dist/shaders/warp.js +132 -0
  101. package/dist/shaders/warp.js.map +7 -0
  102. package/dist/shaders/water.d.ts +39 -0
  103. package/dist/shaders/water.js +120 -0
  104. package/dist/shaders/water.js.map +7 -0
  105. package/dist/shaders/waves.d.ts +38 -0
  106. package/dist/shaders/waves.js +66 -0
  107. package/dist/shaders/waves.js.map +7 -0
  108. package/dist/types.d.ts +3 -0
  109. package/dist/types.js +6 -0
  110. package/dist/types.js.map +7 -0
  111. package/dist/vertex-shader.d.ts +2 -0
  112. package/dist/vertex-shader.js +211 -0
  113. package/dist/vertex-shader.js.map +7 -0
  114. package/package.json +27 -0
@@ -0,0 +1,483 @@
1
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * *
2
+ * Paper Shaders *
3
+ * https://github.com/paper-design/shaders *
4
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * */
5
+
6
+ import { vertexShaderSource } from "./vertex-shader.js";
7
+ const DEFAULT_MAX_PIXEL_COUNT = 1920 * 1080 * 4;
8
+ class ShaderMount {
9
+ parentElement;
10
+ canvasElement;
11
+ gl;
12
+ program = null;
13
+ uniformLocations = {};
14
+ /** The fragment shader that we are using */
15
+ fragmentShader;
16
+ /** Stores the RAF for the render loop */
17
+ rafId = null;
18
+ /** Time of the last rendered frame */
19
+ lastRenderTime = 0;
20
+ /** Total time that we have played any animation, passed as a uniform to the shader for time-based VFX */
21
+ currentFrame = 0;
22
+ /** The speed that we progress through animation time (multiplies by delta time every update). Allows negatives to play in reverse. If set to 0, rAF will stop entirely so static shaders have no recurring performance costs */
23
+ speed = 0;
24
+ /** Actual speed used that accounts for document visibility (we pause the shader if the tab is hidden) */
25
+ currentSpeed = 0;
26
+ /** Uniforms that are provided by the user for the specific shader being mounted (not including uniforms that this Mount adds, like time and resolution) */
27
+ providedUniforms;
28
+ /** Just a sanity check to make sure frames don't run after we're disposed */
29
+ hasBeenDisposed = false;
30
+ /** If the resolution of the canvas has changed since the last render */
31
+ resolutionChanged = true;
32
+ /** Store textures that are provided by the user */
33
+ textures = /* @__PURE__ */ new Map();
34
+ minPixelRatio;
35
+ maxPixelCount;
36
+ isSafari = isSafari();
37
+ uniformCache = {};
38
+ textureUnitMap = /* @__PURE__ */ new Map();
39
+ constructor(parentElement, fragmentShader, uniforms, webGlContextAttributes, speed = 0, frame = 0, minPixelRatio = 2, maxPixelCount = DEFAULT_MAX_PIXEL_COUNT) {
40
+ if (parentElement instanceof HTMLElement) {
41
+ this.parentElement = parentElement;
42
+ } else {
43
+ throw new Error("Paper Shaders: parent element must be an HTMLElement");
44
+ }
45
+ if (!document.querySelector("style[data-paper-shader]")) {
46
+ const styleElement = document.createElement("style");
47
+ styleElement.innerHTML = defaultStyle;
48
+ styleElement.setAttribute("data-paper-shader", "");
49
+ document.head.prepend(styleElement);
50
+ }
51
+ const canvasElement = document.createElement("canvas");
52
+ this.canvasElement = canvasElement;
53
+ this.parentElement.prepend(canvasElement);
54
+ this.fragmentShader = fragmentShader;
55
+ this.providedUniforms = uniforms;
56
+ this.currentFrame = frame;
57
+ this.minPixelRatio = minPixelRatio;
58
+ this.maxPixelCount = maxPixelCount;
59
+ const gl = canvasElement.getContext("webgl2", webGlContextAttributes);
60
+ if (!gl) {
61
+ throw new Error("Paper Shaders: WebGL is not supported in this browser");
62
+ }
63
+ this.gl = gl;
64
+ this.initProgram();
65
+ this.setupPositionAttribute();
66
+ this.setupUniforms();
67
+ this.setUniformValues(this.providedUniforms);
68
+ this.setupResizeObserver();
69
+ visualViewport?.addEventListener("resize", this.handleVisualViewportChange);
70
+ this.setSpeed(speed);
71
+ this.parentElement.setAttribute("data-paper-shader", "");
72
+ this.parentElement.paperShaderMount = this;
73
+ document.addEventListener("visibilitychange", this.handleDocumentVisibilityChange);
74
+ }
75
+ initProgram = () => {
76
+ const program = createProgram(this.gl, vertexShaderSource, this.fragmentShader);
77
+ if (!program) return;
78
+ this.program = program;
79
+ };
80
+ setupPositionAttribute = () => {
81
+ const positionAttributeLocation = this.gl.getAttribLocation(this.program, "a_position");
82
+ const positionBuffer = this.gl.createBuffer();
83
+ this.gl.bindBuffer(this.gl.ARRAY_BUFFER, positionBuffer);
84
+ const positions = [-1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1];
85
+ this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array(positions), this.gl.STATIC_DRAW);
86
+ this.gl.enableVertexAttribArray(positionAttributeLocation);
87
+ this.gl.vertexAttribPointer(positionAttributeLocation, 2, this.gl.FLOAT, false, 0, 0);
88
+ };
89
+ setupUniforms = () => {
90
+ const uniformLocations = {
91
+ u_time: this.gl.getUniformLocation(this.program, "u_time"),
92
+ u_pixelRatio: this.gl.getUniformLocation(this.program, "u_pixelRatio"),
93
+ u_resolution: this.gl.getUniformLocation(this.program, "u_resolution")
94
+ };
95
+ Object.entries(this.providedUniforms).forEach(([key, value]) => {
96
+ uniformLocations[key] = this.gl.getUniformLocation(this.program, key);
97
+ if (value instanceof HTMLImageElement) {
98
+ const aspectRatioUniformName = `${key}AspectRatio`;
99
+ uniformLocations[aspectRatioUniformName] = this.gl.getUniformLocation(this.program, aspectRatioUniformName);
100
+ }
101
+ });
102
+ this.uniformLocations = uniformLocations;
103
+ };
104
+ /**
105
+ * The scale that we should render at.
106
+ * - Used to target 2x rendering even on 1x screens for better antialiasing
107
+ * - Prevents the virtual resolution from going beyond the maximum resolution
108
+ * - Accounts for the page zoom level so we render in physical device pixels rather than CSS pixels
109
+ */
110
+ renderScale = 1;
111
+ parentWidth = 0;
112
+ parentHeight = 0;
113
+ parentDevicePixelWidth = 0;
114
+ parentDevicePixelHeight = 0;
115
+ devicePixelsSupported = false;
116
+ resizeObserver = null;
117
+ setupResizeObserver = () => {
118
+ this.resizeObserver = new ResizeObserver(([entry]) => {
119
+ if (entry?.borderBoxSize[0]) {
120
+ const physicalPixelSize = entry.devicePixelContentBoxSize?.[0];
121
+ if (physicalPixelSize !== void 0) {
122
+ this.devicePixelsSupported = true;
123
+ this.parentDevicePixelWidth = physicalPixelSize.inlineSize;
124
+ this.parentDevicePixelHeight = physicalPixelSize.blockSize;
125
+ }
126
+ this.parentWidth = entry.borderBoxSize[0].inlineSize;
127
+ this.parentHeight = entry.borderBoxSize[0].blockSize;
128
+ }
129
+ this.handleResize();
130
+ });
131
+ this.resizeObserver.observe(this.parentElement);
132
+ };
133
+ // Visual viewport resize handler, mainly used to react to browser zoom changes.
134
+ // Resize observer by itself does not react to pinch zoom, and although it usually
135
+ // reacts to classic browser zoom, it's not guaranteed in edge cases.
136
+ // Since timing between visual viewport changes and resize observer is complex
137
+ // and because we'd like to know the device pixel sizes of elements, we just restart
138
+ // the observer to get a guaranteed fresh callback regardless if it would have triggered or not.
139
+ handleVisualViewportChange = () => {
140
+ this.resizeObserver?.disconnect();
141
+ this.setupResizeObserver();
142
+ };
143
+ /** Resize handler for when the container div changes size or the max pixel count changes and we want to resize our canvas to match */
144
+ handleResize = () => {
145
+ let targetPixelWidth = 0;
146
+ let targetPixelHeight = 0;
147
+ const dpr = Math.max(1, window.devicePixelRatio);
148
+ const pinchZoom = visualViewport?.scale ?? 1;
149
+ if (this.devicePixelsSupported) {
150
+ const scaleToMeetMinPixelRatio = Math.max(1, this.minPixelRatio / dpr);
151
+ targetPixelWidth = this.parentDevicePixelWidth * scaleToMeetMinPixelRatio * pinchZoom;
152
+ targetPixelHeight = this.parentDevicePixelHeight * scaleToMeetMinPixelRatio * pinchZoom;
153
+ } else {
154
+ let targetRenderScale = Math.max(dpr, this.minPixelRatio) * pinchZoom;
155
+ if (this.isSafari) {
156
+ const zoomLevel = bestGuessBrowserZoom();
157
+ targetRenderScale *= Math.max(1, zoomLevel);
158
+ }
159
+ targetPixelWidth = Math.round(this.parentWidth) * targetRenderScale;
160
+ targetPixelHeight = Math.round(this.parentHeight) * targetRenderScale;
161
+ }
162
+ const maxPixelCountHeadroom = Math.sqrt(this.maxPixelCount) / Math.sqrt(targetPixelWidth * targetPixelHeight);
163
+ const scaleToMeetMaxPixelCount = Math.min(1, maxPixelCountHeadroom);
164
+ const newWidth = Math.round(targetPixelWidth * scaleToMeetMaxPixelCount);
165
+ const newHeight = Math.round(targetPixelHeight * scaleToMeetMaxPixelCount);
166
+ const newRenderScale = newWidth / Math.round(this.parentWidth);
167
+ if (this.canvasElement.width !== newWidth || this.canvasElement.height !== newHeight || this.renderScale !== newRenderScale) {
168
+ this.renderScale = newRenderScale;
169
+ this.canvasElement.width = newWidth;
170
+ this.canvasElement.height = newHeight;
171
+ this.resolutionChanged = true;
172
+ this.gl.viewport(0, 0, this.gl.canvas.width, this.gl.canvas.height);
173
+ this.render(performance.now());
174
+ }
175
+ };
176
+ render = (currentTime) => {
177
+ if (this.hasBeenDisposed) return;
178
+ if (this.program === null) {
179
+ console.warn("Tried to render before program or gl was initialized");
180
+ return;
181
+ }
182
+ const dt = currentTime - this.lastRenderTime;
183
+ this.lastRenderTime = currentTime;
184
+ if (this.currentSpeed !== 0) {
185
+ this.currentFrame += dt * this.currentSpeed;
186
+ }
187
+ this.gl.clear(this.gl.COLOR_BUFFER_BIT);
188
+ this.gl.useProgram(this.program);
189
+ this.gl.uniform1f(this.uniformLocations.u_time, this.currentFrame * 1e-3);
190
+ if (this.resolutionChanged) {
191
+ this.gl.uniform2f(this.uniformLocations.u_resolution, this.gl.canvas.width, this.gl.canvas.height);
192
+ this.gl.uniform1f(this.uniformLocations.u_pixelRatio, this.renderScale);
193
+ this.resolutionChanged = false;
194
+ }
195
+ this.gl.drawArrays(this.gl.TRIANGLES, 0, 6);
196
+ if (this.currentSpeed !== 0) {
197
+ this.requestRender();
198
+ } else {
199
+ this.rafId = null;
200
+ }
201
+ };
202
+ requestRender = () => {
203
+ if (this.rafId !== null) {
204
+ cancelAnimationFrame(this.rafId);
205
+ }
206
+ this.rafId = requestAnimationFrame(this.render);
207
+ };
208
+ /** Creates a texture from an image and sets it into a uniform value */
209
+ setTextureUniform = (uniformName, image) => {
210
+ if (!image.complete || image.naturalWidth === 0) {
211
+ throw new Error(`Paper Shaders: image for uniform ${uniformName} must be fully loaded`);
212
+ }
213
+ const existingTexture = this.textures.get(uniformName);
214
+ if (existingTexture) {
215
+ this.gl.deleteTexture(existingTexture);
216
+ }
217
+ if (!this.textureUnitMap.has(uniformName)) {
218
+ this.textureUnitMap.set(uniformName, this.textureUnitMap.size);
219
+ }
220
+ const textureUnit = this.textureUnitMap.get(uniformName);
221
+ this.gl.activeTexture(this.gl.TEXTURE0 + textureUnit);
222
+ const texture = this.gl.createTexture();
223
+ this.gl.bindTexture(this.gl.TEXTURE_2D, texture);
224
+ this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_S, this.gl.CLAMP_TO_EDGE);
225
+ this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_T, this.gl.CLAMP_TO_EDGE);
226
+ this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.LINEAR);
227
+ this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MAG_FILTER, this.gl.LINEAR);
228
+ this.gl.texImage2D(this.gl.TEXTURE_2D, 0, this.gl.RGBA, this.gl.RGBA, this.gl.UNSIGNED_BYTE, image);
229
+ if (uniformName !== "u_noiseTexture") {
230
+ }
231
+ const error = this.gl.getError();
232
+ if (error !== this.gl.NO_ERROR || texture === null) {
233
+ console.error("Paper Shaders: WebGL error when uploading texture:", error);
234
+ return;
235
+ }
236
+ this.textures.set(uniformName, texture);
237
+ const location = this.uniformLocations[uniformName];
238
+ if (location) {
239
+ this.gl.uniform1i(location, textureUnit);
240
+ const aspectRatioUniformName = `${uniformName}AspectRatio`;
241
+ const aspectRatioLocation = this.uniformLocations[aspectRatioUniformName];
242
+ if (aspectRatioLocation) {
243
+ const aspectRatio = image.naturalWidth / image.naturalHeight;
244
+ this.gl.uniform1f(aspectRatioLocation, aspectRatio);
245
+ }
246
+ }
247
+ };
248
+ /** Utility: recursive equality test for all the uniforms */
249
+ areUniformValuesEqual = (a, b) => {
250
+ if (a === b) return true;
251
+ if (Array.isArray(a) && Array.isArray(b) && a.length === b.length) {
252
+ return a.every((val, i) => this.areUniformValuesEqual(val, b[i]));
253
+ }
254
+ return false;
255
+ };
256
+ /** Sets the provided uniform values into the WebGL program, can be a partial list of uniforms that have changed */
257
+ setUniformValues = (updatedUniforms) => {
258
+ this.gl.useProgram(this.program);
259
+ Object.entries(updatedUniforms).forEach(([key, value]) => {
260
+ let cacheValue = value;
261
+ if (value instanceof HTMLImageElement) {
262
+ cacheValue = `${value.src.slice(0, 200)}|${value.naturalWidth}x${value.naturalHeight}`;
263
+ }
264
+ if (this.areUniformValuesEqual(this.uniformCache[key], cacheValue)) return;
265
+ this.uniformCache[key] = cacheValue;
266
+ const location = this.uniformLocations[key];
267
+ if (!location) {
268
+ console.warn(`Uniform location for ${key} not found`);
269
+ return;
270
+ }
271
+ if (value instanceof HTMLImageElement) {
272
+ this.setTextureUniform(key, value);
273
+ } else if (Array.isArray(value)) {
274
+ let flatArray = null;
275
+ let valueLength = null;
276
+ if (value[0] !== void 0 && Array.isArray(value[0])) {
277
+ const firstChildLength = value[0].length;
278
+ if (value.every((arr) => arr.length === firstChildLength)) {
279
+ flatArray = value.flat();
280
+ valueLength = firstChildLength;
281
+ } else {
282
+ console.warn(`All child arrays must be the same length for ${key}`);
283
+ return;
284
+ }
285
+ } else {
286
+ flatArray = value;
287
+ valueLength = flatArray.length;
288
+ }
289
+ switch (valueLength) {
290
+ case 2:
291
+ this.gl.uniform2fv(location, flatArray);
292
+ break;
293
+ case 3:
294
+ this.gl.uniform3fv(location, flatArray);
295
+ break;
296
+ case 4:
297
+ this.gl.uniform4fv(location, flatArray);
298
+ break;
299
+ case 9:
300
+ this.gl.uniformMatrix3fv(location, false, flatArray);
301
+ break;
302
+ case 16:
303
+ this.gl.uniformMatrix4fv(location, false, flatArray);
304
+ break;
305
+ default:
306
+ console.warn(`Unsupported uniform array length: ${valueLength}`);
307
+ }
308
+ } else if (typeof value === "number") {
309
+ this.gl.uniform1f(location, value);
310
+ } else if (typeof value === "boolean") {
311
+ this.gl.uniform1i(location, value ? 1 : 0);
312
+ } else {
313
+ console.warn(`Unsupported uniform type for ${key}: ${typeof value}`);
314
+ }
315
+ });
316
+ };
317
+ /** Gets the current total animation time from 0ms */
318
+ getCurrentFrame = () => {
319
+ return this.currentFrame;
320
+ };
321
+ /** Set a frame to get a deterministic result, frames are literally just milliseconds from zero since the animation started */
322
+ setFrame = (newFrame) => {
323
+ this.currentFrame = newFrame;
324
+ this.lastRenderTime = performance.now();
325
+ this.render(performance.now());
326
+ };
327
+ /** Set an animation speed (or 0 to stop animation) */
328
+ setSpeed = (newSpeed = 1) => {
329
+ this.speed = newSpeed;
330
+ this.setCurrentSpeed(document.hidden ? 0 : newSpeed);
331
+ };
332
+ setCurrentSpeed = (newSpeed) => {
333
+ this.currentSpeed = newSpeed;
334
+ if (this.rafId === null && newSpeed !== 0) {
335
+ this.lastRenderTime = performance.now();
336
+ this.rafId = requestAnimationFrame(this.render);
337
+ }
338
+ if (this.rafId !== null && newSpeed === 0) {
339
+ cancelAnimationFrame(this.rafId);
340
+ this.rafId = null;
341
+ }
342
+ };
343
+ /** Set the maximum pixel count for the shader, this will limit the number of pixels that will be rendered */
344
+ setMaxPixelCount = (newMaxPixelCount = DEFAULT_MAX_PIXEL_COUNT) => {
345
+ this.maxPixelCount = newMaxPixelCount;
346
+ this.handleResize();
347
+ };
348
+ /** Set the minimum pixel ratio for the shader */
349
+ setMinPixelRatio = (newMinPixelRatio = 2) => {
350
+ this.minPixelRatio = newMinPixelRatio;
351
+ this.handleResize();
352
+ };
353
+ /** Update the uniforms that are provided by the outside shader, can be a partial set with only the uniforms that have changed */
354
+ setUniforms = (newUniforms) => {
355
+ this.setUniformValues(newUniforms);
356
+ this.providedUniforms = { ...this.providedUniforms, ...newUniforms };
357
+ this.render(performance.now());
358
+ };
359
+ handleDocumentVisibilityChange = () => {
360
+ this.setCurrentSpeed(document.hidden ? 0 : this.speed);
361
+ };
362
+ /** Dispose of the shader mount, cleaning up all of the WebGL resources */
363
+ dispose = () => {
364
+ this.hasBeenDisposed = true;
365
+ if (this.rafId !== null) {
366
+ cancelAnimationFrame(this.rafId);
367
+ this.rafId = null;
368
+ }
369
+ if (this.gl && this.program) {
370
+ this.textures.forEach((texture) => {
371
+ this.gl.deleteTexture(texture);
372
+ });
373
+ this.textures.clear();
374
+ this.gl.deleteProgram(this.program);
375
+ this.program = null;
376
+ this.gl.bindBuffer(this.gl.ARRAY_BUFFER, null);
377
+ this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, null);
378
+ this.gl.bindRenderbuffer(this.gl.RENDERBUFFER, null);
379
+ this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, null);
380
+ this.gl.getError();
381
+ }
382
+ if (this.resizeObserver) {
383
+ this.resizeObserver.disconnect();
384
+ this.resizeObserver = null;
385
+ }
386
+ visualViewport?.removeEventListener("resize", this.handleVisualViewportChange);
387
+ document.removeEventListener("visibilitychange", this.handleDocumentVisibilityChange);
388
+ this.uniformLocations = {};
389
+ this.canvasElement.remove();
390
+ delete this.parentElement.paperShaderMount;
391
+ };
392
+ }
393
+ function createShader(gl, type, source) {
394
+ const shader = gl.createShader(type);
395
+ if (!shader) return null;
396
+ gl.shaderSource(shader, source);
397
+ gl.compileShader(shader);
398
+ if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
399
+ console.error("An error occurred compiling the shaders: " + gl.getShaderInfoLog(shader));
400
+ gl.deleteShader(shader);
401
+ return null;
402
+ }
403
+ return shader;
404
+ }
405
+ function createProgram(gl, vertexShaderSource2, fragmentShaderSource) {
406
+ const format = gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.MEDIUM_FLOAT);
407
+ const precision = format ? format.precision : null;
408
+ if (precision && precision < 23) {
409
+ vertexShaderSource2 = vertexShaderSource2.replace(/precision\s+(lowp|mediump)\s+float;/g, "precision highp float;");
410
+ fragmentShaderSource = fragmentShaderSource.replace(/precision\s+(lowp|mediump)\s+float/g, "precision highp float").replace(/\b(uniform|varying|attribute)\s+(lowp|mediump)\s+(\w+)/g, "$1 highp $3");
411
+ }
412
+ const vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexShaderSource2);
413
+ const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSource);
414
+ if (!vertexShader || !fragmentShader) return null;
415
+ const program = gl.createProgram();
416
+ if (!program) return null;
417
+ gl.attachShader(program, vertexShader);
418
+ gl.attachShader(program, fragmentShader);
419
+ gl.linkProgram(program);
420
+ if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
421
+ console.error("Unable to initialize the shader program: " + gl.getProgramInfoLog(program));
422
+ gl.deleteProgram(program);
423
+ gl.deleteShader(vertexShader);
424
+ gl.deleteShader(fragmentShader);
425
+ return null;
426
+ }
427
+ gl.detachShader(program, vertexShader);
428
+ gl.detachShader(program, fragmentShader);
429
+ gl.deleteShader(vertexShader);
430
+ gl.deleteShader(fragmentShader);
431
+ return program;
432
+ }
433
+ const defaultStyle = `@layer paper-shaders {
434
+ :where([data-paper-shader]) {
435
+ isolation: isolate;
436
+ position: relative;
437
+
438
+ & canvas {
439
+ contain: strict;
440
+ display: block;
441
+ position: absolute;
442
+ inset: 0;
443
+ z-index: -1;
444
+ width: 100%;
445
+ height: 100%;
446
+ border-radius: inherit;
447
+ corner-shape: inherit;
448
+ }
449
+ }
450
+ }`;
451
+ function isPaperShaderElement(element) {
452
+ return "paperShaderMount" in element;
453
+ }
454
+ function isSafari() {
455
+ const ua = navigator.userAgent.toLowerCase();
456
+ return ua.includes("safari") && !ua.includes("chrome") && !ua.includes("android");
457
+ }
458
+ function bestGuessBrowserZoom() {
459
+ const viewportScale = visualViewport?.scale ?? 1;
460
+ const viewportWidth = visualViewport?.width ?? window.innerWidth;
461
+ const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;
462
+ const innerWidth = viewportScale * viewportWidth + scrollbarWidth;
463
+ const ratio = outerWidth / innerWidth;
464
+ const zoomPercentageRounded = Math.round(100 * ratio);
465
+ if (zoomPercentageRounded % 5 === 0) {
466
+ return zoomPercentageRounded / 100;
467
+ }
468
+ if (zoomPercentageRounded === 33) {
469
+ return 1 / 3;
470
+ }
471
+ if (zoomPercentageRounded === 67) {
472
+ return 2 / 3;
473
+ }
474
+ if (zoomPercentageRounded === 133) {
475
+ return 4 / 3;
476
+ }
477
+ return ratio;
478
+ }
479
+ export {
480
+ ShaderMount,
481
+ isPaperShaderElement
482
+ };
483
+ //# sourceMappingURL=shader-mount.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/shader-mount.ts"],
4
+ "sourcesContent": ["import { vertexShaderSource } from './vertex-shader.js';\n\nconst DEFAULT_MAX_PIXEL_COUNT: number = 1920 * 1080 * 4;\n\nexport class ShaderMount {\n public parentElement: PaperShaderElement;\n public canvasElement: HTMLCanvasElement;\n private gl: WebGL2RenderingContext;\n private program: WebGLProgram | null = null;\n private uniformLocations: Record<string, WebGLUniformLocation | null> = {};\n /** The fragment shader that we are using */\n private fragmentShader: string;\n /** Stores the RAF for the render loop */\n private rafId: number | null = null;\n /** Time of the last rendered frame */\n private lastRenderTime = 0;\n /** Total time that we have played any animation, passed as a uniform to the shader for time-based VFX */\n private currentFrame = 0;\n /** The speed that we progress through animation time (multiplies by delta time every update). Allows negatives to play in reverse. If set to 0, rAF will stop entirely so static shaders have no recurring performance costs */\n private speed = 0;\n /** Actual speed used that accounts for document visibility (we pause the shader if the tab is hidden) */\n private currentSpeed = 0;\n /** Uniforms that are provided by the user for the specific shader being mounted (not including uniforms that this Mount adds, like time and resolution) */\n private providedUniforms: ShaderMountUniforms;\n /** Just a sanity check to make sure frames don't run after we're disposed */\n private hasBeenDisposed = false;\n /** If the resolution of the canvas has changed since the last render */\n private resolutionChanged = true;\n /** Store textures that are provided by the user */\n private textures: Map<string, WebGLTexture> = new Map();\n private minPixelRatio;\n private maxPixelCount;\n private isSafari = isSafari();\n private uniformCache: Record<string, unknown> = {};\n private textureUnitMap: Map<string, number> = new Map();\n\n constructor(\n /** The div you'd like to mount the shader to. The shader will match its size. */\n parentElement: HTMLElement,\n fragmentShader: string,\n uniforms: ShaderMountUniforms,\n webGlContextAttributes?: WebGLContextAttributes,\n /** The speed of the animation, or 0 to stop it. Supports negative values to play in reverse. */\n speed = 0,\n /** Pass a frame to offset the starting u_time value and give deterministic results*/\n frame = 0,\n /**\n * The minimum pixel ratio to render at, defaults to 2.\n * May be reduced to improve performance or increased together with `maxPixelCount` to improve antialiasing.\n */\n minPixelRatio = 2,\n /**\n * The maximum amount of physical device pixels to render for the shader,\n * by default it's 1920 * 1080 * 2x dpi (per each side) = 8,294,400 pixels of a 4K screen.\n * Actual DOM size of the canvas can be larger, it will just lose quality after this.\n *\n * May be reduced to improve performance or increased to improve quality on high-resolution screens.\n */\n maxPixelCount: number = DEFAULT_MAX_PIXEL_COUNT\n ) {\n if (parentElement instanceof HTMLElement) {\n this.parentElement = parentElement as PaperShaderElement;\n } else {\n throw new Error('Paper Shaders: parent element must be an HTMLElement');\n }\n\n if (!document.querySelector('style[data-paper-shader]')) {\n const styleElement = document.createElement('style');\n styleElement.innerHTML = defaultStyle;\n styleElement.setAttribute('data-paper-shader', '');\n document.head.prepend(styleElement);\n }\n\n // Create the canvas element and mount it into the provided element\n const canvasElement = document.createElement('canvas');\n this.canvasElement = canvasElement;\n this.parentElement.prepend(canvasElement);\n this.fragmentShader = fragmentShader;\n this.providedUniforms = uniforms;\n // Base our starting animation time on the provided frame value\n this.currentFrame = frame;\n this.minPixelRatio = minPixelRatio;\n this.maxPixelCount = maxPixelCount;\n\n const gl = canvasElement.getContext('webgl2', webGlContextAttributes);\n if (!gl) {\n throw new Error('Paper Shaders: WebGL is not supported in this browser');\n }\n this.gl = gl;\n\n this.initProgram();\n this.setupPositionAttribute();\n // Grab the locations of the uniforms in the fragment shader\n this.setupUniforms();\n // Put the user provided values into the uniforms\n this.setUniformValues(this.providedUniforms);\n // Set up the resize observer to handle window resizing and set u_resolution\n this.setupResizeObserver();\n // Set up the visual viewport change listener to handle zoom changes (pinch zoom and classic browser zoom)\n visualViewport?.addEventListener('resize', this.handleVisualViewportChange);\n\n // Set the animation speed after everything is ready to go\n this.setSpeed(speed);\n\n // Mark parent element as paper shader mount\n this.parentElement.setAttribute('data-paper-shader', '');\n\n // Add the shaderMount instance to the div mount element to make it easily accessible\n this.parentElement.paperShaderMount = this;\n\n // Listen for document visibility changes to pause the shader when the tab is hidden\n document.addEventListener('visibilitychange', this.handleDocumentVisibilityChange);\n }\n\n private initProgram = () => {\n const program = createProgram(this.gl, vertexShaderSource, this.fragmentShader);\n if (!program) return;\n this.program = program;\n };\n\n private setupPositionAttribute = () => {\n const positionAttributeLocation = this.gl.getAttribLocation(this.program!, 'a_position');\n const positionBuffer = this.gl.createBuffer();\n this.gl.bindBuffer(this.gl.ARRAY_BUFFER, positionBuffer);\n const positions = [-1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1];\n this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array(positions), this.gl.STATIC_DRAW);\n this.gl.enableVertexAttribArray(positionAttributeLocation);\n this.gl.vertexAttribPointer(positionAttributeLocation, 2, this.gl.FLOAT, false, 0, 0);\n };\n\n private setupUniforms = () => {\n // Create a map to store all uniform locations\n const uniformLocations: Record<string, WebGLUniformLocation | null> = {\n u_time: this.gl.getUniformLocation(this.program!, 'u_time'),\n u_pixelRatio: this.gl.getUniformLocation(this.program!, 'u_pixelRatio'),\n u_resolution: this.gl.getUniformLocation(this.program!, 'u_resolution'),\n };\n\n // Add locations for all provided uniforms\n Object.entries(this.providedUniforms).forEach(([key, value]) => {\n uniformLocations[key] = this.gl.getUniformLocation(this.program!, key);\n\n // For texture uniforms, also look for the aspect ratio uniform\n if (value instanceof HTMLImageElement) {\n const aspectRatioUniformName = `${key}AspectRatio`;\n uniformLocations[aspectRatioUniformName] = this.gl.getUniformLocation(this.program!, aspectRatioUniformName);\n }\n });\n\n this.uniformLocations = uniformLocations;\n };\n\n /**\n * The scale that we should render at.\n * - Used to target 2x rendering even on 1x screens for better antialiasing\n * - Prevents the virtual resolution from going beyond the maximum resolution\n * - Accounts for the page zoom level so we render in physical device pixels rather than CSS pixels\n */\n private renderScale = 1;\n private parentWidth = 0;\n private parentHeight = 0;\n private parentDevicePixelWidth = 0;\n private parentDevicePixelHeight = 0;\n private devicePixelsSupported = false;\n\n private resizeObserver: ResizeObserver | null = null;\n private setupResizeObserver = () => {\n this.resizeObserver = new ResizeObserver(([entry]) => {\n if (entry?.borderBoxSize[0]) {\n const physicalPixelSize = entry.devicePixelContentBoxSize?.[0];\n\n if (physicalPixelSize !== undefined) {\n this.devicePixelsSupported = true;\n this.parentDevicePixelWidth = physicalPixelSize.inlineSize;\n this.parentDevicePixelHeight = physicalPixelSize.blockSize;\n }\n\n this.parentWidth = entry.borderBoxSize[0].inlineSize;\n this.parentHeight = entry.borderBoxSize[0].blockSize;\n }\n\n this.handleResize();\n });\n\n this.resizeObserver.observe(this.parentElement);\n };\n\n // Visual viewport resize handler, mainly used to react to browser zoom changes.\n // Resize observer by itself does not react to pinch zoom, and although it usually\n // reacts to classic browser zoom, it's not guaranteed in edge cases.\n // Since timing between visual viewport changes and resize observer is complex\n // and because we'd like to know the device pixel sizes of elements, we just restart\n // the observer to get a guaranteed fresh callback regardless if it would have triggered or not.\n private handleVisualViewportChange = () => {\n this.resizeObserver?.disconnect();\n this.setupResizeObserver();\n\n // In case of debugging timing, from here on:\n // - animation frame 1: a paint after the visual viewport resize\n // - animation frame 2: a paint after the resize observer has been handled, if it was ever triggered\n };\n\n /** Resize handler for when the container div changes size or the max pixel count changes and we want to resize our canvas to match */\n private handleResize = () => {\n // Aim to render at least as many pixels as physically displayed\n // This will overshoot when the user zooms out, but that's acceptable\n\n let targetPixelWidth = 0;\n let targetPixelHeight = 0;\n\n // If window.devicePixelRatio is below 1, it's safe to say the browser is just zoomed out\n // We can use 1 as the minimum value not to upscale it needlessly to meet the min pixel ratio param\n const dpr = Math.max(1, window.devicePixelRatio);\n const pinchZoom = visualViewport?.scale ?? 1;\n\n if (this.devicePixelsSupported) {\n // Use the real pixel size if we know it, plus meet the min pixel ratio requirement and add in pinch zoom\n const scaleToMeetMinPixelRatio = Math.max(1, this.minPixelRatio / dpr);\n targetPixelWidth = this.parentDevicePixelWidth * scaleToMeetMinPixelRatio * pinchZoom;\n targetPixelHeight = this.parentDevicePixelHeight * scaleToMeetMinPixelRatio * pinchZoom;\n } else {\n // Otherwise try to approximate the element size in device pixels using devicePixelRatio.\n // (devicePixelRatio is imprecise and element's width/height may be fractional CSS sizes, not real pixels).\n let targetRenderScale = Math.max(dpr, this.minPixelRatio) * pinchZoom;\n\n if (this.isSafari) {\n // As of 2025, Safari reports physical devicePixelRatio, but other browsers add the current zoom level:\n // https://bugs.webkit.org/show_bug.cgi?id=124862\n //\n // In Safari we need to factor in the zoom level manually in order to set the target resolution.\n // To avoid sidebars upscaling the target resolution, set a minimum zoom level of 1.\n // This will render at higher resolution when zoomed out, but that's fine.\n // (We mostly care about maintaining good quality when zoomed in).\n const zoomLevel = bestGuessBrowserZoom();\n targetRenderScale *= Math.max(1, zoomLevel);\n }\n\n // Rounding the client width/height since they may be fractional in CSS layout values\n targetPixelWidth = Math.round(this.parentWidth) * targetRenderScale;\n targetPixelHeight = Math.round(this.parentHeight) * targetRenderScale;\n }\n\n // Prevent the total rendered pixel count from exceeding maxPixelCount\n const maxPixelCountHeadroom = Math.sqrt(this.maxPixelCount) / Math.sqrt(targetPixelWidth * targetPixelHeight);\n const scaleToMeetMaxPixelCount = Math.min(1, maxPixelCountHeadroom);\n const newWidth = Math.round(targetPixelWidth * scaleToMeetMaxPixelCount);\n const newHeight = Math.round(targetPixelHeight * scaleToMeetMaxPixelCount);\n const newRenderScale = newWidth / Math.round(this.parentWidth);\n\n if (\n this.canvasElement.width !== newWidth ||\n this.canvasElement.height !== newHeight ||\n this.renderScale !== newRenderScale // Usually, only render scale changes when the user zooms in/out\n ) {\n this.renderScale = newRenderScale;\n this.canvasElement.width = newWidth;\n this.canvasElement.height = newHeight;\n this.resolutionChanged = true;\n this.gl.viewport(0, 0, this.gl.canvas.width, this.gl.canvas.height);\n\n // this is necessary to avoid flashes while resizing (the next scheduled render will set uniforms)\n this.render(performance.now());\n }\n };\n\n private render = (currentTime: number) => {\n if (this.hasBeenDisposed) return;\n\n if (this.program === null) {\n console.warn('Tried to render before program or gl was initialized');\n return;\n }\n\n // Calculate the delta time\n const dt = currentTime - this.lastRenderTime;\n this.lastRenderTime = currentTime;\n // Increase the total animation time by dt * animationSpeed\n if (this.currentSpeed !== 0) {\n this.currentFrame += dt * this.currentSpeed;\n }\n\n // Clear the canvas\n this.gl.clear(this.gl.COLOR_BUFFER_BIT);\n\n // Update uniforms\n this.gl.useProgram(this.program);\n\n // Update the time uniform\n this.gl.uniform1f(this.uniformLocations.u_time!, this.currentFrame * 0.001);\n\n // If the resolution has changed, we need to update the uniform\n if (this.resolutionChanged) {\n this.gl.uniform2f(this.uniformLocations.u_resolution!, this.gl.canvas.width, this.gl.canvas.height);\n this.gl.uniform1f(this.uniformLocations.u_pixelRatio!, this.renderScale);\n this.resolutionChanged = false;\n }\n\n this.gl.drawArrays(this.gl.TRIANGLES, 0, 6);\n\n // Loop if we're animating\n if (this.currentSpeed !== 0) {\n this.requestRender();\n } else {\n this.rafId = null;\n }\n };\n\n private requestRender = () => {\n if (this.rafId !== null) {\n cancelAnimationFrame(this.rafId);\n }\n this.rafId = requestAnimationFrame(this.render);\n };\n\n /** Creates a texture from an image and sets it into a uniform value */\n private setTextureUniform = (uniformName: string, image: HTMLImageElement): void => {\n if (!image.complete || image.naturalWidth === 0) {\n throw new Error(`Paper Shaders: image for uniform ${uniformName} must be fully loaded`);\n }\n\n // Clean up existing texture if present\n const existingTexture = this.textures.get(uniformName);\n if (existingTexture) {\n this.gl.deleteTexture(existingTexture);\n }\n\n // Get texture unit\n if (!this.textureUnitMap.has(uniformName)) {\n this.textureUnitMap.set(uniformName, this.textureUnitMap.size);\n }\n const textureUnit = this.textureUnitMap.get(uniformName)!;\n // Activate correct texture unit before creating the texture\n this.gl.activeTexture(this.gl.TEXTURE0 + textureUnit);\n\n // Create and set up the new texture\n const texture = this.gl.createTexture();\n this.gl.bindTexture(this.gl.TEXTURE_2D, texture);\n\n // Set texture parameters\n this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_S, this.gl.CLAMP_TO_EDGE);\n this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_T, this.gl.CLAMP_TO_EDGE);\n this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.LINEAR);\n this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MAG_FILTER, this.gl.LINEAR);\n\n // Upload image to texture\n this.gl.texImage2D(this.gl.TEXTURE_2D, 0, this.gl.RGBA, this.gl.RGBA, this.gl.UNSIGNED_BYTE, image);\n\n if (uniformName !== \"u_noiseTexture\") {\n // this.gl.generateMipmap(this.gl.TEXTURE_2D);\n // this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.LINEAR_MIPMAP_LINEAR);\n }\n const error = this.gl.getError();\n if (error !== this.gl.NO_ERROR || texture === null) {\n console.error('Paper Shaders: WebGL error when uploading texture:', error);\n return;\n }\n\n // Store the texture\n this.textures.set(uniformName, texture);\n\n // Set up texture unit and uniform\n const location = this.uniformLocations[uniformName];\n if (location) {\n this.gl.uniform1i(location, textureUnit);\n\n // Calculate and set the aspect ratio uniform\n const aspectRatioUniformName = `${uniformName}AspectRatio`;\n const aspectRatioLocation = this.uniformLocations[aspectRatioUniformName];\n if (aspectRatioLocation) {\n const aspectRatio = image.naturalWidth / image.naturalHeight;\n this.gl.uniform1f(aspectRatioLocation, aspectRatio);\n }\n }\n };\n\n /** Utility: recursive equality test for all the uniforms */\n private areUniformValuesEqual = (a: any, b: any): boolean => {\n if (a === b) return true;\n if (Array.isArray(a) && Array.isArray(b) && a.length === b.length) {\n return a.every((val, i) => this.areUniformValuesEqual(val, (b as any)[i]));\n }\n return false;\n };\n\n /** Sets the provided uniform values into the WebGL program, can be a partial list of uniforms that have changed */\n private setUniformValues = (updatedUniforms: ShaderMountUniforms) => {\n this.gl.useProgram(this.program);\n Object.entries(updatedUniforms).forEach(([key, value]) => {\n // Grab the value to use in the uniform cache\n let cacheValue: ShaderMountUniforms[keyof ShaderMountUniforms] | string = value;\n if (value instanceof HTMLImageElement) {\n // Images use their src for the cache value to save memory\n cacheValue = `${value.src.slice(0, 200)}|${value.naturalWidth}x${value.naturalHeight}`;\n }\n\n // Check if the uniform value has changed and, if not, bail early to avoid extra work\n if (this.areUniformValuesEqual(this.uniformCache[key], cacheValue)) return;\n // Update the uniform cache if we are still here\n this.uniformCache[key] = cacheValue;\n\n const location = this.uniformLocations[key];\n if (!location) {\n console.warn(`Uniform location for ${key} not found`);\n return;\n }\n\n if (value instanceof HTMLImageElement) {\n // Texture case, requires a good amount of code so it gets its own function:\n this.setTextureUniform(key, value);\n } else if (Array.isArray(value)) {\n // Array case\n let flatArray: number[] | null = null;\n let valueLength: number | null = null;\n\n // If it's an array of same-sized arrays, flatten it down so we can set the uniform\n if (value[0] !== undefined && Array.isArray(value[0])) {\n const firstChildLength = value[0].length;\n if (value.every((arr) => (arr as number[]).length === firstChildLength)) {\n // Array of same-sized arrays case, flattens the array sets it\n flatArray = value.flat();\n valueLength = firstChildLength;\n } else {\n console.warn(`All child arrays must be the same length for ${key}`);\n return;\n }\n } else {\n // Array of primitive values case, supports 2, 3, 4, 9, 16 length arrays\n flatArray = value as number[];\n valueLength = flatArray.length;\n }\n\n // Set the uniform based on array length... supports 2, 3, 4, 9, 16 length arrays of primitive values\n // or arbitrary length arrays of arrays\n switch (valueLength) {\n case 2:\n this.gl.uniform2fv(location, flatArray);\n break;\n case 3:\n this.gl.uniform3fv(location, flatArray);\n break;\n case 4:\n this.gl.uniform4fv(location, flatArray);\n break;\n case 9:\n this.gl.uniformMatrix3fv(location, false, flatArray);\n break;\n case 16:\n this.gl.uniformMatrix4fv(location, false, flatArray);\n break;\n default:\n console.warn(`Unsupported uniform array length: ${valueLength}`);\n }\n } else if (typeof value === 'number') {\n // Number case, supports floats and ints\n this.gl.uniform1f(location, value);\n } else if (typeof value === 'boolean') {\n // Boolean case, supports true and false\n this.gl.uniform1i(location, value ? 1 : 0);\n } else {\n // May happen on the server for SSR when undefined images are passed in\n console.warn(`Unsupported uniform type for ${key}: ${typeof value}`);\n }\n });\n };\n\n /** Gets the current total animation time from 0ms */\n public getCurrentFrame = (): number => {\n return this.currentFrame;\n };\n\n /** Set a frame to get a deterministic result, frames are literally just milliseconds from zero since the animation started */\n public setFrame = (newFrame: number): void => {\n this.currentFrame = newFrame;\n this.lastRenderTime = performance.now();\n this.render(performance.now());\n };\n\n /** Set an animation speed (or 0 to stop animation) */\n public setSpeed = (newSpeed = 1): void => {\n // Set the new animation speed\n this.speed = newSpeed;\n this.setCurrentSpeed(document.hidden ? 0 : newSpeed);\n };\n\n private setCurrentSpeed = (newSpeed: number): void => {\n this.currentSpeed = newSpeed;\n\n if (this.rafId === null && newSpeed !== 0) {\n // Moving from 0 to animating, kick off a new rAF loop\n this.lastRenderTime = performance.now();\n this.rafId = requestAnimationFrame(this.render);\n }\n\n if (this.rafId !== null && newSpeed === 0) {\n // Moving from animating to not animating, cancel the rAF loop\n cancelAnimationFrame(this.rafId);\n this.rafId = null;\n }\n };\n\n /** Set the maximum pixel count for the shader, this will limit the number of pixels that will be rendered */\n public setMaxPixelCount = (newMaxPixelCount: number = DEFAULT_MAX_PIXEL_COUNT): void => {\n this.maxPixelCount = newMaxPixelCount;\n\n this.handleResize();\n };\n\n /** Set the minimum pixel ratio for the shader */\n public setMinPixelRatio = (newMinPixelRatio: number = 2): void => {\n this.minPixelRatio = newMinPixelRatio;\n\n this.handleResize();\n };\n\n /** Update the uniforms that are provided by the outside shader, can be a partial set with only the uniforms that have changed */\n public setUniforms = (newUniforms: ShaderMountUniforms): void => {\n this.setUniformValues(newUniforms);\n this.providedUniforms = { ...this.providedUniforms, ...newUniforms };\n\n this.render(performance.now());\n };\n\n private handleDocumentVisibilityChange = () => {\n this.setCurrentSpeed(document.hidden ? 0 : this.speed);\n };\n\n /** Dispose of the shader mount, cleaning up all of the WebGL resources */\n public dispose = (): void => {\n // Immediately mark as disposed to prevent future renders from leaking in\n this.hasBeenDisposed = true;\n\n // Cancel the rAF loop\n if (this.rafId !== null) {\n cancelAnimationFrame(this.rafId);\n this.rafId = null;\n }\n\n if (this.gl && this.program) {\n // Clean up all textures\n this.textures.forEach((texture) => {\n this.gl.deleteTexture(texture);\n });\n this.textures.clear();\n\n this.gl.deleteProgram(this.program);\n this.program = null;\n\n // Reset the WebGL context\n this.gl.bindBuffer(this.gl.ARRAY_BUFFER, null);\n this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, null);\n this.gl.bindRenderbuffer(this.gl.RENDERBUFFER, null);\n this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, null);\n\n // Clear any errors\n this.gl.getError();\n }\n\n if (this.resizeObserver) {\n this.resizeObserver.disconnect();\n this.resizeObserver = null;\n }\n\n visualViewport?.removeEventListener('resize', this.handleVisualViewportChange);\n document.removeEventListener('visibilitychange', this.handleDocumentVisibilityChange);\n\n this.uniformLocations = {};\n\n // Remove the shader from the div wrapper element\n this.canvasElement.remove();\n // Free up the reference to self to enable garbage collection\n delete this.parentElement.paperShaderMount;\n };\n}\n\nfunction createShader(gl: WebGL2RenderingContext, type: number, source: string): WebGLShader | null {\n const shader = gl.createShader(type);\n if (!shader) return null;\n\n gl.shaderSource(shader, source);\n gl.compileShader(shader);\n\n if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {\n console.error('An error occurred compiling the shaders: ' + gl.getShaderInfoLog(shader));\n gl.deleteShader(shader);\n return null;\n }\n\n return shader;\n}\n\nfunction createProgram(\n gl: WebGL2RenderingContext,\n vertexShaderSource: string,\n fragmentShaderSource: string\n): WebGLProgram | null {\n const format = gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.MEDIUM_FLOAT);\n const precision = format ? format.precision : null;\n // MEDIUM_FLOAT precision can be 10, 16 or 23 bits depending on device;\n // Shaders fail on 10 bit (and 16 bit is hard to test) => we force 23-bit by switching to highp\n if (precision && precision < 23) {\n vertexShaderSource = vertexShaderSource.replace(/precision\\s+(lowp|mediump)\\s+float;/g, 'precision highp float;');\n fragmentShaderSource = fragmentShaderSource\n .replace(/precision\\s+(lowp|mediump)\\s+float/g, 'precision highp float')\n .replace(/\\b(uniform|varying|attribute)\\s+(lowp|mediump)\\s+(\\w+)/g, '$1 highp $3');\n }\n\n const vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexShaderSource);\n const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSource);\n\n if (!vertexShader || !fragmentShader) return null;\n\n const program = gl.createProgram();\n if (!program) return null;\n\n gl.attachShader(program, vertexShader);\n gl.attachShader(program, fragmentShader);\n gl.linkProgram(program);\n\n if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {\n console.error('Unable to initialize the shader program: ' + gl.getProgramInfoLog(program));\n gl.deleteProgram(program);\n gl.deleteShader(vertexShader);\n gl.deleteShader(fragmentShader);\n return null;\n }\n\n // Clean up shaders after successful linking\n gl.detachShader(program, vertexShader);\n gl.detachShader(program, fragmentShader);\n gl.deleteShader(vertexShader);\n gl.deleteShader(fragmentShader);\n\n return program;\n}\n\nconst defaultStyle = `@layer paper-shaders {\n :where([data-paper-shader]) {\n isolation: isolate;\n position: relative;\n\n & canvas {\n contain: strict;\n display: block;\n position: absolute;\n inset: 0;\n z-index: -1;\n width: 100%;\n height: 100%;\n border-radius: inherit;\n corner-shape: inherit;\n }\n }\n}`;\n\n/** The parent `<div>` element that has a ShaderMount available on it */\nexport interface PaperShaderElement extends HTMLElement {\n paperShaderMount: ShaderMount | undefined;\n}\n\n/** Check if an element is a Paper shader element */\nexport function isPaperShaderElement(element: HTMLElement): element is PaperShaderElement {\n return 'paperShaderMount' in element;\n}\n\n/**\n * Uniform types that we support to be auto-mapped into the fragment shader\n *\n * We accept undefined as a convenience for server rendering, when some things may be undefined\n * We just skip setting the uniform if it's undefined. This allows the shader mount to still take up space during server rendering\n */\nexport interface ShaderMountUniforms {\n [key: string]: boolean | number | number[] | number[][] | HTMLImageElement | undefined;\n}\n\nexport interface ShaderMotionParams {\n speed?: number;\n frame?: number;\n}\n\nexport type ShaderPreset<T> = {\n name: string;\n params: Required<T>;\n};\n\nexport type ImageShaderPreset<T> = {\n name: string;\n /**\n * Params for the shader excluding the image.\n * Image is excluded as it isn't considered a preset,\n * e.g. when switching between presets it shouldn't switch the image.\n *\n * While we exclude images from presets they should still be set with a default prop value so the code-first usage of shaders remains great.\n */\n params: Required<Omit<T, 'image'>>;\n};\n\nfunction isSafari() {\n const ua = navigator.userAgent.toLowerCase();\n return ua.includes('safari') && !ua.includes('chrome') && !ua.includes('android');\n}\n\n// Zoom level can be estimated comparing the browser's outerWidth and the viewport width.\n// It's nowhere near perfect because it's affected by the presence of browser sidebars,\n// like a vertical web inspector or Arc's sidebar. Also, both outerWidth and innerWidth\n// are integers, which would almost never give us a perfect ratio at face values.\n//\n// Still, this is pretty accurate in the vast majority of cases.\n//\n// Note 1:\n// Avoid innerWidth, use visualViewport.width instead.\n// - innerWidth is affected by pinch zoom in Safari, but not other browsers.\n// visualViewport.width works consistently in all browsers.\n// - innerWidth is rounded to integer, but not visualViewport.width.\n// - visualViewport.width is affected by hard scrollbars, so they need to be added manually\n//\n// Note 2:\n// Opening a sidebar in Safari like web inspector or bookmarks will throw off the zoom\n// level detection and result in a larger target resolution. Not a concern in real-world usage\n// with Safari, but we'd rather not try to detect zoom levels with other browsers\n// (e.g. Arc always has a sidebar, which affects outerWidth vs visualViewport.width).\nfunction bestGuessBrowserZoom() {\n const viewportScale = visualViewport?.scale ?? 1;\n const viewportWidth = visualViewport?.width ?? window.innerWidth;\n const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;\n const innerWidth = viewportScale * viewportWidth + scrollbarWidth;\n\n // outerWidth and innerWidth are always integers so we won't often get the original zoom ratio\n // E.g. given a 125% zoom, outerWidth = 1657, innerWidth = 1325, 1657 / 1325 = 1.2505660377\n // We check for common zoom levels and return the closest one if found.\n\n const ratio = outerWidth / innerWidth;\n const zoomPercentageRounded = Math.round(100 * ratio);\n\n // All zoom levels divisible by 5%\n if (zoomPercentageRounded % 5 === 0) {\n return zoomPercentageRounded / 100;\n }\n\n // 33% zoom\n if (zoomPercentageRounded === 33) {\n return 1 / 3;\n }\n\n // 67% zoom\n if (zoomPercentageRounded === 67) {\n return 2 / 3;\n }\n\n // 133% zoom\n if (zoomPercentageRounded === 133) {\n return 4 / 3;\n }\n\n return ratio;\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["vertexShaderSource"]
7
+ }
@@ -0,0 +1,35 @@
1
+ export declare const sizingVariablesDeclaration = "\nin vec2 v_objectUV;\nin vec2 v_responsiveUV;\nin vec2 v_responsiveBoxGivenSize;\nin vec2 v_patternUV;\nin vec2 v_imageUV;";
2
+ export declare const sizingDebugVariablesDeclaration = "\nin vec2 v_objectBoxSize;\nin vec2 v_objectHelperBox;\nin vec2 v_responsiveBoxSize;\nin vec2 v_responsiveHelperBox;\nin vec2 v_patternBoxSize;\nin vec2 v_patternHelperBox;";
3
+ export declare const sizingUniformsDeclaration = "\nuniform float u_originX;\nuniform float u_originY;\nuniform float u_worldWidth;\nuniform float u_worldHeight;\nuniform float u_fit;\n\nuniform float u_scale;\nuniform float u_rotation;\nuniform float u_offsetX;\nuniform float u_offsetY;";
4
+ export declare const sizingUV = "\n\n vec2 uv = gl_FragCoord.xy / u_resolution.xy;\n #ifdef USE_PIXELIZATION\n float pxSize = u_pxSize * u_pixelRatio;\n vec2 pxSizeUv = gl_FragCoord.xy;\n pxSizeUv -= .5 * u_resolution;\n pxSizeUv /= pxSize;\n uv = floor(pxSizeUv) * pxSize / u_resolution.xy; \n uv += .5;\n #endif\n uv -= .5;\n\n \n // ===================================================\n // sizing params shared between objects and patterns\n \n vec2 boxOrigin = vec2(.5 - u_originX, u_originY - .5);\n vec2 givenBoxSize = vec2(u_worldWidth, u_worldHeight);\n givenBoxSize = max(givenBoxSize, vec2(1.)) * u_pixelRatio;\n float r = u_rotation * 3.14159265358979323846 / 180.;\n mat2 graphicRotation = mat2(cos(r), sin(r), -sin(r), cos(r));\n vec2 graphicOffset = vec2(-u_offsetX, u_offsetY);\n\n \n // ===================================================\n // Sizing api for objects (graphics with fixed ratio)\n\n #ifdef USE_OBJECT_SIZING\n float fixedRatio = 1.;\n vec2 fixedRatioBoxGivenSize = vec2(\n (u_worldWidth == 0.) ? u_resolution.x : givenBoxSize.x,\n (u_worldHeight == 0.) ? u_resolution.y : givenBoxSize.y\n );\n vec2 objectBoxSize = vec2(0.);\n // fit = none\n objectBoxSize.x = fixedRatio * min(fixedRatioBoxGivenSize.x / fixedRatio, fixedRatioBoxGivenSize.y);\n if (u_fit == 1.) { // fit = contain\n objectBoxSize.x = fixedRatio * min(u_resolution.x / fixedRatio, u_resolution.y);\n } else if (u_fit == 2.) { // fit = cover\n objectBoxSize.x = fixedRatio * max(u_resolution.x / fixedRatio, u_resolution.y);\n }\n objectBoxSize.y = objectBoxSize.x / fixedRatio;\n vec2 objectWorldScale = u_resolution.xy / objectBoxSize;\n \n #ifdef ADD_HELPERS\n vec2 objectHelperBox = gl_FragCoord.xy / u_resolution.xy;\n objectHelperBox -= .5;\n objectHelperBox *= objectWorldScale;\n objectHelperBox += boxOrigin * (objectWorldScale - 1.); \n #endif\n \n vec2 objectUV = uv;\n objectUV *= objectWorldScale;\n objectUV += boxOrigin * (objectWorldScale - 1.);\n objectUV += vec2(-u_offsetX, u_offsetY);\n objectUV /= u_scale;\n objectUV = graphicRotation * objectUV;\n #endif\n \n // ===================================================\n \n // ===================================================\n // Sizing api for patterns (graphics respecting u_worldWidth / u_worldHeight ratio)\n \n #ifdef USE_PATTERN_SIZING\n float patternBoxRatio = givenBoxSize.x / givenBoxSize.y;\n vec2 patternBoxGivenSize = vec2(\n (u_worldWidth == 0.) ? u_resolution.x : givenBoxSize.x,\n (u_worldHeight == 0.) ? u_resolution.y : givenBoxSize.y\n );\n vec2 patternBoxSize = vec2(0.);\n // fit = none\n patternBoxSize.x = patternBoxRatio * min(patternBoxGivenSize.x / patternBoxRatio, patternBoxGivenSize.y);\n float patternWorldNoFitBoxWidth = patternBoxSize.x;\n if (u_fit == 1.) { // fit = contain\n patternBoxSize.x = patternBoxRatio * min(u_resolution.x / patternBoxRatio, u_resolution.y);\n } else if (u_fit == 2.) { // fit = cover\n patternBoxSize.x = patternBoxRatio * max(u_resolution.x / patternBoxRatio, u_resolution.y);\n }\n patternBoxSize.y = patternBoxSize.x / patternBoxRatio;\n vec2 patternWorldScale = u_resolution.xy / patternBoxSize;\n \n #ifdef ADD_HELPERS \n vec2 patternHelperBox = gl_FragCoord.xy / u_resolution.xy;\n patternHelperBox -= .5;\n patternHelperBox *= patternWorldScale;\n patternHelperBox += boxOrigin * (patternWorldScale - 1.); \n #endif\n \n vec2 patternUV = uv;\n patternUV += vec2(-u_offsetX, u_offsetY) / patternWorldScale;\n patternUV += boxOrigin;\n patternUV -= boxOrigin / patternWorldScale;\n patternUV *= u_resolution.xy;\n patternUV /= u_pixelRatio;\n if (u_fit > 0.) {\n patternUV *= (patternWorldNoFitBoxWidth / patternBoxSize.x);\n }\n patternUV /= u_scale;\n patternUV = graphicRotation * patternUV;\n patternUV += boxOrigin / patternWorldScale;\n patternUV -= boxOrigin;\n patternUV += .5;\n #endif\n \n // ===================================================\n \n // ===================================================\n // Sizing api for image filters\n \n #ifdef USE_IMAGE_SIZING\n\n vec2 imageBoxSize;\n if (u_fit == 1.) { // contain\n imageBoxSize.x = min(u_resolution.x / u_imageAspectRatio, u_resolution.y) * u_imageAspectRatio;\n } else if (u_fit == 2.) { // cover\n imageBoxSize.x = max(u_resolution.x / u_imageAspectRatio, u_resolution.y) * u_imageAspectRatio;\n } else {\n imageBoxSize.x = min(10.0, 10.0 / u_imageAspectRatio * u_imageAspectRatio);\n }\n imageBoxSize.y = imageBoxSize.x / u_imageAspectRatio;\n vec2 imageBoxScale = u_resolution.xy / imageBoxSize;\n\n #ifdef ADD_HELPERS\n vec2 imageHelperBox = uv;\n imageHelperBox *= imageBoxScale;\n imageHelperBox += boxOrigin * (imageBoxScale - 1.);\n #endif\n\n vec2 imageUV = uv;\n imageUV *= imageBoxScale;\n imageUV += boxOrigin * (imageBoxScale - 1.);\n imageUV += graphicOffset;\n imageUV /= u_scale;\n imageUV.x *= u_imageAspectRatio;\n imageUV = graphicRotation * imageUV;\n imageUV.x /= u_imageAspectRatio;\n \n imageUV += .5;\n imageUV.y = 1. - imageUV.y;\n #endif\n";
5
+ export declare const drawSizingHelpers = "\n vec2 worldBoxDist = abs(helperBox);\n float boxStroke = (step(max(worldBoxDist.x, worldBoxDist.y), .5) - step(max(worldBoxDist.x, worldBoxDist.y), .495));\n color.rgb = mix(color.rgb, vec3(1., 0., 0.), boxStroke);\n opacity += boxStroke;\n\n vec2 boxOriginCopy = vec2(.5 - u_originX, u_originY - .5);\n vec2 boxOriginDist = helperBox + boxOriginCopy;\n boxOriginDist.x *= (boxSize.x / boxSize.y);\n float boxOriginPoint = 1. - smoothstep(0., .05, length(boxOriginDist));\n \n vec2 graphicOriginPointDist = helperBox + vec2(-u_offsetX, u_offsetY);\n graphicOriginPointDist.x *= (boxSize.x / boxSize.y);\n float graphicOriginPoint = 1. - smoothstep(0., .05, length(graphicOriginPointDist));\n \n color.rgb = mix(color.rgb, vec3(0., 1., 0.), boxOriginPoint);\n opacity += boxOriginPoint;\n color.rgb = mix(color.rgb, vec3(0., 0., 1.), graphicOriginPoint);\n opacity += graphicOriginPoint;\n";
6
+ export interface ShaderSizingUniforms {
7
+ u_fit: (typeof ShaderFitOptions)[ShaderFit];
8
+ u_scale: number;
9
+ u_rotation: number;
10
+ u_originX: number;
11
+ u_originY: number;
12
+ u_offsetX: number;
13
+ u_offsetY: number;
14
+ u_worldWidth: number;
15
+ u_worldHeight: number;
16
+ }
17
+ export interface ShaderSizingParams {
18
+ fit?: 'none' | 'contain' | 'cover';
19
+ scale?: number;
20
+ rotation?: number;
21
+ originX?: number;
22
+ originY?: number;
23
+ offsetX?: number;
24
+ offsetY?: number;
25
+ worldWidth?: number;
26
+ worldHeight?: number;
27
+ }
28
+ export declare const defaultObjectSizing: Required<ShaderSizingParams>;
29
+ export declare const defaultPatternSizing: Required<ShaderSizingParams>;
30
+ export declare const ShaderFitOptions: {
31
+ readonly none: 0;
32
+ readonly contain: 1;
33
+ readonly cover: 2;
34
+ };
35
+ export type ShaderFit = keyof typeof ShaderFitOptions;