JSFiddle - React, Tailwind, and code Playground

by Oski Krawczyk

HTML

<canvas id="glCanvas"></canvas>
    <div id="controls">
        <label for="animationSpeed">Animation Speed:</label>
        <input type="range" id="animationSpeed" min="0.01" max="55.0" step="0.01" value="1.0">
        <span id="speedValue">1.00x</span>

        <label for="verticalOffset">Vertical Offset:</label>
        <input type="range" id="verticalOffset" min="-2.0" max="2.0" step="0.01" value="0.0">
        <span id="offsetValue">0.00</span>

        <label for="patternZoom">Pattern Zoom:</label>
        <input type="range" id="patternZoom" min="0.1" max="2.0" step="0.01" value="0.5">
        <span id="zoomValue">0.50</span>
    </div>

JavaScript

// main.js

const canvas = document.getElementById('glCanvas');
const gl = canvas.getContext('webgl2'); // Use webgl2 for GLSL ES 3.00

if (!gl) {
    alert('Unable to initialize WebGL. Your browser or machine may not support it.');
}

// Vertex shader source
const vsSource = `#version 300 es
    in vec4 a_position;
    out vec2 v_uv;

    void main() {
        gl_Position = a_position;
        // Map clip space (-1 to 1) to UV space (0 to 1)
        v_uv = (a_position.xy * 0.5) + 0.5;
    }
`;

// Fragment shader source (translated from Swift Metal)
const fsSource = `#version 300 es
    precision highp float;

    in vec2 v_uv;
    out vec4 fragColor;

    uniform float u_time;
    uniform float u_tapValue;
    uniform vec2 u_resolution;
    uniform float u_aspectRatio;
    uniform float u_verticalOffset;
    uniform float u_patternZoom; // NEW: Pattern Zoom uniform

    // Helper functions
    float hash(float n) {
        return fract(sin(n) * 753.5453123);
    }

    float noise(vec2 x) {
        vec2 p = floor(x);
        vec2 f = fract(x);
        f = f * f * (3.0 - 2.0 * f);

        float n = p.x + p.y * 157.0;
        return mix(
            mix(hash(n + 0.0), hash(n + 1.0), f.x),
            mix(hash(n + 157.0), hash(n + 158.0), f.x),
            f.y
        );
    }

    float fbm(vec2 p, vec3 a) {
        float v = 0.0;
        v += noise(p * a.x) * 0.50 ;
        v += noise(p * a.y) * 1.50 ;
        v += noise(p * a.z) * 0.125 * 0.1; // variable
        return v;
    }

    vec3 drawLines(
        vec2 uv,
        vec3 fbmOffset,
        vec3 color1,
        vec3 colorSet[4], // GLSL requires size for arrays in parameters
        float secs
    ) {
        float timeVal = secs * 0.1;
        vec3 finalColor = vec3(0.0);

        for (int i = 0; i < 4; ++i) {
            float indexAsFloat = float(i);
            float amp = 80.0 + (indexAsFloat * 0.0);
            float period...