HTML-in-Canvasは何を変えるのか。Liquid Glassヘッダーを試作して考えた

HTMLのレイアウトとCanvasの描画をつなぐHTML-in-Canvas。APIの仕組みと変更点、Liquid Glassヘッダーへの応用、非対応環境のフォールバックを解説します。

TechHiraoku17 分で読めます

ノートパソコンを使う女性が透明なガラス板を持ち、背後のブラウザーカードを透かして見る淡い青灰色の黒ペン風イラスト

このブログのヘッダーに、ガラス越しに本文が見えるような表現を入れたくなりました。

今回HTML-in-Canvasを採用した目的は、二つあります。

  • HTML-in-Canvasそのものを検証し、HTMLの描画結果をCanvasへ取り込む仕組みと制約を確かめること。
  • ブログのヘッダーをLiquid Glass風にし、透明感や、ガラスの縁で背景が曲がって見える表現を試すこと。

半透明の背景とぼかしならCSSで作れます。今回はそこに、関心のあったHTML-in-Canvasを実際の部品で検証する機会を重ねました。ヘッダーの背景を描画の材料として取り込み、場所ごとに加工する構成なら、二つの目的を一緒に追えると考えたのです。

Liquid Glass風の見た目だけが目的なら、まずCSSの半透明背景、backdrop-filter、ガラスの外周に沿った光の反射を使う方法をおすすめします。 今回の採用には、新しいAPI自体を検証する目的も含まれています。

HTML-in-Canvasは、HTML要素の描画結果をCanvasへ取り込むための実験的なAPIです。HTMLとCSSにレイアウトを任せたまま、その結果を2D Canvasや3D描画の材料として扱うことを目指しています。Chromeの紹介記事

ヘッダーは、HTML-in-Canvasが使える環境では背景に屈折を加え、使えない環境ではCSSの半透明背景とぼかしで表示する構成にしました。

記事中のGIFは、Origin Trialを有効にしたローカル環境で撮影したものです。 公開サイトではOrigin Trialを有効にしていないため、必要なAPIが使えないブラウザーではCSSによる表示になります。

技術情報は2026年10月25日に確認しました。APIの説明は、WICGのexplainerの確認時点の版を基準にします。explainerは設計を説明する文書で、確定したWeb標準の仕様書ではありません。

Chrome公式の日本語記事も、次の順に読むと理解しやすいです。

1 HTMLのレイアウトを、Canvasの描画に使いたい

たとえば、名前と説明文とボタンを持つカードを、3D空間の中に置きたいとします。

HTMLなら、文章の折り返しやボタンの配置をCSSで指定できます。フォーム、キーボード操作、支援技術との連携もブラウザーの仕組みを使えます。一方、Canvasで見た目だけを描くと、文字の配置に加えて、どこをクリックしたらボタンを押したことになるか、といった処理も考える必要があります。

Chromeの紹介記事は、このHTMLとCanvasの役割の違いを出発点にしています。HTML-in-Canvasでは、元のHTMLを保ちながら描画結果をCanvasへ渡し、ブラウザーが持つ操作やアクセシビリティの仕組みとの連携を目指します。Chromeの説明

HTMLとCSSが作った描画結果を、Canvasで加工する

ここで、描画することと、操作できることは別の仕事になります。ボタンを変形して表示したら、その見た目の位置とクリック判定の位置を合わせなければなりません。APIを読むときは、「HTMLを画像にする方法」だけでなく、この対応を誰が管理するのかも見る必要があります。

2 描画までの流れは、対象の指定・記録・取り込み

現在のexplainerでは、CanvasをHTML描画用にするためにcontent="drawable"を使い、描画対象の子孫要素にはdrawableを付けます。対象の描画結果が記録され、Canvasのpaintイベントで使えるようになります。2Dの描画へ取り込むメソッドがdrawElementImage()です。WICGの設計説明

drawableは、独立して描画する要素に付けます。その要素の通常の子孫も一緒に描画されますが、子孫へさらにdrawableを付けた場合、その部分は親の描画対象から外れ、別の描画呼び出しが必要になります。

構造を小さくすると、次のようになります。

<canvas id="preview" content="drawable" width="640" height="160">
	<div id="card" drawable>
		<p>HTMLとCSSで組んだカード</p>
	</div>
</canvas>

基本的な描画の流れは次の通りです。実際に組み込む際は、解像度の調整や非対応時・エラー時の処理も必要になります。

const canvas = document.querySelector('#preview');
const card = document.querySelector('#card');
const context = canvas.getContext('2d');

canvas.addEventListener('paint', () => {
	context.clearRect(0, 0, canvas.width, canvas.height);
	context.drawElementImage(card, 0, 0);
});

canvas.requestPaint();

requestPaint()は次の描画機会にpaintを要求するためのメソッドです。毎フレーム繰り返す処理を書く前に、どの変更で描画を更新するかを決められます。

