JSFiddle - React, Tailwind, and code Playground

HTML

<span class="word" data-pulsate="true">Love</span>
<span class="word" data-pulsate="true">Enjoy</span>
<span class="word" data-pulsate="true">Huggs</span>
<span class="word" data-pulsate="true">Huggs</span>

CSS

body {
    background: black;
    color: white;
}

.word {
    position: absolute;
    text-shadow: 0 0 5px rgba(255, 255, 255, 0.9);
    font-size: 0px;
    /* Make height of .word to equal it's font size */
    line-height: 1;
}

JavaScript

+ function($) {
      
    var pulsar = {
        // Delay between words appearance
        delay: 400,
        // Word animation do not really depend on pulsar.delay,
        // but if you set pulsar.delay small and wordAnimationDuration long
        // some words will skip their turns
        wordAnimationDuration: pulsar.delay * 3,
        // Depending on maximum font size of words we calculate the number of rows
        // to which the window can be divided
        maxFontSize: 40,
        start: function () {
            this.computeRows();
            this.fillWords();
            this.animate();
        },
        // Calculate the height or row and store each row's properties in pulsar.rows
        computeRows: function () {
            var height = document.body.parentNode.clientHeight;
            var rowsCount = Math.floor(height/this.maxFontSize);
            this.rows = [];
            for (var i = 0; i < rowsCount; i++) {
                this.rows.push({
                    index: i,
                    isBusy: false
                });   
            }
        },
        // Store Word instances in pulsar.words
        fillWords: function () {
            this.words = [];
            var words = document.querySelectorAll('[data-pulsate="true"]');
            for (var i = 0; i < words.length; i++) {
                this.words.push(new Word(words[i], this.wordAnimationDuration, this.maxFontSize));
            }
        },
        // When it comes time to  animate another word we need to know which row to move it in
        // this random row should be empty at the moment
        getAnyEmptyRowIndex: function () {
            var emptyRows = this.rows.filter(function(row) {
                return !row.isBusy;
            });
            if (emptyRows.length == 0) {
                return -1;
            }
            var index = emptyRows[Math.floor(Math.random() * emptyRows.length)].index;
            this.rows[index].isBusy = true;
            return...