Skip to content

OrlovEvgeny/TinyJPG

Repository files navigation

TinyJPG logo

CI status Releases Install TinyJPG C++23 CMake vcpkg License

TinyJPG

TinyJPG is a C++23 image optimizer built for fast, repeatable asset pipelines. It compresses JPEG, PNG, WebP, AVIF, and JPEG XL files in-process, generates responsive variants, scans folders, watches upload directories, and reports results as text, tables, or JSON.

The executable is available as both tinyjpg and the short alias tj.

Contents

Preview

Before After
Original portrait before compression Portrait after TinyJPG compression
1.5 MB JPEG 243 KB JPEG

This sample keeps the original 2236 x 1792 dimensions and reduces the file by about 84%.

Why TinyJPG

  • Native C++23 command line tool with no shell-outs for image conversion.
  • Codec coverage for JPEG, PNG, WebP, AVIF, and JPEG XL workflows.
  • One-shot run, recursive scan, and continuous watch modes.
  • Built-in responsive presets plus TOML configuration for custom variants.
  • Atomic writes, dry-run planning, skip-if-not-smaller behavior, and structured output for CI and automation.
  • Cross-platform CMake and vcpkg build on Linux, macOS, and Windows.

Install

Linux and macOS:

curl -fsSL https://tj.eorlov.org/install.sh | sh

Windows PowerShell:

irm https://tj.eorlov.org/install.ps1 | iex

Homebrew on Apple Silicon macOS:

brew tap OrlovEvgeny/tinyjpg
brew install tinyjpg

Install a specific version:

curl -fsSL https://tj.eorlov.org/install.sh | sh -s -- --version 1.0.1
irm https://tj.eorlov.org/install.ps1 -OutFile install.ps1
./install.ps1 -Version 1.0.1

The installers read the release manifest, pick the correct archive for the current platform, verify SHA256, and install tinyjpg plus the tj alias.

Quick Start

Run a safe dry-run first:

tj scan ./images --preset web --dry-run --format table

Generate optimized variants:

tj scan ./images --preset web --format table

Create a config file for repeatable jobs:

tj config print --defaults > tinyjpg.toml
tj config validate tinyjpg.toml
tj scan ./images --config tinyjpg.toml

Watch a directory continuously:

tj watch ./uploads --config tinyjpg.toml

Commands

Command Purpose Example
run Optimize explicit files. tj run hero.jpg banner.png --preset web
scan Optimize supported images under files or directories. tj scan ./public/images --config tinyjpg.toml
watch Repeatedly scan paths and process changed images. tj watch ./uploads --config tinyjpg.toml
presets list Show built-in responsive presets. tj presets list --format table
config print Print a default TOML config. tj config print --defaults
config validate Validate a TOML config before automation. tj config validate tinyjpg.toml
doctor Check local runtime basics. tj doctor --format table
completion Print shell completion for bash, zsh, or fish. tj completion zsh > _tj

Common options for run, scan, and watch:

Option Meaning
--config, -c TOML configuration file.
--preset Replace configured variants with web, ecommerce, or avatar.
--dry-run Plan work without writing files.
--quiet, -q Suppress per-file text output.
--format Output as text, table, or json.

Output formats are intended for different uses: text for humans, table for inspection, and json for CI or other automation.

Configuration

Generate and validate a config:

tj config print --defaults > tinyjpg.toml
tj config validate tinyjpg.toml

TinyJPG starts from built-in defaults and then applies values from your TOML file. CLI flags such as --preset and --dry-run override the loaded config for that invocation.

Practical Config

This config is suitable for a service-style image pipeline. It watches an input directory, writes generated files into a separate output directory, and creates three web delivery variants.

[general]
workers = 0
log_level = "info"
queue_capacity = 512
stable_wait_ms = 400
dry_run = false

[watch]
paths = ["/var/lib/tinyjpg/inbox"]
recursive = true
include = ["*.jpg", "*.jpeg", "*.png", "*.webp"]
exclude = ["**/.cache/**", "*.tmp"]
prefix = []

[compress]
mode = "visually_lossless"
effort = "balanced"
keep_metadata = false
skip_if_not_smaller = true
preserve_original = true

[[variant]]
name = "large"
codec = "auto"
max_width = 1920
suffix = "-large"

[[variant]]
name = "card"
codec = "webp"
mode = "lossy"
max_width = 960
quality = 82
suffix = "-card"

[[variant]]
name = "thumb"
codec = "auto"
max_width = 320
max_height = 320
quality = 76
fit = "cover"
suffix = "-thumb"

[output]
directory = "/var/lib/tinyjpg/output"
pattern = "{stem}{suffix}.{ext}"
on_exist = "version"

For /var/lib/tinyjpg/inbox/hero.jpg, this can produce:

/var/lib/tinyjpg/output/hero-large.jpg
/var/lib/tinyjpg/output/hero-card.webp
/var/lib/tinyjpg/output/hero-thumb.jpg

Config Reference

[general]

Setting Values Meaning
workers 0 or positive integer Worker threads. 0 uses hardware concurrency.
log_level trace, debug, info, warn, error Runtime verbosity.
queue_capacity positive integer Maximum queued work items before producers wait.
stable_wait_ms 0 or positive integer Watch-mode delay before reading a changed file, so uploads can finish.
dry_run true, false Plan work without writing files. Can also be set with --dry-run.

[watch]

