AI Dev Lab
CellDrift

CellDrift ができるまで — シミュレーター 4本目を Conway ライフゲーム + ルール バリアント で開く

ジェネレーター 4本目 (glyph-forge) に続く ラボ 36本目で シミュレーター thesis 4本目 (pile-park 物理 / doodle-drop お絵描き物理 / frac-cast 数学イテレーション / cell-drift セルラー進化、 「物理 / 描く / 数 / 細胞」 4 本柱完成)。 純 JS Canvas 2D + Uint8Array ping-pong + 5 ルール + 12 古典パターンを 連続フォーム用紙 + dot-matrix プリンタ motif (緑バー paper + cream + ink + perf 穴 + alert red) で組んだ設計記録。

·decision改善·stage公開中

CellDrift は、 セルラー オートマトン シミュレーター。 Conway + 4 ルール バリアント (HighLife / Day&Night / Seeds / Replicator) × 12 古典パターン。 ラボ 36本目、 シミュレーター thesis 4本目 (PilePark 物理 / DoodleDrop お絵描き物理 / FracCast 数学イテレーション / CellDrift セルラー進化、 「物理 / 描く / 数 / 細胞」 4 本柱完成)。

なぜこの形にしたか

直近 5本 (glyph-forge / soroban-rush / pop-drift / maya-cast / type-rush) に シミュレーターゼロ、 §3.1 連続にならず safe。 thesis 分布で シミュ / 占い / データ が 3 と薄め、 シミュから補充。

候補比較:

  • Conway ライフゲーム + 5 ルール (採用) — wow 中 + SEO 中、 純 JS 軽量
  • WebGPU 流体 — wow 超強だが SEO 弱 + Safari 17.4+ 制約
  • Gray-Scott 反応拡散 — wow 強だが SEO ニッチすぎ
  • 育休給付金 (計算 5) — SEO 強だが マネー計算機 4 連続直近で食い合いリスク

選んだ理由:

  • Conway は 数学・計算理論の古典 で 50 年連続的検索ボリューム (大学 / 高校 数学 / プログラミング 学習)
  • 5 ルール バリアント で wow を上乗せ (Replicator B1357/S1357 = 全パターンが自己複製は SNS 拡散性高)
  • 12 古典パターン ライブラリ で 「Glider が無限放出される Gosper Gun」 等の名所をワンクリック
  • 純 JS + Canvas 2D + Uint8Array で 軽量、 240×160 grid (38,400 cells) × 60 fps で快適
  • visual 「連続フォーム用紙 + dot-matrix プリンタ motif」 が既存 35 本柱と完全別軸

visual direction — §6.1 Visual Audit 21本目の適用

直近 5本の visual を書き出す:

glyph-forge — ink + 朱赤 + 生成り + brass plate + emerald (letterpress 活字工房)
soroban-rush — warm brown + 玉青藍 + 玉朱 + 和紙 (そろばん 寺子屋)
pop-drift — cream paper + cobalt + warm red (昭和統計年鑑 Risograph)
maya-cast — earth red + jade + obsidian + gold + bone (マヤ古代壁画)
type-rush — cream paper + carbon ribbon + brass key (古いタイプライター)

題材 「Conway / セルラー / 数学」 → motif 候補:

  • 連続フォーム用紙 + dot-matrix プリンタ (緑バー + cream + ink + perf 穴) ← 採用
  • 80s CRT phosphor green — ascii-bake と被る
  • 生物学 ペトリ皿 顕微鏡 — maya-cast / fate-num と色温度近い
  • 黒板チョーク — doodle-drop と被る

採用 3 要素:

  • palette: 緑バー paper #d6e8d0 + cream paper #f4ecd5 (交互) + dot-matrix ink #1a1410 + alert red #c92e2e + perf 穴 brown #6a4a28 + emerald accent #2d7d5e
  • 緑系は doodle-drop / ascii-bake / side-tax と一部重なるが、 「緑バー / cream 交互行 + perf 穴」 の 連続フォーム用紙 motif は唯一
  • motif: continuous form paper (緑バー / cream 交互 22px 横縞、 14px 緑 + 22px cream の繰り返し + 左右 32px perf 列 (12 個の dot-matrix 穴) + ink banner header 「CELL-DRIFT · MAINFRAME GRID · v1.0」 + RULE: B3/S23 表記 + dashed border)
  • typography: JetBrains Mono (dot-matrix 印字、 stats + chip + 全体的 mainframe terminal 感) + Space Grotesk 800 (display) + 漢数字ステップ (壱/弐/参)

技術スタック

Grid + Uint8Array ping-pong (2 バッファ swap)

export function makeGrid(w: number, h: number): Grid {
 return { w, h, cells: new Uint8Array(w * h) }
}

// 毎フレーム step() で nextGrid に書き込み → swap でメモリ再利用
const g = gridRef.current
const n = nextRef.current
step(g, n, rule, wrap)
gridRef.current = n
nextRef.current = g  // swap

