https://motioncanvas.online/preview/retro-topographic-grid
Scroll Down

Loading Canvas Shader...

Next-Gen Scroll Mechanics

Experience Beautiful Ambient Depth

This browser window is simulating a production webpage. As you scroll down, the ambient Retro Topographic Grid background dynamically zooms and pulls focus in real-time, creating beautiful, cinematic parallax depth.

Built for Ultra Performance

Modern web layouts require buttery-smooth animations. Our styles run completely on the GPU, avoiding CPU reflow and main-thread layout jank.

Focus Pull Parallax

Scroll depth controls blur intensity and lens scaling simultaneously to guide focus elegantly.

GPU-Accelerated

Uses hardware transforms and native filters, optimized for stable 120fps scrolling.

Highly Customizable

Adjust speed, maximum blur limit, zoom multipliers, and filters in real-time.

© 2026 MOTIONCANVAS. ALL RIGHTS RESERVED.ACTIVE BACKGROUND: RETRO TOPOGRAPHIC GRID

Retro Topographic Grid

🟠 WebGL

A gorgeous 3D wireframe topographic landscape under a neon retro sun. Features a procedural Web Audio synthesizer playing an interactive, heavy-bass ambient beat that drives real-time landscape morphing, pulse lighting, and camera vibrations.

#WebGL#3D#wireframe#interactive#synthesizer#audio-reactive
Advertisement
Sponsor Advertisement
728 × 90 | Responsive Banner

Responsive Horizontal Leaderboard

Slot ID: ca-pub-detail-below-preview

Google AdSense Placeholder

Interactive Settings

Motion PresetsCustom Tuning
Animation Speed1.0x
Blur (Aesthetic diffusion)0px
Layer Opacity100%
Scale Zoom1.0x
Rotation Angle0°
Advertisement
Sponsor Advertisement
300 × 250 | Rectangle

Medium Rectangle Block

Slot ID: ca-pub-detail-after-customization

Google AdSense Placeholder

Quick Copy Actions

Download File Packages

Keyboard Shortcuts

Space Play/Pause
F Fullscreen
R Reset Slider
C Copy Code
Advertisement
Sponsor Advertisement
728 × 90 | Responsive Banner

Responsive Horizontal Leaderboard

Slot ID: ca-pub-detail-before-related

Google AdSense Placeholder

More in WebGL

Retro Topographic Grid - WebGL 3D GPU WebGL background for React & Tailwind

Integrate the Retro Topographic Grid directly into your website. This asset is rendered using raw WebGL shader context. It is optimized for zero layout-shifts and runs with high-performance hardware-accelerated processing.

Performance Specifications

  • Render Mode: WEBGL (WebGL 3D GPU)
  • Fluidity: Locked at 60fps dynamic loop
  • Bundle Footprint: Zero external NPM dependencies
  • SEO Indexing status: 100% Crawlable static semantic HTML

🛠️ Integration Capabilities

Our templates expose inline design tokens like --color-1 and --color-2 for infinite color palettes. This template is designed to fit inside hero elements, full-screen landing pages, and interactive presentation cards.

Technical Code Reference & Syntaxes for Googlebot & Crawlers

The snippets below display the direct, unminified source code utilized for rendering this background.

HTML & Inline CSS Snippet (Vanilla)

<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Retro Topographic Grid</title>
  <style>
    html, body {
      margin: 0;
      padding: 0;
      width: 100%;
      height: 100%;
      overflow: hidden;
      background: #09090b;
    }
    
    .retro-topo-container {
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      overflow: hidden;
    }
    #canvas-webgl-retro-topo {
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      border: none;
    }
    .audio-control-panel {
      position: absolute;
      bottom: 24px;
      right: 24px;
      z-index: 50;
      pointer-events: auto;
    }
    .audio-btn {
      background: rgba(11, 11, 15, 0.75);
      border: 1px solid rgba(255, 0, 128, 0.35);
      box-shadow: 0 4px 20px rgba(255, 0, 128, 0.15);
      border-radius: 12px;
      color: #fff;
      padding: 10px 16px;
      font-family: monospace;
      font-size: 10px;
      font-weight: 800;
      letter-spacing: 0.1em;
      cursor: pointer;
      display: flex;
      align-items: center;
      gap: 10px;
      transition: all 0.3s cubic-bezier(0.16, 1, 0.3, 1);
      backdrop-filter: blur(8px);
    }
    .audio-btn:hover {
      background: rgba(255, 0, 128, 0.15);
      border-color: rgba(255, 0, 128, 0.8);
      box-shadow: 0 4px 25px rgba(255, 0, 128, 0.3);
      transform: translateY(-2px);
    }
    .audio-btn:active {
      transform: translateY(0);
    }
    .audio-btn.active {
      border-color: rgba(0, 240, 255, 0.85);
      box-shadow: 0 4px 25px rgba(0, 240, 255, 0.35);
    }
    .audio-btn.active:hover {
      background: rgba(0, 240, 255, 0.15);
    }
  </style>
