File size: 2,986 Bytes
e6a1f55
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
import { useEffect, useRef } from "react";

/** Full-screen animated SVG sine waves morphing between clean & corrupted forms. */
export default function WaveformBackground() {
  const ref = useRef<SVGSVGElement | null>(null);

  useEffect(() => {
    const svg = ref.current;
    if (!svg) return;
    let frame = 0;
    let raf = 0;

    const paths = svg.querySelectorAll<SVGPathElement>("path[data-wave]");

    const buildPath = (
      width: number,
      height: number,
      amp: number,
      freq: number,
      noise: number,
      phase: number
    ) => {
      const points: string[] = [];
      const step = 6;
      for (let x = 0; x <= width; x += step) {
        const base = Math.sin((x / width) * Math.PI * 2 * freq + phase) * amp;
        const jitter =
          noise *
          (Math.sin((x / width) * Math.PI * 2 * (freq * 4.7) + phase * 1.3) * 0.5 +
            Math.sin((x / width) * Math.PI * 2 * (freq * 11.3) + phase * 0.7) * 0.5);
        const y = height / 2 + base + jitter;
        points.push(`${x},${y.toFixed(2)}`);
      }
      return `M${points.join(" L")}`;
    };

    const tick = () => {
      frame += 1;
      const w = svg.viewBox.baseVal.width || 1600;
      const h = svg.viewBox.baseVal.height || 600;
      const t = frame / 60;
      paths.forEach((p, i) => {
        const layer = i + 1;
        const noiseRamp = 4 + 8 * Math.sin(t * 0.25 + layer * 0.7); // morph clean ↔ corrupted
        const d = buildPath(
          w,
          h,
          18 + layer * 6,
          1.3 + layer * 0.4,
          Math.max(0, noiseRamp),
          t * (0.4 + layer * 0.15)
        );
        p.setAttribute("d", d);
      });
      raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, []);

  return (
    <div className="pointer-events-none fixed inset-0 z-0 overflow-hidden">
      {/* Faint grid */}
      <div className="absolute inset-0 grid-bg opacity-[0.45]" />

      {/* Animated waves */}
      <svg
        ref={ref}
        className="absolute inset-0 h-full w-full"
        viewBox="0 0 1600 600"
        preserveAspectRatio="none"
        aria-hidden
      >
        <defs>
          <linearGradient id="waveGrad" x1="0" y1="0" x2="1" y2="0">
            <stop offset="0%" stopColor="rgb(var(--cyber))" stopOpacity="0.04" />
            <stop offset="50%" stopColor="rgb(var(--cyber))" stopOpacity="0.18" />
            <stop offset="100%" stopColor="rgb(var(--danger))" stopOpacity="0.08" />
          </linearGradient>
        </defs>
        <path data-wave stroke="url(#waveGrad)" strokeWidth="1.2" fill="none" />
        <path data-wave stroke="url(#waveGrad)" strokeWidth="1" fill="none" />
        <path data-wave stroke="url(#waveGrad)" strokeWidth="0.8" fill="none" />
      </svg>

      {/* Soft scan-line vignette */}
      <div className="absolute inset-0 bg-gradient-to-b from-transparent via-transparent to-bg/60" />
    </div>
  );
}