ワンライナー

(function rec(e) { const FLUCTUATION_WIDTH = 0.1; e.nodeType === e.TEXT_NODE ? e.data = [...e.data].map(s => s.match(/\s/) ? s : String.fromCodePoint(Math.max(s.codePointAt(0) + Math.round(Math.random() * (1 + FLUCTUATION_WIDTH * 2) - (0.5 + FLUCTUATION_WIDTH)), 0x20))).join("") : [...e.childNodes].forEach(rec); })(document.body);

Minify

(function rec(e){const FLUCTUATION_WIDTH=0.1;e.nodeType===e.TEXT_NODE?e.data=[...e.data].map(s=>s.match(/\s/)?s:String.fromCodePoint(Math.max(s.codePointAt(0)+Math.round(Math.random()*(1+FLUCTUATION_WIDTH*2)-(0.5+FLUCTUATION_WIDTH)),0x20))).join(""):[...e.childNodes].forEach(rec);})(document.body);

要点解説

インデントして比較的ましになったコード

(function rec(e) {
  const FLUCTUATION_WIDTH = 0.1;
  e.nodeType === e.TEXT_NODE
    ? e.data = [...e.data]
      .map(c =>
        c.match(/\s/)
          ? c
          : String.fromCodePoint(Math.max(c.codePointAt(0) + Math.round(Math.random() * (1 + FLUCTUATION_WIDTH * 2) - (0.5 + FLUCTUATION_WIDTH)), 0x20))
      ).join("")
    : [...e.childNodes].forEach(rec);
})(document.body);

(function rec(e) { /*...*/ })(document.body);

名前付きのIIFEによる即自実行。
名前があることにより、再帰呼出が可能になる。1
引数にはHTMLドキュメントのbody全体。HTMLElementであれば何でもかまわない。(eはelementの略)

const FLUCTUATION_WIDTH = 0.1
後述する文字コードを震わせる処理の震わせる幅・確率を定義する。

e.nodeType === e.TEXT_NODE ? /*...*/ : /*...*/ ;

ノードがTEXT_NODEであれば、文字コードを震わせる処理をテキストに対して行う。

? e.data = [...e.data].map( /*...*/ ).join("")
e(テキストノード)内部の文字列を.map()で変換したものに書き換える。
[...e.data]で文字列を配列へと展開する。.split("")と異なりこの方法だとサロゲートペアを分割しない。
そして、.join("")で配列から文字列へ再結合する。

c => c.match(/\s/) ? c : /*...*/
c(文字)が空白・タブ・改行(文字クラス\s)のいずれかであれば変換しない。 regex
さすがにテキストの構造までは崩したくないし、この辺の文字の周辺は下駄1や豆腐2ばかりだからです。

: String.fromCodePoint(Math.max(c.codePointAt(0) + Math.round(Math.random() * (1 + FLUCTUATION_WIDTH * 2) - (0.5 + FLUCTUATION_WIDTH)), 0x20))
String.fromCodePoint()c.codePointAt(0)1で一度数値に変換し、文字に戻す流れ。
String.fromCodePoint()に負の数を渡すと例外を発生させてしまうため、Math.max(/*...*/, 0x20)でその他有象無象の制御文字と共に全て空白文字にフォールバックしてしまう。

文字コードを震わせる処理をランダム関数で行う。
定義したFLUCTUATION_WIDTHに応じて、以下のように文字が震える確率を制御する。

         ランダム範囲
.........------------.........
-1        一文字範囲        +1
!!!!!!!!!!----------!!!!!!!!!!

String.fromCodePoint()に小数を渡すと例外を発生させてしまうため、Math.round()で整数に丸める。

ノードがTEXT_NODEでなければ、そのノードの全ての子に対してこの関数を呼び出す。

: [...e.childNodes].forEach(rec)
末端のノードに達するまでこの関数を再帰呼び出しする。
末端のノードであればこの時点で自然とコールスタックの積み上げは停止する。

Footnotes

  1. arguments.calleeでも可能だが、現在は非推奨になっているため使用していない。