uniform float uTime; uniform float uProgress; uniform float uMouseX; uniform float uMouseY; uniform float uPixelRatio; attribute vec3 aOrigin; attribute vec3 aTarget; attribute float aSize; attribute float aColorMix; varying float vColorMix; varying float vAlpha; void main() { vColorMix = aColorMix; // Lerp between scattered origin and letter target float progress = smoothstep(0.0, 1.0, uProgress); vec3 pos = mix(aOrigin, aTarget, progress); // Idle turbulence on non-formed particles float turbulence = 1.0 - progress; pos.x += sin(uTime * 0.8 + aOrigin.y * 1.5) * 0.08 * turbulence; pos.y += cos(uTime * 0.6 + aOrigin.x * 1.5) * 0.06 * turbulence; pos.z += sin(uTime * 0.4 + aOrigin.z * 2.0) * 0.04 * turbulence; // Mouse repulsion (on formed state) vec2 mouse = vec2(uMouseX, uMouseY); vec2 diff = pos.xy - mouse * 3.0; float dist = length(diff); float repulsion = smoothstep(1.0, 0.0, dist) * progress * 0.3; pos.xy += normalize(diff) * repulsion; // Gentle formation breathing float breathe = sin(uTime * 1.2 + aColorMix * 6.28) * 0.008 * progress; pos += aTarget * breathe; vec4 mvPosition = modelViewMatrix * vec4(pos, 1.0); gl_Position = projectionMatrix * mvPosition; // Point size with distance attenuation float baseSize = aSize * (1.0 + progress * 0.5); gl_PointSize = baseSize * uPixelRatio * (200.0 / -mvPosition.z); vAlpha = 0.3 + progress * 0.7; }