<pre id="output"></pre>
body {
  background: black;
}

#output {
  color: white;
  
  .w {
    color: white;
  }
  
  .g {
    color: #00DD00;
  }
  
  .d {
    color: #004400;
  }
}
const columns = 80;

// start from 0, 1, 2
let status = Math.random() * 3 | 0;

let pipe = [];
let count = 0;

// create render
let render = [];
for (let i = 0; i < columns; i++) {
    render.push({
      char: getRandomChar()
    });
}

function tick() {

    // fill the pipe

    while (count < columns) {

        let node = {code:status};

        switch (status) {
            case 0:
                node.length = 1;
                break;

            case 1:
                node.length = 1 + Math.random() * columns / 3 * 2 | 0;
                break;

            case 2:
                node.length = 1 + Math.random() * columns / 3 | 0;
                break;
        }

        // switch to next status 0, 1, 2, 0, 1, 2, ...
        status = (status + 1) % 3;

        count += node.length;
        pipe.push(node);
    }

    // render

    let idx = 0;
    outter:
        for (let node of pipe) {
            for (let i = 0; i < node.length; i++) {
                if (idx === columns) break outter;
              
                let r = render[idx];
                r.code = node.code;
                switch (node.code) {

                    case 0:
                        // always update char
                        r.char = getRandomChar();
                        break;

                    case 1:
                        // has a chance to update char
                        if (Math.random() < .05) {
                            r.char = getRandomChar();
                        }
                        break;

                    case 2:
                        /* do nothing */
                        // switch to lowercase
                        // let charCode = r.char.charCodeAt(0);
                        // if (charCode < 97) {
                        //     r.char = String.fromCharCode(charCode + 32);
                        // }
                        break;
                }

                idx++;
            }
        }

    // output
  
    document.getElementById('output').innerHTML = render.map(function(r){
      return '<span class="'+ ['w','g','d'][r.code] + '">' + r.char + '</span>';
    }).join('');

    // tick

    if (!--pipe[0].length) {
        pipe.shift();
    }

    count--;
}

setInterval(tick, 50);

function getRandomChar() {
    return String.fromCharCode(65 + Math.random() * 26 | 0);
}

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.