</head>
<body>

  <div class="retro-topo-container">
    <canvas id="canvas-webgl-retro-topo"></canvas>
    <div class="audio-control-panel">
      <button id="toggle-topo-audio" class="audio-btn">
        <span class="btn-icon">🔇</span>
        <span id="btn-text" class="btn-text">MUTED - CLICK TO START RETRO BEAT</span>
      </button>
    </div>
  </div>
  <script>
  (function() {
    const canvas = document.getElementById('canvas-webgl-retro-topo');
    const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
    if (!gl) return;
  
    const vertices = new Float32Array([
      -1, -1,  1, -1, -1,  1,
      -1,  1,  1, -1,  1,  1
    ]);
  
    const buffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
    gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
  
    function createShader(gl, type, source) {
      const shader = gl.createShader(type);
      gl.shaderSource(shader, source);
      gl.compileShader(shader);
      if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
        console.error(gl.getShaderInfoLog(shader));
        gl.deleteShader(shader);
        return null;
      }
      return shader;
    }
  
    const vsSource = `
      attribute vec2 position;
      void main() {
        gl_Position = vec4(position, 0.0, 1.0);
      }
    `;
  
    const fsSource = `
      precision mediump float;
      uniform vec2 u_resolution;
      uniform float u_time;
      uniform float u_audio_pulse;
      uniform vec2 u_mouse;
  
      float getHeight(vec2 p) {
        float h = 0.0;
        float t = u_time * 0.15;
        
        float wave1 = sin(p.x * 0.25 + t) * cos(p.y * 0.22 - t * 0.8);
        float wave2 = cos(p.x * 0.55 - t * 1.5) * sin(p.y * 0.45 + t);
        float wave3 = sin(p.x * 1.15 + t * 2.0) * cos(p.y * 1.05 - t * 1.2) * 0.35;
        
        h = wave1 * 1.5 + wave2 * 0.75 + wave3 * 0.2;
        h += abs(sin(p.x * 0.15) * cos(p.y * 0.15)) * 1.8;
        
        float pulseMod = 1.0 + u_audio_pulse * 1.6;
        return h * pulseMod - 0.4;
      }
  
      vec3 raymarch(vec3 ro, vec3 rd, out float hitDist) {
        float t = 0.01;
        float max_dist = 28.0;
        for (int i = 0; i < 64; i++) {
          vec3 p = ro + rd * t;
          float h = getHeight(p.xz);
          if (p.y <= h) {
            hitDist = t;
            return p;
          }
          t += max(0.06, (p.y - h) * 0.45);
          if (t > max_dist) break;
        }
        hitDist = -1.0;
        return vec3(0.0);
      }
  
      void main() {
        vec2 p = (gl_FragCoord.xy - 0.5 * u_resolution) / u_resolution.y;
        
        vec2 shake = vec2(0.0);
        if (u_audio_pulse > 0.01) {
          shake = vec2(sin(u_time * 75.0), cos(u_time * 65.0)) * u_audio_pulse * 0.004;
        }
        p += shake;
  
        vec3 ro = vec3(0.0, 3.6, u_time * 0.9);
        vec2 m = u_mouse / u_resolution - 0.5;
        ro.x += m.x * 6.0;
        ro.y += m.y * 2.5;
        
        float floorH = getHeight(ro.xz);
        if (ro.y < floorH + 1.1) {
          ro.y = floorH + 1.1;
        }
        
        vec3 rd = normalize(vec3(p.x, p.y - 0.12, 0.85));
        
        float pitch = -0.3 + m.y * 0.12;
        float cp = cos(pitch), sp = sin(pitch);
        vec3 rdp = vec3(rd.x, rd.y * cp - rd.z * sp, rd.y * sp + rd.z * cp);
        
        float yaw = m.x * 0.3;
        float cy = cos(yaw), sy = sin(yaw);
        vec3 rdy = vec3(rdp.x * cy - rdp.z * sy, rdp.y, rdp.x * sy + rdp.z * cy);
        rd = normalize(rdy);
        
        float hitDist;
        vec3 hitPos = raymarch(ro, rd, hitDist);
        
        vec3 color = vec3(0.003, 0.002, 0.006);
        
        if (hitDist > 0.0) {
          float gridFreq = 1.2;
          vec2 gridPos = hitPos.xz * gridFreq;
          
          float w = 0.02 + hitDist * 0.009;
          
          float gx = abs(fract(gridPos.x - 0.5) - 0.5);
          float gz = abs(fract(gridPos.y - 0.5) - 0.5);
          float gridX = smoothstep(w, 0.0, gx);
          float gridZ = smoothstep(w, 0.0, gz);
          float gridFactor = max(gridX, gridZ);
          
          float contourFreq = 2.0;
          float cy = abs(fract(hitPos.y * contourFreq - 0.5) - 0.5);
          float contourFactor = smoothstep(w * 0.6, 0.0, cy);
          
          float lineGlow = max(gridFactor, contourFactor * 0.8);
          float fog = exp(-hitDist * 0.08);
          
          float heightRel = (hitPos.y + 0.8) / 3.0;
          vec3 lowColor = vec3(0.0, 0.45, 1.0);
          vec3 highColor = vec3(1.0, 0.0, 0.5);
          
          vec3 gridColor = mix(lowColor, highColor, clamp(heightRel + sin(u_time * 0.4) * 0.15 + u_audio_pulse * 0.35, 0.0, 1.0));
          gridColor += vec3(0.12, 0.18, 0.35) * u_audio_pulse;
          
          color = mix(color, gridColor, lineGlow * fog);
          
          float peakSheen = smoothstep(0.4, 2.0, hitPos.y) * 0.12 * fog;
          color += vec3(0.0, 0.85, 1.0) * peakSheen;
        } else {
          float horizonGlow = smoothstep(0.15, -0.22, rd.y) * 0.28;
          color += vec3(0.45, 0.0, 0.75) * horizonGlow;
          
          vec3 sunPos = normalize(vec3(0.0, 0.04, 1.0));
          float dotSun = dot(rd, sunPos);
          if (dotSun > 0.0) {
            float sunRadius = 0.17;
            float sunGlow = exp(-(1.0 - dotSun) * 55.0);
            float sunMask = smoothstep(sunRadius + 0.008, sunRadius, 1.0 - dotSun);
            
            float stripes = step(0.16, fract(rd.y * 38.0));
            sunMask *= mix(1.0, stripes, smoothstep(-0.06, 0.08, rd.y));
            
            vec3 sunColor = mix(vec3(1.0, 0.0, 0.45), vec3(1.0, 0.75, 0.0), rd.y * 4.5 + 0.25);
            color += sunColor * (sunMask + sunGlow * 0.35);
          }
        }
        
        float scanline = sin(gl_FragCoord.y * 2.2) * 0.025;
        color -= vec3(scanline);
        
        gl_FragColor = vec4(color, 1.0);
      }
    `;
  
    const vs = createShader(gl, gl.VERTEX_SHADER, vsSource);
    const fs = createShader(gl, gl.FRAGMENT_SHADER, fsSource);
    if (!vs || !fs) return;
  
    const program = gl.createProgram();
    gl.attachShader(program, vs);
    gl.attachShader(program, fs);
    gl.linkProgram(program);
    if (!gl.getProgramParameter(program, gl.LINK_STATUS)) return;
  
    gl.useProgram(program);
  
    const positionLoc = gl.getAttribLocation(program, 'position');
    gl.enableVertexAttribArray(positionLoc);
    gl.vertexAttribPointer(positionLoc, 2, gl.FLOAT, false, 0, 0);
  
    const uResolution = gl.getUniformLocation(program, 'u_resolution');
    const uTime = gl.getUniformLocation(program, 'u_time');
    const uAudioPulse = gl.getUniformLocation(program, 'u_audio_pulse');
    const uMouse = gl.getUniformLocation(program, 'u_mouse');
  
    let mouseX = 0, mouseY = 0;
    window.addEventListener('mousemove', (e) => {
      const rect = canvas.getBoundingClientRect();
      mouseX = e.clientX - rect.left;
      mouseY = rect.height - (e.clientY - rect.top);
    });
  
    // Web Audio Synthesizer Logic
    let audioCtx = null;
    let analyser = null;
    let dataArray = null;
    let synthInterval = null;
    let isPlaying = false;
    let masterGain = null;
    let step = 0;
  
    function initAudio() {
      const AudioContext = window.AudioContext || window.webkitAudioContext;
      audioCtx = new AudioContext();
      analyser = audioCtx.createAnalyser();
      analyser.fftSize = 32;
      dataArray = new Uint8Array(analyser.frequencyBinCount);
      
      masterGain = audioCtx.createGain();
      masterGain.gain.setValueAtTime(0.0, audioCtx.currentTime);
      
      masterGain.connect(analyser);
      analyser.connect(audioCtx.destination);
      
      // Smooth transition in gain
      masterGain.gain.linearRampToValueAtTime(1.0, audioCtx.currentTime + 1.2);
    }
  
    function playSynthwave() {
      if (synthInterval) clearInterval(synthInterval);
      
      synthInterval = setInterval(() => {
        if (!isPlaying || !audioCtx) return;
        if (audioCtx.state === 'suspended') return;
        
        const now = audioCtx.currentTime;
        
        // KICK DRUM on even beats
        if (step % 2 === 0) {
          const osc = audioCtx.createOscillator();
          const gain = audioCtx.createGain();
          
          osc.type = 'sine';
          osc.frequency.setValueAtTime(125, now);
          osc.frequency.exponentialRampToValueAtTime(0.01, now + 0.28);
          
          gain.gain.setValueAtTime(0.85, now);
          gain.gain.linearRampToValueAtTime(0.01, now + 0.25);
          
          osc.connect(gain);
          gain.connect(masterGain);
          
          osc.start(now);
          osc.stop(now + 0.28);
        }
        
        // SYNTH RESONANT BASSLINE
        const notes = [55.0, 55.0, 65.4, 48.9, 58.2, 58.2, 65.4, 48.9];
        const currentNote = notes[step % notes.length];
        
        const bassOsc = audioCtx.createOscillator();
        const bassFilter = audioCtx.createBiquadFilter();
        const bassGain = audioCtx.createGain();
        
        bassOsc.type = 'sawtooth';
        bassOsc.frequency.setValueAtTime(currentNote, now);
        
        bassFilter.type = 'lowpass';
        bassFilter.frequency.setValueAtTime(280, now);
        bassFilter.Q.setValueAtTime(6.0, now);
        bassFilter.frequency.exponentialRampToValueAtTime(950, now + 0.08);
        bassFilter.frequency.exponentialRampToValueAtTime(180, now + 0.38);
        
        bassGain.gain.setValueAtTime(0.14, now);
        bassGain.gain.linearRampToValueAtTime(0.01, now + 0.42);
        
        bassOsc.connect(bassFilter);
        bassFilter.connect(bassGain);
        bassGain.connect(masterGain);
        
        bassOsc.start(now);
        bassOsc.stop(now + 0.42);
        
        step = (step + 1) % 8;
      }, 380);
    }
  
    const audioToggleBtn = document.getElementById('toggle-topo-audio');
    const btnText = document.getElementById('btn-text');
    const btnIcon = audioToggleBtn.querySelector('.btn-icon');
  
    audioToggleBtn.addEventListener('click', async () => {
      if (!audioCtx) {
        initAudio();
      }
      
      if (audioCtx.state === 'suspended') {
        await audioCtx.resume();
      }
      
      if (!isPlaying) {
        isPlaying = true;
        playSynthwave();
        btnText.innerText = "BEAT ACTIVE - CLICK TO MUTE";
        btnIcon.innerText = "🔊";
        audioToggleBtn.classList.add('active');
      } else {
        isPlaying = false;
        if (synthInterval) {
          clearInterval(synthInterval);
          synthInterval = null;
        }
        btnText.innerText = "MUTED - CLICK TO START RETRO BEAT";
        btnIcon.innerText = "🔇";
        audioToggleBtn.classList.remove('active');
      }
    });
  
    function resize() {
      const rect = canvas.parentNode ? canvas.parentNode.getBoundingClientRect() : null;
      const width = rect ? rect.width : window.innerWidth;
      const height = rect ? rect.height : window.innerHeight;
      if (canvas.width !== width || canvas.height !== height) {
        canvas.width = width;
        canvas.height = height;
        gl.viewport(0, 0, width, height);
      }
    }
    window.addEventListener('resize', resize);
    
    if (canvas.parentNode) {
      const observer = new MutationObserver(resize);
      observer.observe(canvas.parentNode, { attributes: true });
    }
  
    resize();
  
    let startTime = Date.now();
    let audioPulse = 0.0;
  
    function render() {
      const now = Date.now();
      let elapsed = (now - startTime) / 1000.0;
      
      if (analyser && isPlaying) {
        analyser.getByteFrequencyData(dataArray);
        let sum = 0;
        for (let i = 0; i < dataArray.length; i++) {
          sum += dataArray[i];
        }
        const average = sum / dataArray.length;
        audioPulse = average / 255.0;
      } else {
        audioPulse = (Math.sin(elapsed * 2.8) * 0.5 + 0.5) * 0.07;
      }
  
      gl.uniform2f(uResolution, canvas.width, canvas.height);
      gl.uniform1f(uTime, elapsed);
      gl.uniform1f(uAudioPulse, audioPulse);
      gl.uniform2f(uMouse, mouseX, mouseY);
  
      gl.drawArrays(gl.TRIANGLES, 0, 6);
      requestAnimationFrame(render);
    }
    requestAnimationFrame(render);
  
    window.addEventListener('beforeunload', () => {
      if (synthInterval) clearInterval(synthInterval);
      if (audioCtx) audioCtx.close();
    });
  })();
  </script>

  
