<css-doodle id="myDoodle">
        :doodle {
            @grid: 36 / 100%;
            background-color: blue;
        }
        mix-blend-mode: difference;
        :after {
            font-size:@r(1vmax, 4vmax);
            font-family:Saira Stencil One;
            content:\@hex(@pd(65,97));
            color: @pd(#906131,#313190,#313190,#7f9031,#506eaf);
            transform: skew(0deg) scale(1) rotate(-45deg);
        }
    </css-doodle>
    body {
        width: 100vmax;
        height: 100vmax;
        background-color: black;
        overflow: hidden;
        padding: 0px;
        margin: 0px;
        filter: grayscale(90);
    }
(function () {
    const characterSets = {
        key: "As",
        ranges: [
            { min: 0x0041, max: 0x0041 }, /* basic latin "A" */
            { min: 0x0061, max: 0x0061 }, /* basic latin 'a' */
            {  min: 0x0100,  max: 0x0105  }, /* a's from latin extended A */
            {  min: 0x01DE,  max: 0x01E1  }, /* a's from latin extended B */
            {  min: 0x2C6F,  max: 0x2C6F  }, /* a's from latin extended C */
            {  min: 0x1EA2,  max: 0x1EB7  }, /* a's from latin ext additional */
        ]
    }
    const paletteFuncs = [spectrum, triads, analogs, splitComplements, complements]
    const blendModes = ["normal", "difference", "multiply", "exclusion", "hard-light", "luminosity"]
    const rotations = ["@pd(0, 45deg, -45deg, 90deg, -90deg, 180deg)", "@pn(0, 45deg, 90deg, 180deg, -90deg, -45deg)", "@r(360deg)", "45deg", "-45deg", "0", "@pd(0, 180deg)", "@pd(0, 10deg, 20deg, 30deg, 40deg, 50deg, 60deg, 70deg, 80deg, 90deg)", "@pn(0, 10deg, 20deg, 30deg, 40deg, 50deg, 60deg, 70deg, 80deg, 90deg)", "@pn(0, -10deg, -20deg, -30deg, -40deg, -50deg, -60deg, -70deg, -80deg, -90deg)", "@pn(0, 15deg, 30deg, 45deg, 60deg, 75deg, 90deg, 105deg, 120deg, 135deg, 150deg, 165deg, 180deg)", "@pd(-90deg, 90deg)", "@pd(45deg, -135deg)", "@pd(-45deg, 135deg)"]
    const API_KEY = "AIzaSyBySDtOFjhW9moTHVy6qc2gcshwzTTGiDA"
    var Fonts = []
    var selectedFonts = []

    /*INITIALIZE*/
    async function main() {

        Fonts = await loadFontsList()
        var dood = document.querySelector("#myDoodle")
        dood.addEventListener("click", function(e) {
            let fontMultiplier = getRandomIntInclusive(1,3)
            selectedFonts = []
            for(let i = 0; i<fontMultiplier; i++){
                let thisFont = loadRandomFont()
                selectedFonts.push(thisFont)
            }
            dood.update(characters())
        })
    }
    main()

    /* HELPER FUNCTIONS */
    /* main generator function */
    function characters() {

        //fonts
        let fontSize = Math.random() > .5 ? "2vmax" : `@r(1vmax, 4vmax)`
        let fr = Math.random()
        let fontPicker = fr > .66 ? "@pd" : fr > .33 ? "@p" : "@pn"
        fontFamily = `${fontPicker}(${selectedFonts})`
        
        //characters (All A's all the time)
        let myChars = []
        myChars.ranges = Math.random()>.5 ? characterSets.ranges : [characterSets.ranges[0], characterSets.ranges[1]]
        if(Math.random()>.5){
            myChars.ranges = [myChars.ranges[0], myChars.ranges[1]]
        }
        let set = flatten(myChars)
        let pr = Math.random()
        let picker = pr > .66 ? "@p" : pr > .33 ? "@pd" : "@pn"
        if (Math.random() > .5) { //whole set or usually, a slice/selection of set
            let num = getRandomIntInclusive(1, 3)
            set = Math.random() > .5 ? randomPicks(set, num) : randomSlice(set, num)
        }
        let codeSelector = `\\@hex(${picker}(${set}))`

        //rotation, blend modes, other affectations
        let rotation = pick(rotations)
        let blendModePicker = Math.random() > .5 ? "@p" : "@pd"
        r = Math.random()
        let myBlends = r > .66 ? randomSlice(blendModes) : r > .33 ? randomPicks(blendModes) : blendModes
        let mixBlendMode = Math.random() > .5 ? `${blendModePicker}(${myBlends})` : pick(blendModes)
        let scale = Math.random > .5 ? "@r(.5, 2.2)" : getRandomIntInclusive(.5, 2.2)
        let skew = Math.random() > .5 ? getRandomIntInclusive(-40, 40) : 0

        return `:doodle {
            @grid: ${getRandomIntInclusive(10,40)} / 100%;
            background-color:@p(blue, green, red, yellow, pink, cyan, orange, purple);
        }
        mix-blend-mode: ${mixBlendMode};
        :after {
            font-size:${fontSize};
            font-family:${fontFamily};
            content:${codeSelector};
            color: ${Math.random() > .5 ? "@p" : "@pd"}(${getPalette()});
            transform: skew(${skew}deg) scale(${scale}) rotate(${rotation});
        }`
    }
    async function loadFontsList() {
        try {
            const result = await fetch('https://www.googleapis.com/webfonts/v1/webfonts?key=' + API_KEY);
            const data = await result.json();
            return data.items;
        } catch (error) {
            console.log('loadFontsList', error, error.message);
        }
    }

    function loadRandomFont() {
        const index = Math.floor(Math.random() * Fonts.length);
        const chosenFont = Fonts[index].family;
        WebFont.load({
            google: {
                families: [chosenFont]
            }
        });
        return chosenFont;
    }
    function getPalette(seed, palette) {
        if (!palette) palette = pick(paletteFuncs)
        if (!seed) seed = getRandomColor()
        return palette(seed)
    }
    function getRandomColor() {
        let hue = Math.floor(Math.random() * 360)
        let saturation = Math.floor(Math.random() * (100 - 30) + 30)
        return tinycolor(`hsl(${hue}, ${saturation}, 50%)`)
    }
    function pick(arr) {
        return arr[Math.floor(Math.random() * arr.length)]
    }
    function randomSlice(arr, num) {
        num = num || getRandomIntInclusive(1, arr.length)
        let start = getRandomIntInclusive(0, arr.length - 1)
        while (start + num > arr.length) {
            start--
        }
        return arr.slice(start, start + num)
    }
    function randomPicks(arr, num) {
        num = num || getRandomIntInclusive(1, arr.length)
        let picks = []
        for (let i = 0; i < num; i++) {
            picks.push(pick(arr))
        }
        return picks
    }
    function getRandomIntInclusive(min, max) {
        min = Math.ceil(min)
        max = Math.floor(max)
        return Math.floor(Math.random() * (max - min + 1)) + min
    }
    function flatten(set) {
        let flat = []
        let ranges = set.ranges || [{
            min: set.min,
            max: set.max
        }]
        ranges.forEach(range => {
            for (let i = range.min; i <= range.max; i++) {
                flat.push(i)
            }
        })
        return flat
    }

    /*palette functions (overkill for rotating some hues around before applying grayscale filter)*/
    function spectrum(seed) {
        let c1 = tinycolor(seed).spin(getRandomIntInclusive(-160, -130)).toHexString()
        let c2 = tinycolor(seed).spin(getRandomIntInclusive(-92, -52)).toHexString()
        let c3 = seed.toHexString()
        let c4 = tinycolor(seed).spin(getRandomIntInclusive(52, 92)).toHexString()
        let c5 = tinycolor(seed).spin(getRandomIntInclusive(130, 160)).toHexString()
        return [c1, c2, c3, c4, c5]
    }

    function triads(seed) {
        let smallRando = getRandomIntInclusive(5, 25)
        let c1 = tinycolor(seed).desaturate(smallRando).lighten(smallRando).toHexString()
        let c2 = tinycolor(seed).spin(getRandomIntInclusive(-150, -90)).toHexString()
        let c3 = seed.toHexString()
        let c4 = tinycolor(seed).spin(120).toHexString()
        let c5 = tinycolor(seed).spin(getRandomIntInclusive(90, 150)).saturate(smallRando).darken(smallRando).toHexString()
        return [c1, c2, c3, c4, c5]
    }

    function analogs(seed) {
        let smallRando = getRandomIntInclusive(0, 20)
        let c1 = tinycolor(seed).spin(getRandomIntInclusive(-30, -15)).toHexString()
        let c2 = tinycolor(seed).spin(getRandomIntInclusive(-15, 0)).saturate(smallRando).darken(smallRando).toHexString()
        let c3 = seed.toHexString()
        let c4 = tinycolor(seed).spin(getRandomIntInclusive(0, 15)).desaturate(smallRando).lighten(smallRando).toHexString()
        let c5 = tinycolor(seed).spin(getRandomIntInclusive(15, 30)).toHexString()
        return [c1, c2, c3, c4, c5]
    }

    function splitComplements(seed) {
        let smallRando = getRandomIntInclusive(0, 20)
        let c1 = tinycolor(seed).spin(getRandomIntInclusive(-180, -130)).saturate(smallRando).darken(smallRando).toHexString()
        let c2 = tinycolor(seed).spin(-150).toHexString()
        let c3 = seed.toHexString()
        let c4 = tinycolor(seed).spin(getRandomIntInclusive(-180, -130)).toHexString()
        let c5 = tinycolor(seed).spin(150).desaturate(smallRando).lighten(smallRando).toHexString()
        return [c1, c2, c3, c4, c5]
    }

    function complements(seed) {
        let isDark = tinycolor(seed).isDark()
        let c1 = tinycolor(seed).saturate(getRandomIntInclusive(5, 15)).darken(getRandomIntInclusive(10, 20)).toHexString()
        let c2 = tinycolor(seed).desaturate(getRandomIntInclusive(0, 20)).lighten(getRandomIntInclusive(5, 25)).toHexString()
        let c3 = seed.toHexString()
        let c4 = tinycolor(seed).spin(180).toHexString()
        let c5 = isDark ? tinycolor(seed).spin(getRandomIntInclusive(160, 200)).desaturate(getRandomIntInclusive(5, 15)).lighten(getRandomIntInclusive(10, 20)).toHexString() :
            tinycolor(seed).spin(getRandomIntInclusive(160, 200)).saturate(getRandomIntInclusive(0, 20)).darken(getRandomIntInclusive(10, 20)).toHexString()
        return [c1, c2, c3, c4, c5]
    }
})("sweaverD.com")

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://ajax.googleapis.com/ajax/libs/webfont/1.6.26/webfont.js
  2. https://unpkg.com/css-doodle@0.7.2/css-doodle.min.js
  3. https://codepen.io/sweaver2112/pen/f75cd4a9b942eaeacb9aa74adc44c03c.js