ヘッダーでは、スクロールやサイズ変更、画像の読み込みなど、背景が変わるタイミングで描き直します。requestAnimationFrame()で更新をまとめ、変更がない間は再描画を要求しません。

古いサンプルは、そのまま混ぜない

このAPIは変更が続いています。Chromeの紹介記事には、初期のlayoutsubtree属性と、drawElementImage()が返した変換行列を要素へ設定するコードが載っています。

その後の開発者向け告知では、drawableの必須化や、2D描画での位置合わせの自動化が説明されました。2026年9月の変更告知

さらに、Chrome公式の更新記事では、Chrome 150と155での変更が整理されています。以下は、初期の紹介記事と確認時点の設計の主な違いです。

見る場所古いサンプル今回基準にした設計
Canvasの指定layoutsubtreecontent="drawable"
描画対象Canvasの直下の要素を使う例描画対象にdrawableを付ける
2Dの位置合わせ戻り値の変換行列を手動設定drawElementImage()での描画時は既定で自動更新
WebGLの取り込みtexElementImage2D()texElementSubImage2D()。テクスチャ領域は事前確保
WebGPUの取り込みcopyElementImageToTexture()drawElementImageToTexture()

2Dで手動の位置管理が必要な場合は、preserveElementGeometry: trueを指定して自動更新を止められます。WebGLとWebGPUでは、表示位置やクリック判定を合わせるためにupdateElementGeometry()を明示的に呼びます。2Dでも、描画後のピクセルへ独自の変形を加えた結果まで、クリック判定が自動追従すると考えるべきではありません。今回のヘッダーは装飾だけを加工するため、この問題を元のナビゲーションの操作から切り離しています。

記事のコードが新しく見えても、別の時点のサンプルを組み合わせると動かない可能性があります。まず使っているブラウザーがどの版を実装しているかを確認しましょう。

属性の指定だけで対応を判断せず、必要なメソッドの有無と描画の成否を確認します。ヘッダーのフォールバックは後で説明します。

3 Liquid Glassで必要なのは、背景をどう加工するか

CSSのbackdrop-filterは、要素の背後へぼかしなどをかけるプロパティです。効果を見せるには、手前の要素や背景に透過が必要になります。MDNの説明

CSSによる表示では、背景の不透明度を32%、ぼかしを4pxにして、背後の文章や画像が見える透明感を残しました。

.site-header__inner {
	background: color-mix(in srgb, var(--bg) 32%, transparent);
	-webkit-backdrop-filter: blur(4px) saturate(1.2);
	backdrop-filter: blur(4px) saturate(1.2);
}

この透明感はCSSで作れます。HTML-in-Canvasを使うのは、さらにガラスの縁で背景が曲がって見える表現を加えるためです。

ただ、ヘッダーの文字やボタンだけをCanvasへ描いても、背後の記事は描画の材料になりません。記事のどこを背景として取り込むかを決める必要があります。

そこで今回は、本文を複製し、ヘッダーに重なる部分だけ見えるように切り取る構成を選びました。この複製をCanvasへ描き、一度バッファへ移してから、背景の参照位置を変えて描き直します。

上下の縁では縦方向、左右の縁では横方向に背景の参照位置をずらします。縁の断面を丸く近似し、その面の向きに応じて変位を加え、中央部分は保ちます。これは画面上の画像を加工する近似表現であり、厚みのあるガラスを通る光の厳密な計算や、AppleのLiquid Glass実装の再現ではありません。

加工前の背景が透けて二重に見えるのを避けるため、Canvasの描画成功後はCSSの背景ぼかしを止め、取り込んだ背景へ切り替えます。文字やリンク自体は元のHTMLのままで、APIが使えない環境ではCSSの背景を表示します。

ヘッダーの操作は元のHTMLに残し、装飾用の背景だけを加工する

Canvasはaria-hidden="true"とinertを持つ装飾に限定しました。ロゴ、リンク、テーマ切替を操作するのは、Canvasの外にある元のHTMLです。この用途では、ヘッダー全体の操作をCanvas上へ移す必要がありませんでした。

4 複製した本文は、元の表示と同じとは限らない

この方式には、実装前に考えておきたい制約があります。

まず、描画する帯を小さくしても、複製した本文のDOMが小さくなるわけではありません。長い記事なら、複製とレイアウトの負荷が増えます。Canvasの解像度を抑えても、DOM側の負荷は別に考える必要があります。

次に、cloneNode(true)は属性と子孫を複製しますが、addEventListener()で登録したリスナーや、Canvasへ描いた画像は複製しません。onclickのようなHTML属性に書いたイベント処理は属性としてコピーされるため、「イベント処理はすべて消える」ともいえません。MDNのcloneNode()の説明

