@fjandin/react-shader 0.0.16 → 0.0.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/dist/ReactGpuShader.d.ts +11 -0
  2. package/dist/ReactGpuShader.d.ts.map +1 -0
  3. package/dist/example/examples/webgl.d.ts +2 -0
  4. package/dist/example/examples/webgl.d.ts.map +1 -0
  5. package/dist/example/examples/webgpu.d.ts +2 -0
  6. package/dist/example/examples/webgpu.d.ts.map +1 -0
  7. package/dist/example/frontend.d.ts.map +1 -1
  8. package/dist/hooks/useWebGPU.d.ts +13 -0
  9. package/dist/hooks/useWebGPU.d.ts.map +1 -0
  10. package/dist/index.cjs +822 -54
  11. package/dist/index.d.ts +6 -0
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +822 -54
  14. package/dist/shaders/color-palette-gpu.d.ts +2 -0
  15. package/dist/shaders/color-palette-gpu.d.ts.map +1 -0
  16. package/dist/shaders/distortion-ripple-gpu.d.ts +16 -0
  17. package/dist/shaders/distortion-ripple-gpu.d.ts.map +1 -0
  18. package/dist/shaders/scene-circles-gpu.d.ts +21 -0
  19. package/dist/shaders/scene-circles-gpu.d.ts.map +1 -0
  20. package/dist/shaders/simplex-noise-gpu.d.ts +2 -0
  21. package/dist/shaders/simplex-noise-gpu.d.ts.map +1 -0
  22. package/package.json +2 -1
  23. package/dist/example/glsl/cartesian-to-polar.glsl.d.ts +0 -2
  24. package/dist/example/glsl/cartesian-to-polar.glsl.d.ts.map +0 -1
  25. package/dist/example/glsl/circles.glsl.d.ts +0 -3
  26. package/dist/example/glsl/circles.glsl.d.ts.map +0 -1
  27. package/dist/example/glsl/noise.glsl.d.ts +0 -3
  28. package/dist/example/glsl/noise.glsl.d.ts.map +0 -1
  29. package/dist/example/glsl/palette.glsl.d.ts +0 -2
  30. package/dist/example/glsl/palette.glsl.d.ts.map +0 -1
  31. package/dist/example/glsl/texture-demo.glsl.d.ts +0 -2
  32. package/dist/example/glsl/texture-demo.glsl.d.ts.map +0 -1
  33. package/dist/example/lib/logger.d.ts +0 -2
  34. package/dist/example/lib/logger.d.ts.map +0 -1
  35. package/dist/example/shader.d.ts +0 -2
  36. package/dist/example/shader.d.ts.map +0 -1
package/dist/index.cjs CHANGED
@@ -31,11 +31,16 @@ var exports_src = {};
31
31
  __export(exports_src, {
32
32
  useAudio: () => useAudio,
33
33
  generateUtilsFunction: () => generateUtilsFunction,
34
+ generateSimplexNoiseFunctionGpu: () => generateSimplexNoiseFunctionGpu,
34
35
  generateSimplexNoiseFunction: () => generateSimplexNoiseFunction,
36
+ generateSceneCirclesFunctionGpu: () => generateSceneCirclesFunctionGpu,
35
37
  generateSceneCirclesFunction: () => generateSceneCirclesFunction,
38
+ generateDistortionRippleFunctionGpu: () => generateDistortionRippleFunctionGpu,
36
39
  generateDistortionRippleFunction: () => generateDistortionRippleFunction,
40
+ generateColorPaletteFunctionGpu: () => generateColorPaletteFunctionGpu,
37
41
  generateColorPaletteFunction: () => generateColorPaletteFunction,
38
- ReactShader: () => ReactShader
42
+ ReactShader: () => ReactShader,
43
+ ReactGpuShader: () => ReactGpuShader
39
44
  });
40
45
  module.exports = __toCommonJS(exports_src);
41
46
 
@@ -247,11 +252,525 @@ function useAudio(options = {}) {
247
252
  isRunning
248
253
  };
249
254
  }
250
- // src/ReactShader.tsx
255
+ // src/ReactGpuShader.tsx
251
256
  var import_react3 = require("react");
252
257
 
253
- // src/hooks/useWebGL.ts
258
+ // src/hooks/useWebGPU.ts
254
259
  var import_react2 = require("react");
