All formats

ruby-vips

LanguageRuby
LicenseMIT (libvips: LGPL-2.1)
Repositorygithub.com/libvips/ruby-vips
Documentationruby-vips API, webpsave options
Depends onlibvips (8.x) built with libwebp, loaded through ffi
Read / writeyes / yes, lossy, lossless, alpha, animation, metadata

ruby-vips calls libvips, a fast image processing library that streams images in small pieces and uses little memory. libvips reads and writes WebP with libwebp, including animated files. It’s what Rails Active Storage and the image_processing gem use by default to make WebP variants.

Installation#

Install libvips first, then the gem:

Shell
# Debian / Ubuntu
sudo apt install libvips42
# macOS
brew install vips

gem install ruby-vips

Basic usage: convert to WebP#

Ruby
require "vips"

image = Vips::Image.new_from_file("photo.jpg")
puts "#{image.width} x #{image.height}, #{image.bands} bands"

image.webpsave("photo.webp", Q: 80, effort: 6)  # Q 1-100, effort 0 (fast) .. 6 (smallest)

# or let the extension choose the saver, options after "[":
image.write_to_file("photo.webp[Q=80]")

Reading a WebP is the same Vips::Image.new_from_file("in.webp").

How to save a lossless WebP#

Ruby
image = Vips::Image.new_from_file("logo.png")
image.webpsave("logo.webp", lossless: true)
# near-lossless: smaller files, almost no visible change
image.webpsave("logo-nl.webp", near_lossless: true, Q: 60)

How to make a WebP thumbnail#

thumbnail decodes at reduced size where possible and is the fastest way to resize:

Ruby
thumb = Vips::Image.thumbnail("photo.jpg", 400)  # 400 px wide, keeps aspect ratio
thumb.webpsave("thumb.webp", Q: 75, smart_subsample: true)

How to read the frames of an animated WebP#

Load all pages with n: -1; frames are stacked vertically in one tall image:

Ruby
anim = Vips::Image.new_from_file("anim.webp", n: -1)
frame_height = anim.get("page-height")
frames = anim.height / frame_height
puts "#{frames} frames, delays #{anim.get("delay").inspect} ms, loop #{anim.get("loop")}"

frames.times do |i|
  anim.crop(0, i * frame_height, anim.width, frame_height).write_to_file("frame#{i}.png")
end

Saving that tall image with webpsave writes an animated WebP again; resize it with Vips::Image.thumbnail("anim.gif[n=-1]", 200) to convert GIF to animated WebP.

How to get WebP dimensions without decoding#

libvips opens files lazily, so new_from_file reads only the header until you access pixels:

Ruby
image = Vips::Image.new_from_file("in.webp")
puts image.width, image.height, image.has_alpha?

Tips#