画像プレースホルダー技術の比較 - LQIP, BlurHash, SQIP の実装ガイド
画像プレースホルダーとは - なぜ空白よりも優れているのか
画像プレースホルダーとは、本来の画像が読み込まれるまでの間に表示する代替コンテンツです。遅延読み込み (lazy loading) を実装したページでは、ビューポート外の画像は読み込みが遅延されるため、スクロール時に一瞬空白が表示されることがあります。この空白をプレースホルダーで埋めることで、ユーザー体験を大幅に改善できます。
プレースホルダーがない場合の問題点:
- CLS (Cumulative Layout Shift) の悪化: 画像の読み込み完了時にレイアウトがガタつき、Core Web Vitals のスコアが低下する
- 知覚的な遅さ: 空白エリアが表示されると、ユーザーはページが壊れている、または読み込みが遅いと感じる
- コンテンツの文脈喪失: 画像が表示されるまで、そのエリアに何があるのか予測できない
プレースホルダーの種類は大きく分けて以下の 4 つがあります。
- 単色プレースホルダー: 画像の支配的な色 (ドミナントカラー) で塗りつぶす。最もシンプルだが情報量が少ない
- LQIP (Low Quality Image Placeholder): 極小サイズの低品質画像をインラインで埋め込む
- BlurHash: 画像をコンパクトな文字列にエンコードし、クライアントでぼかし画像を生成
- SQIP (SVG-based Quality Image Placeholder): SVG のプリミティブ図形で画像を近似する
どの手法を選ぶかは、データサイズ、視覚的品質、実装の複雑さ、パフォーマンスへの影響のバランスで決まります。以降のセクションで各手法を詳しく比較します。
LQIP の実装 - 極小画像によるプレビュー表示
LQIP (Low Quality Image Placeholder) は、元画像を極端に縮小した低品質版をインラインで HTML に埋め込む手法です。Facebook が 2015 年頃から採用したことで広く知られるようになりました。ブラウザが縮小画像を拡大表示する際に自然なぼかし効果が得られるため、追加の CSS フィルタなしでもプレースホルダーとして機能します。
実装手順:
- 元画像を幅 20-42 ピクセル程度に縮小する (アスペクト比は維持)
- JPEG 品質 20-30 でエンコードする
- Base64 エンコードして
data:image/jpeg;base64,...として<img>のsrcに設定する - 本来の画像が読み込まれたら
srcを差し替える
データサイズの目安: 幅 20px の JPEG (品質 20) で約 300-600 バイト。Base64 エンコードすると約 400-800 文字になります。HTML に直接埋め込んでも、ページサイズへの影響は画像 1 枚あたり 1KB 未満に収まります。
CSS によるぼかし強化: ブラウザのバイリニア補間だけでは、ブロックノイズが目立つ場合があります。filter: blur(20px) を適用し、画像読み込み完了時にトランジションで解除すると、より滑らかな表示が得られます。
.lqip { filter: blur(20px); transform: scale(1.1); transition: filter 0.3s, transform 0.3s; }.lqip.loaded { filter: blur(0); transform: scale(1); }
LQIP の利点は実装のシンプルさと、特別なライブラリが不要な点です。Sharp や ImageMagick で簡単に生成でき、既存のビルドパイプラインに組み込みやすいのが特徴です。
BlurHash の仕組み - 文字列で画像を表現する革新的アプローチ
BlurHash は Wolt (フィンランドのフードデリバリー企業) が開発したアルゴリズムで、画像を 20-30 文字程度の短い文字列にエンコードします。この文字列からクライアントサイドでぼかし画像を生成するため、LQIP のように画像データを転送する必要がありません。
エンコードの原理: BlurHash は画像を離散コサイン変換 (DCT) で周波数成分に分解し、低周波成分のみを保持します。コンポーネント数 (X 方向 x Y 方向) を指定でき、デフォルトは 4x3 です。各コンポーネントの係数を Base83 でエンコードして文字列化します。
デコードの流れ:
- Base83 文字列をデコードして DCT 係数を復元する
- 指定された解像度 (通常 32x32 程度) で逆 DCT を計算する
- 各ピクセルの色値を sRGB に変換して Canvas や ImageData に描画する
データサイズ: 4x3 コンポーネントの場合、わずか 20 文字 (約 20 バイト) で画像の大まかな色分布を表現できます。LQIP の 300-600 バイトと比較して 10-30 倍コンパクトです。JSON API のレスポンスに含めても帯域への影響はほぼゼロです。
実装例 (JavaScript デコード):
import { decode } from 'blurhash';const pixels = decode('LEHV6nWB2yk8pyo0adR*.7kCMdnj', 32, 32);// pixels は Uint8ClampedArray (32*32*4 = 4096 要素)// Canvas の ImageData に設定して描画
BlurHash は Instagram、Mastodon、Unsplash など多くのサービスで採用されています。API レスポンスに BlurHash 文字列を含め、画像 URL の読み込み完了前にプレースホルダーを表示する用途に最適です。
SQIP の特徴 - SVG プリミティブによる芸術的なプレースホルダー
SQIP (SVG-based Quality Image Placeholder) は、画像を SVG のプリミティブ図形 (三角形、楕円、矩形など) の組み合わせで近似する手法です。Tobias Baldauf が 2017 年に公開したツールで、内部的には Primitive (Michael Fogleman 作) のアルゴリズムを使用しています。
生成プロセス:
- 元画像を解析し、最も視覚的に重要な領域を特定する
- 指定された数のプリミティブ図形 (デフォルト 8 個) を配置して画像を近似する
- 各図形の形状、位置、色、透明度を最適化する (ヒルクライミング法)
- 生成された SVG にガウシアンぼかしフィルタを適用する
- 最終的な SVG を Base64 エンコードしてインラインで埋め込む
データサイズ: 8 個のプリミティブで約 800-1200 バイト。LQIP と同程度ですが、SVG はベクター形式のためどの解像度でもシャープに表示されます。Retina ディスプレイでもぼやけません。
視覚的品質: SQIP の最大の特徴は、プレースホルダーとしての美しさです。幾何学的な図形の組み合わせが芸術的な印象を与え、単なるぼかし画像よりもデザイン性が高いプレースホルダーになります。ポートフォリオサイトやギャラリーサイトなど、ビジュアルの品質が重要なサイトに適しています。
SQIP の課題: 生成に時間がかかることが最大の弱点です。1 枚あたり 1-5 秒程度の処理時間が必要で、大量の画像を扱うサイトではビルド時間が大幅に増加します。また、Node.js の sqip パッケージはメンテナンスが停滞気味で、最新の Node.js バージョンとの互換性に問題が生じることがあります。
代替として、primitive コマンドラインツールを直接使用し、出力 SVG に手動でぼかしフィルタを追加する方法もあります。Go で実装されているため高速に動作します。
手法の比較と選定基準 - プロジェクトに最適な手法を選ぶ
4 つのプレースホルダー手法を主要な評価軸で比較します。プロジェクトの要件に応じて最適な手法を選択してください。
データサイズ比較:
- ドミナントカラー: 7 バイト (CSS の色値のみ)
- BlurHash: 20-30 バイト (文字列)
- LQIP: 300-800 バイト (Base64 画像)
- SQIP: 800-1500 バイト (Base64 SVG)
視覚的品質 (情報量の多さ): SQIP > LQIP > BlurHash > ドミナントカラー。SQIP は画像の構造を最もよく保持し、LQIP は色の分布を自然に表現します。BlurHash は大まかな色のグラデーションを再現しますが、エッジ情報は失われます。
生成速度: ドミナントカラー (1ms 未満) > BlurHash (10-50ms) > LQIP (50-200ms) > SQIP (1-5 秒)。大量の画像を処理するビルドパイプラインでは、SQIP の生成時間がボトルネックになる可能性があります。
推奨する使い分け:
- API 駆動のアプリ (SNS、EC サイト): BlurHash を推奨。API レスポンスに文字列を含めるだけで実装でき、データサイズの影響が最小
- ブログ・メディアサイト: LQIP を推奨。ビルド時に生成してインラインで埋め込む。実装がシンプルで十分な視覚品質
- ポートフォリオ・ギャラリー: SQIP を推奨。ビジュアルの美しさが重要なサイトでは、生成コストに見合う価値がある
- パフォーマンス最優先: ドミナントカラーを推奨。データサイズが最小で、CLS 防止の最低限の役割を果たす
複数の手法を組み合わせることも有効です。例えば、ファーストビューの画像には LQIP を使い、スクロール下の画像にはドミナントカラーだけを設定する、といった段階的なアプローチが実用的です。
Next.js と Astro での実装例 - モダンフレームワークとの統合
モダンな Web フレームワークでは、画像プレースホルダーの生成と表示を効率的に統合する仕組みが提供されています。Next.js と Astro での具体的な実装方法を紹介します。
Next.js の Image コンポーネント: Next.js の <Image> コンポーネントは placeholder prop で LQIP と BlurHash をネイティブサポートしています。
<Image src="/photo.jpg" placeholder="blur" blurDataURL="data:image/jpeg;base64,..." />
静的インポートの場合、ビルド時に自動的に blurDataURL が生成されます。動的画像の場合は、plaiceholder ライブラリを使ってサーバーサイドで生成できます。
import { getPlaiceholder } from 'plaiceholder';const { base64 } = await getPlaiceholder('/photo.jpg');
Astro での実装: Astro の <Image> コンポーネントは直接的なプレースホルダーサポートを持ちませんが、ビルド時に Sharp で LQIP を生成し、コンポーネントに渡す方式が一般的です。
import sharp from 'sharp';const buffer = await sharp('src/images/photo.jpg').resize(20).jpeg({ quality: 20 }).toBuffer();const lqip = `data:image/jpeg;base64,${buffer.toString('base64')}`;
BlurHash の React 実装: react-blurhash パッケージを使えば、BlurHash 文字列から Canvas ベースのプレースホルダーを簡単に表示できます。Intersection Observer と組み合わせて、ビューポートに入ったタイミングで本来の画像を読み込む実装が効果的です。
パフォーマンスの注意点: インライン Base64 画像は HTML のサイズを増加させるため、SSR ページでは初期 HTML の転送量に注意が必要です。画像が 50 枚以上あるページでは、ファーストビュー外の画像にはドミナントカラーのみを設定し、LQIP はファーストビュー付近の 5-10 枚に限定することを推奨します。