Look at images quickly. ViewFree opens a photo and gets out of the way — it starts in about a sixth of a second, arrow keys move through the folder with no waiting, and the picture fills the window instead of a wall of toolbars.
Underneath it has the batch tools people normally reach for a much heavier program to get: convert a folder to another format, resize by percentage or longest side, rename hundreds of files from a pattern, strip location data before sharing, or add a watermark.
No ads. No telemetry. No accounts. No paid tiers. Ever.
Download viewfree.flatpak from the
latest release, then:
flatpak install --user viewfree.flatpak(A Flathub submission is prepared but not yet published — see Known limitations.)
Download the .deb from the
latest release, then:
sudo apt install ./viewfree_1.0.3_amd64.debDownload the .rpm from the
latest release, then:
sudo dnf install ./viewfree-1.0.3-1.x86_64.rpmchmod +x ViewFree-x86_64.AppImage
./ViewFree-x86_64.AppImage photo.jpgOpen the menu (☰) → Set ViewFree as Default Viewer… → Make ViewFree the Default.
It only affects your own account, needs no administrator password, and the Restore Previous button puts back whichever application handled each file type before — ViewFree records that when it takes over, so undoing it is exact rather than approximate.
- Opens instantly, even from a cold start
- Arrow through a folder with no perceptible delay; neighbours are decoded in the background before you ask for them
- Zoom (fit, 1:1, free), pan, rotate, flip, fullscreen, slideshow
- Optional thumbnail strip for the current folder
- Camera settings, capture date and location, on demand
- Honours EXIF orientation, so photos are never sideways
Crop, resize, rotate, flip, and brightness/contrast/saturation. That is the whole list, on purpose — this is a viewer that can touch up an image, not a photo editor.
- Save a Copy is the only save. The original is never modified.
- Crop by dragging on the image: corner and edge handles, rule-of-thirds
guides, live size readout.
Aselects everything,Entercrops,Escapecancels.
Ctrl+Zundoes any edit, as many steps back as memory allows.- Lossless JPEG rotation where the file allows it: the orientation tag is rewritten and not a single pixel is re-encoded, so the same photo can be rotated a hundred times with no quality loss. When a JPEG has no orientation tag, ViewFree says so and offers to save a rotated copy rather than quietly degrading it.
- Convert a folder to another format, with a real quality setting — including lossy WebP, so converting to save space actually saves space
- Resize by fit-within, longest side, percentage, width or height — aspect ratio preserved, and small images are not blown up unless you ask
- Rename from a pattern, with a live preview of every resulting name
- Strip or preserve metadata — removing GPS coordinates and camera serial numbers before putting photos online
- Watermark with text, positioned in any of nine places
Every batch job previews exactly what it will do first, shows progress, can be cancelled, and never touches your originals unless you explicitly choose "Replace the originals" — which asks you to confirm and says plainly that there is no undo.
The rename pattern language:
| Token | Meaning |
|---|---|
#, ##, ### |
Counter, padded to the number of # |
{name} |
The original filename, without its extension |
{ext} |
The original extension |
{parent} |
Name of the containing folder |
{date} {time} |
Date/time taken, as 2024-06-01 / 14-23-05 |
{year} {month} {day} {hour} {minute} {second} |
Individual parts |
{width} {height} {size} |
Pixel dimensions |
{camera} {iso} {aperture} {exposure} {focal} |
From the EXIF data |
So {date}_{name}_### turns DSC0042.JPG into 2024-06-01_DSC0042_001.jpg.
| Kind | Formats |
|---|---|
| Photographs | JPEG (including progressive and CMYK), PNG, WebP, AVIF, HEIC/HEIF, TIFF, BMP |
| Animation | Animated GIF, animated WebP, APNG |
| Vector and icons | SVG, ICO |
| Camera raw | CR2, CR3, NEF, NRW, ARW, SRF, SR2, DNG, ORF, RW2, RAF, PEF, SRW, X3F and others |
| Others | QOI, PNM/PPM/PGM/PBM, TGA, DDS, Radiance HDR |
Formats are identified by their contents, not their extension, so a PNG that
somebody named .jpg still opens.
| Key | Action |
|---|---|
→ / Space / Page Down |
Next image |
← / Backspace / Page Up |
Previous image |
Home / End |
First / last image |
Ctrl+O / Ctrl+Shift+O |
Open a file / a folder |
F5 |
Reload from disk |
Escape |
Leave fullscreen, or close |
Ctrl+Q |
Quit |
| Key | Action |
|---|---|
+ / − |
Zoom in / out |
Ctrl+0 / Ctrl+1 |
Actual size / fit to window |
F or F11 |
Fullscreen |
| Drag, or arrows when zoomed | Pan |
| Scroll | Zoom (or next/previous — see Preferences) |
T |
Thumbnail strip |
I |
Image information |
S |
Start or stop the slideshow |
P |
Pause or resume an animation |
| Key | Action |
|---|---|
R / Shift+R |
Rotate clockwise / anticlockwise |
H / V |
Flip horizontally / vertically |
Ctrl+R |
Resize… |
Ctrl+Shift+C |
Crop — then drag a rectangle on the image |
A / Enter / Escape |
While cropping: select all / crop / cancel |
Ctrl+Z |
Undo the last edit |
Ctrl+E |
Brightness, contrast, saturation… |
Ctrl+S |
Save a copy… |
Ctrl+C |
Copy the image to the clipboard |
Delete |
Move to the wastebasket |
| Key | Action |
|---|---|
Ctrl+B |
Batch convert and resize… |
Ctrl+Shift+R |
Batch rename… |
F1 |
All keyboard shortcuts |
- Distributions: Debian, Ubuntu, Mint, MX, Fedora, Arch, openSUSE. Nothing in ViewFree is specific to one family.
- Desktops: GNOME, KDE Plasma, Xfce, Cinnamon, MATE, LXQt. It does not use libadwaita and does not assume GNOME conventions; the toolbar lives inside the window so each desktop keeps drawing its own title bar the way its users expect.
- Display servers: Wayland and X11.
- Theming: follows the system theme, including dark mode, and the system font and scaling settings. HiDPI and fractional scaling work.
- File managers: Nautilus, Dolphin, Thunar, Nemo and Caja get "Open with ViewFree" and the batch tools on a selection. A file manager that is not installed is skipped, never an install error.
An honest list of what is rough in 1.0.0:
- Flathub submission is not done. The manifest builds and the Flatpak
installs, but publishing to Flathub needs a pull request from a human against
flathub/flathub. See
packaging/flatpak/FLATHUB.mdfor exactly what is left. - Camera raw shows the embedded preview, not a developed raw file. Every raw format stores the JPEG the camera itself rendered, and that is what ViewFree displays — instantly, for every make and model, including ones released after this was written. Full raw development (demosaic, white balance, tone curves) takes seconds per image and a large dependency, and is out of scope for a viewer.
- AVIF and HEIC are read but not written. Batch conversion targets JPEG, PNG, WebP, TIFF, BMP and GIF.
- A 100 megapixel image takes about 1.7 seconds to decode. The window and toolbar appear immediately with a progress indicator, but the picture cannot appear faster than the pixels can be decoded.
- Very large images use real memory. Decoded images are held as 8-bit RGBA, so a 24 megapixel photo costs about 96 MB. The cache is capped (224 MB by default, adjustable in Preferences) so memory plateaus rather than growing, but the ceiling is what it is.
- GTK 4.10 or newer is required to build from source. Older distributions are covered by the Flatpak, which carries its own runtime.
- The dialogs are plainer than the modern GNOME standard. The viewer window is where the effort went, and it shows; the batch, resize and preferences windows are tidy, aligned forms rather than designed screens. GNOME's Loupe looks more polished because it uses libadwaita, which ViewFree deliberately does not — libadwaita would make it look foreign on KDE, Xfce, MATE and LXQt. That was the right trade for an app meant to work everywhere, and this is the price of it.
- Only one desktop has been tried by hand. Development was on Xfce, where
dark mode (
GTK_THEME=Adwaita:dark) and HiDPI (GDK_SCALE=2) both check out. Wayland is verified too —tests/wayland_check.shruns ViewFree under a nested compositor withGDK_BACKEND=wayland, so it cannot quietly fall back to X11 — but GNOME, KDE, Cinnamon, MATE and LXQt have not been exercised on real hardware. Reports from those are especially welcome.
Found something else? Open an issue.
Measured on the machine this was developed on — an ordinary quad-core desktop with 8 GB of RAM and no discrete graphics, which is the hardware ViewFree is tuned for. Minimum of twelve runs on an idle machine:
| Time to image on screen | |
|---|---|
| A bare GTK4 window containing nothing | 123 ms |
| ViewFree, opening a photograph | 153 ms |
Both rows come from the same session, alternating between the two, because that is the only comparison that survives a busy desktop: background load moves both numbers together, and on a loaded machine the same pair measures around 220 ms and 250 ms. The gap is the number that means something.
That gap is about 30 ms against the floor for any GTK4 application, and it took two specific decisions to get there:
- No
GtkApplication. It costs roughly 330 ms before a window can appear, almost all of it connecting to the session bus and negotiating a unique name. ViewFree uses a plaingtk::initand its own main loop instead. The cost is losing single-instance behaviour, which for an image viewer is no loss at all. - The icon theme is loaded after the first frame. Loading it costs about 315 ms — more than everything else in startup combined, and more than decoding the image. So the window is presented with no icons in it and the toolbar fills itself in about 55 ms later, by which time the photograph has already been on screen for a twentieth of a second.
Decoding also starts on a worker thread before GTK is initialised, so the two overlap.
Check the numbers yourself:
VIEWFREE_TRACE_STARTUP=1 viewfree photo.jpgMemory, holding down the arrow key through 300 three-megapixel photographs: peaks at about 610 MB and then stays flat, drifting 13 MB over the whole run.
Sitting on a photograph, ViewFree uses 0% CPU — there is no redraw loop, so
it costs nothing to leave open. An animated GIF costs about 1%.
tests/idle_cpu.sh checks this, because a viewer that quietly spins is a flat
battery and an audible fan.
Rust 1.83 or newer and the GTK 4.10+ development files.
# Debian, Ubuntu, Mint, MX
sudo apt install build-essential pkg-config libgtk-4-dev libheif-dev
# Fedora
sudo dnf install gcc pkgconf-pkg-config gtk4-devel libheif-devel
# Arch
sudo pacman -S base-devel gtk4 libheif
# openSUSE
sudo zypper install gcc pkg-config gtk4-devel libheif-develThen:
git clone https://github.com/justlinuxnoob/viewfree
cd viewfree
cargo build --release
./target/release/viewfree photo.jpglibheif is optional — cargo build --release --no-default-features drops HEIC
and AVIF support, and those files then report a clear "this build has no HEIF
support" message rather than failing mysteriously.
# The unit tests need nothing extra.
cargo test
# The corpus and batch tests need test images, and the tools that check them.
sudo apt install python3-pil graphicsmagick libimage-exiftool-perl
python3 tests/make_corpus.py target/corpus
cargo testThe corpus is deliberately nasty: every format, animated GIFs, a 100 megapixel JPEG, one-pixel images, CMYK and progressive JPEGs, all eight EXIF orientations, zeroed capture dates, GPS at 0°0′, truncated and zero-byte files, files whose extension lies about their contents, emoji and quotes in filenames, read-only files and dangling symlinks. Every one must either display correctly or fail with a message fit to show a person.
The batch tests verify their results with gm identify, exiftool and file
rather than trusting ViewFree's own report, and check after every job that the
originals are byte-for-byte unchanged.
Four more checks need a real display:
tests/gui_smoke.sh # the window actually shows a picture, per format
tests/gui_dialogs.sh # Crop/Undo/Adjust/Delete/Batch, checking what lands on disk
tests/gui_interaction.sh # zoom, pan, fullscreen, panels — and that chrome stays put
tests/idle_cpu.sh # an idle window must not burn CPU
tests/wayland_check.sh # runs under a nested Wayland compositor
tests/bench_startup.sh # cold-start timing
tests/memory_soak.sh # 300 large images, watching resident memorygui_dialogs.sh exists because the unit tests covered every operation and
none of the wiring: "Move to wastebasket" deletes somebody's photograph and
had never been run once until that file was written.
| Path | What is in it |
|---|---|
crates/viewfree-core |
Decoding, metadata, editing, saving, the batch engine. No GTK, fully testable headlessly. |
crates/viewfree |
The GTK4 application. |
data/ |
Desktop entry, icon, AppStream metadata, file manager integration. |
packaging/ |
Flatpak, .deb, .rpm and AppImage. |
tests/ |
Corpus generator, startup benchmark, memory soak. |
The split is not decoration: the batch engine is the part that writes to people's files, so it is the part that has to be provable, and keeping it free of GTK means it can be tested exhaustively in CI with no display server.
ViewFree is its own code, but the design owes a great deal to programs that solved these problems first:
- IrfanView — the reference point for the whole project: instant startup, arrow-key browsing, and serious batch tools hidden behind a plain interface. The keyboard shortcuts follow IrfanView's wherever it has an opinion, and the rename pattern syntax is deliberately familiar to anyone coming from it.
- XnView — for how to structure a batch job: choose the operations, preview the result, then run.
- nomacs — read for its issue tracker rather than its source. One of its most-upvoted complaints was that it modified files on disk as a side effect of viewing them, which is why ViewFree's tests assert after every decode that the file's size and modification time are unchanged.
- oculante — a fast Rust viewer whose users reported it failing to start on machines without working GPU drivers. That is why ViewFree's rendering never depends on hardware acceleration.
- gThumb and Gwenview — for sensible defaults around metadata display and folder navigation.
- ArchiveFree — the reversible "become the default handler" logic is modelled on the equivalent code there, which solved the same problem for archive files.
No code was copied from any of them.