260
+ function isVec2(value) {
261
+ return Array.isArray(value) && value.length === 2 && typeof value[0] === "number";
262
+ }
263
+ function isVec3(value) {
264
+ return Array.isArray(value) && value.length === 3 && typeof value[0] === "number";
265
+ }
266
+ function isVec4(value) {
267
+ return Array.isArray(value) && value.length === 4 && typeof value[0] === "number";
268
+ }
269
+ function isVec4Array(value) {
270
+ return Array.isArray(value) && value.length > 0 && Array.isArray(value[0]) && value[0].length === 4;
271
+ }
272
+ function inferWgslType(value) {
273
+ if (typeof value === "number") {
274
+ return { wgslType: "f32", baseType: "f32", isArray: false, arrayLength: 0 };
275
+ }
276
+ if (isVec4Array(value)) {
277
+ return {
278
+ wgslType: `array<vec4f, ${value.length}>`,
279
+ baseType: "vec4f",
280
+ isArray: true,
281
+ arrayLength: value.length
282
+ };
283
+ }
284
+ if (isVec4(value)) {
285
+ return { wgslType: "vec4f", baseType: "vec4f", isArray: false, arrayLength: 0 };
286
+ }
287
+ if (isVec3(value)) {
288
+ return { wgslType: "vec3f", baseType: "vec3f", isArray: false, arrayLength: 0 };
289
+ }
290
+ if (isVec2(value)) {
291
+ return { wgslType: "vec2f", baseType: "vec2f", isArray: false, arrayLength: 0 };
292
+ }
293
+ throw new Error(`Unsupported uniform value type: ${typeof value}`);
294
+ }
295
+ function getTypeAlignment(type) {
296
+ switch (type) {
297
+ case "f32":
298
+ return 4;
299
+ case "vec2f":
300
+ return 8;
301
+ case "vec3f":
302
+ case "vec4f":
303
+ return 16;
304
+ }
305
+ }
306
+ function getTypeSize(type) {
307
+ switch (type) {
308
+ case "f32":
309
+ return 4;
310
+ case "vec2f":
311
+ return 8;
312
+ case "vec3f":
313
+ return 12;
314
+ case "vec4f":
315
+ return 16;
316
+ }
317
+ }
318
+ var UNIFORM_ARRAY_STRIDE = 16;
319
+ var DEFAULT_UNIFORMS = [
320
+ { name: "iTime", baseType: "f32" },
321
+ { name: "iMouseLeftDown", baseType: "f32" },
322
+ { name: "iResolution", baseType: "vec2f" },
323
+ { name: "iMouse", baseType: "vec2f" },
324
+ { name: "iMouseNormalized", baseType: "vec2f" }
325
+ ];
326
+ function calculateUniformLayout(customUniforms) {
327
+ const fields = [];
328
+ let offset = 0;
329
+ const addField = (name, baseType) => {
330
+ const alignment = getTypeAlignment(baseType);
331
+ const size = getTypeSize(baseType);
332
+ offset = Math.ceil(offset / alignment) * alignment;
333
+ fields.push({ name, type: baseType, offset, size, isArray: false });
334
+ offset += size;
335
+ };
336
+ const addArrayField = (name, baseType, arrayLength) => {
337
+ offset = Math.ceil(offset / 16) * 16;
338
+ const totalSize = arrayLength * UNIFORM_ARRAY_STRIDE;
339
+ fields.push({
340
+ name,
341
+ type: `array<${baseType}, ${arrayLength}>`,
342
+ offset,
343
+ size: totalSize,
344
+ isArray: true,
345
+ arrayLength,
346
+ elementStride: UNIFORM_ARRAY_STRIDE
347
+ });
348
+ offset += totalSize;
349
+ };
350
+ for (const u of DEFAULT_UNIFORMS) {
351
+ addField(u.name, u.baseType);
352
+ }
353
+ if (customUniforms) {
354
+ const scalarEntries = [];
355
+ const arrayEntries = [];
356
+ for (const [name, value] of Object.entries(customUniforms)) {
357
+ const inferred = inferWgslType(value);
358
+ if (inferred.isArray) {
359
+ arrayEntries.push({ name, inferred });
360
+ } else {
361
+ scalarEntries.push({ name, inferred });
362
+ }
363
+ }
364
+ for (const { name } of arrayEntries) {
365
+ scalarEntries.push({
366
+ name: `${name}_count`,
367
+ inferred: { wgslType: "f32", baseType: "f32", isArray: false, arrayLength: 0 }
368
+ });
369
+ }
370
+ scalarEntries.sort((a, b) => getTypeAlignment(b.inferred.baseType) - getTypeAlignment(a.inferred.baseType));
371
+ for (const { name, inferred } of scalarEntries) {
372
+ addField(name, inferred.baseType);
373
+ }
374
+ for (const { name, inferred } of arrayEntries) {
375
+ addArrayField(name, inferred.baseType, inferred.arrayLength);
376
+ }
377
+ }
378
+ const bufferSize = Math.ceil(offset / 16) * 16;
379
+ return { fields, bufferSize };
380
+ }
381
+ function generateUniformStruct(layout) {
382
+ const members = layout.fields.map((f) => ` ${f.name}: ${f.type},`).join(`
383
+ `);
384
+ return `struct Uniforms {
385
+ ${members}
386
+ }`;
387
+ }
388
+ function packUniformValue(field, value, floatData) {
389
+ const floatOffset = field.offset / 4;
390
+ if (field.isArray && field.elementStride && field.arrayLength) {
391
+ const stride = field.elementStride / 4;
392
+ const maxLen = field.arrayLength;
393
+ if (isVec4Array(value)) {
394
+ for (let i = 0;i < value.length && i < maxLen; i++) {
395
+ const elemOffset = floatOffset + i * stride;
396
+ floatData[elemOffset] = value[i][0];
397
+ floatData[elemOffset + 1] = value[i][1];
398
+ floatData[elemOffset + 2] = value[i][2];
399
+ floatData[elemOffset + 3] = value[i][3];
400
+ }
401
+ }
402
+ } else if (typeof value === "number") {
403
+ floatData[floatOffset] = value;
404
+ } else if (Array.isArray(value) && typeof value[0] === "number") {
405
+ for (let i = 0;i < value.length; i++) {
406
+ floatData[floatOffset + i] = value[i];
407
+ }
408
+ }
409
+ }
410
+ var VERTEX_SHADER = `
411
+ struct VertexOutput {
412
+ @builtin(position) position: vec4f,
413
+ @location(0) uv: vec2f,
414
+ }
415
+
416
+ @vertex
417
+ fn main(@builtin(vertex_index) vertexIndex: u32) -> VertexOutput {
418
+ // Full-screen triangle (covers clip space with a single triangle)
419
+ var pos = array<vec2f, 3>(
420
+ vec2f(-1.0, -1.0),
421
+ vec2f(3.0, -1.0),
422
+ vec2f(-1.0, 3.0)
423
+ );
424
+
425
+ var output: VertexOutput;
426
+ output.position = vec4f(pos[vertexIndex], 0.0, 1.0);
427
+ // UV coordinates: (0,0) at bottom-left, (1,1) at top-right
428
+ output.uv = (pos[vertexIndex] + 1.0) * 0.5;
429
+ return output;
430
+ }
431
+ `;
432
+ function createFragmentShader(userCode, layout) {
433
+ return `
434
+ ${generateUniformStruct(layout)}
435
+
436
+ @group(0) @binding(0) var<uniform> uniforms: Uniforms;
437
+
438
+ ${userCode}
439
+
440
+ @fragment
441
+ fn main(@location(0) uv: vec2f) -> @location(0) vec4f {
442
+ let fragCoord = uv * uniforms.iResolution;
443
+ let correctedUv = ((uv * uniforms.iResolution) - 0.5 * uniforms.iResolution) / uniforms.iResolution.y;
444
+
445
+ return mainImage(correctedUv);
446
+ }
447
+ `;
448
+ }
449
+ async function initializeWebGPU(canvas, fragmentSource, customUniforms) {
450
+ if (!navigator.gpu) {
451
+ throw new Error("WebGPU not supported in this browser");
452
+ }
453
+ const adapter = await navigator.gpu.requestAdapter();
454
+ if (!adapter) {
455
+ throw new Error("Failed to get WebGPU adapter");
456
+ }
457
+ const device = await adapter.requestDevice();
458
+ const context = canvas.getContext("webgpu");
459
+ if (!context) {
460
+ throw new Error("Failed to get WebGPU context");
461
+ }
462
+ const format = navigator.gpu.getPreferredCanvasFormat();
463
+ context.configure({
464
+ device,
465
+ format,
466
+ alphaMode: "premultiplied"
467
+ });
468
+ const uniformLayout = calculateUniformLayout(customUniforms);
469
+ const vertexModule = device.createShaderModule({
470
+ label: "vertex shader",
471
+ code: VERTEX_SHADER
472
+ });
473
+ const fragmentModule = device.createShaderModule({
474
+ label: "fragment shader",
475
+ code: createFragmentShader(fragmentSource, uniformLayout)
476
+ });
477
+ const uniformBuffer = device.createBuffer({
478
+ label: "uniforms",
479
+ size: uniformLayout.bufferSize,
480
+ usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
481
+ });
482
+ const bindGroupLayout = device.createBindGroupLayout({
483
+ entries: [
484
+ {
485
+ binding: 0,
486
+ visibility: GPUShaderStage.FRAGMENT,
487
+ buffer: { type: "uniform" }
488
+ }
489
+ ]
490
+ });
491
+ const uniformBindGroup = device.createBindGroup({
492
+ layout: bindGroupLayout,
493
+ entries: [
494
+ {
495
+ binding: 0,
496
+ resource: { buffer: uniformBuffer }
497
+ }
498
+ ]
499
+ });
500
+ const pipelineLayout = device.createPipelineLayout({
501
+ bindGroupLayouts: [bindGroupLayout]
502
+ });
503
+ const pipeline = device.createRenderPipeline({
504
+ label: "render pipeline",
505
+ layout: pipelineLayout,
506
+ vertex: {
507
+ module: vertexModule,
508
+ entryPoint: "main"
509
+ },
510
+ fragment: {
511
+ module: fragmentModule,
512
+ entryPoint: "main",
513
+ targets: [{ format }]
514
+ }
515
+ });
516
+ return {
517
+ device,
518
+ context,
519
+ pipeline,
520
+ uniformBuffer,
521
+ uniformBindGroup,
522
+ uniformLayout
523
+ };
524
+ }
525
+ function cleanupWebGPU(state) {
526
+ state.uniformBuffer.destroy();
527
+ state.device.destroy();
528
+ }
529
+ function useWebGPU(options) {
530
+ const canvasRef = import_react2.useRef(null);
531
+ const stateRef = import_react2.useRef(null);
532
+ const animationFrameRef = import_react2.useRef(0);
533
+ const elapsedTimeRef = import_react2.useRef(0);
534
+ const lastFrameTimeRef = import_react2.useRef(0);
535
+ const mouseRef = import_react2.useRef([0, 0]);
536
+ const mouseNormalizedRef = import_react2.useRef([0, 0]);
537
+ const mouseLeftDownRef = import_react2.useRef(false);
538
+ const canvasRectRef = import_react2.useRef(null);
539
+ const onErrorRef = import_react2.useRef(options.onError);
540
+ const fragmentRef = import_react2.useRef(options.fragment);
541
+ const uniformsRef = import_react2.useRef(options.uniforms);
542
+ const dprRef = import_react2.useRef(window.devicePixelRatio || 1);
543
+ onErrorRef.current = options.onError;
544
+ fragmentRef.current = options.fragment;
545
+ uniformsRef.current = options.uniforms;
546
+ const render = import_react2.useCallback((time) => {
547
+ const state = stateRef.current;
548
+ const canvas = canvasRef.current;
549
+ if (!state || !canvas) {
550
+ return;
551
+ }
552
+ const deltaTime = lastFrameTimeRef.current === 0 ? 0 : (time - lastFrameTimeRef.current) / 1000;
553
+ lastFrameTimeRef.current = time;
554
+ elapsedTimeRef.current += deltaTime;
555
+ const { device, context, pipeline, uniformBuffer, uniformBindGroup, uniformLayout } = state;
556
+ const dpr = dprRef.current;
557
+ const displayWidth = canvas.clientWidth;
558
+ const displayHeight = canvas.clientHeight;
559
+ if (displayWidth === 0 || displayHeight === 0) {
560
+ animationFrameRef.current = requestAnimationFrame(render);
561
+ return;
562
+ }
563
+ const bufferWidth = Math.round(displayWidth * dpr);
564
+ const bufferHeight = Math.round(displayHeight * dpr);
565
+ if (canvas.width !== bufferWidth || canvas.height !== bufferHeight) {
566
+ canvas.width = bufferWidth;
567
+ canvas.height = bufferHeight;
568
+ }
569
+ const defaultValues = {
570
+ iTime: elapsedTimeRef.current,
571
+ iMouseLeftDown: mouseLeftDownRef.current ? 1 : 0,
572
+ iResolution: [canvas.width, canvas.height],
573
+ iMouse: mouseRef.current,
574
+ iMouseNormalized: mouseNormalizedRef.current
575
+ };
576
+ const allValues = { ...defaultValues, ...uniformsRef.current };
577
+ if (uniformsRef.current) {
578
+ for (const [name, value] of Object.entries(uniformsRef.current)) {
579
+ if (isVec4Array(value)) {
580
+ allValues[`${name}_count`] = value.length;
581
+ }
582
+ }
583
+ }
584
+ const uniformData = new Float32Array(uniformLayout.bufferSize / 4);
585
+ for (const field of uniformLayout.fields) {
586
+ const value = allValues[field.name];
587
+ if (value === undefined) {
588
+ continue;
589
+ }
590
+ packUniformValue(field, value, uniformData);
591
+ }
592
+ device.queue.writeBuffer(uniformBuffer, 0, uniformData);
593
+ const commandEncoder = device.createCommandEncoder();
594
+ const textureView = context.getCurrentTexture().createView();
595
+ const renderPass = commandEncoder.beginRenderPass({
596
+ colorAttachments: [
597
+ {
598
+ view: textureView,
599
+ clearValue: { r: 0, g: 0, b: 0, a: 1 },
600
+ loadOp: "clear",
601
+ storeOp: "store"
602
+ }
603
+ ]
604
+ });
605
+ renderPass.setPipeline(pipeline);
606
+ renderPass.setBindGroup(0, uniformBindGroup);
607
+ renderPass.draw(3);
608
+ renderPass.end();
609
+ device.queue.submit([commandEncoder.finish()]);
610
+ animationFrameRef.current = requestAnimationFrame(render);
611
+ }, []);
612
+ import_react2.useEffect(() => {
613
+ const canvas = canvasRef.current;
614
+ if (!canvas)
615
+ return;
616
+ let mounted = true;
617
+ const initialize = async () => {
618
+ try {
619
+ const state = await initializeWebGPU(canvas, fragmentRef.current, uniformsRef.current);
620
+ if (!mounted) {
621
+ cleanupWebGPU(state);
622
+ return;
623
+ }
624
+ stateRef.current = state;
625
+ elapsedTimeRef.current = 0;
626
+ lastFrameTimeRef.current = 0;
627
+ animationFrameRef.current = requestAnimationFrame(render);
628
+ } catch (err) {
629
+ if (!mounted)
630
+ return;
631
+ const error = err instanceof Error ? err : new Error(String(err));
632
+ if (onErrorRef.current) {
633
+ onErrorRef.current(error);
634
+ } else {
635
+ console.error("WebGPU initialization failed:", error);
636
+ }
637
+ }
638
+ };
639
+ const dprMediaQuery = window.matchMedia(`(resolution: ${dprRef.current}dppx)`);
640
+ const handleDprChange = () => {
641
+ dprRef.current = window.devicePixelRatio || 1;
642
+ };
643
+ dprMediaQuery.addEventListener("change", handleDprChange);
644
+ initialize();
645
+ return () => {
646
+ mounted = false;
647
+ dprMediaQuery.removeEventListener("change", handleDprChange);
648
+ cancelAnimationFrame(animationFrameRef.current);
649
+ if (stateRef.current) {
650
+ cleanupWebGPU(stateRef.current);
651
+ stateRef.current = null;
652
+ }
653
+ };
654
+ }, [render]);
655
+ import_react2.useEffect(() => {
656
+ const canvas = canvasRef.current;
657
+ if (!canvas)
658
+ return;
659
+ const updateRect = () => {
660
+ canvasRectRef.current = canvas.getBoundingClientRect();
661
+ };
662
+ updateRect();
663
+ const resizeObserver = new ResizeObserver(updateRect);
664
+ resizeObserver.observe(canvas);
665
+ window.addEventListener("scroll", updateRect, { passive: true });
666
+ const handleMouseMove = (event) => {
667
+ const rect = canvasRectRef.current;
668
+ if (!rect)
669
+ return;
670
+ const dpr = dprRef.current;
671
+ const x = (event.clientX - rect.left) * dpr;
672
+ const y = (rect.height - (event.clientY - rect.top)) * dpr;
673
+ mouseRef.current = [x, y];
674
+ const minDimension = Math.min(canvas.width, canvas.height) || 1;
675
+ mouseNormalizedRef.current = [
676
+ (mouseRef.current[0] - canvas.width / 2) / minDimension,
677
+ (mouseRef.current[1] - canvas.height / 2) / minDimension
678
+ ];
679
+ };
680
+ const handleMouseDown = (event) => {
681
+ if (event.button === 0) {
682
+ mouseLeftDownRef.current = true;
683
+ }
684
+ };
685
+ const handleMouseUp = (event) => {
686
+ if (event.button === 0) {
687
+ mouseLeftDownRef.current = false;
688
+ }
689
+ };
690
+ window.addEventListener("mousemove", handleMouseMove);
691
+ window.addEventListener("mousedown", handleMouseDown);
692
+ window.addEventListener("mouseup", handleMouseUp);
693
+ return () => {
694
+ resizeObserver.disconnect();
695
+ window.removeEventListener("scroll", updateRect);
696
+ window.removeEventListener("mousemove", handleMouseMove);
697
+ window.removeEventListener("mousedown", handleMouseDown);
698
+ window.removeEventListener("mouseup", handleMouseUp);
699
+ };
700
+ }, []);
701
+ return { canvasRef, mouseRef };
702
+ }
703
+
704
+ // src/ReactGpuShader.tsx
705
+ var jsx_dev_runtime = require("react/jsx-dev-runtime");
706
+ var FULLSCREEN_CONTAINER_STYLE = {
707
+ position: "fixed",
708
+ top: 0,
709
+ left: 0,
710
+ width: "100vw",
711
+ height: "100vh",
712
+ zIndex: 9000
713
+ };
714
+ var DEFAULT_CONTAINER_STYLE = {
715
+ position: "relative",
716
+ width: "100%",
717
+ height: "100%"
718
+ };
719
+ var CANVAS_STYLE = {
720
+ display: "block",
721
+ width: "100%",
722
+ height: "100%"
723
+ };
724
+ function ReactGpuShader({ className, fragment, uniforms, fullscreen = false }) {
725
+ const [error, setError] = import_react3.useState(null);
726
+ const handleError = import_react3.useCallback((err) => {
727
+ setError(err.message);
728
+ console.error("ReactGpuShader error:", err);
729
+ }, []);
730
+ import_react3.useEffect(() => {
731
+ setError(null);
732
+ }, [fragment]);
733
+ const { canvasRef } = useWebGPU({
734
+ fragment,
735
+ uniforms,
736
+ onError: handleError
737
+ });
738
+ const containerStyle = import_react3.useMemo(() => fullscreen ? FULLSCREEN_CONTAINER_STYLE : DEFAULT_CONTAINER_STYLE, [fullscreen]);
739
+ if (error) {
740
+ return /* @__PURE__ */ jsx_dev_runtime.jsxDEV("div", {
741
+ className,
742
+ style: {
743
+ ...containerStyle,
744
+ display: "flex",
745
+ alignItems: "center",
746
+ justifyContent: "center",
747
+ backgroundColor: "#1a1a1a",
748
+ color: "#ff6b6b",
749
+ fontFamily: "monospace",
750
+ fontSize: "12px",
751
+ padding: "16px",
752
+ overflow: "auto",
753
+ boxSizing: "border-box",
754
+ width: "100%",
755
+ height: "100%"
756
+ },
757
+ children: /* @__PURE__ */ jsx_dev_runtime.jsxDEV("pre", {
758
+ style: { margin: 0, whiteSpace: "pre-wrap" },
759
+ children: error
760
+ }, undefined, false, undefined, this)
761
+ }, undefined, false, undefined, this);
762
+ }
763
+ return /* @__PURE__ */ jsx_dev_runtime.jsxDEV("canvas", {
764
+ ref: canvasRef,
765
+ className,
766
+ style: CANVAS_STYLE
767
+ }, undefined, false, undefined, this);
768
+ }
769
+ // src/ReactShader.tsx
770
+ var import_react5 = require("react");
771
+
772
+ // src/hooks/useWebGL.ts
773
+ var import_react4 = require("react");
255
774
 
