<!DOCTYPE html>

<html lang="en">

<head>

  <meta charset="UTF-8">

  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <title>Holographic Starfield</title>

  <style>

    body, html { margin: 0; padding: 0; overflow: hidden; }

    canvas { display: block; }

  </style>

</head>

<body>

  <canvas id="shaderCanvas"></canvas>

  <script>

    const vertexShaderSource = `

      attribute vec4 position;

      varying vec2 vUv;

      void main() {

        gl_Position = position;

        vUv = position.xy * 0.5 + 0.5;

      }

    `;

    const fragmentShaderSource = `

      precision highp float;

      uniform float time;

      uniform vec2 resolution;

      varying vec2 vUv;

      #define PI 3.141592653589793

      #define TAU (2.0 * PI)

      #define STAR_COUNT 100

      #define DEPTH_LAYERS 8

      // Random function for star positions

      float rand(vec2 co) {

        return fract(sin(dot(co, vec2(12.9898, 78.233))) * 43758.5453);

      }

      // Holographic starfield

      vec3 holographicStarfield(vec2 uv, float t) {

        vec3 color = vec3(0.0);

        // Simulate depth layers

        for (int layer = 0; layer < DEPTH_LAYERS; layer++) {

          float depth = float(layer + 1) / float(DEPTH_LAYERS);

          float scale = 1.0 / depth; // Stars get smaller with depth

          // Generate stars

          for (int i = 0; i < STAR_COUNT; i++) {

            // Random star position

            vec2 starPos = vec2(

              rand(vec2(float(i), float(layer))),

              rand(vec2(float(i) + 1.0, float(layer)))

            );

            // Star size and brightness

            float starSize = 0.005 * scale;

            float starBrightness = rand(vec2(float(i), float(layer) + 1.0));

            // Distance to star

            vec2 delta = uv - starPos;

            float dist = length(delta);

            // Star glow

            float glow = smoothstep(starSize, 0.0, dist);

            // Holographic interference

            float wave = sin(dist * 100.0 - t * 5.0 * depth);

            vec3 starColor = vec3(

              0.5 + 0.5 * sin(wave + t * depth), // Red

              0.5 + 0.5 * cos(wave + t * 0.7 * depth), // Green

              0.5 + 0.5 * sin(wave + t * 1.3 * depth) // Blue

            );

            // Add star to the color

            color += starColor * glow * starBrightness * depth;

          }

        }

        return color;

      }

      void main() {

        vec2 uv = vUv;

        uv.x *= resolution.x / resolution.y; // Correct aspect ratio

        // Dynamic holographic starfield

        float t = time * 0.5;

        vec3 holoColor = holographicStarfield(uv, t);

        gl_FragColor = vec4(holoColor, 1.0);

      }

    `;

    const canvas = document.getElementById('shaderCanvas');

    const gl = canvas.getContext('webgl');

    if (!gl) throw new Error('WebGL not supported');

    function compileShader(gl, source, type) {

      const shader = gl.createShader(type);

      gl.shaderSource(shader, source);

      gl.compileShader(shader);

      if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {

        console.error('Shader error:', gl.getShaderInfoLog(shader));

        return null;

      }

      return shader;

    }

    const vertexShader = compileShader(gl, vertexShaderSource, gl.VERTEX_SHADER);

    const fragmentShader = compileShader(gl, fragmentShaderSource, gl.FRAGMENT_SHADER);

    const program = gl.createProgram();

    gl.attachShader(program, vertexShader);

    gl.attachShader(program, fragmentShader);

    gl.linkProgram(program);

    gl.useProgram(program);

    const positionBuffer = gl.createBuffer();

    gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);

    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, -1, 1, -1, -1, 1, 1, 1]), gl.STATIC_DRAW);

    const positionLocation = gl.getAttribLocation(program, 'position');

    gl.enableVertexAttribArray(positionLocation);

    gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);

    const timeLocation = gl.getUniformLocation(program, 'time');

    const resolutionLocation = gl.getUniformLocation(program, 'resolution');

    function render(time) {

      time *= 0.001;

      gl.uniform1f(timeLocation, time);

      gl.uniform2f(resolutionLocation, canvas.width, canvas.height);

      gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);

      requestAnimationFrame(render);

    }

    function resize() {

      canvas.width = window.innerWidth;

      canvas.height = window.innerHeight;

      gl.viewport(0, 0, canvas.width, canvas.height);

    }

    window.addEventListener('resize', resize);

    resize();

    requestAnimationFrame(render);

  </script>

</body>

</html>

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.