@paper-design/shaders 0.0.1 → 0.0.2

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.
@@ -1,5 +1,4 @@
1
1
  /** The core Shader Mounting class. Pass it a canvas element and a fragment shader to get started. */
2
2
  export { ShaderMount } from './shader-mount';
3
-
4
3
  /** An example fragment shader that renders a grainy texture over top of blobby animated clouds */
5
4
  export { grainCloudsFragmentShader } from './shaders/grain-clouds';
package/dist/index.js ADDED
@@ -0,0 +1,80 @@
1
+ var o=class{canvas;gl;program=null;uniformLocations={};fragmentShader;rafId=null;lastFrameTime=0;totalAnimationTime=0;isAnimated;providedUniforms;hasBeenDisposed=!1;resolutionChanged=!0;constructor(r,e,i={},n,s=!0){this.canvas=r,this.fragmentShader=e,this.providedUniforms=i,this.isAnimated=s;let a=r.getContext("webgl",n);if(!a)throw new Error("WebGL not supported");this.gl=a,this.initWebGL(),this.setupResizeObserver(),this.isAnimated?this.startAnimating():this.render(performance.now())}initWebGL=()=>{let r=m(this.gl,u,this.fragmentShader);r&&(this.program=r,this.setupPositionAttribute(),this.setupUniforms())};setupPositionAttribute=()=>{let r=this.gl.getAttribLocation(this.program,"a_position"),e=this.gl.createBuffer();this.gl.bindBuffer(this.gl.ARRAY_BUFFER,e);let i=[-1,-1,1,-1,-1,1,-1,1,1,-1,1,1];this.gl.bufferData(this.gl.ARRAY_BUFFER,new Float32Array(i),this.gl.STATIC_DRAW),this.gl.enableVertexAttribArray(r),this.gl.vertexAttribPointer(r,2,this.gl.FLOAT,!1,0,0)};setupUniforms=()=>{this.uniformLocations={u_time:this.gl.getUniformLocation(this.program,"u_time"),u_resolution:this.gl.getUniformLocation(this.program,"u_resolution"),...Object.fromEntries(Object.keys(this.providedUniforms).map(r=>[r,this.gl.getUniformLocation(this.program,r)]))}};resizeObserver=null;setupResizeObserver=()=>{this.resizeObserver=new ResizeObserver(()=>this.handleResize()),this.resizeObserver.observe(this.canvas),this.handleResize()};handleResize=()=>{let r=this.canvas.clientWidth,e=this.canvas.clientHeight;(this.canvas.width!==r||this.canvas.height!==e)&&(this.canvas.width=r,this.canvas.height=e,this.resolutionChanged=!0,this.gl.viewport(0,0,this.gl.canvas.width,this.gl.canvas.height),this.render(performance.now()))};render=r=>{if(this.hasBeenDisposed)return;let e=r-this.lastFrameTime;this.lastFrameTime=r,this.isAnimated&&(this.totalAnimationTime+=e),this.gl.useProgram(this.program),this.gl.uniform1f(this.uniformLocations.u_time,this.totalAnimationTime*.001),this.resolutionChanged&&(this.gl.uniform2f(this.uniformLocations.u_resolution,this.gl.canvas.width,this.gl.canvas.height),this.resolutionChanged=!1),this.gl.drawArrays(this.gl.TRIANGLES,0,6),this.isAnimated?this.requestRender():this.rafId=null};requestRender=()=>{this.rafId!==null&&cancelAnimationFrame(this.rafId),this.rafId=requestAnimationFrame(this.render)};updateProvidedUniforms=()=>{this.gl.useProgram(this.program),Object.entries(this.providedUniforms).forEach(([r,e])=>{let i=this.uniformLocations[r];if(i)if(Array.isArray(e))switch(e.length){case 2:this.gl.uniform2fv(i,e);break;case 3:this.gl.uniform3fv(i,e);break;case 4:this.gl.uniform4fv(i,e);break;default:e.length===9?this.gl.uniformMatrix3fv(i,!1,e):e.length===16?this.gl.uniformMatrix4fv(i,!1,e):console.warn(`Unsupported uniform array length: ${e.length}`)}else typeof e=="number"?this.gl.uniform1f(i,e):typeof e=="boolean"?this.gl.uniform1i(i,e?1:0):console.warn(`Unsupported uniform type for ${r}: ${typeof e}`)})};startAnimating=()=>{this.isAnimated=!0,this.lastFrameTime=performance.now(),this.rafId=requestAnimationFrame(this.render)};stopAnimating=()=>{this.isAnimated=!1,this.rafId&&(cancelAnimationFrame(this.rafId),this.rafId=null)};setUniforms=r=>{this.providedUniforms={...this.providedUniforms,...r},this.updateProvidedUniforms(),this.render(performance.now())};dispose=()=>{this.hasBeenDisposed=!0,this.stopAnimating(),this.gl&&this.program&&(this.gl.deleteProgram(this.program),this.program=null,this.gl.bindBuffer(this.gl.ARRAY_BUFFER,null),this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER,null),this.gl.bindRenderbuffer(this.gl.RENDERBUFFER,null),this.gl.bindFramebuffer(this.gl.FRAMEBUFFER,null),this.gl.getError()),this.resizeObserver&&(this.resizeObserver.disconnect(),this.resizeObserver=null),this.uniformLocations={}}},u=`
2
+ attribute vec4 a_position;
3
+ void main() {
4
+ gl_Position = a_position;
5
+ }
6
+ `;function h(t,r,e){let i=t.createShader(r);return i?(t.shaderSource(i,e),t.compileShader(i),t.getShaderParameter(i,t.COMPILE_STATUS)?i:(console.error("An error occurred compiling the shaders: "+t.getShaderInfoLog(i)),t.deleteShader(i),null)):null}function m(t,r,e){let i=h(t,t.VERTEX_SHADER,r),n=h(t,t.FRAGMENT_SHADER,e);if(!i||!n)return null;let s=t.createProgram();return s?(t.attachShader(s,i),t.attachShader(s,n),t.linkProgram(s),t.getProgramParameter(s,t.LINK_STATUS)?(t.detachShader(s,i),t.detachShader(s,n),t.deleteShader(i),t.deleteShader(n),s):(console.error("Unable to initialize the shader program: "+t.getProgramInfoLog(s)),t.deleteProgram(s),t.deleteShader(i),t.deleteShader(n),null)):null}var l=`
7
+ precision highp float;
8
+ uniform vec2 u_resolution;
9
+ uniform float u_time;
10
+ uniform vec3 u_color1;
11
+ uniform vec3 u_color2;
12
+ uniform float u_noiseScale;
13
+ uniform float u_noiseSpeed;
14
+ uniform float u_grainAmount;
15
+
16
+ // Simplex 2D noise
17
+ vec3 permute(vec3 x) { return mod(((x*34.0)+1.0)*x, 289.0); }
18
+
19
+ float snoise(vec2 v) {
20
+ const vec4 C = vec4(0.211324865405187, 0.366025403784439,
21
+ -0.577350269189626, 0.024390243902439);
22
+ vec2 i = floor(v + dot(v, C.yy));
23
+ vec2 x0 = v - i + dot(i, C.xx);
24
+ vec2 i1;
25
+ i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
26
+ vec4 x12 = x0.xyxy + C.xxzz;
27
+ x12.xy -= i1;
28
+ i = mod(i, 289.0);
29
+ vec3 p = permute( permute( i.y + vec3(0.0, i1.y, 1.0))
30
+ + i.x + vec3(0.0, i1.x, 1.0));
31
+ vec3 m = max(0.5 - vec3(dot(x0,x0), dot(x12.xy,x12.xy),
32
+ dot(x12.zw,x12.zw)), 0.0);
33
+ m = m*m ;
34
+ m = m*m ;
35
+ vec3 x = 2.0 * fract(p * C.www) - 1.0;
36
+ vec3 h = abs(x) - 0.5;
37
+ vec3 ox = floor(x + 0.5);
38
+ vec3 a0 = x - ox;
39
+ m *= 1.79284291400159 - 0.85373472095314 * (a0*a0 + h*h);
40
+ vec3 g;
41
+ g.x = a0.x * x0.x + h.x * x0.y;
42
+ g.yz = a0.yz * x12.xz + h.yz * x12.yw;
43
+ return 130.0 * dot(m, g);
44
+ }
45
+
46
+ void main() {
47
+ vec2 st = gl_FragCoord.xy / u_resolution.xy;
48
+
49
+ // Calculate the aspect ratio of the shader
50
+ float shaderAspect = u_resolution.x / u_resolution.y;
51
+
52
+ // Define the aspect ratio of your content (e.g., 1.0 for square)
53
+ float contentAspect = 1.0;
54
+
55
+ // Adjust st to maintain content aspect ratio
56
+ if (shaderAspect > contentAspect) {
57
+ float scale = shaderAspect / contentAspect;
58
+ st.x = (st.x - 0.5) * scale + 0.5;
59
+ } else {
60
+ float scale = contentAspect / shaderAspect;
61
+ st.y = (st.y - 0.5) * scale + 0.5;
62
+ }
63
+
64
+ // Create blobby texture
65
+ float n = snoise(st * u_noiseScale + u_time * u_noiseSpeed);
66
+ n += 0.5 * snoise(st * u_noiseScale * 2.0 - u_time * u_noiseSpeed * 0.5);
67
+ n += 0.25 * snoise(st * u_noiseScale * 4.0 + u_time * u_noiseSpeed * 0.25);
68
+ n = n * 0.5 + 0.5;
69
+
70
+ // Color interpolation
71
+ vec3 color = mix(u_color1, u_color2, n);
72
+
73
+ // Add grain
74
+ float grain = fract(sin(dot(st * 1000.0, vec2(12.9898, 78.233))) * 43758.5453);
75
+ color += (grain - 0.5) * u_grainAmount;
76
+
77
+ gl_FragColor = vec4(color, 1.0);
78
+ }
79
+ `;export{o as ShaderMount,l as grainCloudsFragmentShader};
80
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/shader-mount.ts", "../src/shaders/grain-clouds.ts"],
4
+ "sourcesContent": ["export class ShaderMount {\n private canvas: HTMLCanvasElement;\n private gl: WebGLRenderingContext;\n private program: WebGLProgram | null = null;\n private uniformLocations: Record<string, WebGLUniformLocation | null> = {};\n /** The fragment shader that we are using */\n private fragmentShader: string;\n /** Stores the RAF for the render loop */\n private rafId: number | null = null;\n /** Time of the last rendered frame */\n private lastFrameTime = 0;\n /** Total time that we have played any animation, passed as a uniform to the shader for time-based VFX */\n private totalAnimationTime = 0;\n /** Whether we RAF the render or not */\n private isAnimated: boolean;\n /** Uniforms that are provided by the user for the specific shader being mounted (not including uniforms that this Mount adds, like time and resolution) */\n private providedUniforms: Record<string, number | number[]>;\n /** Just a sanity check to make sure frames don't run after we're disposed */\n private hasBeenDisposed = false;\n /** If the resolution of the canvas has changed since the last render */\n private resolutionChanged = true;\n\n constructor(\n canvas: HTMLCanvasElement,\n fragmentShader: string,\n uniforms: Record<string, number | number[]> = {},\n webGlContextAttributes?: WebGLContextAttributes,\n animated = true\n ) {\n this.canvas = canvas;\n this.fragmentShader = fragmentShader;\n this.providedUniforms = uniforms;\n this.isAnimated = animated;\n\n const gl = canvas.getContext('webgl', webGlContextAttributes);\n if (!gl) {\n throw new Error('WebGL not supported');\n }\n this.gl = gl;\n\n this.initWebGL();\n this.setupResizeObserver();\n if (this.isAnimated) {\n this.startAnimating();\n } else {\n this.render(performance.now());\n }\n }\n\n private initWebGL = () => {\n const program = createProgram(\n this.gl,\n vertexShaderSource,\n this.fragmentShader\n );\n if (!program) return;\n this.program = program;\n\n this.setupPositionAttribute();\n this.setupUniforms();\n };\n\n private setupPositionAttribute = () => {\n const positionAttributeLocation = this.gl.getAttribLocation(\n this.program!,\n 'a_position'\n );\n const positionBuffer = this.gl.createBuffer();\n this.gl.bindBuffer(this.gl.ARRAY_BUFFER, positionBuffer);\n const positions = [-1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1];\n this.gl.bufferData(\n this.gl.ARRAY_BUFFER,\n new Float32Array(positions),\n this.gl.STATIC_DRAW\n );\n this.gl.enableVertexAttribArray(positionAttributeLocation);\n this.gl.vertexAttribPointer(\n positionAttributeLocation,\n 2,\n this.gl.FLOAT,\n false,\n 0,\n 0\n );\n };\n\n private setupUniforms = () => {\n this.uniformLocations = {\n u_time: this.gl.getUniformLocation(this.program!, 'u_time'),\n u_resolution: this.gl.getUniformLocation(this.program!, 'u_resolution'),\n ...Object.fromEntries(\n Object.keys(this.providedUniforms).map((key) => [\n key,\n this.gl.getUniformLocation(this.program!, key),\n ])\n ),\n };\n };\n\n private resizeObserver: ResizeObserver | null = null;\n private setupResizeObserver = () => {\n this.resizeObserver = new ResizeObserver(() => this.handleResize());\n this.resizeObserver.observe(this.canvas);\n this.handleResize();\n };\n\n private handleResize = () => {\n const newWidth = this.canvas.clientWidth;\n const newHeight = this.canvas.clientHeight;\n if (this.canvas.width !== newWidth || this.canvas.height !== newHeight) {\n this.canvas.width = newWidth;\n this.canvas.height = newHeight;\n this.resolutionChanged = true;\n this.gl.viewport(0, 0, this.gl.canvas.width, this.gl.canvas.height);\n this.render(performance.now());\n }\n };\n\n private render = (currentTime: number) => {\n if (this.hasBeenDisposed) return;\n\n const dt = currentTime - this.lastFrameTime;\n this.lastFrameTime = currentTime;\n\n if (this.isAnimated) {\n this.totalAnimationTime += dt;\n }\n\n this.gl.useProgram(this.program);\n\n // Update the time uniform\n this.gl.uniform1f(\n this.uniformLocations.u_time!,\n this.totalAnimationTime * 0.001\n );\n // If the resolution has changed, we need to update the uniform\n if (this.resolutionChanged) {\n this.gl.uniform2f(\n this.uniformLocations.u_resolution!,\n this.gl.canvas.width,\n this.gl.canvas.height\n );\n this.resolutionChanged = false;\n }\n\n this.gl.drawArrays(this.gl.TRIANGLES, 0, 6);\n\n // Loop if we're animating\n\n if (this.isAnimated) {\n this.requestRender();\n } else {\n this.rafId = null;\n }\n };\n\n private requestRender = () => {\n if (this.rafId !== null) {\n cancelAnimationFrame(this.rafId);\n }\n this.rafId = requestAnimationFrame(this.render);\n };\n\n private updateProvidedUniforms = () => {\n this.gl.useProgram(this.program);\n Object.entries(this.providedUniforms).forEach(([key, value]) => {\n const location = this.uniformLocations[key];\n if (location) {\n if (Array.isArray(value)) {\n switch (value.length) {\n case 2:\n this.gl.uniform2fv(location, value);\n break;\n case 3:\n this.gl.uniform3fv(location, value);\n break;\n case 4:\n this.gl.uniform4fv(location, value);\n break;\n default:\n if (value.length === 9) {\n this.gl.uniformMatrix3fv(location, false, value);\n } else if (value.length === 16) {\n this.gl.uniformMatrix4fv(location, false, value);\n } else {\n console.warn(\n `Unsupported uniform array length: ${value.length}`\n );\n }\n }\n } else if (typeof value === 'number') {\n this.gl.uniform1f(location, value);\n } else if (typeof value === 'boolean') {\n this.gl.uniform1i(location, value ? 1 : 0);\n } else {\n console.warn(`Unsupported uniform type for ${key}: ${typeof value}`);\n }\n }\n });\n };\n\n /** Start the animation loop, can be called during instantiation or later by an outside source */\n public startAnimating = () => {\n this.isAnimated = true;\n this.lastFrameTime = performance.now();\n this.rafId = requestAnimationFrame(this.render);\n };\n\n /** Stop the animation loop */\n public stopAnimating = () => {\n this.isAnimated = false;\n if (this.rafId) {\n cancelAnimationFrame(this.rafId);\n this.rafId = null;\n }\n };\n\n /** Update the uniforms that are provided by the outside shader */\n public setUniforms = (newUniforms: Record<string, number | number[]>) => {\n this.providedUniforms = { ...this.providedUniforms, ...newUniforms };\n\n // If we need to allow users to add uniforms after the shader has been created, we can do that here\n // But right now we're expecting the uniform list to be predictable and static\n // this.setupUniforms();\n\n this.updateProvidedUniforms();\n this.render(performance.now());\n };\n\n /** Dispose of the shader mount, cleaning up all of the WebGL resources */\n public dispose = () => {\n this.hasBeenDisposed = true;\n this.stopAnimating();\n if (this.gl && this.program) {\n this.gl.deleteProgram(this.program);\n this.program = null;\n\n // Reset the WebGL context\n this.gl.bindBuffer(this.gl.ARRAY_BUFFER, null);\n this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, null);\n this.gl.bindRenderbuffer(this.gl.RENDERBUFFER, null);\n this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, null);\n\n // Clear any errors\n this.gl.getError();\n }\n\n if (this.resizeObserver) {\n this.resizeObserver.disconnect();\n this.resizeObserver = null;\n }\n\n this.uniformLocations = {};\n };\n}\n\n/** Vertex shader for the shader mount */\nconst vertexShaderSource = `\n attribute vec4 a_position;\n void main() {\n gl_Position = a_position;\n }\n`;\n\nfunction createShader(\n gl: WebGLRenderingContext,\n type: number,\n source: string\n): WebGLShader | null {\n const shader = gl.createShader(type);\n if (!shader) return null;\n\n gl.shaderSource(shader, source);\n gl.compileShader(shader);\n\n if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {\n console.error(\n 'An error occurred compiling the shaders: ' + gl.getShaderInfoLog(shader)\n );\n gl.deleteShader(shader);\n return null;\n }\n\n return shader;\n}\n\nfunction createProgram(\n gl: WebGLRenderingContext,\n vertexShaderSource: string,\n fragmentShaderSource: string\n): WebGLProgram | null {\n const vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexShaderSource);\n const fragmentShader = createShader(\n gl,\n gl.FRAGMENT_SHADER,\n fragmentShaderSource\n );\n\n if (!vertexShader || !fragmentShader) return null;\n\n const program = gl.createProgram();\n if (!program) return null;\n\n gl.attachShader(program, vertexShader);\n gl.attachShader(program, fragmentShader);\n gl.linkProgram(program);\n\n if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {\n console.error(\n 'Unable to initialize the shader program: ' +\n gl.getProgramInfoLog(program)\n );\n gl.deleteProgram(program);\n gl.deleteShader(vertexShader);\n gl.deleteShader(fragmentShader);\n return null;\n }\n\n // Clean up shaders after successful linking\n gl.detachShader(program, vertexShader);\n gl.detachShader(program, fragmentShader);\n gl.deleteShader(vertexShader);\n gl.deleteShader(fragmentShader);\n\n return program;\n}\n\n/** Convert color string to RGB (0 to 1 range) */\nexport function getShaderColorFromString(colorString: string) {\n let r: number, g: number, b: number;\n if (colorString.startsWith('#')) {\n [r, g, b] = hexToRgb(colorString);\n } else if (colorString.startsWith('rgb')) {\n [r, g, b] = parseRgb(colorString);\n } else if (colorString.startsWith('hsl')) {\n [r, g, b] = hslToRgb(parseHsl(colorString));\n } else {\n console.error('Unsupported color format');\n return [0, 0, 0];\n }\n return [r, g, b];\n}\n\n/** Convert hex to RGB (0 to 1 range) */\nfunction hexToRgb(hex: string): [number, number, number] {\n const bigint = parseInt(hex.slice(1), 16);\n const r = ((bigint >> 16) & 255) / 255;\n const g = ((bigint >> 8) & 255) / 255;\n const b = (bigint & 255) / 255;\n return [r, g, b];\n}\n\n/** Parse RGB string to RGB (0 to 1 range) */\nfunction parseRgb(rgb: string): [number, number, number] {\n const match = rgb.match(/^rgb\\s*\\(\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*\\)$/i);\n if (!match) return [0, 0, 0];\n return [\n parseInt(match[1] ?? '0') / 255,\n parseInt(match[2] ?? '0') / 255,\n parseInt(match[3] ?? '0') / 255,\n ];\n}\n\n/** Parse HSL string */\nfunction parseHsl(hsl: string): [number, number, number] {\n const match = hsl.match(/^hsl\\s*\\(\\s*(\\d+)\\s*,\\s*(\\d+)%\\s*,\\s*(\\d+)%\\s*\\)$/i);\n if (!match) return [0, 0, 0];\n return [\n parseInt(match[1] ?? '0'),\n parseInt(match[2] ?? '0'),\n parseInt(match[3] ?? '0'),\n ];\n}\n\n/** Convert HSL to RGB (0 to 1 range) */\nfunction hslToRgb(hsl: [number, number, number]): [number, number, number] {\n const [h, s, l] = hsl;\n const hDecimal = h / 360;\n const sDecimal = s / 100;\n const lDecimal = l / 100;\n let r, g, b;\n\n if (s === 0) {\n r = g = b = lDecimal; // achromatic\n } else {\n const hue2rgb = (p: number, q: number, t: number) => {\n if (t < 0) t += 1;\n if (t > 1) t -= 1;\n if (t < 1 / 6) return p + (q - p) * 6 * t;\n if (t < 1 / 2) return q;\n if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;\n return p;\n };\n\n const q =\n lDecimal < 0.5\n ? lDecimal * (1 + sDecimal)\n : lDecimal + sDecimal - lDecimal * sDecimal;\n const p = 2 * lDecimal - q;\n r = hue2rgb(p, q, hDecimal + 1 / 3);\n g = hue2rgb(p, q, hDecimal);\n b = hue2rgb(p, q, hDecimal - 1 / 3);\n }\n\n return [r, g, b];\n}\n", "/**\n * Renders a grainy texture over top of blobby animated clouds\n * This is an example shader that we're using to bootstrap the project\n * Generated by Claude 3.5 Sonnet\n *\n * Parameters include:\n * color1: The first color of the clouds\n * color2: The second color of the clouds\n * noiseScale: The scale of the noise\n * noiseSpeed: The speed of the noise\n * grainAmount: The amount of grain on the texture\n */\n\nexport const grainCloudsFragmentShader = `\n precision highp float;\n uniform vec2 u_resolution;\n uniform float u_time;\n uniform vec3 u_color1;\n uniform vec3 u_color2;\n uniform float u_noiseScale;\n uniform float u_noiseSpeed;\n uniform float u_grainAmount;\n\n // Simplex 2D noise\n vec3 permute(vec3 x) { return mod(((x*34.0)+1.0)*x, 289.0); }\n\n float snoise(vec2 v) {\n const vec4 C = vec4(0.211324865405187, 0.366025403784439,\n -0.577350269189626, 0.024390243902439);\n vec2 i = floor(v + dot(v, C.yy));\n vec2 x0 = v - i + dot(i, C.xx);\n vec2 i1;\n i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);\n vec4 x12 = x0.xyxy + C.xxzz;\n x12.xy -= i1;\n i = mod(i, 289.0);\n vec3 p = permute( permute( i.y + vec3(0.0, i1.y, 1.0))\n + i.x + vec3(0.0, i1.x, 1.0));\n vec3 m = max(0.5 - vec3(dot(x0,x0), dot(x12.xy,x12.xy),\n dot(x12.zw,x12.zw)), 0.0);\n m = m*m ;\n m = m*m ;\n vec3 x = 2.0 * fract(p * C.www) - 1.0;\n vec3 h = abs(x) - 0.5;\n vec3 ox = floor(x + 0.5);\n vec3 a0 = x - ox;\n m *= 1.79284291400159 - 0.85373472095314 * (a0*a0 + h*h);\n vec3 g;\n g.x = a0.x * x0.x + h.x * x0.y;\n g.yz = a0.yz * x12.xz + h.yz * x12.yw;\n return 130.0 * dot(m, g);\n }\n\n void main() {\n vec2 st = gl_FragCoord.xy / u_resolution.xy;\n\n // Calculate the aspect ratio of the shader\n float shaderAspect = u_resolution.x / u_resolution.y;\n\n // Define the aspect ratio of your content (e.g., 1.0 for square)\n float contentAspect = 1.0;\n\n // Adjust st to maintain content aspect ratio\n if (shaderAspect > contentAspect) {\n float scale = shaderAspect / contentAspect;\n st.x = (st.x - 0.5) * scale + 0.5;\n } else {\n float scale = contentAspect / shaderAspect;\n st.y = (st.y - 0.5) * scale + 0.5;\n }\n\n // Create blobby texture\n float n = snoise(st * u_noiseScale + u_time * u_noiseSpeed);\n n += 0.5 * snoise(st * u_noiseScale * 2.0 - u_time * u_noiseSpeed * 0.5);\n n += 0.25 * snoise(st * u_noiseScale * 4.0 + u_time * u_noiseSpeed * 0.25);\n n = n * 0.5 + 0.5;\n\n // Color interpolation\n vec3 color = mix(u_color1, u_color2, n);\n\n // Add grain\n float grain = fract(sin(dot(st * 1000.0, vec2(12.9898, 78.233))) * 43758.5453);\n color += (grain - 0.5) * u_grainAmount;\n\n gl_FragColor = vec4(color, 1.0);\n }\n`;\n"],
5
+ "mappings": "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",
6
+ "names": ["ShaderMount", "canvas", "fragmentShader", "uniforms", "webGlContextAttributes", "animated", "gl", "program", "createProgram", "vertexShaderSource", "positionAttributeLocation", "positionBuffer", "positions", "key", "newWidth", "newHeight", "currentTime", "dt", "value", "location", "newUniforms", "createShader", "type", "source", "shader", "fragmentShaderSource", "vertexShader", "grainCloudsFragmentShader"]
7
+ }
@@ -0,0 +1,42 @@
1
+ export declare class ShaderMount {
2
+ private canvas;
3
+ private gl;
4
+ private program;
5
+ private uniformLocations;
6
+ /** The fragment shader that we are using */
7
+ private fragmentShader;
8
+ /** Stores the RAF for the render loop */
9
+ private rafId;
10
+ /** Time of the last rendered frame */
11
+ private lastFrameTime;
12
+ /** Total time that we have played any animation, passed as a uniform to the shader for time-based VFX */
13
+ private totalAnimationTime;
14
+ /** Whether we RAF the render or not */
15
+ private isAnimated;
16
+ /** Uniforms that are provided by the user for the specific shader being mounted (not including uniforms that this Mount adds, like time and resolution) */
17
+ private providedUniforms;
18
+ /** Just a sanity check to make sure frames don't run after we're disposed */
19
+ private hasBeenDisposed;
20
+ /** If the resolution of the canvas has changed since the last render */
21
+ private resolutionChanged;
22
+ constructor(canvas: HTMLCanvasElement, fragmentShader: string, uniforms?: Record<string, number | number[]>, webGlContextAttributes?: WebGLContextAttributes, animated?: boolean);
23
+ private initWebGL;
24
+ private setupPositionAttribute;
25
+ private setupUniforms;
26
+ private resizeObserver;
27
+ private setupResizeObserver;
28
+ private handleResize;
29
+ private render;
30
+ private requestRender;
31
+ private updateProvidedUniforms;
32
+ /** Start the animation loop, can be called during instantiation or later by an outside source */
33
+ startAnimating: () => void;
34
+ /** Stop the animation loop */
35
+ stopAnimating: () => void;
36
+ /** Update the uniforms that are provided by the outside shader */
37
+ setUniforms: (newUniforms: Record<string, number | number[]>) => void;
38
+ /** Dispose of the shader mount, cleaning up all of the WebGL resources */
39
+ dispose: () => void;
40
+ }
41
+ /** Convert color string to RGB (0 to 1 range) */
42
+ export declare function getShaderColorFromString(colorString: string): number[];
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Renders a grainy texture over top of blobby animated clouds
3
+ * This is an example shader that we're using to bootstrap the project
4
+ * Generated by Claude 3.5 Sonnet
5
+ *
6
+ * Parameters include:
7
+ * color1: The first color of the clouds
8
+ * color2: The second color of the clouds
9
+ * noiseScale: The scale of the noise
10
+ * noiseSpeed: The speed of the noise
11
+ * grainAmount: The amount of grain on the texture
12
+ */
13
+ export declare const grainCloudsFragmentShader = "\n precision highp float;\n uniform vec2 u_resolution;\n uniform float u_time;\n uniform vec3 u_color1;\n uniform vec3 u_color2;\n uniform float u_noiseScale;\n uniform float u_noiseSpeed;\n uniform float u_grainAmount;\n\n // Simplex 2D noise\n vec3 permute(vec3 x) { return mod(((x*34.0)+1.0)*x, 289.0); }\n\n float snoise(vec2 v) {\n const vec4 C = vec4(0.211324865405187, 0.366025403784439,\n -0.577350269189626, 0.024390243902439);\n vec2 i = floor(v + dot(v, C.yy));\n vec2 x0 = v - i + dot(i, C.xx);\n vec2 i1;\n i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);\n vec4 x12 = x0.xyxy + C.xxzz;\n x12.xy -= i1;\n i = mod(i, 289.0);\n vec3 p = permute( permute( i.y + vec3(0.0, i1.y, 1.0))\n + i.x + vec3(0.0, i1.x, 1.0));\n vec3 m = max(0.5 - vec3(dot(x0,x0), dot(x12.xy,x12.xy),\n dot(x12.zw,x12.zw)), 0.0);\n m = m*m ;\n m = m*m ;\n vec3 x = 2.0 * fract(p * C.www) - 1.0;\n vec3 h = abs(x) - 0.5;\n vec3 ox = floor(x + 0.5);\n vec3 a0 = x - ox;\n m *= 1.79284291400159 - 0.85373472095314 * (a0*a0 + h*h);\n vec3 g;\n g.x = a0.x * x0.x + h.x * x0.y;\n g.yz = a0.yz * x12.xz + h.yz * x12.yw;\n return 130.0 * dot(m, g);\n }\n\n void main() {\n vec2 st = gl_FragCoord.xy / u_resolution.xy;\n\n // Calculate the aspect ratio of the shader\n float shaderAspect = u_resolution.x / u_resolution.y;\n\n // Define the aspect ratio of your content (e.g., 1.0 for square)\n float contentAspect = 1.0;\n\n // Adjust st to maintain content aspect ratio\n if (shaderAspect > contentAspect) {\n float scale = shaderAspect / contentAspect;\n st.x = (st.x - 0.5) * scale + 0.5;\n } else {\n float scale = contentAspect / shaderAspect;\n st.y = (st.y - 0.5) * scale + 0.5;\n }\n\n // Create blobby texture\n float n = snoise(st * u_noiseScale + u_time * u_noiseSpeed);\n n += 0.5 * snoise(st * u_noiseScale * 2.0 - u_time * u_noiseSpeed * 0.5);\n n += 0.25 * snoise(st * u_noiseScale * 4.0 + u_time * u_noiseSpeed * 0.25);\n n = n * 0.5 + 0.5;\n\n // Color interpolation\n vec3 color = mix(u_color1, u_color2, n);\n\n // Add grain\n float grain = fract(sin(dot(st * 1000.0, vec2(12.9898, 78.233))) * 43758.5453);\n color += (grain - 0.5) * u_grainAmount;\n\n gl_FragColor = vec4(color, 1.0);\n }\n";
package/package.json CHANGED
@@ -1,16 +1,19 @@
1
1
  {
2
2
  "name": "@paper-design/shaders",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "exports": {
7
- ".": "./dist/index.ts"
7
+ ".": {
8
+ "types": "./dist/index.d.ts",
9
+ "default": "./dist/index.js"
10
+ }
8
11
  },
12
+ "files": [
13
+ "dist"
14
+ ],
9
15
  "scripts": {
10
- "build": "bun run type-check && esbuild src/index.ts --bundle --outfile=dist/index.ts --format=esm --platform=browser",
11
- "type-check": "tsc --project tsconfig.json",
12
- "publish-test": "bun run build && bun publish --dry-run",
13
- "publish": "bun run build && bun publish --access public"
16
+ "type-check": "tsc --project tsconfig.json"
14
17
  },
15
18
  "dependencies": {},
16
19
  "devDependencies": {
package/dist/index.ts DELETED
@@ -1,341 +0,0 @@
1
- // src/shader-mount.ts
2
- var ShaderMount = class {
3
- canvas;
4
- gl;
5
- program = null;
6
- uniformLocations = {};
7
- /** The fragment shader that we are using */
8
- fragmentShader;
9
- /** Stores the RAF for the render loop */
10
- rafId = null;
11
- /** Time of the last rendered frame */
12
- lastFrameTime = 0;
13
- /** Total time that we have played any animation, passed as a uniform to the shader for time-based VFX */
14
- totalAnimationTime = 0;
15
- /** Whether we RAF the render or not */
16
- isAnimated;
17
- /** Uniforms that are provided by the user for the specific shader being mounted (not including uniforms that this Mount adds, like time and resolution) */
18
- providedUniforms;
19
- /** Just a sanity check to make sure frames don't run after we're disposed */
20
- hasBeenDisposed = false;
21
- /** If the resolution of the canvas has changed since the last render */
22
- resolutionChanged = true;
23
- constructor(canvas, fragmentShader, uniforms = {}, webGlContextAttributes, animated = true) {
24
- this.canvas = canvas;
25
- this.fragmentShader = fragmentShader;
26
- this.providedUniforms = uniforms;
27
- this.isAnimated = animated;
28
- const gl = canvas.getContext("webgl", webGlContextAttributes);
29
- if (!gl) {
30
- throw new Error("WebGL not supported");
31
- }
32
- this.gl = gl;
33
- this.initWebGL();
34
- this.setupResizeObserver();
35
- if (this.isAnimated) {
36
- this.startAnimating();
37
- } else {
38
- this.render(performance.now());
39
- }
40
- }
41
- initWebGL = () => {
42
- const program = createProgram(
43
- this.gl,
44
- vertexShaderSource,
45
- this.fragmentShader
46
- );
47
- if (!program) return;
48
- this.program = program;
49
- this.setupPositionAttribute();
50
- this.setupUniforms();
51
- };
52
- setupPositionAttribute = () => {
53
- const positionAttributeLocation = this.gl.getAttribLocation(
54
- this.program,
55
- "a_position"
56
- );
57
- const positionBuffer = this.gl.createBuffer();
58
- this.gl.bindBuffer(this.gl.ARRAY_BUFFER, positionBuffer);
59
- const positions = [-1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1];
60
- this.gl.bufferData(
61
- this.gl.ARRAY_BUFFER,
62
- new Float32Array(positions),
63
- this.gl.STATIC_DRAW
64
- );
65
- this.gl.enableVertexAttribArray(positionAttributeLocation);
66
- this.gl.vertexAttribPointer(
67
- positionAttributeLocation,
68
- 2,
69
- this.gl.FLOAT,
70
- false,
71
- 0,
72
- 0
73
- );
74
- };
75
- setupUniforms = () => {
76
- this.uniformLocations = {
77
- u_time: this.gl.getUniformLocation(this.program, "u_time"),
78
- u_resolution: this.gl.getUniformLocation(this.program, "u_resolution"),
79
- ...Object.fromEntries(
80
- Object.keys(this.providedUniforms).map((key) => [
81
- key,
82
- this.gl.getUniformLocation(this.program, key)
83
- ])
84
- )
85
- };
86
- };
87
- resizeObserver = null;
88
- setupResizeObserver = () => {
89
- this.resizeObserver = new ResizeObserver(() => this.handleResize());
90
- this.resizeObserver.observe(this.canvas);
91
- this.handleResize();
92
- };
93
- handleResize = () => {
94
- const newWidth = this.canvas.clientWidth;
95
- const newHeight = this.canvas.clientHeight;
96
- if (this.canvas.width !== newWidth || this.canvas.height !== newHeight) {
97
- this.canvas.width = newWidth;
98
- this.canvas.height = newHeight;
99
- this.resolutionChanged = true;
100
- this.gl.viewport(0, 0, this.gl.canvas.width, this.gl.canvas.height);
101
- this.render(performance.now());
102
- }
103
- };
104
- render = (currentTime) => {
105
- if (this.hasBeenDisposed) return;
106
- const dt = currentTime - this.lastFrameTime;
107
- this.lastFrameTime = currentTime;
108
- if (this.isAnimated) {
109
- this.totalAnimationTime += dt;
110
- }
111
- this.gl.useProgram(this.program);
112
- this.gl.uniform1f(
113
- this.uniformLocations.u_time,
114
- this.totalAnimationTime * 1e-3
115
- );
116
- if (this.resolutionChanged) {
117
- this.gl.uniform2f(
118
- this.uniformLocations.u_resolution,
119
- this.gl.canvas.width,
120
- this.gl.canvas.height
121
- );
122
- this.resolutionChanged = false;
123
- }
124
- this.gl.drawArrays(this.gl.TRIANGLES, 0, 6);
125
- if (this.isAnimated) {
126
- this.requestRender();
127
- } else {
128
- this.rafId = null;
129
- }
130
- };
131
- requestRender = () => {
132
- if (this.rafId !== null) {
133
- cancelAnimationFrame(this.rafId);
134
- }
135
- this.rafId = requestAnimationFrame(this.render);
136
- };
137
- updateProvidedUniforms = () => {
138
- this.gl.useProgram(this.program);
139
- Object.entries(this.providedUniforms).forEach(([key, value]) => {
140
- const location = this.uniformLocations[key];
141
- if (location) {
142
- if (Array.isArray(value)) {
143
- switch (value.length) {
144
- case 2:
145
- this.gl.uniform2fv(location, value);
146
- break;
147
- case 3:
148
- this.gl.uniform3fv(location, value);
149
- break;
150
- case 4:
151
- this.gl.uniform4fv(location, value);
152
- break;
153
- default:
154
- if (value.length === 9) {
155
- this.gl.uniformMatrix3fv(location, false, value);
156
- } else if (value.length === 16) {
157
- this.gl.uniformMatrix4fv(location, false, value);
158
- } else {
159
- console.warn(
160
- `Unsupported uniform array length: ${value.length}`
161
- );
162
- }
163
- }
164
- } else if (typeof value === "number") {
165
- this.gl.uniform1f(location, value);
166
- } else if (typeof value === "boolean") {
167
- this.gl.uniform1i(location, value ? 1 : 0);
168
- } else {
169
- console.warn(`Unsupported uniform type for ${key}: ${typeof value}`);
170
- }
171
- }
172
- });
173
- };
174
- /** Start the animation loop, can be called during instantiation or later by an outside source */
175
- startAnimating = () => {
176
- this.isAnimated = true;
177
- this.lastFrameTime = performance.now();
178
- this.rafId = requestAnimationFrame(this.render);
179
- };
180
- /** Stop the animation loop */
181
- stopAnimating = () => {
182
- this.isAnimated = false;
183
- if (this.rafId) {
184
- cancelAnimationFrame(this.rafId);
185
- this.rafId = null;
186
- }
187
- };
188
- /** Update the uniforms that are provided by the outside shader */
189
- setUniforms = (newUniforms) => {
190
- this.providedUniforms = { ...this.providedUniforms, ...newUniforms };
191
- this.updateProvidedUniforms();
192
- this.render(performance.now());
193
- };
194
- /** Dispose of the shader mount, cleaning up all of the WebGL resources */
195
- dispose = () => {
196
- this.hasBeenDisposed = true;
197
- this.stopAnimating();
198
- if (this.gl && this.program) {
199
- this.gl.deleteProgram(this.program);
200
- this.program = null;
201
- this.gl.bindBuffer(this.gl.ARRAY_BUFFER, null);
202
- this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, null);
203
- this.gl.bindRenderbuffer(this.gl.RENDERBUFFER, null);
204
- this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, null);
205
- this.gl.getError();
206
- }
207
- if (this.resizeObserver) {
208
- this.resizeObserver.disconnect();
209
- this.resizeObserver = null;
210
- }
211
- this.uniformLocations = {};
212
- };
213
- };
214
- var vertexShaderSource = `
215
- attribute vec4 a_position;
216
- void main() {
217
- gl_Position = a_position;
218
- }
219
- `;
220
- function createShader(gl, type, source) {
221
- const shader = gl.createShader(type);
222
- if (!shader) return null;
223
- gl.shaderSource(shader, source);
224
- gl.compileShader(shader);
225
- if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
226
- console.error(
227
- "An error occurred compiling the shaders: " + gl.getShaderInfoLog(shader)
228
- );
229
- gl.deleteShader(shader);
230
- return null;
231
- }
232
- return shader;
233
- }
234
- function createProgram(gl, vertexShaderSource2, fragmentShaderSource) {
235
- const vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexShaderSource2);
236
- const fragmentShader = createShader(
237
- gl,
238
- gl.FRAGMENT_SHADER,
239
- fragmentShaderSource
240
- );
241
- if (!vertexShader || !fragmentShader) return null;
242
- const program = gl.createProgram();
243
- if (!program) return null;
244
- gl.attachShader(program, vertexShader);
245
- gl.attachShader(program, fragmentShader);
246
- gl.linkProgram(program);
247
- if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
248
- console.error(
249
- "Unable to initialize the shader program: " + gl.getProgramInfoLog(program)
250
- );
251
- gl.deleteProgram(program);
252
- gl.deleteShader(vertexShader);
253
- gl.deleteShader(fragmentShader);
254
- return null;
255
- }
256
- gl.detachShader(program, vertexShader);
257
- gl.detachShader(program, fragmentShader);
258
- gl.deleteShader(vertexShader);
259
- gl.deleteShader(fragmentShader);
260
- return program;
261
- }
262
-
263
- // src/shaders/grain-clouds.ts
264
- var grainCloudsFragmentShader = `
265
- precision highp float;
266
- uniform vec2 u_resolution;
267
- uniform float u_time;
268
- uniform vec3 u_color1;
269
- uniform vec3 u_color2;
270
- uniform float u_noiseScale;
271
- uniform float u_noiseSpeed;
272
- uniform float u_grainAmount;
273
-
274
- // Simplex 2D noise
275
- vec3 permute(vec3 x) { return mod(((x*34.0)+1.0)*x, 289.0); }
276
-
277
- float snoise(vec2 v) {
278
- const vec4 C = vec4(0.211324865405187, 0.366025403784439,
279
- -0.577350269189626, 0.024390243902439);
280
- vec2 i = floor(v + dot(v, C.yy));
281
- vec2 x0 = v - i + dot(i, C.xx);
282
- vec2 i1;
283
- i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
284
- vec4 x12 = x0.xyxy + C.xxzz;
285
- x12.xy -= i1;
286
- i = mod(i, 289.0);
287
- vec3 p = permute( permute( i.y + vec3(0.0, i1.y, 1.0))
288
- + i.x + vec3(0.0, i1.x, 1.0));
289
- vec3 m = max(0.5 - vec3(dot(x0,x0), dot(x12.xy,x12.xy),
290
- dot(x12.zw,x12.zw)), 0.0);
291
- m = m*m ;
292
- m = m*m ;
293
- vec3 x = 2.0 * fract(p * C.www) - 1.0;
294
- vec3 h = abs(x) - 0.5;
295
- vec3 ox = floor(x + 0.5);
296
- vec3 a0 = x - ox;
297
- m *= 1.79284291400159 - 0.85373472095314 * (a0*a0 + h*h);
298
- vec3 g;
299
- g.x = a0.x * x0.x + h.x * x0.y;
300
- g.yz = a0.yz * x12.xz + h.yz * x12.yw;
301
- return 130.0 * dot(m, g);
302
- }
303
-
304
- void main() {
305
- vec2 st = gl_FragCoord.xy / u_resolution.xy;
306
-
307
- // Calculate the aspect ratio of the shader
308
- float shaderAspect = u_resolution.x / u_resolution.y;
309
-
310
- // Define the aspect ratio of your content (e.g., 1.0 for square)
311
- float contentAspect = 1.0;
312
-
313
- // Adjust st to maintain content aspect ratio
314
- if (shaderAspect > contentAspect) {
315
- float scale = shaderAspect / contentAspect;
316
- st.x = (st.x - 0.5) * scale + 0.5;
317
- } else {
318
- float scale = contentAspect / shaderAspect;
319
- st.y = (st.y - 0.5) * scale + 0.5;
320
- }
321
-
322
- // Create blobby texture
323
- float n = snoise(st * u_noiseScale + u_time * u_noiseSpeed);
324
- n += 0.5 * snoise(st * u_noiseScale * 2.0 - u_time * u_noiseSpeed * 0.5);
325
- n += 0.25 * snoise(st * u_noiseScale * 4.0 + u_time * u_noiseSpeed * 0.25);
326
- n = n * 0.5 + 0.5;
327
-
328
- // Color interpolation
329
- vec3 color = mix(u_color1, u_color2, n);
330
-
331
- // Add grain
332
- float grain = fract(sin(dot(st * 1000.0, vec2(12.9898, 78.233))) * 43758.5453);
333
- color += (grain - 0.5) * u_grainAmount;
334
-
335
- gl_FragColor = vec4(color, 1.0);
336
- }
337
- `;
338
- export {
339
- ShaderMount,
340
- grainCloudsFragmentShader
341
- };
@@ -1,406 +0,0 @@
1
- export class ShaderMount {
2
- private canvas: HTMLCanvasElement;
3
- private gl: WebGLRenderingContext;
4
- private program: WebGLProgram | null = null;
5
- private uniformLocations: Record<string, WebGLUniformLocation | null> = {};
6
- /** The fragment shader that we are using */
7
- private fragmentShader: string;
8
- /** Stores the RAF for the render loop */
9
- private rafId: number | null = null;
10
- /** Time of the last rendered frame */
11
- private lastFrameTime = 0;
12
- /** Total time that we have played any animation, passed as a uniform to the shader for time-based VFX */
13
- private totalAnimationTime = 0;
14
- /** Whether we RAF the render or not */
15
- private isAnimated: boolean;
16
- /** Uniforms that are provided by the user for the specific shader being mounted (not including uniforms that this Mount adds, like time and resolution) */
17
- private providedUniforms: Record<string, number | number[]>;
18
- /** Just a sanity check to make sure frames don't run after we're disposed */
19
- private hasBeenDisposed = false;
20
- /** If the resolution of the canvas has changed since the last render */
21
- private resolutionChanged = true;
22
-
23
- constructor(
24
- canvas: HTMLCanvasElement,
25
- fragmentShader: string,
26
- uniforms: Record<string, number | number[]> = {},
27
- webGlContextAttributes?: WebGLContextAttributes,
28
- animated = true
29
- ) {
30
- this.canvas = canvas;
31
- this.fragmentShader = fragmentShader;
32
- this.providedUniforms = uniforms;
33
- this.isAnimated = animated;
34
-
35
- const gl = canvas.getContext('webgl', webGlContextAttributes);
36
- if (!gl) {
37
- throw new Error('WebGL not supported');
38
- }
39
- this.gl = gl;
40
-
41
- this.initWebGL();
42
- this.setupResizeObserver();
43
- if (this.isAnimated) {
44
- this.startAnimating();
45
- } else {
46
- this.render(performance.now());
47
- }
48
- }
49
-
50
- private initWebGL = () => {
51
- const program = createProgram(
52
- this.gl,
53
- vertexShaderSource,
54
- this.fragmentShader
55
- );
56
- if (!program) return;
57
- this.program = program;
58
-
59
- this.setupPositionAttribute();
60
- this.setupUniforms();
61
- };
62
-
63
- private setupPositionAttribute = () => {
64
- const positionAttributeLocation = this.gl.getAttribLocation(
65
- this.program!,
66
- 'a_position'
67
- );
68
- const positionBuffer = this.gl.createBuffer();
69
- this.gl.bindBuffer(this.gl.ARRAY_BUFFER, positionBuffer);
70
- const positions = [-1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1];
71
- this.gl.bufferData(
72
- this.gl.ARRAY_BUFFER,
73
- new Float32Array(positions),
74
- this.gl.STATIC_DRAW
75
- );
76
- this.gl.enableVertexAttribArray(positionAttributeLocation);
77
- this.gl.vertexAttribPointer(
78
- positionAttributeLocation,
79
- 2,
80
- this.gl.FLOAT,
81
- false,
82
- 0,
83
- 0
84
- );
85
- };
86
-
87
- private setupUniforms = () => {
88
- this.uniformLocations = {
89
- u_time: this.gl.getUniformLocation(this.program!, 'u_time'),
90
- u_resolution: this.gl.getUniformLocation(this.program!, 'u_resolution'),
91
- ...Object.fromEntries(
92
- Object.keys(this.providedUniforms).map((key) => [
93
- key,
94
- this.gl.getUniformLocation(this.program!, key),
95
- ])
96
- ),
97
- };
98
- };
99
-
100
- private resizeObserver: ResizeObserver | null = null;
101
- private setupResizeObserver = () => {
102
- this.resizeObserver = new ResizeObserver(() => this.handleResize());
103
- this.resizeObserver.observe(this.canvas);
104
- this.handleResize();
105
- };
106
-
107
- private handleResize = () => {
108
- const newWidth = this.canvas.clientWidth;
109
- const newHeight = this.canvas.clientHeight;
110
- if (this.canvas.width !== newWidth || this.canvas.height !== newHeight) {
111
- this.canvas.width = newWidth;
112
- this.canvas.height = newHeight;
113
- this.resolutionChanged = true;
114
- this.gl.viewport(0, 0, this.gl.canvas.width, this.gl.canvas.height);
115
- this.render(performance.now());
116
- }
117
- };
118
-
119
- private render = (currentTime: number) => {
120
- if (this.hasBeenDisposed) return;
121
-
122
- const dt = currentTime - this.lastFrameTime;
123
- this.lastFrameTime = currentTime;
124
-
125
- if (this.isAnimated) {
126
- this.totalAnimationTime += dt;
127
- }
128
-
129
- this.gl.useProgram(this.program);
130
-
131
- // Update the time uniform
132
- this.gl.uniform1f(
133
- this.uniformLocations.u_time!,
134
- this.totalAnimationTime * 0.001
135
- );
136
- // If the resolution has changed, we need to update the uniform
137
- if (this.resolutionChanged) {
138
- this.gl.uniform2f(
139
- this.uniformLocations.u_resolution!,
140
- this.gl.canvas.width,
141
- this.gl.canvas.height
142
- );
143
- this.resolutionChanged = false;
144
- }
145
-
146
- this.gl.drawArrays(this.gl.TRIANGLES, 0, 6);
147
-
148
- // Loop if we're animating
149
-
150
- if (this.isAnimated) {
151
- this.requestRender();
152
- } else {
153
- this.rafId = null;
154
- }
155
- };
156
-
157
- private requestRender = () => {
158
- if (this.rafId !== null) {
159
- cancelAnimationFrame(this.rafId);
160
- }
161
- this.rafId = requestAnimationFrame(this.render);
162
- };
163
-
164
- private updateProvidedUniforms = () => {
165
- this.gl.useProgram(this.program);
166
- Object.entries(this.providedUniforms).forEach(([key, value]) => {
167
- const location = this.uniformLocations[key];
168
- if (location) {
169
- if (Array.isArray(value)) {
170
- switch (value.length) {
171
- case 2:
172
- this.gl.uniform2fv(location, value);
173
- break;
174
- case 3:
175
- this.gl.uniform3fv(location, value);
176
- break;
177
- case 4:
178
- this.gl.uniform4fv(location, value);
179
- break;
180
- default:
181
- if (value.length === 9) {
182
- this.gl.uniformMatrix3fv(location, false, value);
183
- } else if (value.length === 16) {
184
- this.gl.uniformMatrix4fv(location, false, value);
185
- } else {
186
- console.warn(
187
- `Unsupported uniform array length: ${value.length}`
188
- );
189
- }
190
- }
191
- } else if (typeof value === 'number') {
192
- this.gl.uniform1f(location, value);
193
- } else if (typeof value === 'boolean') {
194
- this.gl.uniform1i(location, value ? 1 : 0);
195
- } else {
196
- console.warn(`Unsupported uniform type for ${key}: ${typeof value}`);
197
- }
198
- }
199
- });
200
- };
201
-
202
- /** Start the animation loop, can be called during instantiation or later by an outside source */
203
- public startAnimating = () => {
204
- this.isAnimated = true;
205
- this.lastFrameTime = performance.now();
206
- this.rafId = requestAnimationFrame(this.render);
207
- };
208
-
209
- /** Stop the animation loop */
210
- public stopAnimating = () => {
211
- this.isAnimated = false;
212
- if (this.rafId) {
213
- cancelAnimationFrame(this.rafId);
214
- this.rafId = null;
215
- }
216
- };
217
-
218
- /** Update the uniforms that are provided by the outside shader */
219
- public setUniforms = (newUniforms: Record<string, number | number[]>) => {
220
- this.providedUniforms = { ...this.providedUniforms, ...newUniforms };
221
-
222
- // If we need to allow users to add uniforms after the shader has been created, we can do that here
223
- // But right now we're expecting the uniform list to be predictable and static
224
- // this.setupUniforms();
225
-
226
- this.updateProvidedUniforms();
227
- this.render(performance.now());
228
- };
229
-
230
- /** Dispose of the shader mount, cleaning up all of the WebGL resources */
231
- public dispose = () => {
232
- this.hasBeenDisposed = true;
233
- this.stopAnimating();
234
- if (this.gl && this.program) {
235
- this.gl.deleteProgram(this.program);
236
- this.program = null;
237
-
238
- // Reset the WebGL context
239
- this.gl.bindBuffer(this.gl.ARRAY_BUFFER, null);
240
- this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, null);
241
- this.gl.bindRenderbuffer(this.gl.RENDERBUFFER, null);
242
- this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, null);
243
-
244
- // Clear any errors
245
- this.gl.getError();
246
- }
247
-
248
- if (this.resizeObserver) {
249
- this.resizeObserver.disconnect();
250
- this.resizeObserver = null;
251
- }
252
-
253
- this.uniformLocations = {};
254
- };
255
- }
256
-
257
- /** Vertex shader for the shader mount */
258
- const vertexShaderSource = `
259
- attribute vec4 a_position;
260
- void main() {
261
- gl_Position = a_position;
262
- }
263
- `;
264
-
265
- function createShader(
266
- gl: WebGLRenderingContext,
267
- type: number,
268
- source: string
269
- ): WebGLShader | null {
270
- const shader = gl.createShader(type);
271
- if (!shader) return null;
272
-
273
- gl.shaderSource(shader, source);
274
- gl.compileShader(shader);
275
-
276
- if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
277
- console.error(
278
- 'An error occurred compiling the shaders: ' + gl.getShaderInfoLog(shader)
279
- );
280
- gl.deleteShader(shader);
281
- return null;
282
- }
283
-
284
- return shader;
285
- }
286
-
287
- function createProgram(
288
- gl: WebGLRenderingContext,
289
- vertexShaderSource: string,
290
- fragmentShaderSource: string
291
- ): WebGLProgram | null {
292
- const vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexShaderSource);
293
- const fragmentShader = createShader(
294
- gl,
295
- gl.FRAGMENT_SHADER,
296
- fragmentShaderSource
297
- );
298
-
299
- if (!vertexShader || !fragmentShader) return null;
300
-
301
- const program = gl.createProgram();
302
- if (!program) return null;
303
-
304
- gl.attachShader(program, vertexShader);
305
- gl.attachShader(program, fragmentShader);
306
- gl.linkProgram(program);
307
-
308
- if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
309
- console.error(
310
- 'Unable to initialize the shader program: ' +
311
- gl.getProgramInfoLog(program)
312
- );
313
- gl.deleteProgram(program);
314
- gl.deleteShader(vertexShader);
315
- gl.deleteShader(fragmentShader);
316
- return null;
317
- }
318
-
319
- // Clean up shaders after successful linking
320
- gl.detachShader(program, vertexShader);
321
- gl.detachShader(program, fragmentShader);
322
- gl.deleteShader(vertexShader);
323
- gl.deleteShader(fragmentShader);
324
-
325
- return program;
326
- }
327
-
328
- /** Convert color string to RGB (0 to 1 range) */
329
- export function getShaderColorFromString(colorString: string) {
330
- let r: number, g: number, b: number;
331
- if (colorString.startsWith('#')) {
332
- [r, g, b] = hexToRgb(colorString);
333
- } else if (colorString.startsWith('rgb')) {
334
- [r, g, b] = parseRgb(colorString);
335
- } else if (colorString.startsWith('hsl')) {
336
- [r, g, b] = hslToRgb(parseHsl(colorString));
337
- } else {
338
- console.error('Unsupported color format');
339
- return [0, 0, 0];
340
- }
341
- return [r, g, b];
342
- }
343
-
344
- /** Convert hex to RGB (0 to 1 range) */
345
- function hexToRgb(hex: string): [number, number, number] {
346
- const bigint = parseInt(hex.slice(1), 16);
347
- const r = ((bigint >> 16) & 255) / 255;
348
- const g = ((bigint >> 8) & 255) / 255;
349
- const b = (bigint & 255) / 255;
350
- return [r, g, b];
351
- }
352
-
353
- /** Parse RGB string to RGB (0 to 1 range) */
354
- function parseRgb(rgb: string): [number, number, number] {
355
- const match = rgb.match(/^rgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$/i);
356
- if (!match) return [0, 0, 0];
357
- return [
358
- parseInt(match[1] ?? '0') / 255,
359
- parseInt(match[2] ?? '0') / 255,
360
- parseInt(match[3] ?? '0') / 255,
361
- ];
362
- }
363
-
364
- /** Parse HSL string */
365
- function parseHsl(hsl: string): [number, number, number] {
366
- const match = hsl.match(/^hsl\s*\(\s*(\d+)\s*,\s*(\d+)%\s*,\s*(\d+)%\s*\)$/i);
367
- if (!match) return [0, 0, 0];
368
- return [
369
- parseInt(match[1] ?? '0'),
370
- parseInt(match[2] ?? '0'),
371
- parseInt(match[3] ?? '0'),
372
- ];
373
- }
374
-
375
- /** Convert HSL to RGB (0 to 1 range) */
376
- function hslToRgb(hsl: [number, number, number]): [number, number, number] {
377
- const [h, s, l] = hsl;
378
- const hDecimal = h / 360;
379
- const sDecimal = s / 100;
380
- const lDecimal = l / 100;
381
- let r, g, b;
382
-
383
- if (s === 0) {
384
- r = g = b = lDecimal; // achromatic
385
- } else {
386
- const hue2rgb = (p: number, q: number, t: number) => {
387
- if (t < 0) t += 1;
388
- if (t > 1) t -= 1;
389
- if (t < 1 / 6) return p + (q - p) * 6 * t;
390
- if (t < 1 / 2) return q;
391
- if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
392
- return p;
393
- };
394
-
395
- const q =
396
- lDecimal < 0.5
397
- ? lDecimal * (1 + sDecimal)
398
- : lDecimal + sDecimal - lDecimal * sDecimal;
399
- const p = 2 * lDecimal - q;
400
- r = hue2rgb(p, q, hDecimal + 1 / 3);
401
- g = hue2rgb(p, q, hDecimal);
402
- b = hue2rgb(p, q, hDecimal - 1 / 3);
403
- }
404
-
405
- return [r, g, b];
406
- }
@@ -1,87 +0,0 @@
1
- /**
2
- * Renders a grainy texture over top of blobby animated clouds
3
- * This is an example shader that we're using to bootstrap the project
4
- * Generated by Claude 3.5 Sonnet
5
- *
6
- * Parameters include:
7
- * color1: The first color of the clouds
8
- * color2: The second color of the clouds
9
- * noiseScale: The scale of the noise
10
- * noiseSpeed: The speed of the noise
11
- * grainAmount: The amount of grain on the texture
12
- */
13
-
14
- export const grainCloudsFragmentShader = `
15
- precision highp float;
16
- uniform vec2 u_resolution;
17
- uniform float u_time;
18
- uniform vec3 u_color1;
19
- uniform vec3 u_color2;
20
- uniform float u_noiseScale;
21
- uniform float u_noiseSpeed;
22
- uniform float u_grainAmount;
23
-
24
- // Simplex 2D noise
25
- vec3 permute(vec3 x) { return mod(((x*34.0)+1.0)*x, 289.0); }
26
-
27
- float snoise(vec2 v) {
28
- const vec4 C = vec4(0.211324865405187, 0.366025403784439,
29
- -0.577350269189626, 0.024390243902439);
30
- vec2 i = floor(v + dot(v, C.yy));
31
- vec2 x0 = v - i + dot(i, C.xx);
32
- vec2 i1;
33
- i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
34
- vec4 x12 = x0.xyxy + C.xxzz;
35
- x12.xy -= i1;
36
- i = mod(i, 289.0);
37
- vec3 p = permute( permute( i.y + vec3(0.0, i1.y, 1.0))
38
- + i.x + vec3(0.0, i1.x, 1.0));
39
- vec3 m = max(0.5 - vec3(dot(x0,x0), dot(x12.xy,x12.xy),
40
- dot(x12.zw,x12.zw)), 0.0);
41
- m = m*m ;
42
- m = m*m ;
43
- vec3 x = 2.0 * fract(p * C.www) - 1.0;
44
- vec3 h = abs(x) - 0.5;
45
- vec3 ox = floor(x + 0.5);
46
- vec3 a0 = x - ox;
47
- m *= 1.79284291400159 - 0.85373472095314 * (a0*a0 + h*h);
48
- vec3 g;
49
- g.x = a0.x * x0.x + h.x * x0.y;
50
- g.yz = a0.yz * x12.xz + h.yz * x12.yw;
51
- return 130.0 * dot(m, g);
52
- }
53
-
54
- void main() {
55
- vec2 st = gl_FragCoord.xy / u_resolution.xy;
56
-
57
- // Calculate the aspect ratio of the shader
58
- float shaderAspect = u_resolution.x / u_resolution.y;
59
-
60
- // Define the aspect ratio of your content (e.g., 1.0 for square)
61
- float contentAspect = 1.0;
62
-
63
- // Adjust st to maintain content aspect ratio
64
- if (shaderAspect > contentAspect) {
65
- float scale = shaderAspect / contentAspect;
66
- st.x = (st.x - 0.5) * scale + 0.5;
67
- } else {
68
- float scale = contentAspect / shaderAspect;
69
- st.y = (st.y - 0.5) * scale + 0.5;
70
- }
71
-
72
- // Create blobby texture
73
- float n = snoise(st * u_noiseScale + u_time * u_noiseSpeed);
74
- n += 0.5 * snoise(st * u_noiseScale * 2.0 - u_time * u_noiseSpeed * 0.5);
75
- n += 0.25 * snoise(st * u_noiseScale * 4.0 + u_time * u_noiseSpeed * 0.25);
76
- n = n * 0.5 + 0.5;
77
-
78
- // Color interpolation
79
- vec3 color = mix(u_color1, u_color2, n);
80
-
81
- // Add grain
82
- float grain = fract(sin(dot(st * 1000.0, vec2(12.9898, 78.233))) * 43758.5453);
83
- color += (grain - 0.5) * u_grainAmount;
84
-
85
- gl_FragColor = vec4(color, 1.0);
86
- }
87
- `;
package/tsconfig.json DELETED
@@ -1,28 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- // Enable latest features
4
- "lib": ["ESNext", "DOM", "DOM.Iterable"],
5
- "target": "ESNext",
6
- "module": "ESNext",
7
- "moduleDetection": "force",
8
- "jsx": "react-jsx",
9
- "allowJs": true,
10
-
11
- // Bundler mode
12
- "moduleResolution": "bundler",
13
- "allowImportingTsExtensions": true,
14
- "verbatimModuleSyntax": true,
15
- "noEmit": true,
16
-
17
- // Best practices
18
- "strict": true,
19
- "skipLibCheck": true,
20
- "noFallthroughCasesInSwitch": true,
21
-
22
- // Some stricter flags
23
- "noUnusedParameters": false,
24
- "noUncheckedIndexedAccess": true
25
- },
26
- "include": ["src/**/*"],
27
- "exclude": ["node_modules", "dist"]
28
- }