Find the visual character of your image library.
VibeSorter is a local-first Python image organizer that analyzes independent visual attributes, overlapping aesthetic vibes, and confidence signals. It keeps analysis local, stores reusable results in SQLite, and separates safe discovery from explicit filesystem changes.
VibeSorter keeps measurable features separate from semantic attributes. An image can be a photograph + red + blue + cool + vibrant + bright + retro + playful at the same time.
The canonical taxonomy is:
- Media type: photograph, illustration, screenshot, graphic, collage
- Color: red, orange, yellow, green, blue, purple, pink, neutral (multi-valued)
- Temperature: warm, cool, neutral
- Saturation: vibrant, muted, desaturated
- Brightness: bright, mid, dark
- Vibes: retro, dreamy, soft, moody, minimal, cozy, cinematic, playful, edgy, romantic (multi-valued)
These families are independent. Compound labels are not part of the canonical model.
The detector uses lightweight local image features including brightness, saturation, contrast, warm/cool balance, grayscale content, dark/light ratios, dominant colors, text likelihood, and spatial features. Semantic classification stores an ImageProfile with confidence and provenance for each attribute.
Repeated analysis persists raw features and structured profiles in the local SQLite cache at .vibesorter/analysis.db. Cached search and browser filtering operate on stored data without rescanning source images.
VibeSorter requires Python 3.10+.
git clone https://github.com/Ava-91/VibeSorter.git
cd VibeSorter
python -m venv .venv
source .venv/Scripts/activate
python -m pip install --upgrade pip
python -m pip install -e ".[dev]"python -m venv .venv
.venv\Scripts\activate
python -m pip install --upgrade pip
python -m pip install -e ".[dev]"python -m venv .venv
.venv\Scripts\Activate.ps1
python -m pip install --upgrade pip
python -m pip install -e ".[dev]"git clone https://github.com/Ava-91/VibeSorter.git
cd VibeSorter
python3 -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install -e ".[dev]"Verify the installation:
vibesorter --help
vibesorter --versionVibeSorter has three entry points:
The CLI is the main workflow interface:
vibesorter --helpBrowse the cached SQLite analysis locally:
vibesorter-browserOr choose the database, host, and port explicitly:
vibesorter browser --db "path/to/photos/.vibesorter/analysis.db" --host 127.0.0.1 --port 8765Launch the local desktop shell around the browser interface:
vibesorter-desktopOr:
vibesorter desktop --db "path/to/photos/.vibesorter/analysis.db" --port 8765All interfaces are local-first. They do not upload source images to a third-party AI service.
For a first run, use a small test folder instead of your entire library. A folder containing 5–20 images is enough to validate the workflow.
vibesorter scan "path/to/photos"scan is read-only. It lists supported image files and does not analyze, move, rename, copy, or delete them.
vibesorter analyze "path/to/photo.jpg"This prints the best vibe, confidence, text/screenshot likelihood, and the full vibe ranking.
vibesorter index "path/to/photos"index is the preferred incremental library-analysis command. It stores reusable results in:
path/to/photos/.vibesorter/analysis.db
The legacy preview, stats, and propose commands also persist the analyses they perform to the same cache.
vibesorter stats "path/to/photos"You can request JSON output:
vibesorter stats "path/to/photos" --jsonSearch is fast and read-only; it does not re-analyze source images.
vibesorter search "path/to/photos" --limit 20Filter by the best vibe:
vibesorter search "path/to/photos" --vibe minimal
vibesorter search "path/to/photos" --vibe cozy --min-score 0.60Filter by filename/path and visual measurements:
vibesorter search "path/to/photos" --path "billie"
vibesorter search "path/to/photos" --min-brightness 0.65 --max-saturation 0.75 --limit 50Search never modifies source images.
vibesorter duplicates "path/to/photos"This reports exact duplicates and perceptual near-duplicate pairs without changing files.
vibesorter propose "path/to/photos" --output proposal.jsonThis creates a deterministic plan. Nothing is moved yet.
You can choose a different destination root:
vibesorter propose "path/to/photos" --output-root "VibeSorted" --output proposal.jsonAccept specific operation IDs:
vibesorter review proposal.json --accept 1,3-5 --output proposal-reviewed.jsonAccept every operation for a vibe:
vibesorter review proposal.json --accept-vibe minimal --accept-vibe cozyReviewing is still read-only.
Before a real apply, use a dry run:
vibesorter apply proposal-reviewed.json --dry-runA dry run never moves files.
Real filesystem changes require explicit confirmation:
vibesorter apply proposal-reviewed.json --confirmSuccessful applies create an auditable history record and print the generated batch ID. Keep that ID if you may want to roll the operation back.
vibesorter historyMachine-readable history:
vibesorter history --jsonYou can choose another history file with --history on apply, history, and rollback.
Use the batch ID printed by apply:
vibesorter rollback BATCH_ID --dry-runOnly after checking the planned restores should you perform them:
vibesorter rollback BATCH_ID --confirmRollback verifies the recorded file hash before restoring a file and refuses to overwrite an occupied source path or changed destination.
Search combines independent attributes instead of requiring a compound category. Multiple values in a multi-valued family are supported, while different families can be combined with AND semantics.
Physical organization is a separate, explicit step. A folder plan chooses one primary attribute for folder names; secondary attributes remain metadata so multi-label images are not duplicated. Proposed moves are reviewable and read-only until the user explicitly confirms them. Existing destinations, missing sources, duplicate destinations, and low-confidence classifications are blocked before mutation, and applied moves can be rolled back.
Analyze a library:
from vibesorter import analyze_library
for result in analyze_library("path/to/photos"):
print(result.path, result.best.name, result.best.score, result.cached)Structured classification is available directly:
from vibesorter.classifier import classify_profile
from vibesorter.features import extract_features
profile = classify_profile(extract_features("path/to/photo.jpg"))
print(profile.to_dict())Analysis, search, statistics, duplicate checks, proposals, reviews, and browser views are local and read-only with respect to the source library. Filesystem changes require explicit confirmation.
Apply history is written atomically. If durable history cannot be committed after a move, VibeSorter attempts to restore the just-moved files instead of silently leaving filesystem changes without an audit record.
VibeSorter is local-first: source images are not uploaded to a third-party AI service by the project.
The pre-v2 compound vocabulary is retired from the canonical taxonomy. Historical records containing those labels are treated as legacy/unmigrated data rather than being silently guessed into new attributes. New ImageProfile instances accept only canonical family values, so compound labels cannot re-enter the semantic model.
Install development dependencies in the virtual environment:
python -m pip install -e ".[dev]"Run the same checks used by CI:
ruff check .
pytest -qThe CI workflow tests Python 3.10, 3.11, 3.12, and 3.13 because the package declares requires-python = ">=3.10".
When changing the classifier or taxonomy, add regression tests for the changed contract. When changing filesystem operations, test dry-run, confirmation, history, conflict handling, and rollback behavior.
The multidimensional taxonomy migration is complete. The canonical model is stable and protected by regression tests covering schema validation, serialization, multi-valued attributes, per-attribute confidence/provenance, contradictory combinations such as Red + Cool, SQLite persistence, browser filtering, folder planning, filesystem apply/rollback, and rejection of retired compound labels.
- Python
- Pillow
- SQLite
- Deterministic local feature extraction and classification
- Offline evaluation and learned-classifier comparison
- Local browser UI
See LICENSE.