• 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

TRUNK - VANTAJS PROCEDURAL SHADER SOURCE CODE

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
<meta name="theme-color" content="#222426" />
<title>VantaJS: Trunk</title>

<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r134/three.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.1.9/p5.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vanta@0.5.24/dist/vanta.trunk.min.js"></script>

<style>
    :root {
        --ui-fg:      #ffffff;
        --ui-line:    rgba(255, 255, 255, 0.32);
        --ui-panel:   rgba(8, 12, 18, 0.58);
        --ui-hot:     rgba(255, 255, 255, 0.16);
        --ui-glow:    rgba(20, 136, 204, 0.45);
        --mono:       ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Courier New", monospace;
        --safe-b:     max(16px, env(safe-area-inset-bottom));
        --safe-l:     max(16px, env(safe-area-inset-left));
        --safe-r:     max(16px, env(safe-area-inset-right));
    }

    body.retro {
        --ui-fg:    #8affa0;
        --ui-line:  rgba(138, 255, 160, 0.55);
        --ui-panel: rgba(2, 14, 6, 0.72);
        --ui-hot:   rgba(138, 255, 160, 0.16);
        --ui-glow:  rgba(138, 255, 160, 0.4);
    }

    * { margin: 0; padding: 0; box-sizing: border-box; }

    html, body {
        width: 100%;
        height: 100%;
        overflow: hidden;
        overscroll-behavior: none;
        background: #000;
        font-family: var(--mono);
        -webkit-tap-highlight-color: transparent;
        -webkit-text-size-adjust: 100%;
    }

    #vanta {
        position: fixed;
        inset: 0;
        touch-action: none;
        background: #000;
        filter: none;
        transition: filter 0.6s ease, background-color 0.6s ease;
    }
    body.retro #vanta {
        filter: grayscale(1) sepia(1) hue-rotate(75deg) saturate(6) brightness(0.9) contrast(1.25);
    }

    .crt {
        position: fixed;
        inset: 0;
        pointer-events: none;
        opacity: 0;
        z-index: 5;
        transition: opacity 0.6s ease;
    }
    body.retro .crt { opacity: 1; }

    .grain {
        position: fixed;
        top: -50%; left: -50%;
        width: 200%; height: 200%;
        background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
        opacity: 0.05;
        mix-blend-mode: overlay;
        animation: grainMove 0.5s steps(3) infinite;
    }

    .scanlines {
        position: fixed;
        inset: 0;
        background: repeating-linear-gradient(
            to bottom,
            rgba(0,0,0,0) 0px,
            rgba(0,0,0,0) 1px,
            rgba(0,0,0,0.35) 2px,
            rgba(0,0,0,0.35) 3px
        );
        mix-blend-mode: multiply;
    }

    .beam {
        position: fixed;
        left: 0; right: 0;
        top: -3px;
        height: 3px;
        background: linear-gradient(to right, transparent, rgba(140,255,170,0.55), transparent);
        filter: blur(1px);
        mix-blend-mode: screen;
        animation: sweep 9s linear infinite;
    }

    .vignette {
        position: fixed;
        inset: 0;
        background: radial-gradient(ellipse at center, rgba(0,0,0,0) 50%, rgba(0,0,0,0.7) 100%);
    }

    .flicker {
        position: fixed;
        inset: 0;
        animation: flicker 6s infinite;
    }

    @keyframes sweep     { 0% { top: -3px; } 100% { top: 100%; } }
    @keyframes grainMove {
        0%   { transform: translate(0, 0); }
        33%  { transform: translate(-4%, 5%); }
        66%  { transform: translate(3%, -4%); }
        100% { transform: translate(0, 0); }
    }
    @keyframes flicker {
        0%, 100% { background: rgba(0,0,0,0); }
        92%      { background: rgba(0,0,0,0); }
        93%      { background: rgba(120,255,160,0.05); }
        94%      { background: rgba(0,0,0,0.08); }
        95%      { background: rgba(120,255,160,0.03); }
        96%      { background: rgba(0,0,0,0); }
    }

    .controls {
        position: fixed;
        z-index: 20;
        left: var(--safe-l);
        right: var(--safe-r);
        bottom: var(--safe-b);
        display: flex;
        align-items: flex-end;
        justify-content: flex-end;
        pointer-events: none;
    }
    .controls > * { pointer-events: auto; }


    button {
        font-family: var(--mono);
        color: var(--ui-fg);
        background: var(--ui-panel);
        border: 1px solid var(--ui-line);
        backdrop-filter: blur(8px);
        -webkit-backdrop-filter: blur(8px);
        cursor: pointer;
        touch-action: manipulation;
        transition: background 0.25s ease, color 0.25s ease,
                    border-color 0.25s ease, box-shadow 0.25s ease, transform 0.2s ease;
    }
    button:focus-visible {
        outline: 2px solid var(--ui-fg);
        outline-offset: 3px;
    }

    #retro-btn {
        min-height: 48px;
        padding: 0 20px;
        border-radius: 999px;
        font-size: 13px;
        font-weight: 700;
        letter-spacing: 0.1em;
        text-transform: uppercase;
        display: inline-flex;
        align-items: center;
        gap: 10px;
        box-shadow: 0 0 28px var(--ui-glow);
    }
    #retro-btn .lamp {
        width: 8px;
        height: 8px;
        border-radius: 50%;
        background: currentColor;
        opacity: 0.35;
        transition: opacity 0.25s ease, box-shadow 0.25s ease;
    }
    body.retro #retro-btn .lamp {
        opacity: 1;
        box-shadow: 0 0 10px currentColor;
    }
    #retro-btn:disabled {
        opacity: 0.4;
        cursor: not-allowed;
        box-shadow: none;
    }

    @media (hover: hover) {
        .controls button:hover:not(:disabled) { background: var(--ui-hot); transform: translateY(-1px); }
        #retro-btn:hover:not(:disabled) { box-shadow: 0 0 46px var(--ui-glow); }
    }
    button:active:not(:disabled) { transform: scale(0.96); }

    #notice {
        position: fixed;
        z-index: 20;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
        max-width: min(440px, calc(100vw - 32px));
        padding: 18px 20px;
        border-radius: 12px;
        border: 1px solid var(--ui-line);
        background: var(--ui-panel);
        backdrop-filter: blur(12px);
        -webkit-backdrop-filter: blur(12px);
        color: var(--ui-fg);
        font-size: 13px;
        line-height: 1.6;
        text-align: center;
    }
    #notice[hidden] { display: none; }

    @media (max-width: 560px) {
        #retro-btn { padding: 0 16px; font-size: 12px; }
    }

    @media (max-width: 340px) {
        #retro-btn { padding: 0 12px; letter-spacing: 0.06em; }
    }

    @media (prefers-reduced-motion: reduce) {
        .grain, .beam, .flicker { animation: none; }
        * { transition-duration: 0.01ms !important; }
    }
