Resize Modes

“Resize” sounds like one operation. It’s actually a family of about two dozen distinct operations, each producing a different result for the same input. This page is a vendor-neutral catalogue of every meaningful way to resize an image, organised by intent — no library names, no provider tokens, no API. It’s the conceptual map; for the Chainsaw methods that implement these, see Resize & Fit.

Each operation includes two reference tables — how the major image CDNs and the major PHP image libraries (including Chainsaw) name the same operation. ❌ means the operation is not natively supported by that backend; 🟡 means partial / emulated support.

At a glance

The four ops below all take the same source (a 4 × 3 landscape) and the same target box (a 1 × 1 square, dotted outline) — and produce four visibly different results. This is the cluster users get most confused about.

Source 140 × 105 (4 : 3) Fit inside 100 × 75 Stretch 100 × 100 (distorted) Pad to fit 100 × 100 (letterboxed) Cover 100 × 100 (cropped)

Family 1 — Pure scaling

Multiply the source uniformly. No reframing, no cropping, no padding — just resample.

Scale by factor

× 0.6

Multiply both dimensions by the same number. A factor of 0.5 halves the image; 2 doubles it.

Output: source × factor · Aspect: preserved · Upscale: allowed when factor > 1

CDN API
Cloudflare
Cloudinary w_0.5 (fractional value)
Imagor
imgix
imgproxy
Intervention
Thumbor
wsrv.nl
PHP library API
Chainsaw scale(0.5)
Glide
Imagine resize($size->scale(0.5))
JoliCode resize: { width: 50%, height: 50% }
Liip scale: { to: 0.5 }
Spatie
TYPO3 MP

Scale to a target width

w = N

Pick a width; the height follows from the source’s aspect ratio.

Output: width = N, height = source-derived · Aspect: preserved · Upscale: allowed

CDN API
Cloudflare width=N
Cloudinary w_N
Imagor Nx0
imgix w=N
imgproxy w:N
Intervention scale(width: N)
Thumbor Nx0
wsrv.nl w=N
PHP library API
Chainsaw width(N)
Glide w=N
Imagine resize($size->widen(N))
JoliCode widen: { width: N }
Liip relative_resize: { widen: N }
Spatie width(N)
TYPO3 MP setWidth(N)

Scale to a target height

h = N

The mirror of the above, with height as the controlling dimension.

Output: height = N, width = source-derived · Aspect: preserved · Upscale: allowed

CDN API
Cloudflare height=N
Cloudinary h_N
Imagor 0xN
imgix h=N
imgproxy h:N
Intervention scale(height: N)
Thumbor 0xN
wsrv.nl h=N
PHP library API
Chainsaw height(N)
Glide h=N
Imagine resize($size->heighten(N))
JoliCode heighten: { height: N }
Liip relative_resize: { heighten: N }
Spatie height(N)
TYPO3 MP setHeight(N)

Family 2 — Fit inside a target box

Place the source inside a target rectangle, preserving aspect ratio, with the output never exceeding the box on at least one axis. The output size depends on the source’s aspect.

Fit inside

Shrink the source until it fits entirely within the box. The output’s aspect matches the source, so it may be smaller than the box on one axis.

Output: ≤ box on each axis · Aspect: preserved · Upscale: allowed

CDN API
Cloudflare fit=contain
Cloudinary c_fit
Imagor fit-in/WxH + filters:upscale()
imgix fit=clip
imgproxy rs:fit:W:H + el:1
Intervention scale(W, H)
Thumbor fit-in/WxH + filters:upscale()
wsrv.nl fit=inside
PHP library API
Chainsaw contain(W, H)
Glide w=W&h=H&fit=contain
Imagine thumbnail(new Box(W, H), THUMBNAIL_INSET | THUMBNAIL_FLAG_UPSCALE)
JoliCode resize: { width: W, height: H, mode: inside }
Liip thumbnail: { size: [W, H], mode: inset, allow_upscale: true }
Spatie fit(Fit::Contain, W, H)
TYPO3 MP setWidth("Wm") + setHeight("Hm") (suffix m)

Fit inside, no upscale

small source

Same as fit inside, but never enlarges. If the source is already smaller than the box, the output equals the source.

Output: ≤ box and ≤ source on each axis · Aspect: preserved · Upscale: forbidden

