Pantone Color Matcher API
Cloudflare Worker API that matches any CMYK or RGB input to the nearest available Pantone fabric shade using Euclidean distance — built as the color engine for the Mockup Builder.
A lightweight Cloudflare Worker that accepts CMYK or RGB color values and returns the nearest available Pantone fabric shade — ensuring every design produced by the Mockup Builder maps to a color that actually exists in production stock.
Tech Stack
| Layer | Tools |
|---|---|
| Runtime | Cloudflare Workers — serverless edge deployment |
| Language | JavaScript (ESM) |
| Matching | Euclidean distance in 4D CMYK space and 3D RGB space |
| Databases | Two static JSON bundles — available kit fabric shades and sock stock options |
Why It Exists
Designers were occasionally applying colors that looked right on screen but had no equivalent in the available Pantone fabric or sock stock — causing rework at production. Rather than relying on a designer knowing the full color catalogue by heart, a simple API was deployed that maps any picked color to the nearest real option automatically. The Mockup Builder calls it in the background when Match Colors is triggered.
How It Works
The API accepts up to four color buckets in a single POST — CMYK primary, RGB primary, CMYK secondary (socks), and RGB secondary — and returns the nearest Pantone match for each label.
Each input is scored against every entry in the reference database using straight-line (Euclidean) distance across all channels — the entry with the smallest distance wins.
The general Euclidean distance formula across n dimensions:
d(A, B) = √( (a₁−b₁)² + (a₂−b₂)² + ··· + (aₙ−bₙ)² )
Applied to each color space:
CMYK (4D): d = √( (C₁−C₂)² + (M₁−M₂)² + (Y₁−Y₂)² + (K₁−K₂)² )
RGB (3D): d = √( (R₁−R₂)² + (G₁−G₂)² + (B₁−B₂)² )
In code, Math.hypot handles the squaring, summing, and square-root in one call:
// 4-dimensional CMYK space (C, M, Y, K)
const distanceCMYK = (a, b) =>
Math.hypot(a.c - b.c, a.m - b.m, a.y - b.y, a.k - b.k);
// 3-dimensional RGB space (R, G, B)
const distanceRGB = (a, b) =>
Math.hypot(a.r - b.r, a.g - b.g, a.b - b.b);
The full matching loop — scan every entry, track the minimum:
function distanceCMYK(a, b) {
return Math.hypot(a.c - b.c, a.m - b.m, a.y - b.y, a.k - b.k);
}
function findClosestCMYK(input, db) {
let best = null, dist = Infinity;
for (const key in db) {
const d = distanceCMYK(input, db[key].main);
if (d < dist) { dist = d; best = key; }
}
return best;
}
export default {
async fetch(request) {
const { primary, primaryRGB, secondary, secondaryRGB } = await request.json();
return new Response(JSON.stringify({
primary: matchBucket(primary, cmykPrimaryDB, 'cmyk'),
primaryRGB: matchBucket(primaryRGB, rgbPrimaryDB, 'rgb'),
secondary: matchBucket(secondary, cmykSecondaryDB,'cmyk'),
secondaryRGB:matchBucket(secondaryRGB,rgbSecondaryDB, 'rgb'),
}), { headers: { 'Content-Type': 'application/json' } });
}
};
Databases
Two separate reference sets are bundled directly into the Worker — no external DB calls, zero cold-start latency:
| Database | Use |
|---|---|
Kit_Colors.json | Available garment Pantone fabric shades (CMYK) |
Kit_Colors_RGB.json | Same palette in RGB for screen preview |
Socks_Colors.json | Available sock stock shades (CMYK) |
Socks_Colors_RGB.json | Sock palette in RGB |