<canvas id='bird-canvas'></canvas>

<!-- ------------------------------------------------------------------------------------------------------- -->
<!-- BIRD                                                                                                    -->
<!-- ------------------------------------------------------------------------------------------------------- -->

<script id='bird-vertex-shader' type='x-shader/x-vertex'>
    precision mediump float;

    #define SPEED 3.0

    uniform float u_canvas_height;
    uniform float u_canvas_width;

    uniform mat4 u_model_matrix;
    uniform mat4 u_view_matrix;
    uniform mat4 u_projection_matrix;

    uniform float u_time;

    attribute vec2 a_position;

    varying vec2 v_position;

    void main() {
        vec4 new_position = u_projection_matrix * u_view_matrix * u_model_matrix * vec4(a_position, 0.0, 1.0);

        float deltaY = (abs(new_position.x) + 0.3) * sin(abs(new_position.x * 2.0) + u_time * SPEED) / 3.0
            + (sin(abs(a_position.x) * 100.0 + u_time) + sin(a_position.y * 100.0 + u_time)) / 70.0;

        new_position.y += deltaY;

        new_position.x = new_position.x * u_canvas_height / u_canvas_width;

        new_position.x /= (1.0 + 0.9 * new_position.w);
        new_position.y /= (1.0 + 0.9 * new_position.w);

        v_position = a_position;

        gl_Position = new_position;
    }

</script>


<script id='bird-fragment-shader' type='x-shader/x-fragment'>
    precision mediump float;

    uniform float u_canvas_height;
    uniform float u_canvas_width;

    uniform mat4 u_model_matrix;
    uniform mat4 u_view_matrix;
    uniform mat4 u_projection_matrix;

    uniform float u_time;

    varying vec2 v_position;

    void main() {
        float color = v_position.y;

        if (color > 1.0) {
            color = 1.0;
        } else if (color < 0.0) {
            color = 0.0;
        }

        gl_FragColor = vec4(1.0 - color, 1.0 - color, 1.0 - color, 1.0);
    }
</script>


<!-- ------------------------------------------------------------------------------------------------------- -->
<!-- DOTS                                                                                                    -->
<!-- ------------------------------------------------------------------------------------------------------- -->

<script id='bird-dots-vertex-shader' type='x-shader/x-vertex'>
    precision mediump float;

    #define SPEED 3.0

    uniform float u_canvas_height;
    uniform float u_canvas_width;

    uniform mat4 u_model_matrix;
    uniform mat4 u_view_matrix;
    uniform mat4 u_projection_matrix;

    uniform float u_time;

    attribute vec2 a_position;

    varying vec2 v_position;


    void main() {
        vec4 new_position = u_projection_matrix * u_view_matrix * u_model_matrix * vec4(a_position, 0.0, 1.0);

        float time = u_time;


        float deltaY = (abs(new_position.x) + 0.3) * sin(abs(new_position.x * 2.0) + time * SPEED) / 3.0
            + (sin(abs(a_position.x) * 100.0 + time) + sin(a_position.y * 100.0 + time)) / 70.0;

        new_position.y += deltaY;

        new_position.x = new_position.x * u_canvas_height / u_canvas_width;

        new_position.x /= (1.0 + 0.9 * new_position.w);
        new_position.y /= (1.0 + 0.9 * new_position.w);

        v_position = a_position;

        gl_Position = new_position;
        gl_PointSize = 1.0;
    }

</script>


<script id='bird-dots-fragment-shader' type='x-shader/x-fragment'>
    precision mediump float;

    uniform float u_canvas_height;
    uniform float u_canvas_width;

    uniform mat4 u_model_matrix;
    uniform mat4 u_view_matrix;
    uniform mat4 u_projection_matrix;

    uniform float u_time;

    varying vec2 v_position;

    void main() {
        float color = v_position.y;

        if (color > 1.0) {
            color = 1.0;
        } else if (color < 0.0) {
            color = 0.0;
        }

        gl_FragColor = vec4(color, color, color, 1.0);
    }
</script>


<!-- ------------------------------------------------------------------------------------------------------- -->
<!-- TRACK DOTS                                                                                              -->
<!-- ------------------------------------------------------------------------------------------------------- -->