CDN API
Cloudflare fit=scale-down
Cloudinary c_limit
Imagor fit-in/WxH + filters:no_upscale()
imgix fit=max
imgproxy rs:fit:W:H (default el:0)
Intervention scaleDown(W, H)
Thumbor fit-in/WxH + filters:no_upscale()
wsrv.nl fit=inside&we
PHP library API
Chainsaw contain(W, H, noUpscale: true)
Glide w=W&h=H&fit=max
Imagine thumbnail(new Box(W, H), THUMBNAIL_INSET)
JoliCode resize: { width: W, height: H, mode: inside, allow_upscale: false }
Liip downscale: { max: [W, H] }
Spatie fit(Fit::Max, W, H)
TYPO3 MP

Fit inside, only upscale

small source

The opposite cap: never shrinks. The output is at least as large as the box on at least one axis, growing the source if necessary.

Output: ≥ box on at least one axis · Aspect: preserved · Upscale: required when source < box

CDN API
Cloudflare
Cloudinary c_mfit / c_mpad
Imagor
imgix
imgproxy
Intervention
Thumbor
wsrv.nl fit=outside
PHP library API
Chainsaw ❌ (deferred)
Glide
Imagine
JoliCode resize: { width: W, height: H, mode: inside, allow_downscale: false }
Liip upscale: { min: [W, H] }
Spatie
TYPO3 MP

Family 3 — Fill a target box exactly

Force the output dimensions to match the target box exactly. The five ops below differ in how they reconcile the source’s aspect with the box.

Stretch

Force the output to match the box exactly, distorting the source. Aspect ratio is discarded — circles become ovals.

Output: = box · Aspect: dropped · Upscale: allowed

CDN API
Cloudflare fit=squeeze
Cloudinary c_scale
Imagor stretch/WxH
imgix fit=scale
imgproxy rs:force:W:H
Intervention resize(W, H)
Thumbor WxH + filters:stretch()
wsrv.nl fit=fill
PHP library API
Chainsaw stretch(W, H)
Glide w=W&h=H&fit=stretch
Imagine resize(new Box(W, H))
JoliCode resize: { width: W, height: H, mode: exact }
Liip resize: { size: [W, H] }
Spatie fit(Fit::Stretch, W, H)
TYPO3 MP setWidth(W) + setHeight(H) (no suffix)

Pad to fit

Shrink the source to fit inside the box (preserving aspect), then fill the empty area on the short axis with a background colour. The output is partly source pixels, partly fill.

Output: = box · Aspect: preserved · Upscale: allowed

CDN API
Cloudflare fit=pad
Cloudinary c_pad
Imagor fit-in/WxH + filters:fill(RGB)
imgix fit=fill&fill=solid&fill-color=fff
imgproxy rs:fit:W:H/ex:1/bg:RGB
Intervention contain(W, H, bg)
Thumbor fit-in/WxH + filters:fill(RGB)
wsrv.nl fit=contain&cbg=fff
PHP library API
Chainsaw pad(W, H, '#fff')
Glide w=W&h=H&fit=fill-max&bg=fff
Imagine composed: Canvas + thumbnail(INSET)
JoliCode resize: mode: inside then expand: { background_color: '#fff' }
Liip thumbnail: mode: inset then background: { color: '#fff' }
Spatie fit(Fit::FillMax, W, H, backgroundColor: '#fff')
TYPO3 MP

Pad to fit, no upscale

small source

Same as pad to fit, but capped at source dims. Sources smaller than the box are padded around at native size rather than enlarged first.

Output: = box · Aspect: preserved · Upscale: forbidden

CDN API
Cloudflare
Cloudinary c_lpad
Imagor fit-in/WxH + filters:fill(RGB) + filters:no_upscale()
imgix fit=fillmax
imgproxy rs:fit:W:H/ex:1/el:0/bg:RGB
Intervention containDown(W, H, bg)
Thumbor fit-in/WxH + filters:fill(RGB) + filters:no_upscale()
wsrv.nl 🟡 (fit=contain&we&cbg=fff)
PHP library API
Chainsaw pad(W, H, '#fff', noUpscale: true)
Glide w=W&h=H&fit=fill&bg=fff
Imagine composed: Canvas + thumbnail(INSET)
JoliCode expand: { background_color: '#fff' } (no-ops if target < source)
Liip downscale + background (composed)
Spatie fit(Fit::Fill, W, H, backgroundColor: '#fff')
TYPO3 MP