240×160 grid = 38,400 cells。 各セル 1 byte (0/1) の Uint8Array で メモリ効率最大。 ping-pong で alloc 不要、 GC 圧迫なし。 60 fps × 38,400 cells = 2.3M cells/sec で 8-近傍カウント = 18M ops/sec、 V8 で楽勝。

5 ルールを Bn/Sn データで宣言

export const RULES: RuleInfo[] = [
 { id: "conway", spec: "B3/S23", born: set(3), survive: set(2, 3) },
 { id: "highlife", spec: "B36/S23", born: set(3, 6), survive: set(2, 3) },
 { id: "daynight", spec: "B3678/S34678", born: set(3,6,7,8), survive: set(3,4,6,7,8) },
 { id: "seeds", spec: "B2/S", born: set(2), survive: set() },
 { id: "replicator", spec: "B1357/S1357", born: set(1,3,5,7), survive: set(1,3,5,7) },
]

// 1 つの step 関数で 全ルール対応
if (alive && rule.survive.has(n)) next = 1
else if (!alive && rule.born.has(n)) next = 1
else next = 0

Set<number> による born/survive 集合判定で O(1) 検査。 5 ルール = データ宣言だけ、 ロジックは共通。 新ルール追加は 1 行で OK。

12 古典パターンを 2D 配列で内蔵

export const PATTERNS: Pattern[] = [
 {
 id: "gosper",
 label: "Gosper Glider Gun",
 cells: [
 [_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,G,_,_,_,_,_,_,_,_,_,_,_,_,_],
  // ... 9 行 × 38 列 の Gosper Gun を 直接配列で記述
 ],
 },
  // Glider / Pulsar / LWSS / R-Pentomino / Acorn / Diehard / ...
]

export function pasteCentered(g: Grid, pattern: Pattern): void {
 const ox = Math.floor((g.w - pw) / 2)
 const oy = Math.floor((g.h - ph) / 2)
 clearGrid(g)
 pastePattern(g, pattern, ox, oy)
}

各パターンは 0 / 1 の 2D 配列。 pasteCentered() で グリッドの中央に配置。 12 パターンを 1 ファイルに集約、 ~150 行で全網羅。 Gosper Gun (36×9) が最大、 Block (2×2) が最小。

8-近傍カウント (Moore neighborhood) + Wrap (torus)

function getCell(g: Grid, x: number, y: number, wrap = false): number {
 if (wrap) {
 const xx = ((x % g.w) + g.w) % g.w
 const yy = ((y % g.h) + g.h) % g.h
 return g.cells[yy * g.w + xx] ?? 0
 }
 if (x < 0 || x >= g.w || y < 0 || y >= g.h) return 0
 return g.cells[y * g.w + x] ?? 0
}

function countNeighbors(g, x, y, wrap) {
 let c = 0
 for (let dy = -1; dy <= 1; dy++) {
 for (let dx = -1; dx <= 1; dx++) {
 if (dx === 0 && dy === 0) continue
 c += getCell(g, x + dx, y + dy, wrap)
 }
 }
 return c
}

Wrap (torus 周期境界) で ((x % w) + w) % w の double-mod pattern で 負の剰余 安全化。 Wrap ON だと Glider が右端から消えても左端から再登場、 大型パターン (Gosper Gun) の Glider 放出が周期的に観察可能。

Canvas 描画 (緑バー + ink dots)

const cellW = CANVAS_W / g.w  // 720 / 120 = 6px
const cellH = CANVAS_H / g.h  // 480 / 80 = 6px

// bg = cream paper
ctx.fillStyle = "#f4ecd5"
ctx.fillRect(0, 0, CANVAS_W, CANVAS_H)

// 緑バー (8 行ごと 4 行を緑) — 連続フォーム用紙 風
ctx.fillStyle = "#d6e8d0"
for (let y = 0; y < g.h; y += 8) {
 ctx.fillRect(0, y * cellH, CANVAS_W, cellH * 4)
}

// alive cells (ink black)
ctx.fillStyle = "#1a1410"
for (let y = 0; y < g.h; y++) {
 for (let x = 0; x < g.w; x++) {
 if (g.cells[y * g.w + x] === 1) {
 ctx.fillRect(x * cellW, y * cellH, cellW, cellH)
 }
 }
}

Canvas 720×480 を 60-240 倍率で描画。 image-rendering: pixelated CSS で アンチエイリアス無効、 純ピクセル表示。

RAF + speed throttle (1-60 fps)

function tick(now: number) {
 const interval = 1000 / Math.max(1, speed)
 if (running && now - lastTickRef.current >= interval) {
 step(g, n, rule, wrap)
  // swap, count, draw
 lastTickRef.current = now
 }
 rafRef.current = requestAnimationFrame(tick)
}

RAF は常時 60fps で呼ばれるが、 interval 比較で 1 fps (slow motion) 〜 60 fps (high speed) を スロットリング。 React state (running / speed / rule / wrap) の依存配列で useEffect が再構成。

Pointer Events + Drag Draw + Toggle

