You have forty product photos from a client, a folder of screenshots for documentation, or a batch of blog images that all need to be capped at some maximum width before upload. Setting up Sharp, Pillow, or ImageMagick with a Node or Python script for a one-off job is overkill — here's what actually gets the job done without a build step.
Why "Just Write a Script" Is Often the Wrong First Move
Installing an image library for a one-time batch job means dependency resolution, potentially native bindings (Sharp depends on libvips, Pillow needs system image libraries), and a script you'll maintain zero times after today. For a recurring pipeline — say, every image uploaded through your CMS needs resizing — a proper build step with Sharp is the right call. For "I have this folder right now," it's the wrong tool for the job size.
Two paths avoid the setup cost entirely: OS-native CLI tools that already ship on your machine, and browser-based tools that process images client-side using the Canvas API, meaning nothing uploads to a server and nothing to install.
Native CLI Tools You Already Have
macOS: sips
sips (Scriptable Image Processing System) ships with every Mac and handles resizing, format conversion, and basic rotation without any install:
# Resize every JPG in the current folder to max 1200px on the longest side
sips -Z 1200 *.jpg
# Resize into a new folder instead of overwriting originals
mkdir resized
for f in *.jpg; do sips -Z 1200 "$f" --out "resized/$f"; done
The -Z flag constrains the longest edge to the given value while preserving aspect ratio — exactly the behavior you want for responsive web images.
Windows: PowerShell with System.Drawing
Windows has no sips equivalent, but PowerShell can drive .NET's imaging classes directly without installing anything beyond what ships with Windows:
Add-Type -AssemblyName System.Drawing
Get-ChildItem *.jpg | ForEach-Object {
$img = [System.Drawing.Image]::FromFile($_.FullName)
$ratio = 1200 / [Math]::Max($img.Width, $img.Height)
$newW = [int]($img.Width * $ratio)
$newH = [int]($img.Height * $ratio)
$bmp = New-Object System.Drawing.Bitmap($img, $newW, $newH)
$bmp.Save("resized_$($_.Name)")
$img.Dispose(); $bmp.Dispose()
}
It's more code than sips, but it's zero-install and handles a folder of images in one pass.
Cross-platform: ImageMagick (if already installed)
If ImageMagick is already on your system — common on Linux dev boxes and CI images — mogrify batch-processes in place:
mogrify -resize 1200x1200 *.png
Don't install ImageMagick solely for a one-off batch job on a machine that doesn't have it; the CLI tools above or a browser tool get you there faster for a single task.
Browser-Based Batch Resizing
For a mixed-format folder, or when you're not on a machine where you can run scripts (a locked-down work laptop, someone else's machine), a browser tool that resizes client-side is the path of least resistance. The Image Resizer processes files locally in the browser — images never leave your machine — so you get the batch-resize outcome without touching a terminal or installing a dependency.
This matters more than it sounds for two practical reasons: no upload means no wait for large files over a slow connection, and no server-side processing means no privacy concern for images you can't legally or contractually upload to a third-party service (client deliverables under NDA, internal screenshots, unreleased product photography).
Resize, Then Compress — Don't Conflate the Two Steps
Resizing changes pixel dimensions; compression changes how efficiently those pixels are encoded. Running only one of the two leaves size on the table. A 4000px photo resized to 1200px width will drop substantially in file size as a side effect of having fewer pixels, but it still benefits from a subsequent compression pass tuned for its new dimensions — an oversized image compressed hard looks worse and stays bigger than a correctly-sized image compressed reasonably.
The order matters: resize first, compress second. Compressing a 4000px image and then downscaling it wastes compute on pixels you're about to throw away, and in some pipelines re-encodes introduce a second round of quantization loss on the JPEG. Get dimensions right, then run the result through the JPG Compressor for the final pass.
Don't Let Batch Resizing Silently Distort Images
The most common batch-resize mistake isn't a missing dependency, it's a distorted aspect ratio. A script or command that forces both width and height to fixed values — rather than constraining one dimension and letting the other scale proportionally — will stretch or squash every image whose original aspect ratio doesn't match the target exactly. sips -Z and mogrify -resize WxH (without a modifier) both preserve aspect ratio by default, fitting the image within the bounding box you specify, but it's easy to accidentally force exact dimensions:
# Preserves aspect ratio — fits within 1200x1200, doesn't distort
mogrify -resize 1200x1200 *.jpg
# Forces exact dimensions — WILL distort any image not already 1:1
mogrify -resize 1200x1200! *.jpg
That trailing ! in ImageMagick's syntax is easy to add by accident when copying a command from an old script. If a batch job produces oddly stretched thumbnails, check for a forced-dimension flag before assuming the tool is broken.
Preserving Orientation and Metadata
Photos from phones and cameras often carry EXIF orientation data rather than being physically rotated pixels — the file stores "rotate 90° on display" as metadata rather than baking the rotation into the pixel grid. Most modern resize tools read and respect this automatically, but a script built on a lower-level library can silently ignore it, producing a batch of correctly-sized but sideways images. sips respects EXIF orientation by default; if you're scripting against a raw imaging library instead of a CLI tool, confirm it auto-rotates based on EXIF before trusting a large batch run unattended. Since resized web images strip most EXIF anyway (deliberately, since EXIF can leak GPS coordinates and camera/device details), do the orientation check before the resize step, not after.
Picking a Target Size Without Guessing
A common mistake in batch jobs is applying one blanket dimension to every image regardless of where it's used. If your images serve multiple contexts — a thumbnail grid, a full-width hero, an email attachment — generate multiple sized variants rather than picking one compromise dimension that's too large for thumbnails and too small for heroes:
for size in 400 800 1200 1920; do
mkdir -p "resized-$size"
sips -Z $size *.jpg --out "resized-$size/"
done
This produces a proper responsive image set you can wire into srcset without a bundler-driven image pipeline.
Next Step
For a one-off folder, skip the dependency install entirely — either run the native CLI command for your OS above, or drop the files into the Image Resizer and get sized output in one pass. Reserve an actual Sharp or Pillow build step for pipelines that run repeatedly, not for a single afternoon's batch job.