</body>
</html>

React Component Wrapper (TSX)

import React from 'react';

export default function RetroTopographicGridBackground() {
  

  return (
    <div 
       
      style={{ width: '100%', height: '100%', position: 'relative', overflow: 'hidden' }}
    >
      <style dangerouslySetInnerHTML={{ __html: `
        .retro-topo-container {
          position: absolute;
          top: 0;
          left: 0;
          width: 100%;
          height: 100%;
          overflow: hidden;
        }
        #canvas-webgl-retro-topo {
          position: absolute;
          top: 0;
          left: 0;
          width: 100%;
          height: 100%;
          border: none;
        }
        .audio-control-panel {
          position: absolute;
          bottom: 24px;
          right: 24px;
          z-index: 50;
          pointer-events: auto;
        }
        .audio-btn {
          background: rgba(11, 11, 15, 0.75);
          border: 1px solid rgba(255, 0, 128, 0.35);
          box-shadow: 0 4px 20px rgba(255, 0, 128, 0.15);
          border-radius: 12px;
          color: #fff;
          padding: 10px 16px;
          font-family: monospace;
          font-size: 10px;
          font-weight: 800;
          letter-spacing: 0.1em;
          cursor: pointer;
          display: flex;
          align-items: center;
          gap: 10px;
          transition: all 0.3s cubic-bezier(0.16, 1, 0.3, 1);
          backdrop-filter: blur(8px);
        }
        .audio-btn:hover {
          background: rgba(255, 0, 128, 0.15);
          border-color: rgba(255, 0, 128, 0.8);
          box-shadow: 0 4px 25px rgba(255, 0, 128, 0.3);
          transform: translateY(-2px);
        }
        .audio-btn:active {
          transform: translateY(0);
        }
        .audio-btn.active {
          border-color: rgba(0, 240, 255, 0.85);
          box-shadow: 0 4px 25px rgba(0, 240, 255, 0.35);
        }
        .audio-btn.active:hover {
          background: rgba(0, 240, 255, 0.15);
        }
      ` }} />
      
      {/* HTML Structure */}
      <div 
        style={{ width: '100%', height: '100%' }}
        dangerouslySetInnerHTML={{ __html: `
          <div class="retro-topo-container">
            <canvas id="canvas-webgl-retro-topo"></canvas>
            <div class="audio-control-panel">
              <button id="toggle-topo-audio" class="audio-btn">
                <span class="btn-icon">🔇</span>
                <span id="btn-text" class="btn-text">MUTED - CLICK TO START RETRO BEAT</span>
              </button>
            </div>
          </div>
          <script>
          (function() {
            const canvas = document.getElementById('canvas-webgl-retro-topo');
            const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
            if (!gl) return;
          
            const vertices = new Float32Array([
              -1, -1,  1, -1, -1,  1,
              -1,  1,  1, -1,  1,  1
            ]);
          
            const buffer = gl.createBuffer();
            gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
            gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
          
            function createShader(gl, type, source) {
              const shader = gl.createShader(type);
              gl.shaderSource(shader, source);
              gl.compileShader(shader);
              if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
                console.error(gl.getShaderInfoLog(shader));
                gl.deleteShader(shader);
                return null;
              }
              return shader;
            }
          
            const vsSource = \`
              attribute vec2 position;
              void main() {
                gl_Position = vec4(position, 0.0, 1.0);
              }
            \`;
          
            const fsSource = \`
              precision mediump float;
              uniform vec2 u_resolution;
              uniform float u_time;
              uniform float u_audio_pulse;
              uniform vec2 u_mouse;
          
              float getHeight(vec2 p) {
                float h = 0.0;
                float t = u_time * 0.15;
                
                float wave1 = sin(p.x * 0.25 + t) * cos(p.y * 0.22 - t * 0.8);
                float wave2 = cos(p.x * 0.55 - t * 1.5) * sin(p.y * 0.45 + t);
                float wave3 = sin(p.x * 1.15 + t * 2.0) * cos(p.y * 1.05 - t * 1.2) * 0.35;
                
                h = wave1 * 1.5 + wave2 * 0.75 + wave3 * 0.2;
                h += abs(sin(p.x * 0.15) * cos(p.y * 0.15)) * 1.8;
                
                float pulseMod = 1.0 + u_audio_pulse * 1.6;
                return h * pulseMod - 0.4;
              }
          
              vec3 raymarch(vec3 ro, vec3 rd, out float hitDist) {
                float t = 0.01;
                float max_dist = 28.0;
                for (int i = 0; i < 64; i++) {
                  vec3 p = ro + rd * t;
                  float h = getHeight(p.xz);
                  if (p.y <= h) {
                    hitDist = t;
                    return p;
                  }
                  t += max(0.06, (p.y - h) * 0.45);
                  if (t > max_dist) break;
                }
                hitDist = -1.0;
                return vec3(0.0);
              }
          
              void main() {
                vec2 p = (gl_FragCoord.xy - 0.5 * u_resolution) / u_resolution.y;
                
                vec2 shake = vec2(0.0);
                if (u_audio_pulse > 0.01) {
                  shake = vec2(sin(u_time * 75.0), cos(u_time * 65.0)) * u_audio_pulse * 0.004;
                }
                p += shake;
          
                vec3 ro = vec3(0.0, 3.6, u_time * 0.9);
                vec2 m = u_mouse / u_resolution - 0.5;
                ro.x += m.x * 6.0;
                ro.y += m.y * 2.5;
                
                float floorH = getHeight(ro.xz);
                if (ro.y < floorH + 1.1) {
                  ro.y = floorH + 1.1;
                }
                
                vec3 rd = normalize(vec3(p.x, p.y - 0.12, 0.85));
                
                float pitch = -0.3 + m.y * 0.12;
                float cp = cos(pitch), sp = sin(pitch);
                vec3 rdp = vec3(rd.x, rd.y * cp - rd.z * sp, rd.y * sp + rd.z * cp);
                
                float yaw = m.x * 0.3;
                float cy = cos(yaw), sy = sin(yaw);
                vec3 rdy = vec3(rdp.x * cy - rdp.z * sy, rdp.y, rdp.x * sy + rdp.z * cy);
                rd = normalize(rdy);
                
                float hitDist;
                vec3 hitPos = raymarch(ro, rd, hitDist);
                
                vec3 color = vec3(0.003, 0.002, 0.006);
                
                if (hitDist > 0.0) {
                  float gridFreq = 1.2;
                  vec2 gridPos = hitPos.xz * gridFreq;
                  
                  float w = 0.02 + hitDist * 0.009;
                  
                  float gx = abs(fract(gridPos.x - 0.5) - 0.5);
                  float gz = abs(fract(gridPos.y - 0.5) - 0.5);
                  float gridX = smoothstep(w, 0.0, gx);
                  float gridZ = smoothstep(w, 0.0, gz);
                  float gridFactor = max(gridX, gridZ);
                  
                  float contourFreq = 2.0;
                  float cy = abs(fract(hitPos.y * contourFreq - 0.5) - 0.5);
                  float contourFactor = smoothstep(w * 0.6, 0.0, cy);
                  
                  float lineGlow = max(gridFactor, contourFactor * 0.8);
                  float fog = exp(-hitDist * 0.08);
                  
                  float heightRel = (hitPos.y + 0.8) / 3.0;
                  vec3 lowColor = vec3(0.0, 0.45, 1.0);
                  vec3 highColor = vec3(1.0, 0.0, 0.5);
                  
                  vec3 gridColor = mix(lowColor, highColor, clamp(heightRel + sin(u_time * 0.4) * 0.15 + u_audio_pulse * 0.35, 0.0, 1.0));
                  gridColor += vec3(0.12, 0.18, 0.35) * u_audio_pulse;
                  
                  color = mix(color, gridColor, lineGlow * fog);
                  
                  float peakSheen = smoothstep(0.4, 2.0, hitPos.y) * 0.12 * fog;
                  color += vec3(0.0, 0.85, 1.0) * peakSheen;
                } else {
                  float horizonGlow = smoothstep(0.15, -0.22, rd.y) * 0.28;
                  color += vec3(0.45, 0.0, 0.75) * horizonGlow;
                  
                  vec3 sunPos = normalize(vec3(0.0, 0.04, 1.0));
                  float dotSun = dot(rd, sunPos);
                  if (dotSun > 0.0) {
                    float sunRadius = 0.17;
                    float sunGlow = exp(-(1.0 - dotSun) * 55.0);
                    float sunMask = smoothstep(sunRadius + 0.008, sunRadius, 1.0 - dotSun);
                    
                    float stripes = step(0.16, fract(rd.y * 38.0));
                    sunMask *= mix(1.0, stripes, smoothstep(-0.06, 0.08, rd.y));
                    
                    vec3 sunColor = mix(vec3(1.0, 0.0, 0.45), vec3(1.0, 0.75, 0.0), rd.y * 4.5 + 0.25);
                    color += sunColor * (sunMask + sunGlow * 0.35);
                  }
                }
                
                float scanline = sin(gl_FragCoord.y * 2.2) * 0.025;
                color -= vec3(scanline);
                
                gl_FragColor = vec4(color, 1.0);
              }
            \`;
          
            const vs = createShader(gl, gl.VERTEX_SHADER, vsSource);
            const fs = createShader(gl, gl.FRAGMENT_SHADER, fsSource);
            if (!vs || !fs) return;
          
            const program = gl.createProgram();
            gl.attachShader(program, vs);
            gl.attachShader(program, fs);
            gl.linkProgram(program);
            if (!gl.getProgramParameter(program, gl.LINK_STATUS)) return;
          
            gl.useProgram(program);
          
            const positionLoc = gl.getAttribLocation(program, 'position');
            gl.enableVertexAttribArray(positionLoc);
            gl.vertexAttribPointer(positionLoc, 2, gl.FLOAT, false, 0, 0);
          
            const uResolution = gl.getUniformLocation(program, 'u_resolution');
            const uTime = gl.getUniformLocation(program, 'u_time');
            const uAudioPulse = gl.getUniformLocation(program, 'u_audio_pulse');
            const uMouse = gl.getUniformLocation(program, 'u_mouse');
          
            let mouseX = 0, mouseY = 0;
            window.addEventListener('mousemove', (e) => {
              const rect = canvas.getBoundingClientRect();
              mouseX = e.clientX - rect.left;
              mouseY = rect.height - (e.clientY - rect.top);
            });
          
            // Web Audio Synthesizer Logic
            let audioCtx = null;
            let analyser = null;
            let dataArray = null;
            let synthInterval = null;
            let isPlaying = false;
            let masterGain = null;
            let step = 0;
          
            function initAudio() {
              const AudioContext = window.AudioContext || window.webkitAudioContext;
              audioCtx = new AudioContext();
              analyser = audioCtx.createAnalyser();
              analyser.fftSize = 32;
              dataArray = new Uint8Array(analyser.frequencyBinCount);
              
              masterGain = audioCtx.createGain();
              masterGain.gain.setValueAtTime(0.0, audioCtx.currentTime);
              
              masterGain.connect(analyser);
              analyser.connect(audioCtx.destination);
              
              // Smooth transition in gain
              masterGain.gain.linearRampToValueAtTime(1.0, audioCtx.currentTime + 1.2);
            }
          
            function playSynthwave() {
              if (synthInterval) clearInterval(synthInterval);
              
              synthInterval = setInterval(() => {
                if (!isPlaying || !audioCtx) return;
                if (audioCtx.state === 'suspended') return;
                
                const now = audioCtx.currentTime;
                
                // KICK DRUM on even beats
                if (step % 2 === 0) {
                  const osc = audioCtx.createOscillator();
                  const gain = audioCtx.createGain();
                  
                  osc.type = 'sine';
                  osc.frequency.setValueAtTime(125, now);
                  osc.frequency.exponentialRampToValueAtTime(0.01, now + 0.28);
                  
                  gain.gain.setValueAtTime(0.85, now);
                  gain.gain.linearRampToValueAtTime(0.01, now + 0.25);
                  
                  osc.connect(gain);
                  gain.connect(masterGain);
                  
                  osc.start(now);
                  osc.stop(now + 0.28);
                }
                
                // SYNTH RESONANT BASSLINE
                const notes = [55.0, 55.0, 65.4, 48.9, 58.2, 58.2, 65.4, 48.9];
                const currentNote = notes[step % notes.length];
                
                const bassOsc = audioCtx.createOscillator();
                const bassFilter = audioCtx.createBiquadFilter();
                const bassGain = audioCtx.createGain();
                
                bassOsc.type = 'sawtooth';
                bassOsc.frequency.setValueAtTime(currentNote, now);
                
                bassFilter.type = 'lowpass';
                bassFilter.frequency.setValueAtTime(280, now);
                bassFilter.Q.setValueAtTime(6.0, now);
                bassFilter.frequency.exponentialRampToValueAtTime(950, now + 0.08);
                bassFilter.frequency.exponentialRampToValueAtTime(180, now + 0.38);
                
                bassGain.gain.setValueAtTime(0.14, now);
                bassGain.gain.linearRampToValueAtTime(0.01, now + 0.42);
                
                bassOsc.connect(bassFilter);
                bassFilter.connect(bassGain);
                bassGain.connect(masterGain);
                
                bassOsc.start(now);
                bassOsc.stop(now + 0.42);
                
                step = (step + 1) % 8;
              }, 380);
            }
          
            const audioToggleBtn = document.getElementById('toggle-topo-audio');
            const btnText = document.getElementById('btn-text');
            const btnIcon = audioToggleBtn.querySelector('.btn-icon');
          
            audioToggleBtn.addEventListener('click', async () => {
              if (!audioCtx) {
                initAudio();
              }
              
              if (audioCtx.state === 'suspended') {
                await audioCtx.resume();
              }
              
              if (!isPlaying) {
                isPlaying = true;
                playSynthwave();
                btnText.innerText = "BEAT ACTIVE - CLICK TO MUTE";
                btnIcon.innerText = "🔊";
                audioToggleBtn.classList.add('active');
              } else {
                isPlaying = false;
                if (synthInterval) {
                  clearInterval(synthInterval);
                  synthInterval = null;
                }
                btnText.innerText = "MUTED - CLICK TO START RETRO BEAT";
                btnIcon.innerText = "🔇";
                audioToggleBtn.classList.remove('active');
              }
            });
          
            function resize() {
              const rect = canvas.parentNode ? canvas.parentNode.getBoundingClientRect() : null;
              const width = rect ? rect.width : window.innerWidth;
              const height = rect ? rect.height : window.innerHeight;
              if (canvas.width !== width || canvas.height !== height) {
                canvas.width = width;
                canvas.height = height;
                gl.viewport(0, 0, width, height);
              }
            }
            window.addEventListener('resize', resize);
            
            if (canvas.parentNode) {
              const observer = new MutationObserver(resize);
              observer.observe(canvas.parentNode, { attributes: true });
            }
          
            resize();
          
            let startTime = Date.now();
            let audioPulse = 0.0;
          
            function render() {
              const now = Date.now();
              let elapsed = (now - startTime) / 1000.0;
              
              if (analyser && isPlaying) {
                analyser.getByteFrequencyData(dataArray);
                let sum = 0;
                for (let i = 0; i < dataArray.length; i++) {
                  sum += dataArray[i];
                }
                const average = sum / dataArray.length;
                audioPulse = average / 255.0;
              } else {
                audioPulse = (Math.sin(elapsed * 2.8) * 0.5 + 0.5) * 0.07;
              }
          
              gl.uniform2f(uResolution, canvas.width, canvas.height);
              gl.uniform1f(uTime, elapsed);
              gl.uniform1f(uAudioPulse, audioPulse);
              gl.uniform2f(uMouse, mouseX, mouseY);
          
              gl.drawArrays(gl.TRIANGLES, 0, 6);
              requestAnimationFrame(render);
            }
            requestAnimationFrame(render);
          
            window.addEventListener('beforeunload', () => {
              if (synthInterval) clearInterval(synthInterval);
              if (audioCtx) audioCtx.close();
            });
          })();
          </script>
        ` }}
      />
    </div>
  );
}

