@hascii/canvas
v0.1.1
Published
Terminal canvas sketches with p5-like ergonomics, powered by OpenTUI. Bun-only.
Maintainers
Readme
@hascii/canvas
ターミナル上で動く p5 風のクリエイティブコーディング・ライブラリ。@opentui/core を薄くラップし、アニメーションやゲームを気持ちよく書くための小さな関数群を提供します。Bun 専用。
- 描画はすべて
(buffer, props)の素直な関数 - 入力・フレーム・リサイズは自動クリーンアップ付きの関数
- 時間・アニメーション・パーティクルは
step(dt)で自分で進める素のファクトリ関数 - 純粋ロジックはレンダラから完全に分離されていて、すべてユニットテスト済み
- 計算系(
math/color/time/animation/particles/geometry/raster)はサブパスで個別 import でき、ターミナルなしの Node やブラウザでも動く
動かす
bun add @hascii/canvasmy-sketch.ts を作って、矢印キーで四角を動かす最小スケッチを書きます。
import { sketch, axis, clear, square, text } from "@hascii/canvas"
export const config = { loop: true, fps: 60 }
export default sketch((renderer, signal) => {
let x = 0
const moveX = axis(renderer, signal, { negative: ["left", "a"], positive: ["right", "d"] })
return (buffer, dt) => {
x += moveX.value * 30 * dt // dt は秒。30 セル/秒で動く
clear(buffer, "#000")
square(buffer, { x, y: 2, size: 3, char: "█", fg: "#f06" })
text(buffer, { x: 0, y: 0, str: "← → to move", fg: "#888" })
}
})CLI で実行します。ファイルを保存すると自動でリロードします。
hascii-canvas run my-sketch考え方
スケッチは「renderer と signal を受け取り、毎フレーム呼ばれる Draw を返す関数」です。
type Sketch = (renderer: CliRenderer, signal: AbortSignal) => Draw | void
type Draw = (buffer: OptimizedBuffer, dt: number) => void // dt は秒セットアップ(クロージャ変数の用意やフック登録)は外側の関数で一度だけ。毎フレームの描画は返り値の Draw で行います。Draw には毎フレーム最新の buffer が渡されます。これだけ押さえれば、あとは下のリファレンスから必要なものを拾うだけです。
renderer…@opentui/coreのCliRenderer。widthheightで現在のサイズを取得でき、リサイズしても常に最新を返す。フックの登録先でもあるsignal… クリーンアップ用のAbortSignal。スケッチが破棄されると abort され、登録したフックや購読が自動で解除されるbuffer…Drawに毎フレーム渡されるOptimizedBuffer。リサイズで作り直されるため、セットアップで掴まず毎フレーム受け取ったものを使うConfig…{ loop?: boolean; fps?: number }。loop: falseで一枚絵モードmount(sketch, config?) → Promise<CliRenderer | Error>… プログラムから起動したいとき(CLI 経由なら不要)
構造
lib/ は層ごとに分かれていて、下に行くほど積み上がります。ピュアな道具(math / color / time / animation / particles / geometry / raster)はサブパス公開で Node / ブラウザでも動きます。generators はほぼ純粋ですが turtle が描画層を引くためルート公開のみです(純粋な線分生成だけ要るなら turtleSegments)。描画依存層(draw / quadrant / sextant / block / three)はターミナル向けで Bun 専用。汎用 3D エンジンは three だけで、ジャンル固有の擬似 3D(dungeon / raycast / mode7 / voxel / parallax など)は examples/ にレシピとして降りています。
lib/
├─ types/ Sketch / Draw / Config / Rect — 全体の型の土台
├─ runtime.ts mount() — スケッチを起動するエントリ
├─ internal/ OpenTUI レンダラとの結線(普段は触らない)
├─ log/ TUI 専有中の実行ログ(console.log の代わり)
│
├─ math/ clamp / lerp / map / noise / random / ease / vec3 / mat4
├─ color/ 色の正規化・補間・HSL・深度フォグ
├─ time/ 経過秒・FPS を step(dt) で進めるカウンタ
│
├─ animation/ バネ・トゥイーンを step(dt) で進める補間値
├─ particles/ プール付きパーティクルと発生レート
├─ generators/ 迷路・ライフ・L-System・タートル・アトラクタ・TEN PRINT・Truchet・フローフィールド
├─ geometry/ 画家アルゴリズム depth sort・アイソメ投影・ビルボード math(patterns 解体時に抽出)
├─ raster/ 4 階調インデックスのドット絵バッファ + 2D 描画プリミティブ
│
├─ draw/ 点・線・矩形・円・多角形・テキスト・枠線(セル単位、@opentui buffer 直書き)
├─ quadrant/ 2×2 サブセル(16 種クアドラント)— raster の primitives を namespace 公開
├─ sextant/ 2×3 サブセル(64 種セクスタント)
├─ block/ 1×2 ハーフブロック(▀ ▄ █)
│
├─ hooks/ 入力・フレーム・インターバル・リサイズ(自動クリーンアップ)
│ ├─ input/ キー・マウス・軸・ポインタ
│ └─ (直下) onFrame / onInterval / onResize
│
├─ three/ ソフトウェア3D(三角形を CPU で投影して ASCII / quadrant / block 化)
│ ├─ core/ 共有データ型(RGB / Triangle / Light / Scene / RasterBuffer)+ toRGB
│ ├─ scene/ 行列スタック(translate / rotate / push・popMatrix)
│ ├─ shapes/ box / sphere / cylinder / cone / torus / plane
│ ├─ camera/ perspective / orthographic + 操作カメラの純粋コア
│ ├─ lighting/ ambient / directional(ランバート拡散)
│ ├─ raster/ 三角形ラスタライザ + blit(ASCII / RasterBuffer 出力)
│ ├─ ascii/ 輝度・形状を文字へマッチング
│ └─ helpers/ grid / axes
│
├─ controls/ 入力束縛のカメラ操作(orbit / flyCamera)。three の純粋コアに hooks を結線
└─ commands/ hascii-canvas コマンド(スケッチ実行・作例の list / run)
examples/ 作例。各ディレクトリは bun example <path> で実行
├─ math/ 数学系の単品検証
├─ color/ 色変換
├─ time/ 経過秒・FPS カウンタ
├─ animation/ バネ・トゥイーン
├─ particles/ パーティクル
├─ generators/ 生成アルゴリズム
├─ geometry/ 投影・深度ソートの単品検証
├─ draw/ セル描画
├─ quadrant/ 2×2 サブセル系(00..05 はチュートリアル)
├─ hooks/ 入力・フレーム・リサイズ
├─ three/ ソフトウェア3D
│
├─ dungeon/ グリッドダンジョン(旧 patterns.dungeon)
├─ raycast/ Wolfenstein 風レイキャスト
├─ wireframe-dungeon/ 線画版ダンジョン
├─ mode7/ SNES Mode 7 の床
├─ voxel/ VoxelSpace 地形
├─ parallax/ 多層スクロール背景
├─ iso/ アイソメトリック投影
├─ scanline/ アウトラン風の道
├─ stack/ スプライトスタッキング
├─ painter/ 画家のアルゴリズム(深度ソート描画)
│
└─ games/ ゲーム作例各ディレクトリの純粋ロジックはレンダラから分離されていて、lib/<name>/index.ts が公開 API、*.test.ts がその仕様です。
計算系をターミナルなしで使う
ルートの @hascii/canvas は mount() 経由で @opentui/core(bun:ffi)を巻き込むため Bun とターミナルが要ります。一方、ターミナルに依存しない計算系はサブパスとして切り出してあり、素の Node やブラウザからそのまま import できます。
import { lerp, clamp, createNoise } from "@hascii/canvas/math"
import { parseColor, lerpColor } from "@hascii/canvas/color"
import { createElapsed } from "@hascii/canvas/time"
import { createSpring, createTween } from "@hascii/canvas/animation"
import { createParticles, createSpawner } from "@hascii/canvas/particles"
import { geometry } from "@hascii/canvas/geometry"
import { raster } from "@hascii/canvas/raster"
import { multiplyMat4, normalizeVec3 } from "@hascii/canvas/math"これらのサブパスはビルド時点で @opentui/core も bun:ffi も含まないことを保証しています。描画したいときだけルートの @hascii/canvas から描画関数を取ってください。
リファレンス
機能ごとにディレクトリの README へ分けてあります。やりたいことから引いてください。
- 色 … 色の正規化・補間・HSL・深度フォグ
- 描画 … 点・線・矩形・円・多角形・テキスト・枠線
- 数学 … クランプ・補間・再マッピング・ノイズ・乱数・イージング・vec3 / mat4
- フック … 入力・フレーム・インターバル・リサイズ(自動クリーンアップ)
- 時間 … 経過秒・FPS を
step(dt)で進めるカウンタ - アニメーション … バネ・トゥイーンを
step(dt)で進める補間値 - パーティクル … プール付きパーティクルと発生レート計算
- ドット絵 — quadrant … 2×2 サブセル解像度のピクセルバッファ
- ドット絵 — sextant … 2×3 サブセル解像度のピクセルバッファ
- ドット絵 — block … 1×2 ハーフブロック解像度のピクセルバッファ
- ドット絵 — raster … 4 階調インデックスバッファと 2D 描画プリミティブ(純粋・サブパス公開)
- ジオメトリ … 深度ソート・アイソメ投影・ビルボード(純粋・サブパス公開)
- 生成アート — generators … 迷路・ライフゲーム・L-System・タートル・アトラクタ
- ソフトウェア3D — three … 行列スタック式の 3D シーンを ASCII に投影
作例
bun example <path> で同梱の作例を実行します。<path> は examples/ 配下の相対パスです(.ts は省略可、再帰検索なし)。一覧は hascii-canvas examples で取れます。
bun example games/breakout
bun example dungeon/dungeon-animation
bun example three/block/lights
bun example quadrant/00ヘッドレス検証
make snapshot N=games/tetris
make check-examplesCLI
hascii-canvas <file> スケッチファイルを実行
hascii-canvas run <file> 同上
hascii-canvas examples 同梱の作例一覧
hascii-canvas examples <path> 同梱の作例を実行(例: games/breakout)
hascii-canvas samples [...] examples のエイリアス
hascii-canvas help このヘルプを表示ロギング
TUI が標準出力を専有するため、console.log の代わりに実行ログを使います。出力先は ~/.hascii/canvas/logs/{pid}.jsonl です。
log("info", "message", data?) // JSONL を実行ログに追記
logError("message", error)
getLogPath() // 現在のログファイルパス動作要件
- スケッチの実行(描画系・ルートの
@hascii/canvas)は Bun 1.0 以降。mount()が@opentui/core経由でbun:ffiを使うため Node.js では動きません - 計算系のサブパス(
@hascii/canvas/mathなど)は純粋なので、素の Node やブラウザでも動きます - truecolor 対応ターミナル(Ghostty / WezTerm / kitty / 最近の iTerm2 など)
heldKeys/onKeyreleaseを最大限活かすには Kitty キーボードプロトコル対応が望ましい
開発
bun example <path> # 同梱の作例をソースから実行(ホットリロード, ビルド不要。例: bun example games/breakout)
make examples # 同梱の作例一覧
make test # vitest
make check # 型チェック + lint + format
make build # dist を生成
make snapshot N=<path> # 作例を1フレーム分オフスクリーン描画して静止画で確認
make check-examples # 全作例をヘッドレスで起動して描画できるか検証
make check-package # package.json の exports が dist に実在するか確認
make publish # check → test → check-package を通してから bun publish開発中は bun example <path> を使います。lib/ も examples/ もソースをそのまま実行し、保存で自動リロードします。グローバルの hascii-canvas はビルド済み dist を実行するため、lib/ を変更したら make build し直すまで反映されません。
