Update (2026-04-23): v0.3.0 で最後の gem 依存(
image_processing)も削除し、文字通りゼロ依存になりました。さらに ブラウザで動くプレイグラウンド も公開。ImageProcessing::Puraのチェインがブラウザタブ内で ruby.wasm 経由で動きます。
TL;DR
Pure Ruby 製の画像処理 gem pura-image を作りました。これを image_processing gem の variant_processor として指定すると、Rails Active Storage の画像処理から libvips / ImageMagick / ffmpeg を完全に取り除けます。
# Gemfile
gem "image_processing"
+ gem "pura-image"
# config/application.rb
config.active_storage.variant_processor = :pura
# Dockerfile
- RUN apt-get install -y libvips-dev imagemagick
brew install vips も apt install imagemagick も不要。gem install pura-image するだけで動きます。
ブラウザで試せます
文章を読む前に動く様子を見たい方はこちら:
ページを開くと ruby.wasm が起動し、ImageProcessing::Pura のチェインがブラウザ内で実行されます。Rails Active Storage の variant 定義に書くコードと同じものが、サーバーに何も送らずにブラウザタブ内で走ります。
正確に言うと、ブラウザで動くのは ImageProcessing::Pura の処理チェイン部分だけで、Active Storage 本体(blob 管理や variant orchestration)は動いていません。それでも「Rails の variant コードが wasm サンドボックスで動く」のを目で見て確認できます。
対象読者
- Rails アプリで Active Storage を使っている開発者
- Docker イメージから libvips / ImageMagick を抜きたい人
- CI の
apt-get install行を削りたい人 - Windows / WSL での Rails 環境構築に苦しんだことがある人
- 新人教育で「
brew install vipsでつまずいた人」を見たことがある人
なぜ作ったか
プログラミング初学者に Rails を教えていると、rails new した直後の画像アップロード機能のセットアップでつまずく人が多くいます。Mac なら brew install vips、Linux なら apt install libvips-dev、Windows ならまた別の手順… と OS ごとに分岐するうえ、ビルドエラーで詰まることも多く、本題に入る前に脱落者が出てしまいます。
「Pure Ruby だけで動けばこの問題は全部消えるのでは?」というのが出発点です。同時に、業務 Rails アプリでも以下のメリットがあります。
- Docker イメージが軽くなる: libvips / ImageMagick / その依存ライブラリが丸ごと不要
-
CI が速くなる:
apt-get installのオーバーヘッドが消える - デプロイ環境を選ばない: Heroku / Fly.io / Render など PaaS でも追加 buildpack 不要
- Edge / Wasm 環境で動く: ruby.wasm 上でも画像処理ができる(ブラウザデモ)
対応フォーマット
JPEG / PNG / BMP / GIF / TIFF / ICO・CUR / WebP の 7 形式すべてを Pure Ruby で実装しています。マジックバイトでフォーマットを自動判定するので、拡張子に頼る必要はありません。
| Format | Decode | Encode |
|---|---|---|
| JPEG | ✅ | ✅ |
| PNG | ✅ | ✅ |
| BMP | ✅ | ✅ |
| GIF | ✅ | ✅ |
| TIFF | ✅ | ✅ |
| ICO/CUR | ✅ | ✅ |
| WebP | ✅ | ✅ |
v0.3.0 では image_processing gem への依存も切って、自作の pura-processing に置き換えました。これで本当に外部 gem ゼロです。
使い方
単体での利用
require "pura-image"
# どの形式でもマジックバイトで自動判定して読み込み
image = Pura::Image.load("photo.jpg")
image.width #=> 800
image.height #=> 600
# 拡張子から保存形式を決定
Pura::Image.save(image, "output.png")
# メソッドチェイン
Pura::Image.load("photo.jpg")
.resize_to_limit(800, 600)
.rotate(90)
.grayscale
Rails Active Storage との統合
image_processing gem 互換のアダプタを同梱しているので、設定 1 行で切り替わります。モデルやビューの記述は一切変更不要。
# Gemfile
gem "image_processing"
gem "pura-image"
# config/application.rb
config.active_storage.variant_processor = :pura
既存のコードはそのまま動きます。
class User < ApplicationRecord
has_one_attached :avatar do |attachable|
attachable.variant :thumb, resize_to_limit: [200, 200]
end
end
<%= image_tag user.avatar.variant(:thumb) %>
サポートしている操作
image_processing gem の主要 API はひととおり実装しています。
image = Pura::Image.load("photo.jpg")
image.resize_to_limit(800, 600) # 縮小のみ、アスペクト比維持
image.resize_to_fit(400, 400) # フィット、アスペクト比維持
image.resize_to_fill(400, 400) # 指定サイズに塗りつぶし、はみ出し中央クロップ
image.resize_and_pad(400, 400) # フィット後、余白を黒で埋める
image.resize_to_cover(400, 400) # カバー、クロップなし
image.crop(10, 10, 200, 200) # 領域クロップ
image.rotate(90) # 90 / 180 / 270 度回転
image.grayscale # グレースケール変換
ベンチマーク(vs ffmpeg)
400×400 画像、Ruby 4.0.2 + YJIT 環境で計測しました。
Decode
| Format | pura-* | ffmpeg | 比較 |
|---|---|---|---|
| TIFF | 14 ms | 59 ms | 🚀 4× 速い |
| BMP | 39 ms | 59 ms | 🚀 1.5× 速い |
| GIF | 77 ms | 65 ms | ほぼ同等 |
| PNG | 111 ms | 60 ms | 1.9× 遅い |
| WebP | 207 ms | 66 ms | 3.1× 遅い |
| JPEG | 304 ms | 55 ms | 5.5× 遅い |
Encode
| Format | pura-* | ffmpeg | 比較 |
|---|---|---|---|
| TIFF | 0.8 ms | 58 ms | 🚀 73× 速い |
| BMP | 35 ms | 58 ms | 🚀 1.7× 速い |
| PNG | 52 ms | 61 ms | 🚀 速い |
| JPEG | 238 ms | 62 ms | 3.8× 遅い |
| GIF | 377 ms | 59 ms | 6.4× 遅い |
11 操作中 5 つで C 実装より速い結果になりました。これは ffmpeg のプロセス起動オーバーヘッドのほうが処理本体より重いケースがあるためです。JPEG / WebP のような重い圧縮アルゴリズムは Pure Ruby だと不利ですが、サムネイル生成程度であれば実用範囲です。
制限事項・注意点
- JPEG / WebP のエンコード性能は libvips に劣ります。大量の画像を頻繁に生成するワークロードでは libvips のほうが向いています
- 大きな画像(数千ピクセル四方)の処理では C 実装との差が広がる可能性があります
- まだ若いライブラリです。本番投入される際は事前にベンチマーク・E2E テストで検証してください
まとめ
「外部依存を減らして同梱で完結させる」という方向は、Rails 8 の Solid Queue / Solid Cache / Solid Cable などにも通じる流れです。画像処理レイヤでも同じことを Pure Ruby だけで実現したのが pura-image です。
-
gem installで完結 - Active Storage に config 1 行で統合
- 7 フォーマット対応、一部処理は C 実装より速い
- ruby.wasm / JRuby / TruffleRuby でも動く
「Rails の依存をひとつでも減らしたい」「brew install を新人に説明したくない」「Docker イメージを軽くしたい」という方はぜひ試してみてください。GitHub での star や Issue / PR も歓迎です。
リンク
- GitHub: https://github.com/komagata/pura-image
- ブラウザプレイグラウンド (ruby.wasm): https://komagata.github.io/pura-image/
- 元記事(個人ブログ): https://docs.komagata.org/6443
- dev.to 版(英語): https://dev.to/komagata/active-storage-without-libvips-or-imagemagick-a-pure-ruby-alternative-4o99