</style>
</head>
<body>
    <div id="vanta"></div>

    <div class="crt" aria-hidden="true">
        <div class="grain"></div>
        <div class="scanlines"></div>
        <div class="beam"></div>
        <div class="vignette"></div>
        <div class="flicker"></div>
    </div>

    <p id="notice" role="status" hidden></p>

    <div class="controls">
        <button id="retro-btn" type="button" aria-pressed="false">
            <span class="lamp" aria-hidden="true"></span>
            <span id="retro-label">Retroscope</span>
        </button>
    </div>

<script>
document.addEventListener('DOMContentLoaded', function () {

    var COMMON = {
        el: "#vanta",
        mouseControls: true,
        touchControls: true,
        gyroControls: false,
        minHeight: 200.00,
        minWidth: 200.00
    };

    var DEF = {
        id: 'trunk',
        label: 'Trunk',
        ctor: 'TRUNK',
        needsP5: true,
        restartOnPalette: true,
        base: {
            scale: 1.00, scaleMobile: 1.00,
            spacing: 5.50, chaos: 6.00
        },
        retro: { color: 0x8affa0, backgroundColor: 0x02110a },
        bg: { normal: '#222426', retro: '#04120a' }
    };

    var vantaEl  = document.getElementById('vanta');
    var retroBtn = document.getElementById('retro-btn');
    var retroLbl = document.getElementById('retro-label');
    var notice   = document.getElementById('notice');

    var effect = null;
    var normalResolved = null;
    var isRetro = false;

    (function () {
        var NOISE = 'THREE.BufferAttribute: .length has been deprecated';
        var warn = console.warn.bind(console);
        console.warn = function (msg) {
            if (typeof msg === 'string' && msg.indexOf(NOISE) === 0) return;
            warn.apply(null, arguments);
        };
    })();

    function fail(message) {
        notice.textContent = message;
        notice.hidden = false;
        retroBtn.disabled = true;
    }

    function mount() {
        if (typeof VANTA === 'undefined' || !VANTA[DEF.ctor]) {
            fail(DEF.label + " didn't load. Check your connection and reload the page.");
            return;
        }
        if (DEF.needsP5 && typeof window.p5 === 'undefined') {
            fail(DEF.label + " needs p5.js, which didn't load. Check your connection and reload the page.");
            return;
        }

        try {
            effect = VANTA[DEF.ctor](Object.assign({}, COMMON, DEF.base));
        } catch (err) {
            console.error('[trunk] failed to start:', err);
            effect = null;
            fail(DEF.label + " couldn't start. Your browser may not support WebGL.");
            return;
        }

        normalResolved = DEF.normal || Object.keys(DEF.retro).reduce(function (acc, k) {
            acc[k] = effect.options[k];
            return acc;
        }, {});

        applyMode(true);
    }

    function applyMode(justMounted) {
        document.body.classList.toggle('retro', isRetro);
        if (effect) {
            effect.setOptions(isRetro ? DEF.retro : normalResolved);
            if (DEF.restartOnPalette && !(justMounted && !isRetro)) {
                try { effect.restart(); } catch (err) {
                    console.error('[trunk] restart failed:', err);
                }
            }
            vantaEl.style.backgroundColor = isRetro ? DEF.bg.retro : DEF.bg.normal;
        }
        retroLbl.textContent = isRetro ? 'Normal' : 'Retroscope';
        retroBtn.setAttribute('aria-pressed', String(isRetro));
    }

    retroBtn.addEventListener('click', function () {
        isRetro = !isRetro;
        applyMode();
    });

    window.addEventListener('pagehide', function () {
        if (effect) {
            try { effect.destroy(); } catch (err) {}
            effect = null;
        }
    });

    mount();
});
</script>
</body>
</html>
MikeHiggins.me.uk LinkedIn GitHub Bluesky Contra Upwork
© 2026 Mike Higgins. All rights reserved.