Provider Support Matrix
Chainsaw ships with ten providers. Each provider supports a different subset of the available manipulators. Unsupported manipulators throw UnsupportedManipulator at URL-generation time.
Providers at a glance
| Provider | Type | Pricing | URL style | Signing |
|---|---|---|---|---|
| Intervention | Local processing | OSS (free) | n/a (filesystem) | n/a |
| Imagine | Local processing | OSS (free) | n/a (filesystem) | n/a |
| Cloudflare | URL grammar | Freemium SaaS | /cdn-cgi/image/w=…,h=…/source |
Cloudflare-managed |
| Cloudinary | URL grammar | Freemium SaaS | /image/upload/w_…,h_…/source |
HMAC (s--…--) |
| ImageKit | URL grammar | Freemium SaaS | /source?tr=w-…,h-…,c-maintain_ratio |
HMAC-SHA1 (&ik-s=…) |
| imgix | URL grammar | Paid SaaS | /source?w=…&h=…&fit=crop |
MD5 (&s=…) |
| imgproxy | URL grammar | OSS (self-hosted) | /rs:…/w:…/h:…/plain/source |
HMAC (path signature) |
| Thumbor | URL grammar | OSS (self-hosted) | /hmac/WxH/filters:…/source |
HMAC (path prefix) |
| Imagor | URL grammar | OSS (self-hosted) | /fit-in/WxH/filters:…/source |
HMAC (path prefix) |
| wsrv.nl | URL grammar | Free (public) | ?url=…&w=…&h=…&output=webp |
n/a (public service) |
Portability baseline
If your presets stick to the manipulators below, switching providers is a configuration change — no provider will throw UnsupportedManipulator:
- Resize:
Width,Height,Cover(and itscrop()alias),ManualCrop,Contain,Pad,Stretch - Encoding: JPEG, PNG, WebP, AVIF,
Quality - Filters:
Blur,Sharpen,Greyscale - Decoration:
Background,Flip,Rotate(quarter-turns),AutoOrient
“Portable” here means the manipulator is accepted by every provider — not that output is pixel-identical. Numeric ranges and algorithms differ between backends (e.g. Blur radius, Quality scales, AVIF encoder settings), so expect visual differences when switching providers even within the baseline. Run visual regression if that matters to you.
Precision notes
Blur(0..100)— anchored to gaussian σ ≈ radius/2 px. Exact on imgproxy and wsrv.nl (their parameter is a libvips sigma) and on Intervention Imagick (blurImage(N, 0.5N)) / vips (gaussblur(0.53N)). Tolerance-band on the GD paths (measured σ within [0.7, 1.4]× target — planned kernel passes plus a downscale cycle for large sigmas) and on Imagine (±20%). Thumbor and Imagor get an explicit sigma argument; imgix (×2), Cloudflare (×0.4), Cloudinary (×50, itse_bluris exactly 100× a sigma, ceiling at σ 20) and ImageKit (×0.55; measured σ ≈ 0.91 ×bl) were all calibrated against measured output.Sharpen(0..100)— backend granularity varies widely. Cloudflare quantizes to 11 steps (0..10), imgproxy Pro to ~10 sigma steps, Thumbor to 11 amount steps. Expect visible banding at small mid-range deltas on these backends. imgix, Cloudinary, Imagor and wsrv.nl preserve the full 0…100 precision.Gamma(0.1..9.99)— Chainsaw uses a multiplier (1.0 = unchanged). Cloudinary and imgix natively take a percentage, so the providers translate viapct = round((gamma - 1) * 100)and clamp to each backend’s percentage window (Cloudinary-50..150, imgix-100..100). Large multipliers saturate rather than extrapolate.
Manipulation support
Legend: ✅ = supported, ❌ = not supported.
Resize & crop
| Manipulator | Bunny | Cloudflare | Cloudinary | Gumlet | Imagekit | Imagine | Imagor | Imgix | Imgproxy | Intervention | Thumbor | Wserv |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Width | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Height | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Scale | ❌ | ❌ | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ |
| Cover | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| ManualCrop | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Contain | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Pad | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Stretch | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| CropToRatio | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ❌ | ✅ | ❌ | ❌ |
| PadToRatio | ❌ | ❌ | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ |
| Trim | ❌ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
A ❌ for Scale, CropToRatio or PadToRatio marks a provider with no native ratio/factor primitive — not a hard limit. A pre-dispatch DimensionNormalizer still honours them there by re-expressing each as a concrete resize (Scale → width, CropToRatio → cover, PadToRatio → pad) when the source dimensions are known, so a MetadataResolver must be configured on the factory. Without resolved dimensions they throw UnsupportedManipulator rather than guess. See Resize & crop.
Filters & effects
| Manipulator | Bunny | Cloudflare | Cloudinary | Gumlet | Imagekit | Imagine | Imagor | Imgix | Imgproxy | Intervention | Thumbor | Wserv |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Blur | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Sharpen | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Brightness | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Contrast | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Gamma | ✅ | ✅ | ✅ | ❌ | ❌ | ✅ | ❌ | ✅ | ❌ | ✅ | ❌ | ✅ |
| Greyscale | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Sepia | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ | ✅ | ✅ | ✅ | ❌ | ✅ |
| Pixelate | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ |
| Saturation | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Hue | ✅ | ❌ | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ❌ | ✅ | ❌ | ✅ |
| Negate | ❌ | ❌ | ✅ | ✅ | ❌ | ✅ | ❌ | ✅ | ❌ | ✅ | ❌ | ✅ |
Decoration & orientation
| Manipulator | Bunny | Cloudflare | Cloudinary | Gumlet | Imagekit | Imagine | Imagor | Imgix | Imgproxy | Intervention | Thumbor | Wserv |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Background | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Border | ❌ | ❌ | ✅ | ❌ | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ❌ | ❌ |
| Flip | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Rotate | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| AutoOrient | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
Watermark sub-matrix
Source types:
| Source | Intervention | Imagine | Cloudflare | Cloudinary | ImageKit | imgix | imgproxy (free) | imgproxy (Pro) | Thumbor | Imagor | wsrv |
|---|---|---|---|---|---|---|---|---|---|---|---|
WatermarkFromSource |
✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ❌ |
WatermarkFromServerDefault |
❌ | ❌ | ❌ | ✅ | ❌ | ❌ | ✅¹ | ✅ | ❌ | ❌ | ❌ |
Size units ($sizeUnit):
| Unit | Intervention | Imagine | Cloudflare | Cloudinary | ImageKit | imgix | imgproxy (free) | imgproxy (Pro) | Thumbor | Imagor | wsrv |
|---|---|---|---|---|---|---|---|---|---|---|---|
Unit::Pixel |
✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅² | ✅² | ❌ |
Unit::Percent |
✅ | ✅ | ❌ | ✅ | ✅¹⁰ | ✅⁸ | ✅ | ✅ | ✅ | ✅ | ❌ |
Padding units ($paddingUnit):
| Unit | Intervention | Imagine | Cloudflare | Cloudinary | ImageKit | imgix | imgproxy (free) | imgproxy (Pro) | Thumbor | Imagor | wsrv |
|---|---|---|---|---|---|---|---|---|---|---|---|
Unit::Pixel |
✅ | ✅ | ❌ | ✅ | ✅ | ✅⁹ | ❌ | ✅ | ✅ | ✅ | ❌ |
Unit::Percent |
✅³ | ✅³ | ❌ | ✅⁴ | ✅¹⁰ | ❌⁵ | ✅⁶ | ✅⁶ | ✅⁷ | ✅⁷ | ❌ |
Tiling (tiledWatermark()):
| Feature | Intervention | Imagine | Cloudflare | Cloudinary | ImageKit | imgix | imgproxy (free) | imgproxy (Pro) | Thumbor | Imagor | wsrv |
|---|---|---|---|---|---|---|---|---|---|---|---|
| Tile (edge-to-edge) | ✅ | ✅ | ❌ | ✅¹¹ | ❌ | ✅¹² | ❌ | ❌ | ✅¹³ | ✅¹³ | ❌ |
| Inter-tile gap | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
¹ Only key === 'default' (matches the server’s IMGPROXY_WATERMARK_URL). Custom keys require Pro.
² Converts pixels → ratio using the output image width (set via upstream Width/Crop). Without upstream output dims, throws UnsupportedManipulator.
³ Computed locally from base image dims.
⁴ Native via decimal x_0.1 / y_0.05 per Cloudinary docs.
⁵ Imgix mark-x/mark-y/mark-pad are integer-only per docs; throws UnsupportedManipulator.
⁶ Native via relative offset (imgproxy treats |v| < 1 as percent of image dims).
⁷ Native via Xp notation (Thumbor / Imagor docs: “20p” = 20% of image width).
⁸ Converts percent → pixels using the output image dims (set via upstream Width/Crop/Contain). Imgix’s native mark-w=0..1 is a fraction of the watermark source, not the output image — without upstream output dims, throws UnsupportedManipulator.
⁹ Symmetric only (paddingX === paddingY); emits mark-pad. Imgix mark-x / mark-y are absolute pixel offsets that override mark-align, so asymmetric padding from the alignment edge has no clean imgix equivalent — throws UnsupportedManipulator.
¹⁰ Native via ImageKit arithmetic expressions (bw_mul_0.25 = 25% of base width) per ImageKit’s imgix migration guide.
¹¹ Cloudinary fl_tiled on the overlay layer. The inter-tile gap is local-only — a non-zero gap throws UnsupportedManipulator.
¹² Imgix mark-tile=true. Gap is local-only.
¹³ Thumbor / Imagor watermark(…,repeat,repeat,…). Gap is local-only.
Special
| Manipulator | Bunny | Cloudflare | Cloudinary | Gumlet | Imagekit | Imagine | Imagor | Imgix | Imgproxy | Intervention | Thumbor | Wserv |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| BlurHash | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ |
| ThumbHash | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ |
| Dither | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ |
Encoding
Every provider supports format conversion and quality control through the Encoding value object.
| Capability | Intervention | Imagine | Cloudflare | Cloudinary | ImageKit | imgix | imgproxy | Thumbor | Imagor | wsrv |
|---|---|---|---|---|---|---|---|---|---|---|
| JPEG | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| PNG | ✅ | ✅ | ❌¹ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| WebP | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| AVIF | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| GIF | ✅ | ✅ | ❌¹ | ✅ | ❌² | ✅ | ✅ | ✅ | ✅ | ✅ |
| Auto format | ✅³ | ✅³ | ✅ | ✅ | ✅ | ✅ | ❌⁴ | ❌⁴ | ❌⁴ | ❌⁴ |
| Quality | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
¹ Cloudflare’s URL API has no format=png|gif value — an explicit PNG/GIF request is silently ignored and the source format passes through.
² ImageKit does not output GIF — an explicit request throws UnsupportedOperation.
³ Origin-side, resolved by an AutoFormatStrategyInterface you inject — see Format Negotiation. The CDN columns negotiate edge-side (format=auto / f_auto / f-auto / auto=format).
⁴ Format::Auto is silently ignored — set an explicit format or compose a <picture> with typed sources.