PageSourceSearch

https://multicampattern.com/static/marketing/js/patternsGradientBg.00e08802c4a0.js

js multicampattern.com collected 2026-09-26 11:22:11 UTC 15,633 bytes, 430 lines download raw bytes

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.