← Mike Higgins Home
Custom Code Experiment •

SVG: Animated Glyph

Source Code View HTML / CSS / JavaScript
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>SVG: Animated Glyph - Glitch Edition</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            background-color: #000;
            overflow: hidden;
            height: 100vh;
            width: 100vw;
            display: flex;
            justify-content: center;
            align-items: center;
            position: relative;
        }

        /* Glitch Background Effects */
        .glitch-bg {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            z-index: 0;
            opacity: 0.7;
        }

        .color-grid {
            position: absolute;
            width: 100%;
            height: 100%;
            background:
                linear-gradient(90deg,
                    transparent 0%,
                    transparent 50%,
                    rgba(255, 0, 255, 0.1) 50%,
                    rgba(255, 0, 255, 0.1) 100%),
                linear-gradient(0deg,
                    transparent 0%,
                    transparent 50%,
                    rgba(0, 255, 255, 0.1) 50%,
                    rgba(0, 255, 255, 0.1) 100%);
            background-size: 50px 50px;
            animation: gridMove 20s linear infinite;
            mix-blend-mode: screen;
        }

        .noise {
            position: absolute;
            width: 100%;
            height: 100%;
            background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noiseFilter'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.65' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noiseFilter)' opacity='0.3'/%3E%3C/svg%3E");
            opacity: 0.4;
            animation: noise 0.2s steps(10) infinite;
        }

        .color-burst {
            position: absolute;
            width: 100%;
            height: 100%;
            background: radial-gradient(circle at 30% 50%,
                rgba(255, 0, 255, 0.2) 0%,
                transparent 50%),
                radial-gradient(circle at 70% 20%,
                rgba(0, 255, 255, 0.2) 0%,
                transparent 50%),
                radial-gradient(circle at 50% 80%,
                rgba(255, 255, 0, 0.2) 0%,
                transparent 50%);
            animation: colorPulse 4s ease-in-out infinite;
            mix-blend-mode: overlay;
        }

        .scanlines {
            position: absolute;
            width: 100%;
            height: 100%;
            background: repeating-linear-gradient(
                0deg,
                transparent,
                transparent 2px,
                rgba(0, 255, 255, 0.05) 2px,
                rgba(0, 255, 255, 0.05) 4px
            );
            animation: scanline 10s linear infinite;
        }

        .glitch-overlay {
            position: absolute;
            width: 100%;
            height: 100%;
            background: linear-gradient(45deg,
                #ff00ff 0%,
                #00ffff 25%,
                #ffff00 50%,
                #ff00ff 75%,
                #00ffff 100%);
            opacity: 0;
            mix-blend-mode: hard-light;
            animation: glitchFlash 3s infinite;
        }

        .container {
            width: 100%;
            height: 100%;
            display: flex;
            justify-content: center;
            align-items: center;
            position: relative;
            z-index: 1;
        }

        svg {
            width: 90vmin;
            height: 90vmin;
            max-width: 800px;
            max-height: 800px;
            filter:
                brightness(1.2)
                contrast(1.1)
                drop-shadow(0 0 10px rgba(0, 255, 255, 0.3))
                drop-shadow(0 0 20px rgba(255, 0, 255, 0.2));
            animation: svgGlitch 5s infinite;
        }

        use {
            stroke: #ffffff;
            stroke-width: 0.5;
        }

        .frontCross {
            animation: colorShift 2s infinite alternate;
        }

        .instructions {
            position: absolute;
            bottom: 20px;
            color: #0ff;
            font-family: 'Courier New', monospace;
            font-size: 14px;
            text-align: center;
            width: 100%;
            text-shadow: 0 0 10px #0ff, 0 0 20px #0ff;
            z-index: 2;
            animation: textGlitch 5s infinite;
        }

        /* Animations */
        @keyframes gridMove {
            0% { background-position: 0 0; }
            100% { background-position: 100px 100px; }
        }

        @keyframes noise {
            0% { transform: translate(0, 0); }
            100% { transform: translate(10px, 10px); }
        }

        @keyframes colorPulse {
            0%, 100% { opacity: 0.5; transform: scale(1); }
            50% { opacity: 0.8; transform: scale(1.05); }
        }

        @keyframes scanline {
            0% { transform: translateY(0); }
            100% { transform: translateY(100px); }
        }

        @keyframes glitchFlash {
            0%, 95%, 100% { opacity: 0; }
            96%, 97%, 98%, 99% { opacity: 0.1; }
        }

        @keyframes svgGlitch {
            0%, 100% {
                transform: translate(0, 0) scale(1);
                filter: brightness(1.2) contrast(1.1);
            }
            1% {
                transform: translate(-2px, 1px) scale(1.001);
                filter: brightness(1.5) contrast(1.3) hue-rotate(10deg);
            }
            2% {
                transform: translate(2px, -1px) scale(0.999);
                filter: brightness(0.9) contrast(1.4) hue-rotate(-10deg);
            }
        }

        @keyframes colorShift {
            0% { fill: rgba(255, 255, 255, 0.5); }
            25% { fill: rgba(0, 255, 255, 0.5); }
            50% { fill: rgba(255, 0, 255, 0.5); }
            75% { fill: rgba(255, 255, 0, 0.5); }
            100% { fill: rgba(0, 255, 0, 0.5); }
        }

        @keyframes textGlitch {
            0%, 100% {
                opacity: 1;
                transform: translateX(0);
                text-shadow: 0 0 10px #0ff, 0 0 20px #0ff;
            }
            1% {
                opacity: 0.8;
                transform: translateX(-1px);
                text-shadow: 0 0 10px #f0f, 0 0 20px #f0f;
            }
            2% {
                opacity: 0.9;
                transform: translateX(1px);
                text-shadow: 0 0 10px #ff0, 0 0 20px #ff0;
            }
        }

        /* Add chromatic aberration effect */
        .chromatic {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: inherit;
            mix-blend-mode: screen;
            animation: chromatic 4s infinite;
            pointer-events: none;
        }

        .chromatic::before,
        .chromatic::after {
            content: '';
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: inherit;
            mix-blend-mode: screen;
        }

        .chromatic::before {
            filter: blur(2px) hue-rotate(120deg);
            opacity: 0.3;
            animation: chromaticShift 3s infinite;
        }

        .chromatic::after {
            filter: blur(1px) hue-rotate(240deg);
            opacity: 0.2;
            animation: chromaticShift 2s infinite reverse;
        }

        @keyframes chromatic {
            0%, 100% { transform: translate(0, 0); }
            25% { transform: translate(1px, 0); }
            50% { transform: translate(0, 1px); }
            75% { transform: translate(-1px, 0); }
        }

        @keyframes chromaticShift {
            0%, 100% { transform: translate(0, 0); }
            33% { transform: translate(0.5px, -0.5px); }
            66% { transform: translate(-0.5px, 0.5px); }
        }
    </style>
</head>
<body>
    <!-- Glitch Background Elements -->
    <div class="glitch-bg">
        <div class="color-grid"></div>
        <div class="noise"></div>
        <div class="color-burst"></div>
        <div class="scanlines"></div>
        <div class="glitch-overlay"></div>
        <div class="chromatic"></div>
    </div>

    <div class="container">
        <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 600">
            <title>Snake on Planes - Glitch Edition</title>
            <defs>
                <linearGradient id="glitchGradient" x1="0%" y1="0%" x2="100%" y2="100%">
                    <stop offset="0%" style="stop-color:#ff00ff;stop-opacity:1" />
                    <stop offset="50%" style="stop-color:#00ffff;stop-opacity:1" />
                    <stop offset="100%" style="stop-color:#ffff00;stop-opacity:1" />
                </linearGradient>

                <clipPath id="plane1Mask">
                    <polygon id="plane1" points="465.49 307.86 538.74 282.24 538.73 374.89 465.48 400.5 465.49 307.86" fill="#ffffff"/>
                </clipPath>
                <clipPath id="plane2Mask">
                    <polygon id="plane2" points="397.42 280.28 470.67 254.66 538.74 282.24 465.49 307.86 397.42 280.28" fill="#ffffff"/>
                </clipPath>
                <clipPath id="plane3Mask">
                    <polygon id="plane3" points="397.43 187.62 470.68 162.01 470.67 254.66 397.42 280.28 397.43 187.62" fill="#ffffff"/>
                </clipPath>
                <clipPath id="plane4Mask">
                    <polygon id="plane4" points="329.35 160.05 402.61 134.43 470.68 162.01 397.43 187.62 329.35 160.05" fill="#ffffff"/>
                </clipPath>
                <clipPath id="plane5Mask">
                    <polygon id="plane5" points="261.27 225.12 334.52 199.5 402.6 227.09 329.34 252.7 261.27 225.12" fill="#ffffff"/>
                </clipPath>
                <clipPath id="plane6Mask">
                    <polygon id="plane6" points="397.4 372.92 470.66 347.31 470.65 439.95 397.39 465.57 397.4 372.92" fill="#ffffff"/>
                </clipPath>
                <clipPath id="plane7Mask">
                    <polygon id="plane7" points="397.42 372.87 470.67 347.26 538.74 374.83 465.49 400.45 397.42 372.87" fill="#ffffff"/>
                </clipPath>
                <clipPath id="plane8Mask">
                    <polygon id="plane8" points="329.35 160.05 402.61 134.43 402.6 227.09 329.34 252.7 329.35 160.05" fill="#ffffff"/>
                </clipPath>
                <clipPath id="plane9Mask">
                    <polygon id="plane9" points="261.27 225.12 334.52 199.5 334.51 292.16 261.26 317.77 261.27 225.12" fill="#ffffff"/>
                </clipPath>
                <clipPath id="plane10Mask">
                    <polygon id="plane10" points="261.26 317.77 334.51 292.16 402.58 319.74 329.33 345.35 261.26 317.77" fill="#ffffff"/>
                </clipPath>
                <clipPath id="plane11Mask">
                    <polygon id="plane11" points="329.33 345.35 402.59 319.73 402.58 412.38 329.32 437.99 329.33 345.35"  fill="#ffffff"/>
                </clipPath>
                <clipPath id="plane12Mask">
                    <polygon id="plane12" points="329.32 437.99 402.58 412.38 470.65 439.95 397.39 465.57 329.32 437.99"  fill="#ffffff"/>
                </clipPath>
            </defs>

            <g class="whole">
                <g clip-path="url(#plane10Mask)">
                    <polygon id="line10" points="315.81 298.7 383.95 326.18 347.95 338.77 279.81 311.29 315.81 298.7" fill="url(#glitchGradient)"/>
                </g>

                <g clip-path="url(#plane12Mask)">
                    <polygon id="line12" points="383.95 418.9 452.09 446.38 416.09 458.97 347.94 431.49 383.95 418.9" fill="url(#glitchGradient)"/>
                </g>
                <use xlink:href="#plane12" opacity="0.8"/>
                <g clip-path="url(#plane11Mask)">
                    <polygon id="line11" points="383.95 326.18 383.95 418.83 347.95 431.42 347.95 338.77 383.95 326.18" fill="url(#glitchGradient)"/>
                </g>
                <use xlink:href="#plane11" opacity="0.6"/>
                <use xlink:href="#plane10" opacity="0.82"/>
                <use xlink:href="#plane7"/>
                <use xlink:href="#plane5"/>
                <use xlink:href="#plane6" opacity="0.8"/>
                <use xlink:href="#plane3"/>
                <use xlink:href="#plane8" opacity="0.5"/>

                <g clip-path="url(#plane7Mask)">
                    <polygon id="line7" points="451.97 353.82 520.11 381.3 484.11 393.89 415.96 366.41 451.97 353.82" fill="url(#glitchGradient)"/>
                </g>

                <g clip-path="url(#plane8Mask)">
                    <polygon id="line8" points="383.82 141.07 383.82 233.72 347.82 246.31 347.82 153.66 383.82 141.07" fill="url(#glitchGradient)" opacity="0.8"/>
                </g>
                <g clip-path="url(#plane9Mask)">
                    <polygon id="line9" points="315.74 206.15 315.74 298.8 279.74 311.39 279.74 218.73 315.74 206.15" fill="url(#glitchGradient)"/>
                </g>
                <use xlink:href="#plane9" opacity="0.51"/>
                <use xlink:href="#plane4" opacity="0.8"/>
                <use xlink:href="#plane1" opacity="0.8"/>
                <use xlink:href="#plane2"/>
                <g clip-path="url(#plane5Mask)">
                    <polygon id="line5" points="315.8 206.04 383.94 233.52 347.94 246.11 279.8 218.63 315.8 206.04" fill="url(#glitchGradient)" opacity="1"/>
                </g>

                <g clip-path="url(#plane1Mask)">
                    <polygon id="line1" points="520.11 288.75 520.11 381.39 484.11 393.98 484.11 301.34 520.11 288.75" fill="url(#glitchGradient)"/>
                </g>

                <g clip-path="url(#plane3Mask)">
                    <polygon id="line3" points="451.97 168.55 451.97 261.2 415.97 273.79 415.97 181.14 451.97 168.55" fill="url(#glitchGradient)"/>
                </g>

                <g clip-path="url(#plane6Mask)">
                    <polygon id="line6" points="451.97 353.82 451.97 446.47 415.97 459.06 415.97 366.41 451.97 353.82" fill="url(#glitchGradient)"/>
                </g>

                <g clip-path="url(#plane2Mask)">
                    <polygon id="line2" points="451.97 261.2 520.11 288.68 484.11 301.27 415.96 273.79 451.97 261.2" fill="url(#glitchGradient)"/>
                </g>

                <g clip-path="url(#plane4Mask)">
                    <polygon id="line4" points="383.82 141.07 451.97 168.55 415.97 181.14 347.82 153.66 383.82 141.07" fill="url(#glitchGradient)"/>
                </g>

                <polygon id="frontCross" class="frontCross" points="397.43 187.62 397.42 280.28 465.49 307.86 465.48 400.5 397.4 372.92 397.39 465.57 329.32 437.99 329.33 345.35 261.26 317.76 261.27 225.12 329.34 252.7 329.35 160.05 397.43 187.62" fill="#ffffff" stroke="url(#glitchGradient)"/>
            </g>
        </svg>
    </div>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.20.3/TweenMax.min.js"></script>
    <script>
        var xmlns = "http://www.w3.org/2000/svg",
            xlinkns = "http://www.w3.org/1999/xlink",
            select = function(s) {
                return document.querySelector(s);
            },
            selectAll = function(s) {
                return document.querySelectorAll(s);
            },
            mainTl,
            line1Tl,
            line2Tl,
            line3Tl,
            line4Tl,
            line5Tl,
            line6Tl;

        // Make the SVG visible immediately
        TweenMax.set('svg', {
            visibility: 'visible',
            scale: 1.2,
            transformOrigin: "center center"
        });

        // Scale up the entire animation
        TweenMax.set('.whole', {
            scale: 1.3,
            transformOrigin: "center center"
        });

        // Add random glitch effect to SVG
        function randomGlitch() {
            TweenMax.to('svg', 0.05, {
                x: (Math.random() - 0.5) * 20,
                y: (Math.random() - 0.5) * 20,
                scale: 1.2 + (Math.random() - 0.5) * 0.1,
                rotation: (Math.random() - 0.5) * 5,
                onComplete: function() {
                    TweenMax.to('svg', 0.1, {
                        x: 0,
                        y: 0,
                        scale: 1.2,
                        rotation: 0
                    });
                }
            });
        }

        // Trigger random glitches
        setInterval(randomGlitch, 3000);

        mainTl = new TimelineMax({repeat: -1, repeatDelay: 0}).timeScale(2);

        var line1Tl = new TimelineMax();
        line1Tl.fromTo('#line1', 1, {
            y: 100
        }, {
            y: -100,
            ease: Linear.easeNone
        })
        .fromTo('#line7', 0.5, {
            x: -0,
            y: -0
        }, {
            x: 69,
            y: 28,
            ease: Linear.easeNone
        }, '-=1');

        var line2Tl = new TimelineMax();
        line2Tl.fromTo('#line2', 1, {
            x: 69,
            y: 28
        }, {
            x: -69,
            y: -28,
            ease: Linear.easeNone
        });

        var line3Tl = new TimelineMax();
        line3Tl.fromTo('#line3', 1, {
            y: 100
        }, {
            y: -100,
            ease: Linear.easeNone
        });

        var line4Tl = new TimelineMax();
        line4Tl.fromTo('#line4', 1, {
            x: 69,
            y: 28
        }, {
            x: -69,
            y: -28,
            ease: Linear.easeNone
        });

        var line5Tl = new TimelineMax();
        line5Tl.fromTo('#line5', 1, {
            x: 69,
            y: 28
        }, {
            x: -69,
            y: -28,
            ease: Linear.easeNone
        });

        var line6Tl = new TimelineMax();
        line6Tl.fromTo('#line6', 1, {
            y: 100
        }, {
            y: -100,
            ease: Linear.easeNone
        });

        var line7Tl = new TimelineMax();
        line7Tl.fromTo('#line7', 0.5, {
            x: -69,
            y: -28
        }, {
            x: 0,
            y: 0,
            ease: Linear.easeNone
        });

        var line8Tl = new TimelineMax();
        line8Tl.fromTo('#line8', 1, {
            y: -100
        }, {
            y: 100,
            ease: Linear.easeNone
        });

        var line9Tl = new TimelineMax();
        line9Tl.fromTo('#line9', 1, {
            y: -100
        }, {
            y: 100,
            ease: Linear.easeNone
        });

        var line10Tl = new TimelineMax();
        line10Tl.fromTo('#line10', 1, {
            x: -69,
            y: -28
        }, {
            x: 69,
            y: 28,
            ease: Linear.easeNone
        });

        var line11Tl = new TimelineMax();
        line11Tl.fromTo('#line11', 1, {
            y: -100
        }, {
            y: 100,
            ease: Linear.easeNone
        });

        var line12Tl = new TimelineMax();
        line12Tl.fromTo('#line12', 1, {
            x: -69,
            y: -28
        }, {
            x: 69,
            y: 28,
            ease: Linear.easeNone
        });

        mainTl.add(line1Tl)
            .add(line2Tl, '-=0.5')
            .add(line3Tl, '-=0.5')
            .add(line4Tl, '-=0.5')
            .add(line8Tl, '-=0.5')
            .add(line5Tl, '-=0.5')
            .add(line9Tl, '-=0.5')
            .add(line10Tl, '-=0.5')
            .add(line11Tl, '-=0.5')
            .add(line12Tl, '-=0.5')
            .add(line6Tl, '--=0.5')
            .add(line7Tl, '-=0.5');

        TweenMax.set('.whole', {
            rotation: 0,
            svgOrigin: '400 300'
        });

        // Add color cycling to the gradient
        var gradient = document.querySelector('#glitchGradient');
        var stops = gradient.querySelectorAll('stop');

        function cycleColors() {
            TweenMax.to(stops[0], 2, {
                attr: { 'stop-color': getRandomColor() },
                ease: Power1.easeInOut
            });
            TweenMax.to(stops[1], 2, {
                attr: { 'stop-color': getRandomColor() },
                ease: Power1.easeInOut,
                delay: 0.5
            });
            TweenMax.to(stops[2], 2, {
                attr: { 'stop-color': getRandomColor() },
                ease: Power1.easeInOut,
                delay: 1
            });
        }

        function getRandomColor() {
            var colors = ['#ff00ff', '#00ffff', '#ffff00', '#ff0000', '#00ff00', '#0000ff'];
            return colors[Math.floor(Math.random() * colors.length)];
        }

        setInterval(cycleColors, 3000);
        cycleColors();
    </script>
</body>
</html>