<script id='bird-trackdots-vertex-shader' type='x-shader/x-vertex'>
    precision mediump float;

    #define SPEED 3.0
    #define TIME_DELTA -0.2

    uniform float u_canvas_height;
    uniform float u_canvas_width;

    uniform mat4 u_model_matrix;
    uniform mat4 u_view_matrix;
    uniform mat4 u_projection_matrix;

    uniform float u_time;

    attribute vec2 a_position;

    varying vec2 v_position;


    void main() {
        vec4 new_position = u_projection_matrix * u_view_matrix * u_model_matrix * vec4(a_position, 0.0, 1.0);

        float time = u_time + TIME_DELTA;


        float deltaY = (abs(new_position.x) + 0.3) * sin(abs(new_position.x * 2.0) + time * SPEED) / 3.0
            + (sin(abs(a_position.x) * 100.0 + time) + sin(a_position.y * 100.0 + time)) / 70.0;

        new_position.y += deltaY;

        new_position.x = new_position.x * u_canvas_height / u_canvas_width;

        new_position.x /= (1.0 + 0.9 * new_position.w);
        new_position.y /= (1.0 + 0.9 * new_position.w);

        v_position = a_position;

        gl_Position = new_position;
        gl_PointSize = 2.0;
    }

</script>


<script id='bird-trackdots-fragment-shader' type='x-shader/x-fragment'>
    precision mediump float;

    #define SPEED 3.0
    #define PI 3.1415926

    uniform float u_canvas_height;
    uniform float u_canvas_width;

    uniform mat4 u_model_matrix;
    uniform mat4 u_view_matrix;
    uniform mat4 u_projection_matrix;

    uniform float u_time;

    varying vec2 v_position;

    void main() {
        float color = 0.3 * abs(abs(v_position.x) - 4.0) * (1.0 - v_position.y) * (1.0 - sin(PI / 2.0 + u_time * SPEED * 2.0)) * sin(PI + u_time * SPEED);

        if (color > 0.2) {
            color = 0.2;
        } else if (color < 0.0) {
            color = 0.0;
        }

        gl_FragColor = vec4(color * 0.3, color, color * 0.7, 1.0);
    }
</script>


<!-- ------------------------------------------------------------------------------------------------------- -->
<!-- TRACK DOTS 2                                                                                            -->
<!-- ------------------------------------------------------------------------------------------------------- -->

<script id='bird-trackdots2-vertex-shader' type='x-shader/x-vertex'>
    precision mediump float;

    #define SPEED 3.0
    #define TIME_DELTA -0.2

    uniform float u_canvas_height;
    uniform float u_canvas_width;

    uniform mat4 u_model_matrix;
    uniform mat4 u_view_matrix;
    uniform mat4 u_projection_matrix;

    uniform float u_time;

    attribute vec2 a_position;

    varying vec2 v_position;


    void main() {
        vec4 new_position = u_projection_matrix * u_view_matrix * u_model_matrix * vec4(a_position, 0.0, 1.0);

        float time = u_time + TIME_DELTA;


        float deltaY = (abs(new_position.x) + 0.3) * sin(abs(new_position.x * 2.0) + time * SPEED) / 3.0
            + (sin(abs(a_position.x) * 100.0 + time) + sin(a_position.y * 100.0 + time)) / 70.0;

        new_position.y += deltaY;

        new_position.x = new_position.x * u_canvas_height / u_canvas_width;

        new_position.x /= (1.0 + 0.9 * new_position.w);
        new_position.y /= (1.0 + 0.9 * new_position.w);

        v_position = a_position;

        gl_Position = new_position;
        gl_PointSize = 1.0;
    }

</script>


<script id='bird-trackdots2-fragment-shader' type='x-shader/x-fragment'>
    precision mediump float;

    #define SPEED 3.0
    #define PI 3.1415926

    uniform float u_canvas_height;
    uniform float u_canvas_width;

    uniform mat4 u_model_matrix;
    uniform mat4 u_view_matrix;
    uniform mat4 u_projection_matrix;

    uniform float u_time;

    varying vec2 v_position;

    void main() {
        float color = 0.3 * abs(abs(v_position.x) - 4.0) * v_position.y * (1.0 - sin(PI / 2.0 + u_time * SPEED * 2.0)) * sin(u_time * SPEED);

        if (color > 0.2) {
            color = 0.2;
        } else if (color < 0.0) {
            color = 0.0;
        }

        gl_FragColor = vec4(color * 0.5, color, color * 0.7, 1.0);
    }
</script>
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

