Back

ブラウザとターミナルにおけるASCIIアート

ブラウザとターミナルにおけるASCIIアート

アイデアはシンプルです。画像を取り込み、各ピクセルの明るさを測定し、それを文字に置き換えます。@#のような密度の高い文字は暗い領域を表し、. のような軽い文字は明るい領域を表します。これらの置換をグリッド全体で十分に並べると、完全にテキストで構成された認識可能な画像が得られます。

この核となるアイデアが、ターミナルでのASCIIアートブラウザでのASCIIアートの両方を支えており、現代の実装では従来の7ビットASCIIをはるかに超えています。

重要なポイント

  • ASCIIアートは、ピクセルの明るさをソート済みのパレットから文字にマッピングします。@のような密度の高いグリフは暗い領域を、.のような疎なグリフは明るい領域を表します
  • Unicodeブロック要素とブライユパターンは、従来のASCII文字パレットよりも大幅に高い解像度を提供します
  • ブラウザ実装は<canvas>のピクセルデータと等幅CSSに依存し、ターミナルレンダラーは色のためにANSIエスケープシーケンスを使用します
  • ターミナルの機能は大きく異なります — 基本的な16色ANSIから24ビットトゥルーカラー、SIXEL、Kittyグラフィックスプロトコルまで

ピクセルから文字へのマッピングの仕組み

すべてのASCIIレンダラーは、おおよそ同じパイプラインに従います。

  1. ソース画像またはビデオフレームをセルのグリッドにサンプリング
  2. 各セルの平均輝度(明度)を計算
  3. その輝度値をソート済みパレットから文字にマッピング
  4. オプションで色を付けて、結果の文字グリッドを出力

基本的なJavaScript実装は次のようになります。

const palette = ' .:-=+*#%@';

function brightnessToChar(brightness) {
  const index = Math.floor((brightness / 255) * (palette.length - 1));
  return palette[index];
}

ここで、brightnessは0(黒)から255(白)の間の値であることが期待されます。この関数は、それをパレット文字列内のインデックスに正規化します。前の文字は明るいトーンを、後の文字は暗いトーンを表します。

出力の品質は、文字パレットに大きく依存します。従来のASCIIでは、約10〜15の有用な陰影レベルが得られます。現代の実装はより優れています。

ASCIIを超えて:Unicodeによるテキストモードグラフィックス

Unicodeによるテキストモードグラフィックスは、大幅に高い解像度を実現します。疎なASCIIパレットの代わりに、次のものを使用できます。

  • ブロック要素(█ ▓ ▒ ░)でセルごとに4レベルの陰影
  • ブライユパターン(⣿ ⠿ ⠛)は、1文字に最大8つのドットをエンコードでき、従来のASCIIパレットよりも高い実効解像度を可能にします
  • 罫線文字は、構造化されたレイアウトと境界線に使用

Chafaは、ターミナル画像ビューアで、Unicodeブロックとブライユ文字を使用して、ターミナルで写真に近い品質で画像をレンダリングします。プレーンASCIIと比較した違いは顕著です。

ブラウザでのASCIIアート:CanvasとWebGLアプローチ

ブラウザベースのJavaScript ASCIIレンダリングは、通常<canvas>要素を画像ソースとして使用します。プロセスは次のとおりです。

  1. 画像またはビデオフレームをオフスクリーンキャンバスに描画
  2. ctx.getImageData()でピクセルデータを読み取り
  3. ピクセルグリッドをサンプリングし、各セルを文字にマッピング
  4. 結果を<pre>要素にレンダリングするか、fillText()を使用してキャンバスに戻す

p5.jsのようなライブラリを使えば、これが簡単になり、ライブウェブカメラフィードやビデオフレームをリアルタイムで処理できます。より高いパフォーマンスを得るには、WebGLシェーダーがGPU上で輝度サンプリングと文字ルックアップを処理できます。これは、30 fpsでフルビデオをレンダリングする場合に重要です。

