react-shadertoy 0.1.0 → 0.3.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/dist/index.d.mts CHANGED
@@ -1,9 +1,20 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { CSSProperties, RefObject } from 'react';
3
3
 
4
+ /** Texture source: URL string, or an HTML element for dynamic textures */
5
+ type TextureSource = string | HTMLImageElement | HTMLVideoElement | HTMLCanvasElement;
6
+ /** Texture inputs mapped to Shadertoy channels */
7
+ type TextureInputs = {
8
+ iChannel0?: TextureSource;
9
+ iChannel1?: TextureSource;
10
+ iChannel2?: TextureSource;
11
+ iChannel3?: TextureSource;
12
+ };
4
13
  interface ShadertoyProps {
5
14
  /** Shadertoy-compatible GLSL fragment shader (must contain mainImage) */
6
15
  fragmentShader: string;
16
+ /** Texture inputs for iChannel0-3 */
17
+ textures?: TextureInputs;
7
18
  /** Container style */
8
19
  style?: CSSProperties;
9
20
  /** Container className */
@@ -23,6 +34,7 @@ interface ShadertoyProps {
23
34
  }
24
35
  interface UseShadertoyOptions {
25
36
  fragmentShader: string;
37
+ textures?: TextureInputs;
26
38
  paused?: boolean;
27
39
  speed?: number;
28
40
  pixelRatio?: number;
@@ -38,8 +50,8 @@ interface UseShadertoyReturn {
38
50
  resume: () => void;
39
51
  }
40
52
 
41
- declare function Shadertoy({ fragmentShader, style, className, paused, speed, pixelRatio, mouse, onError, onLoad, }: ShadertoyProps): react_jsx_runtime.JSX.Element;
53
+ declare function Shadertoy({ fragmentShader, textures, style, className, paused, speed, pixelRatio, mouse, onError, onLoad, }: ShadertoyProps): react_jsx_runtime.JSX.Element;
42
54
 
43
- declare function useShadertoy({ fragmentShader, paused, speed, pixelRatio, mouse: mouseEnabled, onError, onLoad, }: UseShadertoyOptions): UseShadertoyReturn;
55
+ declare function useShadertoy({ fragmentShader, textures: texturesProp, paused, speed, pixelRatio, mouse: mouseEnabled, onError, onLoad, }: UseShadertoyOptions): UseShadertoyReturn;
44
56
 
45
- export { Shadertoy, type ShadertoyProps, type UseShadertoyOptions, type UseShadertoyReturn, useShadertoy };
57
+ export { Shadertoy, type ShadertoyProps, type TextureInputs, type UseShadertoyOptions, type UseShadertoyReturn, useShadertoy };
package/dist/index.d.ts CHANGED
@@ -1,9 +1,20 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { CSSProperties, RefObject } from 'react';
3
3
 
4
+ /** Texture source: URL string, or an HTML element for dynamic textures */
5
+ type TextureSource = string | HTMLImageElement | HTMLVideoElement | HTMLCanvasElement;
6
+ /** Texture inputs mapped to Shadertoy channels */
7
+ type TextureInputs = {
8
+ iChannel0?: TextureSource;
9
+ iChannel1?: TextureSource;
10
+ iChannel2?: TextureSource;
11
+ iChannel3?: TextureSource;
12
+ };
4
13
  interface ShadertoyProps {
5
14
  /** Shadertoy-compatible GLSL fragment shader (must contain mainImage) */
6
15
  fragmentShader: string;
16
+ /** Texture inputs for iChannel0-3 */
17
+ textures?: TextureInputs;
7
18
  /** Container style */
8
19
  style?: CSSProperties;
9
20
  /** Container className */
@@ -23,6 +34,7 @@ interface ShadertoyProps {
23
34
  }
24
35
  interface UseShadertoyOptions {
25
36
  fragmentShader: string;
37
+ textures?: TextureInputs;
26
38
  paused?: boolean;
27
39
  speed?: number;
28
40
  pixelRatio?: number;
@@ -38,8 +50,8 @@ interface UseShadertoyReturn {
38
50
  resume: () => void;
39
51
  }
40
52
 
41
- declare function Shadertoy({ fragmentShader, style, className, paused, speed, pixelRatio, mouse, onError, onLoad, }: ShadertoyProps): react_jsx_runtime.JSX.Element;
53
+ declare function Shadertoy({ fragmentShader, textures, style, className, paused, speed, pixelRatio, mouse, onError, onLoad, }: ShadertoyProps): react_jsx_runtime.JSX.Element;
42
54
 
43
- declare function useShadertoy({ fragmentShader, paused, speed, pixelRatio, mouse: mouseEnabled, onError, onLoad, }: UseShadertoyOptions): UseShadertoyReturn;
55
+ declare function useShadertoy({ fragmentShader, textures: texturesProp, paused, speed, pixelRatio, mouse: mouseEnabled, onError, onLoad, }: UseShadertoyOptions): UseShadertoyReturn;
44
56
 
45
- export { Shadertoy, type ShadertoyProps, type UseShadertoyOptions, type UseShadertoyReturn, useShadertoy };
57
+ export { Shadertoy, type ShadertoyProps, type TextureInputs, type UseShadertoyOptions, type UseShadertoyReturn, useShadertoy };
package/dist/index.js CHANGED
@@ -1,9 +1,32 @@
1
- 'use strict';
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
2
19
 
3
- var react = require('react');
4
- var jsxRuntime = require('react/jsx-runtime');
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ Shadertoy: () => Shadertoy,
24
+ useShadertoy: () => useShadertoy
25
+ });
26
+ module.exports = __toCommonJS(index_exports);
5
27
 
6
28
  // src/useShadertoy.ts
29
+ var import_react = require("react");
7
30
 
8
31
  // src/renderer.ts
9
32
  var QUAD_VERTICES = new Float32Array([
@@ -35,6 +58,14 @@ uniform float iTimeDelta;
35
58
  uniform int iFrame;
36
59
  uniform vec4 iMouse;
37
60
  uniform vec4 iDate;
61
+ uniform sampler2D iChannel0;
62
+ uniform sampler2D iChannel1;
63
+ uniform sampler2D iChannel2;
64
+ uniform sampler2D iChannel3;
65
+ uniform vec3 iChannelResolution[4];
66
+
67
+ // Shadertoy compatibility: texture() is GLSL 300 es, WebGL1 uses texture2D()
68
+ #define texture texture2D
38
69
 
39
70
  ${shader}
40
71
 
@@ -90,13 +121,21 @@ function createRenderer(canvas, fragmentShader) {
90
121
  iTimeDelta: gl.getUniformLocation(program, "iTimeDelta"),
91
122
  iFrame: gl.getUniformLocation(program, "iFrame"),
92
123
  iMouse: gl.getUniformLocation(program, "iMouse"),
93
- iDate: gl.getUniformLocation(program, "iDate")
124
+ iDate: gl.getUniformLocation(program, "iDate"),
125
+ iChannel: [
126
+ gl.getUniformLocation(program, "iChannel0"),
127
+ gl.getUniformLocation(program, "iChannel1"),
128
+ gl.getUniformLocation(program, "iChannel2"),
129
+ gl.getUniformLocation(program, "iChannel3")
130
+ ],
131
+ iChannelResolution: gl.getUniformLocation(program, "iChannelResolution")
94
132
  };
95
133
  gl.useProgram(program);
96
134
  return {
97
135
  gl,
98
136
  program,
99
137
  locations,
138
+ textures: [null, null, null, null],
100
139
  time: 0,
101
140
  frame: 0,
102
141
  lastTime: 0
@@ -113,6 +152,178 @@ function dispose(state) {
113
152
  gl.getExtension("WEBGL_lose_context")?.loseContext();
114
153
  }
115
154
 
155
+ // src/textures.ts
156
+ function isPOT(v) {
157
+ return (v & v - 1) === 0 && v > 0;
158
+ }
159
+ function initTexture(gl, unit) {
160
+ const texture = gl.createTexture();
161
+ gl.activeTexture(gl.TEXTURE0 + unit);
162
+ gl.bindTexture(gl.TEXTURE_2D, texture);
163
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
164
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
165
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
166
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
167
+ return texture;
168
+ }
169
+ function uploadElement(gl, texture, unit, el) {
170
+ gl.activeTexture(gl.TEXTURE0 + unit);
171
+ gl.bindTexture(gl.TEXTURE_2D, texture);
172
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, el);
173
+ }
174
+ function createTexture(gl, source, unit) {
175
+ const texture = initTexture(gl, unit);
176
+ if (typeof source === "string") {
177
+ gl.texImage2D(
178
+ gl.TEXTURE_2D,
179
+ 0,
180
+ gl.RGBA,
181
+ 1,
182
+ 1,
183
+ 0,
184
+ gl.RGBA,
185
+ gl.UNSIGNED_BYTE,
186
+ new Uint8Array([255, 0, 255, 255])
187
+ );
188
+ const state2 = {
189
+ texture,
190
+ width: 1,
191
+ height: 1,
192
+ unit,
193
+ loaded: false,
194
+ needsUpdate: false,
195
+ source
196
+ };
197
+ const promise = new Promise((resolve, reject) => {
198
+ const img = new Image();
199
+ img.crossOrigin = "anonymous";
200
+ img.onload = () => {
201
+ if (gl.isContextLost()) {
202
+ resolve();
203
+ return;
204
+ }
205
+ uploadElement(gl, texture, unit, img);
206
+ if (isPOT(img.width) && isPOT(img.height)) {
207
+ gl.generateMipmap(gl.TEXTURE_2D);
208
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
209
+ }
210
+ state2.width = img.width;
211
+ state2.height = img.height;
212
+ state2.loaded = true;
213
+ resolve();
214
+ };
215
+ img.onerror = () => reject(new Error(`Failed to load texture: ${source}`));
216
+ img.src = source;
217
+ });
218
+ return { state: state2, promise };
219
+ }
220
+ if (source instanceof HTMLImageElement) {
221
+ const state2 = {
222
+ texture,
223
+ width: source.naturalWidth || 1,
224
+ height: source.naturalHeight || 1,
225
+ unit,
226
+ loaded: source.complete,
227
+ needsUpdate: false,
228
+ source
229
+ };
230
+ if (source.complete && source.naturalWidth > 0) {
231
+ uploadElement(gl, texture, unit, source);
232
+ state2.width = source.naturalWidth;
233
+ state2.height = source.naturalHeight;
234
+ return { state: state2, promise: null };
235
+ }
236
+ const promise = new Promise((resolve, reject) => {
237
+ source.onload = () => {
238
+ if (gl.isContextLost()) {
239
+ resolve();
240
+ return;
241
+ }
242
+ uploadElement(gl, texture, unit, source);
243
+ state2.width = source.naturalWidth;
244
+ state2.height = source.naturalHeight;
245
+ state2.loaded = true;
246
+ resolve();
247
+ };
248
+ source.onerror = () => reject(new Error("Failed to load image element"));
249
+ });
250
+ return { state: state2, promise };
251
+ }
252
+ if (source instanceof HTMLVideoElement) {
253
+ const w = source.videoWidth || 1;
254
+ const h = source.videoHeight || 1;
255
+ if (source.readyState >= 2) {
256
+ uploadElement(gl, texture, unit, source);
257
+ } else {
258
+ gl.texImage2D(
259
+ gl.TEXTURE_2D,
260
+ 0,
261
+ gl.RGBA,
262
+ 1,
263
+ 1,
264
+ 0,
265
+ gl.RGBA,
266
+ gl.UNSIGNED_BYTE,
267
+ new Uint8Array([0, 0, 0, 255])
268
+ );
269
+ }
270
+ const state2 = {
271
+ texture,
272
+ width: w,
273
+ height: h,
274
+ unit,
275
+ loaded: source.readyState >= 2,
276
+ needsUpdate: true,
277
+ source
278
+ };
279
+ return { state: state2, promise: null };
280
+ }
281
+ uploadElement(gl, texture, unit, source);
282
+ const state = {
283
+ texture,
284
+ width: source.width,
285
+ height: source.height,
286
+ unit,
287
+ loaded: true,
288
+ needsUpdate: true,
289
+ source
290
+ };
291
+ return { state, promise: null };
292
+ }
293
+ function updateDynamicTextures(gl, textures) {
294
+ for (const tex of textures) {
295
+ if (!tex || !tex.needsUpdate || !tex.source) continue;
296
+ if (tex.source instanceof HTMLVideoElement) {
297
+ const v = tex.source;
298
+ if (v.readyState < 2) continue;
299
+ uploadElement(gl, tex.texture, tex.unit, v);
300
+ tex.width = v.videoWidth;
301
+ tex.height = v.videoHeight;
302
+ tex.loaded = true;
303
+ } else if (tex.source instanceof HTMLCanvasElement) {
304
+ uploadElement(gl, tex.texture, tex.unit, tex.source);
305
+ tex.width = tex.source.width;
306
+ tex.height = tex.source.height;
307
+ }
308
+ }
309
+ }
310
+ function bindTextures(gl, locations, textures) {
311
+ for (let i = 0; i < 4; i++) {
312
+ const tex = textures[i];
313
+ if (!tex) continue;
314
+ gl.activeTexture(gl.TEXTURE0 + tex.unit);
315
+ gl.bindTexture(gl.TEXTURE_2D, tex.texture);
316
+ if (locations[i]) {
317
+ gl.uniform1i(locations[i], tex.unit);
318
+ }
319
+ }
320
+ }
321
+ function disposeTextures(gl, textures) {
322
+ for (const tex of textures) {
323
+ if (tex) gl.deleteTexture(tex.texture);
324
+ }
325
+ }
326
+
116
327
  // src/uniforms.ts
117
328
  function updateUniforms(state, delta, speed, mouse) {
118
329
  const { gl, locations } = state;
@@ -140,6 +351,18 @@ function updateUniforms(state, delta, speed, mouse) {
140
351
  const mw = mouse.pressed ? mouse.clickY : -Math.abs(mouse.clickY);
141
352
  gl.uniform4f(locations.iMouse, mouse.x, mouse.y, mz, mw);
142
353
  }
354
+ if (locations.iChannelResolution) {
355
+ const res = new Float32Array(12);
356
+ for (let i = 0; i < 4; i++) {
357
+ const tex = state.textures[i];
358
+ if (tex) {
359
+ res[i * 3] = tex.width;
360
+ res[i * 3 + 1] = tex.height;
361
+ res[i * 3 + 2] = 1;
362
+ }
363
+ }
364
+ gl.uniform3fv(locations.iChannelResolution, res);
365
+ }
143
366
  if (locations.iDate) {
144
367
  const now = /* @__PURE__ */ new Date();
145
368
  const seconds = now.getHours() * 3600 + now.getMinutes() * 60 + now.getSeconds() + now.getMilliseconds() / 1e3;
@@ -155,8 +378,10 @@ function updateUniforms(state, delta, speed, mouse) {
155
378
  }
156
379
 
157
380
  // src/useShadertoy.ts
381
+ var CHANNEL_KEYS = ["iChannel0", "iChannel1", "iChannel2", "iChannel3"];
158
382
  function useShadertoy({
159
383
  fragmentShader,
384
+ textures: texturesProp,
160
385
  paused = false,
161
386
  speed = 1,
162
387
  pixelRatio,
@@ -164,14 +389,14 @@ function useShadertoy({
164
389
  onError,
165
390
  onLoad
166
391
  }) {
167
- const canvasRef = react.useRef(null);
168
- const rendererRef = react.useRef(null);
169
- const rafRef = react.useRef(0);
170
- const pausedRef = react.useRef(paused);
171
- const speedRef = react.useRef(speed);
172
- const [isReady, setIsReady] = react.useState(false);
173
- const [error, setError] = react.useState(null);
174
- const mouseState = react.useRef({
392
+ const canvasRef = (0, import_react.useRef)(null);
393
+ const rendererRef = (0, import_react.useRef)(null);
394
+ const rafRef = (0, import_react.useRef)(0);
395
+ const pausedRef = (0, import_react.useRef)(paused);
396
+ const speedRef = (0, import_react.useRef)(speed);
397
+ const [isReady, setIsReady] = (0, import_react.useState)(false);
398
+ const [error, setError] = (0, import_react.useState)(null);
399
+ const mouseState = (0, import_react.useRef)({
175
400
  x: 0,
176
401
  y: 0,
177
402
  clickX: 0,
@@ -180,7 +405,7 @@ function useShadertoy({
180
405
  });
181
406
  pausedRef.current = paused;
182
407
  speedRef.current = speed;
183
- react.useEffect(() => {
408
+ (0, import_react.useEffect)(() => {
184
409
  const canvas = canvasRef.current;
185
410
  if (!canvas) return;
186
411
  const result = createRenderer(canvas, fragmentShader);
@@ -190,16 +415,43 @@ function useShadertoy({
190
415
  return;
191
416
  }
192
417
  rendererRef.current = result;
193
- setIsReady(true);
194
- setError(null);
195
- onLoad?.();
418
+ const texturePromises = [];
419
+ if (texturesProp) {
420
+ for (let i = 0; i < 4; i++) {
421
+ const src = texturesProp[CHANNEL_KEYS[i]];
422
+ if (src != null) {
423
+ const { state, promise } = createTexture(result.gl, src, i);
424
+ result.textures[i] = state;
425
+ if (promise) texturePromises.push(promise);
426
+ }
427
+ }
428
+ }
429
+ const markReady = () => {
430
+ setIsReady(true);
431
+ setError(null);
432
+ onLoad?.();
433
+ };
434
+ if (texturePromises.length > 0) {
435
+ Promise.all(texturePromises).then(() => {
436
+ if (rendererRef.current) markReady();
437
+ }).catch((err) => {
438
+ const msg = err instanceof Error ? err.message : "Texture load failed";
439
+ setError(msg);
440
+ onError?.(msg);
441
+ });
442
+ } else {
443
+ markReady();
444
+ }
196
445
  let lastTimestamp = 0;
197
446
  const loop = (timestamp) => {
198
447
  const delta = lastTimestamp ? (timestamp - lastTimestamp) / 1e3 : 0;
199
448
  lastTimestamp = timestamp;
200
449
  if (!pausedRef.current && rendererRef.current) {
201
- updateUniforms(rendererRef.current, delta, speedRef.current, mouseState.current);
202
- render(rendererRef.current);
450
+ const r = rendererRef.current;
451
+ updateDynamicTextures(r.gl, r.textures);
452
+ bindTextures(r.gl, r.locations.iChannel, r.textures);
453
+ updateUniforms(r, delta, speedRef.current, mouseState.current);
454
+ render(r);
203
455
  }
204
456
  rafRef.current = requestAnimationFrame(loop);
205
457
  };
@@ -207,13 +459,14 @@ function useShadertoy({
207
459
  return () => {
208
460
  cancelAnimationFrame(rafRef.current);
209
461
  if (rendererRef.current) {
462
+ disposeTextures(rendererRef.current.gl, rendererRef.current.textures);
210
463
  dispose(rendererRef.current);
211
464
  rendererRef.current = null;
212
465
  }
213
466
  setIsReady(false);
214
467
  };
215
- }, [fragmentShader, onError, onLoad]);
216
- react.useEffect(() => {
468
+ }, [fragmentShader, texturesProp, onError, onLoad]);
469
+ (0, import_react.useEffect)(() => {
217
470
  const canvas = canvasRef.current;
218
471
  if (!canvas) return;
219
472
  const dpr = pixelRatio ?? (typeof window !== "undefined" ? window.devicePixelRatio : 1);
@@ -227,7 +480,7 @@ function useShadertoy({
227
480
  observer.observe(canvas);
228
481
  return () => observer.disconnect();
229
482
  }, [pixelRatio]);
230
- react.useEffect(() => {
483
+ (0, import_react.useEffect)(() => {
231
484
  if (!mouseEnabled) return;
232
485
  const canvas = canvasRef.current;
233
486
  if (!canvas) return;
@@ -282,16 +535,20 @@ function useShadertoy({
282
535
  window.removeEventListener("touchend", te);
283
536
  };
284
537
  }, [mouseEnabled, pixelRatio]);
285
- const pause = react.useCallback(() => {
538
+ const pause = (0, import_react.useCallback)(() => {
286
539
  pausedRef.current = true;
287
540
  }, []);
288
- const resume = react.useCallback(() => {
541
+ const resume = (0, import_react.useCallback)(() => {
289
542
  pausedRef.current = false;
290
543
  }, []);
291
544
  return { canvasRef, isReady, error, pause, resume };
292
545
  }
546
+
547
+ // src/Shadertoy.tsx
548
+ var import_jsx_runtime = require("react/jsx-runtime");
293
549
  function Shadertoy({
294
550
  fragmentShader,
551
+ textures,
295
552
  style,
296
553
  className,
297
554
  paused,
@@ -303,6 +560,7 @@ function Shadertoy({
303
560
  }) {
304
561
  const { canvasRef } = useShadertoy({
305
562
  fragmentShader,
563
+ textures,
306
564
  paused,
307
565
  speed,
308
566
  pixelRatio,
@@ -310,7 +568,7 @@ function Shadertoy({
310
568
  onError,
311
569
  onLoad
312
570
  });
313
- return /* @__PURE__ */ jsxRuntime.jsx(
571
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
314
572
  "canvas",
315
573
  {
316
574
  ref: canvasRef,
@@ -319,8 +577,8 @@ function Shadertoy({
319
577
  }
320
578
  );
321
579
  }
322
-
323
- exports.Shadertoy = Shadertoy;
324
- exports.useShadertoy = useShadertoy;
325
- //# sourceMappingURL=index.js.map
326
- //# sourceMappingURL=index.js.map
580
+ // Annotate the CommonJS export names for ESM import in node:
581
+ 0 && (module.exports = {
582
+ Shadertoy,
583
+ useShadertoy
584
+ });
package/dist/index.mjs CHANGED
@@ -1,7 +1,5 @@
1
- import { useRef, useState, useEffect, useCallback } from 'react';
2
- import { jsx } from 'react/jsx-runtime';
3
-
4
1
  // src/useShadertoy.ts
2
+ import { useCallback, useEffect, useRef, useState } from "react";
5
3
 
6
4
  // src/renderer.ts
7
5
  var QUAD_VERTICES = new Float32Array([
@@ -33,6 +31,14 @@ uniform float iTimeDelta;
33
31
  uniform int iFrame;
34
32
  uniform vec4 iMouse;
35
33
  uniform vec4 iDate;
34
+ uniform sampler2D iChannel0;
35
+ uniform sampler2D iChannel1;
36
+ uniform sampler2D iChannel2;
37
+ uniform sampler2D iChannel3;
38
+ uniform vec3 iChannelResolution[4];
39
+
40
+ // Shadertoy compatibility: texture() is GLSL 300 es, WebGL1 uses texture2D()
41
+ #define texture texture2D
36
42
 
37
43
  ${shader}
38
44
 
@@ -88,13 +94,21 @@ function createRenderer(canvas, fragmentShader) {
88
94
  iTimeDelta: gl.getUniformLocation(program, "iTimeDelta"),
89
95
  iFrame: gl.getUniformLocation(program, "iFrame"),
90
96
  iMouse: gl.getUniformLocation(program, "iMouse"),
91
- iDate: gl.getUniformLocation(program, "iDate")
97
+ iDate: gl.getUniformLocation(program, "iDate"),
98
+ iChannel: [
99
+ gl.getUniformLocation(program, "iChannel0"),
100
+ gl.getUniformLocation(program, "iChannel1"),
101
+ gl.getUniformLocation(program, "iChannel2"),
102
+ gl.getUniformLocation(program, "iChannel3")
103
+ ],
104
+ iChannelResolution: gl.getUniformLocation(program, "iChannelResolution")
92
105
  };
93
106
  gl.useProgram(program);
94
107
  return {
95
108
  gl,
96
109
  program,
97
110
  locations,
111
+ textures: [null, null, null, null],
98
112
  time: 0,
99
113
  frame: 0,
100
114
  lastTime: 0
@@ -111,6 +125,178 @@ function dispose(state) {
111
125
  gl.getExtension("WEBGL_lose_context")?.loseContext();
112
126
  }
113
127
 
128
+ // src/textures.ts
129
+ function isPOT(v) {
130
+ return (v & v - 1) === 0 && v > 0;
131
+ }
132
+ function initTexture(gl, unit) {
133
+ const texture = gl.createTexture();
134
+ gl.activeTexture(gl.TEXTURE0 + unit);
135
+ gl.bindTexture(gl.TEXTURE_2D, texture);
136
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
137
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
138
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
139
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
140
+ return texture;
141
+ }
142
+ function uploadElement(gl, texture, unit, el) {
143
+ gl.activeTexture(gl.TEXTURE0 + unit);
144
+ gl.bindTexture(gl.TEXTURE_2D, texture);
145
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, el);
146
+ }
147
+ function createTexture(gl, source, unit) {
148
+ const texture = initTexture(gl, unit);
149
+ if (typeof source === "string") {
150
+ gl.texImage2D(
151
+ gl.TEXTURE_2D,
152
+ 0,
153
+ gl.RGBA,
154
+ 1,
155
+ 1,
156
+ 0,
157
+ gl.RGBA,
158
+ gl.UNSIGNED_BYTE,
159
+ new Uint8Array([255, 0, 255, 255])
160
+ );
161
+ const state2 = {
162
+ texture,
163
+ width: 1,
164
+ height: 1,
165
+ unit,
166
+ loaded: false,
167
+ needsUpdate: false,
168
+ source
169
+ };
170
+ const promise = new Promise((resolve, reject) => {
171
+ const img = new Image();
172
+ img.crossOrigin = "anonymous";
173
+ img.onload = () => {
174
+ if (gl.isContextLost()) {
175
+ resolve();
176
+ return;
177
+ }
178
+ uploadElement(gl, texture, unit, img);
179
+ if (isPOT(img.width) && isPOT(img.height)) {
180
+ gl.generateMipmap(gl.TEXTURE_2D);
181
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
182
+ }
183
+ state2.width = img.width;
184
+ state2.height = img.height;
185
+ state2.loaded = true;
186
+ resolve();
187
+ };
188
+ img.onerror = () => reject(new Error(`Failed to load texture: ${source}`));
189
+ img.src = source;
190
+ });
191
+ return { state: state2, promise };
192
+ }
193
+ if (source instanceof HTMLImageElement) {
194
+ const state2 = {
195
+ texture,
196
+ width: source.naturalWidth || 1,
197
+ height: source.naturalHeight || 1,
198
+ unit,
199
+ loaded: source.complete,
200
+ needsUpdate: false,
201
+ source
202
+ };
203
+ if (source.complete && source.naturalWidth > 0) {
204
+ uploadElement(gl, texture, unit, source);
205
+ state2.width = source.naturalWidth;
206
+ state2.height = source.naturalHeight;
207
+ return { state: state2, promise: null };
208
+ }
209
+ const promise = new Promise((resolve, reject) => {
210
+ source.onload = () => {
211
+ if (gl.isContextLost()) {
212
+ resolve();
213
+ return;
214
+ }
215
+ uploadElement(gl, texture, unit, source);
216
+ state2.width = source.naturalWidth;
217
+ state2.height = source.naturalHeight;
218
+ state2.loaded = true;
219
+ resolve();
220
+ };
221
+ source.onerror = () => reject(new Error("Failed to load image element"));
222
+ });
223
+ return { state: state2, promise };
224
+ }
225
+ if (source instanceof HTMLVideoElement) {
226
+ const w = source.videoWidth || 1;
227
+ const h = source.videoHeight || 1;
228
+ if (source.readyState >= 2) {
229
+ uploadElement(gl, texture, unit, source);
230
+ } else {
231
+ gl.texImage2D(
232
+ gl.TEXTURE_2D,
233
+ 0,
234
+ gl.RGBA,
235
+ 1,
236
+ 1,
237
+ 0,
238
+ gl.RGBA,
239
+ gl.UNSIGNED_BYTE,
240
+ new Uint8Array([0, 0, 0, 255])
241
+ );
242
+ }
243
+ const state2 = {
244
+ texture,
245
+ width: w,
246
+ height: h,
247
+ unit,
248
+ loaded: source.readyState >= 2,
249
+ needsUpdate: true,
250
+ source
251
+ };
252
+ return { state: state2, promise: null };
253
+ }
254
+ uploadElement(gl, texture, unit, source);
255
+ const state = {
256
+ texture,
257
+ width: source.width,
258
+ height: source.height,
259
+ unit,
260
+ loaded: true,
261
+ needsUpdate: true,
262
+ source
263
+ };
264
+ return { state, promise: null };
265
+ }
266
+ function updateDynamicTextures(gl, textures) {
267
+ for (const tex of textures) {
268
+ if (!tex || !tex.needsUpdate || !tex.source) continue;
269
+ if (tex.source instanceof HTMLVideoElement) {
270
+ const v = tex.source;
271
+ if (v.readyState < 2) continue;
272
+ uploadElement(gl, tex.texture, tex.unit, v);
273
+ tex.width = v.videoWidth;
274
+ tex.height = v.videoHeight;
275
+ tex.loaded = true;
276
+ } else if (tex.source instanceof HTMLCanvasElement) {
277
+ uploadElement(gl, tex.texture, tex.unit, tex.source);
278
+ tex.width = tex.source.width;
279
+ tex.height = tex.source.height;
280
+ }
281
+ }
282
+ }
283
+ function bindTextures(gl, locations, textures) {
284
+ for (let i = 0; i < 4; i++) {
285
+ const tex = textures[i];
286
+ if (!tex) continue;
287
+ gl.activeTexture(gl.TEXTURE0 + tex.unit);
288
+ gl.bindTexture(gl.TEXTURE_2D, tex.texture);
289
+ if (locations[i]) {
290
+ gl.uniform1i(locations[i], tex.unit);
291
+ }
292
+ }
293
+ }
294
+ function disposeTextures(gl, textures) {
295
+ for (const tex of textures) {
296
+ if (tex) gl.deleteTexture(tex.texture);
297
+ }
298
+ }
299
+
114
300
  // src/uniforms.ts
115
301
  function updateUniforms(state, delta, speed, mouse) {
116
302
  const { gl, locations } = state;
@@ -138,6 +324,18 @@ function updateUniforms(state, delta, speed, mouse) {
138
324
  const mw = mouse.pressed ? mouse.clickY : -Math.abs(mouse.clickY);
139
325
  gl.uniform4f(locations.iMouse, mouse.x, mouse.y, mz, mw);
140
326
  }
327
+ if (locations.iChannelResolution) {
328
+ const res = new Float32Array(12);
329
+ for (let i = 0; i < 4; i++) {
330
+ const tex = state.textures[i];
331
+ if (tex) {
332
+ res[i * 3] = tex.width;
333
+ res[i * 3 + 1] = tex.height;
334
+ res[i * 3 + 2] = 1;
335
+ }
336
+ }
337
+ gl.uniform3fv(locations.iChannelResolution, res);
338
+ }
141
339
  if (locations.iDate) {
142
340
  const now = /* @__PURE__ */ new Date();
143
341
  const seconds = now.getHours() * 3600 + now.getMinutes() * 60 + now.getSeconds() + now.getMilliseconds() / 1e3;
@@ -153,8 +351,10 @@ function updateUniforms(state, delta, speed, mouse) {
153
351
  }
154
352
 
155
353
  // src/useShadertoy.ts
354
+ var CHANNEL_KEYS = ["iChannel0", "iChannel1", "iChannel2", "iChannel3"];
156
355
  function useShadertoy({
157
356
  fragmentShader,
357
+ textures: texturesProp,
158
358
  paused = false,
159
359
  speed = 1,
160
360
  pixelRatio,
@@ -188,16 +388,43 @@ function useShadertoy({
188
388
  return;
189
389
  }
190
390
  rendererRef.current = result;
191
- setIsReady(true);
192
- setError(null);
193
- onLoad?.();
391
+ const texturePromises = [];
392
+ if (texturesProp) {
393
+ for (let i = 0; i < 4; i++) {
394
+ const src = texturesProp[CHANNEL_KEYS[i]];
395
+ if (src != null) {
396
+ const { state, promise } = createTexture(result.gl, src, i);
397
+ result.textures[i] = state;
398
+ if (promise) texturePromises.push(promise);
399
+ }
400
+ }
401
+ }
402
+ const markReady = () => {
403
+ setIsReady(true);
404
+ setError(null);
405
+ onLoad?.();
406
+ };
407
+ if (texturePromises.length > 0) {
408
+ Promise.all(texturePromises).then(() => {
409
+ if (rendererRef.current) markReady();
410
+ }).catch((err) => {
411
+ const msg = err instanceof Error ? err.message : "Texture load failed";
412
+ setError(msg);
413
+ onError?.(msg);
414
+ });
415
+ } else {
416
+ markReady();
417
+ }
194
418
  let lastTimestamp = 0;
195
419
  const loop = (timestamp) => {
196
420
  const delta = lastTimestamp ? (timestamp - lastTimestamp) / 1e3 : 0;
197
421
  lastTimestamp = timestamp;
198
422
  if (!pausedRef.current && rendererRef.current) {
199
- updateUniforms(rendererRef.current, delta, speedRef.current, mouseState.current);
200
- render(rendererRef.current);
423
+ const r = rendererRef.current;
424
+ updateDynamicTextures(r.gl, r.textures);
425
+ bindTextures(r.gl, r.locations.iChannel, r.textures);
426
+ updateUniforms(r, delta, speedRef.current, mouseState.current);
427
+ render(r);
201
428
  }
202
429
  rafRef.current = requestAnimationFrame(loop);
203
430
  };
@@ -205,12 +432,13 @@ function useShadertoy({
205
432
  return () => {
206
433
  cancelAnimationFrame(rafRef.current);
207
434
  if (rendererRef.current) {
435
+ disposeTextures(rendererRef.current.gl, rendererRef.current.textures);
208
436
  dispose(rendererRef.current);
209
437
  rendererRef.current = null;
210
438
  }
211
439
  setIsReady(false);
212
440
  };
213
- }, [fragmentShader, onError, onLoad]);
441
+ }, [fragmentShader, texturesProp, onError, onLoad]);
214
442
  useEffect(() => {
215
443
  const canvas = canvasRef.current;
216
444
  if (!canvas) return;
@@ -288,8 +516,12 @@ function useShadertoy({
288
516
  }, []);
289
517
  return { canvasRef, isReady, error, pause, resume };
290
518
  }
519
+
520
+ // src/Shadertoy.tsx
521
+ import { jsx } from "react/jsx-runtime";
291
522
  function Shadertoy({
292
523
  fragmentShader,
524
+ textures,
293
525
  style,
294
526
  className,
295
527
  paused,
@@ -301,6 +533,7 @@ function Shadertoy({
301
533
  }) {
302
534
  const { canvasRef } = useShadertoy({
303
535
  fragmentShader,
536
+ textures,
304
537
  paused,
305
538
  speed,
306
539
  pixelRatio,
@@ -317,7 +550,7 @@ function Shadertoy({
317
550
  }
318
551
  );
319
552
  }
320
-
321
- export { Shadertoy, useShadertoy };
322
- //# sourceMappingURL=index.mjs.map
323
- //# sourceMappingURL=index.mjs.map
553
+ export {
554
+ Shadertoy,
555
+ useShadertoy
556
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-shadertoy",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "description": "Run Shadertoy GLSL shaders in React. Copy-paste and it works.",
5
5
  "author": "Wrennly (https://github.com/wrennly)",
6
6
  "license": "MIT",
package/dist/index.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/renderer.ts","../src/uniforms.ts","../src/useShadertoy.ts","../src/Shadertoy.tsx"],"names":["useRef","useState","useEffect","useCallback","jsx"],"mappings":"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","file":"index.js","sourcesContent":["import type { RendererState, UniformLocations } from './types'\n\n// Full-screen quad: two triangles covering clip space\nconst QUAD_VERTICES = new Float32Array([\n -1, -1,\n 1, -1,\n -1, 1,\n -1, 1,\n 1, -1,\n 1, 1,\n])\n\nconst VERTEX_SHADER = `\nattribute vec2 position;\nvoid main() {\n gl_Position = vec4(position, 0.0, 1.0);\n}\n`\n\n/**\n * Wrap Shadertoy GLSL: prepend uniform declarations + main() bridge.\n */\nfunction wrapFragmentShader(shader: string): string {\n return `precision highp float;\n\nuniform vec3 iResolution;\nuniform float iTime;\nuniform float iTimeDelta;\nuniform int iFrame;\nuniform vec4 iMouse;\nuniform vec4 iDate;\n\n${shader}\n\nvoid main() {\n mainImage(gl_FragColor, gl_FragCoord.xy);\n}\n`\n}\n\nfunction compileShader(\n gl: WebGLRenderingContext,\n type: number,\n source: string,\n): WebGLShader | string {\n const shader = gl.createShader(type)\n if (!shader) return 'Failed to create shader'\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 compile error'\n gl.deleteShader(shader)\n return log\n }\n\n return shader\n}\n\n/**\n * Initialize WebGL: compile shaders, link program, setup quad.\n * Returns RendererState on success or error string on failure.\n */\nexport function createRenderer(\n canvas: HTMLCanvasElement,\n fragmentShader: string,\n): RendererState | string {\n const gl = canvas.getContext('webgl', {\n antialias: false,\n alpha: true,\n premultipliedAlpha: false,\n })\n if (!gl) return 'WebGL not supported'\n\n // Compile vertex shader\n const vert = compileShader(gl, gl.VERTEX_SHADER, VERTEX_SHADER)\n if (typeof vert === 'string') return vert\n\n // Compile fragment shader (wrapped)\n const frag = compileShader(gl, gl.FRAGMENT_SHADER, wrapFragmentShader(fragmentShader))\n if (typeof frag === 'string') return frag\n\n // Link program\n const program = gl.createProgram()\n if (!program) return 'Failed to create program'\n\n gl.attachShader(program, vert)\n gl.attachShader(program, frag)\n gl.linkProgram(program)\n\n if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {\n const log = gl.getProgramInfoLog(program) || 'Unknown link error'\n gl.deleteProgram(program)\n return log\n }\n\n // Clean up individual shaders (attached to program)\n gl.deleteShader(vert)\n gl.deleteShader(frag)\n\n // Setup quad geometry\n const buffer = gl.createBuffer()\n gl.bindBuffer(gl.ARRAY_BUFFER, buffer)\n gl.bufferData(gl.ARRAY_BUFFER, QUAD_VERTICES, gl.STATIC_DRAW)\n\n const positionLoc = gl.getAttribLocation(program, 'position')\n gl.enableVertexAttribArray(positionLoc)\n gl.vertexAttribPointer(positionLoc, 2, gl.FLOAT, false, 0, 0)\n\n // Get uniform locations\n const locations: UniformLocations = {\n iResolution: gl.getUniformLocation(program, 'iResolution'),\n iTime: gl.getUniformLocation(program, 'iTime'),\n iTimeDelta: gl.getUniformLocation(program, 'iTimeDelta'),\n iFrame: gl.getUniformLocation(program, 'iFrame'),\n iMouse: gl.getUniformLocation(program, 'iMouse'),\n iDate: gl.getUniformLocation(program, 'iDate'),\n }\n\n gl.useProgram(program)\n\n return {\n gl,\n program,\n locations,\n time: 0,\n frame: 0,\n lastTime: 0,\n }\n}\n\n/**\n * Render one frame.\n */\nexport function render(state: RendererState): void {\n const { gl } = state\n gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight)\n gl.drawArrays(gl.TRIANGLES, 0, 6)\n}\n\n/**\n * Clean up WebGL resources.\n */\nexport function dispose(state: RendererState): void {\n const { gl, program } = state\n gl.deleteProgram(program)\n gl.getExtension('WEBGL_lose_context')?.loseContext()\n}\n","import type { MouseState, RendererState } from './types'\n\n/**\n * Update all Shadertoy standard uniforms for one frame.\n */\nexport function updateUniforms(\n state: RendererState,\n delta: number,\n speed: number,\n mouse: MouseState,\n): void {\n const { gl, locations } = state\n\n // iTime\n state.time += delta * speed\n if (locations.iTime) {\n gl.uniform1f(locations.iTime, state.time)\n }\n\n // iTimeDelta\n if (locations.iTimeDelta) {\n gl.uniform1f(locations.iTimeDelta, delta)\n }\n\n // iFrame\n state.frame++\n if (locations.iFrame) {\n gl.uniform1i(locations.iFrame, state.frame)\n }\n\n // iResolution\n if (locations.iResolution) {\n gl.uniform3f(\n locations.iResolution,\n gl.drawingBufferWidth,\n gl.drawingBufferHeight,\n 1.0,\n )\n }\n\n // iMouse — Shadertoy convention:\n // xy: current position (if pressed)\n // zw: click position (positive when pressed, negative when released)\n if (locations.iMouse) {\n const mz = mouse.pressed ? mouse.clickX : -Math.abs(mouse.clickX)\n const mw = mouse.pressed ? mouse.clickY : -Math.abs(mouse.clickY)\n gl.uniform4f(locations.iMouse, mouse.x, mouse.y, mz, mw)\n }\n\n // iDate — vec4(year, month, day, seconds_since_midnight)\n if (locations.iDate) {\n const now = new Date()\n const seconds =\n now.getHours() * 3600 + now.getMinutes() * 60 + now.getSeconds() + now.getMilliseconds() / 1000\n gl.uniform4f(\n locations.iDate,\n now.getFullYear(),\n now.getMonth(), // 0-based, matches Shadertoy\n now.getDate(),\n seconds,\n )\n }\n}\n","import { useCallback, useEffect, useRef, useState } from 'react'\nimport { createRenderer, dispose, render } from './renderer'\nimport type { MouseState, RendererState, UseShadertoyOptions, UseShadertoyReturn } from './types'\nimport { updateUniforms } from './uniforms'\n\nexport function useShadertoy({\n fragmentShader,\n paused = false,\n speed = 1.0,\n pixelRatio,\n mouse: mouseEnabled = true,\n onError,\n onLoad,\n}: UseShadertoyOptions): UseShadertoyReturn {\n const canvasRef = useRef<HTMLCanvasElement | null>(null)\n const rendererRef = useRef<RendererState | null>(null)\n const rafRef = useRef<number>(0)\n const pausedRef = useRef(paused)\n const speedRef = useRef(speed)\n\n const [isReady, setIsReady] = useState(false)\n const [error, setError] = useState<string | null>(null)\n\n const mouseState = useRef<MouseState>({\n x: 0, y: 0,\n clickX: 0, clickY: 0,\n pressed: false,\n })\n\n // Keep refs in sync\n pausedRef.current = paused\n speedRef.current = speed\n\n // Initialize WebGL\n useEffect(() => {\n const canvas = canvasRef.current\n if (!canvas) return\n\n const result = createRenderer(canvas, fragmentShader)\n if (typeof result === 'string') {\n setError(result)\n onError?.(result)\n return\n }\n\n rendererRef.current = result\n setIsReady(true)\n setError(null)\n onLoad?.()\n\n // Render loop\n let lastTimestamp = 0\n\n const loop = (timestamp: number) => {\n const delta = lastTimestamp ? (timestamp - lastTimestamp) / 1000 : 0\n lastTimestamp = timestamp\n\n if (!pausedRef.current && rendererRef.current) {\n updateUniforms(rendererRef.current, delta, speedRef.current, mouseState.current)\n render(rendererRef.current)\n }\n\n rafRef.current = requestAnimationFrame(loop)\n }\n\n rafRef.current = requestAnimationFrame(loop)\n\n return () => {\n cancelAnimationFrame(rafRef.current)\n if (rendererRef.current) {\n dispose(rendererRef.current)\n rendererRef.current = null\n }\n setIsReady(false)\n }\n }, [fragmentShader, onError, onLoad])\n\n // Canvas resize\n useEffect(() => {\n const canvas = canvasRef.current\n if (!canvas) return\n\n const dpr = pixelRatio ?? (typeof window !== 'undefined' ? window.devicePixelRatio : 1)\n\n const observer = new ResizeObserver((entries) => {\n for (const entry of entries) {\n const { width, height } = entry.contentRect\n canvas.width = Math.round(width * dpr)\n canvas.height = Math.round(height * dpr)\n }\n })\n\n observer.observe(canvas)\n return () => observer.disconnect()\n }, [pixelRatio])\n\n // Mouse / touch events\n useEffect(() => {\n if (!mouseEnabled) return\n const canvas = canvasRef.current\n if (!canvas) return\n\n const toPixel = (cx: number, cy: number) => {\n const r = canvas.getBoundingClientRect()\n const dpr = pixelRatio ?? window.devicePixelRatio\n return {\n x: (cx - r.left) * dpr,\n y: (r.height - (cy - r.top)) * dpr, // flip Y\n }\n }\n\n const onMove = (cx: number, cy: number) => {\n if (!mouseState.current.pressed) return\n const { x, y } = toPixel(cx, cy)\n mouseState.current.x = x\n mouseState.current.y = y\n }\n\n const onDown = (cx: number, cy: number) => {\n const { x, y } = toPixel(cx, cy)\n mouseState.current.x = x\n mouseState.current.y = y\n mouseState.current.clickX = x\n mouseState.current.clickY = y\n mouseState.current.pressed = true\n }\n\n const onUp = () => {\n mouseState.current.pressed = false\n }\n\n const mm = (e: MouseEvent) => onMove(e.clientX, e.clientY)\n const md = (e: MouseEvent) => onDown(e.clientX, e.clientY)\n const mu = () => onUp()\n const tm = (e: TouchEvent) => {\n if (e.touches[0]) onMove(e.touches[0].clientX, e.touches[0].clientY)\n }\n const ts = (e: TouchEvent) => {\n if (e.touches[0]) onDown(e.touches[0].clientX, e.touches[0].clientY)\n }\n const te = () => onUp()\n\n window.addEventListener('mousemove', mm)\n canvas.addEventListener('mousedown', md)\n window.addEventListener('mouseup', mu)\n window.addEventListener('touchmove', tm, { passive: true })\n canvas.addEventListener('touchstart', ts, { passive: true })\n window.addEventListener('touchend', te)\n\n return () => {\n window.removeEventListener('mousemove', mm)\n canvas.removeEventListener('mousedown', md)\n window.removeEventListener('mouseup', mu)\n window.removeEventListener('touchmove', tm)\n canvas.removeEventListener('touchstart', ts)\n window.removeEventListener('touchend', te)\n }\n }, [mouseEnabled, pixelRatio])\n\n const pause = useCallback(() => { pausedRef.current = true }, [])\n const resume = useCallback(() => { pausedRef.current = false }, [])\n\n return { canvasRef, isReady, error, pause, resume }\n}\n","import type { ShadertoyProps } from './types'\nimport { useShadertoy } from './useShadertoy'\n\nexport function Shadertoy({\n fragmentShader,\n style,\n className,\n paused,\n speed,\n pixelRatio,\n mouse,\n onError,\n onLoad,\n}: ShadertoyProps) {\n const { canvasRef } = useShadertoy({\n fragmentShader,\n paused,\n speed,\n pixelRatio,\n mouse,\n onError,\n onLoad,\n })\n\n return (\n <canvas\n ref={canvasRef}\n className={className}\n style={{ width: '100%', height: '100%', display: 'block', ...style }}\n />\n )\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/renderer.ts","../src/uniforms.ts","../src/useShadertoy.ts","../src/Shadertoy.tsx"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["import type { RendererState, UniformLocations } from './types'\n\n// Full-screen quad: two triangles covering clip space\nconst QUAD_VERTICES = new Float32Array([\n -1, -1,\n 1, -1,\n -1, 1,\n -1, 1,\n 1, -1,\n 1, 1,\n])\n\nconst VERTEX_SHADER = `\nattribute vec2 position;\nvoid main() {\n gl_Position = vec4(position, 0.0, 1.0);\n}\n`\n\n/**\n * Wrap Shadertoy GLSL: prepend uniform declarations + main() bridge.\n */\nfunction wrapFragmentShader(shader: string): string {\n return `precision highp float;\n\nuniform vec3 iResolution;\nuniform float iTime;\nuniform float iTimeDelta;\nuniform int iFrame;\nuniform vec4 iMouse;\nuniform vec4 iDate;\n\n${shader}\n\nvoid main() {\n mainImage(gl_FragColor, gl_FragCoord.xy);\n}\n`\n}\n\nfunction compileShader(\n gl: WebGLRenderingContext,\n type: number,\n source: string,\n): WebGLShader | string {\n const shader = gl.createShader(type)\n if (!shader) return 'Failed to create shader'\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 compile error'\n gl.deleteShader(shader)\n return log\n }\n\n return shader\n}\n\n/**\n * Initialize WebGL: compile shaders, link program, setup quad.\n * Returns RendererState on success or error string on failure.\n */\nexport function createRenderer(\n canvas: HTMLCanvasElement,\n fragmentShader: string,\n): RendererState | string {\n const gl = canvas.getContext('webgl', {\n antialias: false,\n alpha: true,\n premultipliedAlpha: false,\n })\n if (!gl) return 'WebGL not supported'\n\n // Compile vertex shader\n const vert = compileShader(gl, gl.VERTEX_SHADER, VERTEX_SHADER)\n if (typeof vert === 'string') return vert\n\n // Compile fragment shader (wrapped)\n const frag = compileShader(gl, gl.FRAGMENT_SHADER, wrapFragmentShader(fragmentShader))\n if (typeof frag === 'string') return frag\n\n // Link program\n const program = gl.createProgram()\n if (!program) return 'Failed to create program'\n\n gl.attachShader(program, vert)\n gl.attachShader(program, frag)\n gl.linkProgram(program)\n\n if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {\n const log = gl.getProgramInfoLog(program) || 'Unknown link error'\n gl.deleteProgram(program)\n return log\n }\n\n // Clean up individual shaders (attached to program)\n gl.deleteShader(vert)\n gl.deleteShader(frag)\n\n // Setup quad geometry\n const buffer = gl.createBuffer()\n gl.bindBuffer(gl.ARRAY_BUFFER, buffer)\n gl.bufferData(gl.ARRAY_BUFFER, QUAD_VERTICES, gl.STATIC_DRAW)\n\n const positionLoc = gl.getAttribLocation(program, 'position')\n gl.enableVertexAttribArray(positionLoc)\n gl.vertexAttribPointer(positionLoc, 2, gl.FLOAT, false, 0, 0)\n\n // Get uniform locations\n const locations: UniformLocations = {\n iResolution: gl.getUniformLocation(program, 'iResolution'),\n iTime: gl.getUniformLocation(program, 'iTime'),\n iTimeDelta: gl.getUniformLocation(program, 'iTimeDelta'),\n iFrame: gl.getUniformLocation(program, 'iFrame'),\n iMouse: gl.getUniformLocation(program, 'iMouse'),\n iDate: gl.getUniformLocation(program, 'iDate'),\n }\n\n gl.useProgram(program)\n\n return {\n gl,\n program,\n locations,\n time: 0,\n frame: 0,\n lastTime: 0,\n }\n}\n\n/**\n * Render one frame.\n */\nexport function render(state: RendererState): void {\n const { gl } = state\n gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight)\n gl.drawArrays(gl.TRIANGLES, 0, 6)\n}\n\n/**\n * Clean up WebGL resources.\n */\nexport function dispose(state: RendererState): void {\n const { gl, program } = state\n gl.deleteProgram(program)\n gl.getExtension('WEBGL_lose_context')?.loseContext()\n}\n","import type { MouseState, RendererState } from './types'\n\n/**\n * Update all Shadertoy standard uniforms for one frame.\n */\nexport function updateUniforms(\n state: RendererState,\n delta: number,\n speed: number,\n mouse: MouseState,\n): void {\n const { gl, locations } = state\n\n // iTime\n state.time += delta * speed\n if (locations.iTime) {\n gl.uniform1f(locations.iTime, state.time)\n }\n\n // iTimeDelta\n if (locations.iTimeDelta) {\n gl.uniform1f(locations.iTimeDelta, delta)\n }\n\n // iFrame\n state.frame++\n if (locations.iFrame) {\n gl.uniform1i(locations.iFrame, state.frame)\n }\n\n // iResolution\n if (locations.iResolution) {\n gl.uniform3f(\n locations.iResolution,\n gl.drawingBufferWidth,\n gl.drawingBufferHeight,\n 1.0,\n )\n }\n\n // iMouse — Shadertoy convention:\n // xy: current position (if pressed)\n // zw: click position (positive when pressed, negative when released)\n if (locations.iMouse) {\n const mz = mouse.pressed ? mouse.clickX : -Math.abs(mouse.clickX)\n const mw = mouse.pressed ? mouse.clickY : -Math.abs(mouse.clickY)\n gl.uniform4f(locations.iMouse, mouse.x, mouse.y, mz, mw)\n }\n\n // iDate — vec4(year, month, day, seconds_since_midnight)\n if (locations.iDate) {\n const now = new Date()\n const seconds =\n now.getHours() * 3600 + now.getMinutes() * 60 + now.getSeconds() + now.getMilliseconds() / 1000\n gl.uniform4f(\n locations.iDate,\n now.getFullYear(),\n now.getMonth(), // 0-based, matches Shadertoy\n now.getDate(),\n seconds,\n )\n }\n}\n","import { useCallback, useEffect, useRef, useState } from 'react'\nimport { createRenderer, dispose, render } from './renderer'\nimport type { MouseState, RendererState, UseShadertoyOptions, UseShadertoyReturn } from './types'\nimport { updateUniforms } from './uniforms'\n\nexport function useShadertoy({\n fragmentShader,\n paused = false,\n speed = 1.0,\n pixelRatio,\n mouse: mouseEnabled = true,\n onError,\n onLoad,\n}: UseShadertoyOptions): UseShadertoyReturn {\n const canvasRef = useRef<HTMLCanvasElement | null>(null)\n const rendererRef = useRef<RendererState | null>(null)\n const rafRef = useRef<number>(0)\n const pausedRef = useRef(paused)\n const speedRef = useRef(speed)\n\n const [isReady, setIsReady] = useState(false)\n const [error, setError] = useState<string | null>(null)\n\n const mouseState = useRef<MouseState>({\n x: 0, y: 0,\n clickX: 0, clickY: 0,\n pressed: false,\n })\n\n // Keep refs in sync\n pausedRef.current = paused\n speedRef.current = speed\n\n // Initialize WebGL\n useEffect(() => {\n const canvas = canvasRef.current\n if (!canvas) return\n\n const result = createRenderer(canvas, fragmentShader)\n if (typeof result === 'string') {\n setError(result)\n onError?.(result)\n return\n }\n\n rendererRef.current = result\n setIsReady(true)\n setError(null)\n onLoad?.()\n\n // Render loop\n let lastTimestamp = 0\n\n const loop = (timestamp: number) => {\n const delta = lastTimestamp ? (timestamp - lastTimestamp) / 1000 : 0\n lastTimestamp = timestamp\n\n if (!pausedRef.current && rendererRef.current) {\n updateUniforms(rendererRef.current, delta, speedRef.current, mouseState.current)\n render(rendererRef.current)\n }\n\n rafRef.current = requestAnimationFrame(loop)\n }\n\n rafRef.current = requestAnimationFrame(loop)\n\n return () => {\n cancelAnimationFrame(rafRef.current)\n if (rendererRef.current) {\n dispose(rendererRef.current)\n rendererRef.current = null\n }\n setIsReady(false)\n }\n }, [fragmentShader, onError, onLoad])\n\n // Canvas resize\n useEffect(() => {\n const canvas = canvasRef.current\n if (!canvas) return\n\n const dpr = pixelRatio ?? (typeof window !== 'undefined' ? window.devicePixelRatio : 1)\n\n const observer = new ResizeObserver((entries) => {\n for (const entry of entries) {\n const { width, height } = entry.contentRect\n canvas.width = Math.round(width * dpr)\n canvas.height = Math.round(height * dpr)\n }\n })\n\n observer.observe(canvas)\n return () => observer.disconnect()\n }, [pixelRatio])\n\n // Mouse / touch events\n useEffect(() => {\n if (!mouseEnabled) return\n const canvas = canvasRef.current\n if (!canvas) return\n\n const toPixel = (cx: number, cy: number) => {\n const r = canvas.getBoundingClientRect()\n const dpr = pixelRatio ?? window.devicePixelRatio\n return {\n x: (cx - r.left) * dpr,\n y: (r.height - (cy - r.top)) * dpr, // flip Y\n }\n }\n\n const onMove = (cx: number, cy: number) => {\n if (!mouseState.current.pressed) return\n const { x, y } = toPixel(cx, cy)\n mouseState.current.x = x\n mouseState.current.y = y\n }\n\n const onDown = (cx: number, cy: number) => {\n const { x, y } = toPixel(cx, cy)\n mouseState.current.x = x\n mouseState.current.y = y\n mouseState.current.clickX = x\n mouseState.current.clickY = y\n mouseState.current.pressed = true\n }\n\n const onUp = () => {\n mouseState.current.pressed = false\n }\n\n const mm = (e: MouseEvent) => onMove(e.clientX, e.clientY)\n const md = (e: MouseEvent) => onDown(e.clientX, e.clientY)\n const mu = () => onUp()\n const tm = (e: TouchEvent) => {\n if (e.touches[0]) onMove(e.touches[0].clientX, e.touches[0].clientY)\n }\n const ts = (e: TouchEvent) => {\n if (e.touches[0]) onDown(e.touches[0].clientX, e.touches[0].clientY)\n }\n const te = () => onUp()\n\n window.addEventListener('mousemove', mm)\n canvas.addEventListener('mousedown', md)\n window.addEventListener('mouseup', mu)\n window.addEventListener('touchmove', tm, { passive: true })\n canvas.addEventListener('touchstart', ts, { passive: true })\n window.addEventListener('touchend', te)\n\n return () => {\n window.removeEventListener('mousemove', mm)\n canvas.removeEventListener('mousedown', md)\n window.removeEventListener('mouseup', mu)\n window.removeEventListener('touchmove', tm)\n canvas.removeEventListener('touchstart', ts)\n window.removeEventListener('touchend', te)\n }\n }, [mouseEnabled, pixelRatio])\n\n const pause = useCallback(() => { pausedRef.current = true }, [])\n const resume = useCallback(() => { pausedRef.current = false }, [])\n\n return { canvasRef, isReady, error, pause, resume }\n}\n","import type { ShadertoyProps } from './types'\nimport { useShadertoy } from './useShadertoy'\n\nexport function Shadertoy({\n fragmentShader,\n style,\n className,\n paused,\n speed,\n pixelRatio,\n mouse,\n onError,\n onLoad,\n}: ShadertoyProps) {\n const { canvasRef } = useShadertoy({\n fragmentShader,\n paused,\n speed,\n pixelRatio,\n mouse,\n onError,\n onLoad,\n })\n\n return (\n <canvas\n ref={canvasRef}\n className={className}\n style={{ width: '100%', height: '100%', display: 'block', ...style }}\n />\n )\n}\n"]}