単一画面の Canvas ツールのスタック選定(vanilla TS / Svelte / Preact と PWA ツーリング)
画面が 1 つで Canvas 2D が中心の Web ツールでは、React(約 45 KB gzip)はランタイムが precache の負荷になり、Konva / Fabric も不要。vanilla TS + Vite を基本にし、画面や状態が増えたら Svelte 5(約 4 KB)か Preact(約 4 KB)へ。Service Worker は vite-plugin-pwa の generateSW でアプリシェルだけ precache する。
調査時点: 2026-06。公開プロジェクト frost-mosaic(なぞった所をモザイクにする PWA)の技術選定で調べたもの。数値は当時の概算で、Bundlephobia 等で再確認する前提。
Canvas 2D は、HTML の描画領域に JavaScript で図形や画像を描く API。PWA(Progressive Web App)は、インストールやオフライン利用など、アプリに近い機能を備えた Web アプリ。
ランタイムのサイズ(min+gzip 概算)
| フレームワーク | ランタイム | 備考 |
|---|---|---|
| vanilla TS / JS | 0 KB | |
| Preact | 約 4 KB | 公式の “3KB” は宣伝値。計測値は 4〜4.3 KB |
| Svelte 5 | 約 3〜4 KB | コンパイラ出力は素の JS。コンポーネントのソース量が約 137 KB を超えると React より大きくなる |
| SolidJS | 約 7〜8 KB | |
| Vue 3 | 約 34 KB | |
| React 18 / 19 + ReactDOM | 約 45 KB |
PWA ではこのサイズがそのまま Service Worker の precache に入る。
実在する Canvas 系ツールのスタック
| ツール | フレームワーク | Canvas |
|---|---|---|
| Excalidraw | React + TypeScript | Canvas 2D + Rough.js |
| Squoosh | Preact + TypeScript + Rollup | Canvas + WebAssembly |
| Photopea | 非公開(vanilla に近いとみられる) | Canvas 2D + 独自エンジン |
Excalidraw は共同編集・ルーティング・パッケージ分割など複数画面相当の機能量があり、React の採用理由は画面の複雑さにある。単一画面の画像変換ツールである Squoosh は Preact を選んでいる。
フレームワークが死荷重になる条件 / 正解になる条件
死荷重になりやすいのは、Canvas の描画ループと Virtual DOM の差分検出を重ねる構成。react-konva の開発者自身が「React + Konva は 2 層の抽象化になり遅くなりうる」と書いている。requestAnimationFrame やピクセル処理を頻繁に回すアプリで顕在化する。
フレームワークが正解になるのは次のいずれかが起きたとき。
- 画面が複数になる(About、設定、ヘルプなどのルーティングが発生する)
- スライダーや設定パネルの状態が複雑に相互依存する
- チームで開発し、コンポーネント規約の恩恵が出る
| 状況 | 選択 |
|---|---|
| 依存ゼロを優先 | vanilla TS + Vite |
| 状態管理が煩雑になった | Svelte 5(約 4 KB) |
| React エコシステムを使いたい | Preact(約 4 KB、Squoosh の実績) |
| React | 単一画面 Canvas ツールには過大 |
Canvas ライブラリは要らない
| ライブラリ | サイズ目安 | 向く用途 |
|---|---|---|
| Konva.js | 約 60 KB | ドラッグ・変形・イベントバブリング・ヒットテスト |
| Fabric.js v6 | 200 KB 超 | SVG import / export、画像編集向けの組み込みブラシ |
| Paper.js | 60 KB 超 | ベジェ・パス操作 |
| p5.js | 80 KB 超 | creative coding |
フリーハンドのマスクを描いてモザイク / ぼかしを合成する処理は Canvas 2D API だけで書ける。
- フリーハンド描画:
pointermove→ctx.lineTo+ctx.stroke。マスクは OffscreenCanvas の別レイヤーで持つ - ぼかし:
ctx.filter = "blur(Xpx)"; ctx.drawImage(img, ...)。端のフェードを避けるため、ブラー半径分はみ出して描く - モザイク:
getImageDataでブロックごとの平均色を計算してputImageData、または縮小 →imageSmoothingEnabled = false→ 拡大 - マスク合成:
globalCompositeOperation = "destination-in"または"source-atop"
Konva が解決する「オブジェクトの選択・ドラッグ・変形」は、画像上の塗り領域を扱うツールには出てこない。
UI コンポーネントライブラリも要らない
スライダー 4 個・トグル・ボタン数個の規模では、<input type="range"> + 自前 CSS で足りる。ネイティブ要素はキーボード操作とスクリーンリーダーに対応しており、WCAG 2.1 AA の最低基準は満たせる。コンポーネントライブラリ(shadcn/ui は Radix で約 30 KB、MUI は 100 KB 超)が必要になるのは、複雑なコンボボックス・日付ピッカー・aria-live の動的 UI が要るとき。React を採らないなら shadcn/ui も選択肢から外れる。
PWA ツーリング: vite-plugin-pwa
- devDependency として動く。Workbox のランタイムは生成される
sw.jsに埋め込まれ、アプリ本体のバンドルには入らない。virtual:pwa-registerを import したときだけ 1 KB 未満の登録スニペットが入る generateSW戦略でdist/の HTML / CSS / JS を自動で precache する。画像や利用者のファイルはデフォルトで precache に含まれないinjectManifestはバックグラウンド同期やプッシュ通知など独自の SW ロジックが要るときだけ- Workbox 7 以降、
maximumFileSizeToCacheInBytes(既定 2 MiB)を超えるアセットはビルドエラーになる globPatternsを絞り、アプリシェル(index.html/main.js/style.css)だけ precache する。利用者の画像はキャッシュしない
画面構成の最小セット
Squoosh・Photopea・Excalidraw はいずれも「ツール自体が着地点で、別のランディングページを持たない」構成。個人 OSS ツールで要るのは 3 点。
- エディタ画面(
/): ファイルドロップ → 操作 → 保存 - About / プライバシー: モーダルか
/about。「ブラウザ内で処理・サーバー送信なし・ライセンス」を 3 行程度 - UI 内のインラインヒント(「ここをなぞる」程度のマイクロコピー)
専用 LP、FAQ、チュートリアル動画は要らない。
関連
更新履歴
- 2026-06-15: 初版(ローカル調査ノート)
- 2026-10-17: Wiki へ移植、概念ページとして再構成。冒頭に定義を追加
出典
- https://github.com/preactjs/preact/discussions/3325
- https://khromov.se/svelte-5-brings-up-to-50-bundle-size-decrease-for-existing-svelte-4-apps/
- https://svelte-scaling.acmion.com/
- https://bundlephobia.com/package/vue
- https://github.com/GoogleChromeLabs/squoosh/blob/dev/package.json
- https://github.com/excalidraw/excalidraw
- https://konvajs.org/docs/guides/best-canvas-library.html
- https://vite-pwa-org.netlify.app/guide/service-worker-precache
- https://vite-pwa-org.netlify.app/workbox/generate-sw
- https://www.photopea.com/privacy.html
同じトピック — Web
- DAM カラオケ検索(clubdam)の挙動と制約
- Core Web Vitals と画像最適化
- favicon の最小構成(SVG + ICO + apple-touch-icon + manifest)
- フロントエンドのフレームワークとツールの動き(2025)
- HTML-in-Canvas API
- 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 で動画タイトルとチャンネル名を取る