<!-- code is a mess just playing around -->
* {
  margin: 0;
  padding: 0;
  background: black;
  overflow: hidden;
}
function createShader(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;
    }

    function createProgram(vertexShader, fragmentShader) {
      const program = gl.createProgram();
      gl.attachShader(program, vertexShader);
      gl.attachShader(program, fragmentShader);
      gl.linkProgram(program);
      if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
        console.error(gl.getProgramInfoLog(program));
        gl.deleteProgram(program);
        return null;
      }
      return program;
    }

    const canvas = document.createElement('canvas');
    canvas.style.filter = 'saturate(.8) contrast(1.5)'
    const gl = canvas.getContext('webgl', {
      preserveDrawingBuffer: true,
      powerPreference: 'high-performance'
    });
    document.body.append(canvas);

    function resize() {
      canvas.width = window.innerWidth;
      canvas.height = window.innerHeight;
      gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
    }

    resize();
    window.addEventListener('resize', resize);

    class FlyShape {
      constructor({
        verts = [],
        colors = [],
        sizes = [],
        vert,
        frag,
        indices
      } = {}) {
        Object.assign(this, {
          verts: new Float32Array(verts),
          sizes: new Float32Array(sizes),
          colors: new Float32Array(colors),
          vertexBuffer: gl.createBuffer(),
          indices: indices && new Uint16Array(indices),
          indexBuffer: gl.createBuffer(),
          program: createProgram(
            createShader(gl.VERTEX_SHADER, vert ||
              `
              precision lowp float;
              attribute vec3 position;
              attribute vec4 vertexColor;
              attribute float pointSize; // Use this attribute
              varying vec4 fragColor;
              uniform mat4 flatMatrix;
              void main(void) {
                gl_Position = flatMatrix * vec4(position, 1.0);
                gl_PointSize = pointSize;
                fragColor = vertexColor;
              }
          `),
            createShader(gl.FRAGMENT_SHADER, frag ||
              `
            precision mediump float;
            varying vec4 fragColor;
        void main() {
            float dist = length(gl_PointCoord - vec2(0.5));
            float edge = 0.1;  
            float alpha = 1.0 - smoothstep(0.5 - edge, 0.5, dist);
            gl_FragColor = vec4(fragColor.r, fragColor.g, fragColor.b, fragColor.a * alpha);
        }`))
        })

        gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer)
        gl.bufferData(gl.ARRAY_BUFFER, this.verts, gl.STATIC_DRAW)
        if (indices) {
          gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer)
          gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this.indices, gl.STATIC_DRAW)
        }

        this.position = gl.getAttribLocation(this.program, 'position')
        gl.enableVertexAttribArray(this.position)
        gl.vertexAttribPointer(this.position, 3, gl.FLOAT, false, 0, 0)

        this.colorBuffer = gl.createBuffer()
        gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer)
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(), gl.DYNAMIC_DRAW)

        this.vertexColor = gl.getAttribLocation(this.program, 'vertexColor')
        gl.enableVertexAttribArray(this.vertexColor)
        gl.vertexAttribPointer(this.vertexColor, 4, gl.FLOAT, false, 0, 0)

        this.sizeBuffer = gl.createBuffer()
        gl.bindBuffer(gl.ARRAY_BUFFER, this.sizeBuffer)
        gl.bufferData(gl.ARRAY_BUFFER, this.sizes, gl.STATIC_DRAW)

        this.pointSize = gl.getAttribLocation(this.program, 'pointSize')
        gl.enableVertexAttribArray(this.pointSize)
        gl.vertexAttribPointer(this.pointSize, 1, gl.FLOAT, false, 0, 0)

        this.flatMatrix = gl.getUniformLocation(this.program, 'flatMatrix')
        this.perspective = new DOMMatrix();
      }
      run() {
        gl.useProgram(this.program);
        this.update(this)
      }
      updateBuffers(target) {
        // @TODO consider way to handle index buffers here
        // down the road
        gl.bindBuffer(gl.ARRAY_BUFFER, dots.vertexBuffer);
        gl.bufferData(gl.ARRAY_BUFFER, target.points, gl.DYNAMIC_DRAW);
        gl.vertexAttribPointer(dots.position, 3, gl.FLOAT, false, 0, 0);

        gl.bindBuffer(gl.ARRAY_BUFFER, dots.colorBuffer);
        gl.bufferData(gl.ARRAY_BUFFER, target.colors, gl.DYNAMIC_DRAW);
        gl.vertexAttribPointer(dots.vertexColor, 4, gl.FLOAT, false, 0, 0);

        gl.bindBuffer(gl.ARRAY_BUFFER, dots.sizeBuffer);
        gl.bufferData(gl.ARRAY_BUFFER, target.sizes, gl.DYNAMIC_DRAW);
        gl.vertexAttribPointer(dots.pointSize, 1, gl.FLOAT, false, 0, 0);

        gl.uniformMatrix4fv(this.flatMatrix, false, target.perspective
          .toFloat32Array());
      }

      // @TODO in theory this may also be something that
      // can be flyweight
      update(target) {
        gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);
        gl.vertexAttribPointer(target.position, 3, gl.FLOAT, false, 0, 0);

        gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer);
        gl.bufferData(gl.ARRAY_BUFFER, target.colors, gl.DYNAMIC_DRAW);
        gl.vertexAttribPointer(this.vertexColor, 4, gl.FLOAT, false, 0, 0);

        gl.bindBuffer(gl.ARRAY_BUFFER, this.sizeBuffer);
        gl.bufferData(gl.ARRAY_BUFFER, target.sizes, gl.DYNAMIC_DRAW);
        gl.vertexAttribPointer(this.pointSize, 1, gl.FLOAT, false, 0, 0);

        gl.uniformMatrix4fv(this.flatMatrix, false, target.perspective
          .toFloat32Array());
      }
      draw() {
        gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0);
      }
    }

    const plane = new FlyShape({
      frag: `
             precision lowp float;
              varying vec4 fragColor;
              void main(void) {
                gl_FragColor = fragColor;
              }
            `,
      verts: [
        -1, -1, 0,
        1, -1, 0,
        1, 1, 0,
        -1, 1, 0
      ],
      indices: [0, 1, 2, 0, 2, 3],
      sizes: [0, 0, 0, 0],
      colors: [
        0, 0, 0, .015,
        0, 0, 0, .015,
        0, 0, 0, .015,
        0, 0, 0, .015,
      ]
    })

    const dots = new FlyShape()

    function createPerspectiveMatrix(fov, width, height, near, far, zoom = 1) {
      const aspect = width / height;
      const f = 1.0 / Math.tan(fov / 2) * zoom;
      return new DOMMatrix([
        f / aspect, 0, 0, 0,
        0, f, 0, 0,
        0, 0, (far + near) / (near - far), -1,
        0, 0, (2 * far * near) / (near - far), 0
      ]);
    }

    class Shape {
      constructor(p = {}) {
        this.dynamic = false;
        if (p.init) p.init(this);
        if (!this.points) this.points = cube;

        if (!this.colors) {
          this.colors = []
          for (let i = 0; i < this.points.length / 3; i++) {
            this.colors.push(...(this.color || [1.0, 1.0, 1.0, 1.0]));
          }

        }
        this.colors = new Float32Array(this.colors);

        if (!this.sizes) {
          this.sizes = []
          for (let i = 0; i < this.points.length / 3; i++) {
            this.sizes.push(this.size || 5)
          }
        }

        this.sizes = new Float32Array(this.sizes);

        this.animate = p.animate;
        this.matrix = new DOMMatrix();
        this.t = 0;
        Shape.shapes.push(this);

        this.points = new Float32Array(this.points);
        this.startPoints = new Float32Array(this.points);
      }

      run() {
        this?.animate?.(this);
        this.perspective = perspectiveMatrix.multiply(this.matrix)

        dots.updateBuffers(this)

        gl.uniformMatrix4fv(
          dots.flatMatrix, false, this.perspective.toFloat32Array()
        )

        gl.drawArrays(gl.POINTS, 0, this.points.length / 3);
      }
    }
    Shape.shapes = [];

    const shape = o => new Shape(o);

    function rgba(r, g, b, a = .72) {
      return [
        r / 255,
        g / 255,
        b / 255,
        a
      ]
    }
    const colors = [
      rgba(174, 217, 228),
      rgba(0, 146, 214),
      rgba(0, 179, 214),
      rgba(106, 145, 153),
      rgba(42, 170, 250),
      rgba(0, 0, 0, .12),
      rgba(0, 0, 0, .42),
      rgba(0, 0, 0, .52),
      rgba(0, 0, 0, .52),
      rgba(255, 255, 255, .42),
      rgba(255, 255, 255, .42)
    ];

    function makeColor() {
      return colors[Math.floor(Math.random() * colors.length)];
    }

    let G =
      shape({
        init(s) {
          s.points = [];
          s.vels = []
          s.ts = []
          s.tsi = []
          s.xys = []
          s.sizes = []
          s.colors = []

          const m = e => Math.random() * 4 - 2;
          const v = e => Math.random() * .05 - .025;
          const t = e => Math.random() * 7

          s.reset = s => {
            let inc = 0
            let ci = 0

            for (let i = 0; i < 25000; i += 3) {
              s.points[i] = m()
              s.points[i + 1] = m()
              s.points[i + 2] = m()

              let c = makeColor()
              s.colors[ci++] = c[0]
              s.colors[ci++] = c[1]
              s.colors[ci++] = c[2]
              s.colors[ci++] = c[3]

              s.xys[i] = s.points[i]
              s.xys[i + 1] = s.points[i + 1]
              s.xys[i + 2] = s.points[i + 2]

              s.vels[i] = v()
              s.vels[i + 1] = v()
              s.vels[i + 2] = v()

              s.ts[i] = t()
              s.ts[i + 1] = t()
              s.ts[i + 2] = t()

              s.tsi[i] = v()
              s.tsi[i + 1] = v()
              s.tsi[i + 2] = v()

              s.sizes[inc] = 50 * Math.random() * Math.random() * Math
                .random() + 1
              if (Math.random() < 0.001) s.sizes[inc] = 100
              inc++
            }
          }

          s.reset(s)
          s.scl = 1
          s.ds = 1
        },
        animate(s) {
          s.scl += (s.ds - s.scl) / 15

          if (pressed) {
            s.ds += .1
          } else {
            s.ds = 1
          }

          this.matrix = new DOMMatrix()
            .scaleSelf(s.scl, s.scl, s.scl);

          for (let i = 0; i < s.points.length; i += 3) {

            s.xys[i] += s.vels[i]
            s.xys[i + 1] += s.vels[i + 1]
            s.xys[i + 2] += s.vels[i + 2]

            s.points[i] = s.xys[i] + 1 * Math.cos(s.ts[i])
            s.points[i + 1] = s.xys[i + 1] + 1 * Math.sin(s.ts[i + 1])
            s.points[i + 2] = s.xys[i + 2] + 1 * Math.cos(s.ts[i + 2])
            s.ts[i] += s.tsi[i]
            s.ts[i + 1] += s.tsi[i + 1]
            s.ts[i + 2] += s.tsi[i + 2]


          }
        }
      });




    shape({
      init(s) {
        s.points = []
        s.colors = []
        s.sizes = []


        let S = 2.5;
        for (var i = -2; i < 2; i += .04) {
          for (var j = -2; j < 2; j += .04) {
            for (var k = -2; k < 2; k += .04) {
              // blobby, from here www.iiit.net/techreports/ImplicitTR.pdf
              const sc = i * i + j * j + k * k + Math.sin(4 * i) - Math.cos(
                4 * j) + Math.sin(6 * k) - 1;
              if (sc < 0 && sc > -.2) {
                s.points.push(i * S,
                  j * S,
                  k * S);
                if (s.points.length % 20 == 0) {
                  s.colors.push(1, 0, 0, .5)
                  s.sizes.push(10)
                } else if (s.points.length % 10 == 0) {
                  s.colors.push(0, 0, 0, .5)
                  s.sizes.push(10)

                } else {
                  s.sizes.push(5)
                  let cc = (k + 2 / 4)
                  s.colors.push(cc / 2 + .5, cc / 2, cc / 3, .5)
                }

              }
            }
          }
        }

        s.rotate = 0
        s.scale = 1
        s.scaleDest = 1
      },
      animate(s) {
        this.matrix = new DOMMatrix()
          .rotateSelf(s.rotate)
          .scaleSelf(s.scale, s.scale, s.scale)
        s.rotate += 1;

        s.scale += (s.scaleDest - s.scale) / 33

        if (pressed) {
          s.scaleDest = .3
        } else {
          s.scaleDest = 1
        }

      }
    })



    let perspectiveMatrix;
    let camera;

    let mx = 0
    let my = 0
    let pressed = false
    let worldRot = 0;
    let worldRot2 = 0;

    onpointermove = e => {
      mx = e.clientX;
      my = e.clientY;
    };

    onpointerdown = e => {
      if (e.shiftKey) {
        for (let i = 0; i < Shape.shapes.length; i++) {
          let s = Shape.shapes[i];
          s.reset(s)
          //G.init();
        }
        return;
      }
      pressed = true;
    };
    onpointerup = e => {
      pressed = false;
    }

    onresize = e => {
      canvas.width = innerWidth;
      canvas.height = innerHeight;
    };



    gl.enable(gl.DEPTH_TEST);
    gl.depthFunc(gl.NOTEQUAL);


    function loop() {
      gl.enable(gl.BLEND)

      // this is very important for transparent shaders
      gl.blendFuncSeparate(
        gl.SRC_ALPHA,
        gl.ONE_MINUS_SRC_ALPHA,
        gl.ONE,
        gl.ONE_MINUS_SRC_ALPHA
      )

      plane.run()
      plane.draw()

      gl.useProgram(dots.program);
      perspectiveMatrix = createPerspectiveMatrix(
        Math.PI / 2,
        canvas.width,
        canvas.height,
        0.9,
        20000,
        .5
      );

      camera = new DOMMatrix();
      camera.translateSelf(0, 0, -6)
        .rotateSelf(5, 0, 0)
        .rotateSelf(worldRot2, worldRot, 0);

      worldRot += (mx - worldRot) / 22;
      worldRot2 += (my - worldRot2) / 22;

      perspectiveMatrix.multiplySelf(camera);


      for (let i = 0; i < Shape.shapes.length; i++) {
        Shape.shapes[i].run();
      }


      requestAnimationFrame(loop);
    }

    gl.clearColor(0.0, 0.0, 0.0, 1.0);
    loop();

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.