body {
    overflow: hidden;
}
View Compiled
class Bird {
    constructor(options) {
        this.canvas = options.canvas;

        this.gl = this.canvas.getContext('webgl');
        this.gl.clearColor(0.0, 0.0, 0.0, 1.0);
        this.gl.clear(this.gl.COLOR_BUFFER_BIT);

        this.shaders = {
            bird: {
                vertex:   this.compileShader(this.gl.VERTEX_SHADER,   options.shaders.bird.vertex),
                fragment: this.compileShader(this.gl.FRAGMENT_SHADER, options.shaders.bird.fragment)
            },
            dots: {
                vertex:   this.compileShader(this.gl.VERTEX_SHADER,   options.shaders.dots.vertex),
                fragment: this.compileShader(this.gl.FRAGMENT_SHADER, options.shaders.dots.fragment)
            },
            trackdots: {
                vertex:   this.compileShader(this.gl.VERTEX_SHADER,   options.shaders.trackdots.vertex),
                fragment: this.compileShader(this.gl.FRAGMENT_SHADER, options.shaders.trackdots.fragment)
            },
            trackdots2: {
                vertex:   this.compileShader(this.gl.VERTEX_SHADER,   options.shaders.trackdots2.vertex),
                fragment: this.compileShader(this.gl.FRAGMENT_SHADER, options.shaders.trackdots2.fragment)
            },
        };

        this.cameraOptions = {
            zoom: 0.3,
            angleY: Math.PI * 0.4,
            angleX: 0.0
        };

        this.programs = {
            bird: null,
            dots: null
        };

        this.calculateMatrices();
        this.createBirdGeometry();
        this.createPrograms();
        this.updateCanvasUniforms();
        this.updateMatrixUniforms();
        this.initEventListeners();
        this.updateCanvasSize();

        requestAnimationFrame(this.animate.bind(this));
    }


    calculateMatrices() {
        const x = -4;
        const y = -0.5;

        this.modelMatrix = [
            1, 0, 0, 0,
            0, 1, 0, 0,
            0, 0, 1, 0,
            x, y, 0, 1
        ];

        const z = this.cameraOptions.zoom;
        const a = this.cameraOptions.angleY;
        const b = this.cameraOptions.angleX;

        this.viewMatrix = [
            z *  Math.cos(b),               0,               z * -Math.sin(b),               0,
            z * -Math.sin(a) * Math.sin(b), z * Math.cos(a), z * -Math.sin(a) * Math.cos(b), 0,
            z *  Math.cos(a) * Math.sin(b), z * Math.sin(a), z *  Math.cos(a) * Math.cos(b), 0,
            0,                              0,                   0,                          1
        ];

        const s = 1 / (Math.tan(90 * Math.PI / 360));
        const n = -1;
        const f = 10.0;

        this.projectionMatrix = [
            s, 0, 0,          0,
            0, s, 0,          0,
            0, 0, -(f)/(f-n), -1,
            0, 0, -f*n/(f-n), 1
        ];
    }