Next.js App Router Component (use client)

'use client';

import React from 'react';

export default function RetroTopographicGridBackground() {
  

  return (
    <div 
       
      className="w-full h-full relative overflow-hidden"
    >
      <style dangerouslySetInnerHTML={{ __html: `
        .retro-topo-container {
          position: absolute;
          top: 0;
          left: 0;
          width: 100%;
          height: 100%;
          overflow: hidden;
        }
        #canvas-webgl-retro-topo {
          position: absolute;
          top: 0;
          left: 0;
          width: 100%;
          height: 100%;
          border: none;
        }
        .audio-control-panel {
          position: absolute;
          bottom: 24px;
          right: 24px;
          z-index: 50;
          pointer-events: auto;
        }
        .audio-btn {
          background: rgba(11, 11, 15, 0.75);
          border: 1px solid rgba(255, 0, 128, 0.35);
          box-shadow: 0 4px 20px rgba(255, 0, 128, 0.15);
          border-radius: 12px;
          color: #fff;
          padding: 10px 16px;
          font-family: monospace;
          font-size: 10px;
          font-weight: 800;
          letter-spacing: 0.1em;
          cursor: pointer;
          display: flex;
          align-items: center;
          gap: 10px;
          transition: all 0.3s cubic-bezier(0.16, 1, 0.3, 1);
          backdrop-filter: blur(8px);
        }
        .audio-btn:hover {
          background: rgba(255, 0, 128, 0.15);
          border-color: rgba(255, 0, 128, 0.8);
          box-shadow: 0 4px 25px rgba(255, 0, 128, 0.3);
          transform: translateY(-2px);
        }
        .audio-btn:active {
          transform: translateY(0);
        }
        .audio-btn.active {
          border-color: rgba(0, 240, 255, 0.85);
          box-shadow: 0 4px 25px rgba(0, 240, 255, 0.35);
        }
        .audio-btn.active:hover {
          background: rgba(0, 240, 255, 0.15);
        }
      ` }} />
      
      {/* HTML Structure */}
      <div 
        className="w-full h-full"
        dangerouslySetInnerHTML={{ __html: `
          <div class="retro-topo-container">
            <canvas id="canvas-webgl-retro-topo"></canvas>
            <div class="audio-control-panel">
              <button id="toggle-topo-audio" class="audio-btn">
                <span class="btn-icon">🔇</span>
                <span id="btn-text" class="btn-text">MUTED - CLICK TO START RETRO BEAT</span>
              </button>
            </div>
          </div>
          <script>
          (function() {
            const canvas = document.getElementById('canvas-webgl-retro-topo');
            const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
            if (!gl) return;
          
            const vertices = new Float32Array([
              -1, -1,  1, -1, -1,  1,
              -1,  1,  1, -1,  1,  1
            ]);
          
            const buffer = gl.createBuffer();
            gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
            gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
          
            function createShader(gl, type, source) {
              const shader = gl.createShader(type);
              gl.shaderSource(shader, source);
              gl.compileShader(shader);
              if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
                console.error(gl.getShaderInfoLog(shader));
                gl.deleteShader(shader);
                return null;
              }
              return shader;
            }
          
            const vsSource = \`
              attribute vec2 position;
              void main() {
                gl_Position = vec4(position, 0.0, 1.0);
              }
            \`;
          
            const fsSource = \`
              precision mediump float;
              uniform vec2 u_resolution;
              uniform float u_time;
              uniform float u_audio_pulse;
              uniform vec2 u_mouse;
          
              float getHeight(vec2 p) {
                float h = 0.0;
                float t = u_time * 0.15;
                
                float wave1 = sin(p.x * 0.25 + t) * cos(p.y * 0.22 - t * 0.8);
                float wave2 = cos(p.x * 0.55 - t * 1.5) * sin(p.y * 0.45 + t);
                float wave3 = sin(p.x * 1.15 + t * 2.0) * cos(p.y * 1.05 - t * 1.2) * 0.35;
                
                h = wave1 * 1.5 + wave2 * 0.75 + wave3 * 0.2;
                h += abs(sin(p.x * 0.15) * cos(p.y * 0.15)) * 1.8;
                
                float pulseMod = 1.0 + u_audio_pulse * 1.6;
                return h * pulseMod - 0.4;
              }
          
              vec3 raymarch(vec3 ro, vec3 rd, out float hitDist) {
                float t = 0.01;
                float max_dist = 28.0;
                for (int i = 0; i < 64; i++) {
                  vec3 p = ro + rd * t;
                  float h = getHeight(p.xz);
                  if (p.y <= h) {
                    hitDist = t;
                    return p;
                  }
                  t += max(0.06, (p.y - h) * 0.45);
                  if (t > max_dist) break;
                }
                hitDist = -1.0;
                return vec3(0.0);
              }
          
              void main() {
                vec2 p = (gl_FragCoord.xy - 0.5 * u_resolution) / u_resolution.y;
                
                vec2 shake = vec2(0.0);
                if (u_audio_pulse > 0.01) {
                  shake = vec2(sin(u_time * 75.0), cos(u_time * 65.0)) * u_audio_pulse * 0.004;
                }
                p += shake;
          
                vec3 ro = vec3(0.0, 3.6, u_time * 0.9);
                vec2 m = u_mouse / u_resolution - 0.5;
                ro.x += m.x * 6.0;
                ro.y += m.y * 2.5;
                
                float floorH = getHeight(ro.xz);
                if (ro.y < floorH + 1.1) {
                  ro.y = floorH + 1.1;
                }
                
                vec3 rd = normalize(vec3(p.x, p.y - 0.12, 0.85));
                
                float pitch = -0.3 + m.y * 0.12;
                float cp = cos(pitch), sp = sin(pitch);
                vec3 rdp = vec3(rd.x, rd.y * cp - rd.z * sp, rd.y * sp + rd.z * cp);
                
                float yaw = m.x * 0.3;
                float cy = cos(yaw), sy = sin(yaw);
                vec3 rdy = vec3(rdp.x * cy - rdp.z * sy, rdp.y, rdp.x * sy + rdp.z * cy);
                rd = normalize(rdy);
                
                float hitDist;
                vec3 hitPos = raymarch(ro, rd, hitDist);
                
                vec3 color = vec3(0.003, 0.002, 0.006);
                
                if (hitDist > 0.0) {
                  float gridFreq = 1.2;
                  vec2 gridPos = hitPos.xz * gridFreq;
                  
                  float w = 0.02 + hitDist * 0.009;
                  
                  float gx = abs(fract(gridPos.x - 0.5) - 0.5);
                  float gz = abs(fract(gridPos.y - 0.5) - 0.5);
                  float gridX = smoothstep(w, 0.0, gx);
                  float gridZ = smoothstep(w, 0.0, gz);
                  float gridFactor = max(gridX, gridZ);
                  
                  float contourFreq = 2.0;
                  float cy = abs(fract(hitPos.y * contourFreq - 0.5) - 0.5);
                  float contourFactor = smoothstep(w * 0.6, 0.0, cy);
                  
                  float lineGlow = max(gridFactor, contourFactor * 0.8);
                  float fog = exp(-hitDist * 0.08);
                  
                  float heightRel = (hitPos.y + 0.8) / 3.0;
                  vec3 lowColor = vec3(0.0, 0.45, 1.0);
                  vec3 highColor = vec3(1.0, 0.0, 0.5);
                  
                  vec3 gridColor = mix(lowColor, highColor, clamp(heightRel + sin(u_time * 0.4) * 0.15 + u_audio_pulse * 0.35, 0.0, 1.0));
                  gridColor += vec3(0.12, 0.18, 0.35) * u_audio_pulse;
                  
                  color = mix(color, gridColor, lineGlow * fog);
                  
                  float peakSheen = smoothstep(0.4, 2.0, hitPos.y) * 0.12 * fog;
                  color += vec3(0.0, 0.85, 1.0) * peakSheen;
                } else {
                  float horizonGlow = smoothstep(0.15, -0.22, rd.y) * 0.28;
                  color += vec3(0.45, 0.0, 0.75) * horizonGlow;
                  
                  vec3 sunPos = normalize(vec3(0.0, 0.04, 1.0));
                  float dotSun = dot(rd, sunPos);
                  if (dotSun > 0.0) {
                    float sunRadius = 0.17;
                    float sunGlow = exp(-(1.0 - dotSun) * 55.0);
                    float sunMask = smoothstep(sunRadius + 0.008, sunRadius, 1.0 - dotSun);
                    
                    float stripes = step(0.16, fract(rd.y * 38.0));
                    sunMask *= mix(1.0, stripes, smoothstep(-0.06, 0.08, rd.y));
                    
                    vec3 sunColor = mix(vec3(1.0, 0.0, 0.45), vec3(1.0, 0.75, 0.0), rd.y * 4.5 + 0.25);
                    color += sunColor * (sunMask + sunGlow * 0.35);
                  }
                }
                
                float scanline = sin(gl_FragCoord.y * 2.2) * 0.025;
                color -= vec3(scanline);
                
                gl_FragColor = vec4(color, 1.0);
              }
            \`;
          
            const vs = createShader(gl, gl.VERTEX_SHADER, vsSource);
            const fs = createShader(gl, gl.FRAGMENT_SHADER, fsSource);
            if (!vs || !fs) return;
          
            const program = gl.createProgram();
            gl.attachShader(program, vs);
            gl.attachShader(program, fs);
            gl.linkProgram(program);
            if (!gl.getProgramParameter(program, gl.LINK_STATUS)) return;
          
            gl.useProgram(program);
          
            const positionLoc = gl.getAttribLocation(program, 'position');
            gl.enableVertexAttribArray(positionLoc);
            gl.vertexAttribPointer(positionLoc, 2, gl.FLOAT, false, 0, 0);
          
            const uResolution = gl.getUniformLocation(program, 'u_resolution');
            const uTime = gl.getUniformLocation(program, 'u_time');
            const uAudioPulse = gl.getUniformLocation(program, 'u_audio_pulse');
            const uMouse = gl.getUniformLocation(program, 'u_mouse');
          
            let mouseX = 0, mouseY = 0;
            window.addEventListener('mousemove', (e) => {
              const rect = canvas.getBoundingClientRect();
              mouseX = e.clientX - rect.left;
              mouseY = rect.height - (e.clientY - rect.top);
            });
          
            // Web Audio Synthesizer Logic
            let audioCtx = null;
            let analyser = null;
            let dataArray = null;
            let synthInterval = null;
            let isPlaying = false;
            let masterGain = null;
            let step = 0;
          
            function initAudio() {
              const AudioContext = window.AudioContext || window.webkitAudioContext;
              audioCtx = new AudioContext();
              analyser = audioCtx.createAnalyser();
              analyser.fftSize = 32;
              dataArray = new Uint8Array(analyser.frequencyBinCount);
              
              masterGain = audioCtx.createGain();
              masterGain.gain.setValueAtTime(0.0, audioCtx.currentTime);
              
              masterGain.connect(analyser);
              analyser.connect(audioCtx.destination);
              
              // Smooth transition in gain
              masterGain.gain.linearRampToValueAtTime(1.0, audioCtx.currentTime + 1.2);
            }
          
            function playSynthwave() {
              if (synthInterval) clearInterval(synthInterval);
              
              synthInterval = setInterval(() => {
                if (!isPlaying || !audioCtx) return;
                if (audioCtx.state === 'suspended') return;
                
                const now = audioCtx.currentTime;
                
                // KICK DRUM on even beats
                if (step % 2 === 0) {
                  const osc = audioCtx.createOscillator();
                  const gain = audioCtx.createGain();
                  
                  osc.type = 'sine';
                  osc.frequency.setValueAtTime(125, now);
                  osc.frequency.exponentialRampToValueAtTime(0.01, now + 0.28);
                  
                  gain.gain.setValueAtTime(0.85, now);
                  gain.gain.linearRampToValueAtTime(0.01, now + 0.25);
                  
                  osc.connect(gain);
                  gain.connect(masterGain);
                  
                  osc.start(now);
                  osc.stop(now + 0.28);
                }
                
                // SYNTH RESONANT BASSLINE
                const notes = [55.0, 55.0, 65.4, 48.9, 58.2, 58.2, 65.4, 48.9];
                const currentNote = notes[step % notes.length];
                
                const bassOsc = audioCtx.createOscillator();
                const bassFilter = audioCtx.createBiquadFilter();
                const bassGain = audioCtx.createGain();
                
                bassOsc.type = 'sawtooth';
                bassOsc.frequency.setValueAtTime(currentNote, now);
                
                bassFilter.type = 'lowpass';
                bassFilter.frequency.setValueAtTime(280, now);
                bassFilter.Q.setValueAtTime(6.0, now);
                bassFilter.frequency.exponentialRampToValueAtTime(950, now + 0.08);
                bassFilter.frequency.exponentialRampToValueAtTime(180, now + 0.38);
                
                bassGain.gain.setValueAtTime(0.14, now);
                bassGain.gain.linearRampToValueAtTime(0.01, now + 0.42);
                
                bassOsc.connect(bassFilter);
                bassFilter.connect(bassGain);
                bassGain.connect(masterGain);
                
                bassOsc.start(now);
                bassOsc.stop(now + 0.42);
                
                step = (step + 1) % 8;
              }, 380);
            }
          
            const audioToggleBtn = document.getElementById('toggle-topo-audio');
            const btnText = document.getElementById('btn-text');
            const btnIcon = audioToggleBtn.querySelector('.btn-icon');
          
            audioToggleBtn.addEventListener('click', async () => {
              if (!audioCtx) {
                initAudio();
              }
              
              if (audioCtx.state === 'suspended') {
                await audioCtx.resume();
              }
              
              if (!isPlaying) {
                isPlaying = true;
                playSynthwave();
                btnText.innerText = "BEAT ACTIVE - CLICK TO MUTE";
                btnIcon.innerText = "🔊";
                audioToggleBtn.classList.add('active');
              } else {
                isPlaying = false;
                if (synthInterval) {
                  clearInterval(synthInterval);
                  synthInterval = null;
                }
                btnText.innerText = "MUTED - CLICK TO START RETRO BEAT";
                btnIcon.innerText = "🔇";
                audioToggleBtn.classList.remove('active');
              }
            });
          
            function resize() {
              const rect = canvas.parentNode ? canvas.parentNode.getBoundingClientRect() : null;
              const width = rect ? rect.width : window.innerWidth;
              const height = rect ? rect.height : window.innerHeight;
              if (canvas.width !== width || canvas.height !== height) {
                canvas.width = width;
                canvas.height = height;
                gl.viewport(0, 0, width, height);
              }
            }
            window.addEventListener('resize', resize);
            
            if (canvas.parentNode) {
              const observer = new MutationObserver(resize);
              observer.observe(canvas.parentNode, { attributes: true });
            }
          
            resize();
          
            let startTime = Date.now();
            let audioPulse = 0.0;
          
            function render() {
              const now = Date.now();
              let elapsed = (now - startTime) / 1000.0;
              
              if (analyser && isPlaying) {
                analyser.getByteFrequencyData(dataArray);
                let sum = 0;
                for (let i = 0; i < dataArray.length; i++) {
                  sum += dataArray[i];
                }
                const average = sum / dataArray.length;
                audioPulse = average / 255.0;
              } else {
                audioPulse = (Math.sin(elapsed * 2.8) * 0.5 + 0.5) * 0.07;
              }
          
              gl.uniform2f(uResolution, canvas.width, canvas.height);
              gl.uniform1f(uTime, elapsed);
              gl.uniform1f(uAudioPulse, audioPulse);
              gl.uniform2f(uMouse, mouseX, mouseY);
          
              gl.drawArrays(gl.TRIANGLES, 0, 6);
              requestAnimationFrame(render);
            }
            requestAnimationFrame(render);
          
            window.addEventListener('beforeunload', () => {
              if (synthInterval) clearInterval(synthInterval);
              if (audioCtx) audioCtx.close();
            });
          })();
          </script>
        ` }}
      />
    </div>
  );
}