Setting Values Meaning
paths array of paths Default paths for tj watch when no CLI path is passed.
recursive true, false Parsed from TOML; directory scanning currently walks recursively.
include glob array Parsed from TOML; current processing still selects files by supported image codec.
exclude glob array Parsed from TOML; generated TinyJPG variants are skipped automatically.
prefix string array Parsed from TOML and reserved for path-prefix filtering.

[compress]

Setting Values Meaning
mode lossless, visually_lossless, lossy Default fidelity target for variants.
effort fast, balanced, max Encoder effort/speed preference.
keep_metadata true, false Keep image metadata when possible.
skip_if_not_smaller true, false Skip outputs that would be larger than the source.
preserve_original true, false Avoid overwriting the source path; an empty suffix becomes -optimized.

[[variant]]

Each variant describes one output image. At least one variant is required. Variant names may contain letters, digits, _, and -.

Setting Values Meaning
name string Variant name shown in output and available as {name}.
codec auto, jpeg, png, webp, avif, jxl Output codec. auto keeps the source codec.
mode same as [compress].mode Optional per-variant fidelity override.
quality 1 to 100 Encoder quality. If omitted, TinyJPG uses 82.
max_width positive integer Maximum output width.
max_height positive integer Maximum output height.
fit contain, cover, fill Resize behavior when both dimensions are set.
suffix string Added to output file names, for example -thumb.

Every non-original variant must set max_width, max_height, or both. The special variant name original is allowed without size constraints.

[output]

Setting Values Meaning
directory path or empty string Output directory. Empty means next to the input file.
pattern string with tokens Output path template.
on_exist skip, overwrite, version Behavior when the output path already exists.

Supported pattern tokens:

Token Value
{dir} Input directory or configured output directory.
{stem} Input file name without extension.
{suffix} Variant suffix.
{ext} Output extension for the chosen codec.
{name} Variant name.
{codec} Output codec name.
{width} Planned output width.
{height} Planned output height.

Useful patterns:

pattern = "{dir}/{stem}{suffix}.{ext}"
pattern = "{stem}-{width}w.{ext}"
pattern = "{codec}/{stem}-{name}.{ext}"

Presets

Use presets when you do not need custom variants:

tj scan ./images --preset web
tj scan ./products --preset ecommerce
tj scan ./avatars --preset avatar
Preset Variants
web original, large 1920w, medium 1024w, thumb 320x320 cover
ecommerce original, hero 1600w, listing 900w, thumb 320w
avatar original, full 512x512 cover, thumb 128x128 cover

Services

Release archives include service helpers for long-running optimization:

  • systemd unit, sysusers, and tmpfiles snippets for Linux.
  • launchd plist template for macOS.
  • PowerShell install and uninstall scripts for Windows services.

systemd Example

The generated Linux unit runs:

tinyjpg watch --config /etc/tinyjpg/tinyjpg.toml

It uses a dedicated tinyjpg user, protects the host filesystem, and only grants write access to /var/lib/tinyjpg and /var/log/tinyjpg. Keep watched input and output directories under /var/lib/tinyjpg, or add a systemd override with extra ReadWritePaths=.

Example setup:

sudo systemd-sysusers packaging/systemd/tinyjpg.sysusers
sudo systemd-tmpfiles --create packaging/systemd/tinyjpg.tmpfiles

sudo install -d -m 0750 -o root -g root /etc/tinyjpg
sudo install -d -m 0750 -o tinyjpg -g tinyjpg /var/lib/tinyjpg/inbox
sudo install -d -m 0750 -o tinyjpg -g tinyjpg /var/lib/tinyjpg/output
sudo install -m 0640 -o root -g tinyjpg tinyjpg.toml /etc/tinyjpg/tinyjpg.toml
sudo install -m 0644 build/packaging/systemd/tinyjpg.service /etc/systemd/system/tinyjpg.service
sudo systemctl daemon-reload
sudo systemctl enable --now tinyjpg.service

Check service health and logs:

systemctl status tinyjpg.service
journalctl -u tinyjpg.service -f

If your images live outside /var/lib/tinyjpg, add an override:

sudo systemctl edit tinyjpg.service
[Service]
ReadWritePaths=/srv/uploads /srv/images /var/lib/tinyjpg /var/log/tinyjpg

Then reload and restart:

sudo systemctl daemon-reload
sudo systemctl restart tinyjpg.service

Release Assets

Tagged releases publish platform archives to Cloudflare R2 and create a GitHub Release with changelog and install instructions.

Platform Archive
Linux x86_64 tinyjpg-{version}-linux-x86_64.tar.gz
Linux aarch64 tinyjpg-{version}-linux-aarch64.tar.gz
macOS arm64 tinyjpg-{version}-macos-arm64.tar.gz
Windows x86_64 tinyjpg-{version}-windows-x86_64.zip

The public manifest for installers and future self-update support is:

https://tinyjpg.eorlov.org/tinyjpg/manifest.json

Build From Source

Requirements:

  • CMake 3.28 or newer.
  • Ninja.
  • A C++23 compiler.
  • vcpkg.
  • nasm on Linux for codec dependencies.

Configure, build, test, and install:

cmake -S . -B build -G Ninja \
  -DCMAKE_BUILD_TYPE=Release \
  -DCMAKE_TOOLCHAIN_FILE="$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake" \
  -DBUILD_TESTING=ON

cmake --build build
ctest --test-dir build --output-on-failure
cmake --install build --prefix ~/.local

Create local CPack archives:

cpack --config build/CPackConfig.cmake

License

MIT