静的表示の場合、HTML/CSSのセットアップは最小限ですが重要です。

.ascii-art {
  font-family: monospace;
  white-space: pre;
  line-height: 1;
  letter-spacing: 0;
}

white-space: preがないと、ブラウザはスペースを折りたたみ、レイアウトを破壊します。等幅フォントがないと、文字の幅が不均一になり、グリッドが崩れます。letter-spacing: 0を設定すると、一部のブラウザがデフォルトで導入する微妙な水平方向のギャップを防ぎます。

ターミナルASCIIグラフィックス:ANSIカラーとプロトコルの多様性

ターミナル環境では、ASCIIまたはUnicode文字が標準出力に書き込まれます。色はANSIエスケープシーケンスを通じて追加されます。最新のターミナルは24ビット(トゥルーカラー)出力をサポートしています。

printf '\033[38;2;255;100;0m%s\033[0m\n' "orange text"

これは、38;2;R;G;Bシーケンスを使用して前景色をRGB(255, 100, 0)に設定し、\033[0mでフォーマットをリセットします。

jp2aascii-image-converterのようなツールは、このアプローチを使用して、画像ファイルから色付きのターミナルASCIIグラフィックスを生成します。

ただし、ターミナルの機能は大きく異なります。一部のターミナルは基本的な16色ANSIのみをサポートしています。他のターミナルは256色モード、フル24ビットトゥルーカラー、SIXELグラフィックス、またはKittyグラフィックスプロトコルをサポートしており、文字の近似ではなく実際のピクセル画像をターミナル内にレンダリングできます。Unicodeの幅処理もターミナル間で異なるため、ブライユや全角文字は環境によって位置がずれる場合があります。

まとめ

ブラウザ用のJavaScript ASCIIレンダラーを書く場合でも、ターミナルツールを通じて画像データをパイプする場合でも、基本的なロジックは同じです。ピクセルは文字になり、明るさは密度になり、色はエスケープコードまたはCSSになります。ブラウザはフォントとレイアウトを正確に制御でき、ターミナルは速度と組み合わせ可能性を提供します。環境の選択がツールを形作りますが、変換パイプライン — サンプル、測定、マッピング、レンダリング — は同じままです。

FAQ

ほとんどの等幅フォント文字は幅よりも高さが高いため、各文字セルは完全な正方形ではありません。この縦方向の伸びが画像を歪めます。CSSでline-heightを調整したり、変換前にソース画像を非正方形のアスペクト比にスケーリングしたり、列に対して相対的に少ない行をサンプリングすることで補正できます。

視覚的な密度で文字を順序付けし、最も明るいものから最も暗いものまで並べます。長いパレットはより多くの陰影レベルと滑らかなグラデーションを提供します。グラデーション画像をレンダリングして目に見えるバンディングをチェックすることで、パレットをテストします。Unicodeブロック要素とブライユパターンは、標準ASCII文字よりも細かい粒度を提供します。

はい。ブラウザでは、各ビデオフレームをオフスクリーンキャンバスに描画し、ピクセルデータを読み取り、アニメーションフレームごとにセルを文字にマッピングします。30 fpsで許容可能なパフォーマンスを得るには、文字グリッドの解像度を控えめに保つか、輝度計算をWebGLシェーダーにオフロードします。Chafaのようなターミナルツールもビデオ入力をサポートしています。

ブライユ文字はUnicodeであり、レンダリングされる幅はターミナルエミュレータとアクティブなフォントに依存します。一部のターミナルはそれらを全角文字として扱い、他のターミナルは半角として扱います。出力が壊れて見える場合は、別のターミナルまたはフォントでテストしてください。Chafaのようなツールは、ターミナルの機能を検出し、それに応じて出力モードを調整します。

Understand every bug

Uncover frustrations, understand bugs and fix slowdowns like never before with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.

OpenReplay