• 01 - Critter Models 02
  • 02 - VantaJS Shaders
  • 03 - App Bundle 02
  • 04 - Cathedrals
  • 05 - App Bundle 01
  • 06 - Retroscopic
  • 07 - Games
  • 08 - Voronoi
  • 09 - Terrain
  • 10 - Critter Models 01
  • 11 - Chromatophores
  • 12 - Conformal Maps
  • 13 - Echo Logix
  • 14 - Music
  • 15 - Colour Theory
MIKE'S NOTEBOOK
Notes
Number:
Project:
01Critter Models 02
└ Locust└ Butterfly└ Cockroach└ Spider└ Scorpion└ Dragon└ Brazilian Treehopper└ Tardigrade└ Diatoms└ Bumblebee└ Wasp└ Hammerhead Worm└ Giraffe Weevil└ Trilobite Beetle
02VantaJS Shaders
└ Clouds└ Waves└ Cells└ Rings└ Halo└ Globe└ Net└ Dots└ Birds└ Trunk└ Topology└ About VantaJS
03App Bundle 02
└ Gutindex└ Insect Builder└ OpenLayers
04Cathedrals
└ Lattice└ Lost└ Lucent└ Lumen└ Glass└ Disco└ Hyperion└ Latent
05App Bundle 01
└ Robohash└ PrismJS└ Horoscope└ Binary Jazz└ Morse Code└ URL Extractor└ Entity Escaper└ Phonetic Alphabet└ Toki Pona└ Semaphore└ Cut Up Machine└ Text Mixer└ Currency Freaks└ Host IO└ CrUX API└ Logo Dev└ Web Speech API└ GEO DB
06Retroscopic
└ Galaxy└ Ocean└ Slime└ 24-Cell└ Unicorn└ Polyp└ Oneiric 1└ Oneiric 2└ Node└ Torus Knot└ GLSL Grid└ Liquid Crystal└ Silky└ Butterfly└ Ocean 2└ Icosahedron└ Luma└ Swarm└ Broken Solid└ Hypersphere└ Constraints└ Psycho Killer
01 / 03

LIQUID CRYSTAL - RETROSCOPIC CRT SHADER SOURCE CODE

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Liquid Crystal Shader</title>
    <style>
        * { margin: 0; padding: 0; box-sizing: border-box; }
        html, body { width: 100%; height: 100%; background: #000; overflow: hidden; }

        canvas {
            display: block;
            width: 100vw; 
            height: 100vh; 
            filter: grayscale(1) sepia(1) hue-rotate(80deg) saturate(5) brightness(1.15) contrast(1.2);
            position: relative;
            z-index: 2;
        }

        /* Retroscopic Overlay */
        #scope {
            position: fixed;
            inset: 0;
            z-index: 4;
            pointer-events: none;
            display: block;
        }
        #scope>div {
            position: absolute;
            inset: 0;
        }
        #scope .glow {
            background: radial-gradient(circle at 50% 46%, rgba(30, 255, 130, .08), rgba(0, 0, 0, 0) 62%);
            animation: nvflick 3.6s steps(24) infinite;
        }
        #scope .scan {
            background: repeating-linear-gradient(to bottom, rgba(0, 0, 0, 0) 0 2px, rgba(0, 25, 8, .45) 2px 3px);
            animation: nvscan 7s linear infinite;
        }
        #scope .vig {
            box-shadow: inset 0 0 200px 50px rgba(0, 0, 0, .8);
        }
        #scope .mask {
            background: radial-gradient(circle at 50% 50%, rgba(0, 0, 0, 0) 54%, rgba(0, 0, 0, .5) 72%, rgba(0, 0, 0, .94) 100%);
        }
        @keyframes nvscan {
            from { background-position: 0 0; }
            to { background-position: 0 240px; }
        }
        @keyframes nvflick {
            0%, 100% { opacity: .55; }
            40% { opacity: .9; }
            62% { opacity: .4; }
            80% { opacity: .75; }
        }
    </style>
