HTML-in-Canvas API
canvas の子に置いた実 DOM 要素を、DOM として生かしたまま 2D / WebGL / WebGPU に描画する WICG 提案。アクセシビリティツリー・フォーカス・テキスト選択が残るのが html2canvas や SVG foreignObject との決定的な差。2026-06 時点で Chrome の Origin Trial 中、Firefox / Safari は態度未表明。
調査時点: 2026-06。Origin Trial 中の API なので、シグネチャ・ブラウザ対応・標準化状況は変わりうる。実装上の検証(a11y・性能・セットアップ・シェーダー)は HTML-in-Canvas の実現性 に分けた。
何か
<canvas> の子要素として置いた DOM 要素を、canvas の描画コンテキスト(2D / WebGL / WebGPU)にピクセルとして描く API 群。設計の核は二層構造にある。
- DOM 層: 子要素は
layoutsubtree属性付き canvas の直接の子として実 DOM に存在し続ける。アクセシビリティツリー、フォーカス、find-in-page、テキスト選択、CSS レイアウト計算、ヒットテストはこの層で動く。子要素自体のペイントは画面に出ない。 - canvas 描画層:
drawElementImage()を呼ぶたびにブラウザが DOM 要素のスナップショットを canvas に転写する。画面に見えるのはこのピクセルだけ。
<canvas layoutsubtree> ├ DOM 層(実在): <input> はフォーカス・入力、<p> は a11y・検索の対象 └ 描画層: ctx.drawElementImage(content, x, y) の戻り値 transform を content.style.transform に入れて DOM 位置を描画位置に合わせる子要素に指定した CSS transform は描画には使われず、ヒットテストとアクセシビリティの位置計算にだけ使われる。drawElementImage() が返す DOMMatrix を element.style.transform に代入して両者を同期させるのが基本パターン。
なぜ生まれたか — 従来手法の限界
canvas の fallback content は実描画と一致する保証がなく、スクリーンリーダー・find-in-page・翻訳が canvas 内のテキストに届かない。これを埋めてきた手法はどれも根本解ではなかった。
| 手法 | 限界 |
|---|---|
| html2canvas | CSS を JS で再実装する近似。box-shadow・clip-path・3D transform・iframe・動画は非対応。外部画像で canvas が tainted になる。サブピクセルのフォント描画を再現できない。公式 FAQ が「完全な CSS 対応は永遠にない」と明言 |
SVG <foreignObject> + drawImage() | Safari で描画失敗や背景画像の欠落が多い。tainting の挙動がブラウザ間で不一致だった(blob URL 経由で Firefox だけ非 tainted。Chrome は 2024 年に Firefox に合わせた)。base64 化した時点で a11y 情報は消える |
| CSS Houdini Paint API | CSS プロパティをトリガーにカスタム描画する仕組みで、既存 DOM 要素のスナップショットを取る API はない |
drawWindow()(Firefox 非標準) | 拡張機能専用。同期 API で Fission と非互換。標準化されなかった |
| 軸 | html2canvas | SVG foreignObject | Houdini Paint | HTML-in-Canvas |
|---|---|---|---|---|
| アクセシビリティ | なし | なし | なし | 実 DOM がツリーに残る |
| CSS 再現精度 | 部分的 | ブラウザ依存 | canvas サブセット | レンダリングエンジン直結 |
| 入力・フォーカス | 不可 | 不可 | 不可 | 可 |
| WebGL / WebGPU 統合 | 不可 | 不可 | 不可 | 可 |
| ブラウザ対応(2026-06) | 全主要 | 全主要(挙動差) | ほぼ Chrome | Chrome のみ(OT) |
提案の系譜
| 時期 | できごと |
|---|---|
| 2020-12 | Microsoft が blink-dev に Intent to Prototype: Canvas Formatted Text。fillText() が 1 行しか描けず、改行・CJK・双方向テキストを JS で再実装する困難が動機 |
| 2024-06 | Google が Intent to Prototype: canvas placeElement()。スコープがテキストから任意の HTML 要素へ拡大 |
| 2024-08 | WICG に canvas-place-element リポジトリ。2024-09 に W3C TAG レビュー #997 と WHATWG issue #10650 |
| 2024-09〜2025 初 | フィードバックを受けて API を immediate-mode に再設計し、WICG/html-in-canvas に移管。TAG #997 は “overtaken” でクローズ |
| 2025-05 | Chrome M138 で DevTrial(フラグ)。2025-08-21 に WHATWG HTML への PR #11588 |
| 2025-09 | プライバシー設計を更新し、「センシティブ情報を描画できるオプション」を削除 |
| 2026-03-13 | 新 TAG レビュー #1204。“Missing: Multi-stakeholder support” ラベル付き |
| 2026-05-19 | Chrome M148 で Origin Trial 開始(Google I/O で発表)。2026-06-10 に M154 まで延長 |
著者は Google と Igalia のメンバー 7 名。前身の drawElement / texElement2D という名前は WebGL の gl.drawElements() との混同を避けて drawElementImage / texElementImage2D に改名された。
API サーフェス(2026-06 時点の WebIDL)
partial interface HTMLCanvasElement { [CEReactions, Reflect] attribute boolean layoutSubtree; attribute EventHandler onpaint; void requestPaint(); ElementImage captureElementImage(Element element); DOMMatrix getElementTransform((Element or ElementImage) element, DOMMatrix drawTransform);};
interface mixin CanvasDrawElementImage { DOMMatrix drawElementImage((Element or ElementImage) element, double dx, double dy); // dWidth/dHeight 付き、sx/sy/sWidth/sHeight 付きのオーバーロードあり};CanvasRenderingContext2D includes CanvasDrawElementImage;OffscreenCanvasRenderingContext2D includes CanvasDrawElementImage;
partial interface WebGLRenderingContext { void texElementImage2D(GLenum target, GLenum internalformat, (Element or ElementImage) element, optional WebGLCopyElementImageConfig config = {});};partial interface GPUQueue { void copyElementImageToTexture(GPUCopyElementImageSource source, GPUCopyElementImageDestination destination);};
[Exposed=(Window,Worker), Transferable]interface ElementImage { readonly attribute double width; readonly attribute double height; undefined close(); };interface PaintEvent : Event { readonly attribute FrozenArray<Element> changedElements; };| メンバ | 役割 |
|---|---|
layoutsubtree 属性 | canvas の直接の子要素を CSS レイアウトとヒットテストに参加させる opt-in。子はスタッキングコンテキストと containing block になり、paint containment が効く |
paint イベント | 子要素の描画が変化したフレームに 1 回だけ発火(intersection observer の直後)。changedElements で変化した要素を得る |
requestPaint() | 変化がなくても次フレームで paint を 1 回発火させる。requestAnimationFrame() の類似物 |
drawElementImage() | 2D context に描画し、DOM 位置同期用の DOMMatrix を返す。paint の外で呼ぶと前フレームのスナップショットを使う |
texElementImage2D() / copyElementImageToTexture() | DOM 要素を WebGL / WebGPU のテクスチャへ直接転送 |
captureElementImage() | Transferable な ElementImage を生成。Worker に送って OffscreenCanvas で描ける |
getElementTransform() | WebGL / WebGPU で drawElementImage() の戻り値に相当する同期用行列を得る |
| 2D | WebGL | WebGPU | OffscreenCanvas(2D) | |
|---|---|---|---|---|
drawElementImage() | ○ | — | — | ○(ElementImage 経由) |
texElementImage2D() | — | ○ | — | — |
copyElementImageToTexture() | — | — | ○ | — |
layoutSubtree / paint / requestPaint() / captureElementImage() | canvas 要素側 | 同左 | 同左 | — |
Chrome 150.0.7869.0 で WebGL の texElementImage2D が 5 引数から 3 引数 + config に、WebGPU の copyElementImageToTexture がディクショナリ形式に変わった(Issue #132)。OT 期間中にシグネチャが動く前提で、try { 新 } catch { 旧 } のフォールバックが推奨されている。
セキュリティ・プライバシーモデル
描画できる情報は白リストで制限される。クロスオリジンコンテンツ(他オリジンの iframe)、訪問済みリンクのスタイル、スペルチェックマーカー、オートフィルのプレビュー、OS のテーマ色・アクセシビリティ色設定は描画から除外される。フォント描画の差がフィンガープリントに使われる懸念と、セキュリティ文書に TODO が残っている点がコミュニティで指摘されている。
標準化とブラウザ対応(2026-06-14 時点)
| ブラウザ | 状況 |
|---|---|
| Chrome | OT が M148〜M154。Intent to Ship 未提出。ローカルは chrome://flags/#canvas-draw-element |
| Brave | Chromium 147 以降で同じフラグから有効化できる |
| Firefox | standards-positions #1076(2024-09 開設)は verdict 未付与。フィンガープリント懸念を残しつつ stage 2 進行は容認、実装予定なし |
| Safari | standards-positions #630(2026-03 開設)にコメントなし |
WHATWG HTML への統合 PR #11588 は open のまま。TAG レビュー #1204 は進行中で、Google / Igalia 単独推進を TAG が懸念している。open issue は 32 件で、シェーダー歪み時のヒットテスト(#140)、style.transform に流し込む設計(#135)、直接の子要素しか描けない制限(#134)などが残る。将来の retained mode(canvas がコマンドバッファを保持し、スクロールやアニメーションをメインスレッドなしで自動更新)は設計段階で具体的な PR はない。
機能検出と graceful degradation
const supported = "layoutSubtree" in HTMLCanvasElement.prototype && "drawElementImage" in CanvasRenderingContext2D.prototype;非対応ブラウザでは layoutsubtree canvas の子要素がそのまま通常の DOM として表示される。「fallback content = 実際の描画内容」という設計なので、degradation は API レベルで保証されている。
向く用途・向かない用途
| 類型 | この API でないと難しい理由 | 公式デモ例 |
|---|---|---|
| (a) DOM へのシェーダーエフェクト | CSS filter は組み込みカーネル限定で任意の GLSL は走らない。スクショ経由だとインタラクティブ性が消える | Liquid Glass Distortion、Elastic Bulge、Pixel Disintegration |
| (b) 3D 空間へのライブ HTML 埋め込み | Three.js の CSS3DRenderer はオーバーレイ方式で、メッシュの裏に回せず他の描画と合成できない | 3D Room with Live Web Content |
| (c) ページ遷移・トランジション | View Transitions はシェーダー合成ができず、スクショ方式は遷移中に操作できない | Page Curl、Morphing Text |
| (d) スクリーンショット・OG 画像・動画書き出し | html2canvas の CSS 再現は約 8 割、satori は JSX と制限 CSS への変換が要る | HTML-to-Image Export、HTML Video Recording |
| (e) ゲーム UI・キャンバス系ツールの DOM UI | Figma・Google Docs・Miro が独自実装してきたテキストレイアウト・IME・CJK フォールバックをブラウザに委ねられる | Interactive Form、Rich Text Canvas Editor |
| (f) アクセシブルなデータ可視化 | canvas チャートは非表示の DOM ミラーで a11y を保っており、表示との一致保証が脆い | Accessible Charts |
CSS / SVG で足りる場合はこの API は過剰。すりガラスは backdrop-filter: blur(8px) saturate(180%)、カスタムカーネルや変位マップは SVG filter(feConvolveMatrix・feDisplacementMap)、レイヤー合成は mix-blend-mode、遷移のモーフィングは View Transitions で済むことが多い。
| 手法 | 向く用途 | 限界 |
|---|---|---|
| html2canvas | 静的な HTML スナップショット | 高度な CSS 非対応、操作不能 |
| html-to-image | dom-to-image の後継。foreignObject 経由 | 外部フォント・疑似要素・CORS |
| satori | OG 画像生成 | 既存 HTML をそのまま使えない |
| Puppeteer / Playwright | ピクセルパーフェクトなサーバー側キャプチャ | ヘッドレスブラウザが要る |
| HTML-in-Canvas | ブラウザ上でネイティブ描画を書き出す | Chrome 限定、OT 中 |
エコシステム(2026-06 時点)
- Three.js r184(2026-04-16):
THREE.HTMLTextureとInteractionManagerが実験的に追加(PR #31233)。InteractionManagerが毎フレーム CSSmatrix3dを計算し、ブラウザがポインタ・ホバー・フォーカス・入力をネイティブに処理する。 - PlayCanvas: WebGL バックエンドで対応済み(
device.supportsHtmlTexturesで検出)。WebGPU は未対応。getElementTransform()で DOM と投影行列を同期するHtmlSyncを提供。 - Babylon.js: フォーラムで議論のみ、公式ロードマップなし。
- React Three Fiber / drei: 固有の抽象化は見当たらない(Three.js 経由では原理的に可能)。
- 公式デモサイト(html-in-canvas.dev)が 17 本のリファレンス実装を公開。
現実的な使いどころ(OT 段階)
- プログレッシブエンハンスメントとしての視覚効果(Chrome だけ強化、他は通常 HTML)。
- 使用ブラウザを Chrome に固定できる内部ツール・ダッシュボード。
- デモ・ポートフォリオ・クリエイティブ作品。
- html2canvas をフォールバックに残した段階的移行。
関連
更新履歴
- 2026-06-14: 初版(ローカル調査ノート)
- 2026-10-17: Wiki へ移植、概念ページとして再構成
出典
- https://github.com/WICG/html-in-canvas/blob/main/README.md
- https://wicg.github.io/html-in-canvas/
- https://developer.chrome.com/blog/html-in-canvas-origin-trial
- https://html-in-canvas.dev/demos/
- https://groups.google.com/a/chromium.org/g/blink-dev/c/t_nGEmJ_v4s
- https://github.com/mozilla/standards-positions/issues/1076
- https://github.com/WebKit/standards-positions/issues/630
- https://github.com/w3ctag/design-reviews/issues/1204
- https://github.com/whatwg/html/pull/11588
- https://github.com/WICG/html-in-canvas/issues/132
- https://github.com/mrdoob/three.js/pull/31233
- https://developer.playcanvas.com/user-manual/graphics/advanced-rendering/html-in-canvas/
- https://tympanus.net/codrops/2026/05/13/exploring-the-html-in-canvas-proposal/
同じトピック — Web
- 単一画面の Canvas ツールのスタック選定(vanilla TS / Svelte / Preact と PWA ツーリング)
- DAM カラオケ検索(clubdam)の挙動と制約
- Core Web Vitals と画像最適化
- favicon の最小構成(SVG + ICO + apple-touch-icon + manifest)
- フロントエンドのフレームワークとツールの動き(2025)
- HTML-in-Canvas の実現性(a11y・性能・セットアップ・シェーダー)
- iOS Safari で canvas の画像を保存させる(download 属性 / Web Share / 長押し)
- 日本語 Web フォントのライセンスとサブセット化
- 個人サイトのライブウィジェットと外部 API の制限
- モダン CSS / Web API の対応状況(2025-2026)
- OPFS(Origin Private File System)
- Google Places API のキーの守り方(ブラウザ露出 vs サーバ中継)
- iOS の PWA 制限
- 機能別 React / TS ライブラリ対応表(2026-04 時点)
- URL 長の上限と日本語の URL エンコード膨張
- 書き出すテキストファイルの UTF-8 BOM と Markdown 継続行
- 縦書き Web の実装(writing-mode・ルビ・圏点・縦中横・禁則)
- PWA の Web Share Target(Android)の実測と落とし穴
- WebAssembly(Wasm)
- YouTube oEmbed で動画タイトルとチャンネル名を取る