Cover

Scale the source so it covers the entire box, preserving aspect, then crop the overflow on the long axis. The output is fully composed of source pixels — no fill colour.

Where the overflow is cropped from is configurable — the same anchor vocabulary as Family 4 applies: centre (default), one of eight compass positions (top, bottom, left, right, top-left, …), a focal point, or smart / saliency / face detection.

Output: = box · Aspect: preserved · Upscale: allowed · Anchor: centre / compass / focal / smart

CDN API
Cloudflare fit=cover
Cloudinary c_fill
Imagor WxH + halign/valign
imgix fit=crop
imgproxy rs:fill:W:H
Intervention cover(W, H)
Thumbor WxH + halign/valign
wsrv.nl fit=cover
PHP library API
Chainsaw cover(W, H) (or crop(W, H) alias)
Glide w=W&h=H&fit=crop
Imagine thumbnail(new Box(W, H), THUMBNAIL_OUTBOUND | THUMBNAIL_FLAG_UPSCALE)
JoliCode thumbnail: { width: W, height: H }
Liip thumbnail: { size: [W, H], mode: outbound, allow_upscale: true }
Spatie fit(Fit::Crop, W, H)
TYPO3 MP setWidth("Wc") + setHeight("Hc") (suffix c)

Cover, no upscale

small source

Same as cover, but never enlarges. If the source is smaller than the box, the output stays at source dims (no upscale, no padding). When cover does apply, the overflow anchor is the same as Cover above.

Output: ≤ box, ≤ source · Aspect: preserved · Upscale: forbidden · Anchor: centre / compass / focal / smart

CDN API
Cloudflare fit=crop
Cloudinary c_lfill
Imagor 🟡 (composed)
imgix fit=min
imgproxy rs:fill-down:W:H
Intervention coverDown(W, H)
Thumbor 🟡 (composed)
wsrv.nl 🟡 (fit=cover&we)
PHP library API
Chainsaw cover(W, H, noUpscale: true)
Glide
Imagine thumbnail(new Box(W, H), THUMBNAIL_OUTBOUND)
JoliCode thumbnail: { width: W, height: H, allow_upscale: false }
Liip thumbnail: { size: [W, H], mode: outbound }
Spatie
TYPO3 MP

Family 4 — Region selection

Cut a rectangle out of the source. The defining choice is where the rectangle sits.

Centre crop to dimensions

Cut a rectangle of the requested size from the centre of the source. Pixels outside the rectangle are discarded.

CDN API
Cloudflare fit=cover (no gravity = centre)
Cloudinary c_fill,g_center
Imagor WxH (default centre)
imgix fit=crop&crop=center
imgproxy rs:fill:W:H&g:ce
Intervention cover(W, H, "center")
Thumbor WxH/center/middle
wsrv.nl fit=cover&a=center
PHP library API
Chainsaw cover(W, H) (default centre)
Glide w=W&h=H&fit=crop (default centre)
Imagine crop(new Point($x, $y), new Box(W, H)) (caller computes centre offset)
JoliCode thumbnail: { width: W, height: H, crop_position: center }
Liip fixed: { width: W, height: H }
Spatie crop(W, H, CropPosition::Center)
TYPO3 MP

Compass-anchored crop

anchor: top-left

Same as centre crop, but anchored to one of eight compass directions instead of the centre: top-left, top, top-right, left, right, bottom-left, bottom, bottom-right. The anchor decides which edge survives and which is cropped away.

CDN API
Cloudflare gravity=top (no corner keywords — corners map to coordinates: gravity=0x0)
Cloudinary g_north_west (compass set)
Imagor halign + valign segments
imgix crop=top,left
imgproxy g:nowe (compass set: no/so/ea/we/noea/nowe/...)
Intervention cover(W, H, "top-left")
Thumbor halign + valign segments
wsrv.nl a=top-left
PHP library API
Chainsaw cover(W, H, CropPosition::TopLeft)
Glide fit=crop-top-left
Imagine ❌ (caller computes Point manually; only Center enum)
JoliCode crop_position: start | end (1-axis only — no NW/NE/SW/SE)
Liip ❌ (compass on background/watermark only, not crop)
Spatie crop(W, H, CropPosition::TopLeft)
TYPO3 MP 🟡 (only imgproxy/optimole builders; not wired)

Manual pixel rectangle

