Fast, minimalist image and video viewer for Linux, written in Rust with GTK4/libadwaita.
- Navigates with arrows or gestures, three-finger swipe (optional two-finger); slides when the prefetched frame is ready, cuts otherwise
- Decodes on worker threads, prefetches neighbors, applies EXIF orientation, fits to window with aspect ratio kept
- Zooms with keyboard, pinch, or double-click; double-click anchors at the pointer, drag pans while zoomed
- Rotates left/right and mirrors in place from header buttons; JPEG re-encoded at q95 with EXIF normalized, no undo; animated GIF/WebP and video are view-only
- Plays video muted, autoplaying, looped, with play/pause, seek, volume, mute, elapsed and total time; unmuting is remembered for the next videos
- Deletes with
Delete: Trash first, direct delete where Trash is unsupported (remote mounts, portal paths) - Auto-hides header and video controls, supports fullscreen.
| Type | Formats |
|---|---|
| Images | JPEG (.jpg, .jpeg), PNG, WebP, GIF, BMP, TIFF (.tif, .tiff) |
| Video | MP4, WebM, Matroska (.mkv) |
Extensions match case-insensitively. Image decoding reads file contents; video codecs come from installed GStreamer plugins.
A per-user Meson install under ~/.local avoids touching Pacman files and needs no sudo to install.
Install the build and runtime dependencies:
sudo pacman -S --needed \
base-devel git meson rust gtk4 libadwaita \
hicolor-icon-theme desktop-file-utilsFor broad video-codec support, also install GStreamer's common plugin sets:
sudo pacman -S --needed \
gstreamer gst-plugins-good gst-plugins-bad \
gst-plugins-ugly gst-libavClone, build, and install under ~/.local:
git clone https://github.com/grigio/carosello.git
cd carosello
meson setup builddir --prefix="$HOME/.local"
meson compile -C builddir
meson install -C builddirMake sure ~/.local/bin is in your PATH, then launch Carosello from the
application menu or run:
caroselloThis installs binary, desktop entry, AppStream metadata, and icon under ~/.local. After pulling changes, rerun:
meson compile -C builddir
meson install -C builddirBuild a package from this checkout and install it system-wide with pacman. The package is built locally with makepkg — there is no AUR recipe.
Install the build and test dependencies (the GStreamer sets above stay optional):
sudo pacman -S --needed \
base-devel git meson rust gtk4 libadwaita \
hicolor-icon-theme desktop-file-utils appstream gdk-pixbuf2Then from the repository root:
makepkg -si-s resolves missing dependencies through pacman, -i installs the package it just built (a sudo prompt — add --noconfirm in scripts). To build and install as two steps:
makepkg
sudo pacman -U carosello-*.pkg.tar.zstPKGBUILD downloads the release tarball named by pkgver from the GitHub tag, so makepkg builds the tagged source, not your working tree — for local changes use the Meson or Cargo build above. A run also leaves src/carosello-*/ and src/*.tar.gz behind (gitignored): rm -rf src/carosello-* when done, and never git add -A right after a package build.
Download the latest .flatpak file from
GitHub Releases and install it:
flatpak install --user ./carosello.flatpak
flatpak run io.github.grigio.caroselloCI artifacts are x86_64 only.
Needs GNOME 50 runtime plus freedesktop 25.08 Rust extension:
sudo pacman -S --needed flatpak flatpak-builder
flatpak install --user flathub \
org.gnome.Platform//50 org.gnome.Sdk//50 \
org.freedesktop.Sdk.Extension.rust-stable//25.08Also generate the ignored cargo-sources.json from Cargo.lock with the Generate Cargo sources for Flatpak command in
.github/workflows/ci.yml, then:
flatpak-builder --user --install --force-clean \
build-dir io.github.grigio.carosello.ymlThe Flatpak has host and GVfs read/write access for remote folders, in-place edits, and Trash. A single file picked through the document portal exposes only that file; use Open Folder… to browse siblings.
No packaging step, version comes from Cargo.toml:
nix run github:grigio/carosello # run straight from GitHub
nix profile install github:grigio/carosello # installFrom a checkout: nix build / nix run. Only flake.lock moves, via a weekly workflow that builds before opening the PR; every push also builds through the Nix workflow.
To compile without installing desktop integration:
cargo build --release
./target/release/carosello# View supported media in the current directory
carosello
# View supported media in a specific directory
carosello /path/to/directory
# View a specific local file and its supported siblings
carosello /path/to/image.jpgTakes one positional path; a second launch presents the existing window. Open… (Ctrl+O) picks a file, Open Folder… (Ctrl+Shift+O) picks a directory. Under the Flatpak portal, Open Folder… is the way to get siblings and a writable export.
Header buttons rotate left/right and mirror. Each edit overwrites the original and refreshes the view, with JPEG orientation normalized so reloads do not rotate twice. Edits are destructive with no undo, and JPEG bytes change even for a reversed operation.
In the app menu. Slide animation defaults on (falls back to an instant cut if the frame is not prefetched); two-finger swipe defaults off (replaces three-finger nav when on). Videos also start muted, and the mute choice you make with M, the mute button or the volume slider is remembered for the next videos (video-muted). Stored in settings.conf under ~/.config/carosello.
The desktop entry registers JPEG, PNG, WebP, GIF, BMP, TIFF, MP4, WebM, and Matroska types, mirrored in AppStream <provides> plus legacy BMP/TIFF aliases.
To make it the default for all supported types:
xdg-mime default io.github.grigio.carosello.desktop \
image/jpeg image/png image/webp image/gif image/bmp \
image/x-bmp image/x-ms-bmp image/tiff image/x-tiff \
video/mp4 video/webm video/x-matroskaOr set one type through GIO:
gio mime image/jpeg io.github.grigio.carosello.desktop| Key | Action |
|---|---|
Left / Right |
Previous / next file |
Page Up / Page Down |
Previous / next file |
Home / End |
First / last file |
| Key | Action |
|---|---|
Ctrl++ / Ctrl+= |
Zoom in |
Ctrl+- / Ctrl+_ |
Zoom out |
Ctrl+0 |
Reset zoom to fit |
Escape |
Exit fullscreen; otherwise reset zoom when zoomed |
Double-click |
Toggle between fit and 2.5× zoom at the pointer |
F11 / F |
Toggle fullscreen |
| Key | Action |
|---|---|
Ctrl+O |
Open a file |
Ctrl+Shift+O |
Open a folder |
Delete |
Move to Trash, or delete directly if the filesystem has no Trash |
Ctrl+? / Ctrl+/ / Ctrl+K |
Show keyboard shortcuts |
F1 |
About Carosello |
Ctrl+W |
Close the window |
Ctrl+Q |
Quit |
| Key | Action |
|---|---|
Space / K |
Play / pause |
M |
Mute / unmute |
[ |
Seek backward 5 seconds |
] |
Seek forward 5 seconds |
| Click seek bar | Seek to position |
| Gesture | Action |
|---|---|
| Pinch | Zoom in or out |
Ctrl + scroll |
Zoom in or out |
| Scroll | Pan while zoomed; horizontal two-finger scroll can navigate at fit when enabled in Preferences |
| Three-finger swipe left | Next file |
| Three-finger swipe right | Previous file |
| Drag while zoomed | Pan around the image or video |
| Double-click | Toggle fit and pointer-anchored 2.5× zoom |
| Drag and drop | Open dropped files or directories |
With Two-finger swipe on, two-finger gestures replace three-finger ones.
Fast (decode off the UI thread, prefetch neighbors, never block on a missing frame), minimal (no browser, thumbnails, or metadata viewer), keyboard-first (Ctrl + scroll, pinch, shortcuts for zoom).
Rust, GTK4 + libadwaita, image crate on worker threads, GDK MemoryTexture, GTK MediaFile with GStreamer, kamadak-exif.
GPL-3.0-or-later
- Monero:
88LyqYXn4LdCVDtPWKuton9hJwbo8ZduNEGuARHGdeSJ79BBYWGpMQR8VGWxGDKtTLLM6E9MJm8RvW9VMUgCcSXu19L9FSv - Bitcoin:
bc1q6mh77hfv8x8pa0clzskw6ndysujmr78j6se025
