Bad Matrix Reloaded — Listful Andrew

4 min read Original article ↗

Inspired by Bad Print, Aditya, in a fit of wild fancy (his words), wrote Bad Matrix. And you can promptly see that the results look just like Matrix digital rain not at all.

I was amused by the animation shown, so I tried to run the code — a single Bash function that delivers on its promise: the displayed effect is just as bad as advertized.

Now, the dollar-quoted stuff that shows up there, such as $'\235\246', is random, but the order of the kanji sequence isn’t — so I thought “hey, let’s change something to make this random as well”. Which I did.

Yet I seem to be incapable of restraining myself from tweaking other people’s Bash code when I put my hands on it. So there I go, renaming and regrouping things, and shuttling local functions out of the main one, and unbracing variables and compound commands, and adding arrays, and “hmm, I think this deserves to be a script instead of a single function”, and soon enough I have a whole file with another version of it — and then I might as well publish it, because people out there might really be in need of more Bad Matrices, and only a monster would hold back and deprive them of such basic necessities.

Therefore:

#!/usr/bin/env bash

## Bad Matrix Reloaded --- A still bad implementation of Matrix digital rain

# SPDX-FileCopyrightText: © 2023 Aditya Athalye
# SPDX-FileCopyrightText: © 2026 flandrew

#---------------------#
# Updated: 2026-04-20 #
#---------------------#
# Version: 0.2.0      #
#---------------------#

# SPDX-License-Identifier: GPL-3.0-or-later

## Commentary
#
# This is an intentionally bad implementation of Matrix digital rain.
#   https://en.wikipedia.org/wiki/Matrix_digital_rain
#
# "Bad" in the sense that the effect should look exactly like the original one
# not at all — but with enough green dimmed Japanese stuff showing up that you
# may perhaps be vaaaaguely reminded of the real thing.
#
# Aditya's original bad_matrix() function can be found here:
#   https://www.evalapply.org/posts/bad-matrix/index.html
#
# I (flandrew) pulled that function apart into this script; restructured and
# simplified a few things; and further decomplected some internal functions.
#
# The only visible change for the user should be that the kanji selection is
# now random (instead of always printing from the start: 日 一 大 年 中 会...)
# I think this looks better, which means it's less bad — which may be worse.
# But it still looks nearly as bad as before — which is most certainly good.
#
#############################################################################

#⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
## Code
### Setup

set -eo pipefail
hash sed tput || exit 127

#⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
### Variables

cols=$(tput cols)
rows=$(tput lines)               # one hundred common kanji:
ohck=(日 一 大 年 中 会 人 本 月 長 国 出 上 十 生 子 分 東 三 行
      同 今 高 金 時 手 見 市 力 米 自 前 円 合 立 内 二 事 社 者
      地 京 間 田 体 学 下 目 五 後 新 明 方 部 女 八 心 四 民 対
      主 正 代 言 九 小 思 七 山 実 入 回 場 野 開 万 全 定 家 北
      六 問 話 文 動 度 県 水 安 氏 和 政 保 表 道 相 意 発 不 党)

#⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
### Functions
#### Main

main() { cat /dev/urandom | print-line-fragment ;}

print-line-fragment()
while read -n "$((1 + RANDOM % cols))" line
do sleep 0.5
   maybe-goto-Y
   inject-matrix
   tput dim
   print-string-slowly "$line" &
done

#⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
#### Kanji

inject-matrix()
if ((RANDOM % 3 == 0))
then tput el
else maybe-goto-Y
     color-me-matrix
     print-kanji
fi

print-kanji()
for ((i=0; i++ < 1 + RANDOM % cols;))
do sleep 0.05
   print-a-random-kanji &
done

print-a-random-kanji()
{ printf "%s " "${ohck[RANDOM % ${#ohck[@]}]}" ;}

#⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
#### Slow-printing

print-string-slowly()
{ <<<"$1"  parse-chars | print-chars-slowly ;}

parse-chars()
{ LANG="en_US.UTF-8" sed -E 's/(.)/\1\n/g' &}

print-chars-slowly()
while read char
do printf "%q" "$char" | sed "s/''/ /g" &
done

#⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
#### Color

color-me-matrix()
{ maybe-dim; tput setaf 2 ;}

maybe-dim()
if ((RANDOM % 3 == 0))
then tput dim
else tput sgr0
fi

#⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
#### Position

maybe-goto-Y()
if ((RANDOM % 2 == 0))
then tput cup "$((RANDOM % rows))" 0
fi

#⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
### Run it!

main
exit 0

# Local Variables:
# coding:                     utf-8
# indent-tabs-mode:           nil
# sentence-end-double-space:  nil
# outline-regexp:             "###* "
# End:

## Bad Matrix Reloaded ends here

I thought about also changing something to make it rain from the top, instead of from the left; and that maybe we could replace the kanji with mirrored half-width kana. But better not: that would make it come dangerously close to the original, thereby running the risk of it actually looking good — which would be bad.

📆 2026-W17-1📆 2026-04-20