@frapx/shader 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,189 @@
1
+ # @frapx/shader
2
+
3
+ Lightweight WebGL1 shader background runtime for websites.
4
+
5
+ This is not a scene, camera, or mesh abstraction. It creates and manages a WebGL canvas for an existing DOM region, then lets you drive fragment shaders with built-in and custom uniforms.
6
+
7
+ ## Install
8
+
9
+ ```sh
10
+ pnpm add @frapx/shader
11
+ ```
12
+
13
+ ## Basic Usage
14
+
15
+ ```ts
16
+ import { createShaderBackground, glsl } from "@frapx/shader";
17
+
18
+ const fx = createShaderBackground({
19
+ target: ".hero",
20
+ fragment: glsl`
21
+ precision highp float;
22
+
23
+ uniform vec2 u_resolution;
24
+ uniform vec2 u_pointerUv;
25
+ uniform float u_time;
26
+
27
+ void main() {
28
+ vec2 uv = gl_FragCoord.xy / u_resolution;
29
+ float glow = 0.5 + 0.5 * sin(u_time + uv.x * 8.0);
30
+ gl_FragColor = vec4(uv.x, u_pointerUv.y, glow, 1.0);
31
+ }
32
+ `
33
+ });
34
+ ```
35
+
36
+ JS uniform names omit `u_`; GLSL uniforms use `u_`.
37
+
38
+ ```ts
39
+ fx.setUniform("progress", 0.4);
40
+ // GLSL: uniform float u_progress;
41
+ ```
42
+
43
+ ## Textures
44
+
45
+ ```ts
46
+ const fx = createShaderBackground({
47
+ target: ".hero",
48
+ fragment,
49
+ textures: {
50
+ image: "/hero.webp",
51
+ mask: {
52
+ source: "/mask.webp",
53
+ wrap: "clamp",
54
+ filter: "linear",
55
+ flipY: true
56
+ }
57
+ }
58
+ });
59
+ ```
60
+
61
+ Each texture creates a sampler and size uniform:
62
+
63
+ ```glsl
64
+ uniform sampler2D u_image;
65
+ uniform vec2 u_imageSize;
66
+ ```
67
+
68
+ Supported v1 sources are image URL, `HTMLImageElement`, and `HTMLCanvasElement`.
69
+
70
+ ## External Uniforms
71
+
72
+ Scroll is intentionally not built in. Use any scroll or animation library and push values into uniforms.
73
+
74
+ ```ts
75
+ const fx = createShaderBackground({
76
+ target: ".hero",
77
+ fragment,
78
+ uniforms: {
79
+ progress: 0,
80
+ velocity: 0
81
+ },
82
+ renderMode: "demand"
83
+ });
84
+
85
+ window.addEventListener("scroll", () => {
86
+ const max = document.documentElement.scrollHeight - innerHeight;
87
+ fx.setUniform("progress", max > 0 ? scrollY / max : 0);
88
+ });
89
+ ```
90
+
91
+ ## Render Modes
92
+
93
+ ```ts
94
+ createShaderBackground({
95
+ target: ".hero",
96
+ fragment,
97
+ renderMode: "always" // default
98
+ });
99
+ ```
100
+
101
+ `"demand"` renders when uniforms, pointer state, texture load, or resize changes. `fx.render()` is also available.
102
+
103
+ ## Built-In Uniforms
104
+
105
+ ```glsl
106
+ uniform vec2 u_resolution; // drawing buffer px
107
+ uniform vec2 u_viewportSize; // CSS px
108
+ uniform float u_pixelRatio;
109
+ uniform float u_time; // seconds, paused offscreen
110
+ uniform float u_delta; // seconds, clamped to 0.1
111
+ uniform vec2 u_pointer; // drawing buffer px, bottom-left origin
112
+ uniform vec2 u_pointerUv; // 0..1, bottom-left origin
113
+ uniform float u_pointerActive; // 0 or 1
114
+ ```
115
+
116
+ ## Options
117
+
118
+ ```ts
119
+ createShaderBackground({
120
+ target: ".hero",
121
+ canvas: existingCanvas,
122
+ fragment,
123
+ vertex,
124
+ uniforms,
125
+ textures,
126
+ layer: "background",
127
+ autoStart: true,
128
+ pauseWhenOffscreen: true,
129
+ renderMode: "always",
130
+ dpr: "auto",
131
+ maxDpr: 2,
132
+ autoResize: true,
133
+ debug: false,
134
+ canvasClass: "hero-fx",
135
+ canvasStyle: {
136
+ opacity: "0.8",
137
+ mixBlendMode: "screen"
138
+ },
139
+ onReady(instance) {},
140
+ onError(error) {},
141
+ onBeforeRender(state) {},
142
+ onAfterRender(state) {}
143
+ });
144
+ ```
145
+
146
+ `layer: "background"` inserts the canvas as the first child with `z-index: 0`. `layer: "overlay"` inserts it as the last child with `z-index: 1`. Existing child styles are not changed.
147
+
148
+ ## Instance API
149
+
150
+ ```ts
151
+ fx.ready;
152
+ fx.start();
153
+ fx.stop();
154
+ fx.render();
155
+ fx.resize();
156
+ fx.destroy();
157
+ fx.setUniform("progress", 0.5);
158
+ fx.setUniforms({ progress: 0.5, color: [1, 0, 0] });
159
+ ```
160
+
161
+ Unsupported environments return a no-op instance. `ready` rejects and `debug: true` prints warnings.
162
+
163
+ ## GLSL Helpers
164
+
165
+ ```ts
166
+ import { glsl, glslUtils } from "@frapx/shader/glsl";
167
+
168
+ const fragment = glsl`
169
+ precision highp float;
170
+ ${glslUtils.coverUv}
171
+
172
+ uniform vec2 u_resolution;
173
+ uniform vec2 u_imageSize;
174
+ uniform sampler2D u_image;
175
+
176
+ void main() {
177
+ vec2 uv = gl_FragCoord.xy / u_resolution;
178
+ gl_FragColor = texture2D(u_image, coverUv(uv, u_resolution, u_imageSize));
179
+ }
180
+ `;
181
+ ```
182
+
183
+ ## SSR Notes
184
+
185
+ The package is safe to import during SSR. Calling `createShaderBackground()` without a browser returns a no-op instance whose `ready` promise rejects.
186
+
187
+ ## Browser Support
188
+
189
+ v1 targets WebGL1 / GLSL ES 1.00. WebGL2-only shader syntax is out of scope.
@@ -0,0 +1,35 @@
1
+ // src/glsl.ts
2
+ var glsl = (strings, ...values) => String.raw({ raw: strings }, ...values);
3
+ var glslUtils = {
4
+ coverUv: glsl`
5
+ vec2 coverUv(vec2 uv, vec2 viewportSize, vec2 textureSize) {
6
+ vec2 ratio = vec2(
7
+ min((viewportSize.x / viewportSize.y) / (textureSize.x / textureSize.y), 1.0),
8
+ min((viewportSize.y / viewportSize.x) / (textureSize.y / textureSize.x), 1.0)
9
+ );
10
+ return uv * ratio + (1.0 - ratio) * 0.5;
11
+ }
12
+ `,
13
+ containUv: glsl`
14
+ vec2 containUv(vec2 uv, vec2 viewportSize, vec2 textureSize) {
15
+ vec2 ratio = vec2(
16
+ max((viewportSize.x / viewportSize.y) / (textureSize.x / textureSize.y), 1.0),
17
+ max((viewportSize.y / viewportSize.x) / (textureSize.y / textureSize.x), 1.0)
18
+ );
19
+ return uv * ratio + (1.0 - ratio) * 0.5;
20
+ }
21
+ `,
22
+ rotate2d: glsl`
23
+ mat2 rotate2d(float angle) {
24
+ float s = sin(angle);
25
+ float c = cos(angle);
26
+ return mat2(c, -s, s, c);
27
+ }
28
+ `
29
+ };
30
+
31
+ export {
32
+ glsl,
33
+ glslUtils
34
+ };
35
+ //# sourceMappingURL=chunk-PD6KUFXI.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/glsl.ts"],"sourcesContent":["export const glsl = (\n strings: TemplateStringsArray,\n ...values: unknown[]\n): string => String.raw({ raw: strings }, ...values);\n\nexport const glslUtils = {\n coverUv: glsl`\nvec2 coverUv(vec2 uv, vec2 viewportSize, vec2 textureSize) {\n vec2 ratio = vec2(\n min((viewportSize.x / viewportSize.y) / (textureSize.x / textureSize.y), 1.0),\n min((viewportSize.y / viewportSize.x) / (textureSize.y / textureSize.x), 1.0)\n );\n return uv * ratio + (1.0 - ratio) * 0.5;\n}\n`,\n containUv: glsl`\nvec2 containUv(vec2 uv, vec2 viewportSize, vec2 textureSize) {\n vec2 ratio = vec2(\n max((viewportSize.x / viewportSize.y) / (textureSize.x / textureSize.y), 1.0),\n max((viewportSize.y / viewportSize.x) / (textureSize.y / textureSize.x), 1.0)\n );\n return uv * ratio + (1.0 - ratio) * 0.5;\n}\n`,\n rotate2d: glsl`\nmat2 rotate2d(float angle) {\n float s = sin(angle);\n float c = cos(angle);\n return mat2(c, -s, s, c);\n}\n`\n} as const;\n"],"mappings":";AAAO,IAAM,OAAO,CAClB,YACG,WACQ,OAAO,IAAI,EAAE,KAAK,QAAQ,GAAG,GAAG,MAAM;AAE5C,IAAM,YAAY;AAAA,EACvB,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAST,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASX,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAOZ;","names":[]}
package/dist/glsl.d.ts ADDED
@@ -0,0 +1,8 @@
1
+ declare const glsl: (strings: TemplateStringsArray, ...values: unknown[]) => string;
2
+ declare const glslUtils: {
3
+ readonly coverUv: string;
4
+ readonly containUv: string;
5
+ readonly rotate2d: string;
6
+ };
7
+
8
+ export { glsl, glslUtils };
package/dist/glsl.js ADDED
@@ -0,0 +1,9 @@
1
+ import {
2
+ glsl,
3
+ glslUtils
4
+ } from "./chunk-PD6KUFXI.js";
5
+ export {
6
+ glsl,
7
+ glslUtils
8
+ };
9
+ //# sourceMappingURL=glsl.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,95 @@
1
+ export { glsl, glslUtils } from './glsl.js';
2
+
3
+ type ShaderLayer = "background" | "overlay";
4
+ type RenderMode = "always" | "demand";
5
+ type ShaderStatus = "idle" | "loading" | "ready" | "running" | "paused" | "error" | "unsupported" | "context-lost" | "destroyed";
6
+ type DprOption = "auto" | number | (() => number);
7
+ type UniformScalar = number;
8
+ type UniformVec2 = [number, number] | readonly [number, number];
9
+ type UniformVec3 = [number, number, number] | readonly [number, number, number];
10
+ type UniformVec4 = [number, number, number, number] | readonly [number, number, number, number];
11
+ type UniformType = "float" | "vec2" | "vec3" | "vec4" | "mat3" | "mat4";
12
+ type ExplicitUniformInput = {
13
+ type: UniformType;
14
+ value: number[] | Float32Array;
15
+ };
16
+ type UniformInput = UniformScalar | UniformVec2 | UniformVec3 | UniformVec4 | Float32Array | ExplicitUniformInput;
17
+ type UniformInputMap = Record<string, UniformInput>;
18
+ type UniformRuntimeValue<T> = T extends ExplicitUniformInput ? ExplicitUniformInput : T extends Float32Array ? Float32Array : T extends readonly [number, number] ? UniformVec2 : T extends readonly [number, number, number] ? UniformVec3 : T extends readonly [number, number, number, number] ? UniformVec4 : T extends number ? number : UniformInput;
19
+ type TextureSource = string | HTMLImageElement | HTMLCanvasElement;
20
+ type TextureOptions = {
21
+ source: TextureSource;
22
+ wrap?: "clamp" | "repeat" | "mirror";
23
+ filter?: "nearest" | "linear";
24
+ flipY?: boolean;
25
+ };
26
+ type TextureInput = TextureSource | TextureOptions;
27
+ type TextureMap = Record<string, TextureInput>;
28
+ type RenderState<TUniforms extends UniformInputMap = UniformInputMap> = {
29
+ instance: ShaderBackgroundInstance<TUniforms>;
30
+ gl: WebGLRenderingContext;
31
+ canvas: HTMLCanvasElement;
32
+ time: number;
33
+ delta: number;
34
+ frame: number;
35
+ width: number;
36
+ height: number;
37
+ viewportWidth: number;
38
+ viewportHeight: number;
39
+ pixelRatio: number;
40
+ };
41
+ type CreateShaderBackgroundOptions<TUniforms extends UniformInputMap = UniformInputMap> = {
42
+ target?: string | Element;
43
+ canvas?: HTMLCanvasElement;
44
+ fragment: string;
45
+ vertex?: string;
46
+ uniforms?: TUniforms;
47
+ textures?: TextureMap;
48
+ layer?: ShaderLayer;
49
+ autoStart?: boolean;
50
+ pauseWhenOffscreen?: boolean;
51
+ renderMode?: RenderMode;
52
+ dpr?: DprOption;
53
+ maxDpr?: number;
54
+ autoResize?: boolean;
55
+ debug?: boolean;
56
+ canvasClass?: string;
57
+ canvasStyle?: Partial<CSSStyleDeclaration>;
58
+ onReady?: (instance: ShaderBackgroundInstance<TUniforms>) => void;
59
+ onError?: (error: Error) => void;
60
+ onBeforeRender?: (state: RenderState<TUniforms>) => void;
61
+ onAfterRender?: (state: RenderState<TUniforms>) => void;
62
+ };
63
+ type ShaderBackgroundInstance<TUniforms extends UniformInputMap = UniformInputMap> = {
64
+ readonly canvas: HTMLCanvasElement | null;
65
+ readonly gl: WebGLRenderingContext | null;
66
+ readonly status: ShaderStatus;
67
+ readonly supported: boolean;
68
+ readonly ready: Promise<void>;
69
+ start(): void;
70
+ stop(): void;
71
+ render(): void;
72
+ resize(): void;
73
+ destroy(): void;
74
+ setUniform<K extends keyof TUniforms & string>(name: K, value: UniformRuntimeValue<TUniforms[K]>): void;
75
+ setUniform(name: string, value: UniformInput): void;
76
+ setUniforms(values: Record<string, UniformInput>): void;
77
+ };
78
+
79
+ declare const createShaderBackground: <TUniforms extends UniformInputMap = UniformInputMap>(options: CreateShaderBackgroundOptions<TUniforms>) => ShaderBackgroundInstance<TUniforms>;
80
+
81
+ declare class ShaderError extends Error {
82
+ constructor(message: string);
83
+ }
84
+ declare class UnsupportedError extends ShaderError {
85
+ }
86
+ declare class TargetNotFoundError extends ShaderError {
87
+ }
88
+ declare class ShaderCompileError extends ShaderError {
89
+ }
90
+ declare class TextureLoadError extends ShaderError {
91
+ }
92
+ declare class DestroyedError extends ShaderError {
93
+ }
94
+
95
+ export { type CreateShaderBackgroundOptions, DestroyedError, type DprOption, type ExplicitUniformInput, type RenderMode, type RenderState, type ShaderBackgroundInstance, ShaderCompileError, ShaderError, type ShaderLayer, type ShaderStatus, TargetNotFoundError, type TextureInput, TextureLoadError, type TextureMap, type TextureOptions, type TextureSource, type UniformInput, type UniformInputMap, type UniformRuntimeValue, UnsupportedError, createShaderBackground };
package/dist/index.js ADDED
@@ -0,0 +1,727 @@
1
+ import {
2
+ glsl,
3
+ glslUtils
4
+ } from "./chunk-PD6KUFXI.js";
5
+
6
+ // src/internal/errors.ts
7
+ var ShaderError = class extends Error {
8
+ constructor(message) {
9
+ super(message);
10
+ this.name = new.target.name;
11
+ }
12
+ };
13
+ var UnsupportedError = class extends ShaderError {
14
+ };
15
+ var TargetNotFoundError = class extends ShaderError {
16
+ };
17
+ var ShaderCompileError = class extends ShaderError {
18
+ };
19
+ var TextureLoadError = class extends ShaderError {
20
+ };
21
+ var DestroyedError = class extends ShaderError {
22
+ };
23
+
24
+ // src/internal/dom.ts
25
+ var resolveElement = (target) => {
26
+ if (!target) return null;
27
+ if (typeof target === "string") return document.querySelector(target);
28
+ return target;
29
+ };
30
+ var setupCanvas = (options) => {
31
+ const target = resolveElement(options.target);
32
+ const canvas = options.canvas ?? document.createElement("canvas");
33
+ const measurementTarget = target ?? options.canvas;
34
+ if (!measurementTarget) return null;
35
+ const createdCanvas = !options.canvas;
36
+ const restoredPosition = target && getComputedStyle(target).position === "static" ? target.style.position : null;
37
+ if (target && restoredPosition !== null) {
38
+ target.style.position = "relative";
39
+ }
40
+ applyCanvasStyle(canvas, options, createdCanvas);
41
+ if (options.canvasClass) {
42
+ canvas.classList.add(options.canvasClass);
43
+ }
44
+ if (options.canvasStyle) {
45
+ Object.assign(canvas.style, options.canvasStyle);
46
+ }
47
+ if (createdCanvas && target) {
48
+ if ((options.layer ?? "background") === "overlay") {
49
+ target.append(canvas);
50
+ } else {
51
+ target.prepend(canvas);
52
+ }
53
+ }
54
+ return {
55
+ target: measurementTarget,
56
+ canvas,
57
+ createdCanvas,
58
+ restoredPosition
59
+ };
60
+ };
61
+ var resolveDpr = (dpr, maxDpr) => {
62
+ const raw = typeof dpr === "function" ? dpr() : typeof dpr === "number" ? dpr : typeof window === "undefined" ? 1 : window.devicePixelRatio || 1;
63
+ return Math.max(1, Math.min(raw, maxDpr ?? 2));
64
+ };
65
+ var resizeCanvasToTarget = (canvas, target, dpr) => {
66
+ const rect = target.getBoundingClientRect();
67
+ const viewportWidth = Math.max(1, rect.width || canvas.clientWidth || 1);
68
+ const viewportHeight = Math.max(1, rect.height || canvas.clientHeight || 1);
69
+ const width = Math.max(1, Math.round(viewportWidth * dpr));
70
+ const height = Math.max(1, Math.round(viewportHeight * dpr));
71
+ if (canvas.width !== width) canvas.width = width;
72
+ if (canvas.height !== height) canvas.height = height;
73
+ return { width, height, viewportWidth, viewportHeight };
74
+ };
75
+ var applyCanvasStyle = (canvas, options, shouldPosition) => {
76
+ canvas.dataset.frapxShaderCanvas = "";
77
+ if (!shouldPosition) return;
78
+ const layer = options.layer ?? "background";
79
+ Object.assign(canvas.style, {
80
+ position: "absolute",
81
+ inset: "0",
82
+ width: "100%",
83
+ height: "100%",
84
+ display: "block",
85
+ pointerEvents: "none",
86
+ zIndex: layer === "overlay" ? "1" : "0"
87
+ });
88
+ };
89
+
90
+ // src/internal/names.ts
91
+ var toUniformName = (name) => name.startsWith("u_") ? name : `u_${name}`;
92
+
93
+ // src/internal/textures.ts
94
+ var loadTextures = async (gl, textures, isDestroyed) => {
95
+ const entries = Object.entries(textures ?? {});
96
+ const loaded = [];
97
+ for (let index = 0; index < entries.length; index += 1) {
98
+ if (isDestroyed()) break;
99
+ const [name, input] = entries[index];
100
+ loaded.push(await loadTexture(gl, name, input, index));
101
+ }
102
+ return loaded;
103
+ };
104
+ var loadTexture = async (gl, name, input, unit) => {
105
+ const options = normalizeTextureInput(input);
106
+ const source = await resolveSource(options.source);
107
+ const texture = gl.createTexture();
108
+ if (!texture) {
109
+ throw new TextureLoadError(`Failed to create texture: ${name}`);
110
+ }
111
+ gl.activeTexture(gl.TEXTURE0 + unit);
112
+ gl.bindTexture(gl.TEXTURE_2D, texture);
113
+ gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, options.flipY ?? true);
114
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, source);
115
+ const wrap = getWrap(gl, options.wrap ?? "clamp");
116
+ const filter = getFilter(gl, options.filter ?? "linear");
117
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, wrap);
118
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, wrap);
119
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, filter);
120
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, filter);
121
+ return {
122
+ name,
123
+ uniformName: toUniformName(name),
124
+ sizeUniformName: toUniformName(`${name}Size`),
125
+ texture,
126
+ width: getSourceWidth(source),
127
+ height: getSourceHeight(source)
128
+ };
129
+ };
130
+ var normalizeTextureInput = (input) => {
131
+ if (typeof input === "string" || isTextureSource(input)) {
132
+ return { source: input };
133
+ }
134
+ return input;
135
+ };
136
+ var resolveSource = async (source) => {
137
+ if (typeof source !== "string") {
138
+ return source;
139
+ }
140
+ const image = new Image();
141
+ image.crossOrigin = "anonymous";
142
+ image.src = source;
143
+ try {
144
+ await image.decode();
145
+ } catch (error) {
146
+ throw new TextureLoadError(`Failed to load texture: ${source}`);
147
+ }
148
+ return image;
149
+ };
150
+ var getWrap = (gl, wrap) => {
151
+ if (wrap === "repeat") return gl.REPEAT;
152
+ if (wrap === "mirror") return gl.MIRRORED_REPEAT;
153
+ return gl.CLAMP_TO_EDGE;
154
+ };
155
+ var getFilter = (gl, filter) => filter === "nearest" ? gl.NEAREST : gl.LINEAR;
156
+ var isTextureSource = (input) => typeof HTMLImageElement !== "undefined" && (input instanceof HTMLImageElement || input instanceof HTMLCanvasElement);
157
+ var getSourceWidth = (source) => "naturalWidth" in source ? source.naturalWidth : source.width;
158
+ var getSourceHeight = (source) => "naturalHeight" in source ? source.naturalHeight : source.height;
159
+
160
+ // src/internal/uniforms.ts
161
+ var normalizeUniform = (input) => {
162
+ if (typeof input === "number") {
163
+ return { type: "float", value: [input] };
164
+ }
165
+ if (input instanceof Float32Array) {
166
+ if (input.length === 9) return { type: "mat3", value: input };
167
+ if (input.length === 16) return { type: "mat4", value: input };
168
+ if (input.length === 2) return { type: "vec2", value: input };
169
+ if (input.length === 3) return { type: "vec3", value: input };
170
+ if (input.length === 4) return { type: "vec4", value: input };
171
+ }
172
+ if (isExplicitUniform(input)) {
173
+ return { type: input.type, value: input.value };
174
+ }
175
+ if (Array.isArray(input)) {
176
+ if (input.length === 2) return { type: "vec2", value: input };
177
+ if (input.length === 3) return { type: "vec3", value: input };
178
+ if (input.length === 4) return { type: "vec4", value: input };
179
+ }
180
+ return { type: "float", value: [0] };
181
+ };
182
+ var applyUniform = (gl, location, uniform) => {
183
+ const value = uniform.value;
184
+ switch (uniform.type) {
185
+ case "float":
186
+ gl.uniform1f(location, value[0] ?? 0);
187
+ break;
188
+ case "vec2":
189
+ gl.uniform2f(location, value[0] ?? 0, value[1] ?? 0);
190
+ break;
191
+ case "vec3":
192
+ gl.uniform3f(location, value[0] ?? 0, value[1] ?? 0, value[2] ?? 0);
193
+ break;
194
+ case "vec4":
195
+ gl.uniform4f(
196
+ location,
197
+ value[0] ?? 0,
198
+ value[1] ?? 0,
199
+ value[2] ?? 0,
200
+ value[3] ?? 0
201
+ );
202
+ break;
203
+ case "mat3":
204
+ gl.uniformMatrix3fv(location, false, value);
205
+ break;
206
+ case "mat4":
207
+ gl.uniformMatrix4fv(location, false, value);
208
+ break;
209
+ }
210
+ };
211
+ var collectUniformLocations = (gl, program) => {
212
+ const locations = /* @__PURE__ */ new Map();
213
+ const count = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS);
214
+ for (let index = 0; index < count; index += 1) {
215
+ const active = gl.getActiveUniform(program, index);
216
+ if (!active) continue;
217
+ const name = active.name.replace(/\[0\]$/, "");
218
+ const location = gl.getUniformLocation(program, name);
219
+ if (location) {
220
+ locations.set(name, location);
221
+ }
222
+ }
223
+ return locations;
224
+ };
225
+ var createUniformCache = (uniforms) => {
226
+ const cache = /* @__PURE__ */ new Map();
227
+ for (const [name, value] of Object.entries(uniforms ?? {})) {
228
+ cache.set(toUniformName(name), normalizeUniform(value));
229
+ }
230
+ return cache;
231
+ };
232
+ var isExplicitUniform = (input) => {
233
+ if (!input || typeof input !== "object" || Array.isArray(input)) return false;
234
+ return "type" in input && "value" in input;
235
+ };
236
+
237
+ // src/internal/webgl.ts
238
+ var defaultVertexShader = `
239
+ attribute vec2 a_position;
240
+ varying vec2 v_uv;
241
+
242
+ void main() {
243
+ v_uv = a_position * 0.5 + 0.5;
244
+ gl_Position = vec4(a_position, 0.0, 1.0);
245
+ }
246
+ `;
247
+ var createProgram = (gl, vertexSource, fragmentSource) => {
248
+ const vertex = compileShader(gl, gl.VERTEX_SHADER, vertexSource);
249
+ const fragment = compileShader(gl, gl.FRAGMENT_SHADER, fragmentSource);
250
+ const program = gl.createProgram();
251
+ if (!program) {
252
+ throw new ShaderCompileError("Failed to create WebGL program.");
253
+ }
254
+ gl.attachShader(program, vertex);
255
+ gl.attachShader(program, fragment);
256
+ gl.linkProgram(program);
257
+ if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
258
+ const log = gl.getProgramInfoLog(program) ?? "Unknown program link error.";
259
+ gl.deleteProgram(program);
260
+ gl.deleteShader(vertex);
261
+ gl.deleteShader(fragment);
262
+ throw new ShaderCompileError(log);
263
+ }
264
+ gl.deleteShader(vertex);
265
+ gl.deleteShader(fragment);
266
+ return program;
267
+ };
268
+ var createFullscreenBuffer = (gl, program) => {
269
+ const buffer = gl.createBuffer();
270
+ if (!buffer) {
271
+ throw new ShaderCompileError("Failed to create fullscreen buffer.");
272
+ }
273
+ const location = gl.getAttribLocation(program, "a_position");
274
+ gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
275
+ gl.bufferData(
276
+ gl.ARRAY_BUFFER,
277
+ new Float32Array([-1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1]),
278
+ gl.STATIC_DRAW
279
+ );
280
+ if (location >= 0) {
281
+ gl.enableVertexAttribArray(location);
282
+ gl.vertexAttribPointer(location, 2, gl.FLOAT, false, 0, 0);
283
+ }
284
+ return buffer;
285
+ };
286
+ var compileShader = (gl, type, source) => {
287
+ const shader = gl.createShader(type);
288
+ if (!shader) {
289
+ throw new ShaderCompileError("Failed to create WebGL shader.");
290
+ }
291
+ gl.shaderSource(shader, source);
292
+ gl.compileShader(shader);
293
+ if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
294
+ const log = gl.getShaderInfoLog(shader) ?? "Unknown shader compile error.";
295
+ gl.deleteShader(shader);
296
+ throw new ShaderCompileError(log);
297
+ }
298
+ return shader;
299
+ };
300
+
301
+ // src/createShaderBackground.ts
302
+ var MAX_DELTA_SECONDS = 0.1;
303
+ var createShaderBackground = (options) => {
304
+ return new ShaderBackground(options);
305
+ };
306
+ var ShaderBackground = class {
307
+ constructor(options) {
308
+ this.warnedUniforms = /* @__PURE__ */ new Set();
309
+ this.readyResolve = null;
310
+ this.readyReject = null;
311
+ this.dom = null;
312
+ this.program = null;
313
+ this.buffer = null;
314
+ this.uniformLocations = /* @__PURE__ */ new Map();
315
+ this.textures = [];
316
+ this.resizeObserver = null;
317
+ this.intersectionObserver = null;
318
+ this.rafId = 0;
319
+ this.running = false;
320
+ this.inRender = false;
321
+ this.destroyed = false;
322
+ this.onscreen = true;
323
+ this.frame = 0;
324
+ this.elapsed = 0;
325
+ this.lastTimestamp = 0;
326
+ this.pixelRatio = 1;
327
+ this.width = 1;
328
+ this.height = 1;
329
+ this.viewportWidth = 1;
330
+ this.viewportHeight = 1;
331
+ this.pointer = { x: 0, y: 0, uvX: 0, uvY: 0, active: 0 };
332
+ this.removeListeners = [];
333
+ this.currentStatus = "idle";
334
+ this.contextRestoredShouldRun = false;
335
+ this.options = options;
336
+ this.uniformCache = createUniformCache(options.uniforms);
337
+ this.ready = new Promise((resolve, reject) => {
338
+ this.readyResolve = resolve;
339
+ this.readyReject = reject;
340
+ });
341
+ queueMicrotask(() => {
342
+ void this.init();
343
+ });
344
+ }
345
+ get canvas() {
346
+ return this.dom?.canvas ?? null;
347
+ }
348
+ get gl() {
349
+ return this.dom?.canvas.getContext("webgl") ?? null;
350
+ }
351
+ get status() {
352
+ return this.currentStatus;
353
+ }
354
+ get supported() {
355
+ return !["unsupported", "destroyed"].includes(this.currentStatus);
356
+ }
357
+ start() {
358
+ if (this.destroyed || this.currentStatus === "unsupported") return;
359
+ this.running = true;
360
+ if (!this.onscreen && (this.options.pauseWhenOffscreen ?? true)) {
361
+ this.currentStatus = "paused";
362
+ return;
363
+ }
364
+ if (this.options.renderMode === "demand") {
365
+ this.currentStatus = "running";
366
+ this.render();
367
+ return;
368
+ }
369
+ this.currentStatus = "running";
370
+ this.scheduleFrame();
371
+ }
372
+ stop() {
373
+ this.running = false;
374
+ this.cancelFrame();
375
+ if (!this.destroyed && this.currentStatus !== "unsupported") {
376
+ this.currentStatus = "paused";
377
+ }
378
+ }
379
+ render() {
380
+ if (this.destroyed || !this.dom || !this.program || this.currentStatus === "unsupported" || (this.options.pauseWhenOffscreen ?? true) && !this.onscreen) {
381
+ return;
382
+ }
383
+ const gl = this.dom.canvas.getContext("webgl");
384
+ if (!gl) return;
385
+ this.inRender = true;
386
+ this.resize();
387
+ gl.viewport(0, 0, this.width, this.height);
388
+ gl.clear(gl.COLOR_BUFFER_BIT);
389
+ gl.useProgram(this.program);
390
+ this.applyBuiltInUniforms(gl);
391
+ this.applyCachedUniforms(gl);
392
+ this.applyTextures(gl);
393
+ const state = this.createRenderState(gl);
394
+ this.options.onBeforeRender?.(state);
395
+ this.applyCachedUniforms(gl);
396
+ gl.drawArrays(gl.TRIANGLES, 0, 6);
397
+ this.options.onAfterRender?.(state);
398
+ this.frame += 1;
399
+ this.inRender = false;
400
+ }
401
+ resize() {
402
+ if (this.destroyed || !this.dom) return;
403
+ this.pixelRatio = resolveDpr(this.options.dpr, this.options.maxDpr);
404
+ const size = resizeCanvasToTarget(
405
+ this.dom.canvas,
406
+ this.dom.target,
407
+ this.pixelRatio
408
+ );
409
+ this.width = size.width;
410
+ this.height = size.height;
411
+ this.viewportWidth = size.viewportWidth;
412
+ this.viewportHeight = size.viewportHeight;
413
+ if (this.options.renderMode === "demand" && !this.inRender) {
414
+ this.render();
415
+ }
416
+ }
417
+ destroy() {
418
+ if (this.destroyed) return;
419
+ this.destroyed = true;
420
+ this.running = false;
421
+ this.cancelFrame();
422
+ this.resizeObserver?.disconnect();
423
+ this.intersectionObserver?.disconnect();
424
+ for (const remove of this.removeListeners) remove();
425
+ this.removeListeners = [];
426
+ const gl = this.dom?.canvas.getContext("webgl");
427
+ if (gl) {
428
+ for (const texture of this.textures) gl.deleteTexture(texture.texture);
429
+ if (this.buffer) gl.deleteBuffer(this.buffer);
430
+ if (this.program) gl.deleteProgram(this.program);
431
+ }
432
+ if (this.dom?.createdCanvas) {
433
+ this.dom.canvas.remove();
434
+ }
435
+ const dom = this.dom;
436
+ if (dom?.restoredPosition !== null && dom && this.options.target) {
437
+ const target = dom.target;
438
+ target.style.position = dom.restoredPosition;
439
+ }
440
+ this.currentStatus = "destroyed";
441
+ this.readyReject?.(new DestroyedError("Shader background was destroyed."));
442
+ }
443
+ setUniform(name, value) {
444
+ if (this.destroyed) return;
445
+ const uniformName = toUniformName(name);
446
+ this.uniformCache.set(uniformName, normalizeUniform(value));
447
+ const gl = this.dom?.canvas.getContext("webgl");
448
+ const location = this.uniformLocations.get(uniformName);
449
+ if (gl && location) {
450
+ applyUniform(gl, location, this.uniformCache.get(uniformName));
451
+ } else {
452
+ this.warnUnknownUniform(uniformName);
453
+ }
454
+ if (this.options.renderMode === "demand" && !this.inRender && this.onscreen) {
455
+ this.render();
456
+ }
457
+ }
458
+ setUniforms(values) {
459
+ for (const [name, value] of Object.entries(values)) {
460
+ this.setUniform(name, value);
461
+ }
462
+ }
463
+ async init() {
464
+ if (typeof window === "undefined" || typeof document === "undefined") {
465
+ this.failUnsupported(new UnsupportedError("WebGL requires a browser."));
466
+ return;
467
+ }
468
+ this.dom = setupCanvas(this.options);
469
+ if (!this.dom) {
470
+ this.failUnsupported(new TargetNotFoundError("Target or canvas not found."));
471
+ return;
472
+ }
473
+ const gl = this.dom.canvas.getContext("webgl", {
474
+ alpha: true,
475
+ antialias: false,
476
+ depth: false,
477
+ stencil: false,
478
+ premultipliedAlpha: true,
479
+ preserveDrawingBuffer: false
480
+ });
481
+ if (!gl) {
482
+ this.failUnsupported(new UnsupportedError("WebGL is not supported."));
483
+ return;
484
+ }
485
+ this.currentStatus = "loading";
486
+ try {
487
+ this.setupDomObservers();
488
+ this.setupContextEvents();
489
+ this.setupProgram(gl);
490
+ this.resize();
491
+ this.textures = await loadTextures(
492
+ gl,
493
+ this.options.textures,
494
+ () => this.destroyed
495
+ );
496
+ if (this.destroyed) return;
497
+ this.applyTextureSizeUniforms();
498
+ this.currentStatus = "ready";
499
+ this.readyResolve?.();
500
+ this.options.onReady?.(this);
501
+ if (this.running || (this.options.autoStart ?? true)) {
502
+ this.start();
503
+ } else if (this.options.renderMode === "demand") {
504
+ this.render();
505
+ }
506
+ } catch (error) {
507
+ this.fail(error instanceof Error ? error : new Error(String(error)));
508
+ }
509
+ }
510
+ setupProgram(gl) {
511
+ if (this.program) gl.deleteProgram(this.program);
512
+ if (this.buffer) gl.deleteBuffer(this.buffer);
513
+ this.program = createProgram(
514
+ gl,
515
+ this.options.vertex ?? defaultVertexShader,
516
+ this.options.fragment
517
+ );
518
+ gl.useProgram(this.program);
519
+ this.buffer = createFullscreenBuffer(gl, this.program);
520
+ this.uniformLocations = collectUniformLocations(gl, this.program);
521
+ }
522
+ setupDomObservers() {
523
+ if (!this.dom) return;
524
+ if (this.options.autoResize ?? true) {
525
+ this.resizeObserver = new ResizeObserver(() => {
526
+ this.resize();
527
+ });
528
+ this.resizeObserver.observe(this.dom.target);
529
+ }
530
+ if (this.options.pauseWhenOffscreen ?? true) {
531
+ this.intersectionObserver = new IntersectionObserver((entries) => {
532
+ const entry = entries[0];
533
+ this.onscreen = Boolean(entry?.isIntersecting);
534
+ if (!this.onscreen) {
535
+ this.cancelFrame();
536
+ if (this.running) this.currentStatus = "paused";
537
+ return;
538
+ }
539
+ if (this.running) {
540
+ this.start();
541
+ } else if (this.options.renderMode === "demand") {
542
+ this.render();
543
+ }
544
+ });
545
+ this.intersectionObserver.observe(this.dom.target);
546
+ }
547
+ const onPointerMove = (event) => {
548
+ if (!this.dom) return;
549
+ const pointerEvent = event;
550
+ const rect = this.dom.target.getBoundingClientRect();
551
+ const x = pointerEvent.clientX - rect.left;
552
+ const y = pointerEvent.clientY - rect.top;
553
+ this.pointer.x = x * this.pixelRatio;
554
+ this.pointer.y = (rect.height - y) * this.pixelRatio;
555
+ this.pointer.uvX = rect.width > 0 ? x / rect.width : 0;
556
+ this.pointer.uvY = rect.height > 0 ? 1 - y / rect.height : 0;
557
+ this.pointer.active = 1;
558
+ if (this.options.renderMode === "demand") this.render();
559
+ };
560
+ const onPointerLeave = () => {
561
+ this.pointer.active = 0;
562
+ if (this.options.renderMode === "demand") this.render();
563
+ };
564
+ this.dom.target.addEventListener("pointermove", onPointerMove);
565
+ this.dom.target.addEventListener("pointerenter", onPointerMove);
566
+ this.dom.target.addEventListener("pointerleave", onPointerLeave);
567
+ this.removeListeners.push(() => {
568
+ this.dom?.target.removeEventListener("pointermove", onPointerMove);
569
+ this.dom?.target.removeEventListener("pointerenter", onPointerMove);
570
+ this.dom?.target.removeEventListener("pointerleave", onPointerLeave);
571
+ });
572
+ }
573
+ setupContextEvents() {
574
+ if (!this.dom) return;
575
+ const onLost = (event) => {
576
+ event.preventDefault();
577
+ this.contextRestoredShouldRun = this.running;
578
+ this.cancelFrame();
579
+ this.currentStatus = "context-lost";
580
+ this.options.onError?.(new UnsupportedError("WebGL context lost."));
581
+ };
582
+ const onRestored = () => {
583
+ if (!this.dom || this.destroyed) return;
584
+ const gl = this.dom.canvas.getContext("webgl");
585
+ if (!gl) return;
586
+ try {
587
+ this.setupProgram(gl);
588
+ this.applyCachedUniforms(gl);
589
+ void loadTextures(gl, this.options.textures, () => this.destroyed).then(
590
+ (textures) => {
591
+ this.textures = textures;
592
+ this.applyTextureSizeUniforms();
593
+ if (this.contextRestoredShouldRun) this.start();
594
+ else this.render();
595
+ },
596
+ (error) => this.fail(error instanceof Error ? error : new Error(String(error)))
597
+ );
598
+ } catch (error) {
599
+ this.fail(error instanceof Error ? error : new Error(String(error)));
600
+ }
601
+ };
602
+ this.dom.canvas.addEventListener("webglcontextlost", onLost);
603
+ this.dom.canvas.addEventListener("webglcontextrestored", onRestored);
604
+ this.removeListeners.push(() => {
605
+ this.dom?.canvas.removeEventListener("webglcontextlost", onLost);
606
+ this.dom?.canvas.removeEventListener("webglcontextrestored", onRestored);
607
+ });
608
+ }
609
+ scheduleFrame() {
610
+ if (this.rafId || this.options.renderMode === "demand") return;
611
+ this.rafId = requestAnimationFrame((timestamp) => {
612
+ this.rafId = 0;
613
+ if (!this.running || this.destroyed) return;
614
+ const seconds = timestamp / 1e3;
615
+ const rawDelta = this.lastTimestamp ? seconds - this.lastTimestamp : 0;
616
+ const delta = Math.min(Math.max(rawDelta, 0), MAX_DELTA_SECONDS);
617
+ this.lastTimestamp = seconds;
618
+ this.elapsed += delta;
619
+ this.setBuiltInTime(delta);
620
+ this.render();
621
+ this.scheduleFrame();
622
+ });
623
+ }
624
+ cancelFrame() {
625
+ if (this.rafId) {
626
+ cancelAnimationFrame(this.rafId);
627
+ this.rafId = 0;
628
+ }
629
+ this.lastTimestamp = 0;
630
+ }
631
+ setBuiltInTime(delta) {
632
+ this.uniformCache.set("u_time", { type: "float", value: [this.elapsed] });
633
+ this.uniformCache.set("u_delta", { type: "float", value: [delta] });
634
+ }
635
+ applyBuiltInUniforms(gl) {
636
+ const builtIns = {
637
+ resolution: [this.width, this.height],
638
+ viewportSize: [this.viewportWidth, this.viewportHeight],
639
+ pixelRatio: this.pixelRatio,
640
+ pointer: [this.pointer.x, this.pointer.y],
641
+ pointerUv: [this.pointer.uvX, this.pointer.uvY],
642
+ pointerActive: this.pointer.active
643
+ };
644
+ for (const [name, value] of Object.entries(builtIns)) {
645
+ this.uniformCache.set(toUniformName(name), normalizeUniform(value));
646
+ }
647
+ this.applyCachedUniforms(gl);
648
+ }
649
+ applyTextureSizeUniforms() {
650
+ for (const texture of this.textures) {
651
+ this.uniformCache.set(texture.sizeUniformName, {
652
+ type: "vec2",
653
+ value: [texture.width, texture.height]
654
+ });
655
+ }
656
+ }
657
+ applyCachedUniforms(gl) {
658
+ for (const [name, uniform] of this.uniformCache) {
659
+ const location = this.uniformLocations.get(name);
660
+ if (!location) {
661
+ this.warnUnknownUniform(name);
662
+ continue;
663
+ }
664
+ applyUniform(gl, location, uniform);
665
+ }
666
+ }
667
+ applyTextures(gl) {
668
+ for (let unit = 0; unit < this.textures.length; unit += 1) {
669
+ const texture = this.textures[unit];
670
+ if (!texture) continue;
671
+ const location = this.uniformLocations.get(texture.uniformName);
672
+ if (!location) continue;
673
+ gl.activeTexture(gl.TEXTURE0 + unit);
674
+ gl.bindTexture(gl.TEXTURE_2D, texture.texture);
675
+ gl.uniform1i(location, unit);
676
+ }
677
+ }
678
+ createRenderState(gl) {
679
+ return {
680
+ instance: this,
681
+ gl,
682
+ canvas: this.dom.canvas,
683
+ time: this.elapsed,
684
+ delta: this.uniformCache.get("u_delta")?.value[0] ?? 0,
685
+ frame: this.frame,
686
+ width: this.width,
687
+ height: this.height,
688
+ viewportWidth: this.viewportWidth,
689
+ viewportHeight: this.viewportHeight,
690
+ pixelRatio: this.pixelRatio
691
+ };
692
+ }
693
+ warnUnknownUniform(name) {
694
+ if (!this.options.debug || this.warnedUniforms.has(name)) return;
695
+ this.warnedUniforms.add(name);
696
+ console.warn(`[frapx/shader] Uniform not found or optimized out: ${name}`);
697
+ }
698
+ failUnsupported(error) {
699
+ this.currentStatus = "unsupported";
700
+ this.debugWarn(error.message);
701
+ this.options.onError?.(error);
702
+ this.readyReject?.(error);
703
+ }
704
+ fail(error) {
705
+ this.currentStatus = "error";
706
+ this.debugWarn(error.message);
707
+ this.options.onError?.(error);
708
+ this.readyReject?.(error);
709
+ }
710
+ debugWarn(message) {
711
+ if (this.options.debug) {
712
+ console.warn(`[frapx/shader] ${message}`);
713
+ }
714
+ }
715
+ };
716
+ export {
717
+ DestroyedError,
718
+ ShaderCompileError,
719
+ ShaderError,
720
+ TargetNotFoundError,
721
+ TextureLoadError,
722
+ UnsupportedError,
723
+ createShaderBackground,
724
+ glsl,
725
+ glslUtils
726
+ };
727
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/internal/errors.ts","../src/internal/dom.ts","../src/internal/names.ts","../src/internal/textures.ts","../src/internal/uniforms.ts","../src/internal/webgl.ts","../src/createShaderBackground.ts"],"sourcesContent":["export class ShaderError extends Error {\n constructor(message: string) {\n super(message);\n this.name = new.target.name;\n }\n}\n\nexport class UnsupportedError extends ShaderError {}\nexport class TargetNotFoundError extends ShaderError {}\nexport class ShaderCompileError extends ShaderError {}\nexport class TextureLoadError extends ShaderError {}\nexport class DestroyedError extends ShaderError {}\n","import type { CreateShaderBackgroundOptions, DprOption } from \"../types\";\n\nexport type DomSetup = {\n target: Element;\n canvas: HTMLCanvasElement;\n createdCanvas: boolean;\n restoredPosition: string | null;\n};\n\nexport const resolveElement = (\n target: string | Element | undefined\n): Element | null => {\n if (!target) return null;\n if (typeof target === \"string\") return document.querySelector(target);\n return target;\n};\n\nexport const setupCanvas = (\n options: CreateShaderBackgroundOptions\n): DomSetup | null => {\n const target = resolveElement(options.target);\n const canvas = options.canvas ?? document.createElement(\"canvas\");\n const measurementTarget = target ?? options.canvas;\n\n if (!measurementTarget) return null;\n\n const createdCanvas = !options.canvas;\n const restoredPosition =\n target && getComputedStyle(target).position === \"static\"\n ? (target as HTMLElement).style.position\n : null;\n\n if (target && restoredPosition !== null) {\n (target as HTMLElement).style.position = \"relative\";\n }\n\n applyCanvasStyle(canvas, options, createdCanvas);\n\n if (options.canvasClass) {\n canvas.classList.add(options.canvasClass);\n }\n\n if (options.canvasStyle) {\n Object.assign(canvas.style, options.canvasStyle);\n }\n\n if (createdCanvas && target) {\n if ((options.layer ?? \"background\") === \"overlay\") {\n target.append(canvas);\n } else {\n target.prepend(canvas);\n }\n }\n\n return {\n target: measurementTarget,\n canvas,\n createdCanvas,\n restoredPosition\n };\n};\n\nexport const resolveDpr = (\n dpr: DprOption | undefined,\n maxDpr: number | undefined\n): number => {\n const raw =\n typeof dpr === \"function\"\n ? dpr()\n : typeof dpr === \"number\"\n ? dpr\n : typeof window === \"undefined\"\n ? 1\n : window.devicePixelRatio || 1;\n\n return Math.max(1, Math.min(raw, maxDpr ?? 2));\n};\n\nexport const resizeCanvasToTarget = (\n canvas: HTMLCanvasElement,\n target: Element,\n dpr: number\n): {\n width: number;\n height: number;\n viewportWidth: number;\n viewportHeight: number;\n} => {\n const rect = target.getBoundingClientRect();\n const viewportWidth = Math.max(1, rect.width || canvas.clientWidth || 1);\n const viewportHeight = Math.max(1, rect.height || canvas.clientHeight || 1);\n const width = Math.max(1, Math.round(viewportWidth * dpr));\n const height = Math.max(1, Math.round(viewportHeight * dpr));\n\n if (canvas.width !== width) canvas.width = width;\n if (canvas.height !== height) canvas.height = height;\n\n return { width, height, viewportWidth, viewportHeight };\n};\n\nconst applyCanvasStyle = (\n canvas: HTMLCanvasElement,\n options: CreateShaderBackgroundOptions,\n shouldPosition: boolean\n): void => {\n canvas.dataset.frapxShaderCanvas = \"\";\n\n if (!shouldPosition) return;\n\n const layer = options.layer ?? \"background\";\n Object.assign(canvas.style, {\n position: \"absolute\",\n inset: \"0\",\n width: \"100%\",\n height: \"100%\",\n display: \"block\",\n pointerEvents: \"none\",\n zIndex: layer === \"overlay\" ? \"1\" : \"0\"\n });\n};\n","export const toUniformName = (name: string): string =>\n name.startsWith(\"u_\") ? name : `u_${name}`;\n\nexport const fromUniformName = (name: string): string =>\n name.startsWith(\"u_\") ? name.slice(2) : name;\n","import type { TextureInput, TextureOptions, TextureSource } from \"../types\";\nimport { TextureLoadError } from \"./errors\";\nimport { toUniformName } from \"./names\";\n\nexport type LoadedTexture = {\n name: string;\n uniformName: string;\n sizeUniformName: string;\n texture: WebGLTexture;\n width: number;\n height: number;\n};\n\nexport const loadTextures = async (\n gl: WebGLRenderingContext,\n textures: Record<string, TextureInput> | undefined,\n isDestroyed: () => boolean\n): Promise<LoadedTexture[]> => {\n const entries = Object.entries(textures ?? {});\n const loaded: LoadedTexture[] = [];\n\n for (let index = 0; index < entries.length; index += 1) {\n if (isDestroyed()) break;\n const [name, input] = entries[index] as [string, TextureInput];\n loaded.push(await loadTexture(gl, name, input, index));\n }\n\n return loaded;\n};\n\nconst loadTexture = async (\n gl: WebGLRenderingContext,\n name: string,\n input: TextureInput,\n unit: number\n): Promise<LoadedTexture> => {\n const options = normalizeTextureInput(input);\n const source = await resolveSource(options.source);\n const texture = gl.createTexture();\n\n if (!texture) {\n throw new TextureLoadError(`Failed to create texture: ${name}`);\n }\n\n gl.activeTexture(gl.TEXTURE0 + unit);\n gl.bindTexture(gl.TEXTURE_2D, texture);\n gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, options.flipY ?? true);\n gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, source);\n\n const wrap = getWrap(gl, options.wrap ?? \"clamp\");\n const filter = getFilter(gl, options.filter ?? \"linear\");\n\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, wrap);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, wrap);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, filter);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, filter);\n\n return {\n name,\n uniformName: toUniformName(name),\n sizeUniformName: toUniformName(`${name}Size`),\n texture,\n width: getSourceWidth(source),\n height: getSourceHeight(source)\n };\n};\n\nconst normalizeTextureInput = (input: TextureInput): TextureOptions => {\n if (typeof input === \"string\" || isTextureSource(input)) {\n return { source: input };\n }\n\n return input;\n};\n\nconst resolveSource = async (\n source: TextureSource\n): Promise<HTMLImageElement | HTMLCanvasElement> => {\n if (typeof source !== \"string\") {\n return source;\n }\n\n const image = new Image();\n image.crossOrigin = \"anonymous\";\n image.src = source;\n\n try {\n await image.decode();\n } catch (error) {\n throw new TextureLoadError(`Failed to load texture: ${source}`);\n }\n\n return image;\n};\n\nconst getWrap = (\n gl: WebGLRenderingContext,\n wrap: NonNullable<TextureOptions[\"wrap\"]>\n): number => {\n if (wrap === \"repeat\") return gl.REPEAT;\n if (wrap === \"mirror\") return gl.MIRRORED_REPEAT;\n return gl.CLAMP_TO_EDGE;\n};\n\nconst getFilter = (\n gl: WebGLRenderingContext,\n filter: NonNullable<TextureOptions[\"filter\"]>\n): number => (filter === \"nearest\" ? gl.NEAREST : gl.LINEAR);\n\nconst isTextureSource = (input: unknown): input is HTMLImageElement | HTMLCanvasElement =>\n typeof HTMLImageElement !== \"undefined\" &&\n (input instanceof HTMLImageElement || input instanceof HTMLCanvasElement);\n\nconst getSourceWidth = (source: HTMLImageElement | HTMLCanvasElement): number =>\n \"naturalWidth\" in source ? source.naturalWidth : source.width;\n\nconst getSourceHeight = (source: HTMLImageElement | HTMLCanvasElement): number =>\n \"naturalHeight\" in source ? source.naturalHeight : source.height;\n","import type { ExplicitUniformInput, UniformInput, UniformType } from \"../types\";\nimport { toUniformName } from \"./names\";\n\nexport type NormalizedUniform = {\n type: UniformType;\n value: number[] | Float32Array;\n};\n\nexport const normalizeUniform = (input: UniformInput): NormalizedUniform => {\n if (typeof input === \"number\") {\n return { type: \"float\", value: [input] };\n }\n\n if (input instanceof Float32Array) {\n if (input.length === 9) return { type: \"mat3\", value: input };\n if (input.length === 16) return { type: \"mat4\", value: input };\n if (input.length === 2) return { type: \"vec2\", value: input };\n if (input.length === 3) return { type: \"vec3\", value: input };\n if (input.length === 4) return { type: \"vec4\", value: input };\n }\n\n if (isExplicitUniform(input)) {\n return { type: input.type, value: input.value };\n }\n\n if (Array.isArray(input)) {\n if (input.length === 2) return { type: \"vec2\", value: input };\n if (input.length === 3) return { type: \"vec3\", value: input };\n if (input.length === 4) return { type: \"vec4\", value: input };\n }\n\n return { type: \"float\", value: [0] };\n};\n\nexport const applyUniform = (\n gl: WebGLRenderingContext,\n location: WebGLUniformLocation,\n uniform: NormalizedUniform\n): void => {\n const value = uniform.value;\n\n switch (uniform.type) {\n case \"float\":\n gl.uniform1f(location, value[0] ?? 0);\n break;\n case \"vec2\":\n gl.uniform2f(location, value[0] ?? 0, value[1] ?? 0);\n break;\n case \"vec3\":\n gl.uniform3f(location, value[0] ?? 0, value[1] ?? 0, value[2] ?? 0);\n break;\n case \"vec4\":\n gl.uniform4f(\n location,\n value[0] ?? 0,\n value[1] ?? 0,\n value[2] ?? 0,\n value[3] ?? 0\n );\n break;\n case \"mat3\":\n gl.uniformMatrix3fv(location, false, value);\n break;\n case \"mat4\":\n gl.uniformMatrix4fv(location, false, value);\n break;\n }\n};\n\nexport const collectUniformLocations = (\n gl: WebGLRenderingContext,\n program: WebGLProgram\n): Map<string, WebGLUniformLocation> => {\n const locations = new Map<string, WebGLUniformLocation>();\n const count = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS) as number;\n\n for (let index = 0; index < count; index += 1) {\n const active = gl.getActiveUniform(program, index);\n if (!active) continue;\n\n const name = active.name.replace(/\\[0\\]$/, \"\");\n const location = gl.getUniformLocation(program, name);\n if (location) {\n locations.set(name, location);\n }\n }\n\n return locations;\n};\n\nexport const createUniformCache = (\n uniforms: Record<string, UniformInput> | undefined\n): Map<string, NormalizedUniform> => {\n const cache = new Map<string, NormalizedUniform>();\n\n for (const [name, value] of Object.entries(uniforms ?? {})) {\n cache.set(toUniformName(name), normalizeUniform(value));\n }\n\n return cache;\n};\n\nconst isExplicitUniform = (input: unknown): input is ExplicitUniformInput => {\n if (!input || typeof input !== \"object\" || Array.isArray(input)) return false;\n return \"type\" in input && \"value\" in input;\n};\n","import { ShaderCompileError } from \"./errors\";\n\nexport const defaultVertexShader = `\nattribute vec2 a_position;\nvarying vec2 v_uv;\n\nvoid main() {\n v_uv = a_position * 0.5 + 0.5;\n gl_Position = vec4(a_position, 0.0, 1.0);\n}\n`;\n\nexport const createProgram = (\n gl: WebGLRenderingContext,\n vertexSource: string,\n fragmentSource: string\n): WebGLProgram => {\n const vertex = compileShader(gl, gl.VERTEX_SHADER, vertexSource);\n const fragment = compileShader(gl, gl.FRAGMENT_SHADER, fragmentSource);\n const program = gl.createProgram();\n\n if (!program) {\n throw new ShaderCompileError(\"Failed to create WebGL program.\");\n }\n\n gl.attachShader(program, vertex);\n gl.attachShader(program, fragment);\n gl.linkProgram(program);\n\n if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {\n const log = gl.getProgramInfoLog(program) ?? \"Unknown program link error.\";\n gl.deleteProgram(program);\n gl.deleteShader(vertex);\n gl.deleteShader(fragment);\n throw new ShaderCompileError(log);\n }\n\n gl.deleteShader(vertex);\n gl.deleteShader(fragment);\n return program;\n};\n\nexport const createFullscreenBuffer = (\n gl: WebGLRenderingContext,\n program: WebGLProgram\n): WebGLBuffer => {\n const buffer = gl.createBuffer();\n if (!buffer) {\n throw new ShaderCompileError(\"Failed to create fullscreen buffer.\");\n }\n\n const location = gl.getAttribLocation(program, \"a_position\");\n gl.bindBuffer(gl.ARRAY_BUFFER, buffer);\n gl.bufferData(\n gl.ARRAY_BUFFER,\n new Float32Array([-1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1]),\n gl.STATIC_DRAW\n );\n\n if (location >= 0) {\n gl.enableVertexAttribArray(location);\n gl.vertexAttribPointer(location, 2, gl.FLOAT, false, 0, 0);\n }\n\n return buffer;\n};\n\nconst compileShader = (\n gl: WebGLRenderingContext,\n type: number,\n source: string\n): WebGLShader => {\n const shader = gl.createShader(type);\n if (!shader) {\n throw new ShaderCompileError(\"Failed to create WebGL shader.\");\n }\n\n gl.shaderSource(shader, source);\n gl.compileShader(shader);\n\n if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {\n const log = gl.getShaderInfoLog(shader) ?? \"Unknown shader compile error.\";\n gl.deleteShader(shader);\n throw new ShaderCompileError(log);\n }\n\n return shader;\n};\n","import type {\n CreateShaderBackgroundOptions,\n RenderState,\n ShaderBackgroundInstance,\n ShaderStatus,\n UniformInput,\n UniformInputMap,\n UniformRuntimeValue\n} from \"./types\";\nimport {\n DestroyedError,\n TargetNotFoundError,\n UnsupportedError\n} from \"./internal/errors\";\nimport {\n resolveDpr,\n resizeCanvasToTarget,\n setupCanvas,\n type DomSetup\n} from \"./internal/dom\";\nimport { toUniformName } from \"./internal/names\";\nimport { loadTextures, type LoadedTexture } from \"./internal/textures\";\nimport {\n applyUniform,\n collectUniformLocations,\n createUniformCache,\n normalizeUniform,\n type NormalizedUniform\n} from \"./internal/uniforms\";\nimport {\n createFullscreenBuffer,\n createProgram,\n defaultVertexShader\n} from \"./internal/webgl\";\n\nconst MAX_DELTA_SECONDS = 0.1;\n\nexport const createShaderBackground = <\n TUniforms extends UniformInputMap = UniformInputMap\n>(\n options: CreateShaderBackgroundOptions<TUniforms>\n): ShaderBackgroundInstance<TUniforms> => {\n return new ShaderBackground<TUniforms>(options);\n};\n\nclass ShaderBackground<TUniforms extends UniformInputMap>\n implements ShaderBackgroundInstance<TUniforms>\n{\n readonly ready: Promise<void>;\n\n private readonly options: CreateShaderBackgroundOptions<TUniforms>;\n private readonly uniformCache: Map<string, NormalizedUniform>;\n private readonly warnedUniforms = new Set<string>();\n private readyResolve: (() => void) | null = null;\n private readyReject: ((error: Error) => void) | null = null;\n private dom: DomSetup | null = null;\n private program: WebGLProgram | null = null;\n private buffer: WebGLBuffer | null = null;\n private uniformLocations = new Map<string, WebGLUniformLocation>();\n private textures: LoadedTexture[] = [];\n private resizeObserver: ResizeObserver | null = null;\n private intersectionObserver: IntersectionObserver | null = null;\n private rafId = 0;\n private running = false;\n private inRender = false;\n private destroyed = false;\n private onscreen = true;\n private frame = 0;\n private elapsed = 0;\n private lastTimestamp = 0;\n private pixelRatio = 1;\n private width = 1;\n private height = 1;\n private viewportWidth = 1;\n private viewportHeight = 1;\n private pointer = { x: 0, y: 0, uvX: 0, uvY: 0, active: 0 };\n private removeListeners: Array<() => void> = [];\n private currentStatus: ShaderStatus = \"idle\";\n private contextRestoredShouldRun = false;\n\n constructor(options: CreateShaderBackgroundOptions<TUniforms>) {\n this.options = options;\n this.uniformCache = createUniformCache(options.uniforms);\n this.ready = new Promise<void>((resolve, reject) => {\n this.readyResolve = resolve;\n this.readyReject = reject;\n });\n\n queueMicrotask(() => {\n void this.init();\n });\n }\n\n get canvas(): HTMLCanvasElement | null {\n return this.dom?.canvas ?? null;\n }\n\n get gl(): WebGLRenderingContext | null {\n return this.dom?.canvas.getContext(\"webgl\") ?? null;\n }\n\n get status(): ShaderStatus {\n return this.currentStatus;\n }\n\n get supported(): boolean {\n return ![\"unsupported\", \"destroyed\"].includes(this.currentStatus);\n }\n\n start(): void {\n if (this.destroyed || this.currentStatus === \"unsupported\") return;\n this.running = true;\n\n if (!this.onscreen && (this.options.pauseWhenOffscreen ?? true)) {\n this.currentStatus = \"paused\";\n return;\n }\n\n if (this.options.renderMode === \"demand\") {\n this.currentStatus = \"running\";\n this.render();\n return;\n }\n\n this.currentStatus = \"running\";\n this.scheduleFrame();\n }\n\n stop(): void {\n this.running = false;\n this.cancelFrame();\n if (!this.destroyed && this.currentStatus !== \"unsupported\") {\n this.currentStatus = \"paused\";\n }\n }\n\n render(): void {\n if (\n this.destroyed ||\n !this.dom ||\n !this.program ||\n this.currentStatus === \"unsupported\" ||\n (this.options.pauseWhenOffscreen ?? true) && !this.onscreen\n ) {\n return;\n }\n\n const gl = this.dom.canvas.getContext(\"webgl\");\n if (!gl) return;\n\n this.inRender = true;\n this.resize();\n gl.viewport(0, 0, this.width, this.height);\n gl.clear(gl.COLOR_BUFFER_BIT);\n gl.useProgram(this.program);\n this.applyBuiltInUniforms(gl);\n this.applyCachedUniforms(gl);\n this.applyTextures(gl);\n\n const state = this.createRenderState(gl);\n this.options.onBeforeRender?.(state);\n this.applyCachedUniforms(gl);\n\n gl.drawArrays(gl.TRIANGLES, 0, 6);\n this.options.onAfterRender?.(state);\n this.frame += 1;\n this.inRender = false;\n }\n\n resize(): void {\n if (this.destroyed || !this.dom) return;\n this.pixelRatio = resolveDpr(this.options.dpr, this.options.maxDpr);\n const size = resizeCanvasToTarget(\n this.dom.canvas,\n this.dom.target,\n this.pixelRatio\n );\n this.width = size.width;\n this.height = size.height;\n this.viewportWidth = size.viewportWidth;\n this.viewportHeight = size.viewportHeight;\n\n if (this.options.renderMode === \"demand\" && !this.inRender) {\n this.render();\n }\n }\n\n destroy(): void {\n if (this.destroyed) return;\n this.destroyed = true;\n this.running = false;\n this.cancelFrame();\n this.resizeObserver?.disconnect();\n this.intersectionObserver?.disconnect();\n\n for (const remove of this.removeListeners) remove();\n this.removeListeners = [];\n\n const gl = this.dom?.canvas.getContext(\"webgl\");\n if (gl) {\n for (const texture of this.textures) gl.deleteTexture(texture.texture);\n if (this.buffer) gl.deleteBuffer(this.buffer);\n if (this.program) gl.deleteProgram(this.program);\n }\n\n if (this.dom?.createdCanvas) {\n this.dom.canvas.remove();\n }\n\n const dom = this.dom;\n if (dom?.restoredPosition !== null && dom && this.options.target) {\n const target = dom.target as HTMLElement;\n target.style.position = dom.restoredPosition;\n }\n\n this.currentStatus = \"destroyed\";\n this.readyReject?.(new DestroyedError(\"Shader background was destroyed.\"));\n }\n\n setUniform<K extends keyof TUniforms & string>(\n name: K,\n value: UniformRuntimeValue<TUniforms[K]>\n ): void;\n setUniform(name: string, value: UniformInput): void;\n setUniform(name: string, value: UniformInput): void {\n if (this.destroyed) return;\n const uniformName = toUniformName(name);\n this.uniformCache.set(uniformName, normalizeUniform(value));\n\n const gl = this.dom?.canvas.getContext(\"webgl\");\n const location = this.uniformLocations.get(uniformName);\n if (gl && location) {\n applyUniform(gl, location, this.uniformCache.get(uniformName)!);\n } else {\n this.warnUnknownUniform(uniformName);\n }\n\n if (\n this.options.renderMode === \"demand\" &&\n !this.inRender &&\n this.onscreen\n ) {\n this.render();\n }\n }\n\n setUniforms(values: Record<string, UniformInput>): void {\n for (const [name, value] of Object.entries(values)) {\n this.setUniform(name, value);\n }\n }\n\n private async init(): Promise<void> {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") {\n this.failUnsupported(new UnsupportedError(\"WebGL requires a browser.\"));\n return;\n }\n\n this.dom = setupCanvas(this.options);\n if (!this.dom) {\n this.failUnsupported(new TargetNotFoundError(\"Target or canvas not found.\"));\n return;\n }\n\n const gl = this.dom.canvas.getContext(\"webgl\", {\n alpha: true,\n antialias: false,\n depth: false,\n stencil: false,\n premultipliedAlpha: true,\n preserveDrawingBuffer: false\n });\n\n if (!gl) {\n this.failUnsupported(new UnsupportedError(\"WebGL is not supported.\"));\n return;\n }\n\n this.currentStatus = \"loading\";\n\n try {\n this.setupDomObservers();\n this.setupContextEvents();\n this.setupProgram(gl);\n this.resize();\n this.textures = await loadTextures(\n gl,\n this.options.textures,\n () => this.destroyed\n );\n if (this.destroyed) return;\n this.applyTextureSizeUniforms();\n this.currentStatus = \"ready\";\n this.readyResolve?.();\n this.options.onReady?.(this);\n\n if (this.running || (this.options.autoStart ?? true)) {\n this.start();\n } else if (this.options.renderMode === \"demand\") {\n this.render();\n }\n } catch (error) {\n this.fail(error instanceof Error ? error : new Error(String(error)));\n }\n }\n\n private setupProgram(gl: WebGLRenderingContext): void {\n if (this.program) gl.deleteProgram(this.program);\n if (this.buffer) gl.deleteBuffer(this.buffer);\n\n this.program = createProgram(\n gl,\n this.options.vertex ?? defaultVertexShader,\n this.options.fragment\n );\n gl.useProgram(this.program);\n this.buffer = createFullscreenBuffer(gl, this.program);\n this.uniformLocations = collectUniformLocations(gl, this.program);\n }\n\n private setupDomObservers(): void {\n if (!this.dom) return;\n\n if (this.options.autoResize ?? true) {\n this.resizeObserver = new ResizeObserver(() => {\n this.resize();\n });\n this.resizeObserver.observe(this.dom.target);\n }\n\n if (this.options.pauseWhenOffscreen ?? true) {\n this.intersectionObserver = new IntersectionObserver((entries) => {\n const entry = entries[0];\n this.onscreen = Boolean(entry?.isIntersecting);\n if (!this.onscreen) {\n this.cancelFrame();\n if (this.running) this.currentStatus = \"paused\";\n return;\n }\n\n if (this.running) {\n this.start();\n } else if (this.options.renderMode === \"demand\") {\n this.render();\n }\n });\n this.intersectionObserver.observe(this.dom.target);\n }\n\n const onPointerMove = (event: Event) => {\n if (!this.dom) return;\n const pointerEvent = event as PointerEvent;\n const rect = this.dom.target.getBoundingClientRect();\n const x = pointerEvent.clientX - rect.left;\n const y = pointerEvent.clientY - rect.top;\n this.pointer.x = x * this.pixelRatio;\n this.pointer.y = (rect.height - y) * this.pixelRatio;\n this.pointer.uvX = rect.width > 0 ? x / rect.width : 0;\n this.pointer.uvY = rect.height > 0 ? 1 - y / rect.height : 0;\n this.pointer.active = 1;\n\n if (this.options.renderMode === \"demand\") this.render();\n };\n const onPointerLeave = () => {\n this.pointer.active = 0;\n if (this.options.renderMode === \"demand\") this.render();\n };\n\n this.dom.target.addEventListener(\"pointermove\", onPointerMove);\n this.dom.target.addEventListener(\"pointerenter\", onPointerMove);\n this.dom.target.addEventListener(\"pointerleave\", onPointerLeave);\n this.removeListeners.push(() => {\n this.dom?.target.removeEventListener(\"pointermove\", onPointerMove);\n this.dom?.target.removeEventListener(\"pointerenter\", onPointerMove);\n this.dom?.target.removeEventListener(\"pointerleave\", onPointerLeave);\n });\n }\n\n private setupContextEvents(): void {\n if (!this.dom) return;\n\n const onLost = (event: Event) => {\n event.preventDefault();\n this.contextRestoredShouldRun = this.running;\n this.cancelFrame();\n this.currentStatus = \"context-lost\";\n this.options.onError?.(new UnsupportedError(\"WebGL context lost.\"));\n };\n\n const onRestored = () => {\n if (!this.dom || this.destroyed) return;\n const gl = this.dom.canvas.getContext(\"webgl\");\n if (!gl) return;\n\n try {\n this.setupProgram(gl);\n this.applyCachedUniforms(gl);\n void loadTextures(gl, this.options.textures, () => this.destroyed).then(\n (textures) => {\n this.textures = textures;\n this.applyTextureSizeUniforms();\n if (this.contextRestoredShouldRun) this.start();\n else this.render();\n },\n (error: unknown) =>\n this.fail(error instanceof Error ? error : new Error(String(error)))\n );\n } catch (error) {\n this.fail(error instanceof Error ? error : new Error(String(error)));\n }\n };\n\n this.dom.canvas.addEventListener(\"webglcontextlost\", onLost);\n this.dom.canvas.addEventListener(\"webglcontextrestored\", onRestored);\n this.removeListeners.push(() => {\n this.dom?.canvas.removeEventListener(\"webglcontextlost\", onLost);\n this.dom?.canvas.removeEventListener(\"webglcontextrestored\", onRestored);\n });\n }\n\n private scheduleFrame(): void {\n if (this.rafId || this.options.renderMode === \"demand\") return;\n\n this.rafId = requestAnimationFrame((timestamp) => {\n this.rafId = 0;\n if (!this.running || this.destroyed) return;\n\n const seconds = timestamp / 1000;\n const rawDelta = this.lastTimestamp ? seconds - this.lastTimestamp : 0;\n const delta = Math.min(Math.max(rawDelta, 0), MAX_DELTA_SECONDS);\n this.lastTimestamp = seconds;\n this.elapsed += delta;\n this.setBuiltInTime(delta);\n this.render();\n this.scheduleFrame();\n });\n }\n\n private cancelFrame(): void {\n if (this.rafId) {\n cancelAnimationFrame(this.rafId);\n this.rafId = 0;\n }\n this.lastTimestamp = 0;\n }\n\n private setBuiltInTime(delta: number): void {\n this.uniformCache.set(\"u_time\", { type: \"float\", value: [this.elapsed] });\n this.uniformCache.set(\"u_delta\", { type: \"float\", value: [delta] });\n }\n\n private applyBuiltInUniforms(gl: WebGLRenderingContext): void {\n const builtIns: Record<string, UniformInput> = {\n resolution: [this.width, this.height],\n viewportSize: [this.viewportWidth, this.viewportHeight],\n pixelRatio: this.pixelRatio,\n pointer: [this.pointer.x, this.pointer.y],\n pointerUv: [this.pointer.uvX, this.pointer.uvY],\n pointerActive: this.pointer.active\n };\n\n for (const [name, value] of Object.entries(builtIns)) {\n this.uniformCache.set(toUniformName(name), normalizeUniform(value));\n }\n\n this.applyCachedUniforms(gl);\n }\n\n private applyTextureSizeUniforms(): void {\n for (const texture of this.textures) {\n this.uniformCache.set(texture.sizeUniformName, {\n type: \"vec2\",\n value: [texture.width, texture.height]\n });\n }\n }\n\n private applyCachedUniforms(gl: WebGLRenderingContext): void {\n for (const [name, uniform] of this.uniformCache) {\n const location = this.uniformLocations.get(name);\n if (!location) {\n this.warnUnknownUniform(name);\n continue;\n }\n applyUniform(gl, location, uniform);\n }\n }\n\n private applyTextures(gl: WebGLRenderingContext): void {\n for (let unit = 0; unit < this.textures.length; unit += 1) {\n const texture = this.textures[unit];\n if (!texture) continue;\n const location = this.uniformLocations.get(texture.uniformName);\n if (!location) continue;\n\n gl.activeTexture(gl.TEXTURE0 + unit);\n gl.bindTexture(gl.TEXTURE_2D, texture.texture);\n gl.uniform1i(location, unit);\n }\n }\n\n private createRenderState(\n gl: WebGLRenderingContext\n ): RenderState<TUniforms> {\n return {\n instance: this,\n gl,\n canvas: this.dom!.canvas,\n time: this.elapsed,\n delta: this.uniformCache.get(\"u_delta\")?.value[0] ?? 0,\n frame: this.frame,\n width: this.width,\n height: this.height,\n viewportWidth: this.viewportWidth,\n viewportHeight: this.viewportHeight,\n pixelRatio: this.pixelRatio\n };\n }\n\n private warnUnknownUniform(name: string): void {\n if (!this.options.debug || this.warnedUniforms.has(name)) return;\n this.warnedUniforms.add(name);\n console.warn(`[frapx/shader] Uniform not found or optimized out: ${name}`);\n }\n\n private failUnsupported(error: Error): void {\n this.currentStatus = \"unsupported\";\n this.debugWarn(error.message);\n this.options.onError?.(error);\n this.readyReject?.(error);\n }\n\n private fail(error: Error): void {\n this.currentStatus = \"error\";\n this.debugWarn(error.message);\n this.options.onError?.(error);\n this.readyReject?.(error);\n }\n\n private debugWarn(message: string): void {\n if (this.options.debug) {\n console.warn(`[frapx/shader] ${message}`);\n }\n }\n}\n"],"mappings":"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","names":[]}
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@frapx/shader",
3
+ "version": "0.1.0",
4
+ "description": "Lightweight WebGL shader background runtime for websites.",
5
+ "keywords": [
6
+ "webgl",
7
+ "shader",
8
+ "glsl",
9
+ "canvas",
10
+ "background",
11
+ "visual-effects",
12
+ "web-animation"
13
+ ],
14
+ "type": "module",
15
+ "sideEffects": false,
16
+ "exports": {
17
+ ".": {
18
+ "types": "./dist/index.d.ts",
19
+ "import": "./dist/index.js"
20
+ },
21
+ "./glsl": {
22
+ "types": "./dist/glsl.d.ts",
23
+ "import": "./dist/glsl.js"
24
+ }
25
+ },
26
+ "files": [
27
+ "dist"
28
+ ],
29
+ "scripts": {
30
+ "build": "tsup",
31
+ "typecheck": "tsc --noEmit",
32
+ "test": "vitest run",
33
+ "test:e2e": "playwright test"
34
+ },
35
+ "license": "MIT",
36
+ "repository": {
37
+ "type": "git",
38
+ "directory": "packages/shader"
39
+ },
40
+ "publishConfig": {
41
+ "access": "public"
42
+ }
43
+ }