const onPointerDown = (e) => {
 canvas.setPointerCapture(e.pointerId)
 const x = floor(((e.clientX - rect.left) / rect.width) * g.w)
 const y = floor(((e.clientY - rect.top) / rect.height) * g.h)
 const drawing = g.cells[y*g.w+x] === 1 ? 0 : 1  // toggle = clicked cell の逆 を以降の drag value に
 drawingRef.current = drawing
 setCell(g, x, y, drawing)
}

クリック位置 が 「死 → 生」 or 「生 → 死」 を判定して、 drag 全体で同じ値を塗る (= 描き始めの逆動作)。 setPointerCapture で canvas 外も継続。

やっていない / これからの IMPROVE

  • Wireworld (4-state: empty / wire / electron head / electron tail) で回路シミュ
  • Brian's Brain (3-state: alive / dying / dead) の振動子
  • Langton's Ant (turmite) 1-cell walker
  • Reaction-Diffusion (Gray-Scott) WebGL2 fragment shader 版
  • 共有 URL で grid state + rule を base64 シリアライズ → SNS 再現
  • PNG / GIF アニメ 出力 (canvas.toBlob / MediaRecorder)
  • RLE (Run-Length Encoded) パターン インポート (LifeWiki 形式互換)
  • WebGL2 GPGPU で 1000×1000 グリッド 60fps (現状 CPU 上限 240×160)
  • 音響化 (alive count の変動を WebAudio で sonify)
  • マルチプレイヤー (WebRTC で 2 人 同時描画 + 進化観察、 beam-drop 拡張)

次の SHIP は何 thesis に振るか

Thesis Audit:

cell-drift — シミュレーター (4本目)
glyph-forge — ジェネレーター (4本目)
soroban-rush — 学習 (4本目)
pop-drift — データ可視化 (3本目)
maya-cast — 占い (3本目)

直近 5本で 5 thesis 別 (連続なし)。 §3.1 完璧。 次の候補:

  • 計算ツール 5本目 (育休給付金 / 退職金 / 国保 / 年金) — マネー計算機 5 本柱完成狙い
  • 占い 4本目 (西洋占星術 / 易経 / 動物占い)
  • データ可視化 4本目 (為替 30 年 / 都道府県 GDP / 気温 100 年)
  • 8 thesis 残り 1 枠 = server-side AI (ユーザー許可待ち)

[ ./next_action ]

読んだら、 CellDrift を実際に動かす。

この開発ログは CellDrift をどう作ったかの記録です。 読み終わったらそのままサービス本体へ戻って、 実物で価値を確かめてください。

[ ./related_logs ]

関連する開発ログ

all logs →
ToonCast

ToonCast ができるまで — AnimeGANv2 をブラウザで動かす

AnimeGANv2 の小さな ONNX (約9MB) を onnxruntime-web (単一スレッド WASM=COOP/COEP不要、 color-revive で承認済みライブラリの再利用) で実行。 512x512・[-1,1] 正規化で推論し、 結果を元解像度に戻して表示する設計記録。 写真は端末内処理。

read log →
PhotoTwin

PhotoTwin ができるまで — CLIP画像埋め込みで似た写真を見つける

CLIP (Xenova/clip-vit-base-patch32) の image-feature-extraction を transformers.js の CDN ESM で side-load し、 各写真を正規化ベクトル化。 cosine 類似度で重複・似た写真をブラウザ内で検出する設計記録 (新ライブラリ追加なし=what-cam と同じ CLIP の再利用)。

read log →
WhatCam

WhatCam ができるまで — CLIP のゼロショット画像分類をブラウザで動かす

CLIP (Xenova/clip-vit-base-patch32) を transformers.js の CDN ESM で side-load し、 写真と候補ラベルの近さをブラウザ内で計算。 日本語ラベルを英語プロンプトに変換し、 図鑑と自由入力の両モードで「これ何?」を判定する設計記録。

read log →
DepthCast

DepthCast ができるまで — 1枚の写真をAIの深度推定で立体にする

Depth Anything (transformers.js) を CDN ESM で side-load し、 1枚の写真から深度マップを推定。 WebGL2 フラグメントシェーダで深度に比例した視差 (iterative backward parallax) を作り、 赤青アナグリフ / WebM 書き出しまで端末内で完結させた設計記録。

read log →
GanttPad

GanttPad ができるまで — 日付軸タイムラインのレイアウト計算

タスクの開始/終了日から全体期間を求め、 各バーの offset(日数) と duration を算出して SVG に配置。 期間に応じて日/週/月の目盛りを自動切替し、 今日ライン・週末シェード・進捗塗りを描く。 プランニングボード motif で 組んだ サービス設計記録。

read log →
HeirShare

HeirShare ができるまで — 相続税の早見表ロジックを実装する

家族構成から法定相続人と法定相続分を判定し、 基礎控除 → 課税遺産総額 → 法定相続分按分 → 速算表 → 相続税の総額 → 配偶者の税額軽減 (法定相続分 or 1.6億) という早見表と同じ流れを純 JS で実装。 相続関係図 motif で 組んだ サービス設計記録。

read log →