A high performance, concurrent command-line image processing utility written in Go. GoFormat is designed for bulk asset conversion, resizing and standardisation.
- Format Conversion: Convert between nine image formats, including AVIF, ICO and iPhone HEIC photos.
- Batch Processing: Process entire directories concurrently using multi core worker pools, with a progress bar and a summary report.
- Image Resizing: Scale images up or down to specific dimensions, stretching, fitting inside or cropping to fill them.
- Compression Control: Adjust the output quality of JPEG, WebP and AVIF to optimise file sizes, or write lossless WebP.
- Pixel Art Support: Upscale pixel art and low-resolution graphics using nearest-neighbour scaling to preserve edges without blurring.
- Animated GIFs: Converting GIF to GIF keeps every frame, frame delays and the loop count, including when resizing.
- Transparency: Transparent areas are preserved when converting to GIF. JPEG and BMP cannot store transparency, so transparent areas become white.
- Metadata: EXIF data and colour profiles are carried over between JPEG, PNG and WebP, and converted files keep the date of the original.
- Photo Orientation: Photos stored sideways with an EXIF orientation tag, as most phone cameras do, come out the right way up.
| Format | Extensions | Read | Write |
|---|---|---|---|
| JPEG | .jpeg, .jpg |
Yes | Yes |
| PNG | .png |
Yes | Yes |
| WebP | .webp |
Yes | Yes |
| TIFF | .tiff, .tif |
Yes | Yes |
| BMP | .bmp |
Yes | Yes |
| GIF | .gif |
Yes | Yes |
| ICO | .ico |
Yes | Yes |
| AVIF | .avif |
Yes | Yes |
| HEIC | .heic, .heif |
Yes | No |
Download the binary for your platform from the latest release:
| Platform | File |
|---|---|
| Windows (64-bit) | goformat-windows-amd64.exe |
| Linux (64-bit) | goformat-linux-amd64 |
| Linux (ARM 64-bit) | goformat-linux-arm64 |
| macOS (Apple Silicon) | goformat-macos-arm64 |
| macOS (Intel) | goformat-macos-amd64 |
Each release includes a checksums.txt for verifying downloads. On Linux and macOS, make the file executable first:
chmod +x goformat-linux-amd64Requires Go 1.27 or newer.
go install github.com/Furkan5E/GoFormat/v2@latestThis installs the tool as GoFormat in your Go bin directory.
Requires Go 1.27 or newer.
git clone https://github.com/Furkan5E/GoFormat.git
cd GoFormatCompile the tool into an executable:
# Windows
go build -ldflags="-s -w" -o goformat.exe .
# Linux / macOS
go build -ldflags="-s -w" -o goformat .Released binaries and go install report the release they came from, and a binary built from source reports a development version based on its commit:
.\goformat.exe -versionOr run it without compiling by replacing .\goformat.exe with go run . in the examples below.
The examples use Windows syntax. On Linux and macOS, use ./goformat instead of .\goformat.exe.
Convert a single image:
.\goformat.exe -i source.jpg -o final_images -f pngBatch process a directory, including subdirectories:
.\goformat.exe -i pictures -r -f webp -q 80 -width 1920Upscale 2D assets:
.\goformat.exe -i sprites -o assets -f png -width 1024 -pixelWebP output is lossy by default, which suits photos. Add -lossless for pixel art, screenshots and anything else with hard edges:
.\goformat.exe -i sprites -o assets -f webp -losslessSet only -width or only -height to keep the original aspect ratio. Setting both resizes to exactly that size, which stretches the image if the proportions differ:
.\goformat.exe -i pictures -f tiff -width 1920 -height 1080Use -fit to keep the proportions when both are set:
-fit |
Result |
|---|---|
stretch |
Exactly -width by -height, distorted if the proportions differ (default) |
contain |
The whole image, as large as fits inside -width by -height, so one side may come out smaller |
cover |
Exactly -width by -height, filled by the middle of the image with the overflowing edges cropped off |
For example, square thumbnails cropped from the centre of each photo:
.\goformat.exe -i pictures -o thumbnails -f jpeg -width 300 -height 300 -fit cover- The output directory mirrors the input folder structure, and folders without images are not copied.
- If the output directory is inside the input directory, it is skipped so earlier results are not converted again.
- Files in the same folder that share a name (
a.pnganda.jpg) would produce the same output file, so each keeps its original extension in the output name (a_png.webpanda_jpg.webp). - A progress bar with an estimated time remaining is shown in the terminal. When output is redirected to a file or another program, one line per file is printed instead.
- Memory use is kept in check: very large images are converted a few at a time rather than one per worker, and an image over
-max-megapixels(250 by default, about 1 GB once decoded) is refused before it is loaded. Set it to0to remove the limit. - The final report lists skipped unsupported files, failed files and any warnings.
Add -dry-run to see what a command would do before running it for real. It lists every file with the output it would produce, marks the ones that would replace or skip an existing file, and writes nothing, not even the output directory:
.\goformat.exe -i pictures -r -f webp -dry-run-vprints a line for every file with its size in pixels and bytes before and after, and how long it took. Failures are shown as they happen as well as in the final report.-quietprints nothing unless something goes wrong. Errors and warnings are still written to stderr, and the exit code still says whether the run succeeded.
For scripts, -json prints a report of the run as JSON instead of the usual output, so it can be saved or passed to another program:
.\goformat.exe -i pictures -r -f webp -json > report.jsonThe report has a summary with the number of files converted, replaced, skipped, failed and unsupported, and a files list in the order the files were found. Each file has its input and output path, a status (converted, skipped or failed; would_convert or would_skip on a dry run), the error if it failed, any warnings, and its size in pixels and bytes before and after. It works for a single file too, and the exit code is the same as without it.
- A file already in the output directory with the same name is replaced. The output says when that happened, and the batch report counts the replaced files.
- With
-skip-existingthose files are left untouched instead. This also lets an interrupted batch carry on where it stopped, as only the missing files are converted:
.\goformat.exe -i pictures -r -f webp -skip-existing- A file is never converted onto itself. Pointing
-oat the input's own directory without changing the format fails for that file instead of destroying the original.
- EXIF and colour profiles: when converting between JPEG, PNG and WebP, the EXIF data (camera, lens, exposure, date, GPS position) and the ICC colour profile are copied into the output. The profile is what keeps colours right for photos taken in a wider colour space such as Display P3.
- The copied EXIF data is corrected for the output: the orientation is set to normal because the picture is saved upright, and the embedded thumbnail is dropped because it shows the picture before it was turned or resized.
- TIFF, BMP, GIF, ICO and AVIF output is written without metadata, and nothing is read from TIFF, HEIC or AVIF input apart from the orientation and date.
- Removing it:
-stripwrites nothing but the picture, leaving out the EXIF data, the colour profile and the original date. Use it before sharing photos you do not want to reveal a location or camera for. Photos taken in a wide colour space can look slightly less vivid without their profile.
.\goformat.exe -i pictures -o shared -f jpeg -strip- Dates: the converted file's modified time is set to when the picture was taken, read from its EXIF data. Images without that information keep the modified time of the source file, so sorting by date still works after converting.
Converting an animated GIF to GIF keeps all frames. Converting it to any other format keeps only the first frame, as those formats do not support animation.
Resizing a GIF with -pixel scales each frame's pixels as they are, so every colour is kept exactly. Without -pixel the smooth scaling blends neighbouring pixels into new colours, which have to be matched to a 256 colour palette again. The source's own colours are used when they fit in one palette, otherwise each frame gets a palette fitted to it. The same fitting is used when converting a photo or other colourful image to GIF.
| Flag | Description | Default |
|---|---|---|
-i |
Path to the input image or directory | Required |
-o |
Path to the output directory | output |
-f |
Target format (jpeg, png, webp, tiff, bmp, gif, ico, avif) |
jpeg |
-q |
Compression quality for JPEG, WebP and AVIF (1 to 100) | 85 |
-lossless |
Write lossless WebP, keeping every pixel exactly (-q is ignored) |
false |
-width |
Target width in pixels (0 to keep original) | 0 |
-height |
Target height in pixels (0 to keep original) | 0 |
-fit |
How an image is fitted when both -width and -height are set (stretch, contain, cover) |
stretch |
-pixel |
Use nearest neighbour scaling to preserve pixel edges | false |
-r |
Process subdirectories recursively | false |
-strip |
Leave out EXIF data, the colour profile and the original date | false |
-skip-existing |
Leave files that already exist in the output directory untouched | false |
-dry-run |
Show what would be converted without writing anything | false |
-max-megapixels |
Largest input image accepted, in megapixels (0 for no limit) | 250 |
-workers |
Number of images to convert at once in batch mode | CPU cores |
-v |
Print a detailed line for every file | false |
-quiet |
Print nothing but errors and warnings | false |
-json |
Print a JSON report of the run instead of the usual output | false |
-version |
Print the version and exit | |
-h |
Show help |
| Code | Meaning |
|---|---|
0 |
All files converted successfully |
1 |
One or more files failed, or the input/output path could not be used |
2 |
Invalid usage, such as a missing -i |
130 |
Interrupted with Ctrl+C before finishing |
Errors and warnings are written to stderr, so they stay visible when the normal output is redirected and can be captured separately with 2> errors.log.
go test ./...