このサイトのドット絵——ロゴの星、モード切替のアイコン、浮かんでいる月や雲——は、画像ファイルではありません。すべて文字列のグリッドから canvas に描画しています。

スプライトは「読めるデータ」で持つ

ドット絵の定義はこれだけです。1文字が1ピクセル、. は透明、他の文字はパレットの色に対応します。

const star = {
  palette: { A: "#ffe7a3", C: "#ffffff" },
  rows: [
    "...A...",
    "...A...",
    "..AAA..",
    "AAACAAA",
    "..AAA..",
    "...A...",
    "...A...",
  ],
};

この形式の良いところは、コードがそのままドット絵のプレビューになることです。差分レビューでも形の変化が一目で分かり、「月をもう少し太く」のような修正依頼にエディタだけで応えられます。

描画は fillRect を並べるだけ

レンダラーはTypeScriptで20行ほどです。グリッドを走査して、透明以外のマスを拡大率ぶんの矩形で塗ります。

for (let y = 0; y < rows.length; y++) {
  for (let x = 0; x < rows[y].length; x++) {
    const color = palette[rows[y][x]];
    if (!color) continue;
    ctx.fillRect(x * scale, y * scale, scale, scale);
  }
}

image-rendering: pixelated を添えれば、拡大してもエッジはくっきりしたままです。

マークアップ側は宣言だけ

使う側は data 属性を書くだけにしました。起動時に走査して、該当箇所へ canvas を差し込みます。

<span data-pixel="moon" data-scale="7"></span>

絵柄と倍率がHTMLから読めるので、装飾の配置換えにスクリプトの修正が要りません。

画像ゼロ運用の副産物

  • アイコンの色をテーマに合わせて差し替えるのも、パレットの変更だけ
  • 転送量はスプライト定義の数百バイトのみ
  • 「サイトと同じ筆致で」新しいアイコンを増やせるので、デザインの統一感が崩れない

最後の点は思った以上に効きます。既製のアイコン集を混ぜると途端に「borrowed感」が出ますが、同じエンジンから生まれた絵は自然と馴染みます。