blog.dopana

Back

画像のリサイズ、トリミング、WebP/AVIF形式への変換、動的OGP(OpenGraph)画像の生成は、WebアプリケーションやSSRサーバーにおいて最もCPU負荷の高い処理の1つです。

これまでNode.jsエコシステムでは sharp が標準として広く使われてきました。しかし、sharp はC++ネイティブアドオン(libvips)を必要とし、アーキテクチャ間の依存関係やDockerコンテナ肥大化の要因となっていました。

Bun v1.4.0 から導入された Bun.Image は、ランタイムに組み込まれた高速ネイティブ画像処理パイプラインであり、これらの課題を根本から解決します。

flowchart LR
    NodeApproach["Node.js + sharp<br/>(C++ Addon, 重いバイナリ, node-gyp)"] -.->|置き換え| BunApproach["Bun.Image<br/>(依存ゼロ, SIMD高速化, オフスレッド)"]
    style NodeApproach fill:#ffeef0,stroke:#d73a49,stroke-width:1px
    style BunApproach fill:#f0fff4,stroke:#2da44e,stroke-width:2px

1. 10歳でもわかる:Bun.Imageとは? (ELI5)#

ケーキ屋さんをイメージしてください:

  • 従来の方法 (sharp / npm addon):ケーキにイラストを描くたびに、外部の専門パティシエ(C++ binding)を呼び、重い道具箱(native binaries)を持ってきてもらう必要があります。別の工房に移動すると道具が合わなくなることもあります。
  • Bunの方法 (Bun.Image):メインシェフ(Bunランタイム自身)がすでに超高速ロボットアーム(SIMDアクセラレーション)を備えています。外部の業者を呼ばなくても、材料を渡すだけで瞬時に綺麗なケーキが完成します。

Bun.Image は追加のnpmパッケージを一切インストールすることなく、高速に画像を変換・最適化できるBun内蔵の仕組みです。

2. Bun.Image の動作メカニズム#

Bun.Image はハードウェアレベルの最適化からWeb標準APIまでシームレスに設計されています:

flowchart TD
    subgraph MainThread["JSメインスレッド (Non-blocking)"]
        Req["Astro SSR / APIリクエスト"] --> Input["Input Buffer / Uint8Array / Blob / File"]
        Input --> Chain["パイプライン構築 (Lazy)<br/>new Bun.Image(input).resize(1200, 630).webp()"]
        Chain --> Await["終端メソッド呼び出し<br/>await pipeline.blob()"]
    end

    subgraph NativeWorker["オフスレッド ネイティブエンジン (Zig / C++ & SIMD)"]
        Await --> Decode["並列デコード<br/>(libjpeg-turbo / spng / libwebp)"]
        Decode --> SIMD["SIMD幾何学変換<br/>(Apple vImage / Highway SIMD)"]
        SIMD --> Encode["ネイティブエンコード<br/>(WebP / PNG / JPEG)"]
    end

    subgraph OutputStage["Web標準出力"]
        Encode --> BlobRes["MIMEタイプ付きBlob (image/webp)"]
        BlobRes --> HTTP["Response(blob, &#123; headers &#125;)"]
    end

主な技術的特長:#

  1. ゼロ依存 & ネイティブアドオン不要: NODE_MODULE_VERSION の不一致やAlpine Dockerでのビルドエラーが解消されます。
  2. SIMDハードウェアアクセラレーション: x86_64/ARM LinuxではGoogle Highway SIMD、macOSではApple Accelerate vImage を利用し、CPU性能を限界まで引き出します。
  3. オフスレッドでの遅延評価 (Lazy Evaluation):
    • new Bun.Image(input).resize(800, 600).webp() を呼び出した時点では、軽量な変換設定のみが保持されます。
    • .blob().bytes().write() などの終端メソッドを await して初めて、バックグラウンドのネイティブスレッドで実際のデコード・変換・エンコードが実行されます(JSのイベントループをブロックしません)。
  4. Web標準 Response との親和性:
    • .blob() 出力には自動的に適切なMIMEタイプ(image/webpimage/png など)が付与されるため、AstroのSSRエンドポイントでそのまま return new Response(blob) として返却できます。

3. Bun.Image の活用シーン#

ユースケース実装内容主なメリット
動的OGP(アイキャッチ)生成記事タイトルからリアルタイムでSNS共有画像を生成。satori と組み合わせてSVG→PNG/WebPをミリ秒単位で生成可能。
Astro SSR オンデマンド最適化クライアントの画面幅に応じた画像リサイズ・WebP変換プロキシ。メインスレッドを阻害せずに転送量を大幅削減。
ユーザー投稿アバター処理アップロードされた画像のトリミング・圧縮とS3/R2保存。ネイティブ速度で70〜80%の容量圧縮。
ビルド時アセット最適化静的サイトビルド時に src/assets/ 配下の画像を一括最適化。ビルド時間を大幅に短縮。

4. Astroでの実践:動的OGP画像エンドポイント#

以下は、AstroのAPIエンドポイントで Bun.Image を使ってOGP画像を生成する実装例です:

[!TIP] PNGの代わりに .webp({ quality: 80 }) を指定すると、ファイルサイズをさらに約30%削減できます。

5. 一括画像最適化スクリプト例#

ビルド前後に静的アセットを一括で変換・最適化するCLIスクリプトも簡単に記述できます:

6. Bun.Image vs sharp 比較#

項目sharpBun.Image (Bun 1.4+)
インストールbun add sharp + ネイティブバイナリ不要(内蔵)
依存関係サイズ約30MB〜50MB0 MB(追加なし)
CI/Docker構成glibc/muslの互換性設定が必要Bunバイナリ単体で即座に動作
オフスレッド実行対応 (libuvスレッドプール)対応 (ネイティブワーカースレッド)
SIMD対応libvips SIMDApple Accelerate vImage / Highway SIMD
Web Standard ResponseStream/Bufferの変換が必要.blob() / .bytes() で直接連携

[!NOTE] 複雑なカラープロファイル処理や高度な画像合成などのニッチな用途では依然として sharp が適している場合があります。しかし、一般的なWeb用途(リサイズ、変換、OGP生成)の95%においては、Bun.Image の方がシンプルかつ高速です。

まとめ#

Bun.Image は、オールインワンランタイムとしてのBunの強みを象徴する機能です。Astro SSRエンドポイントや画像変換パイプラインにおいて、余計な依存関係を持たずにネイティブの処理性能を手に入れることができます。

参考文献#