今回の複製からは、スクリプト、iframe、別のCanvas、ダイアログを除外しました。IDの重複を避ける処理も入れています。複製を元の表示の完全な再現としては扱っておらず、埋め込み投稿、動画、sticky要素、動いている表現などは、実際の画面と一致しない場合があります。

また、APIそのものも、画面に見える情報をすべて自由に読み取る設計ではありません。WICGのセキュリティ・プライバシーの説明では、クロスオリジンの情報、訪問済みリンク、スペルチェック、自動入力のプレビューなどを描画へ漏らさない方針が示されています。セキュリティ・プライバシーの説明

「ページをそのままスクリーンショットにして使える」と考えるより、描画対象と扱える情報に条件がある、と捉えたほうが実装の判断をしやすくなります。

この方式を使う際は、複製する範囲と更新頻度を絞り、実際のコンテンツで負荷を確かめる必要があります。

5 フォールバックを先に作る

2026年10月25日に確認したWICGの文書では、Chromiumでフラグを有効にして試す実験的な機能として説明されています。WICGの状態説明

Chromeの初期の紹介記事には148〜150でのOrigin Trialの案内が残っていますが、2026年9月29日更新の公式更新記事では、Chrome 160まで延長されたと説明されています。Origin Trialは登録したサイトで実験機能を試す仕組みで、すべての閲覧者のブラウザーで既定で有効になったという意味ではありません。

今回のヘッダーでは、先に不透明な背景を定義し、CSSのぼかしが使える環境で半透明にします。APIが使えて描画に成功した場合だけ、Canvasの背景へ切り替えます。

Canvasが使えなくても、CSSでヘッダーを表示する

APIの有無は、ブラウザー名ではなく、今回使うメソッドで調べます。

function canUseHtmlInCanvas(canvas) {
	const context = canvas.getContext('2d');
	return (
		typeof context?.drawElementImage === 'function' &&
		typeof canvas.requestPaint === 'function'
	);
}

この判定が成功しても、描画が成功するとは限りません。Canvasは最初は透明にし、描画が成功した後だけ表示します。要求や描画で例外が起きたら、複製した要素を取り除き、イベントリスナーと監視も止めます。非表示になった後の不要な更新も止めます。

動きを減らす設定ではCanvasを有効にしません。透明度を減らす設定や強制色表示では、不透明な背景へ戻します。新しい描画機能が使えないことが、ナビゲーションを使えない理由にならないようにしています。

6 Origin Trialで動作を確認する

今回の検証では、ChromeでHTML-in-Canvasを試すために、localhostを対象としたOrigin Trialを利用しました。発行されたトークンをローカル環境に設定すると、通常設定では使えなかったAPIが有効になり、HTML要素をCanvasへ描画できることを確認しました。

ヘッダーでは、記事のHTMLを背景として取り込み、縁に合わせた加工とスクロールに連動した更新が動作しました。以下のGIFで、その見え方を紹介します。

Origin Trialを有効にしたローカル環境で、記事をスクロールしたときのヘッダーの屈折

画像や文字がヘッダーの下を通るとき、縁の近くで背景が変位します。GIFは見え方を伝えるためのもので、滑らかさや描画負荷の評価には別途測定が必要です。長い記事での性能や、他のブラウザーでの動作はまだ検証していません。

公開サイトとの表示の違い

Origin Trialのトークンを配信したのはローカルの検証環境だけで、公開サイトには配信していません。そのため、必要なAPIが通常設定で利用できないブラウザーでは、公開サイトのヘッダーはCSSの半透明背景とぼかしで表示され、GIFのような屈折は加わりません。

ローカル環境では、Origin Trialを無効にした場合も、描画の失敗を模擬した場合も、CSS表示へ戻ることを確認しました。

7 HTML-in-Canvasを試して

HTML-in-Canvasは、HTMLで組んだ要素をCanvasへ取り込み、変形や合成を加えるための仕組みです。HTMLのレイアウトを活用しながら、CSSだけでは難しい描画へつなげられます。要素と描画位置の対応を扱うAPIも含まれており、HTMLとCanvasを組み合わせる表現の幅を広げてくれます。

今回の検証では、記事のHTMLを取り込み、ヘッダーの位置で切り抜いて、ガラスの縁に合わせて背景を変位させるところまで確認できました。HTML-in-Canvas自体がLiquid Glassを描くわけではなく、取り込んだ背景をどう加工するかは実装側の仕事になります。実用に向けては、描画負荷の評価を続けたいと思います。

このブログのヘッダーは、必要なAPIが利用できて描画に成功すれば、HTML-in-Canvasを使う表示へ自動で切り替わるようにしています。現行のAPIと互換性のある形でブラウザーが通常対応すれば、Origin Trialを有効にしなくても、この表現が見られるはずです。動きや透明度を抑える設定では、引き続きCSSの表示を使います。

ブラウザーの対応が進んで、このブログのヘッダーがHTML-in-Canvasで表示されるようになるのが楽しみです。

ほかの記事も読む