GODRICH
#cssアニメーション#ボタン#ローディング#トランジション#スクロール#ダッシュボード#実用ui#デザイントレンド

ローディング アニメーション js 9パターン — 物理演算で作る

ローディング アニメーション js の一つの姿: 減衰定数0.92が重力ローダーの跳ね上がる高さを縮め、セルオートマトンローダーは一つの生存ルール値から世代ごとに満たされるセルの割合を計算し、磁場ローダーは軌道半径の値が0に近づいた瞬間に終わる — 同じローダーなのに読み取る計算はそれぞれ違う。

自動再生 · タイルを押すとその項目へ · すべて1つのzip

9という数は適当に選んだ丸い数字ではありません。それぞれが「進行度」を違う方法で表現しています — 上昇する水面の高さ(01)、粒子の到着比率(02)、積み上がったブロックの数(03)、ボロノイ図を埋める種点の数(04)、縮んでいく軌道半径(05)、削られるノイズリングの厚さ(06)、波打つバネ格子の半径(07)、掃くように進む角度(08)、セルオートマトンの生存比率(09)です。単純な1つの数値が形を動かすところから、格子全体が近傍と相談して結果を出すところまで、段階を追って並べています。

デモ

上のグリッドは9つの独立したcanvas要素で、それぞれが自前のrequestAnimationFrameループを2Dコンテキスト上で回しています。どこかのペンを埋め込んだだけのcss ローディング アニメーションのコピペ集ではありません — 各タイルの実際のソースは、この下のパターンごとに全文が印刷されています。タイルをクリックするか、Enterキー・Spaceキーで操作するか、ウィンドウをリサイズすると、そのパターンはその瞬間の進行度値を再計算します。CSSアニメーションのtransitionendを待つ処理はどこにもありません — そもそもCSSアニメーションが下で動いていないからです。

なぜローダーは進行度を自分で計算するのか

CSSキーフレームアニメーションは、透明度・transform・枠線の太さといったプロパティ自身の値を、決まったタイミング曲線に沿って一つの数字から別の数字へ補間することしかできません。到着した粒子の数を数えたり、落下したブロックを高さ順に並べたり、格子の9つの隣接セルに「生きるべきか」を尋ねたりはできません。以下の9つはどれも、水面の高さ・到着数・跳ね返りの高さ・種点の数・軌道半径・浸食の深さ・波紋の半径・掃引角度・生存比率という、1フレームごとに読み取るプレーンなJavaScriptの数値を持っていて、その数値からcanvasのfillとstrokeで直接絵を描いています。つまり進行度はCSSのパーセンテージから読み取るのではなく、物理演算そのものから生まれます。

9つとも本物のMath.randomは使っていません。02の粒子の位相や04の種点の揺れ幅は、ページ読み込み時に一度だけ決まる値(粒子ごとの位相・周波数、あるいは種点の揺れ半径)から作られているので、リロードするたびに同じ動きが再現され、毎回バラバラに崩れることはありません。この記事の裏にある9つのファイルには、ライブラリもWebGLコンテキストもCDNスクリプトも一つもありません。

下の計測値が実際に何を数えているか

下の数値はそれぞれのデモの画面収録の実測値であり、目分量ではありません。累積面積はループ全体でキャンバスのどれだけが変化したか、平均強度は変化した箇所のピクセル差分バイトの平均値、動いたフレームは23回のフレーム間差分のうち実際にピクセルが動いた回数です。

# パターン 累積面積 平均強度 動いたフレーム
01 液体波の水位ローダー 57.08% 187.0 23/23
02 粒子群収束カウントローダー 15.628% 41.7 23/23
03 重力落下スタックローダー 6.757% 145.2 23/23
04 ボロノイセル種点成長ローダー 37.312% 82.1 23/23
05 磁場軌道半径ローダー 11.671% 65.7 23/23
06 ノイズリング浸食ローダー 8.508% 99.3 23/23
07 バネ格子波紋半径ローダー 22.819% 58.1 23/23
08 衛星軌道スイープローダー 19.327% 51.2 23/23
09 セルオートマトン充填率ローダー 33.618% 43.4 23/23

パターンごとのコード

以下の9つはどれも同じ形を辿ります — 進行度を0〜1の数値として読み取り、物理か場の計算を1つ実行し、結果を2Dコンテキストに描く、という形です。違うのは中間の計算だけです。コードの範囲はデモのソースからそのまま切り出しています。