256
775
  // src/utils/shader.ts
257
776
  function compileShader(gl, type, source) {
@@ -440,13 +959,13 @@ function cleanupTextures(gl, manager) {
440
959
 
441
960
  // src/utils/uniforms.ts
442
961
  var MAX_ARRAY_LENGTH = 100;
443
- function isVec2(value) {
962
+ function isVec22(value) {
444
963
  return Array.isArray(value) && value.length === 2 && typeof value[0] === "number";
445
964
  }
446
- function isVec3(value) {
965
+ function isVec32(value) {
447
966
  return Array.isArray(value) && value.length === 3 && typeof value[0] === "number";
448
967
  }
449
- function isVec4(value) {
968
+ function isVec42(value) {
450
969
  return Array.isArray(value) && value.length === 4 && typeof value[0] === "number";
451
970
  }
452
971
  function isFloatArray(value) {
@@ -458,11 +977,11 @@ function isVec2Array(value) {
458
977
  function isVec3Array(value) {
459
978
  return Array.isArray(value) && value.length > 0 && Array.isArray(value[0]) && value[0].length === 3;
460
979
  }
461
- function isVec4Array(value) {
980
+ function isVec4Array2(value) {
462
981
  return Array.isArray(value) && value.length > 0 && Array.isArray(value[0]) && value[0].length === 4;
463
982
  }
464
983
  function isArrayUniform(value) {
465
- return isFloatArray(value) || isVec2Array(value) || isVec3Array(value) || isVec4Array(value);
984
+ return isFloatArray(value) || isVec2Array(value) || isVec3Array(value) || isVec4Array2(value);
466
985
  }
467
986
  function setUniform(gl, location, value) {
468
987
  if (location === null) {
@@ -470,7 +989,7 @@ function setUniform(gl, location, value) {
470
989
  }
471
990
  if (typeof value === "number") {
472
991
  gl.uniform1f(location, value);
473
- } else if (isVec4Array(value)) {
992
+ } else if (isVec4Array2(value)) {
474
993
  gl.uniform4fv(location, value.flat());
475
994
  } else if (isVec3Array(value)) {
476
995
  gl.uniform3fv(location, value.flat());
@@ -478,11 +997,11 @@ function setUniform(gl, location, value) {
478
997
  gl.uniform2fv(location, value.flat());
479
998
  } else if (isFloatArray(value)) {
480
999
  gl.uniform1fv(location, value);
481
- } else if (isVec4(value)) {
1000
+ } else if (isVec42(value)) {
482
1001
  gl.uniform4f(location, value[0], value[1], value[2], value[3]);
483
- } else if (isVec3(value)) {
1002
+ } else if (isVec32(value)) {
484
1003
  gl.uniform3f(location, value[0], value[1], value[2]);
485
- } else if (isVec2(value)) {
1004
+ } else if (isVec22(value)) {
486
1005
  gl.uniform2f(location, value[0], value[1]);
487
1006
  }
488
1007
  }
@@ -526,7 +1045,7 @@ function getUniformType(value) {
526
1045
  if (typeof value === "number") {
527
1046
  return "float";
528
1047
  }
529
- if (isVec4Array(value)) {
1048
+ if (isVec4Array2(value)) {
530
1049
  return `vec4[${MAX_ARRAY_LENGTH}]`;
531
1050
  }
532
1051
  if (isVec3Array(value)) {
@@ -538,13 +1057,13 @@ function getUniformType(value) {
538
1057
  if (isFloatArray(value)) {
539
1058
  return `float[${MAX_ARRAY_LENGTH}]`;
540
1059
  }
541
- if (isVec4(value)) {
1060
+ if (isVec42(value)) {
542
1061
  return "vec4";
543
1062
  }
544
- if (isVec3(value)) {
1063
+ if (isVec32(value)) {
545
1064
  return "vec3";
546
1065
  }
547
- if (isVec2(value)) {
1066
+ if (isVec22(value)) {
548
1067
  return "vec2";
549
1068
  }
550
1069
  return "float";
@@ -616,29 +1135,29 @@ function cleanupWebGL(gl, state) {
616
1135
  gl.deleteProgram(state.program);
617
1136
  }
618
1137
  function useWebGL(options) {
619
- const canvasRef = import_react2.useRef(null);
620
- const stateRef = import_react2.useRef(null);
621
- const animationFrameRef = import_react2.useRef(0);
622
- const elapsedTimeRef = import_react2.useRef(0);
623
- const lastFrameTimeRef = import_react2.useRef(0);
624
- const mouseRef = import_react2.useRef([0, 0]);
625
- const mouseNormalizedRef = import_react2.useRef([0, 0]);
626
- const mouseLeftDownRef = import_react2.useRef(false);
627
- const canvasRectRef = import_react2.useRef(null);
628
- const contextLostRef = import_react2.useRef(false);
629
- const uniformsRef = import_react2.useRef(options.uniforms);
630
- const onErrorRef = import_react2.useRef(options.onError);
631
- const onFrameRef = import_react2.useRef(options.onFrame);
632
- const onClickRef = import_react2.useRef(options.onClick);
633
- const onMouseDownRef = import_react2.useRef(options.onMouseDown);
634
- const onMouseUpRef = import_react2.useRef(options.onMouseUp);
635
- const onMouseMoveRef = import_react2.useRef(options.onMouseMove);
636
- const onMouseWheelRef = import_react2.useRef(options.onMouseWheel);
637
- const timeScaleRef = import_react2.useRef(options.timeScale ?? 1);
638
- const vertexRef = import_react2.useRef(options.vertex);
639
- const fragmentRef = import_react2.useRef(options.fragment);
640
- const dprRef = import_react2.useRef(window.devicePixelRatio || 1);
641
- const defaultUniformsRef = import_react2.useRef({
1138
+ const canvasRef = import_react4.useRef(null);
1139
+ const stateRef = import_react4.useRef(null);
1140
+ const animationFrameRef = import_react4.useRef(0);
1141
+ const elapsedTimeRef = import_react4.useRef(0);
1142
+ const lastFrameTimeRef = import_react4.useRef(0);
1143
+ const mouseRef = import_react4.useRef([0, 0]);
1144
+ const mouseNormalizedRef = import_react4.useRef([0, 0]);
1145
+ const mouseLeftDownRef = import_react4.useRef(false);
1146
+ const canvasRectRef = import_react4.useRef(null);
1147
+ const contextLostRef = import_react4.useRef(false);
1148
+ const uniformsRef = import_react4.useRef(options.uniforms);
1149
+ const onErrorRef = import_react4.useRef(options.onError);
1150
+ const onFrameRef = import_react4.useRef(options.onFrame);
1151
+ const onClickRef = import_react4.useRef(options.onClick);
1152
+ const onMouseDownRef = import_react4.useRef(options.onMouseDown);
1153
+ const onMouseUpRef = import_react4.useRef(options.onMouseUp);
1154
+ const onMouseMoveRef = import_react4.useRef(options.onMouseMove);
1155
+ const onMouseWheelRef = import_react4.useRef(options.onMouseWheel);
1156
+ const timeScaleRef = import_react4.useRef(options.timeScale ?? 1);
1157
+ const vertexRef = import_react4.useRef(options.vertex);
1158
+ const fragmentRef = import_react4.useRef(options.fragment);
1159
+ const dprRef = import_react4.useRef(window.devicePixelRatio || 1);
1160
+ const defaultUniformsRef = import_react4.useRef({
642
1161
  iTime: 0,
643
1162
  iMouse: [0, 0],
644
1163
  iMouseNormalized: [0, 0],
@@ -656,7 +1175,7 @@ function useWebGL(options) {
656
1175
  timeScaleRef.current = options.timeScale ?? 1;
657
1176
  vertexRef.current = options.vertex;
658
1177
  fragmentRef.current = options.fragment;
659
- const render = import_react2.useCallback((time) => {
1178
+ const render = import_react4.useCallback((time) => {
660
1179
  if (contextLostRef.current)
661
1180
  return;
662
1181
  const state = stateRef.current;
@@ -708,7 +1227,7 @@ function useWebGL(options) {
708
1227
  }
709
1228
  animationFrameRef.current = requestAnimationFrame(render);
710
1229
  }, []);
711
- import_react2.useEffect(() => {
1230
+ import_react4.useEffect(() => {
712
1231
  const canvas = canvasRef.current;
713
1232
  if (!canvas)
714
1233
  return;
@@ -756,7 +1275,7 @@ function useWebGL(options) {
756
1275
  }
757
1276
  };
758
1277
  }, [render]);
759
- import_react2.useEffect(() => {
1278
+ import_react4.useEffect(() => {
760
1279
  const canvas = canvasRef.current;
761
1280
  if (!canvas)
762
1281
  return;
@@ -856,7 +1375,7 @@ function useWebGL(options) {
856
1375
  }
857
1376
 
858
1377
  // src/ReactShader.tsx
859
- var jsx_dev_runtime = require("react/jsx-dev-runtime");
1378
+ var jsx_dev_runtime2 = require("react/jsx-dev-runtime");
860
1379
  var DEFAULT_VERTEX = `#version 300 es
861
1380
  precision highp float;
862
1381
  in vec2 a_position;
@@ -865,7 +1384,7 @@ void main() {
865
1384
  gl_Position = vec4(a_position, 0.0, 1.0);
866
1385
  }
867
1386
  `;
868
- var FULLSCREEN_CONTAINER_STYLE = {
1387
+ var FULLSCREEN_CONTAINER_STYLE2 = {
869
1388
  position: "fixed",
870
1389
  top: 0,
871
1390
  left: 0,
@@ -873,12 +1392,12 @@ var FULLSCREEN_CONTAINER_STYLE = {
873
1392
  height: "100vh",
874
1393
  zIndex: 9000
875
1394
  };
876
- var DEFAULT_CONTAINER_STYLE = {
1395
+ var DEFAULT_CONTAINER_STYLE2 = {
877
1396
  position: "relative",
878
1397
  width: "100%",
879
1398
  height: "100%"
880
1399
  };
881
- var CANVAS_STYLE = {
1400
+ var CANVAS_STYLE2 = {
882
1401
  display: "block",
883
1402
  width: "100%",
884
1403
  height: "100%"
@@ -897,12 +1416,12 @@ function ReactShader({
897
1416
  onMouseUp,
898
1417
  onMouseWheel
899
1418
  }) {
900
- const [error, setError] = import_react3.useState(null);
901
- const handleError = import_react3.useCallback((err) => {
1419
+ const [error, setError] = import_react5.useState(null);
1420
+ const handleError = import_react5.useCallback((err) => {
902
1421
  setError(err.message);
903
1422
  console.error("ReactShader error:", err);
904
1423
  }, []);
905
- import_react3.useEffect(() => {
1424
+ import_react5.useEffect(() => {
906
1425
  setError(null);
907
1426
  }, [fragment, vertex]);
908
1427
  const { canvasRef } = useWebGL({
@@ -918,9 +1437,9 @@ function ReactShader({
918
1437
  onMouseWheel,
919
1438
  timeScale
920
1439
  });
921
- const containerStyle = import_react3.useMemo(() => fullscreen ? FULLSCREEN_CONTAINER_STYLE : DEFAULT_CONTAINER_STYLE, [fullscreen]);
1440
+ const containerStyle = import_react5.useMemo(() => fullscreen ? FULLSCREEN_CONTAINER_STYLE2 : DEFAULT_CONTAINER_STYLE2, [fullscreen]);
922
1441
  if (error) {
923
- return /* @__PURE__ */ jsx_dev_runtime.jsxDEV("div", {
1442
+ return /* @__PURE__ */ jsx_dev_runtime2.jsxDEV("div", {
924
1443
  className,
925
1444
  style: {
926
1445
  ...containerStyle,
@@ -937,16 +1456,16 @@ function ReactShader({
937
1456
  width: "100%",
938
1457
  height: "100%"
939
1458
  },
940
- children: /* @__PURE__ */ jsx_dev_runtime.jsxDEV("pre", {
1459
+ children: /* @__PURE__ */ jsx_dev_runtime2.jsxDEV("pre", {
941
1460
  style: { margin: 0, whiteSpace: "pre-wrap" },
942
1461
  children: error
943
1462
  }, undefined, false, undefined, this)
944
1463
  }, undefined, false, undefined, this);
945
1464
  }
946
- return /* @__PURE__ */ jsx_dev_runtime.jsxDEV("canvas", {
1465
+ return /* @__PURE__ */ jsx_dev_runtime2.jsxDEV("canvas", {
947
1466
  ref: canvasRef,
948
1467
  className,
949
- style: CANVAS_STYLE
1468
+ style: CANVAS_STYLE2
950
1469
  }, undefined, false, undefined, this);
951
1470
  }
952
1471
  // src/shaders/color-palette.ts
@@ -962,6 +1481,19 @@ function generateColorPaletteFunction(name, paletteString) {
962
1481
  }
963
1482
  `;
964
1483
  }
1484
+ // src/shaders/color-palette-gpu.ts
1485
+ function generateColorPaletteFunctionGpu(name, paletteString) {
1486
+ const paletteArray = paletteString.replace("[[", "").replace("]]", "").split("] [").map((s) => s.split(" "));
1487
+ return `
1488
+ fn ${name}(t: f32) -> vec3f {
1489
+ let a = vec3f(${paletteArray[0].join(", ")});
1490
+ let b = vec3f(${paletteArray[1].join(", ")});
1491
+ let c = vec3f(${paletteArray[2].join(", ")});
1492
+ let d = vec3f(${paletteArray[3].join(", ")});
1493
+ return a + b * cos(6.28318 * (c * t + d));
1494
+ }
1495
+ `;
1496
+ }
965
1497
  // src/shaders/distortion-ripple.ts
966
1498
  function generateDistortionRippleFunction() {
967
1499
  return `
@@ -985,6 +1517,29 @@ function generateDistortionRippleFunction() {
985
1517
  }
986
1518
  `;
987
1519
  }
1520
+ // src/shaders/distortion-ripple-gpu.ts
1521
+ function generateDistortionRippleFunctionGpu() {
1522
+ return `
1523
+ fn DistortionRipple(uv: vec2f, center: vec2f, radius: f32, intensity: f32, thickness: f32) -> vec2f {
1524
+ // 1. Calculate vector and distance from center
1525
+ let dir = uv - center;
1526
+ let dist = length(dir);
1527
+
1528
+ // 2. Create a mask so the ripple only exists near the radius
1529
+ // Using smoothstep creates a soft edge for the ripple
1530
+ let mask = smoothstep(radius + thickness, radius, dist) * smoothstep(radius - thickness, radius, dist);
1531
+
1532
+ // 3. Calculate the displacement amount using a Sine wave
1533
+ // We subtract dist from radius to orient the wave correctly
1534
+ let wave = sin((dist - radius) * 20.0);
1535
+
1536
+ // 4. Apply intensity and mask, then offset the UV
1537
+ let offset = normalize(dir) * wave * intensity * mask;
1538
+
1539
+ return offset;
1540
+ }
1541
+ `;
1542
+ }
988
1543
  // src/shaders/scene-circles.ts
989
1544
  function generateSceneCirclesFunction(paletteString = "[[0.5 0.5 0.5] [0.5 0.5 0.5] [1.0 1.0 1.0] [0.263 0.416 0.557]]") {
990
1545
  return `
@@ -1019,6 +1574,44 @@ function generateSceneCirclesFunction(paletteString = "[[0.5 0.5 0.5] [0.5 0.5 0
1019
1574
  }
1020
1575
  `;
1021
1576
  }
1577
+ // src/shaders/scene-circles-gpu.ts
1578
+ function generateSceneCirclesFunctionGpu(paletteString = "[[0.5 0.5 0.5] [0.5 0.5 0.5] [1.0 1.0 1.0] [0.263 0.416 0.557]]") {
1579
+ return `
1580
+ ${generateColorPaletteFunctionGpu("circlesPalette", paletteString)}
1581
+
1582
+ fn glsl_fract(x: vec2f) -> vec2f {
1583
+ return x - floor(x);
1584
+ }
1585
+
1586
+ fn SceneCircles(
1587
+ uv0: vec2f,
1588
+ iterations: f32,
1589
+ fractMultiplier: f32,
1590
+ time: f32,
1591
+ waveLength: f32,
1592
+ edgeBlur: f32,
1593
+ contrast: f32
1594
+ ) -> vec3f {
1595
+ var col = vec3f(0.0);
1596
+ var uv = uv0;
1597
+
1598
+ for (var i = 0.0; i < iterations; i += 1.0) {
1599
+ uv = fract(uv * fractMultiplier) - 0.5;
1600
+
1601
+ var d = length(uv) * exp(-length(uv0));
1602
+
1603
+ let color = circlesPalette(length(uv0) + i * 0.4 + time * 0.4);
1604
+
1605
+ d = sin(d * waveLength + time) / waveLength;
1606
+ d = abs(d);
1607
+ d = pow(edgeBlur / d, contrast);
1608
+
1609
+ col += color * d;
1610
+ }
1611
+ return col;
1612
+ }
1613
+ `;
1614
+ }
1022
1615
  // src/shaders/simplex-noise.ts
1023
1616
  function generateSimplexNoiseFunction() {
1024
1617
  return `
@@ -1203,6 +1796,181 @@ function generateSimplexNoiseFunction() {
1203
1796
  }
1204
1797
  `;
1205
1798
  }
1799
+ // src/shaders/simplex-noise-gpu.ts
1800
+ function generateSimplexNoiseFunctionGpu() {
1801
+ return `
1802
+
1803
+ fn mod289_4(x: vec4f) -> vec4f { return x - floor(x * (1.0 / 289.0)) * 289.0; }
1804
+
1805
+ fn mod289_3(x: vec3f) -> vec3f { return x - floor(x * (1.0 / 289.0)) * 289.0; }
1806
+
1807
+ fn mod289_1(x: f32) -> f32 { return x - floor(x * (1.0 / 289.0)) * 289.0; }
1808
+
1809
+ fn permute_4(x: vec4f) -> vec4f { return mod289_4(((x * 34.0) + 10.0) * x); }
1810
+
1811
+ fn permute_1(x: f32) -> f32 { return mod289_1(((x * 34.0) + 10.0) * x); }
1812
+
1813
+ fn taylorInvSqrt_4(r: vec4f) -> vec4f { return 1.79284291400159 - 0.85373472095314 * r; }
1814
+
1815
+ fn taylorInvSqrt_1(r: f32) -> f32 { return 1.79284291400159 - 0.85373472095314 * r; }
1816
+
1817
+ fn grad4(j: f32, ip: vec4f) -> vec4f {
1818
+ let ones = vec4f(1.0, 1.0, 1.0, -1.0);
1819
+ var p: vec4f;
1820
+ var s: vec4f;
1821
+
1822
+ p = vec4f(
1823
+ floor(fract(vec3f(j) * ip.xyz) * 7.0) * ip.z - 1.0,
1824
+ 0.0
1825
+ );
1826
+ p.w = 1.5 - dot(abs(p.xyz), ones.xyz);
1827
+ s = select(vec4f(0.0), vec4f(1.0), p < vec4f(0.0));
1828
+ p = vec4f(p.xyz + (s.xyz * 2.0 - 1.0) * s.www, p.w);
1829
+
1830
+ return p;
1831
+ }
1832
+
1833
+ // (sqrt(5) - 1)/4 = F4
1834
+ const F4: f32 = 0.309016994374947451;
1835
+
1836
+ fn SimplexNoise4D(v: vec4f) -> f32 {
1837
+ let C = vec4f(
1838
+ 0.138196601125011, // (5 - sqrt(5))/20 G4
1839
+ 0.276393202250021, // 2 * G4
1840
+ 0.414589803375032, // 3 * G4
1841
+ -0.447213595499958 // -1 + 4 * G4
1842
+ );
1843
+
1844
+ // First corner
1845
+ var i = floor(v + dot(v, vec4f(F4)));
1846
+ let x0 = v - i + dot(i, C.xxxx);
1847
+
1848
+ // Other corners
1849
+ // Rank sorting originally contributed by Bill Licea-Kane, AMD (formerly ATI)
1850
+ var i0: vec4f;
1851
+ let isX = step(x0.yzw, x0.xxx);
1852
+ let isYZ = step(x0.zww, x0.yyz);
1853
+ i0.x = isX.x + isX.y + isX.z;
1854
+ i0 = vec4f(i0.x, 1.0 - isX);
1855
+ i0.y = i0.y + isYZ.x + isYZ.y;
1856
+ i0 = vec4f(i0.x, i0.y, i0.zw + 1.0 - isYZ.xy);
1857
+ i0.z = i0.z + isYZ.z;
1858
+ i0.w = i0.w + 1.0 - isYZ.z;
1859
+
1860
+ // i0 now contains the unique values 0,1,2,3 in each channel
1861
+ let i3 = clamp(i0, vec4f(0.0), vec4f(1.0));
1862
+ let i2 = clamp(i0 - 1.0, vec4f(0.0), vec4f(1.0));
1863
+ let i1 = clamp(i0 - 2.0, vec4f(0.0), vec4f(1.0));
1864
+
1865
+ let x1 = x0 - i1 + C.xxxx;
1866
+ let x2 = x0 - i2 + C.yyyy;
1867
+ let x3 = x0 - i3 + C.zzzz;
1868
+ let x4 = x0 + C.wwww;
1869
+
1870
+ // Permutations
1871
+ i = mod289_4(i);
1872
+ let j0 = permute_1(permute_1(permute_1(permute_1(i.w) + i.z) + i.y) + i.x);
1873
+ let j1 = permute_4(permute_4(permute_4(permute_4(
1874
+ i.w + vec4f(i1.w, i2.w, i3.w, 1.0))
1875
+ + i.z + vec4f(i1.z, i2.z, i3.z, 1.0))
1876
+ + i.y + vec4f(i1.y, i2.y, i3.y, 1.0))
1877
+ + i.x + vec4f(i1.x, i2.x, i3.x, 1.0));
1878
+
1879
+ // Gradients: 7x7x6 points over a cube, mapped onto a 4-cross polytope
1880
+ // 7*7*6 = 294, which is close to the ring size 17*17 = 289.
1881
+ let ip = vec4f(1.0 / 294.0, 1.0 / 49.0, 1.0 / 7.0, 0.0);
1882
+
1883
+ var p0 = grad4(j0, ip);
1884
+ var p1 = grad4(j1.x, ip);
1885
+ var p2 = grad4(j1.y, ip);
1886
+ var p3 = grad4(j1.z, ip);
1887
+ var p4 = grad4(j1.w, ip);
1888
+
1889
+ // Normalise gradients
1890
+ let norm = taylorInvSqrt_4(vec4f(dot(p0, p0), dot(p1, p1), dot(p2, p2), dot(p3, p3)));
1891
+ p0 = p0 * norm.x;
1892
+ p1 = p1 * norm.y;
1893
+ p2 = p2 * norm.z;
1894
+ p3 = p3 * norm.w;
1895
+ p4 = p4 * taylorInvSqrt_1(dot(p4, p4));
1896
+
1897
+ // Mix contributions from the five corners
1898
+ var m0 = max(0.6 - vec3f(dot(x0, x0), dot(x1, x1), dot(x2, x2)), vec3f(0.0));
1899
+ var m1 = max(0.6 - vec2f(dot(x3, x3), dot(x4, x4)), vec2f(0.0));
1900
+ m0 = m0 * m0;
1901
+ m1 = m1 * m1;
1902
+ return 49.0 * (dot(m0 * m0, vec3f(dot(p0, x0), dot(p1, x1), dot(p2, x2)))
1903
+ + dot(m1 * m1, vec2f(dot(p3, x3), dot(p4, x4))));
1904
+ }
1905
+
1906
+ fn SimplexNoise3D(v: vec3f) -> f32 {
1907
+ let C = vec2f(1.0 / 6.0, 1.0 / 3.0);
1908
+ let D = vec4f(0.0, 0.5, 1.0, 2.0);
1909
+
1910
+ // First corner
1911
+ var i = floor(v + dot(v, C.yyy));
1912
+ let x0 = v - i + dot(i, C.xxx);
1913
+
1914
+ // Other corners
1915
+ let g = step(x0.yzx, x0.xyz);
1916
+ let l = 1.0 - g;
1917
+ let i1 = min(g.xyz, l.zxy);
1918
+ let i2 = max(g.xyz, l.zxy);
1919
+
1920
+ let x1 = x0 - i1 + C.xxx;
1921
+ let x2 = x0 - i2 + C.yyy; // 2.0*C.x = 1/3 = C.y
1922
+ let x3 = x0 - D.yyy; // -1.0+3.0*C.x = -0.5 = -D.y
1923
+
1924
+ // Permutations
1925
+ i = mod289_3(i);
1926
+ let p = permute_4(permute_4(permute_4(
1927
+ i.z + vec4f(0.0, i1.z, i2.z, 1.0))
1928
+ + i.y + vec4f(0.0, i1.y, i2.y, 1.0))
1929
+ + i.x + vec4f(0.0, i1.x, i2.x, 1.0));
1930
+
1931
+ // Gradients: 7x7 points over a square, mapped onto an octahedron.
1932
+ // The ring size 17*17 = 289 is close to a multiple of 49 (49*6 = 294)
1933
+ let n_ = 0.142857142857; // 1.0/7.0
1934
+ let ns = n_ * D.wyz - D.xzx;
1935
+
1936
+ let j = p - 49.0 * floor(p * ns.z * ns.z); // mod(p,7*7)
1937
+
1938
+ let x_ = floor(j * ns.z);
1939
+ let y_ = floor(j - 7.0 * x_); // mod(j,N)
1940
+
1941
+ let x = x_ * ns.x + ns.yyyy;
1942
+ let y = y_ * ns.x + ns.yyyy;
1943
+ let h = 1.0 - abs(x) - abs(y);
1944
+
1945
+ let b0 = vec4f(x.xy, y.xy);
1946
+ let b1 = vec4f(x.zw, y.zw);
1947
+
1948
+ let s0 = floor(b0) * 2.0 + 1.0;
1949
+ let s1 = floor(b1) * 2.0 + 1.0;
1950
+ let sh = -step(h, vec4f(0.0));
1951
+
1952
+ let a0 = b0.xzyw + s0.xzyw * sh.xxyy;
1953
+ let a1 = b1.xzyw + s1.xzyw * sh.zzww;
1954
+
1955
+ var p0 = vec3f(a0.xy, h.x);
1956
+ var p1 = vec3f(a0.zw, h.y);
1957
+ var p2 = vec3f(a1.xy, h.z);
1958
+ var p3 = vec3f(a1.zw, h.w);
1959
+
1960
+ // Normalise gradients
1961
+ let norm = taylorInvSqrt_4(vec4f(dot(p0, p0), dot(p1, p1), dot(p2, p2), dot(p3, p3)));
1962
+ p0 = p0 * norm.x;
1963
+ p1 = p1 * norm.y;
1964
+ p2 = p2 * norm.z;
1965
+ p3 = p3 * norm.w;
1966
+
1967
+ // Mix final noise value
1968
+ var m = max(0.5 - vec4f(dot(x0, x0), dot(x1, x1), dot(x2, x2), dot(x3, x3)), vec4f(0.0));
1969
+ m = m * m;
1970
+ return 105.0 * dot(m * m, vec4f(dot(p0, x0), dot(p1, x1), dot(p2, x2), dot(p3, x3)));
1971
+ }
1972
+ `;
1973
+ }
1206
1974
  // src/shaders/utils.ts
1207
1975
  function generateUtilsFunction() {
1208
1976
  return `