pura-image
Pure Ruby image processing library with zero C extension dependencies. Bundles all pura-* format gems and provides an ImageProcessing adapter for Rails Active Storage.
Why this exists (in 30 seconds)
Stop installing system libraries just to resize an image in Rails.
# 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
That's it. No brew install vips. No apt install imagemagick. No C compiler. Works on macOS, Linux, Windows, Docker, CI, ruby.wasm — anywhere Ruby runs.
- ✅ Drop-in for
ImageProcessing::Vips/ImageProcessing::MiniMagick - ✅ 7 formats — JPEG, PNG, BMP, GIF, TIFF, ICO/CUR, WebP
- ✅ Some operations are faster than C (no process-spawn overhead — see Benchmark)
- ✅ Runs on ruby.wasm, JRuby, TruffleRuby
Supported Formats
| Format | Decode | Encode | Gem |
|---|---|---|---|
| JPEG | ✅ | ✅ | pura-jpeg |
| PNG | ✅ | ✅ | pura-png |
| BMP | ✅ | ✅ | pura-bmp |
| GIF | ✅ | ✅ | pura-gif |
| TIFF | ✅ | ✅ | pura-tiff |
| ICO/CUR | ✅ | ✅ | pura-ico |
| WebP | ✅ | ✅ | pura-webp |
Format auto-detection by magic bytes — no extension guessing needed for decode.
Installation
gem install pura-image
Usage
require "pura-image"
# Load any format (auto-detected from magic bytes)
image = Pura::Image.load("photo.jpg")
image = Pura::Image.load("logo.png")
image.width #=> 800
image.height #=> 600
# Save to any format (detected from extension)
Pura::Image.save(image, "output.png")
# Convert between formats
Pura::Image.convert("input.bmp", "output.jpg", quality: 85)
Pura::Image.convert("photo.tiff", "photo.png")
Image Operations
All operations from the image_processing gem are supported:
image = Pura::Image.load("photo.jpg")
# Resize
image.resize_to_limit(800, 600) # downsize only, keep aspect ratio
image.resize_to_fit(400, 400) # resize to fit, keep aspect ratio
image.resize_to_fill(400, 400) # fill exact size, center crop excess
image.resize_and_pad(400, 400) # fit within bounds, pad with black
image.resize_to_cover(400, 400) # cover bounds, no crop
# Transform
image.crop(10, 10, 200, 200) # crop region
image.rotate(90) # rotate 90/180/270 degrees
image.grayscale # convert to grayscale
# Chain operations
result = Pura::Image.load("photo.jpg")
.resize_to_limit(800, 600)
.rotate(90)
.grayscale
Pura::Image.save(result, "thumb.jpg", quality: 80)
Rails Active Storage Integration
Drop-in replacement for libvips/ImageMagick:
# Gemfile
gem "image_processing"
gem "pura-image"
# config/application.rb
config.active_storage.variant_processor = :pura
Models and views stay exactly the same:
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) %>
No brew install vips. No apt install imagemagick. Just gem install and go.
ImageProcessing::Pura API
require "image_processing/pura"
# Same API as ImageProcessing::Vips
processed = ImageProcessing::Pura
.source("photo.jpg")
.resize_to_limit(400, 400)
.convert("png")
.call(destination: "output.png")
# Pipeline branching
pipeline = ImageProcessing::Pura.source("photo.jpg")
large = pipeline.resize_to_limit(800, 800).call(destination: "large.jpg")
medium = pipeline.resize_to_limit(500, 500).call(destination: "medium.jpg")
small = pipeline.resize_to_limit(300, 300).call(destination: "small.jpg")
# Validation
ImageProcessing::Pura.valid_image?("photo.jpg") #=> true
Benchmark
400×400 image, Ruby 4.0.2 + YJIT vs ffmpeg (C + SIMD).
Decode
| Format | pura-* | ffmpeg | vs ffmpeg |
|---|---|---|---|
| TIFF | 14 ms | 59 ms | 🚀 4× faster |
| BMP | 39 ms | 59 ms | 🚀 1.5× faster |
| GIF | 77 ms | 65 ms | ~1× (comparable) |
| PNG | 111 ms | 60 ms | 1.9× slower |
| JPEG | 304 ms | 55 ms | 5.5× slower |
Encode
| Format | pura-* | ffmpeg | vs ffmpeg |
|---|---|---|---|
| TIFF | 0.8 ms | 58 ms | 🚀 73× faster |
| BMP | 35 ms | 58 ms | 🚀 1.7× faster |
| PNG | 52 ms | 61 ms | 🚀 faster |
| JPEG | 238 ms | 62 ms | 3.8× slower |
| GIF | 377 ms | 59 ms | 6.4× slower |
5 out of 11 operations are faster than C (ffmpeg process-spawn overhead).
Why pure Ruby?
gem installand go — nobrew install, noapt install, no C compiler- Works everywhere Ruby works — CRuby, ruby.wasm, mruby, JRuby, TruffleRuby
- Edge/Wasm ready — browsers (ruby.wasm), sandboxed environments, no system libraries needed
- Perfect for dev/CI — no ImageMagick/libvips setup.
rails new→ image upload → it just works - 7 formats, 1 interface — unified API across JPEG, PNG, BMP, GIF, TIFF, ICO, WebP
License
MIT