Raw CSS Stylesheet Snippet

.retro-topo-container {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  overflow: hidden;
}
#canvas-webgl-retro-topo {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  border: none;
}
.audio-control-panel {
  position: absolute;
  bottom: 24px;
  right: 24px;
  z-index: 50;
  pointer-events: auto;
}
.audio-btn {
  background: rgba(11, 11, 15, 0.75);
  border: 1px solid rgba(255, 0, 128, 0.35);
  box-shadow: 0 4px 20px rgba(255, 0, 128, 0.15);
  border-radius: 12px;
  color: #fff;
  padding: 10px 16px;
  font-family: monospace;
  font-size: 10px;
  font-weight: 800;
  letter-spacing: 0.1em;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 10px;
  transition: all 0.3s cubic-bezier(0.16, 1, 0.3, 1);
  backdrop-filter: blur(8px);
}
.audio-btn:hover {
  background: rgba(255, 0, 128, 0.15);
  border-color: rgba(255, 0, 128, 0.8);
  box-shadow: 0 4px 25px rgba(255, 0, 128, 0.3);
  transform: translateY(-2px);
}
.audio-btn:active {
  transform: translateY(0);
}
.audio-btn.active {
  border-color: rgba(0, 240, 255, 0.85);
  box-shadow: 0 4px 25px rgba(0, 240, 255, 0.35);
}
.audio-btn.active:hover {
  background: rgba(0, 240, 255, 0.15);
}