PageSourceSearch

https://shadermaterial.com/_nuxt/E36QTQpW.js

js shadermaterial.com collected 2026-10-09 21:45:00 UTC 28,887 bytes, 772 lines download raw bytes

1const e=`<!--
2  Shader Studio (ID: mu7j1h)
3  Generated on: 2026-10-07T03:15:11.534Z
4  License: Commercial / Max Tier (Own business, finished client projects, and reusable template and asset redistribution permitted)
5  
6  Post-Processing Effects:
7  1. bloom - intensity: 0.18, threshold: 0.5, radius: 0.9
8  2. colorgrade - brightness: 0, contrast: 0.16, saturation: -0.08, temperature: -0.04
9  3. vignette - intensity: 0.28, smoothness: 0.7, roundness: 0.94
10  
11  Timeline Animations:
12  No timeline animations configured
13  
14  Timeline Playback:
15  - Playback: starts as soon as the shader loads.
16  - Change it by editing "playback" in the timelineData object below ('auto' | 'once' | 'visible' | 'click').
17  - The player also exposes play(), pause(), seek(t), restart() and dispose() if you want to drive it yourself.
18  - Install GSAP (npm install gsap) to unlock advanced easing curves; without it, non-linear easings fall back to linear.
19  - Full timeline JSON is included at the end of this file if you want to customize the playback logic manually.
20
21  Tailwind layout (optional):
22  The canvas fills its parent (width/height 100%). Pass Tailwind classes via the
23  'canvasClass' prop, e.g. <MuseumLiquidPlasma :canvas-class="'h-screen w-full'" />.
24  For a full-bleed hero wrap it:
25    <div class="relative h-screen w-full">
26      <div class="absolute inset-0"><MuseumLiquidPlasma canvas-class="h-full w-full" /></div>
27      <div class="relative z-10 ...">content</div>
28    </div>
29
30  CSS variable binding (optional):
31  Pass a 'cssVars' map to drive uniforms from CSS variables on the parent element.
32  Example: <MuseumLiquidPlasma :css-vars="{ u_color1: '--shader-color1', u_speed: '--shader-speed' }" />
33  and in CSS: .parent { --shader-color1: #ff006e; --shader-speed: 0.5; }
34
35  Interaction uniforms exposed: u_scrollVelocity (scroll velocity -1..1),
36  u_gyro (device orientation vec3 -1..1), u_clickPosition (vec2), u_lastClickTime (sec).
37-->
38<template>
39  <canvas ref="canvasEl_mu7j1h" :class="['shader-canvas-mu7j1h', props.canvasClass]"></canvas>
40</template>
41
42<script setup>
43import { ref, onMounted, onUnmounted } from 'vue';
44import * as THREE from 'three';
45
46const props = defineProps({ cssVars: { type: Object, default: () => null }, canvasClass: { type: String, default: '' } });
47
48const canvasEl_mu7j1h = ref(null);
49
50const vertexShader = \`varying vec2 vUv;
51void main() {
52  vUv = uv;
53  gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
54}\`;
55
56const fragmentShader = \`precision highp float;
57
58// Safely handle smoothstep on strict software renderers (prevents NaN when edge0 >= edge1)
59float safe_smoothstep(float edge0, float edge1, float x) {
60    if (edge0 <= edge1) return smoothstep(edge0, edge1, x);
61    return 1.0 - smoothstep(edge1, edge0, x);
62}
63vec2 safe_smoothstep(float edge0, float edge1, vec2 x) {
64    if (edge0 <= edge1) return smoothstep(edge0, edge1, x);
65    return 1.0 - smoothstep(edge1, edge0, x);
66}
67vec3 safe_smoothstep(float edge0, float edge1, vec3 x) {
68    if (edge0 <= edge1) return smoothstep(edge0, edge1, x);
69    return 1.0 - smoothstep(edge1, edge0, x);
70}
71vec4 safe_smoothstep(float edge0, float edge1, vec4 x) {
72    if (edge0 <= edge1) return smoothstep(edge0, edge1, x);
73    return 1.0 - smoothstep(edge1, edge0, x);
74}
75vec2 safe_smoothstep(vec2 edge0, vec2 edge1, vec2 x) {
76    return vec2(
77        (edge0.x <= edge1.x) ? smoothstep(edge0.x, edge1.x, x.x) : (1.0 - smoothstep(edge1.x, edge0.x, x.x)),
78        (edge0.y <= edge1.y) ? smoothstep(edge0.y, edge1.y, x.y) : (1.0 - smoothstep(edge1.y, edge0.y, x.y))
79    );
80}
81vec3 safe_smoothstep(vec3 edge0, vec3 edge1, vec3 x) {
82    return vec3(
83        (edge0.x <= edge1.x) ? smoothstep(edge0.x, edge1.x, x.x) : (1.0 - smoothstep(edge1.x, edge0.x, x.x)),
84        (edge0.y <= edge1.y) ? smoothstep(edge0.y, edge1.y, x.y) : (1.0 - smoothstep(edge1.y, edge0.y, x.y)),
85        (edge0.z <= edge1.z) ? smoothstep(edge0.z, edge1.z, x.z) : (1.0 - smoothstep(edge1.z, edge0.z, x.z))
86    );
87}
88vec4 safe_smoothstep(vec4 edge0, vec4 edge1, vec4 x) {
89    return vec4(
90        (edge0.x <= edge1.x) ? smoothstep(edge0.x, edge1.x, x.x) : (1.0 - smoothstep(edge1.x, edge0.x, x.x)),
91        (edge0.y <= edge1.y) ? smoothstep(edge0.y, edge1.y, x.y) : (1.0 - smoothstep(edge1.y, edge0.y, x.y)),
92        (edge0.z <= edge1.z) ? smoothstep(edge0.z, edge1.z, x.z) : (1.0 - smoothstep(edge1.z, edge0.z, x.z)),
93        (edge0.w <= edge1.w) ? smoothstep(edge0.w, edge1.w, x.w) : (1.0 - smoothstep(edge1.w, edge0.w, x.w))
94    );
95}
96#define smoothstep safe_smoothstep
97
98// Add a glow to a colour without letting it change hue on the way to white.
99//
100// A plain "color += tint * glow" clips the brightest channel first, so a
101// saturated orange loses red headroom while green keeps climbing and the result
102// walks to yellow — the layer never looks brighter, only wronger. Lifting and
103// then normalising by the lifted peak keeps the channel ratios, and the crest
104// blooms toward white only as far as it actually overshot. Below the clip point
105// this is exactly "base + add", so it changes no pixel that was already legal.
106vec3 glow_lift(vec3 base, vec3 add) {
107    vec3 lifted = base + add;
108    float peak = max(max(lifted.r, lifted.g), max(lifted.b, 1.0));
109    return mix(lifted / peak, vec3(1.0), clamp((peak - 1.0) * 0.25, 0.0, 0.75));
110}
111
112varying vec2 vUv;
113uniform float u_time;
114uniform vec2 u_resolution;
115// Global playback rate. Was a baked literal until the timeline needed to
116// animate it without recompiling; see the time assignment in main() below.
117uniform float u_timeSpeed;
118// Pointer uniforms — see app/lib/shaders/pointer-state.ts
119uniform vec2 u_mouse;           // smoothed pointer position (0..1, y up)
120uniform vec2 u_mouseVelocity;   // pointer velocity in uv/second, clamped to -1..1
121uniform float u_mouseStrength;  // 0..1 pointer presence; decays to 0 on leave
122uniform vec2 u_mouseTrail[8]; // recent positions, [0] = newest
123
124// Interactive input uniforms
125uniform float u_scrollVelocity; // scroll velocity (-1..1), driven by JS
126uniform vec3 u_gyro;          // device orientation (x,y,z in -1..1)
127uniform vec2 u_clickPosition; // last click position (vec2 0..1)
128uniform float u_lastClickTime; // time of last click (seconds; -10 = none yet)
129
130vec2 spcAspect() {
131    return u_resolution.x > 0.0 && u_resolution.y > 0.0
132        ? vec2(u_resolution.x / u_resolution.y, 1.0)
133        : vec2(1.0);
134}
135
136vec2 spcToUvSpace(vec2 p) {
137    return (p - 0.5) * spcAspect() + 0.5;
138}
139
140vec2 spcFromUvSpace(vec2 p) {
141    return (p - 0.5) / spcAspect() + 0.5;
142}
143
144// Audio uniforms
145uniform float u_audioHigh;
146uniform float u_audioMid;
147uniform float u_audioBass;
148uniform float u_audioLevel;
149
150
151// Brand theme uniforms (no active theme — defaults emitted)
152uniform vec3 u_brandBackground;
153uniform vec3 u_brandSurface;
154uniform vec3 u_brandText;
155uniform vec3 u_brandTextMuted;
156uniform vec3 u_brandAccent;
157uniform vec3 u_brandAccentAlt;
158uniform vec3 u_brandRamp[8];
159uniform int  u_brandRampLength;
160uniform float u_brandGrainAmount;
161uniform float u_brandNoiseScale;
162uniform float u_brandSoftness;
163uniform float u_brandAmbientSpeed;
164
165
166// Dynamic Layer Uniforms
167uniform float u_layer_0_opacity;
168uniform vec3 u_layer_0_tint1;
169uniform vec3 u_layer_0_tint2;
170uniform vec3 u_layer_0_tint3;
171uniform float u_layer_0_scale;
172uniform float u_layer_0_speed;
173uniform float u_layer_0_viscosity;
174uniform float u_layer_0_intensity;
175
176// PBR Layering Struct
177struct LayerOutput {
178    vec4 color;
179    vec3 normal;
180    float height;
181    float roughness;
182};
183
184
185
186float hash21_layer_0_fluid_sim(vec2 p) {
187  p = fract(p * vec2(123.34, 345.45));
188  p += dot(p, p + 34.345);
189  return fract(p.x * p.y);
190}
191float vnoise_layer_0_fluid_sim(vec2 p) {
192  vec2 i = floor(p); vec2 f = fract(p);
193  vec2 u = f * f * (3.0 - 2.0 * f);
194  float a = hash21_layer_0_fluid_sim(i);
195  float b = hash21_layer_0_fluid_sim(i + vec2(1.0, 0.0));
196  float c = hash21_layer_0_fluid_sim(i + vec2(0.0, 1.0));
197  float d = hash21_layer_0_fluid_sim(i + vec2(1.0, 1.0));
198  return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);
199}
200float fbm_layer_0_fluid_sim(vec2 p) {
201  float v = 0.0; float a = 0.5;
202  for (int i = 0; i < 5; i++) { v += a * vnoise_layer_0_fluid_sim(p); p *= 2.0; a *= 0.5; }
203  return v;
204}
205
206vec2 layer_0_fluid_sim_flow(vec2 uv, float time) {
207  vec2 q = vec2(fbm_layer_0_fluid_sim(uv * 3.0 + time * 0.1), fbm_layer_0_fluid_sim(uv * 3.0 + vec2(5.2, 1.3) + time * 0.12));
208  vec2 r = vec2(fbm_layer_0_fluid_sim(uv * 3.0 + q + vec2(1.7, 9.2) + time * 0.15), fbm_layer_0_fluid_sim(uv * 3.0 + q + vec2(8.3, 2.8) + time * 0.13));
209  return r;
210}
211vec4 layer_0_fluid_sim_inner(vec2 uv, float time) {
212  float t = time * 0.6;
213  vec2 r = layer_0_fluid_sim_flow(uv, t);
214  float f = fbm_layer_0_fluid_sim(uv * 3.0 + r * 0.85 + t * 0.2);
215  vec3 c1 = vec3(0.055, 0.647, 0.914);
216  vec3 c2 = vec3(0.659, 0.333, 0.969);
217  vec3 c3 = vec3(0.133, 0.827, 0.933);
218  vec3 col = mix(c1, c2, smoothstep(0.2, 0.6, f));
219  col = mix(col, c3, smoothstep(0.5, 0.9, length(r)));
220  col *= 1.0;
221  return vec4(clamp(col, 0.0, 1.0), 1.0);
222}
223
224LayerOutput layer_0_fluid_sim(vec2 uv, float time, LayerOutput previousLayer) {
225    vec2 distortedUv = uv;
226    #if defined(GL_OES_standard_derivatives) || __VERSION__ >= 300
227    float disp = 0.0;
228    if (abs(disp) > 0.0001) {
229        vec2 heightGradient = vec2(dFdx(previousLayer.height), dFdy(previousLayer.height));
230        distortedUv += heightGradient * disp;
231    }
232    #endif
233    vec4 color = layer_0_fluid_sim_inner(distortedUv, time);
234    float luminance = dot(color.rgb, vec3(0.299, 0.587, 0.114));
235    float height = luminance * color.a * 0.0;
236    vec3 normal = vec3(0.0, 0.0, 1.0);
237    #if defined(GL_OES_standard_derivatives) || __VERSION__ >= 300
238    if (height > 0.0001) {
239        vec2 dHeight = vec2(dFdx(height), dFdy(height));
240        normal = normalize(vec3(-dHeight * 10.0, 1.0));
241    }
242    #endif
243    float roughness = 0.5;
244    return LayerOutput(color, normal, height, roughness);
245}
246
247
248vec4 blend_normal(vec4 base, vec4 blend) {
249    return mix(base, blend, blend.a);
250}
251
252
253void main() {
254    vec2 aspectVec = spcAspect();
255    vec2 uv = (vUv - 0.5) * aspectVec + 0.5;
256    // Map the pointer into the same aspect-corrected space as uv. Comparing a
257    // raw 0..1 u_mouse against a corrected uv leaves the effect the right shape
258    // but in the wrong place — horizontally offset from the cursor by an amount
259    // that grows with the aspect ratio.
260    vec2 mouse = spcToUvSpace(u_mouse);
261    // Uniform rather than a baked literal so the timeline can drive it without
262    // a recompile. The literal was also rounded to one decimal, which re-timed
263    // any composition whose speed was not a multiple of 0.1; the uniform now
264    // carries the authored value exactly.
265    float time = u_time * u_timeSpeed;
266    
267    // Initialize PBR LayerOutput struct
268    LayerOutput accumulated;
269    accumulated.color = vec4(vec3(0.020, 0.031, 0.086), 1.0);
270    accumulated.normal = vec3(0.0, 0.0, 1.0);
271    accumulated.height = 0.0;
272    accumulated.roughness = 0.5;
273    
274    // Layer 0: Fluid Simulation
275    {
276        vec2 layerUv = uv;
277        LayerOutput layerOut = layer_0_fluid_sim(layerUv, time, accumulated);
278        layerOut.color.a *= u_layer_0_opacity;
279        float layerAlpha_0 = layerOut.color.a;
280        accumulated.color = blend_normal(accumulated.color, layerOut.color);
281        vec3 blendedNormal_0 = normalize(vec3(accumulated.normal.xy + layerOut.normal.xy, accumulated.normal.z * layerOut.normal.z));
282        accumulated.normal = normalize(mix(accumulated.normal, blendedNormal_0, layerOut.color.a));
283        accumulated.height = mix(accumulated.height, accumulated.height + layerOut.height, layerOut.color.a);
284        accumulated.roughness = mix(accumulated.roughness, layerOut.roughness, layerOut.color.a);
285    }
286
287    // Flat 2D rendering bypassing viewport lighting
288    vec4 finalColor = accumulated.color;
289
290    // Post-Processing Effects
291    vec3 color = finalColor.rgb;
292    vec2 resolution = u_resolution;
293
294    // PostFX 0: bloom
295    {
296        vec3 bloomColor = color;
297        
298        // Extract bright areas
299        float luminance = dot(bloomColor, vec3(0.299, 0.587, 0.114));
300        vec3 bright = bloomColor * smoothstep(0.5 - 0.1, 0.5 + 0.1, luminance);
301        
302        // Simple blur approximation
303        vec3 blurred = bright;
304        float blurSize = 0.9 * 0.002;
305        
306        for(float x = -2.0; x <= 2.0; x += 1.0) {
307          for(float y = -2.0; y <= 2.0; y += 1.0) {
308            vec2 offset = vec2(x, y) * blurSize;
309            vec2 sampleUV = uv + offset;
310            vec3 sampleColor = color; // In real implementation, would sample from texture
311            float sampleLum = dot(sampleColor, vec3(0.299, 0.587, 0.114));
312            blurred += sampleColor * smoothstep(0.5 - 0.1, 0.5 + 0.1, sampleLum) * 0.04;
313          }
314        }
315        
316        color = mix(color, color + blurred * 0.18, 0.18);
317      }
318
319    // PostFX 1: colorgrade
320    {
321        // Brightness
322        color += vec3(0.0);
323        
324        // Contrast
325        color = (color - 0.5) * (1.0 + 0.16) + 0.5;
326        
327        // Saturation
328        vec3 gray = vec3(dot(color, vec3(0.299, 0.587, 0.114)));
329        color = mix(gray, color, 1.0 + -0.08);
330        
331        // Temperature (warm/cool)
332        color.r += -0.04 * 0.3;
333        color.b -= -0.04 * 0.3;
334        
335        // Clamp
336        color = clamp(color, 0.0, 1.0);
337      }
338
339    // PostFX 2: vignette
340    {
341        vec2 vignetteUV = uv - 0.5;
342        vignetteUV.x /= mix(1.0, resolution.x / resolution.y, 0.94);
343        float vignette = length(vignetteUV);
344        // Use 1.0 - smoothstep(min, max) to avoid undefined behavior when edge0 >= edge1
345        vignette = 1.0 - smoothstep(max(0.0, 0.7 - 0.5), 0.7 + 0.01, vignette);
346        color *= mix(1.0, vignette, 0.28);
347      }
348
349    finalColor = vec4(clamp(color, 0.0, 1.0), finalColor.a);
350    // Opaque background: the matte covers every pixel, so the canvas is
351    // fully opaque regardless of what the per-layer blends left in alpha.
352    gl_FragColor = vec4(finalColor.rgb, 1.0);
353}\`;
354
355const shaderTextures = [];
356
357function createShaderPlaceholderTexture(textureInfo) {
358  if (textureInfo.type === 'cubemap') {
359    const faces = [];
360    for (let index = 0; index < 6; index++) {
361      const canvas = document.createElement('canvas');
362      canvas.width = 4;
363      canvas.height = 4;
364      const context = canvas.getContext('2d');
365      if (context) {
366        context.fillStyle = '#000000';
367        context.fillRect(0, 0, 4, 4);
368      }
369      faces.push(canvas);
370    }
371    const texture = new THREE.CubeTexture(faces);
372    texture.needsUpdate = true;
373    return texture;
374  }
375
376  if (textureInfo.type === 'volume' && THREE.Data3DTexture) {
377    const size = 2;
378    const data = new Uint8Array(size * size * size);
379    const texture = new THREE.Data3DTexture(data, size, size, size);
380    texture.format = THREE.RedFormat;
381    texture.type = THREE.UnsignedByteType;
382    texture.minFilter = THREE.LinearFilter;
383    texture.magFilter = THREE.LinearFilter;
384    texture.wrapS = THREE.RepeatWrapping;
385    texture.wrapT = THREE.RepeatWrapping;
386    texture.wrapR = THREE.RepeatWrapping;
387    texture.needsUpdate = true;
388    return texture;
389  }
390
391  const alpha = textureInfo.uniformName.startsWith('u_selectionMask_') ? 0 : 255;
392  const texture = new THREE.DataTexture(new Uint8Array([0, 0, 0, alpha]), 1, 1, THREE.RGBAFormat);
393  texture.needsUpdate = true;
394  return texture;
395}
396
397function disposeShaderTextureValue(value) {
398  if (value && typeof value.dispose === 'function') {
399    value.dispose();
400  }
401}
402
403function addShaderTexturePlaceholders(uniforms) {
404  shaderTextures.forEach(textureInfo => {
405    uniforms[textureInfo.uniformName] = { value: createShaderPlaceholderTexture(textureInfo) };
406  });
407}
408
409function loadShaderTextures(material) {
410  shaderTextures.forEach(textureInfo => {
411    if (textureInfo.type === 'cubemap') {
412      const faces = textureInfo.faces;
413      if (!faces || !faces.px || !faces.nx || !faces.py || !faces.ny || !faces.pz || !faces.nz) return;
414
415      new THREE.CubeTextureLoader().load(
416        [faces.px, faces.nx, faces.py, faces.ny, faces.pz, faces.nz],
417        (texture) => {
418          texture.minFilter = THREE.LinearFilter;
419          texture.magFilter = THREE.LinearFilter;
420          const uniform = material.uniforms[textureInfo.uniformName];
421          if (!uniform) return;
422          disposeShaderTextureValue(uniform.value);
423          uniform.value = texture;
424          material.needsUpdate = true;
425        }
426      );
427      return;
428    }
429
430    if (textureInfo.type === 'volume' && THREE.Data3DTexture) {
431      fetch(textureInfo.url)
432        .then(response => {
433          if (!response.ok) throw new Error('Failed to load volume texture');
434          return response.arrayBuffer();
435        })
436        .then(buffer => {
437          const size = 32;
438          const texture = new THREE.Data3DTexture(new Uint8Array(buffer), size, size, size);
439          texture.format = THREE.RedFormat;
440          texture.type = THREE.UnsignedByteType;
441          texture.minFilter = THREE.LinearFilter;
442          texture.magFilter = THREE.LinearFilter;
443          texture.wrapS = THREE.RepeatWrapping;
444          texture.wrapT = THREE.RepeatWrapping;
445          texture.wrapR = THREE.RepeatWrapping;
446          texture.needsUpdate = true;
447          const uniform = material.uniforms[textureInfo.uniformName];
448          if (!uniform) return;
449          disposeShaderTextureValue(uniform.value);
450          uniform.value = texture;
451          material.needsUpdate = true;
452        })
453        .catch(() => {});
454      return;
455    }
456
457    new THREE.TextureLoader().load(
458      textureInfo.url,
459      (texture) => {
460        texture.wrapS = THREE.RepeatWrapping;
461        texture.wrapT = THREE.RepeatWrapping;
462        texture.minFilter = THREE.LinearFilter;
463        texture.magFilter = THREE.LinearFilter;
464        const uniform = material.uniforms[textureInfo.uniformName];
465        if (!uniform) return;
466        disposeShaderTextureValue(uniform.value);
467        uniform.value = texture;
468        material.needsUpdate = true;
469      }
470    );
471  });
472}
473
474function disposeShaderTextures(material) {
475  if (!material) return;
476  shaderTextures.forEach(textureInfo => {
477    const uniform = material.uniforms[textureInfo.uniformName];
478    if (uniform) disposeShaderTextureValue(uniform.value);
479  });
480}
481
482/* ---------- Colour helper (module scope: the uniform block and the
483   timeline runtime both call it) ---------- */
484// Shader colours are consumed as raw 0..1 sRGB components, exactly as
485// hexToVec3 bakes them into the shader's own literals. new THREE.Color(hex)
486// would convert sRGB -> linear and hand the shader much darker, hue-shifted
487// values than the editor preview showed, so build the colour by hand.
488function __spcColor(hex, target) {
489  var c = target && target.isColor ? target : new THREE.Color();
490  if (typeof hex !== 'string' || hex.charAt(0) !== '#') return c;
491  var h = hex.slice(1);
492  if (h.length === 3) h = h[0] + h[0] + h[1] + h[1] + h[2] + h[2];
493  var n = parseInt(h.slice(0, 6), 16);
494  if (!isFinite(n)) return c;
495  c.r = ((n >> 16) & 255) / 255;
496  c.g = ((n >> 8) & 255) / 255;
497  c.b = (n & 255) / 255;
498  return c;
499}
500
501
502
503onMounted(() => {
504  let renderer, scene, camera, material, animationFrameId;
505  let timelinePlayer = null;
506  const canvas = canvasEl_mu7j1h.value;
507  if (!canvas) return;
508
509  const uniforms = {
510    u_time: { value: 0.0 },
511    u_resolution: { value: new THREE.Vector2() },
512    u_mouse: { value: new THREE.Vector2(0.5, 0.5) },
513    u_mouseVelocity: { value: new THREE.Vector2() },
514    u_mouseStrength: { value: 0 },
515    u_mouseTrail: { value: Array.from({ length: 8 }, () => new THREE.Vector2(0.5, 0.5)) },
516    u_scrollVelocity: { value: 0 },
517    u_gyro: { value: new THREE.Vector3() },
518    u_clickPosition: { value: new THREE.Vector2(0.5, 0.5) },
519    u_lastClickTime: { value: -10 },
520    // User-defined uniforms:
521    u_timeSpeed: { value: 0.36 },
522    u_layer_0_opacity: { value: 1 },
523    u_layer_0_tint1: { value: __spcColor("#0ea5e9") },
524    u_layer_0_tint2: { value: __spcColor("#a855f7") },
525    u_layer_0_tint3: { value: __spcColor("#22d3ee") },
526    u_layer_0_scale: { value: 3 },
527    u_layer_0_speed: { value: 0.6 },
528    u_layer_0_viscosity: { value: 0.85 },
529    u_layer_0_intensity: { value: 1 }
530  };
531
532  addShaderTexturePlaceholders(uniforms);
533
534
535
536// Pointer state (mirrors app/lib/shaders/pointer-state.ts)
537const __spcPointer = (() => {
538  const TRAIL = 8;
539  const SAMPLE = 0.04;
540  const clamp = (v, a, b) => (v < a ? a : v > b ? b : v);
541  let tx = 0.5, ty = 0.5, x = 0.5, y = 0.5, vx = 0, vy = 0;
542  let strength = 0, active = false, trailClock = 0;
543  let clickX = 0.5, clickY = 0.5, lastClickTime = -10;
544  const trail = new Array(TRAIL * 2).fill(0.5);
545  const follow = 0.18, decay = 2.5;
546  return {
547    trail,
548    get x() { return x; }, get y() { return y; },
549    get vx() { return vx; }, get vy() { return vy; },
550    get strength() { return strength; },
551    get clickX() { return clickX; }, get clickY() { return clickY; },
552    get lastClickTime() { return lastClickTime; },
553    setTarget(nx, ny) { tx = nx; ty = ny; active = true; },
554    setAbsent() { active = false; },
555    setClick(nx, ny, elapsed) { clickX = nx; clickY = ny; lastClickTime = elapsed; },
556    update(dt) {
557      const step = Math.min(Math.max(dt, 0), 0.1);
558      const k = follow >= 1 ? 1 : follow <= 0 ? 0 : 1 - Math.pow(1 - follow, step * 60);
559      const nx = x + (tx - x) * k, ny = y + (ty - y) * k;
560      if (step > 0) {
561        vx += (clamp((nx - x) / step, -1, 1) - vx) * k;
562        vy += (clamp((ny - y) / step, -1, 1) - vy) * k;
563      }
564      x = nx; y = ny;
565      strength += ((active ? 1 : 0) - strength) * Math.min(1, decay * step);
566      trailClock += step;
567      if (trailClock >= SAMPLE) {
568        trailClock = 0;
569        trail.pop(); trail.pop(); trail.unshift(x, y);
570      }
571    },
572    /** Attach pointer listeners to an element; returns a teardown function. */
573    attach(el) {
574      const move = (e) => {
575        const r = el.getBoundingClientRect();
576        if (!r.width || !r.height) return;
577        __spcPointer.setTarget((e.clientX - r.left) / r.width, 1 - (e.clientY - r.top) / r.height);
578      };
579      const leave = () => __spcPointer.setAbsent();
580      const down = (e) => {
581        const r = el.getBoundingClientRect();
582        if (!r.width || !r.height) return;
583        __spcPointer.setClick((e.clientX - r.left) / r.width, 1 - (e.clientY - r.top) / r.height, performance.now() / 1000);
584      };
585      el.addEventListener('pointermove', move, { passive: true });
586      el.addEventListener('pointerleave', leave, { passive: true });
587      el.addEventListener('pointerdown', down, { passive: true });
588      return () => {
589        el.removeEventListener('pointermove', move);
590        el.removeEventListener('pointerleave', leave);
591        el.removeEventListener('pointerdown', down);
592      };
593    },
594  };
595})();
596  const detachPointer = __spcPointer.attach(canvas);
597
598  // Scene
599  scene = new THREE.Scene();
600
601  // Camera
602  camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1);
603
604  // Renderer
605  // alpha + premultipliedAlpha:false so a shader authored with a transparent
606  // background composites over whatever sits behind the canvas. A shader with
607  // an opaque background writes alpha 1 everywhere and is unaffected.
608  renderer = new THREE.WebGLRenderer({ canvas, antialias: true, alpha: true, premultipliedAlpha: false });
609  renderer.setClearColor(0x000000, 0);
610  
611  // Geometry
612  const geometry = new THREE.PlaneGeometry(2, 2);
613
614  // Material
615  // NoBlending: the fragment shader has already composited every layer and emits
616  // straight (un-premultiplied) alpha, so THREE must write it through untouched.
617  // Blending it again against the cleared buffer would multiply colour by alpha a
618  // second time and darken every semi-transparent pixel.
619  material = new THREE.ShaderMaterial({
620    vertexShader,
621    fragmentShader,
622    uniforms,
623    blending: THREE.NoBlending,
624  });
625  loadShaderTextures(material);
626
627  // Mesh
628  const mesh = new THREE.Mesh(geometry, material);
629  scene.add(mesh);
630  
631  // Resize handler
632  const resizeObserver = new ResizeObserver(entries => {
633    for (let entry of entries) {
634      const { width, height } = entry.contentRect;
635      renderer.setSize(width, height, false);
636      uniforms.u_resolution.value.set(width, height);
637    }
638  });
639  resizeObserver.observe(canvas);
640
641  // Animation loop
642  let lastTime = performance.now();
643
644  function animate() {
645    animationFrameId = requestAnimationFrame(animate);
646    const now = performance.now();
647    const delta = (now - lastTime) / 1000;
648    lastTime = now;
649
650    uniforms.u_time.value += delta;
651    syncCssVars();
652    uniforms.u_scrollVelocity.value *= 0.92; // decay scroll velocity toward 0
653    __spcPointer.update(delta);
654    if (uniforms.u_mouse) uniforms.u_mouse.value.set(__spcPointer.x, __spcPointer.y);
655    if (uniforms.u_mouseVelocity) uniforms.u_mouseVelocity.value.set(__spcPointer.vx, __spcPointer.vy);
656    if (uniforms.u_mouseStrength) uniforms.u_mouseStrength.value = __spcPointer.strength;
657    if (uniforms.u_mouseTrail) {
658      const t = uniforms.u_mouseTrail.value;
659      for (let i = 0; i < t.length; i++) t[i].set(__spcPointer.trail[i * 2], __spcPointer.trail[i * 2 + 1]);
660    }
661    if (uniforms.u_clickPosition) uniforms.u_clickPosition.value.set(__spcPointer.clickX, __spcPointer.clickY);
662    if (uniforms.u_lastClickTime) uniforms.u_lastClickTime.value = __spcPointer.last
662ClickTime;
663    renderer.render(scene, camera);
664  }
665  animate();
666
667  // --- Frontend integration: CSS variable binding, scroll velocity, gyroscope, click ripple ---
668  function syncCssVars() {
669    if (!props.cssVars) return;
670    const el = canvas.parentElement || canvas;
671    const styles = getComputedStyle(el);
672    for (const uniformName in props.cssVars) {
673      const cssVar = props.cssVars[uniformName];
674      const raw = styles.getPropertyValue(cssVar).trim();
675      if (!raw || !uniforms[uniformName]) continue;
676      if (raw[0] === '#') {
677        uniforms[uniformName].value = __spcColor(raw, uniforms[uniformName].value);
678      } else {
679        const num = Number(raw);
680        if (Number.isFinite(num)) uniforms[uniformName].value = num;
681      }
682    }
683  }
684  syncCssVars();
685
686  let lastScrollY = window.scrollY;
687  let lastScrollTime = performance.now();
688  const handleScrollVelocity = () => {
689    const now = performance.now();
690    const v = (window.scrollY - lastScrollY) / Math.max(now - lastScrollTime, 1.0);
691    lastScrollY = window.scrollY;
692    lastScrollTime = now;
693    uniforms.u_scrollVelocity.value = Math.max(-1.0, Math.min(1.0, v * 0.2));
694  };
695  window.addEventListener('scroll', handleScrollVelocity, { passive: true });
696
697  const handleDeviceOrientation = (event) => {
698    const gamma = Number(event.gamma) || 0;
699    const beta = Number(event.beta) || 0;
700    const alpha = Number(event.alpha) || 0;
701    uniforms.u_gyro.value.set(
702      Math.max(-1.0, Math.min(1.0, gamma / 45.0)),
703      Math.max(-1.0, Math.min(1.0, beta / 45.0)),
704      Math.max(-1.0, Math.min(1.0, (alpha - 180.0) / 180.0))
705    );
706  };
707  window.addEventListener('deviceorientation', handleDeviceOrientation);
708
709  const handleClick = (event) => {
710    const rect = canvas.getBoundingClientRect();
711    uniforms.u_clickPosition.value.set(
712      (event.clientX - rect.left) / Math.max(rect.width, 1),
713      1.0 - (event.clientY - rect.top) / Math.max(rect.height, 1)
714    );
715    uniforms.u_lastClickTime.value = uniforms.u_time.value;
716  };
717  canvas.addEventListener('click', handleClick);
718
719  const visibilityObserver = new IntersectionObserver((entries) => {
720    const entry = entries[0];
721    if (!entry) return;
722    if (entry.isIntersecting && animationFrameId === 0) {
723      lastTime = performance.now();
724      animate();
725    } else if (!entry.isIntersecting) {
726      cancelAnimationFrame(animationFrameId);
727      animationFrameId = 0;
728    }
729  }, { threshold: 0 });
730  visibilityObserver.observe(canvas);
731
732  onUnmounted(() => {
733    cancelAnimationFrame(animationFrameId);
734    detachPointer();
735    window.removeEventListener('scroll', handleScrollVelocity);
736    window.removeEventListener('deviceorientation', handleDeviceOrientation);
737    canvas.removeEventListener('click', handleClick);
738    if (visibilityObserver) visibilityObserver.disconnect();
739    resizeObserver.disconnect();
740  disposeShaderTextures(material);
741    if (renderer) renderer.dispose();
742    if (geometry) geometry.dispose();
743    if (material) material.dispose();
744  });
745});
746
747/*
748  TIMELINE ANIMATION DATA (JSON)
749  ==============================
750  No timeline data
751    
752  To use this timeline with GSAP:
753  
754  import gsap from 'gsap';
755  
756  const timeline = gsap.timeline({ 
757    repeat: 0, 
758    timeScale: 1 
759  });
760  
761  // Example: Animate uniform values
762  // No tracks defined
763*/
764<\/script>
765
766<style scoped>
767.shader-canvas-mu7j1h {
768  width: 100%;
769  height: 100%;
770  display: block;
771}
772</style>`;export{e as default};

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.