Visual Chromatics
Point it at a photo and get a named color palette as ready-to-use CSS custom properties. Unsplash search, file upload, voice input, HEX/RGB/HSL output, and shareable URLs. No account, no server, no install.
Eyedropper isn't a color system
Every designer has opened a reference photo just to use the eyedropper. You sample five colors,
manually convert them to hex, name them something like --accent-1, and repeat.
Twenty minutes later you have a palette that's still not consistent with what the image actually
communicates.
Visual Chromatics does the pixel clustering for you via ColorThief, names the results in
human-readable terms,
and writes the :root {} block immediately. The output isn't just color values. It's
already CSS:
/* Extracted from a coastal landscape */
:root {
--primary: #2d5a7b; /* Deep Ocean */
--secondary: #8fb4c8; /* Sky Haze */
--accent: #d4a853; /* Sunlit Sand */
--color-4: #f2efe6; /* Sea Foam */
--color-5: #1a3a50; /* Midnight Tide */
}
Three ways to get an image in
Pick the workflow that fits. All three end up at the same extraction step.
Unsplash Search
Type a keyword like "autumn forest" or tap a suggestion chip. A random matching photo loads instantly. Refresh pulls a new result for the same query without clearing your extracted palette.
File Upload
Drag in any local image or browse for it. The file is read via the Browser File API and never leaves the device. No upload, no server round-trip.
Voice Search
Tap the microphone and say a concept. Annyang captures the phrase via the Web Speech API and passes it to Unsplash. Works in Chrome and Edge; degrades gracefully elsewhere.
ColorThief Extraction
Colors are clustered from actual pixel data, not averaged. The result reflects the palette that visually defines the image, not a muddy blend of everything in it. Choose to extract 5, 7, or 9 colors.
- ▸Pixel-level color clustering via median cut
- ▸Human-readable name approximation (HSL range matching)
- ▸Large swatch + HEX + RGB label per color
Three Output Formats
The code block updates live as you switch formats. Copy the full CSS block
in the format your codebase expects.
/* RGB */
--primary: rgb(194, 169, 134);
/* HEX */
--primary: #c2a986;
/* HSL */
--primary: hsl(33, 29%, 64%);
Palette Controls
Lock the current palette before refreshing the image. Useful for testing how the same set of colors reads across different photos. Export as PNG for design handoff.
- ▸Lock: freeze palette across image refreshes
- ▸Copy All:
copies full
CSSblock to clipboard - ▸Export PNG: labeled swatch strip as a downloadable image
Share and History
Share Palette encodes the full state into a URL hash. Anyone opening the link sees the same
image and palette instantly, no account needed. The last 10 palettes persist in
localStorage for quick recall.
- ▸URL hash encodes palette + image URL + query
- ▸Fully stateless links, no session or account required
- ▸10-item
archive grid stored in
localStorage
Architecture
A single Vue component tree with no router and no state management library. All state lives in
ImageSearch.vue, broken across four focused composables so each concern stays testable
in isolation.
| Composable | Responsibility |
|---|---|
useColorConversion |
Pure functions: RGB to HEX, HEX to RGB, RGB to HSL, and approximate color naming via HSL range matching. |
usePaletteHistory |
Reads and writes the 10-item palette archive to localStorage. |
useNotification |
Manages the transient toast notification shown after copy and export actions. |
useVoiceSearch |
Wraps Annyang lifecycle (start, stop, cleanup) and surfaces a reactive
listening state.
|
Stack
| Layer | Technology | Notes |
|---|---|---|
| Framework | Vue 3 (Composition API, <script setup>) |
No router, no Vuex/Pinia |
| Build tool | Vite | Hot reload in dev, optimized bundle in prod |
| Styling | Tailwind CSS v3 | |
| Color extraction | ColorThief | Median cut clustering on raw pixel data |
| Image source | Unsplash API | Key injected at build time via Vite env var, never committed |
| HTTP client | Axios | |
| Voice | Annyang (Web Speech API wrapper) | Chrome/Edge only; feature-detected before use |
| PWA | vite-plugin-pwa | Service worker + manifest for offline use |
Privacy by Default
No data is collected or transmitted to any first-party server.
Uploaded Images
Files are read locally via the Browser File API. The pixel data never leaves the device. No upload, no CDN, no logging.
API Key Handling
The Unsplash API key is injected at build time via a Vite environment variable. It is never stored in the repository and never visible in the built output.
Palette History
The 10-item archive is stored in your own localStorage.
It stays on your device and is never read by the app server because there is no app server.
Voice Input
Voice recognition uses the browser's native Web Speech API. Audio is processed by the browser, not by any server this app controls.
Try it on a photo you have open
Upload a screenshot, a mood board image, or a product photo. In moments you have a
named palette and a CSS block ready to paste.