</head>
<body>
    <div id="scope">
        <div class="glow"></div>
        <div class="scan"></div>
        <div class="vig"></div>
        <div class="mask"></div>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.min.js">
    </script>
    <script>
        const U = v => ({ value: v });

        const renderer = new THREE.WebGLRenderer({ antialias: false });
        renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
        renderer.setSize(window.innerWidth, window.innerHeight);
        document.body.appendChild(renderer.domElement);

        const scene = new THREE.Scene();
        const camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1);

        const mat = new THREE.ShaderMaterial({
            uniforms: {
                time:       U(0.0),
                res:        U(new THREE.Vector2(window.innerWidth, window.innerHeight)),
                uvScale:    U(1.4),
                uvOffX:     U(0.00),
                uvOffY:     U(0.5),
                depthStep:  U(0.75),
                rotSpeed:   U(0.00000001),
                outerIter:  U(40),
                innerIter:  U(3),
                sInit:      U(0.7),
                minFold:    U(1.2),
                foldStr:    U(2.2),
                foldOff:    U(3.5),
                foldBX:     U(0.65),
                foldBY:     U(1.40),
                foldBZ:     U(2.00),
                modScale:   U(7.00),
                sLogMul:    U(0.2),
                brightness: U(440.0),
                hueOff:     U(0.78),
                hueScale:   U(.55),
                satMult:    U(0.50),
            },

            vertexShader: `void main(){ gl_Position = vec4(position.xy, 0.0, 1.0); }`,

            fragmentShader: `
                precision highp float;
                uniform float time;
                uniform vec2  res;
                uniform float uvScale;
                uniform float uvOffX;
                uniform float uvOffY;
                uniform float depthStep;
                uniform float rotSpeed;
                uniform int   outerIter;
                uniform int   innerIter;
                uniform float sInit;
                uniform float minFold;
                uniform float foldStr;
                uniform float foldOff;
                uniform float foldBX;
                uniform float foldBY;
                uniform float foldBZ;
                uniform float modScale;
                uniform float sLogMul;
                uniform float brightness;
                uniform float hueOff;
                uniform float hueScale;
                uniform float satMult;

                #define MAX_OUTER 200
                #define MAX_INNER  40

                vec3 hsv(float h, float s, float v){
                    vec4 K = vec4(1., 2./3., 1./3., 3.);
                    vec3 p = abs(fract(vec3(h) + K.xyz) * 6. - K.www);
                    return v * mix(K.xxx, clamp(p - K.xxx, 0., 1.), s);
                }
                mat2 rotate2D(float a){ float c=cos(a),s=sin(a); return mat2(c,-s,s,c); }

                void main(){
                    vec4  o = vec4(0.0);
                    vec2 FC = gl_FragCoord.xy;
                    float t = time;
                    float g = 0.0;

                    for(int oi = 0; oi < MAX_OUTER; oi++){
                        if(oi >= outerIter) break;
                        vec3 p = vec3(((FC.xy*0.5 - res)/res.x)*uvScale + vec2(uvOffX,uvOffY), g + depthStep);
                        p.zx *= rotate2D(t * rotSpeed);
                        float s = sInit;

                        for(int ii = 0; ii < MAX_INNER; ii++){
                            if(ii >= innerIter) break;
                            p = abs(p);
                            if(p.x < p.y) p.xy = p.yx;
                            if(p.x < p.z) p.xz = p.zx;
                            if(p.y < p.z) p.yz = p.zy;
                            p = p * foldStr - vec3(foldBX, foldBY, foldBZ);
                            s *= foldOff;
                        }
                        float d = length(p);
                        g += mod(d, modScale) / (s + 0.001);
                        s = log(abs(s) * sLogMul + 1.0);
                        o.rgb += hsv(hueOff + g*hueScale, satMult, s/brightness);
                    }
                    o.a = 1.0;
                    gl_FragColor = o;
                }
            `,
        });

        scene.add(new THREE.Mesh(new THREE.PlaneGeometry(2, 2), mat));

        const ANIM_DEFS = [
            { key: 'depthStep', min: 0.175, max: 0.8,  freq: 1.0,  phase: 0.00 },
            { key: 'foldBX',    min: 1.10,  max: 3.20, freq: 0.5,  phase: 1.05 },
            { key: 'foldBY',    min: 4.85,  max: 6.00, freq: 0.4,  phase: 2.09 },
            { key: 'foldOff',   min: 4.00,  max: 5.75, freq: 0.5,  phase: 3.14 },
            { key: 'hueOff',    min: 0.00,  max: 1.00, freq: 0.45, phase: 4.19 },
            { key: 'hueScale',  min: 1.20,  max: 4.00, freq: 0.15, phase: 5.24 },
        ];

        const ANIM_SPEED = 0.03;
        const clock = new THREE.Clock();
        let animTime = 0;

        (function animate(){
            requestAnimationFrame(animate);
            const delta = clock.getDelta();
            animTime += delta * ANIM_SPEED;
            mat.uniforms.time.value = clock.elapsedTime;

            for (const def of ANIM_DEFS) {
                const t   = (Math.sin(animTime * def.freq * Math.PI * 2 + def.phase) + 1) / 2;
                const val = def.min + (def.max - def.min) * t;
                mat.uniforms[def.key].value = val;
            }
            renderer.render(scene, camera);
        })();

        window.addEventListener('resize', () => {
            renderer.setSize(window.innerWidth, window.innerHeight);
            mat.uniforms.res.value.set(window.innerWidth, window.innerHeight);
        });
    </script>
</body>
</html>
MikeHiggins.me.uk LinkedIn GitHub Bluesky Contra Upwork
© 2026 Mike Higgins. All rights reserved.