01進行度の値が満ちていく液体波ローダー

進行度(0〜1)をキャンバスの水面の高さに変換し、2つの正弦波を重ねて表面を揺らすことで、値が上がるほど液体が満ちるようにローダーの内側が埋まっていきます。ファイルアップロード進捗モーダルやアプリのスプラッシュローダーに向いています — パーセンテージだけだと静止して見える場面です。タイルをクリックするか、Enterキー・Spaceキーを押すと、2つの波がもう一度表面を揺らします。

進行度の値が満ちていく液体波ローダー
新しいタブで開く

01 · 進行度の値が満ちていく液体波ローダー — 計測 累積面積 57.08% · 動いたフレーム 23/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

FREQcreateLinearGradientLIQUID_DEEP
function draw(sec) {
  ctx.clearRect(0, 0, W, H);
  ctx.fillStyle = PAPER;
  ctx.fillRect(0, 0, W, H);

  var t = sec % LOOP;
  var p = t / LOOP;                  // 0→1 進捗 — ノコギリ波。ループするので値は0から上がるのを繰り返す
  var y0 = level(p);

  function waveY(x) {
    return y0
      + AMP1 * Math.sin(x * FREQ + sec * SPEED1)
      + AMP2 * Math.sin(x * FREQ * 1.8 + sec * SPEED2);
  }

  ctx.beginPath();
  ctx.moveTo(0, H);
  ctx.lineTo(0, waveY(0));
  var step = 4;
  for (var x = 0; x <= W; x += step) ctx.lineTo(x, waveY(x));
  ctx.lineTo(W, H);
  ctx.closePath();
  var grad = ctx.createLinearGradient(0, y0 - 24, 0, H);
  grad.addColorStop(0, LIQUID);
  grad.addColorStop(1, LIQUID_DEEP);

02到着した粒子数の値が満たす群れ収束ローダー

ランダムに散らばった数十個の粒子のうち中心に到着した数を全体数で割った比率を進行度とし、到着した粒子が増えるほど中心の円が明るくなっていきます。AI応答待機画面やダッシュボードのデータ集計ローダーに向いています — スピナーだけだと実際どれだけ処理が進んだか分かりません。ウィンドウをリサイズしても、群れは同じ到着比率のまま再描画されます。

到着した粒子数の値が満たす群れ収束ローダー
新しいタブで開く

02 · 到着した粒子数の値が満たす群れ収束ローダー — 計測 累積面積 15.628% · 動いたフレーム 23/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

createRadialGradientglobalAlphaPAPER_FADE
function draw(sec) {
  // 完全にクリアせず、薄い板色を毎回重ね塗りする — 粒子が通った跡に
  // 尾が残り、群れが生きて動いている印象になる(フレームごとの画素変化が大きい理由)。
  ctx.fillStyle = PAPER_FADE;
  ctx.fillRect(0, 0, W, H);

  var t = sec % LOOP;
  var p = t / LOOP;                              // 0→1 進捗 — 到着済み粒子の割合の目標値
  var arrived = Math.floor(p * N);
  var cx = W * 0.5, cy = H * 0.5;

  for (var i = 0; i < N; i++) {
    var q = particles[i];
    var tx, ty;
    if (q.rank < arrived) {
      var orbit = 5 + 3 * Math.sin(sec * q.jf + q.jp);
      tx = cx + Math.cos(sec * 1.4 + q.jp) * orbit;
      ty = cy + Math.sin(sec * 1.4 + q.jp) * orbit;
    } else {
      tx = q.hx * W + Math.sin(sec * q.jf + q.jp) * 4;
      ty = q.hy * H + Math.cos(sec * q.jf * 0.8 + q.jp) * 4;
    }
    q.px += (tx - q.px) * 0.14;
    q.py += (ty - q.py) * 0.14;
  }

03落下高さの値が積み上げる重力ブロックローダー

進行度に応じたブロック数を決めて上から重力加速度で一つずつ落とし、床に触れるたびに跳ねる高さを減衰させ、積み上がったブロック数で進行度を示します。ゲームのリソース読み込み画面やオンボーディング待機画面に向いています — 物理的に積み上がる様子はバーより実感が湧きます。タイルの上でRキーを押すと、落下を最初からやり直せます。

落下高さの値が積み上げる重力ブロックローダー
新しいタブで開く

03 · 落下高さの値が積み上げる重力ブロックローダー — 計測 累積面積 6.757% · 動いたフレーム 23/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

DAMPINGGRAVITYarcTo
  for (var i = 0; i < BLOCKS; i++) {
    var b = blocks[i];
    if (b.state === 'waiting') {
      if (t >= i * DROP_INTERVAL) { b.state = 'falling'; b.y = -blockH; b.vy = 0; }
    }
    if (b.state === 'falling') {
      b.vy += GRAVITY * dt;
      b.y += b.vy * dt;
      var floorY = (H - margin) - (i + 1) * blockH;
      if (b.y >= floorY) {
        b.y = floorY;
        b.vy = -b.vy * DAMPING;
        if (Math.abs(b.vy) < REST_V) { b.state = 'settled'; b.vy = 0; }
      }
    }
    if (b.state === 'settled') settled++;
  }

04シード数の値が広げるボロノイセルローダー

進行度に比例して種点の数を増やし、毎フレームボロノイ図を再計算することで、セルが密になるほどローダーの内側が完成したモザイクで埋まっていきます。ポートフォリオページの読み込みや画像ギャラリーのプリローダーに向いています — 完成したモザイクは「単に読み込み中」より「コンテンツが揃った」感じに見えます。タイルをクリックすると、種点の成長を空の格子からやり直します。

シード数の値が広げるボロノイセルローダー
新しいタブで開く

04 · シード数の値が広げるボロノイセルローダー — 計測 累積面積 37.312% · 動いたフレーム 23/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

MAXSEEDSwobbleRsetTransform
  for (var i = 0; i < MAXSEEDS; i++) {
    seeds[i].px = seeds[i].hx0 + Math.sin(sec * seeds[i].wf + seeds[i].wp) * wobbleR;
    seeds[i].py = seeds[i].hy0 + Math.cos(sec * seeds[i].wf * 0.86 + seeds[i].wp) * wobbleR;
  }

  ctx.fillStyle = PAPER;
  ctx.fillRect(0, 0, W, H);

  for (var r = 0; r < rows; r++) {
    var cy = r * CELL + CELL / 2;
    for (var c = 0; c < cols; c++) {
      var cx = c * CELL + CELL / 2;
      var best = 0, bestD = Infinity;
      for (var s = 0; s < count; s++) {
        var dx = cx - seeds[s].px, dy = cy - seeds[s].py;
        var d = dx * dx + dy * dy;
        if (d < bestD) { bestD = d; best = s; }
      }
      ctx.fillStyle = PALETTE[best % PALETTE.length];
      ctx.globalAlpha = 0.55;
      ctx.fillRect(c * CELL, r * CELL, CELL - 1, CELL - 1);
    }
  }

05軌道半径の値が縮める磁場ローダー

進行度を軌道半径の値に変換し、粒子が磁場線に沿って中心に引き寄せられるように円軌道を描きながら縮まり、半径が0に近づくとローダーが終わります。カード型ローディングスピナーやヘッダーの読み込みインジケーターに向いています — 磁場線が能動的に引き寄せている印象を与えます。タイルにタブで移動してEnterキーを押すと、半径がもう一度縮んでいきます。

軌道半径の値が縮める磁場ローダー
新しいタブで開く

05 · 軌道半径の値が縮める磁場ローダー — 計測 累積面積 11.671% · 動いたフレーム 23/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

createRadialGradientPAPER_FADEshrink
  var t = sec % LOOP;
  var p = t / LOOP;                       // 0→1 進捗
  var shrink = 1 - p;                     // 軌道半径の倍率 — p=1でちょうど0(吸引完了)

  ctx.fillStyle = PAPER_FADE;
  ctx.fillRect(0, 0, W, H);

  ctx.strokeStyle = LINE;
  ctx.lineWidth = 1.6;
  for (var i = 0; i < N; i++) {
    var q = parts[i];
    var rad = fieldR * q.r0 * shrink;
    var ang = q.phase + sec * q.speed;
    var x = cxp + Math.cos(ang) * rad;
    var y = cyp + Math.sin(ang) * rad;
    ctx.beginPath();
    ctx.moveTo(cxp, cyp);
    ctx.lineTo(x, y);
    ctx.stroke();
    ctx.fillStyle = PALETTE[i % PALETTE.length];
    ctx.beginPath();
    ctx.arc(x, y, 6.2, 0, Math.PI * 2);
    ctx.fill();
  }

06浸食厚さの値が削るノイズリングローダー

2Dノイズフィールド上に描いたリングの内側半径を、進行度に応じて食い込む浸食厚さの値で決め、ノイズがでこぼこに削られながらリングが細くなっていきます。検索結果待機画面や長いフォーム送信時のローダーに向いています — でこぼこした浸食はなめらかな偽物より「本当に処理している」感じが出ます。ウィンドウをリサイズすると、新しいサイズでリングが再び浸食されます。

浸食厚さの値が削るノイズリングローダー
新しいタブで開く

06 · 浸食厚さの値が削るノイズリングローダー — 計測 累積面積 8.508% · 動いたフレーム 23/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

LATTICE_NfieldNoiseevenodd
function smooth(f) { return f * f * (3 - 2 * f); }
function valueNoise(x) {
  var xi = ((x % LATTICE_N) + LATTICE_N) % LATTICE_N;
  var i0 = Math.floor(xi);
  var i1 = (i0 + 1) % LATTICE_N;
  var f = smooth(xi - i0);
  return lattice[i0] * (1 - f) + lattice[i1] * f;
}
function fieldNoise(theta, sec) {
  var x1 = (theta / (Math.PI * 2)) * LATTICE_N + sec * 1.6;
  var x2 = (theta / (Math.PI * 2)) * LATTICE_N * 2.3 + 40 - sec * 2.4;
  return valueNoise(x1) * 0.68 + valueNoise(x2) * 0.32;
}

07波紋半径の値が広がるバネ格子ローダー

格子状に並べた点をバネで結び、進行度を中心から広がる波紋半径の値として扱うことで、半径が通り過ぎた点だけが跳ね上がり波打つ格子が残ります。ヒーローセクションのプリローダーやインタラクティブなランディング待機画面に向いています — ページの最初のフレームに、本物のコンテンツが描画される前の動きを与えられます。タイルをクリックすると、中心からもう一度波紋を送り出します。

波紋半径の値が広がるバネ格子ローダー
新しいタブで開く

07 · 波紋半径の値が広がるバネ格子ローダー — 計測 累積面積 22.819% · 動いたフレーム 23/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

STIFFNESSDAMPripple
function ripple(dt) {
  if (dt < 0) return 0;
  return AMP * Math.exp(-DAMP * dt) * Math.cos(STIFFNESS * dt);
}

function draw(sec) {
  var t = sec % LOOP;
  var p = t / LOOP;
  var frontR = p * maxR;
  var speed = maxR / LOOP;

  ctx.fillStyle = PAPER;
  ctx.fillRect(0, 0, W, H);

  ctx.strokeStyle = RING;
  ctx.lineWidth = 1.4;
  ctx.beginPath();
  ctx.arc(cxp, cyp, Math.max(2, frontR), 0, Math.PI * 2);
  ctx.stroke();

  var disp = new Array(pts.length);
  for (var i = 0; i < pts.length; i++) {
    var q = pts[i];
    var dt = (t - q.dist / speed);
    disp[i] = ripple(dt);

08角度の値が掃くように進む衛星軌道ローダー

進行度を0〜360度の角度値に変換し、衛星の点がその角度まで軌道を描いて通過するようにして、通った跡に尾を残すことで角度がそのまま進行度として読めます。決済処理待機画面やAPI応答待ちバッジに向いています — 掃くように進む角度は、終わりの見えない回転よりも着実な進行に見えます。タイルの上でSpaceキーを押すと、スイープを途中で止められます。

角度の値が掃くように進む衛星軌道ローダー
新しいタブで開く

08 · 角度の値が掃くように進む衛星軌道ローダー — 計測 累積面積 19.327% · 動いたフレーム 23/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

WIDE_SEGglobalAlphaRINGS
function draw(sec) {
  var t = sec % LOOP;
  var p = t / LOOP;
  var sweep = p * Math.PI * 2;         // 0→2π — 全体スイープ角度

  ctx.fillStyle = PAPER;
  ctx.fillRect(0, 0, W, H);

  var WIDE_SEG = 30, WIDE_SPAN = 1.7;
  for (var w = WIDE_SEG; w >= 0; w--) {
    var wa0 = sweep - WIDE_SPAN * (w / WIDE_SEG);
    var wa1 = sweep - WIDE_SPAN * ((w - 1) / WIDE_SEG);
    var walpha = (1 - w / WIDE_SEG);
    ctx.fillStyle = S1;
    ctx.globalAlpha = walpha * walpha * 0.14;
    ctx.beginPath();
    ctx.moveTo(cxp, cyp);
    ctx.arc(cxp, cyp, baseR, wa0, wa1);
    ctx.closePath();
    ctx.fill();
  }
  ctx.globalAlpha = 1;

09生存ルールの値が満たすセルオートマトンローダー

格子の各セルに生存ルールを適用し、世代が進むたびに点灯セルの割合を計算して、その割合が進行度の目標値に届くまで世代を重ねてローダーの内側を満たします。大量データ処理画面やバックグラウンド同期ローダーに向いています — 1世代ごとに1バッチ終わったように見えるので、単調な1本のバーより実際の処理に近く読めます。タイルをクリックすると、格子を再びシードしてルールが埋めていく様子をやり直せます。

生存ルールの値が満たすセルオートマトンローダー
新しいタブで開く

09 · 生存ルールの値が満たすセルオートマトンローダー — 計測 累積面積 33.618% · 動いたフレーム 23/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

BIRTHneighborCountstepGen
  var c = 0;
  for (var dy = -1; dy <= 1; dy++) {
    for (var dx = -1; dx <= 1; dx++) {
      if (dx === 0 && dy === 0) continue;
      var nx = x + dx, ny = y + dy;
      if (nx < 0 || ny < 0 || nx >= GX || ny >= GY) continue;
      if (g[ny * GX + nx]) c++;
    }
  }
  return c;
}
function stepGen(prev) {
  var next = new Uint8Array(TOTAL);
  for (var y = 0; y < GY; y++) {
    for (var x = 0; x < GX; x++) {
      var idx = y * GX + x;
      if (prev[idx]) { next[idx] = 1; continue; }
      next[idx] = neighborCount(prev, x, y) >= BIRTH ? 1 : 0;
    }
  }
  return next;
}

どこで壊れるか — DAMPINGとSTIFFNESSの落とし穴

落とし穴は、DAMPING・STIFFNESS・FREQを際限のない自由なつまみとして扱ってしまうことです。03の跳ね返りはDAMPING 0.92で動いていますが、この値を1に近づけるほどb.vyは床に触れるたびに失う速度が小さくなり、Math.abs(b.vy)がREST_Vを下回るまでの時間が延び、ブロックの状態がfallingからsettledに変わるまでの時間も延びていきます — 早く終わるはずのスタックが、いつまでも収まらなくなります。07のバネ格子は逆の落とし穴を持っています。ripple()はAMP * Math.exp(-DAMP * dt)で減衰しながらMath.cos(STIFFNESS * dt)で振動するので、DAMPを一緒に上げずにSTIFFNESSだけを下げると、振幅の項が波を平らにするはずのタイミングを過ぎてもコサインの項が鳴り続け、前の波紋が目に見えて消える前に次の波紋が始まってしまいます。01のFREQにも似た、もっと静かな落とし穴があります。Math.sin(x * FREQ + sec * SPEED1)は描画ループの固定4px刻みのx方向ステップに対して計算されているので、FREQを十分に上げるとその正弦波を細かく拾いきれなくなり、表面が水ではなくノイズのように見えてしまいます。どれも実行時エラーではありません — 計算は毎フレーム動き続けますが、作ったはずのローダーとしては読めなくなるのです。

最初に触るべき3つの定数と、reduced-motionが取り除くもの

変形 変えた値 感触の予想
波の振動数値を調整する 01 FREQ 0.045 → 0.09 振動数値を上げると液体表面が細かく速く波打つ方向に近づくはずです。
跳ね返り減衰値を調整する 03 DAMPING 0.92 → 0.6 減衰値を下げるとブロックが床に触れるたびに低く跳ねて早く収まる方向に近づくはずです。
バネ剛性値を調整する 07 STIFFNESS 9 → 16 剛性値を上げると格子点がより早く元の位置に戻り波紋が短く途切れる方向に近づくはずです。

9つとも、ページが読み込まれた瞬間に一度だけprefers-reduced-motionを読み取り、毎フレーム読み取ることはしません。そのメディアクエリがreduceを返した場合は、ループを一切開始せず、ループ途中の値ではなく固定した進行度で静止フレームを1枚描くだけです。クリックできるタイルにはすべてrole="application"・tabindex="0"・aria-labelが付いていて、クリック・Enterキー・Spaceキー(どちらもループの切り替え)・Rキー(再スタート)に同じハンドラーが応答し、さらにリサイズリスナーがタイルを新しいサイズで再描画します。マウスなしでも9つすべてを再生・一時停止・再スタートできます。パターンごとの9つのソースファイルは、このデモと同じフォルダの中で5ert9w9qの向こうに置かれています。上のDAMPINGやSTIFFNESSの落とし穴に近づける前に一度開いておくと、表の上では問題なく見えても、実際には収まらないスタックや静まらない格子が本物の読み込み画面を止めてしまう事故を避けられます。

この9つを実際にどこで使うか

01の水面が上昇するローダーは、ファイルアップロードの進捗モーダルやアプリのスプラッシュ画面でこそ使う価値があります。実際のアップロードがすでに持っている進行度の数値を1つ渡すだけで動き、2つの正弦波が平坦なバーに質感を足してくれるので、別のデータソースを新たに用意する必要がありません。

05の縮んでいく軌道半径と08の掃くように進む角度は、どちらもカード型のスピナーや決済処理の待機バッジのような、丸くて小さな場所によく合います。直線のバーではその形の場所に収まらないためです。どちらを選ぶかは、中心に引き寄せられる粒子と、固定半径を回る点のどちらが周りのUIの他の動きに馴染むかで決まります。

09のセルオートマトンの充填は、大量データ処理画面やバックグラウンド同期ローダーで使う価値があります。1世代が1バッチの完了のように見えるので、UIスレッドが適当に作ったパーセンテージより、実際のバッチ処理に近い読み方ができます。

css ローディング アニメーション js の実例はどこで見られる?

css ローディング アニメーション の作り方 を検索するとジェネレーターがすぐに動くキーフレームを返してくれますが、CSSキーフレームを前提にしたジェネレーターには、収束させる粒子配列も、生存ルールを回す格子も、波打たせるバネ格子もありません。02の到着比率や09の世代ごとの充填は、プロパティが一つの値から別の値へイージングするからではなく、プレーンなJavaScriptの数値が毎フレームcanvasを動かしているからこそ成立しています。ローディング アニメーション フリー でそのままプロジェクトに使えるものを探しているなら、以下の9つがまさにそれです — パターンの下のソースを開いてファイルをコピーし、進行度の値だけ自分のアップロードやリクエストが実際に返す数値に差し替えてください。

9つのローダーを組み込む前によくある質問

この9つは、ローディング アニメーション おしゃれ な見た目のまま自分のプロジェクトに使えますか?

はい。01を例にすると、level(p)にデモのループするpの代わりに実際のアップロードの進行度比率を渡すだけで、2つの正弦波が値が1に近づくまで表面を揺らし続けます。色や余白を触らなくても、見た目のおしゃれさは保たれます。

03のスタックと09の充填はなぜ見え方が違うのですか?

03は落下するブロックという固定された少数の要素を追跡し、最後の1つが収まった瞬間に止まります。一方09は毎世代ごとに格子全体を再計算し、点灯セルの割合が目標値を超えるまで続けます。片方は数えられるスタック、もう片方は場全体から読み取るパーセンテージです。

この9つを動かすのにライブラリやCDNは必要ですか?

いいえ。9つとも同じファイルの中にある自前のcanvas 2D描画とシード付きの値だけで動いており、ライブラリもWebGLコンテキストも外部スクリプトタグも一つもありません。

Sources: MDN — Canvas API · MDN — CanvasRenderingContext2D · MDN — requestAnimationFrame · MDN — prefers-reduced-motion · MDN — Using the progressbar role

関連記事: キャンバス メタボール アニメーション9種 — マーチングスクエアで融合する液体の塊 · css 背景 パターン ジェネレーター 自作9種 · 【コピペOK】AIチャット ローディング アニメーション css 9選 — 1つ目はこの9つと同じくライブラリなしの2Dキャンバスでメタボールのしきい値という別の物理場を計算しており、2つ目は02や04が粒子や揺れの値をシードするのと同じ考え方で自前の生成パターンをシードしています。3つ目は同じ9パターン構成のローディングアニメーションで、こちらは物理演算ではなくチャット特有の動きを軸にしています。

解凍パスワードを入力してください

パスワードはこの記事の本文の中にあります。読み進めると出てきます。