▍AI Dev Lab
ASCIIBake

ASCIIBake ができるまで — Canvas だけで画像を文字に焼く軽量ピース

アスキーアート変換ツールの開発ログ。 Canvas API だけで画像の明暗を読み取り、 濃さの順に並べた文字へ割り当てる仕組みと、 機械学習も WASM も使わずに済ませた設計を記録しています。

·decision改善·stage公開中

ASCIIBake は、画像をブラウザ内で アスキーアート (ASCII art)に変換する Webサービスです。 ML も WASM も外部 API も使わず、 Canvas API の createImageBitmap → drawImage → getImageData だけで完結。 メディア処理ラボ 11 本に続く 12本目、 新カテゴリ 「クリエイティブ / 視覚遊び」 を追加しました。

#なぜこの形にしたか

ラボはここまで 「重いライブラリをブラウザで動かす」 ことを示すサービスを中心に出してきました。 voice-scribe / word-warp / bg-snap などはモデル DL に数十〜数百MB かかる。

逆に モデルゼロ・WASM ゼロ・依存ゼロ の軽量ピースがあると、 「」 の幅が広がります。 アスキーアート (ASCII art)化は明暗を文字に置き換えるだけの古典タスクで、 Canvas API でそのまま書ける。 「重い処理だけがの価値ではない」 を 12本目で示す位置づけです。

#見た目の方針

フォスファーグリーン × CRT ブラック (vintage terminal)。

  • 真黒の背景 (#000000)
  • フォスファーグリーン (#39ff14) を主色、 text-shadow で残光
  • アンバー CRT 残光 (#ffb000) を副色
  • 横方向の CRT スキャンライン (背景パターン)

既存 11本 (mint / amber / violet / cobalt / lime / gold / rose / indigo / emerald / vermilion / uranium) と完全に別軸で、 アスキーアート (ASCII art)の 「ターミナル時代」 メタファーを濃く出しました。

#実装の見どころ

#1. Canvas で 1工程完結

ts
const bitmap = await createImageBitmap(file)
const canvas = document.createElement("canvas")
canvas.width = cols
canvas.height = rows
const ctx = canvas.getContext("2d")
ctx.drawImage(bitmap, 0, 0, cols, rows)
const data = ctx.getImageData(0, 0, cols, rows).data

ファイルを読む → 縮小して描画 → ピクセルデータ取得。 これで終わり。 ライブラリ 0、 モデル 0、 ネットワーク 0。

#2. 文字セルのアスペクト補正

モノスペースフォントは概ね 2:1 (縦長) の比率です。 cols だけ指定して rows をピクセル比でそのまま割ると、 出力 ASCII が縦に潰れて見えます。 cellAspect (charset ごと) を導入して:

ts
const rows = Math.round((cols * srcAspect) / charset.cellAspect)

charset によって最適セル比は変わります — ブロック文字 (█▓▒░) は 1:1 で、 標準ランプ (@%#*+=-:.) は 2:1。 これを charset ごとにメタとして持たせて自動補正。

#3. 輝度計算 + アルファ合成

Rec.601 で R/G/B から輝度を計算。 透過部分は白に合成 (PNG の透過は白背景と仮定):

ts
const yLum = (0.299 * r + 0.587 * g + 0.114 * b) / 255
const aN = a / 255
const lum = yLum * aN + (1 - aN) * 1

これで透過 PNG (アイコン等) も意図通り焼ける。

#4. 文字セット 4種

  • 標準: @%#*+=-:. (10階調) — ディテール重視、 写真向き
  • ブロック: █▓▒░ (5階調) — ピクセルアート風、 一見で形が伝わる
  • ミニマル: #+-. (5階調) — 印刷向き、 紙でも読める
  • 二値: # (2階調) — SVG / 簡素ロゴ向き

charset を変えるたびに即座に再ベイク (useEffect で file / charset / cols / invert を監視)。

#5. 出力が壊れていないかの検査

ts
return {
 text: lines.join("\n"),
 cols, rows, charset,
 charsUsed: Array.from(charsUsedSet).sort,
 srcWidth, srcHeight,
 inferMs,
}

charsUsed は出力に実際に登場した文字の重複排除リスト。 e2e で 「charsUsed が charset の ramp の strict subset である」 を assert して silent ramp drop を防ぎます。 result panel の data attribute (data-cols / data-rows / data-chars-used) で直接読める。

#このサービスから言える事

ラボの 12 本:

  • voice-scribe / clip-cast / bg-snap / text-pluck / pdf-anvil / pixel-lift / pic-flip / mind-cell / beam-drop / word-warp / exif-peel / ascii-bake

カテゴリ:

  • 認識 (voice / text / bg)
  • 変換 (clip / pdf / pixel / pic / word)
  • 生成 (mind)
  • 転送 (beam)
  • インスペクション (exif)
  • クリエイティブ / 視覚遊び (ascii) ← 新

「 = 重いライブラリだけ」 ではなく、 軽量ピースもラボの幅を広げる。 12本目はその実証として置きました。

[ ./next_action ]

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

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

[ ./related_logs ]

関連する開発ログ

all logs →
ShadowTwin

立体パズルの別解を作る:同じ影と最少の積み木

ShadowTwinの開発記録。3方向の図から別々の最少配置を2つ探す仕組み、5問の独立全探索、スマホの横並び比較、保存・再編集と入力検査を解説します。実照明の計算や能力診断ではありません。

read log →
SlitCard

スリットアニメーションを作る:4コマの合成と透明原稿の設計

SlitCardの開発記録。4コマの帯を交互に合成し、すき間と黒帯で別のコマを見せる仕組みを解説。同縮尺の台紙・透明シートSVG、50mm確認線、手動プレビュー・初期非表示、再編集と実出力検査を整理します。

read log →
WindowLight

日当たりシミュレーションを作る:窓の光を床へ投影する幾何モデル

WindowLightの開発記録。太陽位置近似から窓の4隅を床へ投影し、凸多角形を部屋で切り取る設計を解説。国立天文台の16時刻との限定照合、大気差・低高度・実際の採光との違い、SVGと設定の再編集を整理します。

read log →
GridLights

電源構成の比較を作る:割合と実量を100個の電球で読み解く

電源構成比較GridLightsの開発記録。12か国の年次発電データを最大剰余法で100個の電球に変換し、割合とTWhを比較する設計を解説。欠損を0補完しない7分類、予想と実値、出典付きSVGと実データ検証を整理します。

read log →
SoundLoom

音のお絵かきを作る:実WAVから絵を測り直す設計

音のお絵かきの開発記録。描画を正弦波のPCMとWAVへ変換し、保存音声の周波数をGoertzel法で測って比較する仕組みを解説。音量・測定窓・量子化の限界と、編集・離脱時の古い結果を抑止する検証を整理します。

read log →
MazeLoom

宝探し迷路を作る:寄り道まで含めた最短ルートの探索

宝探し迷路の開発記録。通路を木として生成し、現在地と3つの宝の取得状態を組み合わせて最短ルートを探す仕組みを解説。問題コードの版固定、画面と解答付きPDFの一致、手数と印刷の検証を整理します。

read log →