    createBirdGeometry() {
        const points = [];

        const stepX = 0.4;
        const stepY = 0.2;

        for (let x = 0; x <= 8; x += stepX) {
            for (let y = 0; y <= 1; y += stepY) {
                if (y === 0) {
                    points.push(x);
                    points.push(y);
                    points.push(x + stepX);
                    points.push(y);
                }

                points.push(x + stepX);
                points.push(y);
                points.push(x + stepX);
                points.push(y + stepY);
                points.push(x + stepX);
                points.push(y + stepY);
                points.push(x);
                points.push(y + stepY);
                points.push(x);
                points.push(y + stepY);
                points.push(x);
                points.push(y);
                points.push(x);
                points.push(y);
                points.push(x + stepX);
                points.push(y + stepY);
            }
        }

        this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.gl.createBuffer());
        this.gl.bufferData(
            this.gl.ARRAY_BUFFER,
            new Float32Array(points),
            this.gl.STATIC_DRAW
        );
    }


    compileShader(type, source) {
        const shader = this.gl.createShader(type);

        this.gl.shaderSource(shader, source);
        this.gl.compileShader(shader);

        console.log(this.gl.getShaderInfoLog(shader));

        return shader;
    }


    createPrograms() {
        this.createProgramByName('bird');
        this.createProgramByName('dots');
        this.createProgramByName('trackdots');
        this.createProgramByName('trackdots2');
    }
    
    
    createProgramByName(name) {
        this.programs[name] = this.gl.createProgram();

        this.gl.attachShader(this.programs[name], this.shaders[name].vertex);
        this.gl.attachShader(this.programs[name], this.shaders[name].fragment);
        this.gl.linkProgram(this.programs[name]);

        const vertexPositionAttribute = this.gl.getAttribLocation(this.programs[name], 'a_position');

        this.gl.enableVertexAttribArray(vertexPositionAttribute);
        this.gl.vertexAttribPointer(vertexPositionAttribute, 2, this.gl.FLOAT, false, 0, 0);
    }


    updateMatrixUniforms() {
        this.updateMatrixUniformsForProgram(this.programs.bird);
        this.updateMatrixUniformsForProgram(this.programs.dots);
        this.updateMatrixUniformsForProgram(this.programs.trackdots);
        this.updateMatrixUniformsForProgram(this.programs.trackdots2);
    }


    updateMatrixUniformsForProgram(program) {
        this.gl.useProgram(program);
        this.gl.uniformMatrix4fv(this.gl.getUniformLocation(program, 'u_model_matrix'),      false, this.modelMatrix);
        this.gl.uniformMatrix4fv(this.gl.getUniformLocation(program, 'u_view_matrix'),       false, this.viewMatrix);
        this.gl.uniformMatrix4fv(this.gl.getUniformLocation(program, 'u_projection_matrix'), false, this.projectionMatrix);
    }


    updateCanvasUniforms() {
        this.updateCanvasUniformsForProgram(this.programs.bird);
        this.updateCanvasUniformsForProgram(this.programs.dots);
        this.updateCanvasUniformsForProgram(this.programs.trackdots);
        this.updateCanvasUniformsForProgram(this.programs.trackdots2);
    }


    updateCanvasUniformsForProgram(program) {
        this.gl.useProgram(program);
        this.gl.uniform1f(this.gl.getUniformLocation(program, 'u_canvas_height'), this.canvas.height);
        this.gl.uniform1f(this.gl.getUniformLocation(program, 'u_canvas_width'),  this.canvas.width);
    }


    updateTimeUniform(timeStamp) {
        this.updateTimeUniformForProgram(this.programs.bird, timeStamp);
        this.updateTimeUniformForProgram(this.programs.dots, timeStamp);
        this.updateTimeUniformForProgram(this.programs.trackdots, timeStamp);
        this.updateTimeUniformForProgram(this.programs.trackdots2, timeStamp);
    }


    updateTimeUniformForProgram(program, timeStamp) {
        this.gl.useProgram(program);
        this.gl.uniform1f(this.gl.getUniformLocation(program, 'u_time'), timeStamp / 1000.0);
    }


    updateCanvasSize() {
        this.canvas.height = Math.ceil(window.innerHeight);
        this.canvas.width = Math.ceil(window.innerWidth);

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

        this.updateCanvasUniforms();
    }


    updateCameraAngles(mouseX, mouseY) {
        const modX = (mouseX - window.innerWidth / 2) / window.innerWidth;
        const modY = (mouseY - window.innerHeight / 2) / window.innerHeight;

        this.cameraOptions.angleY = Math.PI * 0.4 + modY / 4;
        this.cameraOptions.angleX = modX;

        this.calculateMatrices();
        this.updateMatrixUniforms();
    }


    initEventListeners() {
        window.addEventListener('resize', this.updateCanvasSize.bind(this));

        document.body.addEventListener('mousemove', (e) => {
            this.updateCameraAngles(e.clientX, e.clientY);
        });
    }


    animate(timeStamp) {
        this.gl.clearColor(0.0, 0.0, 0.05, 1.0);
        this.gl.clear(this.gl.COLOR_BUFFER_BIT);

        this.updateTimeUniform(timeStamp);

        this.gl.useProgram(this.programs.trackdots);
        this.gl.drawArrays(this.gl.POINTS, 0, 1000);

        this.gl.useProgram(this.programs.bird);
        this.gl.drawArrays(this.gl.LINES, 0, 1000);

        this.gl.useProgram(this.programs.dots);
        this.gl.drawArrays(this.gl.POINTS, 0, 1000);

        this.gl.useProgram(this.programs.trackdots2);
        this.gl.drawArrays(this.gl.POINTS, 0, 1000);

        requestAnimationFrame(this.animate.bind(this));
    }
}



const myBird = new Bird({
    canvas:         document.getElementById('bird-canvas'),
    shaders: {
        bird: {
            vertex:   document.getElementById('bird-vertex-shader').textContent,
            fragment: document.getElementById('bird-fragment-shader').textContent
        },
        dots: {
            vertex:   document.getElementById('bird-dots-vertex-shader').textContent,
            fragment: document.getElementById('bird-dots-fragment-shader').textContent
        },
        trackdots: {
            vertex:   document.getElementById('bird-trackdots-vertex-shader').textContent,
            fragment: document.getElementById('bird-trackdots-fragment-shader').textContent
        },
        trackdots2: {
            vertex:   document.getElementById('bird-trackdots2-vertex-shader').textContent,
            fragment: document.getElementById('bird-trackdots2-fragment-shader').textContent
        },
    }
});
View Compiled

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.