x, y, w, h

Specify the crop region in absolute pixel coordinates: a starting point (x, y) and a size (w, h). Useful when the coordinates come from upstream — an image editor, a database, a CMS field.

CDN API
Cloudflare ❌ (trim is edge-trim, not arbitrary rect)
Cloudinary c_crop,w_W,h_H,x_X,y_Y
Imagor AxB:CxD (start point + end point)
imgix rect=X,Y,W,H
imgproxy c:W:H:nowe:X:Y
Intervention crop(W, H, X, Y)
Thumbor AxB:CxD
wsrv.nl cx=X&cy=Y&cw=W&ch=H
PHP library API
Chainsaw manualCrop(W, H, X, Y)
Glide crop=W,H,X,Y
Imagine crop(new Point(X, Y), new Box(W, H))
JoliCode crop: { start_x: X, start_y: Y, width: W, height: H }
Liip crop: { start: [X, Y], size: [W, H] }
Spatie manualCrop(W, H, X, Y)
TYPO3 MP setCrop(W, H, X, Y)

Focal-point crop

focal: 0.65, 0.45

Like a compass crop, but the anchor is an arbitrary point given as normalised coordinates: (0.0, 0.0) is top-left, (1.0, 1.0) is bottom-right. The crop window is positioned so the focal point stays inside it (and ideally near its centre).

CDN API
Cloudflare gravity=fxXfy (decimal × ratio)
Cloudinary ❌ (g_xy_center centres the crop on the point — different operation)
Imagor focal(fx,fy) filter
imgix crop=focalpoint&fp-x=fx&fp-y=fy
imgproxy g:fp:fx:fy
Intervention ❌ (compute manual rect)
Thumbor ❌ (focal() filter accepts a rect, not a point)
wsrv.nl a=focal&fpx=fx&fpy=fy
PHP library API
Chainsaw cover(W, H, Anchor::focal(x, y))
Glide fit=crop-X-Y (percent ints, e.g. crop-25-75)
Imagine
JoliCode crop: { start_x: '50%', start_y: '50%', ... } (percent strings)
Liip
Spatie focalCropAndResize(W, H, fxPct, fyPct)
TYPO3 MP

Focal-point crop with zoom

zoom: 1.5×

Same as focal-point crop, plus a magnification factor around the point. Useful for portrait thumbnails that should “zoom in” on a face rather than show the whole shoulders-up region.

CDN API
Cloudflare 🟡 (gravity=face + zoom only)
Cloudinary
Imagor
imgix fp-z=N
imgproxy
Intervention
Thumbor
wsrv.nl
PHP library API
Chainsaw cover(W, H, Anchor::focal(x, y, zoom))
Glide fit=crop-X-Y-Z (percent + zoom int, e.g. crop-25-75-2)
Imagine
JoliCode
Liip
Spatie
TYPO3 MP

Smart / saliency crop

smart

The image service analyses the source and picks the most “interesting” rectangle of the requested size — typically by visual saliency, contrast, or edge detection. Often called auto or smart by image services. The result is content-aware: a portrait gets cropped around the face, a landscape around the dominant feature.

CDN API
Cloudflare gravity=auto
Cloudinary g_auto
Imagor smart segment
imgix crop=edges (or crop=entropy)
imgproxy g:sm
Intervention
Thumbor smart segment
wsrv.nl a=attention (or a=entropy)
PHP library API
Chainsaw cover(W, H, Anchor::smart())
Glide
Imagine
JoliCode
Liip
Spatie
TYPO3 MP ❌ (constants exist but not wired)

Entropy crop

entropy

A specific kind of smart crop: pick the rectangle with the highest Shannon entropy (busiest area). Tends to favour textured regions over flat ones.

CDN API
Cloudflare 🟡 (under gravity=auto)
Cloudinary 🟡 (under g_auto:classic)
Imagor 🟡 (under smart)
imgix crop=entropy
imgproxy 🟡 (under g:sm)
Intervention
Thumbor 🟡 (under smart)
wsrv.nl a=entropy
PHP library API
Chainsaw cover(W, H, Anchor::entropy())
Glide
Imagine
JoliCode
Liip
Spatie
TYPO3 MP

Attention crop

attention

Another smart variant, based on a visual-attention model rather than entropy. Often gives a different result than entropy on the same image — both are “smart”, but they answer “interesting where?” differently.

