Color Extraction Vue 3 No Backend PWA

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 CSS block 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.

Open Visual Chromatics

Related

Tools