Lei Wu, Web Developer

Keeping simple things simple.

Find the Name of Your Colour... With F# Html Type Provider

How many colour names are there in the world? Wikipedia lists a whopping 1,500 of them: https://en.wikipedia.org/wiki/List_of_colors

The following F# script, given any hex colour value, returns the closet match. For example, it tells you “#FFFFF1” matches “Ivory #FFFFF0”.

The script uses Html Type Provider to query the following three pages and generates a list of colour names:

It then calculates the shortest 3-D distance between your colour and the colours in the list.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#if INTERACTIVE
#r @"..\..\packages\FSharp.Data\lib\net45\FSharp.Data.dll"
#else
module Colours
#endif

open FSharp.Data

[<Literal>]
let Url_a_f = "https://en.wikipedia.org/wiki/List_of_colors:_A%E2%80%93F"
[<Literal>]
let Url_g_m = "https://en.wikipedia.org/wiki/List_of_colors:_G%E2%80%93M"
[<Literal>]
let Url_n_z = "https://en.wikipedia.org/wiki/List_of_colors:_N%E2%80%93Z"

type ``Colours A-F`` = HtmlProvider<Url_a_f>
let colours_a_f =
    ``Colours A-F``.Load(Url_a_f).Tables.``Colors in alphabetical order A-F``.Rows
    |> Seq.map(fun r -> r.Name, r.``Hex (RGB)``)


type ``Colours G-M`` =  HtmlProvider<Url_g_m>
let colours_g_m =
    ``Colours G-M``.Load(Url_g_m).Tables.``Colors in alphabetical order G-M``.Rows
    |> Seq.map(fun r -> r.Name, r.``Hex (RGB)``)

type ``Colours N-Z`` = HtmlProvider<Url_n_z>
let colours_n_z =
    ``Colours N-Z``.Load(Url_n_z).Tables.``Colors in alphabetical order N-Z``.Rows
    |> Seq.map(fun r -> r.Name, r.``Hex (RGB)``)

// Combine the three
let colours =
    colours_n_z
    |> Seq.append colours_g_m
    |> Seq.append colours_a_f
    |> Seq.cache

let distance hex1 hex2 =

    let hexToInt s =
        System.Convert.ToInt32(s, 16)

    // convert hex string to (R:int * G:int * B:int)
    // e.g. "#FFFFFF" to (255, 255, 255)
    let getRGB (hex:string) =
        let r = hex.[1..2] |> hexToInt
        let g = hex.[3..4] |> hexToInt
        let b = hex.[5..6] |> hexToInt
        (r, g, b)

    let (r1, g1, b1) = getRGB hex1
    let (r2, g2, b2) = getRGB hex2
    (r1 - r2)*(r1 - r2) + (g1 - g2)*(g1 - g2) + (b1 - b2)*(b1 - b2)

let matchColour hex' =
    colours |> Seq.minBy (fun (_, hex) -> distance hex' hex)

colours |> Seq.length |> printfn "Total number of colours: %i"
"#FFFFF1" |> matchColour |> printfn "Best match for your colour: %A"