CDN API
Cloudflare 🟡 (under gravity=auto)
Cloudinary 🟡 (under g_auto:subject)
Imagor 🟡 (under smart)
imgix
imgproxy 🟡 (under g:sm)
Intervention
Thumbor 🟡 (under smart)
wsrv.nl a=attention
PHP library API
Chainsaw cover(W, H, Anchor::attention())
Glide
Imagine
JoliCode
Liip
Spatie
TYPO3 MP

Face-detection crop

face

A smart crop biased toward detected faces. If multiple faces are detected, the crop is positioned to include them all (or the largest, depending on the service).

CDN API
Cloudflare gravity=face
Cloudinary g_face (or g_faces)
Imagor 🟡 (under smart)
imgix crop=faces (or fit=facearea)
imgproxy obj:face (Pro)
Intervention
Thumbor 🟡 (under smart)
wsrv.nl
PHP library API
Chainsaw cover(W, H, Anchor::face())
Glide
Imagine
JoliCode
Liip
Spatie
TYPO3 MP

Object-class detection crop

class: hill

A smart crop biased toward a named class — dog, car, food. The service runs object detection and centres the crop on the matching region. Limited to services with an object detector and a published class vocabulary.

CDN API
Cloudflare
Cloudinary g_auto:<object> (e.g. g_auto:dog)
Imagor
imgix
imgproxy obj:<class> / objw: (Pro)
Intervention
Thumbor
wsrv.nl
PHP library API
Chainsaw cover(W, H, Anchor::object('dog'))
Glide
Imagine
JoliCode
Liip
Spatie
TYPO3 MP

Family 5 — Aspect-ratio targeting

Specify the output’s aspect ratio without specifying absolute dimensions. The dimensions follow from the source size and the chosen ratio.

Crop to aspect ratio

target: 16 : 9

Specify a target aspect (e.g. 16 : 9). The largest rectangle of that ratio that fits inside the source is cut, anchored at the centre (or another anchor).

CDN API
Cloudflare
Cloudinary ar_16:9
Imagor
imgix ar=16:9
imgproxy ❌ (no ratio-only primitive — exar requires w/h)
Intervention
Thumbor
wsrv.nl
PHP library API
Chainsaw cropToRatio('16:9')
Glide
Imagine
JoliCode
Liip
Spatie
TYPO3 MP

Pad to aspect ratio

target: 16 : 9

The mirror operation: extend the canvas so its aspect ratio matches the target, padding the new space with a background colour. The source is preserved at its original size; only the surrounding canvas grows.

CDN API
Cloudflare
Cloudinary ar_16:9,c_pad
Imagor
imgix ar=16:9&fit=fill&fill=solid
imgproxy ❌ (no ratio-only primitive — exar requires w/h)
Intervention
Thumbor
wsrv.nl
PHP library API
Chainsaw padToRatio('16:9', '#fff')
Glide
Imagine
JoliCode
Liip
Spatie
TYPO3 MP

Family 6 — Content-detected

The output dimensions aren’t specified by the caller — they’re decided by the image’s own content.

Auto-trim uniform borders

Detect and remove pixels around the edges that match a reference colour (typically the corner colour, or a user-supplied colour, with a tolerance). The output dimensions are derived from where the trim algorithm decides the content begins.

CDN API
Cloudflare trim=border&trim.tolerance=N
Cloudinary e_trim:N:col
Imagor trim:[anchor][:tol]
imgix trim=color&trim-tol=N&trim-color=fff
imgproxy t:thr:col
Intervention trim($tol)
Thumbor trim:[anchor][:tol]
wsrv.nl trim=N&tbg=fff
PHP library API
Chainsaw trim(N)
Glide
Imagine
JoliCode
Liip
Spatie
TYPO3 MP

Modifiers and ordering

Two cross-cutting concerns apply on top of the catalogue above:

  • Pre-crop then resize — apply an explicit pixel rectangle (Family 4) before the main resize. Useful when the source has known padding or banner areas that should never appear in any output.
  • Resize then post-crop — chain the operations the other way. Less common, but sometimes intentional, especially when the resize is a scale-by-factor and the crop selects a region of the resized result.

These aren’t separate operations — they’re sequencing choices when chaining multiple operations.


For the Chainsaw API that implements many of these operations, see Resize & Fit. For the per-provider availability matrix, see Provider Support.