1/** 2 * Full-viewport WebGL2 noise gradient for The Patterns chapter. 3 */ 4(function () { 5 'use strict'; 6 7 /** All palettes padded to 7 stops (multicam length) so depth-gallery transitions use uniform stop count. */ 8 var PALETTES = { 9 multicam: [[0.29, 0.241, 0.218], [0.399, 0.319, 0.255], [0.348, 0.354, 0.267], [0.504, 0.449, 0.332], [0.52, 0.476, 0.387], [0.59, 0.506, 0.428], [0.66, 0.615, 0.532]], 10 tropic: [[0.373, 0.384, 0.278], [0.2, 0.22, 0.141], [0.239, 0.22, 0.145], [0.424, 0.443, 0.231], [0.278, 0.271, 0.188], [0.278, 0.271, 0.188], [0.278, 0.271, 0.188]], 11 arid: [[0.373, 0.239, 0.133], [0.529, 0.431, 0.271], [0.525, 0.388, 0.263], [0.635, 0.545, 0.475], [0.671, 0.6, 0.522], [0.671, 0.6, 0.522], [0.671, 0.6, 0.522]], 12 alpine: [[1.0, 0.992, 0.965], [1.0, 0.992, 0.973], [0.898, 0.906, 0.886], [0.796, 0.831, 0.812], [0.678, 0.725, 0.71], [0.678, 0.725, 0.71], [0.678, 0.725, 0.71]], 13 black: [[0, 0, 0], [0.035, 0.035, 0.035], [0.055, 0.071, 0.067], [0.145, 0.165, 0.149], [0.212, 0.22, 0.176], [0.212, 0.22, 0.176], [0.212, 0.22, 0.176]] 14 }; 15 16 var vsSource = [ 17 'attribute vec2 aPos;', 18 'varying vec2 vUv;', 19 'void main(){', 20 ' vUv = aPos * 0.5 + 0.5;', 21 ' gl_Position = vec4(aPos, 0.0, 1.0);', 22 '}' 23 ].join('\n'); 24 25 var fsSource = 26 'precision highp float;\n' + 27 'varying vec2 vUv;\n' + 28 'uniform float uTime;\n' + 29 'uniform vec2 uMouse;\n' + 30 'uniform vec2 uResolution;\n' + 31 'uniform vec3 uC0,uC1,uC2,uC3,uC4,uC5,uC6,uC7,uC8,uC9,uCA,uCB;\n' + 32 'uniform float uCount;\n' + 33 'vec3 mod289v3(vec3 x){return x-floor(x*(1.0/289.0))*289.0;}\n' + 34 'vec4 mod289v4(vec4 x){return x-floor(x*(1.0/289.0))*289.0;}\n' + 35 'vec4 permute(vec4 x){return mod289v4(((x*34.0)+1.0)*x);}\n' + 36 'vec4 taylorInvSqrt(vec4 r){return 1.79284291400159-0.85373472095314*r;}\n' + 37 'float snoise(vec3 v){\n' + 38 ' const vec2 C=vec2(1.0/6.0,1.0/3.0);\n' + 39 ' const vec4 D=vec4(0.0,0.5,1.0,2.0);\n' + 40 ' vec3 i=floor(v+dot(v,C.yyy));\n' + 41 ' vec3 x0=v-i+dot(i,C.xxx);\n' + 42 ' vec3 g=step(x0.yzx,x0.xyz);\n' + 43 ' vec3 l=1.0-g;\n' + 44 ' vec3 i1=min(g.xyz,l.zxy);\n' + 45 ' vec3 i2=max(g.xyz,l.zxy);\n' + 46 ' vec3 x1=x0-i1+C.xxx;\n' + 47 ' vec3 x2=x0-i2+C.yyy;\n' + 48 ' vec3 x3=x0-D.yyy;\n' + 49 ' i=mod289v3(i);\n' + 50 ' vec4 p=permute(permute(permute(\n' + 51 ' i.z+vec4(0.0,i1.z,i2.z,1.0))\n' + 52 ' +i.y+vec4(0.0,i1.y,i2.y,1.0))\n' + 53 ' +i.x+vec4(0.0,i1.x,i2.x,1.0));\n' + 54 ' float n_=0.142857142857;\n' + 55 ' vec3 ns=n_*D.wyz-D.xzx;\n' + 56 ' vec4 j=p-49.0*floor(p*ns.z*ns.z);\n' + 57 ' vec4 x_=floor(j*ns.z);\n' + 58 ' vec4 y_=floor(j-7.0*x_);\n' + 59 ' vec4 x=x_*ns.x+ns.yyyy;\n' + 60 ' vec4 y=y_*ns.x+ns.yyyy;\n' + 61 ' vec4 h=1.0-abs(x)-abs(y);\n' + 62 ' vec4 b0=vec4(x.xy,y.xy);\n' + 63 ' vec4 b1=vec4(x.zw,y.zw);\n' + 64 ' vec4 s0=floor(b0)*2.0+1.0;\n' + 65 ' vec4 s1=floor(b1)*2.0+1.0;\n' + 66 ' vec4 sh=-step(h,vec4(0.0));\n' + 67 ' vec4 a0=b0.xzyw+s0.xzyw*sh.xxyy;\n' + 68 ' vec4 a1=b1.xzyw+s1.xzyw*sh.zzww;\n' + 69 ' vec3 p0=vec3(a0.xy,h.x);\n' + 70 ' vec3 p1=vec3(a0.zw,h.y);\n' + 71 ' vec3 p2=vec3(a1.xy,h.z);\n' + 72 ' vec3 p3=vec3(a1.zw,h.w);\n' + 73 ' vec4 norm=taylorInvSqrt(vec4(dot(p0,p0),dot(p1,p1),dot(p2,p2),dot(p3,p3)));\n' + 74 ' p0*=norm.x;p1*=norm.y;p2*=norm.z;p3*=norm.w;\n' + 75 ' vec4 m=max(0.6-vec4(dot(x0,x0),dot(x1,x1),dot(x2,x2),dot(x3,x3)),0.0);\n' + 76 ' m=m*m;\n' + 77 ' return 42.0*dot(m*m,vec4(dot(p0,x0),dot(p1,x1),dot(p2,x2),dot(p3,x3)));\n' + 78 '}\n' + 79 'float hash(vec2 p){\n' + 80 ' vec3 p3=fract(vec3(p.xyx)*0.1031);\n' + 81 ' p3+=dot(p3,p3.yzx+33.33);\n' + 82 ' return fract((p3.x+p3.y)*p3.z);\n' + 83 '}\n' + 84 'vec3 getColor(float t){\n' + 85 ' float last=uCount-1.0;\n' + 86 ' float idx=t*last;\n' + 87 ' float f=fract(idx);\n' + 88 ' float seg=floor(idx);\n' + 89 ' vec3 a,b;\n' + 90 ' if(seg<1.0){a=uC0;b=uC1;}\n' + 91 ' else if(seg<2.0){a=uC1;b=uC2;}\n' + 92 ' else if(seg<3.0){a=uC2;b=uC3;}\n' + 93 ' else if(seg<4.0){a=uC3;b=uC4;}\n' + 94 ' else if(seg<5.0){a=uC4;b=uC5;}\n' + 95 ' else if(seg<6.0){a=uC5;b=uC6;}\n' + 96 ' else if(seg<7.0){a=uC6;
96b=uC7;}\n' + 97 ' else if(seg<8.0){a=uC7;b=uC8;}\n' + 98 ' else if(seg<9.0){a=uC8;b=uC9;}\n' + 99 ' else if(seg<10.0){a=uC9;b=uCA;}\n' + 100 ' else{a=uCA;b=uCB;}\n' + 101 ' return mix(a,b,smoothstep(0.0,1.0,f));\n' + 102 '}\n' + 103 'void main(){\n' + 104 ' float rh=max(uResolution.y,1.0);\n' + 105 ' vec2 uv=vec2(vUv.x*uResolution.x/rh,vUv.y);\n' + 106 ' vec2 flow=vec2(uMouse.x*uResolution.x/rh,uMouse.y)*0.4;\n' + 107 ' float t=uTime*0.085;\n' + 108 ' float n1=snoise(vec3(uv*0.9+flow,t))*0.5+0.5;\n' + 109 ' float n2=snoise(vec3(uv*1.6-flow*0.7,t*1.3+10.0))*0.5+0.5;\n' + 110 ' float n3=snoise(vec3(uv*2.75+flow*0.3,t*0.7+20.0))*0.5+0.5;\n' + 111 ' float blend=n1*0.5+n2*0.35+n3*0.15;\n' + 112 ' vec3 color=getColor(blend);\n' + 113 ' vec2 gcoord=gl_FragCoord.xy+vec2(uTime*73.1,uTime*59.3);\n' + 114 ' float grain=hash(gcoord*0.65)-0.5;\n' + 115 ' color+=grain*0.22;\n' + 116 ' gl_FragColor=vec4(color,1.0);\n' + 117 '}\n'; 118 119 function compileShader(gl, src, type) { 120 var s = gl.createShader(type); 121 gl.shaderSource(s, src); 122 gl.compileShader(s); 123 if (!gl.getShaderParameter(s, gl.COMPILE_STATUS)) { 124 console.error('patternsGradientBg shader compile:', gl.getShaderInfoLog(s)); 125 return null; 126 } 127 return s; 128 } 129 130 function setPalette(gl, prog, uColorLocs, uCountLoc, name) { 131 var pal = PALETTES[name] || PALETTES.multicam; 132 gl.useProgram(prog); 133 gl.uniform1f(uCountLoc, pal.length); 134 for (var i = 0; i < 12; i++) { 135 var ci = i < pal.length ? i : pal.length - 1; 136 gl.uniform3f(uColorLocs[i], pal[ci][0], pal[ci][1], pal[ci][2]); 137 } 138 } 139 140 /** Expand palette to 12 RGB triples (same indexing as setPalette). */ 141 function expandPalette12(name) { 142 var pal = PALETTES[name] || PALETTES.multicam; 143 var out = []; 144 for (var i = 0; i < 12; i++) { 145 var ci = i < pal.length ? i : pal.length - 1; 146 out.push([pal[ci][0], pal[ci][1], pal[ci][2]]); 147 } 148 return { colors: out, count: pal.length }; 149 } 150 151 function smoothstep01Cross(u) { 152 var x = u <= 0 ? 0 : u >= 1 ? 1 : u; 153 return x * x * (3 - 2 * x); 154 } 155 156 function uploadPaletteLerp(gl, prog, uColorLocs, uCountLoc, colorsA, colorsB, countA, countB, t) { 157 var sm = smoothstep01Cross(t); 158 gl.useProgram(prog); 159 gl.uniform1f(uCountLoc, countA + (countB - countA) * sm); 160 for (var i = 0; i < 12; i++) { 161 var a = colorsA[i]; 162 var b = colorsB[i]; 163 gl.uniform3f(uColorLocs[i], a[0] + (b[0] - a[0]) * sm, a[1] + (b[1] - a[1]) * sm, a[2] + (b[2] - a[2]) * sm); 164 } 165 } 166 167 var state = null; 168 169 function destroy() { 170 if (!state) return; 171 state.running = false; 172 if (state.io) state.io.disconnect(); 173 if (state.animId) cancelAnimationFrame(state.animId); 174 state.animId = null; 175 window.removeEventListener('resize', state.onResize); 176 if (!state.noMouse) { 177 document.removeEventListener('mousemove', state.onMouseMove); 178 } 179 if (state.ro) state.ro.disconnect(); 180 if (state.gl && state.prog) { 181 state.gl.deleteProgram(state.prog); 182 state.gl.deleteShader(state.vs); 183 state.gl.deleteShader(state.fs); 184 state.gl.deleteBuffer(state.buf); 185 } 186 state = null; 187 window._mcPatternsGradientBgCleanup = null; 188 window.mcPatternsGradientSnapKey = null; 189 } 190 191 /** 192 * @param {HTMLCanvasElement} canvas 193 * @param {function(): string} getPatternKey â returns 'multicam' | 'alpine' | ... 194 * @param {{ noMouse?: boolean }}
194 [options] â if noMouse, skip mouse-driven flow in the shader 195 */ 196 function initPatternsGradientBg(canvas, getPatternKey, options) { 197 destroy(); 198 if (!canvas || typeof getPatternKey !== 'function') return null; 199 options = options || {}; 200 var noMouse = !!options.noMouse; 201 var getGradientBlend = options.getGradientBlend; 202 203 var gl = canvas.getContext('webgl2') || canvas.getContext('webgl'); 204 if (!gl) return null; 205 206 var vs = compileShader(gl, vsSource, gl.VERTEX_SHADER); 207 var fs = compileShader(gl, fsSource, gl.FRAGMENT_SHADER); 208 if (!vs || !fs) return null; 209 var prog = gl.createProgram(); 210 gl.attachShader(prog, vs); 211 gl.attachShader(prog, fs); 212 gl.linkProgram(prog); 213 if (!gl.getProgramParameter(prog, gl.LINK_STATUS)) { 214 console.error('patternsGradientBg link:', gl.getProgramInfoLog(prog)); 215 return null; 216 } 217 218 var buf = gl.createBuffer(); 219 gl.bindBuffer(gl.ARRAY_BUFFER, buf); 220 gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, -1, 1, -1, -1, 1, 1, 1]), gl.STATIC_DRAW); 221 gl.useProgram(prog); 222 var aPos = gl.getAttribLocation(prog, 'aPos'); 223 gl.enableVertexAttribArray(aPos); 224 gl.vertexAttribPointer(aPos, 2, gl.FLOAT, false, 0, 0); 225 226 var uTime = gl.getUniformLocation(prog, 'uTime'); 227 var uMouse = gl.getUniformLocation(prog, 'uMouse'); 228 var uResolution = gl.getUniformLocation(prog, 'uResolution'); 229 var uCount = gl.getUniformLocation(prog, 'uCount'); 230 var colorNames = ['uC0', 'uC1', 'uC2', 'uC3', 'uC4', 'uC5', 'uC6', 'uC7', 'uC8', 'uC9', 'uCA', 'uCB']; 231 var uColorLocs = []; 232 for (var c = 0; c < 12; c++) { 233 uColorLocs.push(gl.getUniformLocation(prog, colorNames[c])); 234 } 235 236 var mx = 0; 237 var my = 0; 238 var time = 0; 239 var animId = null; 240 var running = false; 241 /* Cap this full-screen shader to ~60fps; at 120Hz it would otherwise run 242 twice as often as perceptible, doubling fragment-shader GPU cost. */ 243 var GRAD_FRAME_MS = 1000 / 60; 244 var lastDrawTime = 0; 245 var BLEND_SEC = 0.35; 246 var lastFrameTime = typeof performance !== 'undefined' ? performance.now() : 0; 247 var colorsFrom = null; 248 var colorsTo = null; 249 var countFrom = 7; 250 var countTo = 7; 251 var blendP = 1; 252 var logicalKey = ''; 253 254 function deepCopy12(src) { 255 var o = []; 256 for (var di = 0; di < 12; di++) { 257 o.push([src[di][0], src[di][1], src[di][2]]); 258 } 259 return o; 260 } 261 262 function onMouseMove(e) { 263 mx = (e.clientX / Math.max(window.innerWidth, 1)) * 2 - 1; 264 my = (e.clientY / Math.max(window.innerHeight, 1)) * 2 - 1; 265 } 266 267 function resize() { 268 var dpr = Math.min(window.devicePixelRatio || 1, 2); 269 var w = Math.round(canvas.clientWidth * dpr); 270 var h = Math.round(canvas.clientHeight * dpr); 271 if (w < 2 || h < 2) return; 272 if (canvas.width !== w || canvas.height !== h) { 273 canvas.width = w; 274 canvas.height = h; 275 } 276 } 277 278 function frame() { 279 if (!running) { 280 animId = null; 281 return; 282 } 283 animId = requestAnimationFrame(frame); 284 var now = typeof performance !== 'undefined' ? performance.now() : 0; 285 /* Frame-rate cap: skip frames arriving sooner than the target interval. 286 lastFrameTime (dt source) only advances on frames that actually draw. */ 287 if (now - lastDrawTime < GRAD_FRAME_MS - 0.5) return; 288 lastDrawTime = now; 289 resize(); 290 var dt = Math.min(0.05, Math.max(0, (now - lastFrameTime) / 1000)); 291 lastFrameTime = now; 292 293 var key = getPatternKey() || 'multicam'; 294 var blend = getGradientBlend && getGradientBlend(); 295 if (blend && blend.from && blend.to && typeof blend.t === 'number') { 296 var bt = blend.t <= 0 ? 0 : blend.t >= 1 ? 1 : blend.t; 297 var blendFrom = expandPalette12(blend.from); 298 var blendTo = expandPalette12(blend.to); 299 uploadPaletteLerp( 300 gl, prog, uColorLocs, uCount, 301 blendFrom.colors, blendTo.colors, blendFrom.count, blendTo.count, bt 302 ); 303 } else if (key !== logicalKey) { 304 var smSnap = smoothstep01Cross(blendP >= 1 ? 1 : blendP); 305 for (var ii = 0; ii < 12; ii++) { 306 colorsFrom[ii][0] += (colorsTo[ii][0] - colorsFrom[ii][0]) * smSnap; 307 colorsFrom[ii][1] += (colorsTo[ii][1] - colorsFrom[ii][1]) * smSnap; 308 colorsFrom[ii][2] += (colorsTo[ii][2] - colorsFrom[ii][2]) * smSnap; 309 } 310 countFrom = countFrom + (countTo - countFrom) * smSnap; 311 var nx = expandPalette12(key); 312 colorsTo = deepCopy12(nx.colors); 313 countTo = nx.count; 314 blendP = 0; 315 logicalKey = key; 316 } 317 if (blend && blend.from && blend.to && typeof blend.t === 'number') { 318 /* palette uploaded above */ 319 } else if (blendP < 1) { 320 blendP += dt / BLEND_SEC; 321 if (blendP > 1) blendP = 1; 322 } 323 if (!(blend && blend.from && blend.to && typeof blend.t === 'number')) { 324 uploadPaletteLerp(gl, prog, uColorLocs, uCount, colorsFrom, colorsTo, countFrom, countTo, blendP); 325 } 326 time += dt * 1.12; 327 gl.viewport(0, 0, canvas.width, canvas.height); 328 gl.useProgram(prog); 329 gl.uniform1f(uTime, time); 330 gl.uniform2f(uMouse, mx, my); 331 gl.uniform2f(uResolution, canvas.width, Math.max(canvas.height, 1)); 332 gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); 333 } 334 335 function startLoop() { 336 if (running && animId == null) { 337 animId = requestAnimationFrame(frame); 338 } 339 } 340 341 function onResize() { 342 resize(); 343 } 344 345 var section = document.getElementById('home-patterns-intro'); 346 var io = new IntersectionObserver( 347 function (entries) {
348 var on = entries[0] && entries[0].isIntersecting; 349 running = !!on; 350 if (running) startLoop(); 351 else if (animId) { 352 cancelAnimationFrame(animId); 353 animId = null; 354 } 355 }, 356 { rootMargin: '80px 0px', threshold: 0.01 } 357 ); 358 if (section) io.observe(section); 359 else io.observe(canvas); 360 361 var ro = typeof ResizeObserver !== 'undefined' ? new ResizeObserver(resize) : null; 362 if (ro) ro.observe(canvas); 363 364 window.addEventListener('resize', onResize); 365 if (!noMouse) { 366 document.addEventListener('mousemove', onMouseMove, { passive: true }); 367 } 368 369 var initPal = expandPalette12(getPatternKey() || 'multicam'); 370 colorsFrom = deepCopy12(initPal.colors); 371 colorsTo = deepCopy12(initPal.colors); 372 countFrom = initPal.count; 373 countTo = initPal.count; 374 blendP = 1; 375 logicalKey = getPatternKey() || 'multicam'; 376 uploadPaletteLerp(gl, prog, uColorLocs, uCount, colorsFrom, colorsTo, countFrom, countTo, 1); 377 378 state = { 379 canvas: canvas, 380 gl: gl, 381 prog: prog, 382 vs: vs, 383 fs: fs, 384 buf: buf, 385 uTime: uTime, 386 uMouse: uMouse, 387 uResolution: uResolution, 388 uCount: uCount, 389 uColorLocs: uColorLocs, 390 getPatternKey: getPatternKey, 391 io: io, 392 ro: ro, 393 animId: null, 394 running: false, 395 onResize: onResize, 396 onMouseMove: onMouseMove, 397 noMouse: noMouse 398 }; 399 400 window._mcPatternsGradientBgCleanup = destroy; 401 402 function snapGradientKey(key) { 403 var k = key || 'multicam'; 404 var pal = expandPalette12(k); 405 colorsFrom = deepCopy12(pal.colors); 406 colorsTo = deepCopy12(pal.colors); 407 countFrom = pal.count; 408 countTo = pal.count; 409 logicalKey = k; 410 blendP = 1; 411 uploadPaletteLerp(gl, prog, uColorLocs, uCount, colorsFrom, colorsTo, countFrom, countTo, 1); 412 } 413 414 window.mcPatternsGradientSnapKey = snapGradientKey; 415 416 resize(); 417 if (section) { 418 var r = section.getBoundingClientRect(); 419 running = r.bottom > 0 && r.top < window.innerHeight; 420 } else { 421 running = true; 422 } 423 if (running) startLoop(); 424 425 return { destroy: destroy }; 426 } 427 428 window.mcPatternsGradientBgInit = initPatternsGradientBg; 429 window.mcPatternsGradientBgDestroy = destroy; 430})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.