Extracts a dominant color palette from a JPEG photo and renders it as a PNG — original image alongside color swatches and optional EXIF metadata.
Download: latest release (Windows x64, Linux x64, macOS arm64, Android APK)
Desktop app — folder browser, palette settings, and live preview:
Collage mode — check several photos into a tray, generate one palette pooled across all of them (weighted by displayed area), with EXIF taken from the first photo:
Generated palettes — portrait and landscape layouts, with and without hex labels:
Releases are tagged vX.Y.Z (semantic versioning) and each one bundles all four platforms —
PhSpectre-X.Y.Z-windows-x64.zip, PhSpectre-X.Y.Z-linux-x64.tar.gz,
PhSpectre-X.Y.Z-macos-arm64.zip (a proper .app bundle), PhSpectre-X.Y.Z-android.apk — with an auto-generated
changelog. Grab the latest from the
Releases page.
The apps check for a newer release on startup (once a day at most) and show a small, dismissible notice when one's available — no auto-download/auto-install, it just links to the release page.
Every commit to master still gets a plain build-and-test check on all four platforms
(no version, no release) via the Build/Test workflows above — those aren't installable
releases, just CI signal.
Cutting a release (maintainers): git tag vX.Y.Z && git push origin vX.Y.Z — CI builds,
versions, and publishes the release automatically.
Cross-platform GUI built with Avalonia. Open a folder, browse photos, preview the generated palette side-by-side and save it as PNG.
Collage mode (desktop and Android) checks several photos into a tray and renders one card from all of them: photos are laid out into a single image with a configurable gutter, and the palette is pooled across the source photos — weighted by how much area each one occupies — so the gutter itself never affects the result. EXIF metadata is read from the first photo in the tray.
Self-contained — no .NET runtime required on the target machine.
| Key | Action |
|---|---|
↑ / ↓ |
Navigate file list |
Ctrl+S |
Save palette PNG |
dotnet run --project PhSpectre.CLI -- <image.jpg> [options]| Flag | Default | Description |
|---|---|---|
--colors <n> |
auto | Number of palette colors (1–32). Auto uses elbow method (k=3–8). |
--no-hex |
— | Hide hex labels on swatches |
--theme dark|light |
dark |
Dark (#111111) or light (#F5F5F5) panel background |
--no-meta |
— | Suppress EXIF metadata strip |
--meta-short |
— | One line: camera · focal · aperture · shutter · ISO |
--meta-detail |
— | Two lines: camera+lens / focal+params+date |
--meta-full |
— | Three lines: detail + S/N, WB, exposure program, EV |
--meta-overlay |
— | Overlay style (semi-transparent) instead of film-strip bar |
Default metadata verbosity (no flag): camera + lens name.
# Auto palette, dark theme, default metadata
dotnet run --project PhSpectre.CLI -- photo.jpg
# 6 colors, light theme, full metadata
dotnet run --project PhSpectre.CLI -- photo.jpg --colors 6 --theme light --meta-full
# Swatches only, no metadata, no hex labels
dotnet run --project PhSpectre.CLI -- photo.jpg --no-meta --no-hexOutput is saved as <original-name>_palette.png next to the source file. Hex codes and percentages are printed to stdout.
dotnet run --project PhSpectre.API
# Swagger UI: http://localhost:5000/swaggerdocker compose up --build
# Swagger UI: http://localhost:8080/swaggerPOST /api/palette
Content-Type: multipart/form-data
| Field | Type | Required | Description |
|---|---|---|---|
file |
JPEG file | yes | Source photo (.jpg / .jpeg) |
colors |
int | no | Palette size 1–32. Omit for auto. |
theme |
dark | light |
no | Panel background. Default: dark. |
Returns the palette PNG as application/octet-stream.
curl -X POST http://localhost:8080/api/palette \
-F "file=@photo.jpg" \
-F "colors=6" \
-F "theme=light" \
--output palette.pngRate limit: 10 requests per minute per IP. Exceeding returns 429 Too Many Requests.
- Portrait (height > width): swatches panel on the right, metadata strip below the photo
- Landscape (width ≥ height): metadata strip between photo and swatch panel at the bottom
- Sampling — resizes the image to 150×150 for fast processing
- Clustering — k-means++ in HSL space with circular hue distance; automatic k via the elbow method if
--colorsis not set - Rendering — composites the original photo (full resolution, auto-oriented) with the palette panel using SixLabors.ImageSharp
- .NET 8
- Docker (optional, for containerised API)
- Supports JPEG input only (
.jpg/.jpeg)







