diff --git a/src/App.svelte b/src/App.svelte
index 0c85d92..ede4a3a 100644
--- a/src/App.svelte
+++ b/src/App.svelte
@@ -1,47 +1,13 @@
diff --git a/src/colorUtils.ts b/src/colorUtils.ts
new file mode 100644
index 0000000..776aba6
--- /dev/null
+++ b/src/colorUtils.ts
@@ -0,0 +1,56 @@
+export function randomRgbColor(): number[] {
+ const r = Math.floor(Math.random() * 255);
+ const g = Math.floor(Math.random() * 255);
+ const b = Math.floor(Math.random() * 255);
+ return [r, g, b];
+}
+
+export function cmykToRgb(cmyk: number[]): number[] {
+ return [
+ Math.round(255 * (1 - cmyk[0] / 100) * (1 - cmyk[3] / 100)),
+ Math.round(255 * (1 - cmyk[1] / 100) * (1 - cmyk[3] / 100)),
+ Math.round(255 * (1 - cmyk[2] / 100) * (1 - cmyk[3] / 100)),
+ ];
+}
+
+export function rgbToCmyk(rgb: number[]): number[] {
+ let r = rgb[0] / 255;
+ let g = rgb[1] / 255;
+ let b = rgb[2] / 255;
+ let k = 1 - Math.max(r, g, b);
+ let c = Math.round(((1 - r - k) / (1 - k)) * 100);
+ let m = Math.round(((1 - g - k) / (1 - k)) * 100);
+ let y = Math.round(((1 - b - k) / (1 - k)) * 100);
+ return [c || 0, m || 0, y || 0, Math.round(k * 100)];
+}
+
+export function stringToRgb(input: string): number[] | undefined {
+ if (input.startsWith("#")) {
+ let parts = input.match(/([0-f0-F]){2}/g);
+ if (!parts || parts.length != 3) return undefined;
+ return parts.map((e) => parseInt(e, 16));
+ } else if (input.startsWith("rgb(")) {
+ let parts = input.match(/[0-9]{1,3}/g);
+ if (!parts || parts.length != 3) return undefined;
+ return parts.map((e) => parseInt(e));
+ } else if (input.startsWith("cmyk(")) {
+ let parts = input.match(/[0-9]{1,3}/g);
+ if (!parts || parts.length != 4) return undefined;
+ return cmykToRgb(parts.map((e) => parseInt(e)));
+ }
+}
+
+// From: https://github.com/susam/myrgb/blob/main/myrgb.html#L206
+export function score(rgb1: number[], rgb2: number[]) {
+ const maxRErr = Math.max(rgb1[0], 15 - rgb1[0]);
+ const maxGErr = Math.max(rgb1[1], 15 - rgb1[1]);
+ const maxBErr = Math.max(rgb1[2], 15 - rgb1[2]);
+ const maxDist = Math.sqrt(
+ maxRErr * maxRErr + maxGErr * maxGErr + maxBErr * maxBErr
+ );
+ const rErr = Math.abs(rgb2[0] - rgb1[0]);
+ const gErr = Math.abs(rgb2[1] - rgb1[1]);
+ const bErr = Math.abs(rgb2[2] - rgb1[2]);
+ const dist = Math.sqrt(rErr * rErr + gErr * gErr + bErr * bErr);
+ return Math.max(0, Math.floor(100 * (1 - dist / maxDist)));
+}