<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://austinpray.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://austinpray.com/" rel="alternate" type="text/html" /><updated>2025-12-12T21:28:24+00:00</updated><id>https://austinpray.com/feed.xml</id><title type="html">Austin Pray</title><subtitle>Austin loves building teams, tools, and platforms that empower others to do their best work.
</subtitle><entry><title type="html">Detecting Grayscale Colors</title><link href="https://austinpray.com/2020/05/25/detecting-grayscale-colors.html" rel="alternate" type="text/html" title="Detecting Grayscale Colors" /><published>2020-05-25T00:00:00+00:00</published><updated>2020-05-25T00:00:00+00:00</updated><id>https://austinpray.com/2020/05/25/detecting-grayscale-colors</id><content type="html" xml:base="https://austinpray.com/2020/05/25/detecting-grayscale-colors.html"><![CDATA[<style>
@keyframes bounce {
  from,
  20%,
  53%,
  to {
    animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
    transform: translate3d(0, 0, 0);
  }

  40%,
  43% {
    animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06);
    transform: translate3d(0, -30px, 0) scaleY(1.1);
  }

  70% {
    animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06);
    transform: translate3d(0, -15px, 0) scaleY(1.05);
  }

  80% {
    transition-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
    transform: translate3d(0, 0, 0) scaleY(0.95);
  }

  90% {
    transform: translate3d(0, -4px, 0) scaleY(1.02);
  }
}

.bounce.animate {
  animation-delay: 1s;
  animation-duration: 1s;
  animation-iteration-count: 1;
  animation-name: bounce;
  transform-origin: center bottom;
}

.figure {
  display: block;
  width: 100%;
  max-width: 30em;
  margin: 0 auto;
}

.colorScale {
  display: grid;
  height: 1em;
  grid-template-columns: repeat(auto-fit, minmax(1ch, 1fr));
}

.colorScale > div {
  width: 100%;
  height: 100%;
}


.table {
  width: 100%;
  margin-bottom: 1rem;
  color: #212529;
  border-collapse: collapse;
  text-align: left;
}

.table th,
.table td {
  padding: 0.75rem;
  vertical-align: top;
  border-top: 1px solid #dee2e6;
}

.table thead th {
  vertical-align: bottom;
  border-bottom: 2px solid #dee2e6;
}

.table tbody + tbody {
  border-top: 2px solid #dee2e6;
}

.color-preview {
  vertical-align: middle;
  display: inline-block;
  width: 1em;
  height: 1em;
  border: 1px solid #1d1d1d;
  border-radius: 2px;
  user-select: none;
  margin-left: 0.3em;
}

.color-preview:hover {
  cursor: pointer;
}

.color-preview.show {
  border: 0;
  position: fixed;
  width: 100%;
  height: 100%;
  top: 0;
  left: 0;
  bottom: 0;
  right: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}
.color-preview.show:before {
  margin: 1em;
  content: 'click to close';
  font-weight: bolder;
  font-size: 1.5em;
  text-shadow: 0 0 4px #1d1d1d;
  color: white;
}

[data-color] {
  display: inline-flex;
  align-items: center;
}

.color-picker {
  background: linear-gradient(bottom, #000 0%, rgba(0, 0, 0, 0) 100%),
              linear-gradient(left, #FFF 0%, rgba(255, 255, 255, 0) 100%)
}

.sxs {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: 0.25em;
}

.tableContainer {
  max-height: 440px;
  overflow-y: auto;
}

.colorbox {
  padding: 2em;
  display: grid;
  gap: 2em;
  grid-template-columns: repeat(auto-fill, minmax(25%, 1fr));
}

.colorbox code {
}

.colorbox > code::before {
  content: "";
  display: inline-block;
  width: 1px;
  height: 0;
  padding-bottom: 100%;
}

.finalTable {
  font-size: 0.7rem;
}

.finalTable td:nth-child(n+8) {
  color: #f1f1f1;
}

</style>

<p>Let’s say you want to detect some <a href="https://en.wikipedia.org/wiki/Grayscale">grayscale</a> colors:</p>

<div style="display: flex; align-items: center">
<div><code>isGrayscale(</code></div>
<div class="colorScale" style="flex: 1">
    <div style="background:#FFFFFF"></div>
    <div style="background:#EEEEEE"></div>
    <div style="background:#DDDDDD"></div>
    <div style="background:#CCCCCC"></div>
    <div style="background:#BBBBBB"></div>
    <div style="background:#AAAAAA"></div>
    <div style="background:#999999"></div>
    <div style="background:#888888"></div>
    <div style="background:#777777"></div>
    <div style="background:#666666"></div>
    <div style="background:#555555"></div>
    <div style="background:#444444"></div>
    <div style="background:#333333"></div>
    <div style="background:#222222"></div>
    <div style="background:#111111"></div>
    <div style="background:#000000"></div>
</div>
<div><code>)</code></div>
</div>
<div>
<code>// returns true</code>
</div>

<p>A color is grayscale if the color’s <a href="https://en.wikipedia.org/wiki/RGB_color_model">red, green, blue (RGB)</a> component values are exactly equal.
A really common format for colors on the web is a 6 digit hexidecimal representation of the RGB component values:
<code style="background: #f1f1f1"># + <span style="color:red">RR</span><span style="color:green">GG</span><span style="color:blue">BB</span></code>.</p>

<ul style="display: grid; grid-template-columns: repeat(auto-fit, minmax(13em, 1fr));">
<li><code data-color="">#FFFFFF</code></li>
<li><code data-color="">#000000</code></li>
<li><code data-color="">#808080</code></li>
<li><code data-color="">#FF0000</code></li>
<li><code data-color="">#00FF00</code></li>
<li><code data-color="">#0000FF</code></li>
</ul>

<p>Knowing the above, detecting grayscale colors is simple:</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">isGrayscale</span><span class="p">(</span><span class="nx">color</span><span class="p">)</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="p">{</span> <span class="nx">r</span><span class="p">,</span> <span class="nx">g</span><span class="p">,</span> <span class="nx">b</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">parseColor</span><span class="p">(</span><span class="nx">color</span><span class="p">);</span>

  <span class="k">return</span> <span class="nx">r</span> <span class="o">===</span> <span class="nx">g</span> <span class="o">&amp;&amp;</span> <span class="nx">r</span> <span class="o">===</span> <span class="nx">b</span><span class="p">;</span>
<span class="p">}</span>

<span class="kd">function</span> <span class="nx">parseColor</span><span class="p">(</span><span class="nx">color</span><span class="p">)</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">match</span> <span class="o">=</span> <span class="nx">color</span><span class="p">.</span><span class="nx">match</span><span class="p">(</span><span class="sr">/#</span><span class="se">((?:[</span><span class="sr">0-9A-F</span><span class="se">]{1,2}){3})</span><span class="sr">/i</span><span class="p">);</span>
  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">match</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">throw</span> <span class="k">new</span> <span class="nb">Error</span><span class="p">(</span><span class="dl">'</span><span class="s1">invalid color format</span><span class="dl">'</span><span class="p">);</span>
  <span class="p">}</span>
  <span class="kd">const</span> <span class="p">[</span><span class="nx">r</span><span class="p">,</span> <span class="nx">g</span><span class="p">,</span> <span class="nx">b</span><span class="p">]</span> <span class="o">=</span> <span class="nx">match</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
    <span class="p">.</span><span class="nx">repeat</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
    <span class="p">.</span><span class="nx">slice</span><span class="p">(</span><span class="o">-</span><span class="mi">6</span><span class="p">)</span>
    <span class="p">.</span><span class="nx">match</span><span class="p">(</span><span class="sr">/.</span><span class="se">{2}</span><span class="sr">/g</span><span class="p">)</span>
    <span class="p">.</span><span class="nx">map</span><span class="p">(</span><span class="nx">hex</span> <span class="o">=&gt;</span> <span class="nb">parseInt</span><span class="p">(</span><span class="nx">hex</span><span class="p">,</span> <span class="mi">16</span><span class="p">));</span>
  <span class="k">return</span> <span class="p">{</span><span class="nx">r</span><span class="p">,</span> <span class="nx">g</span><span class="p">,</span> <span class="nx">b</span><span class="p">};</span>
<span class="p">}</span>
</code></pre></div></div>

<div style="overflow-x: auto">
<table class="table" style="width: inherit">
<tbody>
<tr><td><code>isGrayscale('<span data-color="">#FFFFFF</span>')</code></td><td>✅</td></tr>
<tr><td><code>isGrayscale('<span data-color="">#000000</span>')</code></td><td>✅</td></tr>
<tr><td><code>isGrayscale('<span data-color="">#808080</span>')</code></td><td>✅</td></tr>
<tr><td><code>isGrayscale('<span data-color="">#FF0000</span>')</code></td><td>❌</td></tr>
<tr><td><code>isGrayscale('<span data-color="">#00FF00</span>')</code></td><td>❌</td></tr>
<tr><td><code>isGrayscale('<span data-color="">#0000FF</span>')</code></td><td>❌</td></tr>
</tbody>
</table>
</div>

<p>Be aware that there are a <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/color_value">boatload of valid formats for web colors</a>.
For production use you are better off using a library like <a href="https://github.com/d3/d3-color"><code class="language-plaintext highlighter-rouge">d3-color</code></a> to parse untrusted color values.</p>

<p>Anyway, the above code works great! I guess we are done. Hope you learned something! I should blog more often.
See you next time!</p>

<p>Wait, what was that? You plugged some <a href="https://github.com/twbs/bootstrap/blob/e94795562b71ecbbe46c9050420f05fd2f602c39/scss/_variables.scss#L9-L17">Bootstrap 4 gray colors</a> into the function like
<code>isGrayscale('<span data-color="">#adb5bd</span>')</code> and you got <code class="language-plaintext highlighter-rouge">false</code>?
Well, I would indeed consider those Bootstrap grays to be “grayish” colors.
However, those colors are not strictly grayscale colors according to the definition we agreed
upon above. So yeah, it’s actually a <em>good</em> thing our function doesn’t match them. Closed as <code>wontfix</code>.</p>

<p>What? This blog post sucks? Okay, fine, there’s more we can do here. I’m no stranger to scope creep.</p>

<h2 id="classifying-colors-as-grayish">Classifying colors as “grayish”</h2>

<p>A color is generally considered “grayish”, neutral, achromatic if all of its
<a href="https://en.wikipedia.org/wiki/RGB_color_model">red, green, blue (RGB)</a> component values are a close distance to each other.
Makes sense: we know from earlier that a color is strictly grayscale if it has uniform RGB values.
A distance of zero is about as close as you can get.</p>

<p>Let’s define our test cases.
We can use those pesky <a href="https://github.com/twbs/bootstrap/blob/e94795562b71ecbbe46c9050420f05fd2f602c39/scss/_variables.scss#L9-L17">Bootstrap 4 gray colors</a> as colors we want to positively match.</p>

<ul style="display: grid; grid-template-columns: repeat(auto-fit, minmax(13em, 1fr));">
<li><code>gray-100: <span data-color="">#f8f9fa</span></code></li>
<li><code>gray-200: <span data-color="">#e9ecef</span></code></li>
<li><code>gray-300: <span data-color="">#dee2e6</span></code></li>
<li><code>gray-400: <span data-color="">#ced4da</span></code></li>
<li><code>gray-500: <span data-color="">#adb5bd</span></code></li>
<li><code>gray-600: <span data-color="">#6c757d</span></code></li>
<li><code>gray-700: <span data-color="">#495057</span></code></li>
<li><code>gray-800: <span data-color="">#343a40</span></code></li>
<li><code>gray-900: <span data-color="">#212529</span></code></li>
</ul>

<p>As a sanity check let’s use <a href="https://colorbrewer2.org/#type=sequential&amp;scheme=YlOrRd&amp;n=9">ColorBrewer 2</a> as colors we want to negatively match.</p>

<p>Let’s visualize the RGB values of our test cases and check if we see the pattern we expect.</p>

<table class="table">
<tbody>
<tr>
<th>True Grayscale</th>
<td>
<div class="colorScale" style="margin-bottom: 0.5em">
    <div style="background:#222222"></div>
    <div style="background:#333333"></div>
    <div style="background:#444444"></div>
    <div style="background:#666666"></div>
    <div style="background:#aaaaaa"></div>
    <div style="background:#cccccc"></div>
    <div style="background:#dddddd"></div>
    <div style="background:#eeeeee"></div>
    <div style="background:#ffffff"></div>
</div>

<svg viewBox="0 0 256 18" class="figure">
<!-- #fff -->
<circle cx="255" cy="1" r="1" fill="red" />
<circle cx="255" cy="1" r="1" fill="green" />
<circle cx="255" cy="1" r="1" fill="blue" />
<!-- #eee -->
<circle cx="238" cy="3" r="1" fill="red" />
<circle cx="238" cy="3" r="1" fill="green" />
<circle cx="238" cy="3" r="1" fill="blue" />
<!-- #ddd -->
<circle cx="221" cy="5" r="1" fill="red" />
<circle cx="221" cy="5" r="1" fill="green" />
<circle cx="221" cy="5" r="1" fill="blue" />
<!-- #ccc -->
<circle cx="204" cy="7" r="1" fill="red" />
<circle cx="204" cy="7" r="1" fill="green" />
<circle cx="204" cy="7" r="1" fill="blue" />
<!-- #aaa -->
<circle cx="170" cy="9" r="1" fill="red" />
<circle cx="170" cy="9" r="1" fill="green" />
<circle cx="170" cy="9" r="1" fill="blue" />
<!-- #666 -->
<circle cx="102" cy="11" r="1" fill="red" />
<circle cx="102" cy="11" r="1" fill="green" />
<circle cx="102" cy="11" r="1" fill="blue" />
<!-- #444 -->
<circle cx="68" cy="13" r="1" fill="red" />
<circle cx="68" cy="13" r="1" fill="green" />
<circle cx="68" cy="13" r="1" fill="blue" />
<!-- #333 -->
<circle cx="51" cy="15" r="1" fill="red" />
<circle cx="51" cy="15" r="1" fill="green" />
<circle cx="51" cy="15" r="1" fill="blue" />
<!-- #222 -->
<circle cx="34" cy="17" r="1" fill="red" />
<circle cx="34" cy="17" r="1" fill="green" />
<circle cx="34" cy="17" r="1" fill="blue" />
</svg>
</td>
</tr>




<tr>
<th>Bootstrap 4</th>
<td>
<div class="colorScale" style="margin-bottom: 0.5em">
<div style="background:#212529"></div>
<div style="background:#343a40"></div>
<div style="background:#495057"></div>
<div style="background:#6c757d"></div>
<div style="background:#adb5bd"></div>
<div style="background:#ced4da"></div>
<div style="background:#dee2e6"></div>
<div style="background:#e9ecef"></div>
<div style="background:#f8f9fa"></div>
</div>

<svg viewBox="0 0 255 18" class="figure">
<!-- #f8f9fa -->
<circle cx="248" cy="1" r="1" fill="red" />
<circle cx="249" cy="1" r="1" fill="green" />
<circle cx="250" cy="1" r="1" fill="blue" />
<!-- #e9ecef -->
<circle cx="233" cy="3" r="1" fill="red" />
<circle cx="236" cy="3" r="1" fill="green" />
<circle cx="239" cy="3" r="1" fill="blue" />
<!-- #dee2e6 -->
<circle cx="222" cy="5" r="1" fill="red" />
<circle cx="226" cy="5" r="1" fill="green" />
<circle cx="230" cy="5" r="1" fill="blue" />
<!-- #ced4da -->
<circle cx="206" cy="7" r="1" fill="red" />
<circle cx="212" cy="7" r="1" fill="green" />
<circle cx="218" cy="7" r="1" fill="blue" />
<!-- #adb5bd -->
<circle cx="173" cy="9" r="1" fill="red" />
<circle cx="181" cy="9" r="1" fill="green" />
<circle cx="189" cy="9" r="1" fill="blue" />
<!-- #6c757d -->
<circle cx="108" cy="11" r="1" fill="red" />
<circle cx="117" cy="11" r="1" fill="green" />
<circle cx="125" cy="11" r="1" fill="blue" />
<!-- #495057 -->
<circle cx="73" cy="13" r="1" fill="red" />
<circle cx="80" cy="13" r="1" fill="green" />
<circle cx="87" cy="13" r="1" fill="blue" />
<!-- #343a40 -->
<circle cx="52" cy="15" r="1" fill="red" />
<circle cx="58" cy="15" r="1" fill="green" />
<circle cx="64" cy="15" r="1" fill="blue" />
<!-- #212529 -->
<circle cx="33" cy="17" r="1" fill="red" />
<circle cx="37" cy="17" r="1" fill="green" />
<circle cx="41" cy="17" r="1" fill="blue" />
</svg>
</td>
</tr>



<tr>
<th>ColorBrewer 2</th>
<td>
<div class="colorScale" style="margin-bottom: 0.5em">
<div style="background:rgb(128,0,38)"></div>
<div style="background:rgb(189,0,38)"></div>
<div style="background:rgb(227,26,28)"></div>
<div style="background:rgb(252,78,42)"></div>
<div style="background:rgb(253,141,60)"></div>
<div style="background:rgb(254,178,76)"></div>
<div style="background:rgb(254,217,118)"></div>
<div style="background:rgb(255,237,160)"></div>
<div style="background:rgb(255,255,204)"></div>
</div>

<svg viewBox="0 0 257 18" class="figure">

<circle cx="255" cy="1" r="1" fill="red" />
<circle cx="255" cy="1" r="1" fill="green" />
<circle cx="204" cy="1" r="1" fill="blue" />

<circle cx="255" cy="3" r="1" fill="red" />
<circle cx="237" cy="3" r="1" fill="green" />
<circle cx="160" cy="3" r="1" fill="blue" />

<circle cx="254" cy="5" r="1" fill="red" />
<circle cx="217" cy="5" r="1" fill="green" />
<circle cx="118" cy="5" r="1" fill="blue" />

<circle cx="254" cy="7" r="1" fill="red" />
<circle cx="178" cy="7" r="1" fill="green" />
<circle cx="76" cy="7" r="1" fill="blue" />

<circle cx="253" cy="9" r="1" fill="red" />
<circle cx="141" cy="9" r="1" fill="green" />
<circle cx="60" cy="9" r="1" fill="blue" />

<circle cx="252" cy="11" r="1" fill="red" />
<circle cx="78" cy="11" r="1" fill="green" />
<circle cx="42" cy="11" r="1" fill="blue" />

<circle cx="227" cy="13" r="1" fill="red" />
<circle cx="26" cy="13" r="1" fill="green" />
<circle cx="28" cy="13" r="1" fill="blue" />

<circle cx="189" cy="15" r="1" fill="red" />
<circle cx="0" cy="15" r="1" fill="green" />
<circle cx="38" cy="15" r="1" fill="blue" />

<circle cx="128" cy="17" r="1" fill="red" />
<circle cx="0" cy="17" r="1" fill="green" />
<circle cx="38" cy="17" r="1" fill="blue" />

</svg>
</td>
</tr>

</tbody>
</table>

<!--
#f8f9fa
#e9ecef
#dee2e6
#ced4da
#adb5bd
#6c757d
#495057
#343a40
#212529
-->

<p>Okay, so we can clearly see the pattern we expect. The colors we want to match have equidistant RGB components.
The ColorBrewer colors are off doing their own thing because they are clearly not gray colors.</p>

<p>If we want to classify these colors in code: we need a way to measure the degree to which the RGB components are close to each other.
The <a href="https://en.wikipedia.org/wiki/Range_(statistics)">range</a> of the RGB components should measure this nicely.</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">RANGE_LIMIT</span> <span class="o">=</span> <span class="mi">17</span><span class="p">;</span>

<span class="kd">function</span> <span class="nx">isGray</span><span class="p">(</span><span class="nx">color</span><span class="p">)</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="p">{</span> <span class="nx">r</span><span class="p">,</span> <span class="nx">g</span><span class="p">,</span> <span class="nx">b</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">parseColor</span><span class="p">(</span><span class="nx">color</span><span class="p">);</span>
  <span class="kd">const</span> <span class="nx">rgb</span> <span class="o">=</span> <span class="p">[</span><span class="nx">r</span><span class="p">,</span> <span class="nx">g</span><span class="p">,</span> <span class="nx">b</span><span class="p">];</span>
  <span class="k">return</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">max</span><span class="p">(...</span><span class="nx">rgb</span><span class="p">)</span> <span class="o">-</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">min</span><span class="p">(...</span><span class="nx">rgb</span><span class="p">)</span> <span class="o">&lt;=</span> <span class="nx">RANGE_LIMIT</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<details>
<summary>Where do we get <code>RANGE_LIMIT</code>?</summary>
<table class="table">
<thead><tr><th></th><th>red</th><th>green</th><th>blue</th><th>range</th></tr></thead>
<tbody>
 <tr><td><span data-color="">#f8f9fa</span></td><td>248</td><td>249</td><td>250</td><td>2</td></tr>
 <tr><td><span data-color="">#e9ecef</span></td><td>233</td><td>236</td><td>239</td><td>6</td></tr>
 <tr><td><span data-color="">#dee2e6</span></td><td>222</td><td>226</td><td>230</td><td>8</td></tr>
 <tr><td><span data-color="">#ced4da</span></td><td>206</td><td>212</td><td>218</td><td>12</td></tr>
 <tr><td><span data-color="">#adb5bd</span></td><td>173</td><td>181</td><td>189</td><td>16</td></tr>
 <tr><td><span data-color="">#6c757d</span></td><td>108</td><td>117</td><td>125</td><td><mark>17</mark></td></tr>
 <tr><td><span data-color="">#495057</span></td><td>73</td><td>80</td><td>87</td>   <td>14</td></tr>
 <tr><td><span data-color="">#343a40</span></td><td>52</td><td>58</td><td>64</td>   <td>12</td></tr>
 <tr><td><span data-color="">#212529</span></td><td>33</td><td>37</td><td>41</td>   <td>8</td></tr>
</tbody></table>
</details>

<p>This matches the original desired Bootstrap colors:</p>

<div class="colorScale">
<div style="background:rgb(248, 249, 250)"></div>
<div style="background:rgb(233, 236, 239)"></div>
<div style="background:rgb(222, 226, 230)"></div>
<div style="background:rgb(206, 212, 218)"></div>
<div style="background:rgb(173, 181, 189)"></div>
<div style="background:rgb(108, 117, 125)"></div>
<div style="background:rgb(73, 80, 87)"></div>
<div style="background:rgb(52, 58, 64)"></div>
<div style="background:rgb(33, 37, 41)"></div>
</div>

<p>Similar “grayish” colors will be matched as well:</p>

<div class="colorScale">
<div style="background:rgb(248, 248, 250)"></div>
<div style="background:rgb(233, 233, 239)"></div>
<div style="background:rgb(222, 222, 230)"></div>
<div style="background:rgb(206, 206, 218)"></div>
<div style="background:rgb(173, 173, 189)"></div>
<div style="background:rgb(108, 108, 125)"></div>
<div style="background:rgb(73, 73, 87)"></div>
<div style="background:rgb(52, 52, 64)"></div>
<div style="background:rgb(33, 33, 41)"></div>
</div>
<div class="colorScale">
<div style="background:rgb(248, 249, 249)"></div>
<div style="background:rgb(233, 236, 236)"></div>
<div style="background:rgb(222, 226, 226)"></div>
<div style="background:rgb(206, 212, 212)"></div>
<div style="background:rgb(173, 181, 181)"></div>
<div style="background:rgb(108, 117, 117)"></div>
<div style="background:rgb(73, 80, 80)"></div>
<div style="background:rgb(52, 58, 58)"></div>
<div style="background:rgb(33, 37, 37)"></div>
</div>
<div class="colorScale">
<div style="background:rgb(248,249,250)"></div>
<div style="background:rgb(235,236,235)"></div>
<div style="background:rgb(225,226,225)"></div>
<div style="background:rgb(211,212,211)"></div>
<div style="background:rgb(180,181,180)"></div>
<div style="background:rgb(116,117,116)"></div>
<div style="background:rgb(79,80,79)"></div>
<div style="background:rgb(57,58,57)"></div>
<div style="background:rgb(36,37,36)"></div>
</div>
<div class="colorScale">
    <div style="background:#ffffff"></div>
    <div style="background:#eeeeee"></div>
    <div style="background:#dddddd"></div>
    <div style="background:#cccccc"></div>
    <div style="background:#aaaaaa"></div>
    <div style="background:#666666"></div>
    <div style="background:#444444"></div>
    <div style="background:#333333"></div>
    <div style="background:#222222"></div>
</div>

<p>Great! Looks good to me so far.
Now, how can we exhaustively prove that this function matches only the colors we want?
The best way to spot check our function is probably to visualize what it considers to be “grayish”.</p>

<h2 id="checking-our-work">Checking our work</h2>

<p><a href="https://en.wikipedia.org/wiki/RGB_color_model">RGB</a> is a nice and simple color model that is easy for machines to deal with.
However, for our purposes here it would be nice to use a human-friendly color model.
I will choose the <a href="https://en.wikipedia.org/wiki/HSL_and_HSV">hue, saturation, value (HSV)</a> color model to make a simple 2D plot of the colors matched by our function.</p>

<p><a href="https://en.wikipedia.org/wiki/HSL_and_HSV">HSV</a> has some useful geometry for detecting and visualizing grayish colors.
The HSV color model can be represented as a cylinder where the central vertical axis comprises 
neutral, achromatic, or gray colors. Those colors range, from top to bottom, white at value 1 and black at value 0.</p>

<blockquote>
<img src="/assets/960px-HSV_color_solid_cylinder_saturation_gray.png" alt="hsv cylinder" class="figure" />
By HSV_color_solid_cylinder.png: SharkDderivative work: SharkD  Talk - HSV_color_solid_cylinder.png, CC BY-SA 3.0,
<a href="https://commons.wikimedia.org/w/index.php?curid=9801673">wiki</a>
</blockquote>

<p>I’m not smart enough to do math in 3D: so let’s just chart 2D slices of this cylinder at different hues and see how far we get.
We are mostly concerned with the interaction between saturation and value right now anyway.
Let’s fix the hue to
<code data-color="hsl(210 100% 50%)">210°</code>.
since all those Bootstrap grays tend to hover around that hue.</p>

<figure>
<div class="sxs">

  <svg viewBox="0 0 1 1" class="figure">
  <defs>
  <linearGradient id="value" x2="0%" y2="0%" x1="0%" y1="100%">
  <stop offset="0%" stop-color="rgba(0, 0, 0, 1)" />
  <stop offset="100%" stop-color="rgba(0, 0, 0, 0)" />
  </linearGradient>
  <linearGradient id="saturation">
  <stop offset="0%" stop-color="rgba(255, 255, 255, 1)" />
  <stop offset="100%" stop-color="rgba(255, 255, 255, 0)" />
  </linearGradient>
  </defs>

  <rect id="fillBg" width="100%" height="100%" fill="rgb(0, 119, 255)" />
  <rect width="100%" height="100%" fill="url('#saturation')" />
  <rect width="100%" height="100%" fill="url('#value')" />
    <circle cx="0.007999999999999972" cy="0.019607843137254943" r="1%" stroke-width="1%" stroke="red" fill="transparent" />
    <circle cx="0.025104602510460282" cy="0.06274509803921569" r="1%" stroke-width="1%" stroke="red" fill="transparent" />
    <circle cx="0.034782608695652174" cy="0.0980392156862745" r="1%" stroke-width="1%" stroke="red" fill="transparent" />
    <circle cx="0.05504587155963296" cy="0.14509803921568631" r="1%" stroke-width="1%" stroke="red" fill="transparent" />
    <circle cx="0.08" cy="0.2588235294117647" r="1%" stroke-width="1%" stroke="red" fill="transparent" />
    <circle cx="0.13" cy="0.5098039215686274" r="1%" stroke-width="1%" stroke="red" fill="transparent" />
    <circle cx="0.16091954022988514" cy="0.6588235294117647" r="1%" stroke-width="1%" stroke="red" fill="transparent" />
    <circle cx="0.1875" cy="0.7490196078431373" r="1%" stroke-width="1%" stroke="red" fill="transparent" />
    <circle cx="0.19512195121951217" cy="0.8392156862745098" r="1%" stroke-width="1%" stroke="red" fill="transparent" />

  <polyline points="0.05882352941176472,0 0.06746031746031755,0.0117647058823529 0.06000000000000002,0.019607843137254943 0.06882591093117407,0.03137254901960784 0.0693877551020408,0.039215686274509776 0.07024793388429751,0.050980392156862786 0.07083333333333332,0.05882352941176472 0.06751054852320675,0.07058823529411762 0.07234042553191489,0.07843137254901966 0.06896551724137931,0.09019607843137256 0.07391304347826085,0.0980392156862745 0.07048458149779736,0.1098039215686275 0.06696428571428573,0.1215686274509804 0.07207207207207207,0.12941176470588234 0.07762557077625569,0.14117647058823535 0.06912442396313366,0.14901960784313728 0.07943925233644858,0.16078431372549018 0.08018867924528313,0.1686274509803921 0.08133971291866027,0.18039215686274512 0.08212560386473428,0.18823529411764706 0.0784313725490196,0.19999999999999996 0.07960199004975124,0.21176470588235297 0.08040201005025126,0.2196078431372549 0.08673469387755114,0.2313725490196078 0.08247422680412371,0.23921568627450984 0.08900523560209422,0.25098039215686274 0.08994708994708993,0.2588235294117647 0.09139784946236558,0.2705882352941177 0.09239130434782607,0.2784313725490196 0.08839779005524862,0.2901960784313725 0.09497206703910613,0.29803921568627456 0.09090909090909091,0.30980392156862746 0.0982658959537572,0.32156862745098036 0.09941520467836255,0.3294117647058824 0.10119047619047616,0.3411764705882353 0.10240963855421684,0.34901960784313724 0.09815950920245399,0.36078431372549025 0.09937888198757765,0.3686274509803922 0.10759493670886074,0.3803921568627451 0.10256410256410256,0.388235294117647 0.11111111111111109,0.4 0.10666666666666666,0.4117647058823529 0.1081081081081081,0.41960784313725485 0.11724137931034481,0.43137254901960786 0.11888111888111885,0.4392156862745098 0.1214285714285715,0.4509803921568627 0.12318840579710143,0.45882352941176474 0.12592592592592589,0.47058823529411764 0.12030075187969934,0.4784313725490196 0.13076923076923075,0.4901960784313726 0.13281249999999997,0.4980392156862745 0.128,0.5098039215686274 0.13934426229508204,0.5215686274509803 0.14166666666666664,0.5294117647058824 0.14529914529914528,0.5411764705882354 0.1478260869565217,0.5490196078431373 0.15178571428571425,0.5607843137254902 0.15454545454545462,0.5686274509803921 0.15887850467289716,0.580392156862745 0.16190476190476188,0.5882352941176471 0.16666666666666677,0.6 0.17171717171717168,0.611764705882353 0.17525773195876285,0.6196078431372549 0.18085106382978733,0.6313725490196078 0.18478260869565213,0.6392156862745098 0.19101123595505615,0.6509803921568628 0.19540229885057467,0.6588235294117647 0.20238095238095233,0.6705882352941177 0.2073170731707318,0.6784313725490196 0.21518987341772156,0.6901960784313725 0.22077922077922074,0.6980392156862745 0.22972972972972974,0.7098039215686274 0.2394366197183099,0.7215686274509804 0.24637681159420285,0.7294117647058824 0.2575757575757576,0.7411764705882353 0.26562499999999994,0.7490196078431373 0.27868852459016397,0.7607843137254902 0.28813559322033905,0.7686274509803921 0.3035714285714285,0.7803921568627451 0.31481481481481477,0.788235294117647 0.33333333333333337,0.8 0.3541666666666667,0.8117647058823529 0.3695652173913043,0.8196078431372549 0.39534883720930236,0.8313725490196078 0.41463414634146345,0.8392156862745098 0.4473684210526316,0.8509803921568627 0.4722222222222222,0.8588235294117648 0.5151515151515152,0.8705882352941177 0.5483870967741935,0.8784313725490196 0.6071428571428571,0.8901960784313725 0.6538461538461539,0.8980392156862745 0.7391304347826086,0.9098039215686274 0.85,0.9215686274509804 0.9444444444444444,0.9294117647058824 1,0.9411764705882353 1,0.9490196078431372 1,0.9607843137254902 1,0.9686274509803922 1,0.9803921568627451 1,0.9882352941176471" fill="none" stroke-width="1%" stroke="blue" />
  </svg>


  <div class="tableContainer">
  
  <table class="table">
    <thead>
      <tr><th>hex</th><th>saturation</th><th>value</th></tr>
    </thead>
  <tbody>
  <tr>
    <td><code data-color="">#f0f7ff</code></td>
    <td>6</td>
    <td>100</td>
  </tr>
<tr>
    <td><code data-color="">#ebf4fc</code></td>
    <td>7</td>
    <td>99</td>
  </tr>
<tr>
    <td><code data-color="">#ebf2fa</code></td>
    <td>6</td>
    <td>98</td>
  </tr>
<tr>
    <td><code data-color="">#e6eff7</code></td>
    <td>7</td>
    <td>97</td>
  </tr>
<tr>
    <td><code data-color="">#e4ecf5</code></td>
    <td>7</td>
    <td>96</td>
  </tr>
<tr>
    <td><code data-color="">#e1eaf2</code></td>
    <td>7</td>
    <td>95</td>
  </tr>
<tr>
    <td><code data-color="">#dfe7f0</code></td>
    <td>7</td>
    <td>94</td>
  </tr>
<tr>
    <td><code data-color="">#dde5ed</code></td>
    <td>7</td>
    <td>93</td>
  </tr>
<tr>
    <td><code data-color="">#dae2eb</code></td>
    <td>7</td>
    <td>92</td>
  </tr>
<tr>
    <td><code data-color="">#d8e0e8</code></td>
    <td>7</td>
    <td>91</td>
  </tr>
<tr>
    <td><code data-color="">#d5dde6</code></td>
    <td>7</td>
    <td>90</td>
  </tr>
<tr>
    <td><code data-color="">#d3dbe3</code></td>
    <td>7</td>
    <td>89</td>
  </tr>
<tr>
    <td><code data-color="">#d1d9e0</code></td>
    <td>7</td>
    <td>88</td>
  </tr>
<tr>
    <td><code data-color="">#ced6de</code></td>
    <td>7</td>
    <td>87</td>
  </tr>
<tr>
    <td><code data-color="">#cad3db</code></td>
    <td>8</td>
    <td>86</td>
  </tr>
<tr>
    <td><code data-color="">#cad1d9</code></td>
    <td>7</td>
    <td>85</td>
  </tr>
<tr>
    <td><code data-color="">#c5ced6</code></td>
    <td>8</td>
    <td>84</td>
  </tr>
<tr>
    <td><code data-color="">#c3cbd4</code></td>
    <td>8</td>
    <td>83</td>
  </tr>
<tr>
    <td><code data-color="">#c0c9d1</code></td>
    <td>8</td>
    <td>82</td>
  </tr>
<tr>
    <td><code data-color="">#bec6cf</code></td>
    <td>8</td>
    <td>81</td>
  </tr>
<tr>
    <td><code data-color="">#bcc4cc</code></td>
    <td>8</td>
    <td>80</td>
  </tr>
<tr>
    <td><code data-color="">#b9c1c9</code></td>
    <td>8</td>
    <td>79</td>
  </tr>
<tr>
    <td><code data-color="">#b7bfc7</code></td>
    <td>8</td>
    <td>78</td>
  </tr>
<tr>
    <td><code data-color="">#b3bcc4</code></td>
    <td>9</td>
    <td>77</td>
  </tr>
<tr>
    <td><code data-color="">#b2bac2</code></td>
    <td>8</td>
    <td>76</td>
  </tr>
<tr>
    <td><code data-color="">#aeb7bf</code></td>
    <td>9</td>
    <td>75</td>
  </tr>
<tr>
    <td><code data-color="">#acb4bd</code></td>
    <td>9</td>
    <td>74</td>
  </tr>
<tr>
    <td><code data-color="">#a9b2ba</code></td>
    <td>9</td>
    <td>73</td>
  </tr>
<tr>
    <td><code data-color="">#a7afb8</code></td>
    <td>9</td>
    <td>72</td>
  </tr>
<tr>
    <td><code data-color="">#a5adb5</code></td>
    <td>9</td>
    <td>71</td>
  </tr>
<tr>
    <td><code data-color="">#a2aab3</code></td>
    <td>9</td>
    <td>70</td>
  </tr>
<tr>
    <td><code data-color="">#a0a8b0</code></td>
    <td>9</td>
    <td>69</td>
  </tr>
<tr>
    <td><code data-color="">#9ca5ad</code></td>
    <td>10</td>
    <td>68</td>
  </tr>
<tr>
    <td><code data-color="">#9aa2ab</code></td>
    <td>10</td>
    <td>67</td>
  </tr>
<tr>
    <td><code data-color="">#97a0a8</code></td>
    <td>10</td>
    <td>66</td>
  </tr>
<tr>
    <td><code data-color="">#959da6</code></td>
    <td>10</td>
    <td>65</td>
  </tr>
<tr>
    <td><code data-color="">#939ba3</code></td>
    <td>10</td>
    <td>64</td>
  </tr>
<tr>
    <td><code data-color="">#9199a1</code></td>
    <td>10</td>
    <td>63</td>
  </tr>
<tr>
    <td><code data-color="">#8d959e</code></td>
    <td>11</td>
    <td>62</td>
  </tr>
<tr>
    <td><code data-color="">#8c949c</code></td>
    <td>10</td>
    <td>61</td>
  </tr>
<tr>
    <td><code data-color="">#889199</code></td>
    <td>11</td>
    <td>60</td>
  </tr>
<tr>
    <td><code data-color="">#868e96</code></td>
    <td>11</td>
    <td>59</td>
  </tr>
<tr>
    <td><code data-color="">#848c94</code></td>
    <td>11</td>
    <td>58</td>
  </tr>
<tr>
    <td><code data-color="">#808991</code></td>
    <td>12</td>
    <td>57</td>
  </tr>
<tr>
    <td><code data-color="">#7e868f</code></td>
    <td>12</td>
    <td>56</td>
  </tr>
<tr>
    <td><code data-color="">#7b848c</code></td>
    <td>12</td>
    <td>55</td>
  </tr>
<tr>
    <td><code data-color="">#79818a</code></td>
    <td>12</td>
    <td>54</td>
  </tr>
<tr>
    <td><code data-color="">#767e87</code></td>
    <td>13</td>
    <td>53</td>
  </tr>
<tr>
    <td><code data-color="">#757d85</code></td>
    <td>12</td>
    <td>52</td>
  </tr>
<tr>
    <td><code data-color="">#717a82</code></td>
    <td>13</td>
    <td>51</td>
  </tr>
<tr>
    <td><code data-color="">#6f7780</code></td>
    <td>13</td>
    <td>50</td>
  </tr>
<tr>
    <td><code data-color="">#6d757d</code></td>
    <td>13</td>
    <td>49</td>
  </tr>
<tr>
    <td><code data-color="">#69727a</code></td>
    <td>14</td>
    <td>48</td>
  </tr>
<tr>
    <td><code data-color="">#676f78</code></td>
    <td>14</td>
    <td>47</td>
  </tr>
<tr>
    <td><code data-color="">#646d75</code></td>
    <td>15</td>
    <td>46</td>
  </tr>
<tr>
    <td><code data-color="">#626a73</code></td>
    <td>15</td>
    <td>45</td>
  </tr>
<tr>
    <td><code data-color="">#5f6870</code></td>
    <td>15</td>
    <td>44</td>
  </tr>
<tr>
    <td><code data-color="">#5d656e</code></td>
    <td>15</td>
    <td>43</td>
  </tr>
<tr>
    <td><code data-color="">#5a636b</code></td>
    <td>16</td>
    <td>42</td>
  </tr>
<tr>
    <td><code data-color="">#586069</code></td>
    <td>16</td>
    <td>41</td>
  </tr>
<tr>
    <td><code data-color="">#555d66</code></td>
    <td>17</td>
    <td>40</td>
  </tr>
<tr>
    <td><code data-color="">#525a63</code></td>
    <td>17</td>
    <td>39</td>
  </tr>
<tr>
    <td><code data-color="">#505961</code></td>
    <td>18</td>
    <td>38</td>
  </tr>
<tr>
    <td><code data-color="">#4d565e</code></td>
    <td>18</td>
    <td>37</td>
  </tr>
<tr>
    <td><code data-color="">#4b545c</code></td>
    <td>18</td>
    <td>36</td>
  </tr>
<tr>
    <td><code data-color="">#485159</code></td>
    <td>19</td>
    <td>35</td>
  </tr>
<tr>
    <td><code data-color="">#464e57</code></td>
    <td>20</td>
    <td>34</td>
  </tr>
<tr>
    <td><code data-color="">#434c54</code></td>
    <td>20</td>
    <td>33</td>
  </tr>
<tr>
    <td><code data-color="">#414952</code></td>
    <td>21</td>
    <td>32</td>
  </tr>
<tr>
    <td><code data-color="">#3e464f</code></td>
    <td>22</td>
    <td>31</td>
  </tr>
<tr>
    <td><code data-color="">#3c444d</code></td>
    <td>22</td>
    <td>30</td>
  </tr>
<tr>
    <td><code data-color="">#39414a</code></td>
    <td>23</td>
    <td>29</td>
  </tr>
<tr>
    <td><code data-color="">#363e47</code></td>
    <td>24</td>
    <td>28</td>
  </tr>
<tr>
    <td><code data-color="">#343c45</code></td>
    <td>25</td>
    <td>27</td>
  </tr>
<tr>
    <td><code data-color="">#313a42</code></td>
    <td>26</td>
    <td>26</td>
  </tr>
<tr>
    <td><code data-color="">#2f3740</code></td>
    <td>27</td>
    <td>25</td>
  </tr>
<tr>
    <td><code data-color="">#2c353d</code></td>
    <td>28</td>
    <td>24</td>
  </tr>
<tr>
    <td><code data-color="">#2a323b</code></td>
    <td>29</td>
    <td>23</td>
  </tr>
<tr>
    <td><code data-color="">#272f38</code></td>
    <td>30</td>
    <td>22</td>
  </tr>
<tr>
    <td><code data-color="">#252d36</code></td>
    <td>31</td>
    <td>21</td>
  </tr>
<tr>
    <td><code data-color="">#222a33</code></td>
    <td>33</td>
    <td>20</td>
  </tr>
<tr>
    <td><code data-color="">#1f2730</code></td>
    <td>35</td>
    <td>19</td>
  </tr>
<tr>
    <td><code data-color="">#1d252e</code></td>
    <td>37</td>
    <td>18</td>
  </tr>
<tr>
    <td><code data-color="">#1a222b</code></td>
    <td>40</td>
    <td>17</td>
  </tr>
<tr>
    <td><code data-color="">#182029</code></td>
    <td>41</td>
    <td>16</td>
  </tr>
<tr>
    <td><code data-color="">#151d26</code></td>
    <td>45</td>
    <td>15</td>
  </tr>
<tr>
    <td><code data-color="">#131b24</code></td>
    <td>47</td>
    <td>14</td>
  </tr>
<tr>
    <td><code data-color="">#101821</code></td>
    <td>52</td>
    <td>13</td>
  </tr>
<tr>
    <td><code data-color="">#0e161f</code></td>
    <td>55</td>
    <td>12</td>
  </tr>
<tr>
    <td><code data-color="">#0b131c</code></td>
    <td>61</td>
    <td>11</td>
  </tr>
<tr>
    <td><code data-color="">#09111a</code></td>
    <td>65</td>
    <td>10</td>
  </tr>
<tr>
    <td><code data-color="">#060e17</code></td>
    <td>74</td>
    <td>9</td>
  </tr>
<tr>
    <td><code data-color="">#030c14</code></td>
    <td>85</td>
    <td>8</td>
  </tr>
<tr>
    <td><code data-color="">#010912</code></td>
    <td>94</td>
    <td>7</td>
  </tr>
<tr>
    <td><code data-color="">#00080f</code></td>
    <td>100</td>
    <td>6</td>
  </tr>
<tr>
    <td><code data-color="">#00060d</code></td>
    <td>100</td>
    <td>5</td>
  </tr>
<tr>
    <td><code data-color="">#00050a</code></td>
    <td>100</td>
    <td>4</td>
  </tr>
<tr>
    <td><code data-color="">#000408</code></td>
    <td>100</td>
    <td>3</td>
  </tr>
<tr>
    <td><code data-color="">#000305</code></td>
    <td>100</td>
    <td>2</td>
  </tr>
<tr>
    <td><code data-color="">#000103</code></td>
    <td>100</td>
    <td>1</td>
  </tr>
  </tbody>
  </table>
  
  </div>
  

</div>
<figcaption>
The <span data-color="">blue</span> line is the maximum color matched by <code>isGray</code>
when it is fed colors with a hue fixed at
<code data-color="hsl(210 100% 50%)">210°</code>.
Colors below this line will be matched.
Our target Bootstrap colors are plotted in <span data-color="">red</span>.
</figcaption>
</figure>

<div class="colorScale" style="margin-bottom: 0.5em">
<div style="background:#f0f7ff"></div>
<div style="background:#ebf2fa"></div>
<div style="background:#dfe7f0"></div>
<div style="background:#ced6de"></div>
<div style="background:#a0a8b0"></div>
<div style="background:#5d656e"></div>
<div style="background:#414952"></div>
<div style="background:#2a323b"></div>
<div style="background:#010912"></div>
</div>

<p>Great! Not perfect, but it’s definitely clear to me that we are on the right track.</p>

<p>Let’s check another hue to see what our function matches there. Let’s check the
<code data-color="hsl(120 100% 50%)">120°</code> hue.</p>

<figure>
<div class="sxs">

  <svg viewBox="0 0 1 1" class="figure">
  <defs>
  <linearGradient id="value" x2="0%" y2="0%" x1="0%" y1="100%">
  <stop offset="0%" stop-color="rgba(0, 0, 0, 1)" />
  <stop offset="100%" stop-color="rgba(0, 0, 0, 0)" />
  </linearGradient>
  <linearGradient id="saturation">
  <stop offset="0%" stop-color="rgba(255, 255, 255, 1)" />
  <stop offset="100%" stop-color="rgba(255, 255, 255, 0)" />
  </linearGradient>
  </defs>

  <rect id="fillBg" width="100%" height="100%" fill="rgb(0, 255, 0)" />
  <rect width="100%" height="100%" fill="url('#saturation')" />
  <rect width="100%" height="100%" fill="url('#value')" />
  
  <polyline points="0.05882352941176472,0 0.06746031746031755,0.0117647058823529 0.06000000000000002,0.019607843137254943 0.06882591093117407,0.03137254901960784 0.0693877551020408,0.039215686274509776 0.07024793388429751,0.050980392156862786 0.07083333333333332,0.05882352941176472 0.06751054852320675,0.07058823529411762 0.07234042553191489,0.07843137254901966 0.06896551724137931,0.09019607843137256 0.07391304347826085,0.0980392156862745 0.07048458149779736,0.1098039215686275 0.06696428571428573,0.1215686274509804 0.07207207207207207,0.12941176470588234 0.07762557077625569,0.14117647058823535 0.06912442396313366,0.14901960784313728 0.07943925233644858,0.16078431372549018 0.08018867924528313,0.1686274509803921 0.08133971291866027,0.18039215686274512 0.08212560386473428,0.18823529411764706 0.0784313725490196,0.19999999999999996 0.07960199004975124,0.21176470588235297 0.08040201005025126,0.2196078431372549 0.08673469387755114,0.2313725490196078 0.08247422680412371,0.23921568627450984 0.08900523560209422,0.25098039215686274 0.08994708994708993,0.2588235294117647 0.09139784946236558,0.2705882352941177 0.09239130434782607,0.2784313725490196 0.08839779005524862,0.2901960784313725 0.09497206703910613,0.29803921568627456 0.09090909090909091,0.30980392156862746 0.0982658959537572,0.32156862745098036 0.09941520467836255,0.3294117647058824 0.10119047619047616,0.3411764705882353 0.10240963855421684,0.34901960784313724 0.09815950920245399,0.36078431372549025 0.09937888198757765,0.3686274509803922 0.10759493670886074,0.3803921568627451 0.10256410256410256,0.388235294117647 0.11111111111111109,0.4 0.10666666666666666,0.4117647058823529 0.1081081081081081,0.41960784313725485 0.11724137931034481,0.43137254901960786 0.11888111888111885,0.4392156862745098 0.1214285714285715,0.4509803921568627 0.12318840579710143,0.45882352941176474 0.12592592592592589,0.47058823529411764 0.12030075187969934,0.4784313725490196 0.13076923076923075,0.4901960784313726 0.13281249999999997,0.4980392156862745 0.128,0.5098039215686274 0.13934426229508204,0.5215686274509803 0.14166666666666664,0.5294117647058824 0.14529914529914528,0.5411764705882354 0.1478260869565217,0.5490196078431373 0.15178571428571425,0.5607843137254902 0.15454545454545462,0.5686274509803921 0.15887850467289716,0.580392156862745 0.16190476190476188,0.5882352941176471 0.16666666666666677,0.6 0.17171717171717168,0.611764705882353 0.17525773195876285,0.6196078431372549 0.18085106382978733,0.6313725490196078 0.18478260869565213,0.6392156862745098 0.19101123595505615,0.6509803921568628 0.19540229885057467,0.6588235294117647 0.20238095238095233,0.6705882352941177 0.2073170731707318,0.6784313725490196 0.21518987341772156,0.6901960784313725 0.22077922077922074,0.6980392156862745 0.22972972972972974,0.7098039215686274 0.2394366197183099,0.7215686274509804 0.24637681159420285,0.7294117647058824 0.2575757575757576,0.7411764705882353 0.26562499999999994,0.7490196078431373 0.27868852459016397,0.7607843137254902 0.28813559322033905,0.7686274509803921 0.3035714285714285,0.7803921568627451 0.31481481481481477,0.788235294117647 0.33333333333333337,0.8 0.3541666666666667,0.8117647058823529 0.3695652173913043,0.8196078431372549 0.39534883720930236,0.8313725490196078 0.41463414634146345,0.8392156862745098 0.4473684210526316,0.8509803921568627 0.4722222222222222,0.8588235294117648 0.5151515151515152,0.8705882352941177 0.5483870967741935,0.8784313725490196 0.6071428571428571,0.8901960784313725 0.6538461538461539,0.8980392156862745 0.7391304347826086,0.9098039215686274 0.85,0.9215686274509804 0.9444444444444444,0.9294117647058824 1,0.9411764705882353 1,0.9490196078431372 1,0.9607843137254902 1,0.9686274509803922 1,0.9803921568627451 1,0.9882352941176471" fill="none" stroke-width="1%" stroke="blue" />
  </svg>


  <div class="tableContainer">
  
  <table class="table">
    <thead>
      <tr><th>hex</th><th>saturation</th><th>value</th></tr>
    </thead>
  <tbody>
  <tr>
    <td><code data-color="">#f0fff0</code></td>
    <td>6</td>
    <td>100</td>
  </tr>
<tr>
    <td><code data-color="">#ebfceb</code></td>
    <td>7</td>
    <td>99</td>
  </tr>
<tr>
    <td><code data-color="">#ebfaeb</code></td>
    <td>6</td>
    <td>98</td>
  </tr>
<tr>
    <td><code data-color="">#e6f7e6</code></td>
    <td>7</td>
    <td>97</td>
  </tr>
<tr>
    <td><code data-color="">#e4f5e4</code></td>
    <td>7</td>
    <td>96</td>
  </tr>
<tr>
    <td><code data-color="">#e1f2e1</code></td>
    <td>7</td>
    <td>95</td>
  </tr>
<tr>
    <td><code data-color="">#dff0df</code></td>
    <td>7</td>
    <td>94</td>
  </tr>
<tr>
    <td><code data-color="">#ddeddd</code></td>
    <td>7</td>
    <td>93</td>
  </tr>
<tr>
    <td><code data-color="">#daebda</code></td>
    <td>7</td>
    <td>92</td>
  </tr>
<tr>
    <td><code data-color="">#d8e8d8</code></td>
    <td>7</td>
    <td>91</td>
  </tr>
<tr>
    <td><code data-color="">#d5e6d5</code></td>
    <td>7</td>
    <td>90</td>
  </tr>
<tr>
    <td><code data-color="">#d3e3d3</code></td>
    <td>7</td>
    <td>89</td>
  </tr>
<tr>
    <td><code data-color="">#d1e0d1</code></td>
    <td>7</td>
    <td>88</td>
  </tr>
<tr>
    <td><code data-color="">#cedece</code></td>
    <td>7</td>
    <td>87</td>
  </tr>
<tr>
    <td><code data-color="">#cadbca</code></td>
    <td>8</td>
    <td>86</td>
  </tr>
<tr>
    <td><code data-color="">#cad9ca</code></td>
    <td>7</td>
    <td>85</td>
  </tr>
<tr>
    <td><code data-color="">#c5d6c5</code></td>
    <td>8</td>
    <td>84</td>
  </tr>
<tr>
    <td><code data-color="">#c3d4c3</code></td>
    <td>8</td>
    <td>83</td>
  </tr>
<tr>
    <td><code data-color="">#c0d1c0</code></td>
    <td>8</td>
    <td>82</td>
  </tr>
<tr>
    <td><code data-color="">#becfbe</code></td>
    <td>8</td>
    <td>81</td>
  </tr>
<tr>
    <td><code data-color="">#bcccbc</code></td>
    <td>8</td>
    <td>80</td>
  </tr>
<tr>
    <td><code data-color="">#b9c9b9</code></td>
    <td>8</td>
    <td>79</td>
  </tr>
<tr>
    <td><code data-color="">#b7c7b7</code></td>
    <td>8</td>
    <td>78</td>
  </tr>
<tr>
    <td><code data-color="">#b3c4b3</code></td>
    <td>9</td>
    <td>77</td>
  </tr>
<tr>
    <td><code data-color="">#b2c2b2</code></td>
    <td>8</td>
    <td>76</td>
  </tr>
<tr>
    <td><code data-color="">#aebfae</code></td>
    <td>9</td>
    <td>75</td>
  </tr>
<tr>
    <td><code data-color="">#acbdac</code></td>
    <td>9</td>
    <td>74</td>
  </tr>
<tr>
    <td><code data-color="">#a9baa9</code></td>
    <td>9</td>
    <td>73</td>
  </tr>
<tr>
    <td><code data-color="">#a7b8a7</code></td>
    <td>9</td>
    <td>72</td>
  </tr>
<tr>
    <td><code data-color="">#a5b5a5</code></td>
    <td>9</td>
    <td>71</td>
  </tr>
<tr>
    <td><code data-color="">#a2b3a2</code></td>
    <td>9</td>
    <td>70</td>
  </tr>
<tr>
    <td><code data-color="">#a0b0a0</code></td>
    <td>9</td>
    <td>69</td>
  </tr>
<tr>
    <td><code data-color="">#9cad9c</code></td>
    <td>10</td>
    <td>68</td>
  </tr>
<tr>
    <td><code data-color="">#9aab9a</code></td>
    <td>10</td>
    <td>67</td>
  </tr>
<tr>
    <td><code data-color="">#97a897</code></td>
    <td>10</td>
    <td>66</td>
  </tr>
<tr>
    <td><code data-color="">#95a695</code></td>
    <td>10</td>
    <td>65</td>
  </tr>
<tr>
    <td><code data-color="">#93a393</code></td>
    <td>10</td>
    <td>64</td>
  </tr>
<tr>
    <td><code data-color="">#91a191</code></td>
    <td>10</td>
    <td>63</td>
  </tr>
<tr>
    <td><code data-color="">#8d9e8d</code></td>
    <td>11</td>
    <td>62</td>
  </tr>
<tr>
    <td><code data-color="">#8c9c8c</code></td>
    <td>10</td>
    <td>61</td>
  </tr>
<tr>
    <td><code data-color="">#889988</code></td>
    <td>11</td>
    <td>60</td>
  </tr>
<tr>
    <td><code data-color="">#869686</code></td>
    <td>11</td>
    <td>59</td>
  </tr>
<tr>
    <td><code data-color="">#849484</code></td>
    <td>11</td>
    <td>58</td>
  </tr>
<tr>
    <td><code data-color="">#809180</code></td>
    <td>12</td>
    <td>57</td>
  </tr>
<tr>
    <td><code data-color="">#7e8f7e</code></td>
    <td>12</td>
    <td>56</td>
  </tr>
<tr>
    <td><code data-color="">#7b8c7b</code></td>
    <td>12</td>
    <td>55</td>
  </tr>
<tr>
    <td><code data-color="">#798a79</code></td>
    <td>12</td>
    <td>54</td>
  </tr>
<tr>
    <td><code data-color="">#768776</code></td>
    <td>13</td>
    <td>53</td>
  </tr>
<tr>
    <td><code data-color="">#758575</code></td>
    <td>12</td>
    <td>52</td>
  </tr>
<tr>
    <td><code data-color="">#718271</code></td>
    <td>13</td>
    <td>51</td>
  </tr>
<tr>
    <td><code data-color="">#6f806f</code></td>
    <td>13</td>
    <td>50</td>
  </tr>
<tr>
    <td><code data-color="">#6d7d6d</code></td>
    <td>13</td>
    <td>49</td>
  </tr>
<tr>
    <td><code data-color="">#697a69</code></td>
    <td>14</td>
    <td>48</td>
  </tr>
<tr>
    <td><code data-color="">#677867</code></td>
    <td>14</td>
    <td>47</td>
  </tr>
<tr>
    <td><code data-color="">#647564</code></td>
    <td>15</td>
    <td>46</td>
  </tr>
<tr>
    <td><code data-color="">#627362</code></td>
    <td>15</td>
    <td>45</td>
  </tr>
<tr>
    <td><code data-color="">#5f705f</code></td>
    <td>15</td>
    <td>44</td>
  </tr>
<tr>
    <td><code data-color="">#5d6e5d</code></td>
    <td>15</td>
    <td>43</td>
  </tr>
<tr>
    <td><code data-color="">#5a6b5a</code></td>
    <td>16</td>
    <td>42</td>
  </tr>
<tr>
    <td><code data-color="">#586958</code></td>
    <td>16</td>
    <td>41</td>
  </tr>
<tr>
    <td><code data-color="">#556655</code></td>
    <td>17</td>
    <td>40</td>
  </tr>
<tr>
    <td><code data-color="">#526352</code></td>
    <td>17</td>
    <td>39</td>
  </tr>
<tr>
    <td><code data-color="">#506150</code></td>
    <td>18</td>
    <td>38</td>
  </tr>
<tr>
    <td><code data-color="">#4d5e4d</code></td>
    <td>18</td>
    <td>37</td>
  </tr>
<tr>
    <td><code data-color="">#4b5c4b</code></td>
    <td>18</td>
    <td>36</td>
  </tr>
<tr>
    <td><code data-color="">#485948</code></td>
    <td>19</td>
    <td>35</td>
  </tr>
<tr>
    <td><code data-color="">#465746</code></td>
    <td>20</td>
    <td>34</td>
  </tr>
<tr>
    <td><code data-color="">#435443</code></td>
    <td>20</td>
    <td>33</td>
  </tr>
<tr>
    <td><code data-color="">#415241</code></td>
    <td>21</td>
    <td>32</td>
  </tr>
<tr>
    <td><code data-color="">#3e4f3e</code></td>
    <td>22</td>
    <td>31</td>
  </tr>
<tr>
    <td><code data-color="">#3c4d3c</code></td>
    <td>22</td>
    <td>30</td>
  </tr>
<tr>
    <td><code data-color="">#394a39</code></td>
    <td>23</td>
    <td>29</td>
  </tr>
<tr>
    <td><code data-color="">#364736</code></td>
    <td>24</td>
    <td>28</td>
  </tr>
<tr>
    <td><code data-color="">#344534</code></td>
    <td>25</td>
    <td>27</td>
  </tr>
<tr>
    <td><code data-color="">#314231</code></td>
    <td>26</td>
    <td>26</td>
  </tr>
<tr>
    <td><code data-color="">#2f402f</code></td>
    <td>27</td>
    <td>25</td>
  </tr>
<tr>
    <td><code data-color="">#2c3d2c</code></td>
    <td>28</td>
    <td>24</td>
  </tr>
<tr>
    <td><code data-color="">#2a3b2a</code></td>
    <td>29</td>
    <td>23</td>
  </tr>
<tr>
    <td><code data-color="">#273827</code></td>
    <td>30</td>
    <td>22</td>
  </tr>
<tr>
    <td><code data-color="">#253625</code></td>
    <td>31</td>
    <td>21</td>
  </tr>
<tr>
    <td><code data-color="">#223322</code></td>
    <td>33</td>
    <td>20</td>
  </tr>
<tr>
    <td><code data-color="">#1f301f</code></td>
    <td>35</td>
    <td>19</td>
  </tr>
<tr>
    <td><code data-color="">#1d2e1d</code></td>
    <td>37</td>
    <td>18</td>
  </tr>
<tr>
    <td><code data-color="">#1a2b1a</code></td>
    <td>40</td>
    <td>17</td>
  </tr>
<tr>
    <td><code data-color="">#182918</code></td>
    <td>41</td>
    <td>16</td>
  </tr>
<tr>
    <td><code data-color="">#152615</code></td>
    <td>45</td>
    <td>15</td>
  </tr>
<tr>
    <td><code data-color="">#132413</code></td>
    <td>47</td>
    <td>14</td>
  </tr>
<tr>
    <td><code data-color="">#102110</code></td>
    <td>52</td>
    <td>13</td>
  </tr>
<tr>
    <td><code data-color="">#0e1f0e</code></td>
    <td>55</td>
    <td>12</td>
  </tr>
<tr>
    <td><code data-color="">#0b1c0b</code></td>
    <td>61</td>
    <td>11</td>
  </tr>
<tr>
    <td><code data-color="">#091a09</code></td>
    <td>65</td>
    <td>10</td>
  </tr>
<tr>
    <td><code data-color="">#061706</code></td>
    <td>74</td>
    <td>9</td>
  </tr>
<tr>
    <td><code data-color="">#031403</code></td>
    <td>85</td>
    <td>8</td>
  </tr>
<tr>
    <td><code data-color="">#011201</code></td>
    <td>94</td>
    <td>7</td>
  </tr>
<tr>
    <td><code data-color="">#000f00</code></td>
    <td>100</td>
    <td>6</td>
  </tr>
<tr>
    <td><code data-color="">#000d00</code></td>
    <td>100</td>
    <td>5</td>
  </tr>
<tr>
    <td><code data-color="">#000a00</code></td>
    <td>100</td>
    <td>4</td>
  </tr>
<tr>
    <td><code data-color="">#000800</code></td>
    <td>100</td>
    <td>3</td>
  </tr>
<tr>
    <td><code data-color="">#000500</code></td>
    <td>100</td>
    <td>2</td>
  </tr>
<tr>
    <td><code data-color="">#000300</code></td>
    <td>100</td>
    <td>1</td>
  </tr>
  </tbody>
  </table>
  
  </div>
  

</div>
</figure>

<p>Uhoh. Yeah, this isn’t working at all.
Most all of these matched colors look downright green to me on all my devices.
At best, it seems to be matching greens that look like olive drab.</p>

<div class="colorScale" style="margin-bottom: 0.5em">
<div style="background:#f0fff0"></div>
<div style="background:#ebfaeb"></div>
<div style="background:#dff0df"></div>
<div style="background:#cedece"></div>
<div style="background:#a0b0a0"></div>
<div style="background:#5d6e5d"></div>
<div style="background:#415241"></div>
<div style="background:#2a3b2a"></div>
<div style="background:#011201"></div>
</div>

<p>If you are not familiar with color spaces: you might be wondering
how <code data-color="">#f0f7ff</code> and <code data-color="">#f0fff0</code>
can have the same saturation (6) and value (100) yet <code data-color="">#f0fff0</code>
is disproportionately offensive.
Due to <a href="https://en.wikipedia.org/wiki/Color_vision#Physiology_of_color_perception">the way color vision works</a>,
we are naturally more sensitive to colors in the greenish-yellow area of the color spectrum.
So we need to use a <a href="https://en.wikipedia.org/wiki/List_of_color_spaces_and_their_uses#CIE">perceptually uniform color model</a> that accounts for this.</p>

<h2 id="perceiving-colors-in-code">Perceiving colors in code</h2>

<p>Let’s tighten up our terminology in the hopes of finding a good color model for our problem.
Classifying colors as “grayish” can be more accurately stated as classifying:</p>

<ul>
  <li>neutral colors</li>
  <li>colors with low colorfulness</li>
  <li>colors with low saturation</li>
  <li>achromatic colors, colors with a low chroma value</li>
</ul>

<p>If we are looking for achromatic colors, we have the <a href="https://en.wikipedia.org/wiki/CIELAB_color_space#Cylindrical_representation:_CIELCh_or_CIEHLC">CIELCh<sub>ab</sub></a>
perceptual color model at our disposal. This is a cylindrical representation of the <a href="https://en.wikipedia.org/wiki/CIELAB_color_space">CIELAB</a>
color space with a channel for <strong>L</strong>ightness, <strong>C</strong>hroma, and <strong>h</strong>ue.
The chroma channel is what we will use to determine if a color is achromatic enough to be matched.
<code data-color="">#6c757d</code> from our bootstrap colors has the highest chroma value at <code>6.05</code>.
Let’s use that as our threshold.</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="p">{</span> <span class="nx">lch</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">d3-color</span><span class="dl">'</span><span class="p">;</span>

<span class="kd">const</span> <span class="nx">CHROMA_THRESHOLD</span> <span class="o">=</span> <span class="mf">6.05</span><span class="p">;</span>

<span class="kd">function</span> <span class="nx">isGray</span><span class="p">(</span><span class="nx">color</span><span class="p">)</span> <span class="p">{</span>
  <span class="kd">let</span> <span class="nx">chroma</span> <span class="o">=</span> <span class="nx">lch</span><span class="p">(</span><span class="nx">color</span><span class="p">).</span><span class="nx">c</span><span class="p">;</span>
  <span class="k">return</span> <span class="nb">isNaN</span><span class="p">(</span><span class="nx">chroma</span><span class="p">)</span> <span class="o">||</span> <span class="nx">chroma</span> <span class="o">&lt;=</span> <span class="nx">CHROMA_THRESHOLD</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Let’s chart it at the
<code data-color="hsl(210 100% 50%)">210°</code> hue:</p>

<figure>
<div class="sxs">

  <svg viewBox="0 0 1 1" class="figure">
  <defs>
  <linearGradient id="value" x2="0%" y2="0%" x1="0%" y1="100%">
  <stop offset="0%" stop-color="rgba(0, 0, 0, 1)" />
  <stop offset="100%" stop-color="rgba(0, 0, 0, 0)" />
  </linearGradient>
  <linearGradient id="saturation">
  <stop offset="0%" stop-color="rgba(255, 255, 255, 1)" />
  <stop offset="100%" stop-color="rgba(255, 255, 255, 0)" />
  </linearGradient>
  </defs>

  <rect id="fillBg" width="100%" height="100%" fill="rgb(0, 128, 255)" />
  <rect width="100%" height="100%" fill="url('#saturation')" />
  <rect width="100%" height="100%" fill="url('#value')" />

    <circle cx="0.007999999999999972" cy="0.019607843137254943" r="1%" stroke-width="1%" stroke="red" fill="transparent" />
    <circle cx="0.025104602510460282" cy="0.06274509803921569" r="1%" stroke-width="1%" stroke="red" fill="transparent" />
    <circle cx="0.034782608695652174" cy="0.0980392156862745" r="1%" stroke-width="1%" stroke="red" fill="transparent" />
    <circle cx="0.05504587155963296" cy="0.14509803921568631" r="1%" stroke-width="1%" stroke="red" fill="transparent" />
    <circle cx="0.08" cy="0.2588235294117647" r="1%" stroke-width="1%" stroke="red" fill="transparent" />
    <circle cx="0.13" cy="0.5098039215686274" r="1%" stroke-width="1%" stroke="red" fill="transparent" />
    <circle cx="0.16091954022988514" cy="0.6588235294117647" r="1%" stroke-width="1%" stroke="red" fill="transparent" />
    <circle cx="0.1875" cy="0.7490196078431373" r="1%" stroke-width="1%" stroke="red" fill="transparent" />
    <circle cx="0.19512195121951217" cy="0.8392156862745098" r="1%" stroke-width="1%" stroke="red" fill="transparent" />

    <polyline points="0.05882352941176472,0 0.06746031746031755,0.0117647058823529 0.06000000000000002,0.019607843137254943 0.06882591093117407,0.03137254901960784 0.0693877551020408,0.039215686274509776 0.07024793388429751,0.050980392156862786 0.07083333333333332,0.05882352941176472 0.06751054852320675,0.07058823529411762 0.07234042553191489,0.07843137254901966 0.06896551724137931,0.09019607843137256 0.07391304347826085,0.0980392156862745 0.07048458149779736,0.1098039215686275 0.06696428571428573,0.1215686274509804 0.07207207207207207,0.12941176470588234 0.07762557077625569,0.14117647058823535 0.06912442396313366,0.14901960784313728 0.07943925233644858,0.16078431372549018 0.08018867924528313,0.1686274509803921 0.08133971291866027,0.18039215686274512 0.08212560386473428,0.18823529411764706 0.0784313725490196,0.19999999999999996 0.07960199004975124,0.21176470588235297 0.08040201005025126,0.2196078431372549 0.08673469387755114,0.2313725490196078 0.08247422680412371,0.23921568627450984 0.08900523560209422,0.25098039215686274 0.08994708994708993,0.2588235294117647 0.09139784946236558,0.2705882352941177 0.09239130434782607,0.2784313725490196 0.08839779005524862,0.2901960784313725 0.09497206703910613,0.29803921568627456 0.09090909090909091,0.30980392156862746 0.0982658959537572,0.32156862745098036 0.09941520467836255,0.3294117647058824 0.10119047619047616,0.3411764705882353 0.10240963855421684,0.34901960784313724 0.09815950920245399,0.36078431372549025 0.09937888198757765,0.3686274509803922 0.10759493670886074,0.3803921568627451 0.10256410256410256,0.388235294117647 0.11111111111111109,0.4 0.10666666666666666,0.4117647058823529 0.1081081081081081,0.41960784313725485 0.11724137931034481,0.43137254901960786 0.11888111888111885,0.4392156862745098 0.1214285714285715,0.4509803921568627 0.12318840579710143,0.45882352941176474 0.12592592592592589,0.47058823529411764 0.12030075187969934,0.4784313725490196 0.13076923076923075,0.4901960784313726 0.13281249999999997,0.4980392156862745 0.128,0.5098039215686274 0.13934426229508204,0.5215686274509803 0.14166666666666664,0.5294117647058824 0.14529914529914528,0.5411764705882354 0.1478260869565217,0.5490196078431373 0.15178571428571425,0.5607843137254902 0.15454545454545462,0.5686274509803921 0.15887850467289716,0.580392156862745 0.16190476190476188,0.5882352941176471 0.16666666666666677,0.6 0.17171717171717168,0.611764705882353 0.17525773195876285,0.6196078431372549 0.18085106382978733,0.6313725490196078 0.18478260869565213,0.6392156862745098 0.19101123595505615,0.6509803921568628 0.19540229885057467,0.6588235294117647 0.20238095238095233,0.6705882352941177 0.2073170731707318,0.6784313725490196 0.21518987341772156,0.6901960784313725 0.22077922077922074,0.6980392156862745 0.22972972972972974,0.7098039215686274 0.2394366197183099,0.7215686274509804 0.24637681159420285,0.7294117647058824 0.2575757575757576,0.7411764705882353 0.26562499999999994,0.7490196078431373 0.27868852459016397,0.7607843137254902 0.28813559322033905,0.7686274509803921 0.3035714285714285,0.7803921568627451 0.31481481481481477,0.788235294117647 0.33333333333333337,0.8 0.3541666666666667,0.8117647058823529 0.3695652173913043,0.8196078431372549 0.39534883720930236,0.8313725490196078 0.41463414634146345,0.8392156862745098 0.4473684210526316,0.8509803921568627 0.4722222222222222,0.8588235294117648 0.5151515151515152,0.8705882352941177 0.5483870967741935,0.8784313725490196 0.6071428571428571,0.8901960784313725 0.6538461538461539,0.8980392156862745 0.7391304347826086,0.9098039215686274 0.85,0.9215686274509804 0.9444444444444444,0.9294117647058824 1,0.9411764705882353 1,0.9490196078431372 1,0.9607843137254902 1,0.9686274509803922 1,0.9803921568627451 1,0.9882352941176471" fill="none" stroke-width="1%" stroke="purple" />
  <polyline points="0.07058823529411762,0 0.06746031746031755,0.0117647058823529 0.07199999999999997,0.019607843137254943 0.06882591093117407,0.03137254901960784 0.0693877551020408,0.039215686274509776 0.07024793388429751,0.050980392156862786 0.07083333333333332,0.05882352941176472 0.06751054852320675,0.07058823529411762 0.07234042553191489,0.07843137254901966 0.08189655172413789,0.09019607843137256 0.08260869565217399,0.0980392156862745 0.079295154185022,0.1098039215686275 0.08035714285714282,0.1215686274509804 0.08108108108108104,0.12941176470588234 0.07762557077625569,0.14117647058823535 0.08294930875576034,0.14901960784313728 0.07943925233644858,0.16078431372549018 0.08018867924528313,0.1686274509803921 0.08133971291866027,0.18039215686274512 0.08212560386473428,0.18823529411764706 0.0784313725490196,0.19999999999999996 0.08955223880597012,0.21176470588235297 0.09045226130653262,0.2196078431372549 0.08673469387755114,0.2313725490196078 0.09278350515463914,0.23921568627450984 0.08900523560209422,0.25098039215686274 0.08994708994708993,0.2588235294117647 0.09139784946236558,0.2705882352941177 0.09239130434782607,0.2784313725490196 0.08839779005524862,0.2901960784313725 0.09497206703910613,0.29803921568627456 0.10227272727272724,0.30980392156862746 0.0982658959537572,0.32156862745098036 0.09941520467836255,0.3294117647058824 0.10119047619047616,0.3411764705882353 0.10240963855421684,0.34901960784313724 0.09815950920245399,0.36078431372549025 0.09937888198757765,0.3686274509803922 0.10759493670886074,0.3803921568627451 0.11538461538461552,0.388235294117647 0.11111111111111109,0.4 0.10666666666666666,0.4117647058823529 0.1081081081081081,0.41960784313725485 0.1103448275862069,0.43137254901960786 0.11188811188811189,0.4392156862745098 0.1214285714285715,0.4509803921568627 0.12318840579710143,0.45882352941176474 0.11851851851851851,0.47058823529411764 0.12030075187969934,0.4784313725490196 0.12307692307692297,0.4901960784313726 0.13281249999999997,0.4980392156862745 0.128,0.5098039215686274 0.13114754098360654,0.5215686274509803 0.13333333333333333,0.5294117647058824 0.13675213675213677,0.5411764705882354 0.1391304347826087,0.5490196078431373 0.14285714285714285,0.5607843137254902 0.14545454545454545,0.5686274509803921 0.14953271028037382,0.580392156862745 0.1523809523809524,0.5882352941176471 0.1470588235294118,0.6 0.15151515151515155,0.611764705882353 0.16494845360824742,0.6196078431372549 0.15957446808510642,0.6313725490196078 0.17391304347826086,0.6392156862745098 0.1685393258426965,0.6509803921568628 0.1839080459770115,0.6588235294117647 0.1785714285714286,0.6705882352941177 0.19512195121951217,0.6784313725490196 0.18987341772151903,0.6901960784313725 0.20779220779220772,0.6980392156862745 0.20270270270270274,0.7098039215686274 0.21126760563380287,0.7215686274509804 0.21739130434782605,0.7294117647058824 0.2121212121212122,0.7411764705882353 0.23437499999999994,0.7490196078431373 0.22950819672131145,0.7607843137254902 0.25423728813559326,0.7686274509803921 0.25,0.7803921568627451 0.27777777777777773,0.788235294117647 0.2745098039215686,0.8 0.2916666666666667,0.8117647058823529 0.30434782608695654,0.8196078431372549 0.3255813953488373,0.8313725490196078 0.3414634146341464,0.8392156862745098 0.3684210526315789,0.8509803921568627 0.3888888888888889,0.8588235294117648 0.393939393939394,0.8705882352941177 0.45161290322580644,0.8784313725490196 0.5,0.8901960784313725 0.6153846153846154,0.8980392156862745 0.7391304347826086,0.9098039215686274 0.95,0.9215686274509804 1,0.9294117647058824 1,0.9411764705882353 1,0.9490196078431372 1,0.9607843137254902 1,0.9686274509803922 1,0.9803921568627451 1,0.9882352941176471" fill="none" stroke-width="1%" stroke="blue" />

  </svg>


  <div class="tableContainer">
  
  <table class="table">
    <thead>
      <tr><th>hex</th><th>saturation</th><th>value</th></tr>
    </thead>
  <tbody>
  <tr>
    <td><code data-color="">#edf6ff</code></td>
    <td>7</td>
    <td>100</td>
  </tr>
<tr>
    <td><code data-color="">#ebf4fc</code></td>
    <td>7</td>
    <td>99</td>
  </tr>
<tr>
    <td><code data-color="">#e8f1fa</code></td>
    <td>7</td>
    <td>98</td>
  </tr>
<tr>
    <td><code data-color="">#e6eff7</code></td>
    <td>7</td>
    <td>97</td>
  </tr>
<tr>
    <td><code data-color="">#e4ecf5</code></td>
    <td>7</td>
    <td>96</td>
  </tr>
<tr>
    <td><code data-color="">#e1eaf2</code></td>
    <td>7</td>
    <td>95</td>
  </tr>
<tr>
    <td><code data-color="">#dfe7f0</code></td>
    <td>7</td>
    <td>94</td>
  </tr>
<tr>
    <td><code data-color="">#dde5ed</code></td>
    <td>7</td>
    <td>93</td>
  </tr>
<tr>
    <td><code data-color="">#dae2eb</code></td>
    <td>7</td>
    <td>92</td>
  </tr>
<tr>
    <td><code data-color="">#d5dfe8</code></td>
    <td>8</td>
    <td>91</td>
  </tr>
<tr>
    <td><code data-color="">#d3dce6</code></td>
    <td>8</td>
    <td>90</td>
  </tr>
<tr>
    <td><code data-color="">#d1dae3</code></td>
    <td>8</td>
    <td>89</td>
  </tr>
<tr>
    <td><code data-color="">#ced7e0</code></td>
    <td>8</td>
    <td>88</td>
  </tr>
<tr>
    <td><code data-color="">#ccd5de</code></td>
    <td>8</td>
    <td>87</td>
  </tr>
<tr>
    <td><code data-color="">#cad3db</code></td>
    <td>8</td>
    <td>86</td>
  </tr>
<tr>
    <td><code data-color="">#c7d0d9</code></td>
    <td>8</td>
    <td>85</td>
  </tr>
<tr>
    <td><code data-color="">#c5ced6</code></td>
    <td>8</td>
    <td>84</td>
  </tr>
<tr>
    <td><code data-color="">#c3cbd4</code></td>
    <td>8</td>
    <td>83</td>
  </tr>
<tr>
    <td><code data-color="">#c0c9d1</code></td>
    <td>8</td>
    <td>82</td>
  </tr>
<tr>
    <td><code data-color="">#bec6cf</code></td>
    <td>8</td>
    <td>81</td>
  </tr>
<tr>
    <td><code data-color="">#bcc4cc</code></td>
    <td>8</td>
    <td>80</td>
  </tr>
<tr>
    <td><code data-color="">#b7c0c9</code></td>
    <td>9</td>
    <td>79</td>
  </tr>
<tr>
    <td><code data-color="">#b5bec7</code></td>
    <td>9</td>
    <td>78</td>
  </tr>
<tr>
    <td><code data-color="">#b3bcc4</code></td>
    <td>9</td>
    <td>77</td>
  </tr>
<tr>
    <td><code data-color="">#b0b9c2</code></td>
    <td>9</td>
    <td>76</td>
  </tr>
<tr>
    <td><code data-color="">#aeb7bf</code></td>
    <td>9</td>
    <td>75</td>
  </tr>
<tr>
    <td><code data-color="">#acb4bd</code></td>
    <td>9</td>
    <td>74</td>
  </tr>
<tr>
    <td><code data-color="">#a9b2ba</code></td>
    <td>9</td>
    <td>73</td>
  </tr>
<tr>
    <td><code data-color="">#a7afb8</code></td>
    <td>9</td>
    <td>72</td>
  </tr>
<tr>
    <td><code data-color="">#a5adb5</code></td>
    <td>9</td>
    <td>71</td>
  </tr>
<tr>
    <td><code data-color="">#a2aab3</code></td>
    <td>9</td>
    <td>70</td>
  </tr>
<tr>
    <td><code data-color="">#9ea7b0</code></td>
    <td>10</td>
    <td>69</td>
  </tr>
<tr>
    <td><code data-color="">#9ca5ad</code></td>
    <td>10</td>
    <td>68</td>
  </tr>
<tr>
    <td><code data-color="">#9aa2ab</code></td>
    <td>10</td>
    <td>67</td>
  </tr>
<tr>
    <td><code data-color="">#97a0a8</code></td>
    <td>10</td>
    <td>66</td>
  </tr>
<tr>
    <td><code data-color="">#959da6</code></td>
    <td>10</td>
    <td>65</td>
  </tr>
<tr>
    <td><code data-color="">#939ba3</code></td>
    <td>10</td>
    <td>64</td>
  </tr>
<tr>
    <td><code data-color="">#9199a1</code></td>
    <td>10</td>
    <td>63</td>
  </tr>
<tr>
    <td><code data-color="">#8d959e</code></td>
    <td>11</td>
    <td>62</td>
  </tr>
<tr>
    <td><code data-color="">#8a939c</code></td>
    <td>12</td>
    <td>61</td>
  </tr>
<tr>
    <td><code data-color="">#889199</code></td>
    <td>11</td>
    <td>60</td>
  </tr>
<tr>
    <td><code data-color="">#868e96</code></td>
    <td>11</td>
    <td>59</td>
  </tr>
<tr>
    <td><code data-color="">#848c94</code></td>
    <td>11</td>
    <td>58</td>
  </tr>
<tr>
    <td><code data-color="">#818991</code></td>
    <td>11</td>
    <td>57</td>
  </tr>
<tr>
    <td><code data-color="">#7f878f</code></td>
    <td>11</td>
    <td>56</td>
  </tr>
<tr>
    <td><code data-color="">#7b848c</code></td>
    <td>12</td>
    <td>55</td>
  </tr>
<tr>
    <td><code data-color="">#79818a</code></td>
    <td>12</td>
    <td>54</td>
  </tr>
<tr>
    <td><code data-color="">#777f87</code></td>
    <td>12</td>
    <td>53</td>
  </tr>
<tr>
    <td><code data-color="">#757d85</code></td>
    <td>12</td>
    <td>52</td>
  </tr>
<tr>
    <td><code data-color="">#727a82</code></td>
    <td>12</td>
    <td>51</td>
  </tr>
<tr>
    <td><code data-color="">#6f7780</code></td>
    <td>13</td>
    <td>50</td>
  </tr>
<tr>
    <td><code data-color="">#6d757d</code></td>
    <td>13</td>
    <td>49</td>
  </tr>
<tr>
    <td><code data-color="">#6a727a</code></td>
    <td>13</td>
    <td>48</td>
  </tr>
<tr>
    <td><code data-color="">#687078</code></td>
    <td>13</td>
    <td>47</td>
  </tr>
<tr>
    <td><code data-color="">#656d75</code></td>
    <td>14</td>
    <td>46</td>
  </tr>
<tr>
    <td><code data-color="">#636b73</code></td>
    <td>14</td>
    <td>45</td>
  </tr>
<tr>
    <td><code data-color="">#606870</code></td>
    <td>14</td>
    <td>44</td>
  </tr>
<tr>
    <td><code data-color="">#5e666e</code></td>
    <td>15</td>
    <td>43</td>
  </tr>
<tr>
    <td><code data-color="">#5b636b</code></td>
    <td>15</td>
    <td>42</td>
  </tr>
<tr>
    <td><code data-color="">#596169</code></td>
    <td>15</td>
    <td>41</td>
  </tr>
<tr>
    <td><code data-color="">#575e66</code></td>
    <td>15</td>
    <td>40</td>
  </tr>
<tr>
    <td><code data-color="">#545b63</code></td>
    <td>15</td>
    <td>39</td>
  </tr>
<tr>
    <td><code data-color="">#515961</code></td>
    <td>16</td>
    <td>38</td>
  </tr>
<tr>
    <td><code data-color="">#4f575e</code></td>
    <td>16</td>
    <td>37</td>
  </tr>
<tr>
    <td><code data-color="">#4c545c</code></td>
    <td>17</td>
    <td>36</td>
  </tr>
<tr>
    <td><code data-color="">#4a5259</code></td>
    <td>17</td>
    <td>35</td>
  </tr>
<tr>
    <td><code data-color="">#474f57</code></td>
    <td>18</td>
    <td>34</td>
  </tr>
<tr>
    <td><code data-color="">#454d54</code></td>
    <td>18</td>
    <td>33</td>
  </tr>
<tr>
    <td><code data-color="">#424a52</code></td>
    <td>20</td>
    <td>32</td>
  </tr>
<tr>
    <td><code data-color="">#40484f</code></td>
    <td>19</td>
    <td>31</td>
  </tr>
<tr>
    <td><code data-color="">#3d454d</code></td>
    <td>21</td>
    <td>30</td>
  </tr>
<tr>
    <td><code data-color="">#3b434a</code></td>
    <td>20</td>
    <td>29</td>
  </tr>
<tr>
    <td><code data-color="">#384047</code></td>
    <td>21</td>
    <td>28</td>
  </tr>
<tr>
    <td><code data-color="">#363e45</code></td>
    <td>22</td>
    <td>27</td>
  </tr>
<tr>
    <td><code data-color="">#343b42</code></td>
    <td>21</td>
    <td>26</td>
  </tr>
<tr>
    <td><code data-color="">#313840</code></td>
    <td>23</td>
    <td>25</td>
  </tr>
<tr>
    <td><code data-color="">#2f363d</code></td>
    <td>23</td>
    <td>24</td>
  </tr>
<tr>
    <td><code data-color="">#2c333b</code></td>
    <td>25</td>
    <td>23</td>
  </tr>
<tr>
    <td><code data-color="">#2a3138</code></td>
    <td>25</td>
    <td>22</td>
  </tr>
<tr>
    <td><code data-color="">#272e36</code></td>
    <td>28</td>
    <td>21</td>
  </tr>
<tr>
    <td><code data-color="">#252c33</code></td>
    <td>27</td>
    <td>20</td>
  </tr>
<tr>
    <td><code data-color="">#222930</code></td>
    <td>29</td>
    <td>19</td>
  </tr>
<tr>
    <td><code data-color="">#20272e</code></td>
    <td>30</td>
    <td>18</td>
  </tr>
<tr>
    <td><code data-color="">#1d242b</code></td>
    <td>33</td>
    <td>17</td>
  </tr>
<tr>
    <td><code data-color="">#1b2229</code></td>
    <td>34</td>
    <td>16</td>
  </tr>
<tr>
    <td><code data-color="">#181f26</code></td>
    <td>37</td>
    <td>15</td>
  </tr>
<tr>
    <td><code data-color="">#161d24</code></td>
    <td>39</td>
    <td>14</td>
  </tr>
<tr>
    <td><code data-color="">#141a21</code></td>
    <td>39</td>
    <td>13</td>
  </tr>
<tr>
    <td><code data-color="">#11181f</code></td>
    <td>45</td>
    <td>12</td>
  </tr>
<tr>
    <td><code data-color="">#0e151c</code></td>
    <td>50</td>
    <td>11</td>
  </tr>
<tr>
    <td><code data-color="">#0a121a</code></td>
    <td>62</td>
    <td>10</td>
  </tr>
<tr>
    <td><code data-color="">#060e17</code></td>
    <td>74</td>
    <td>9</td>
  </tr>
<tr>
    <td><code data-color="">#010b14</code></td>
    <td>95</td>
    <td>8</td>
  </tr>
<tr>
    <td><code data-color="">#000912</code></td>
    <td>100</td>
    <td>7</td>
  </tr>
<tr>
    <td><code data-color="">#00080f</code></td>
    <td>100</td>
    <td>6</td>
  </tr>
<tr>
    <td><code data-color="">#00060d</code></td>
    <td>100</td>
    <td>5</td>
  </tr>
<tr>
    <td><code data-color="">#00050a</code></td>
    <td>100</td>
    <td>4</td>
  </tr>
<tr>
    <td><code data-color="">#000408</code></td>
    <td>100</td>
    <td>3</td>
  </tr>
<tr>
    <td><code data-color="">#000305</code></td>
    <td>100</td>
    <td>2</td>
  </tr>
<tr>
    <td><code data-color="">#000103</code></td>
    <td>100</td>
    <td>1</td>
  </tr>
  </tbody>
  </table>
  
  </div>
  

</div>
<figcaption>
The <span data-color="">blue</span> line is our new LCh-based <code>isGray</code>.
The <span data-color="">purple</span> line is our old range-based <code>isGray</code>.
Great! It's mostly the same as our previous function. 
Glad to see we weren't completely off-base with our previous idea.
</figcaption>
</figure>

<p>Let’s chart it at the
<code data-color="hsl(120 100% 50%)">120°</code> hue:</p>

<figure>
<div class="sxs">

  <svg viewBox="0 0 1 1" class="figure">
  <defs>
  <linearGradient id="value" x2="0%" y2="0%" x1="0%" y1="100%">
  <stop offset="0%" stop-color="rgba(0, 0, 0, 1)" />
  <stop offset="100%" stop-color="rgba(0, 0, 0, 0)" />
  </linearGradient>
  <linearGradient id="saturation">
  <stop offset="0%" stop-color="rgba(255, 255, 255, 1)" />
  <stop offset="100%" stop-color="rgba(255, 255, 255, 0)" />
  </linearGradient>
  </defs>

  <rect id="fillBg" width="100%" height="100%" fill="rgb(0, 255, 0)" />
  <rect width="100%" height="100%" fill="url('#saturation')" />
  <rect width="100%" height="100%" fill="url('#value')" />
  
  <polyline points="0.03137254901960784,0 0.039682539682539764,0.0117647058823529 0.03999999999999997,0.019607843137254943 0.04048582995951414,0.03137254901960784 0.04081632653061233,0.039215686274509776 0.037190082644628086,0.050980392156862786 0.04166666666666664,0.05882352941176472 0.03797468354430378,0.07058823529411762 0.04255319148936167,0.07843137254901966 0.03879310344827585,0.09019607843137256 0.04347826086956519,0.0980392156862745 0.039647577092511,0.1098039215686275 0.04017857142857141,0.1215686274509804 0.04054054054054052,0.12941176470588234 0.03652968036529681,0.14117647058823535 0.04147465437788017,0.14901960784313728 0.037383177570093455,0.16078431372549018 0.04245283018867936,0.1686274509803921 0.03827751196172249,0.18039215686274512 0.0434782608695652,0.18823529411764706 0.0392156862745098,0.19999999999999996 0.03980099502487562,0.21176470588235297 0.04020100502512563,0.2196078431372549 0.04081632653061224,0.2313725490196078 0.041237113402061855,0.23921568627450984 0.04712041884816752,0.25098039215686274 0.05291005291005302,0.2588235294117647 0.04838709677419353,0.2705882352941177 0.054347826086956486,0.2784313725490196 0.049723756906077325,0.2901960784313725 0.05027932960893853,0.29803921568627456 0.05113636363636362,0.30980392156862746 0.046242774566473986,0.32156862745098036 0.052631578947368404,0.3294117647058824 0.047619047619047616,0.3411764705882353 0.05421686746987949,0.34901960784313724 0.049079754601226995,0.36078431372549025 0.04968944099378882,0.3686274509803922 0.05063291139240506,0.3803921568627451 0.05128205128205128,0.388235294117647 0.058823529411764684,0.4 0.05999999999999998,0.4117647058823529 0.060810810810810974,0.41960784313725485 0.05517241379310345,0.43137254901960786 0.06293706293706292,0.4392156862745098 0.057142857142857134,0.4509803921568627 0.06521739130434781,0.45882352941176474 0.059259259259259255,0.47058823529411764 0.060150375939849725,0.4784313725490196 0.06153846153846143,0.4901960784313726 0.07031249999999997,0.4980392156862745 0.07199999999999997,0.5098039215686274 0.06557377049180327,0.5215686274509803 0.07499999999999997,0.5294117647058824 0.06837606837606838,0.5411764705882354 0.06956521739130435,0.5490196078431373 0.07142857142857142,0.5607843137254902 0.07272727272727272,0.5686274509803921 0.07476635514018691,0.580392156862745 0.08571428571428569,0.5882352941176471 0.0784313725490196,0.6 0.0808080808080808,0.611764705882353 0.08247422680412371,0.6196078431372549 0.07446808510638302,0.6313725490196078 0.08695652173913043,0.6392156862745098 0.0898876404494382,0.6509803921568628 0.09195402298850575,0.6588235294117647 0.08333333333333337,0.6705882352941177 0.10975609756097573,0.6784313725490196 0.10126582278481013,0.6901960784313725 0.1038961038961039,0.6980392156862745 0.09459459459459463,0.7098039215686274 0.0985915492957747,0.7215686274509804 0.11594202898550715,0.7294117647058824 0.12121212121212131,0.7411764705882353 0.125,0.7490196078431373 0.1147540983606558,0.7607843137254902 0.13559322033898305,0.7686274509803921 0.12499999999999993,0.7803921568627451 0.14814814814814814,0.788235294117647 0.13725490196078438,0.8 0.14583333333333326,0.8117647058823529 0.1521739130434782,0.8196078431372549 0.16279069767441867,0.8313725490196078 0.19512195121951217,0.8392156862745098 0.18421052631578946,0.8509803921568627 0.19444444444444445,0.8588235294117648 0.2121212121212122,0.8705882352941177 0.22580645161290322,0.8784313725490196 0.25,0.8901960784313725 0.3076923076923077,0.8980392156862745 0.34782608695652173,0.9098039215686274 0.45,0.9215686274509804 0.6111111111111112,0.9294117647058824 0.8,0.9411764705882353 1,0.9490196078431372 1,0.9607843137254902 1,0.9686274509803922 1,0.9803921568627451 1,0.9882352941176471" fill="none" stroke-width="1%" stroke="blue" />

    <polyline points="0.05882352941176472,0 0.06746031746031755,0.0117647058823529 0.06000000000000002,0.019607843137254943 0.06882591093117407,0.03137254901960784 0.0693877551020408,0.039215686274509776 0.07024793388429751,0.050980392156862786 0.07083333333333332,0.05882352941176472 0.06751054852320675,0.07058823529411762 0.07234042553191489,0.07843137254901966 0.06896551724137931,0.09019607843137256 0.07391304347826085,0.0980392156862745 0.07048458149779736,0.1098039215686275 0.06696428571428573,0.1215686274509804 0.07207207207207207,0.12941176470588234 0.07762557077625569,0.14117647058823535 0.06912442396313366,0.14901960784313728 0.07943925233644858,0.16078431372549018 0.08018867924528313,0.1686274509803921 0.08133971291866027,0.18039215686274512 0.08212560386473428,0.18823529411764706 0.0784313725490196,0.19999999999999996 0.07960199004975124,0.21176470588235297 0.08040201005025126,0.2196078431372549 0.08673469387755114,0.2313725490196078 0.08247422680412371,0.23921568627450984 0.08900523560209422,0.25098039215686274 0.08994708994708993,0.2588235294117647 0.09139784946236558,0.2705882352941177 0.09239130434782607,0.2784313725490196 0.08839779005524862,0.2901960784313725 0.09497206703910613,0.29803921568627456 0.09090909090909091,0.30980392156862746 0.0982658959537572,0.32156862745098036 0.09941520467836255,0.3294117647058824 0.10119047619047616,0.3411764705882353 0.10240963855421684,0.34901960784313724 0.09815950920245399,0.36078431372549025 0.09937888198757765,0.3686274509803922 0.10759493670886074,0.3803921568627451 0.10256410256410256,0.388235294117647 0.11111111111111109,0.4 0.10666666666666666,0.4117647058823529 0.1081081081081081,0.41960784313725485 0.11724137931034481,0.43137254901960786 0.11888111888111885,0.4392156862745098 0.1214285714285715,0.4509803921568627 0.12318840579710143,0.45882352941176474 0.12592592592592589,0.47058823529411764 0.12030075187969934,0.4784313725490196 0.13076923076923075,0.4901960784313726 0.13281249999999997,0.4980392156862745 0.128,0.5098039215686274 0.13934426229508204,0.5215686274509803 0.14166666666666664,0.5294117647058824 0.14529914529914528,0.5411764705882354 0.1478260869565217,0.5490196078431373 0.15178571428571425,0.5607843137254902 0.15454545454545462,0.5686274509803921 0.15887850467289716,0.580392156862745 0.16190476190476188,0.5882352941176471 0.16666666666666677,0.6 0.17171717171717168,0.611764705882353 0.17525773195876285,0.6196078431372549 0.18085106382978733,0.6313725490196078 0.18478260869565213,0.6392156862745098 0.19101123595505615,0.6509803921568628 0.19540229885057467,0.6588235294117647 0.20238095238095233,0.6705882352941177 0.2073170731707318,0.6784313725490196 0.21518987341772156,0.6901960784313725 0.22077922077922074,0.6980392156862745 0.22972972972972974,0.7098039215686274 0.2394366197183099,0.7215686274509804 0.24637681159420285,0.7294117647058824 0.2575757575757576,0.7411764705882353 0.26562499999999994,0.7490196078431373 0.27868852459016397,0.7607843137254902 0.28813559322033905,0.7686274509803921 0.3035714285714285,0.7803921568627451 0.31481481481481477,0.788235294117647 0.33333333333333337,0.8 0.3541666666666667,0.8117647058823529 0.3695652173913043,0.8196078431372549 0.39534883720930236,0.8313725490196078 0.41463414634146345,0.8392156862745098 0.4473684210526316,0.8509803921568627 0.4722222222222222,0.8588235294117648 0.5151515151515152,0.8705882352941177 0.5483870967741935,0.8784313725490196 0.6071428571428571,0.8901960784313725 0.6538461538461539,0.8980392156862745 0.7391304347826086,0.9098039215686274 0.85,0.9215686274509804 0.9444444444444444,0.9294117647058824 1,0.9411764705882353 1,0.9490196078431372 1,0.9607843137254902 1,0.9686274509803922 1,0.9803921568627451 1,0.9882352941176471" fill="none" stroke-width="1%" stroke="purple" />
  </svg>


  <div class="tableContainer">
  
  <table class="table">
    <thead>
      <tr><th>hex</th><th>saturation</th><th>value</th></tr>
    </thead>
  <tbody>
  <tr>
    <td><code data-color="">#f7fff7</code></td>
    <td>3</td>
    <td>100</td>
  </tr>
<tr>
    <td><code data-color="">#f2fcf2</code></td>
    <td>4</td>
    <td>99</td>
  </tr>
<tr>
    <td><code data-color="">#f0faf0</code></td>
    <td>4</td>
    <td>98</td>
  </tr>
<tr>
    <td><code data-color="">#edf7ed</code></td>
    <td>4</td>
    <td>97</td>
  </tr>
<tr>
    <td><code data-color="">#ebf5eb</code></td>
    <td>4</td>
    <td>96</td>
  </tr>
<tr>
    <td><code data-color="">#e9f2e9</code></td>
    <td>4</td>
    <td>95</td>
  </tr>
<tr>
    <td><code data-color="">#e6f0e6</code></td>
    <td>4</td>
    <td>94</td>
  </tr>
<tr>
    <td><code data-color="">#e4ede4</code></td>
    <td>4</td>
    <td>93</td>
  </tr>
<tr>
    <td><code data-color="">#e1ebe1</code></td>
    <td>4</td>
    <td>92</td>
  </tr>
<tr>
    <td><code data-color="">#dfe8df</code></td>
    <td>4</td>
    <td>91</td>
  </tr>
<tr>
    <td><code data-color="">#dce6dc</code></td>
    <td>4</td>
    <td>90</td>
  </tr>
<tr>
    <td><code data-color="">#dae3da</code></td>
    <td>4</td>
    <td>89</td>
  </tr>
<tr>
    <td><code data-color="">#d7e0d7</code></td>
    <td>4</td>
    <td>88</td>
  </tr>
<tr>
    <td><code data-color="">#d5ded5</code></td>
    <td>4</td>
    <td>87</td>
  </tr>
<tr>
    <td><code data-color="">#d3dbd3</code></td>
    <td>4</td>
    <td>86</td>
  </tr>
<tr>
    <td><code data-color="">#d0d9d0</code></td>
    <td>4</td>
    <td>85</td>
  </tr>
<tr>
    <td><code data-color="">#ced6ce</code></td>
    <td>4</td>
    <td>84</td>
  </tr>
<tr>
    <td><code data-color="">#cbd4cb</code></td>
    <td>4</td>
    <td>83</td>
  </tr>
<tr>
    <td><code data-color="">#c9d1c9</code></td>
    <td>4</td>
    <td>82</td>
  </tr>
<tr>
    <td><code data-color="">#c6cfc6</code></td>
    <td>4</td>
    <td>81</td>
  </tr>
<tr>
    <td><code data-color="">#c4ccc4</code></td>
    <td>4</td>
    <td>80</td>
  </tr>
<tr>
    <td><code data-color="">#c1c9c1</code></td>
    <td>4</td>
    <td>79</td>
  </tr>
<tr>
    <td><code data-color="">#bfc7bf</code></td>
    <td>4</td>
    <td>78</td>
  </tr>
<tr>
    <td><code data-color="">#bcc4bc</code></td>
    <td>4</td>
    <td>77</td>
  </tr>
<tr>
    <td><code data-color="">#bac2ba</code></td>
    <td>4</td>
    <td>76</td>
  </tr>
<tr>
    <td><code data-color="">#b6bfb6</code></td>
    <td>5</td>
    <td>75</td>
  </tr>
<tr>
    <td><code data-color="">#b3bdb3</code></td>
    <td>5</td>
    <td>74</td>
  </tr>
<tr>
    <td><code data-color="">#b1bab1</code></td>
    <td>5</td>
    <td>73</td>
  </tr>
<tr>
    <td><code data-color="">#aeb8ae</code></td>
    <td>5</td>
    <td>72</td>
  </tr>
<tr>
    <td><code data-color="">#acb5ac</code></td>
    <td>5</td>
    <td>71</td>
  </tr>
<tr>
    <td><code data-color="">#aab3aa</code></td>
    <td>5</td>
    <td>70</td>
  </tr>
<tr>
    <td><code data-color="">#a7b0a7</code></td>
    <td>5</td>
    <td>69</td>
  </tr>
<tr>
    <td><code data-color="">#a5ada5</code></td>
    <td>5</td>
    <td>68</td>
  </tr>
<tr>
    <td><code data-color="">#a2aba2</code></td>
    <td>5</td>
    <td>67</td>
  </tr>
<tr>
    <td><code data-color="">#a0a8a0</code></td>
    <td>5</td>
    <td>66</td>
  </tr>
<tr>
    <td><code data-color="">#9da69d</code></td>
    <td>5</td>
    <td>65</td>
  </tr>
<tr>
    <td><code data-color="">#9ba39b</code></td>
    <td>5</td>
    <td>64</td>
  </tr>
<tr>
    <td><code data-color="">#99a199</code></td>
    <td>5</td>
    <td>63</td>
  </tr>
<tr>
    <td><code data-color="">#969e96</code></td>
    <td>5</td>
    <td>62</td>
  </tr>
<tr>
    <td><code data-color="">#949c94</code></td>
    <td>5</td>
    <td>61</td>
  </tr>
<tr>
    <td><code data-color="">#909990</code></td>
    <td>6</td>
    <td>60</td>
  </tr>
<tr>
    <td><code data-color="">#8d968d</code></td>
    <td>6</td>
    <td>59</td>
  </tr>
<tr>
    <td><code data-color="">#8b948b</code></td>
    <td>6</td>
    <td>58</td>
  </tr>
<tr>
    <td><code data-color="">#899189</code></td>
    <td>6</td>
    <td>57</td>
  </tr>
<tr>
    <td><code data-color="">#868f86</code></td>
    <td>6</td>
    <td>56</td>
  </tr>
<tr>
    <td><code data-color="">#848c84</code></td>
    <td>6</td>
    <td>55</td>
  </tr>
<tr>
    <td><code data-color="">#818a81</code></td>
    <td>7</td>
    <td>54</td>
  </tr>
<tr>
    <td><code data-color="">#7f877f</code></td>
    <td>6</td>
    <td>53</td>
  </tr>
<tr>
    <td><code data-color="">#7d857d</code></td>
    <td>6</td>
    <td>52</td>
  </tr>
<tr>
    <td><code data-color="">#7a827a</code></td>
    <td>6</td>
    <td>51</td>
  </tr>
<tr>
    <td><code data-color="">#778077</code></td>
    <td>7</td>
    <td>50</td>
  </tr>
<tr>
    <td><code data-color="">#747d74</code></td>
    <td>7</td>
    <td>49</td>
  </tr>
<tr>
    <td><code data-color="">#727a72</code></td>
    <td>7</td>
    <td>48</td>
  </tr>
<tr>
    <td><code data-color="">#6f786f</code></td>
    <td>7</td>
    <td>47</td>
  </tr>
<tr>
    <td><code data-color="">#6d756d</code></td>
    <td>7</td>
    <td>46</td>
  </tr>
<tr>
    <td><code data-color="">#6b736b</code></td>
    <td>7</td>
    <td>45</td>
  </tr>
<tr>
    <td><code data-color="">#687068</code></td>
    <td>7</td>
    <td>44</td>
  </tr>
<tr>
    <td><code data-color="">#666e66</code></td>
    <td>7</td>
    <td>43</td>
  </tr>
<tr>
    <td><code data-color="">#636b63</code></td>
    <td>7</td>
    <td>42</td>
  </tr>
<tr>
    <td><code data-color="">#606960</code></td>
    <td>9</td>
    <td>41</td>
  </tr>
<tr>
    <td><code data-color="">#5e665e</code></td>
    <td>8</td>
    <td>40</td>
  </tr>
<tr>
    <td><code data-color="">#5b635b</code></td>
    <td>8</td>
    <td>39</td>
  </tr>
<tr>
    <td><code data-color="">#596159</code></td>
    <td>8</td>
    <td>38</td>
  </tr>
<tr>
    <td><code data-color="">#575e57</code></td>
    <td>7</td>
    <td>37</td>
  </tr>
<tr>
    <td><code data-color="">#545c54</code></td>
    <td>9</td>
    <td>36</td>
  </tr>
<tr>
    <td><code data-color="">#515951</code></td>
    <td>9</td>
    <td>35</td>
  </tr>
<tr>
    <td><code data-color="">#4f574f</code></td>
    <td>9</td>
    <td>34</td>
  </tr>
<tr>
    <td><code data-color="">#4d544d</code></td>
    <td>8</td>
    <td>33</td>
  </tr>
<tr>
    <td><code data-color="">#495249</code></td>
    <td>11</td>
    <td>32</td>
  </tr>
<tr>
    <td><code data-color="">#474f47</code></td>
    <td>10</td>
    <td>31</td>
  </tr>
<tr>
    <td><code data-color="">#454d45</code></td>
    <td>10</td>
    <td>30</td>
  </tr>
<tr>
    <td><code data-color="">#434a43</code></td>
    <td>9</td>
    <td>29</td>
  </tr>
<tr>
    <td><code data-color="">#404740</code></td>
    <td>10</td>
    <td>28</td>
  </tr>
<tr>
    <td><code data-color="">#3d453d</code></td>
    <td>12</td>
    <td>27</td>
  </tr>
<tr>
    <td><code data-color="">#3a423a</code></td>
    <td>12</td>
    <td>26</td>
  </tr>
<tr>
    <td><code data-color="">#384038</code></td>
    <td>13</td>
    <td>25</td>
  </tr>
<tr>
    <td><code data-color="">#363d36</code></td>
    <td>11</td>
    <td>24</td>
  </tr>
<tr>
    <td><code data-color="">#333b33</code></td>
    <td>14</td>
    <td>23</td>
  </tr>
<tr>
    <td><code data-color="">#313831</code></td>
    <td>12</td>
    <td>22</td>
  </tr>
<tr>
    <td><code data-color="">#2e362e</code></td>
    <td>15</td>
    <td>21</td>
  </tr>
<tr>
    <td><code data-color="">#2c332c</code></td>
    <td>14</td>
    <td>20</td>
  </tr>
<tr>
    <td><code data-color="">#293029</code></td>
    <td>15</td>
    <td>19</td>
  </tr>
<tr>
    <td><code data-color="">#272e27</code></td>
    <td>15</td>
    <td>18</td>
  </tr>
<tr>
    <td><code data-color="">#242b24</code></td>
    <td>16</td>
    <td>17</td>
  </tr>
<tr>
    <td><code data-color="">#212921</code></td>
    <td>20</td>
    <td>16</td>
  </tr>
<tr>
    <td><code data-color="">#1f261f</code></td>
    <td>18</td>
    <td>15</td>
  </tr>
<tr>
    <td><code data-color="">#1d241d</code></td>
    <td>19</td>
    <td>14</td>
  </tr>
<tr>
    <td><code data-color="">#1a211a</code></td>
    <td>21</td>
    <td>13</td>
  </tr>
<tr>
    <td><code data-color="">#181f18</code></td>
    <td>23</td>
    <td>12</td>
  </tr>
<tr>
    <td><code data-color="">#151c15</code></td>
    <td>25</td>
    <td>11</td>
  </tr>
<tr>
    <td><code data-color="">#121a12</code></td>
    <td>31</td>
    <td>10</td>
  </tr>
<tr>
    <td><code data-color="">#0f170f</code></td>
    <td>35</td>
    <td>9</td>
  </tr>
<tr>
    <td><code data-color="">#0b140b</code></td>
    <td>45</td>
    <td>8</td>
  </tr>
<tr>
    <td><code data-color="">#071207</code></td>
    <td>61</td>
    <td>7</td>
  </tr>
<tr>
    <td><code data-color="">#030f03</code></td>
    <td>80</td>
    <td>6</td>
  </tr>
<tr>
    <td><code data-color="">#000d00</code></td>
    <td>100</td>
    <td>5</td>
  </tr>
<tr>
    <td><code data-color="">#000a00</code></td>
    <td>100</td>
    <td>4</td>
  </tr>
<tr>
    <td><code data-color="">#000800</code></td>
    <td>100</td>
    <td>3</td>
  </tr>
<tr>
    <td><code data-color="">#000500</code></td>
    <td>100</td>
    <td>2</td>
  </tr>
<tr>
    <td><code data-color="">#000300</code></td>
    <td>100</td>
    <td>1</td>
  </tr>
  </tbody>
  </table>
  
  </div>
  

</div>
<figcaption>
The <span data-color="">blue</span> line is our new LCh-based <code>isGray</code>.
The <span data-color="">purple</span> line is our old range-based <code>isGray</code>.
You can see the LCh-based curve is way more reluctant to match saturated colors for a given brightness at this hue.
It plays it safe and sticks closer to the Y axis.
</figcaption>
</figure>

<p>For completeness let’s try 
Let’s chart it at the
<code data-color="hsl(0 100% 50%)">0°</code> hue:</p>

<figure>
<div class="sxs">

  <svg viewBox="0 0 1 1" class="figure">
  <defs>
  <linearGradient id="value" x2="0%" y2="0%" x1="0%" y1="100%">
  <stop offset="0%" stop-color="rgba(0, 0, 0, 1)" />
  <stop offset="100%" stop-color="rgba(0, 0, 0, 0)" />
  </linearGradient>
  <linearGradient id="saturation">
  <stop offset="0%" stop-color="rgba(255, 255, 255, 1)" />
  <stop offset="100%" stop-color="rgba(255, 255, 255, 0)" />
  </linearGradient>
  </defs>

  <rect id="fillBg" width="100%" height="100%" fill="rgb(255, 0, 0)" />
  <rect width="100%" height="100%" fill="url('#saturation')" />
  <rect width="100%" height="100%" fill="url('#value')" />
  
  <polyline points="0.05882352941176472,0 0.059523809523809534,0.0117647058823529 0.06000000000000002,0.019607843137254943 0.056680161943319866,0.03137254901960784 0.06122448979591838,0.039215686274509776 0.0578512396694214,0.050980392156862786 0.06250000000000001,0.05882352941176472 0.059071729957805935,0.07058823529411762 0.059574468085106295,0.07843137254901966 0.06034482758620693,0.09019607843137256 0.060869565217391335,0.0980392156862745 0.0616740088105726,0.1098039215686275 0.06696428571428573,0.1215686274509804 0.07207207207207207,0.12941176470588234 0.06849315068493139,0.14117647058823535 0.06912442396313366,0.14901960784313728 0.07009345794392524,0.16078431372549018 0.07075471698113209,0.1686274509803921 0.07177033492822968,0.18039215686274512 0.07246376811594205,0.18823529411764706 0.06862745098039219,0.19999999999999996 0.06965174129353237,0.21176470588235297 0.07035175879396989,0.2196078431372549 0.06632653061224494,0.2313725490196078 0.07216494845360814,0.23921568627450984 0.07853403141361258,0.25098039215686274 0.07936507936507937,0.2588235294117647 0.0806451612903226,0.2705882352941177 0.0815217391304348,0.2784313725490196 0.07734806629834258,0.2901960784313725 0.08379888268156412,0.29803921568627456 0.07954545454545459,0.30980392156862746 0.07514450867052029,0.32156862745098036 0.08187134502923965,0.3294117647058824 0.07738095238095244,0.3411764705882353 0.0843373493975904,0.34901960784313724 0.08588957055214712,0.36078431372549025 0.09316770186335406,0.3686274509803922 0.0886075949367089,0.3803921568627451 0.08974358974358979,0.388235294117647 0.09150326797385626,0.4 0.08666666666666674,0.4117647058823529 0.0878378378378379,0.41960784313725485 0.08965517241379298,0.43137254901960786 0.09790209790209795,0.4392156862745098 0.10000000000000005,0.4509803921568627 0.10144927536231879,0.45882352941176474 0.09629629629629627,0.47058823529411764 0.10526315789473689,0.4784313725490196 0.09999999999999998,0.4901960784313726 0.11718750000000003,0.4980392156862745 0.11199999999999995,0.5098039215686274 0.1065573770491804,0.5215686274509803 0.10833333333333331,0.5294117647058824 0.11111111111111109,0.5411764705882354 0.12173913043478267,0.5490196078431373 0.11607142857142853,0.5607843137254902 0.12727272727272732,0.5686274509803921 0.1214953271028037,0.580392156862745 0.12380952380952379,0.5882352941176471 0.12745098039215697,0.6 0.1212121212121212,0.611764705882353 0.134020618556701,0.6196078431372549 0.13829787234042565,0.6313725490196078 0.14130434782608692,0.6392156862745098 0.1348314606741573,0.6509803921568628 0.1494252873563218,0.6588235294117647 0.14285714285714285,0.6705882352941177 0.15853658536585377,0.6784313725490196 0.16455696202531642,0.6901960784313725 0.1688311688311688,0.6980392156862745 0.1756756756756757,0.7098039215686274 0.16901408450704225,0.7215686274509804 0.18840579710144922,0.7294117647058824 0.1818181818181819,0.7411764705882353 0.1875,0.7490196078431373 0.18032786885245908,0.7607843137254902 0.20338983050847456,0.7686274509803921 0.21428571428571427,0.7803921568627451 0.2222222222222222,0.788235294117647 0.215686274509804,0.8 0.2291666666666666,0.8117647058823529 0.23913043478260862,0.8196078431372549 0.25581395348837216,0.8313725490196078 0.26829268292682934,0.8392156862745098 0.2631578947368421,0.8509803921568627 0.3055555555555556,0.8588235294117648 0.3030303030303031,0.8705882352941177 0.3548387096774194,0.8784313725490196 0.42857142857142855,0.8901960784313725 0.5384615384615384,0.8980392156862745 0.6956521739130435,0.9098039215686274 0.85,0.9215686274509804 1,0.9294117647058824 1,0.9411764705882353 1,0.9490196078431372 1,0.9607843137254902 1,0.9686274509803922 1,0.9803921568627451 1,0.9882352941176471" fill="none" stroke-width="1%" stroke="blue" />
    <polyline points="0.05882352941176472,0 0.06746031746031755,0.0117647058823529 0.06000000000000002,0.019607843137254943 0.06882591093117407,0.03137254901960784 0.0693877551020408,0.039215686274509776 0.07024793388429751,0.050980392156862786 0.07083333333333332,0.05882352941176472 0.06751054852320675,0.07058823529411762 0.07234042553191489,0.07843137254901966 0.06896551724137931,0.09019607843137256 0.07391304347826085,0.0980392156862745 0.07048458149779736,0.1098039215686275 0.06696428571428573,0.1215686274509804 0.07207207207207207,0.12941176470588234 0.07762557077625569,0.14117647058823535 0.06912442396313366,0.14901960784313728 0.07943925233644858,0.16078431372549018 0.08018867924528313,0.1686274509803921 0.08133971291866027,0.18039215686274512 0.08212560386473428,0.18823529411764706 0.0784313725490196,0.19999999999999996 0.07960199004975124,0.21176470588235297 0.08040201005025126,0.2196078431372549 0.08673469387755114,0.2313725490196078 0.08247422680412371,0.23921568627450984 0.08900523560209422,0.25098039215686274 0.08994708994708993,0.2588235294117647 0.09139784946236558,0.2705882352941177 0.09239130434782607,0.2784313725490196 0.08839779005524862,0.2901960784313725 0.09497206703910613,0.29803921568627456 0.09090909090909091,0.30980392156862746 0.0982658959537572,0.32156862745098036 0.09941520467836255,0.3294117647058824 0.10119047619047616,0.3411764705882353 0.10240963855421684,0.34901960784313724 0.09815950920245399,0.36078431372549025 0.09937888198757765,0.3686274509803922 0.10759493670886074,0.3803921568627451 0.10256410256410256,0.388235294117647 0.11111111111111109,0.4 0.10666666666666666,0.4117647058823529 0.1081081081081081,0.41960784313725485 0.11724137931034481,0.43137254901960786 0.11888111888111885,0.4392156862745098 0.1214285714285715,0.4509803921568627 0.12318840579710143,0.45882352941176474 0.12592592592592589,0.47058823529411764 0.12030075187969934,0.4784313725490196 0.13076923076923075,0.4901960784313726 0.13281249999999997,0.4980392156862745 0.128,0.5098039215686274 0.13934426229508204,0.5215686274509803 0.14166666666666664,0.5294117647058824 0.14529914529914528,0.5411764705882354 0.1478260869565217,0.5490196078431373 0.15178571428571425,0.5607843137254902 0.15454545454545462,0.5686274509803921 0.15887850467289716,0.580392156862745 0.16190476190476188,0.5882352941176471 0.16666666666666677,0.6 0.17171717171717168,0.611764705882353 0.17525773195876285,0.6196078431372549 0.18085106382978733,0.6313725490196078 0.18478260869565213,0.6392156862745098 0.19101123595505615,0.6509803921568628 0.19540229885057467,0.6588235294117647 0.20238095238095233,0.6705882352941177 0.2073170731707318,0.6784313725490196 0.21518987341772156,0.6901960784313725 0.22077922077922074,0.6980392156862745 0.22972972972972974,0.7098039215686274 0.2394366197183099,0.7215686274509804 0.24637681159420285,0.7294117647058824 0.2575757575757576,0.7411764705882353 0.26562499999999994,0.7490196078431373 0.27868852459016397,0.7607843137254902 0.28813559322033905,0.7686274509803921 0.3035714285714285,0.7803921568627451 0.31481481481481477,0.788235294117647 0.33333333333333337,0.8 0.3541666666666667,0.8117647058823529 0.3695652173913043,0.8196078431372549 0.39534883720930236,0.8313725490196078 0.41463414634146345,0.8392156862745098 0.4473684210526316,0.8509803921568627 0.4722222222222222,0.8588235294117648 0.5151515151515152,0.8705882352941177 0.5483870967741935,0.8784313725490196 0.6071428571428571,0.8901960784313725 0.6538461538461539,0.8980392156862745 0.7391304347826086,0.9098039215686274 0.85,0.9215686274509804 0.9444444444444444,0.9294117647058824 1,0.9411764705882353 1,0.9490196078431372 1,0.9607843137254902 1,0.9686274509803922 1,0.9803921568627451 1,0.9882352941176471" fill="none" stroke-width="1%" stroke="purple" />
  </svg>


  <div class="tableContainer">
  
  <table class="table">
    <thead>
      <tr><th>hex</th><th>saturation</th><th>value</th></tr>
    </thead>
  <tbody>
  <tr>
    <td><code data-color="">#fff0f0</code></td>
    <td>6</td>
    <td>100</td>
  </tr>
<tr>
    <td><code data-color="">#fceded</code></td>
    <td>6</td>
    <td>99</td>
  </tr>
<tr>
    <td><code data-color="">#faebeb</code></td>
    <td>6</td>
    <td>98</td>
  </tr>
<tr>
    <td><code data-color="">#f7e9e9</code></td>
    <td>6</td>
    <td>97</td>
  </tr>
<tr>
    <td><code data-color="">#f5e6e6</code></td>
    <td>6</td>
    <td>96</td>
  </tr>
<tr>
    <td><code data-color="">#f2e4e4</code></td>
    <td>6</td>
    <td>95</td>
  </tr>
<tr>
    <td><code data-color="">#f0e1e1</code></td>
    <td>6</td>
    <td>94</td>
  </tr>
<tr>
    <td><code data-color="">#eddfdf</code></td>
    <td>6</td>
    <td>93</td>
  </tr>
<tr>
    <td><code data-color="">#ebdddd</code></td>
    <td>6</td>
    <td>92</td>
  </tr>
<tr>
    <td><code data-color="">#e8dada</code></td>
    <td>6</td>
    <td>91</td>
  </tr>
<tr>
    <td><code data-color="">#e6d8d8</code></td>
    <td>6</td>
    <td>90</td>
  </tr>
<tr>
    <td><code data-color="">#e3d5d5</code></td>
    <td>6</td>
    <td>89</td>
  </tr>
<tr>
    <td><code data-color="">#e0d1d1</code></td>
    <td>7</td>
    <td>88</td>
  </tr>
<tr>
    <td><code data-color="">#decece</code></td>
    <td>7</td>
    <td>87</td>
  </tr>
<tr>
    <td><code data-color="">#dbcccc</code></td>
    <td>7</td>
    <td>86</td>
  </tr>
<tr>
    <td><code data-color="">#d9caca</code></td>
    <td>7</td>
    <td>85</td>
  </tr>
<tr>
    <td><code data-color="">#d6c7c7</code></td>
    <td>7</td>
    <td>84</td>
  </tr>
<tr>
    <td><code data-color="">#d4c5c5</code></td>
    <td>7</td>
    <td>83</td>
  </tr>
<tr>
    <td><code data-color="">#d1c2c2</code></td>
    <td>7</td>
    <td>82</td>
  </tr>
<tr>
    <td><code data-color="">#cfc0c0</code></td>
    <td>7</td>
    <td>81</td>
  </tr>
<tr>
    <td><code data-color="">#ccbebe</code></td>
    <td>7</td>
    <td>80</td>
  </tr>
<tr>
    <td><code data-color="">#c9bbbb</code></td>
    <td>7</td>
    <td>79</td>
  </tr>
<tr>
    <td><code data-color="">#c7b9b9</code></td>
    <td>7</td>
    <td>78</td>
  </tr>
<tr>
    <td><code data-color="">#c4b7b7</code></td>
    <td>7</td>
    <td>77</td>
  </tr>
<tr>
    <td><code data-color="">#c2b4b4</code></td>
    <td>7</td>
    <td>76</td>
  </tr>
<tr>
    <td><code data-color="">#bfb0b0</code></td>
    <td>8</td>
    <td>75</td>
  </tr>
<tr>
    <td><code data-color="">#bdaeae</code></td>
    <td>8</td>
    <td>74</td>
  </tr>
<tr>
    <td><code data-color="">#baabab</code></td>
    <td>8</td>
    <td>73</td>
  </tr>
<tr>
    <td><code data-color="">#b8a9a9</code></td>
    <td>8</td>
    <td>72</td>
  </tr>
<tr>
    <td><code data-color="">#b5a7a7</code></td>
    <td>8</td>
    <td>71</td>
  </tr>
<tr>
    <td><code data-color="">#b3a4a4</code></td>
    <td>8</td>
    <td>70</td>
  </tr>
<tr>
    <td><code data-color="">#b0a2a2</code></td>
    <td>8</td>
    <td>69</td>
  </tr>
<tr>
    <td><code data-color="">#ada0a0</code></td>
    <td>8</td>
    <td>68</td>
  </tr>
<tr>
    <td><code data-color="">#ab9d9d</code></td>
    <td>8</td>
    <td>67</td>
  </tr>
<tr>
    <td><code data-color="">#a89b9b</code></td>
    <td>8</td>
    <td>66</td>
  </tr>
<tr>
    <td><code data-color="">#a69898</code></td>
    <td>8</td>
    <td>65</td>
  </tr>
<tr>
    <td><code data-color="">#a39595</code></td>
    <td>9</td>
    <td>64</td>
  </tr>
<tr>
    <td><code data-color="">#a19292</code></td>
    <td>9</td>
    <td>63</td>
  </tr>
<tr>
    <td><code data-color="">#9e9090</code></td>
    <td>9</td>
    <td>62</td>
  </tr>
<tr>
    <td><code data-color="">#9c8e8e</code></td>
    <td>9</td>
    <td>61</td>
  </tr>
<tr>
    <td><code data-color="">#998b8b</code></td>
    <td>9</td>
    <td>60</td>
  </tr>
<tr>
    <td><code data-color="">#968989</code></td>
    <td>9</td>
    <td>59</td>
  </tr>
<tr>
    <td><code data-color="">#948787</code></td>
    <td>9</td>
    <td>58</td>
  </tr>
<tr>
    <td><code data-color="">#918484</code></td>
    <td>9</td>
    <td>57</td>
  </tr>
<tr>
    <td><code data-color="">#8f8181</code></td>
    <td>10</td>
    <td>56</td>
  </tr>
<tr>
    <td><code data-color="">#8c7e7e</code></td>
    <td>10</td>
    <td>55</td>
  </tr>
<tr>
    <td><code data-color="">#8a7c7c</code></td>
    <td>10</td>
    <td>54</td>
  </tr>
<tr>
    <td><code data-color="">#877a7a</code></td>
    <td>10</td>
    <td>53</td>
  </tr>
<tr>
    <td><code data-color="">#857777</code></td>
    <td>11</td>
    <td>52</td>
  </tr>
<tr>
    <td><code data-color="">#827575</code></td>
    <td>10</td>
    <td>51</td>
  </tr>
<tr>
    <td><code data-color="">#807171</code></td>
    <td>12</td>
    <td>50</td>
  </tr>
<tr>
    <td><code data-color="">#7d6f6f</code></td>
    <td>11</td>
    <td>49</td>
  </tr>
<tr>
    <td><code data-color="">#7a6d6d</code></td>
    <td>11</td>
    <td>48</td>
  </tr>
<tr>
    <td><code data-color="">#786b6b</code></td>
    <td>11</td>
    <td>47</td>
  </tr>
<tr>
    <td><code data-color="">#756868</code></td>
    <td>11</td>
    <td>46</td>
  </tr>
<tr>
    <td><code data-color="">#736565</code></td>
    <td>12</td>
    <td>45</td>
  </tr>
<tr>
    <td><code data-color="">#706363</code></td>
    <td>12</td>
    <td>44</td>
  </tr>
<tr>
    <td><code data-color="">#6e6060</code></td>
    <td>13</td>
    <td>43</td>
  </tr>
<tr>
    <td><code data-color="">#6b5e5e</code></td>
    <td>12</td>
    <td>42</td>
  </tr>
<tr>
    <td><code data-color="">#695c5c</code></td>
    <td>12</td>
    <td>41</td>
  </tr>
<tr>
    <td><code data-color="">#665959</code></td>
    <td>13</td>
    <td>40</td>
  </tr>
<tr>
    <td><code data-color="">#635757</code></td>
    <td>12</td>
    <td>39</td>
  </tr>
<tr>
    <td><code data-color="">#615454</code></td>
    <td>13</td>
    <td>38</td>
  </tr>
<tr>
    <td><code data-color="">#5e5151</code></td>
    <td>14</td>
    <td>37</td>
  </tr>
<tr>
    <td><code data-color="">#5c4f4f</code></td>
    <td>14</td>
    <td>36</td>
  </tr>
<tr>
    <td><code data-color="">#594d4d</code></td>
    <td>13</td>
    <td>35</td>
  </tr>
<tr>
    <td><code data-color="">#574a4a</code></td>
    <td>15</td>
    <td>34</td>
  </tr>
<tr>
    <td><code data-color="">#544848</code></td>
    <td>14</td>
    <td>33</td>
  </tr>
<tr>
    <td><code data-color="">#524545</code></td>
    <td>16</td>
    <td>32</td>
  </tr>
<tr>
    <td><code data-color="">#4f4242</code></td>
    <td>16</td>
    <td>31</td>
  </tr>
<tr>
    <td><code data-color="">#4d4040</code></td>
    <td>17</td>
    <td>30</td>
  </tr>
<tr>
    <td><code data-color="">#4a3d3d</code></td>
    <td>18</td>
    <td>29</td>
  </tr>
<tr>
    <td><code data-color="">#473b3b</code></td>
    <td>17</td>
    <td>28</td>
  </tr>
<tr>
    <td><code data-color="">#453838</code></td>
    <td>19</td>
    <td>27</td>
  </tr>
<tr>
    <td><code data-color="">#423636</code></td>
    <td>18</td>
    <td>26</td>
  </tr>
<tr>
    <td><code data-color="">#403434</code></td>
    <td>19</td>
    <td>25</td>
  </tr>
<tr>
    <td><code data-color="">#3d3232</code></td>
    <td>18</td>
    <td>24</td>
  </tr>
<tr>
    <td><code data-color="">#3b2f2f</code></td>
    <td>20</td>
    <td>23</td>
  </tr>
<tr>
    <td><code data-color="">#382c2c</code></td>
    <td>21</td>
    <td>22</td>
  </tr>
<tr>
    <td><code data-color="">#362a2a</code></td>
    <td>22</td>
    <td>21</td>
  </tr>
<tr>
    <td><code data-color="">#332828</code></td>
    <td>22</td>
    <td>20</td>
  </tr>
<tr>
    <td><code data-color="">#302525</code></td>
    <td>23</td>
    <td>19</td>
  </tr>
<tr>
    <td><code data-color="">#2e2323</code></td>
    <td>24</td>
    <td>18</td>
  </tr>
<tr>
    <td><code data-color="">#2b2020</code></td>
    <td>26</td>
    <td>17</td>
  </tr>
<tr>
    <td><code data-color="">#291e1e</code></td>
    <td>27</td>
    <td>16</td>
  </tr>
<tr>
    <td><code data-color="">#261c1c</code></td>
    <td>26</td>
    <td>15</td>
  </tr>
<tr>
    <td><code data-color="">#241919</code></td>
    <td>31</td>
    <td>14</td>
  </tr>
<tr>
    <td><code data-color="">#211717</code></td>
    <td>30</td>
    <td>13</td>
  </tr>
<tr>
    <td><code data-color="">#1f1414</code></td>
    <td>35</td>
    <td>12</td>
  </tr>
<tr>
    <td><code data-color="">#1c1010</code></td>
    <td>43</td>
    <td>11</td>
  </tr>
<tr>
    <td><code data-color="">#1a0c0c</code></td>
    <td>54</td>
    <td>10</td>
  </tr>
<tr>
    <td><code data-color="">#170707</code></td>
    <td>70</td>
    <td>9</td>
  </tr>
<tr>
    <td><code data-color="">#140303</code></td>
    <td>85</td>
    <td>8</td>
  </tr>
<tr>
    <td><code data-color="">#120000</code></td>
    <td>100</td>
    <td>7</td>
  </tr>
<tr>
    <td><code data-color="">#0f0000</code></td>
    <td>100</td>
    <td>6</td>
  </tr>
<tr>
    <td><code data-color="">#0d0000</code></td>
    <td>100</td>
    <td>5</td>
  </tr>
<tr>
    <td><code data-color="">#0a0000</code></td>
    <td>100</td>
    <td>4</td>
  </tr>
<tr>
    <td><code data-color="">#080000</code></td>
    <td>100</td>
    <td>3</td>
  </tr>
<tr>
    <td><code data-color="">#050000</code></td>
    <td>100</td>
    <td>2</td>
  </tr>
<tr>
    <td><code data-color="">#030000</code></td>
    <td>100</td>
    <td>1</td>
  </tr>
  </tbody>
  </table>
  
  </div>
  

</div>
<figcaption>
The <span data-color="">blue</span> line is our new LCh-based <code>isGray</code>.
The <span data-color="">purple</span> line is our old range-based <code>isGray</code>.
</figcaption>
</figure>

<p>Let’s sample the edge case colors:</p>

<div class="colorbox" style="background: #454545">
<code style="background:#fff0f0">#fff0f0</code>
<code style="background:#f7fff7">#f7fff7</code>
<code style="background:#edf6ff">#edf6ff</code>

<code style="background:#807171">#807171</code>
<code style="background:#778077">#778077</code>
<code style="background:#6f7780">#6f7780</code>

<code style="background:#403434">#403434</code>
<code style="background:#384038">#384038</code>
<code style="background:#313840">#313840</code>
</div>

<div class="colorbox" style="background: #c0c0c0">
<code style="background:#fff0f0">#fff0f0</code>
<code style="background:#f7fff7">#f7fff7</code>
<code style="background:#edf6ff">#edf6ff</code>

<code style="background:#807171">#807171</code>
<code style="background:#778077">#778077</code>
<code style="background:#6f7780">#6f7780</code>

<code style="background:#403434">#403434</code>
<code style="background:#384038">#384038</code>
<code style="background:#313840">#313840</code>
</div>

<div class="colorbox" style="background: #fff">
<code style="background:#fff0f0">#fff0f0</code>
<code style="background:#f7fff7">#f7fff7</code>
<code style="background:#edf6ff">#edf6ff</code>

<code style="background:#807171">#807171</code>
<code style="background:#778077">#778077</code>
<code style="background:#6f7780">#6f7780</code>

<code style="background:#403434">#403434</code>
<code style="background:#384038">#384038</code>
<code style="background:#313840">#313840</code>
</div>

<p>We are definitely getting somewhere.
At this point I’m beginning to doubt my monitor calibration/eyes.
Regardless, I’m gonna go ahead and judge that most people would struggle to classify the above colors as “gray”.
If I asked people to name those colors I’m guessing they would say things like 
“pale blue”, “olive drab”, “dark blue”.</p>

<p>Here’s an example of me manually tweaking these colors in an LCh color picker until
I personally would call them “gray”.</p>

<div class="colorbox" style="background: #454545">
<code style="background:#f8f3f3">#f8f3f3</code>
<code style="background:#fbfefb">#fbfefb</code>
<code style="background:#f1f5f9">#f1f5f9</code>

<code style="background:#7a7373">#7a7373</code>
<code style="background:#7c7e7c">#7c7e7c</code>
<code style="background:#71777d">#71777d</code>

<code style="background:#3c3635">#3c3635</code>
<code style="background:#3d3f3d">#3d3f3d</code>
<code style="background:#34383c">#34383c</code>
</div>

<div class="colorbox" style="background: #c0c0c0">
<code style="background:#f8f3f3">#f8f3f3</code>
<code style="background:#fbfefb">#fbfefb</code>
<code style="background:#f1f5f9">#f1f5f9</code>

<code style="background:#7a7373">#7a7373</code>
<code style="background:#7c7e7c">#7c7e7c</code>
<code style="background:#71777d">#71777d</code>

<code style="background:#3c3635">#3c3635</code>
<code style="background:#3d3f3d">#3d3f3d</code>
<code style="background:#34383c">#34383c</code>
</div>

<div class="colorbox" style="background: #fff">
<code style="background:#f8f3f3">#f8f3f3</code>
<code style="background:#fbfefb">#fbfefb</code>
<code style="background:#f1f5f9">#f1f5f9</code>

<code style="background:#7a7373">#7a7373</code>
<code style="background:#7c7e7c">#7c7e7c</code>
<code style="background:#71777d">#71777d</code>

<code style="background:#3c3635">#3c3635</code>
<code style="background:#3d3f3d">#3d3f3d</code>
<code style="background:#34383c">#34383c</code>
</div>

<p>That is the best I could do in 10 minutes or so.
I’m hesitant to tweak further as there are so many equipment and biological factors
that could cause me to waste a ton of time with diminising returns.
<a href="http://persci.mit.edu/pub_pdfs/gazzan.pdf">The limitations of human vision</a>
could certainly trip me up.</p>

<blockquote>
  <p><img src="/assets/gray/16adelson.jpg" alt="optical illusion" /></p>
</blockquote>

<p>Anyway, the above exercise of manually finding the chroma threshold for those example colors was useful.
Let’s encode my common sense judgement above into an algorithm.</p>

<h2 id="coding-common-sense">Coding common sense</h2>

<p>When determining a chroma threshold for our algorithm, we should consider these findings from above:</p>

<ul>
  <li>Blue colors should be given lots of leeway. Blues with a low chroma look like “cool grays” to me.</li>
  <li>Orange colors should be given lots of leeway.
Orange is a gateway to brown. Oranges with low chroma look like “warm grays” to me.</li>
  <li>Red colors should be judged somewhere in the middle.</li>
  <li>Green colors should be scrutinized closely.</li>
</ul>

<p>Let’s slice up our 3D problem into 2D projections and 
apply some shader functions to our classifier curves to approximate our desired result.</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">_clamp</span><span class="p">(</span><span class="nx">x</span><span class="p">,</span> <span class="nx">min</span><span class="p">,</span> <span class="nx">max</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">if</span> <span class="p">(</span><span class="nx">x</span> <span class="o">&lt;</span> <span class="nx">min</span><span class="p">)</span> <span class="p">{</span>
    <span class="nx">x</span> <span class="o">=</span> <span class="nx">min</span>
  <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nx">x</span> <span class="o">&gt;</span> <span class="nx">max</span><span class="p">)</span> <span class="p">{</span>
    <span class="nx">x</span> <span class="o">=</span> <span class="nx">max</span>
  <span class="p">}</span>
  <span class="k">return</span> <span class="nx">x</span>
<span class="p">}</span>

<span class="kd">function</span> <span class="nx">_smoothstep</span><span class="p">(</span><span class="nx">a</span><span class="p">,</span> <span class="nx">b</span><span class="p">,</span> <span class="nx">x</span><span class="p">)</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">t</span> <span class="o">=</span> <span class="nx">_clamp</span><span class="p">((</span><span class="nx">x</span> <span class="o">-</span> <span class="nx">a</span><span class="p">)</span> <span class="o">/</span> <span class="p">(</span><span class="nx">b</span> <span class="o">-</span> <span class="nx">a</span><span class="p">),</span> <span class="mf">0.0</span><span class="p">,</span> <span class="mf">1.0</span><span class="p">);</span>
  <span class="k">return</span> <span class="nx">t</span> <span class="o">*</span> <span class="nx">t</span> <span class="o">*</span> <span class="p">(</span><span class="mf">3.0</span> <span class="o">-</span> <span class="mf">2.0</span> <span class="o">*</span> <span class="nx">t</span><span class="p">);</span>
<span class="p">}</span>

<span class="kd">const</span> <span class="nx">hueToleranceStopsV1</span> <span class="o">=</span> <span class="p">[</span>
  <span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="mf">3.1</span><span class="p">],</span> <span class="c1">// red</span>
  <span class="p">[</span><span class="mi">20</span><span class="p">,</span> <span class="mf">3.1</span><span class="p">],</span> <span class="c1">// orange</span>
  <span class="p">[</span><span class="mf">61.8</span><span class="p">,</span> <span class="mi">8</span><span class="p">],</span> <span class="c1">// orange</span>
  <span class="p">[</span><span class="mi">66</span><span class="p">,</span> <span class="mf">7.49</span><span class="p">],</span> <span class="c1">// orange</span>
  <span class="p">[</span><span class="mi">75</span><span class="p">,</span> <span class="mf">6.8</span><span class="p">],</span> <span class="c1">// orange</span>
  <span class="p">[</span><span class="mi">100</span><span class="p">,</span> <span class="mi">2</span><span class="p">],</span> <span class="c1">// yellow-green</span>
  <span class="p">[</span><span class="mi">180</span><span class="p">,</span> <span class="mi">2</span><span class="p">],</span> <span class="c1">// green</span>
  <span class="p">[</span><span class="mi">240</span><span class="p">,</span> <span class="mf">5.4</span><span class="p">],</span> <span class="c1">// blue</span>
  <span class="p">[</span><span class="mi">262</span><span class="p">,</span> <span class="mi">14</span><span class="p">],</span> <span class="c1">// blue</span>
  <span class="p">[</span><span class="mi">263</span><span class="p">,</span> <span class="mi">14</span><span class="p">],</span> <span class="c1">// blue</span>
  <span class="p">[</span><span class="mi">290</span><span class="p">,</span> <span class="mf">6.5</span><span class="p">],</span>
  <span class="p">[</span><span class="mi">310</span><span class="p">,</span> <span class="mf">3.1</span><span class="p">],</span> <span class="c1">// red</span>
  <span class="p">[</span><span class="mi">360</span><span class="p">,</span> <span class="mf">3.1</span><span class="p">],</span>
<span class="p">];</span>

<span class="kd">function</span> <span class="nx">_buildCurve</span><span class="p">(</span><span class="nx">stops</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">return</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">value</span><span class="p">)</span> <span class="p">{</span>
    <span class="kd">let</span> <span class="nx">a</span><span class="p">,</span> <span class="nx">b</span><span class="p">,</span> <span class="k">from</span><span class="p">,</span> <span class="nx">to</span><span class="p">;</span>
    <span class="k">for</span> <span class="p">(</span><span class="kd">const</span> <span class="p">[</span><span class="nx">x</span><span class="p">,</span> <span class="nx">y</span><span class="p">]</span> <span class="k">of</span> <span class="nx">stops</span><span class="p">)</span> <span class="p">{</span>
      <span class="k">if</span> <span class="p">(</span><span class="nx">value</span> <span class="o">&gt;=</span> <span class="nx">x</span><span class="p">)</span> <span class="p">{</span>
        <span class="nx">a</span> <span class="o">=</span> <span class="nx">x</span><span class="p">;</span>
        <span class="k">from</span> <span class="o">=</span> <span class="nx">y</span><span class="p">;</span>
      <span class="p">}</span>
      <span class="k">if</span> <span class="p">(</span><span class="nx">value</span> <span class="o">&lt;=</span> <span class="nx">x</span><span class="p">)</span> <span class="p">{</span>
        <span class="nx">b</span> <span class="o">=</span> <span class="nx">x</span><span class="p">;</span>
        <span class="nx">to</span> <span class="o">=</span> <span class="nx">y</span><span class="p">;</span>
        <span class="k">break</span><span class="p">;</span>
      <span class="p">}</span>
    <span class="p">}</span>
    <span class="k">if</span> <span class="p">(</span><span class="nx">a</span> <span class="o">===</span> <span class="nx">b</span><span class="p">)</span> <span class="p">{</span>
      <span class="k">return</span> <span class="nx">to</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="k">return</span> <span class="nx">_smoothstep</span><span class="p">(</span><span class="nx">a</span><span class="p">,</span> <span class="nx">b</span><span class="p">,</span> <span class="nx">value</span><span class="p">)</span> <span class="o">*</span> <span class="p">(</span><span class="nx">to</span> <span class="o">-</span> <span class="k">from</span><span class="p">)</span> <span class="o">+</span> <span class="k">from</span><span class="p">;</span>
  <span class="p">}</span>
<span class="p">}</span>

<span class="kd">const</span> <span class="nx">hueCurveLCh</span> <span class="o">=</span> <span class="nx">_buildCurve</span><span class="p">(</span><span class="nx">hueToleranceStopsV1</span><span class="p">);</span>

<span class="kd">function</span> <span class="nx">isGray</span><span class="p">(</span><span class="nx">color</span><span class="p">)</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="p">{</span><span class="na">l</span><span class="p">:</span> <span class="nx">lightness</span><span class="p">,</span> <span class="na">c</span><span class="p">:</span> <span class="nx">chroma</span><span class="p">,</span> <span class="na">h</span><span class="p">:</span> <span class="nx">hue</span><span class="p">}</span> <span class="o">=</span> <span class="nx">lch</span><span class="p">(</span><span class="nx">color</span><span class="p">);</span>
  <span class="c1">// black and white</span>
  <span class="k">if</span> <span class="p">([</span><span class="mi">0</span><span class="p">,</span> <span class="mi">100</span><span class="p">].</span><span class="nx">includes</span><span class="p">(</span><span class="nx">lightness</span><span class="p">))</span> <span class="p">{</span>
    <span class="k">return</span> <span class="kc">true</span><span class="p">;</span>
  <span class="p">}</span>
  <span class="c1">// grayscale</span>
  <span class="k">if</span> <span class="p">(</span><span class="nx">chroma</span> <span class="o">===</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span> <span class="kc">true</span><span class="p">;</span>
  <span class="p">}</span>
  <span class="k">return</span> <span class="nx">chroma</span> <span class="o">&lt;=</span> <span class="nx">hueCurveLCh</span><span class="p">(</span><span class="nx">hue</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Below is a visualization of <code class="language-plaintext highlighter-rouge">hueCurveLCh</code>:</p>

<figure>
<img src="/assets/gray/hue-curve.svg" alt="hue sensitivity" />
<figcaption>The <span data-color="">blue</span> line is how much leeway I want to give our
chroma threshold for a given hue. The higher the value the more leeway we give it.</figcaption>
</figure>

<p>Let’s dump all the edge case values this function matches. The following table shows
the <strong>most</strong> chromatic colors matched for a given hue and lightness.
These colors sit at the threshold of what this function considers “gray”.</p>

<div class="tableContainer">
<table class="table finalTable">
<tbody>
<tr>
<td></td>
<td style="background: white">#ffffff</td>
<td colspan="8"></td>
<td style="background: black; color: white">#000000</td>
</tr>
<tr>
<td style="background:rgb(255, 0, 0)">0&deg;&nbsp;HSV / 
20&deg;&nbsp;LCh</td>
<td style="background:rgb(255, 247, 247)">#fff7f7</td><td style="background:rgb(230, 223, 223)">#e6dfdf</td><td style="background:rgb(204, 198, 198)">#ccc6c6</td><td style="background:rgb(179, 171, 171)">#b3abab</td><td style="background:rgb(153, 147, 147)">#999393</td><td style="background:rgb(128, 121, 121)">#807979</td><td style="background:rgb(102, 96, 96)">#666060</td><td style="background:rgb(77, 70, 70)">#4d4646</td><td style="background:rgb(51, 45, 45)">#332d2d</td><td style="background:rgb(26, 20, 20)">#1a1414</td>
</tr>
<tr>
<td style="background:rgb(255, 21, 0)">5&deg;&nbsp;HSV / 
27&deg;&nbsp;LCh</td>
<td style="background:rgb(255, 248, 247)">#fff8f7</td><td style="background:rgb(230, 221, 220)">#e6dddc</td><td style="background:rgb(204, 197, 196)">#ccc5c4</td><td style="background:rgb(179, 170, 170)">#b3aaaa</td><td style="background:rgb(153, 146, 145)">#999291</td><td style="background:rgb(128, 120, 120)">#807878</td><td style="background:rgb(102, 95, 94)">#665f5e</td><td style="background:rgb(77, 69, 69)">#4d4545</td><td style="background:rgb(51, 44, 44)">#332c2c</td><td style="background:rgb(26, 19, 19)">#1a1313</td>
</tr>
<tr>
<td style="background:rgb(255, 42, 0)">10&deg;&nbsp;HSV / 
35&deg;&nbsp;LCh</td>
<td style="background:rgb(255, 244, 242)">#fff4f2</td><td style="background:rgb(230, 220, 218)">#e6dcda</td><td style="background:rgb(204, 194, 192)">#ccc2c0</td><td style="background:rgb(179, 168, 166)">#b3a8a6</td><td style="background:rgb(153, 143, 141)">#998f8d</td><td style="background:rgb(128, 118, 116)">#807674</td><td style="background:rgb(102, 93, 91)">#665d5b</td><td style="background:rgb(77, 68, 66)">#4d4442</td><td style="background:rgb(51, 43, 41)">#332b29</td><td style="background:rgb(26, 18, 16)">#1a1210</td>
</tr>
<tr>
<td style="background:rgb(255, 64, 0)">15&deg;&nbsp;HSV / 
43&deg;&nbsp;LCh</td>
<td style="background:rgb(255, 242, 237)">#fff2ed</td><td style="background:rgb(230, 216, 211)">#e6d8d3</td><td style="background:rgb(204, 192, 188)">#ccc0bc</td><td style="background:rgb(179, 166, 162)">#b3a6a2</td><td style="background:rgb(153, 140, 136)">#998c88</td><td style="background:rgb(128, 116, 112)">#807470</td><td style="background:rgb(102, 91, 87)">#665b57</td><td style="background:rgb(77, 66, 62)">#4d423e</td><td style="background:rgb(51, 41, 38)">#332926</td><td style="background:rgb(26, 16, 13)">#1a100d</td>
</tr>
<tr>
<td style="background:rgb(255, 85, 0)">20&deg;&nbsp;HSV / 
52&deg;&nbsp;LCh</td>
<td style="background:rgb(255, 240, 232)">#fff0e8</td><td style="background:rgb(230, 216, 209)">#e6d8d1</td><td style="background:rgb(204, 190, 184)">#ccbeb8</td><td style="background:rgb(179, 164, 157)">#b3a49d</td><td style="background:rgb(153, 140, 133)">#998c85</td><td style="background:rgb(128, 115, 108)">#80736c</td><td style="background:rgb(102, 90, 84)">#665a54</td><td style="background:rgb(77, 65, 59)">#4d413b</td><td style="background:rgb(51, 40, 35)">#332823</td><td style="background:rgb(26, 14, 8)">#1a0e08</td>
</tr>
<tr>
<td style="background:rgb(255, 106, 0)">25&deg;&nbsp;HSV / 
61&deg;&nbsp;LCh</td>
<td style="background:rgb(255, 242, 232)">#fff2e8</td><td style="background:rgb(230, 216, 207)">#e6d8cf</td><td style="background:rgb(204, 191, 182)">#ccbfb6</td><td style="background:rgb(179, 165, 155)">#b3a59b</td><td style="background:rgb(153, 141, 132)">#998d84</td><td style="background:rgb(128, 115, 106)">#80736a</td><td style="background:rgb(102, 90, 82)">#665a52</td><td style="background:rgb(77, 65, 57)">#4d4139</td><td style="background:rgb(51, 41, 33)">#332921</td><td style="background:rgb(26, 13, 4)">#1a0d04</td>
</tr>
<tr>
<td style="background:rgb(255, 128, 0)">30&deg;&nbsp;HSV / 
70&deg;&nbsp;LCh</td>
<td style="background:rgb(255, 245, 235)">#fff5eb</td><td style="background:rgb(230, 219, 209)">#e6dbd1</td><td style="background:rgb(204, 194, 184)">#ccc2b8</td><td style="background:rgb(179, 169, 159)">#b3a99f</td><td style="background:rgb(153, 143, 133)">#998f85</td><td style="background:rgb(128, 118, 108)">#80766c</td><td style="background:rgb(102, 93, 84)">#665d54</td><td style="background:rgb(77, 68, 59)">#4d443b</td><td style="background:rgb(51, 43, 35)">#332b23</td><td style="background:rgb(26, 13, 1)">#1a0d01</td>
</tr>
<tr>
<td style="background:rgb(255, 149, 0)">35&deg;&nbsp;HSV / 
78&deg;&nbsp;LCh</td>
<td style="background:rgb(255, 248, 237)">#fff8ed</td><td style="background:rgb(230, 222, 211)">#e6ded3</td><td style="background:rgb(204, 196, 186)">#ccc4ba</td><td style="background:rgb(179, 171, 161)">#b3aba1</td><td style="background:rgb(153, 146, 136)">#999288</td><td style="background:rgb(128, 121, 111)">#80796f</td><td style="background:rgb(102, 95, 86)">#665f56</td><td style="background:rgb(77, 70, 61)">#4d463d</td><td style="background:rgb(51, 45, 37)">#332d25</td><td style="background:rgb(26, 16, 4)">#1a1004</td>
</tr>
<tr>
<td style="background:rgb(255, 170, 0)">40&deg;&nbsp;HSV / 
85&deg;&nbsp;LCh</td>
<td style="background:rgb(255, 251, 242)">#fffbf2</td><td style="background:rgb(230, 226, 218)">#e6e2da</td><td style="background:rgb(204, 200, 192)">#ccc8c0</td><td style="background:rgb(179, 174, 166)">#b3aea6</td><td style="background:rgb(153, 149, 141)">#99958d</td><td style="background:rgb(128, 124, 116)">#807c74</td><td style="background:rgb(102, 98, 91)">#66625b</td><td style="background:rgb(77, 73, 65)">#4d4941</td><td style="background:rgb(51, 47, 40)">#332f28</td><td style="background:rgb(26, 20, 9)">#1a1409</td>
</tr>
<tr>
<td style="background:rgb(255, 191, 0)">45&deg;&nbsp;HSV / 
92&deg;&nbsp;LCh</td>
<td style="background:rgb(255, 253, 247)">#fffdf7</td><td style="background:rgb(230, 228, 223)">#e6e4df</td><td style="background:rgb(204, 202, 198)">#cccac6</td><td style="background:rgb(179, 177, 171)">#b3b1ab</td><td style="background:rgb(153, 151, 145)">#999791</td><td style="background:rgb(128, 126, 121)">#807e79</td><td style="background:rgb(102, 100, 95)">#66645f</td><td style="background:rgb(77, 75, 70)">#4d4b46</td><td style="background:rgb(51, 49, 44)">#33312c</td><td style="background:rgb(26, 24, 19)">#1a1813</td>
</tr>
<tr>
<td style="background:rgb(255, 213, 0)">50&deg;&nbsp;HSV / 
97&deg;&nbsp;LCh</td>
<td style="background:rgb(255, 254, 250)">#fffefa</td><td style="background:rgb(230, 229, 225)">#e6e5e1</td><td style="background:rgb(204, 203, 200)">#cccbc8</td><td style="background:rgb(179, 178, 175)">#b3b2af</td><td style="background:rgb(153, 152, 148)">#999894</td><td style="background:rgb(128, 127, 124)">#807f7c</td><td style="background:rgb(102, 101, 98)">#666562</td><td style="background:rgb(77, 76, 73)">#4d4c49</td><td style="background:rgb(51, 50, 47)">#33322f</td><td style="background:rgb(26, 25, 22)">#1a1916</td>
</tr>
<tr>
<td style="background:rgb(255, 234, 0)">55&deg;&nbsp;HSV / 
101&deg;&nbsp;LCh</td>
<td style="background:rgb(255, 255, 252)">#fffffc</td><td style="background:rgb(230, 229, 227)">#e6e5e3</td><td style="background:rgb(204, 204, 200)">#ccccc8</td><td style="background:rgb(179, 178, 175)">#b3b2af</td><td style="background:rgb(153, 153, 150)">#999996</td><td style="background:rgb(128, 127, 124)">#807f7c</td><td style="background:rgb(102, 102, 99)">#666663</td><td style="background:rgb(77, 76, 73)">#4d4c49</td><td style="background:rgb(51, 51, 48)">#333330</td><td style="background:rgb(26, 25, 23)">#1a1917</td>
</tr>
<tr>
<td style="background:rgb(255, 255, 0)">60&deg;&nbsp;HSV / 
105&deg;&nbsp;LCh</td>
<td style="background:rgb(255, 255, 252)">#fffffc</td><td style="background:rgb(230, 230, 227)">#e6e6e3</td><td style="background:rgb(204, 204, 202)">#ccccca</td><td style="background:rgb(179, 179, 175)">#b3b3af</td><td style="background:rgb(153, 153, 150)">#999996</td><td style="background:rgb(128, 128, 125)">#80807d</td><td style="background:rgb(102, 102, 99)">#666663</td><td style="background:rgb(77, 77, 73)">#4d4d49</td><td style="background:rgb(51, 51, 48)">#333330</td><td style="background:rgb(26, 26, 23)">#1a1a17</td>
</tr>
<tr>
<td style="background:rgb(234, 255, 0)">65&deg;&nbsp;HSV / 
109&deg;&nbsp;LCh</td>
<td style="background:rgb(255, 255, 252)">#fffffc</td><td style="background:rgb(229, 230, 227)">#e5e6e3</td><td style="background:rgb(204, 204, 202)">#ccccca</td><td style="background:rgb(178, 179, 175)">#b2b3af</td><td style="background:rgb(153, 153, 150)">#999996</td><td style="background:rgb(127, 128, 125)">#7f807d</td><td style="background:rgb(102, 102, 99)">#666663</td><td style="background:rgb(76, 77, 73)">#4c4d49</td><td style="background:rgb(51, 51, 48)">#333330</td><td style="background:rgb(25, 26, 23)">#191a17</td>
</tr>
<tr>
<td style="background:rgb(212, 255, 0)">70&deg;&nbsp;HSV / 
112&deg;&nbsp;LCh</td>
<td style="background:rgb(255, 255, 252)">#fffffc</td><td style="background:rgb(229, 230, 227)">#e5e6e3</td><td style="background:rgb(204, 204, 202)">#ccccca</td><td style="background:rgb(178, 179, 175)">#b2b3af</td><td style="background:rgb(152, 153, 150)">#989996</td><td style="background:rgb(127, 128, 125)">#7f807d</td><td style="background:rgb(101, 102, 99)">#656663</td><td style="background:rgb(76, 77, 73)">#4c4d49</td><td style="background:rgb(51, 51, 48)">#333330</td><td style="background:rgb(25, 26, 23)">#191a17</td>
</tr>
<tr>
<td style="background:rgb(191, 255, 0)">75&deg;&nbsp;HSV / 
116&deg;&nbsp;LCh</td>
<td style="background:rgb(254, 255, 252)">#fefffc</td><td style="background:rgb(229, 230, 227)">#e5e6e3</td><td style="background:rgb(203, 204, 202)">#cbccca</td><td style="background:rgb(178, 179, 175)">#b2b3af</td><td style="background:rgb(152, 153, 150)">#989996</td><td style="background:rgb(127, 128, 125)">#7f807d</td><td style="background:rgb(101, 102, 99)">#656663</td><td style="background:rgb(76, 77, 73)">#4c4d49</td><td style="background:rgb(50, 51, 48)">#323330</td><td style="background:rgb(25, 26, 23)">#191a17</td>
</tr>
<tr>
<td style="background:rgb(170, 255, 0)">80&deg;&nbsp;HSV / 
119&deg;&nbsp;LCh</td>
<td style="background:rgb(254, 255, 252)">#fefffc</td><td style="background:rgb(229, 230, 227)">#e5e6e3</td><td style="background:rgb(203, 204, 202)">#cbccca</td><td style="background:rgb(177, 179, 175)">#b1b3af</td><td style="background:rgb(152, 153, 150)">#989996</td><td style="background:rgb(127, 128, 125)">#7f807d</td><td style="background:rgb(101, 102, 99)">#656663</td><td style="background:rgb(75, 77, 73)">#4b4d49</td><td style="background:rgb(50, 51, 48)">#323330</td><td style="background:rgb(25, 26, 23)">#191a17</td>
</tr>
<tr>
<td style="background:rgb(149, 255, 0)">85&deg;&nbsp;HSV / 
122&deg;&nbsp;LCh</td>
<td style="background:rgb(254, 255, 252)">#fefffc</td><td style="background:rgb(229, 230, 227)">#e5e6e3</td><td style="background:rgb(203, 204, 202)">#cbccca</td><td style="background:rgb(177, 179, 175)">#b1b3af</td><td style="background:rgb(152, 153, 150)">#989996</td><td style="background:rgb(126, 128, 125)">#7e807d</td><td style="background:rgb(101, 102, 99)">#656663</td><td style="background:rgb(75, 77, 73)">#4b4d49</td><td style="background:rgb(50, 51, 48)">#323330</td><td style="background:rgb(24, 26, 23)">#181a17</td>
</tr>
<tr>
<td style="background:rgb(128, 255, 0)">90&deg;&nbsp;HSV / 
126&deg;&nbsp;LCh</td>
<td style="background:rgb(254, 255, 252)">#fefffc</td><td style="background:rgb(228, 230, 227)">#e4e6e3</td><td style="background:rgb(203, 204, 202)">#cbccca</td><td style="background:rgb(177, 179, 175)">#b1b3af</td><td style="background:rgb(151, 153, 150)">#979996</td><td style="background:rgb(126, 128, 125)">#7e807d</td><td style="background:rgb(100, 102, 99)">#646663</td><td style="background:rgb(75, 77, 73)">#4b4d49</td><td style="background:rgb(50, 51, 48)">#323330</td><td style="background:rgb(24, 26, 23)">#181a17</td>
</tr>
<tr>
<td style="background:rgb(106, 255, 0)">95&deg;&nbsp;HSV / 
129&deg;&nbsp;LCh</td>
<td style="background:rgb(254, 255, 252)">#fefffc</td><td style="background:rgb(228, 230, 227)">#e4e6e3</td><td style="background:rgb(203, 204, 202)">#cbccca</td><td style="background:rgb(177, 179, 177)">#b1b3b1</td><td style="background:rgb(151, 153, 150)">#979996</td><td style="background:rgb(126, 128, 125)">#7e807d</td><td style="background:rgb(100, 102, 99)">#646663</td><td style="background:rgb(75, 77, 73)">#4b4d49</td><td style="background:rgb(50, 51, 48)">#323330</td><td style="background:rgb(24, 26, 23)">#181a17</td>
</tr>
<tr>
<td style="background:rgb(85, 255, 0)">100&deg;&nbsp;HSV / 
132&deg;&nbsp;LCh</td>
<td style="background:rgb(253, 255, 252)">#fdfffc</td><td style="background:rgb(228, 230, 227)">#e4e6e3</td><td style="background:rgb(203, 204, 202)">#cbccca</td><td style="background:rgb(177, 179, 177)">#b1b3b1</td><td style="background:rgb(151, 153, 150)">#979996</td><td style="background:rgb(126, 128, 125)">#7e807d</td><td style="background:rgb(100, 102, 99)">#646663</td><td style="background:rgb(75, 77, 74)">#4b4d4a</td><td style="background:rgb(49, 51, 48)">#313330</td><td style="background:rgb(24, 26, 23)">#181a17</td>
</tr>
<tr>
<td style="background:rgb(64, 255, 0)">105&deg;&nbsp;HSV / 
135&deg;&nbsp;LCh</td>
<td style="background:rgb(253, 255, 252)">#fdfffc</td><td style="background:rgb(228, 230, 227)">#e4e6e3</td><td style="background:rgb(202, 204, 202)">#caccca</td><td style="background:rgb(177, 179, 177)">#b1b3b1</td><td style="background:rgb(151, 153, 150)">#979996</td><td style="background:rgb(126, 128, 125)">#7e807d</td><td style="background:rgb(100, 102, 99)">#646663</td><td style="background:rgb(75, 77, 74)">#4b4d4a</td><td style="background:rgb(49, 51, 48)">#313330</td><td style="background:rgb(24, 26, 23)">#181a17</td>
</tr>
<tr>
<td style="background:rgb(43, 255, 0)">110&deg;&nbsp;HSV / 
138&deg;&nbsp;LCh</td>
<td style="background:rgb(253, 255, 252)">#fdfffc</td><td style="background:rgb(228, 230, 227)">#e4e6e3</td><td style="background:rgb(202, 204, 202)">#caccca</td><td style="background:rgb(177, 179, 177)">#b1b3b1</td><td style="background:rgb(150, 153, 150)">#969996</td><td style="background:rgb(125, 128, 125)">#7d807d</td><td style="background:rgb(100, 102, 100)">#646664</td><td style="background:rgb(75, 77, 74)">#4b4d4a</td><td style="background:rgb(49, 51, 48)">#313330</td><td style="background:rgb(24, 26, 23)">#181a17</td>
</tr>
<tr>
<td style="background:rgb(21, 255, 0)">115&deg;&nbsp;HSV / 
140&deg;&nbsp;LCh</td>
<td style="background:rgb(253, 255, 252)">#fdfffc</td><td style="background:rgb(227, 230, 227)">#e3e6e3</td><td style="background:rgb(202, 204, 202)">#caccca</td><td style="background:rgb(177, 179, 177)">#b1b3b1</td><td style="background:rgb(150, 153, 150)">#969996</td><td style="background:rgb(125, 128, 125)">#7d807d</td><td style="background:rgb(100, 102, 100)">#646664</td><td style="background:rgb(74, 77, 74)">#4a4d4a</td><td style="background:rgb(49, 51, 48)">#313330</td><td style="background:rgb(23, 26, 23)">#171a17</td>
</tr>
<tr>
<td style="background:rgb(0, 255, 0)">120&deg;&nbsp;HSV / 
143&deg;&nbsp;LCh</td>
<td style="background:rgb(252, 255, 252)">#fcfffc</td><td style="background:rgb(227, 230, 227)">#e3e6e3</td><td style="background:rgb(202, 204, 202)">#caccca</td><td style="background:rgb(177, 179, 177)">#b1b3b1</td><td style="background:rgb(150, 153, 150)">#969996</td><td style="background:rgb(125, 128, 125)">#7d807d</td><td style="background:rgb(100, 102, 100)">#646664</td><td style="background:rgb(74, 77, 74)">#4a4d4a</td><td style="background:rgb(49, 51, 49)">#313331</td><td style="background:rgb(23, 26, 23)">#171a17</td>
</tr>
<tr>
<td style="background:rgb(0, 255, 21)">125&deg;&nbsp;HSV / 
145&deg;&nbsp;LCh</td>
<td style="background:rgb(252, 255, 253)">#fcfffd</td><td style="background:rgb(227, 230, 227)">#e3e6e3</td><td style="background:rgb(202, 204, 202)">#caccca</td><td style="background:rgb(177, 179, 177)">#b1b3b1</td><td style="background:rgb(150, 153, 150)">#969996</td><td style="background:rgb(125, 128, 125)">#7d807d</td><td style="background:rgb(100, 102, 100)">#646664</td><td style="background:rgb(74, 77, 74)">#4a4d4a</td><td style="background:rgb(48, 51, 49)">#303331</td><td style="background:rgb(23, 26, 23)">#171a17</td>
</tr>
<tr>
<td style="background:rgb(0, 255, 42)">130&deg;&nbsp;HSV / 
148&deg;&nbsp;LCh</td>
<td style="background:rgb(252, 255, 253)">#fcfffd</td><td style="background:rgb(227, 230, 228)">#e3e6e4</td><td style="background:rgb(202, 204, 202)">#caccca</td><td style="background:rgb(177, 179, 177)">#b1b3b1</td><td style="background:rgb(150, 153, 150)">#969996</td><td style="background:rgb(125, 128, 125)">#7d807d</td><td style="background:rgb(99, 102, 99)">#636663</td><td style="background:rgb(74, 77, 75)">#4a4d4b</td><td style="background:rgb(48, 51, 49)">#303331</td><td style="background:rgb(23, 26, 23)">#171a17</td>
</tr>
<tr>
<td style="background:rgb(0, 255, 64)">135&deg;&nbsp;HSV / 
152&deg;&nbsp;LCh</td>
<td style="background:rgb(252, 255, 253)">#fcfffd</td><td style="background:rgb(227, 230, 228)">#e3e6e4</td><td style="background:rgb(202, 204, 202)">#caccca</td><td style="background:rgb(175, 179, 176)">#afb3b0</td><td style="background:rgb(150, 153, 151)">#969997</td><td style="background:rgb(125, 128, 126)">#7d807e</td><td style="background:rgb(99, 102, 100)">#636664</td><td style="background:rgb(73, 77, 74)">#494d4a</td><td style="background:rgb(48, 51, 49)">#303331</td><td style="background:rgb(23, 26, 24)">#171a18</td>
</tr>
<tr>
<td style="background:rgb(0, 255, 85)">140&deg;&nbsp;HSV / 
155&deg;&nbsp;LCh</td>
<td style="background:rgb(252, 255, 253)">#fcfffd</td><td style="background:rgb(227, 230, 228)">#e3e6e4</td><td style="background:rgb(202, 204, 203)">#cacccb</td><td style="background:rgb(175, 179, 176)">#afb3b0</td><td style="background:rgb(150, 153, 151)">#969997</td><td style="background:rgb(125, 128, 126)">#7d807e</td><td style="background:rgb(99, 102, 100)">#636664</td><td style="background:rgb(73, 77, 74)">#494d4a</td><td style="background:rgb(48, 51, 49)">#303331</td><td style="background:rgb(23, 26, 24)">#171a18</td>
</tr>
<tr>
<td style="background:rgb(0, 255, 106)">145&deg;&nbsp;HSV / 
159&deg;&nbsp;LCh</td>
<td style="background:rgb(252, 255, 254)">#fcfffe</td><td style="background:rgb(227, 230, 228)">#e3e6e4</td><td style="background:rgb(200, 204, 202)">#c8ccca</td><td style="background:rgb(175, 179, 176)">#afb3b0</td><td style="background:rgb(150, 153, 151)">#969997</td><td style="background:rgb(124, 128, 125)">#7c807d</td><td style="background:rgb(99, 102, 100)">#636664</td><td style="background:rgb(73, 77, 75)">#494d4b</td><td style="background:rgb(48, 51, 49)">#303331</td><td style="background:rgb(23, 26, 24)">#171a18</td>
</tr>
<tr>
<td style="background:rgb(0, 255, 128)">150&deg;&nbsp;HSV / 
164&deg;&nbsp;LCh</td>
<td style="background:rgb(252, 255, 254)">#fcfffe</td><td style="background:rgb(225, 230, 227)">#e1e6e3</td><td style="background:rgb(200, 204, 202)">#c8ccca</td><td style="background:rgb(175, 179, 177)">#afb3b1</td><td style="background:rgb(150, 153, 151)">#969997</td><td style="background:rgb(124, 128, 126)">#7c807e</td><td style="background:rgb(99, 102, 100)">#636664</td><td style="background:rgb(73, 77, 75)">#494d4b</td><td style="background:rgb(48, 51, 49)">#303331</td><td style="background:rgb(22, 26, 24)">#161a18</td>
</tr>
<tr>
<td style="background:rgb(0, 255, 149)">155&deg;&nbsp;HSV / 
169&deg;&nbsp;LCh</td>
<td style="background:rgb(252, 255, 254)">#fcfffe</td><td style="background:rgb(225, 230, 228)">#e1e6e4</td><td style="background:rgb(200, 204, 202)">#c8ccca</td><td style="background:rgb(175, 179, 177)">#afb3b1</td><td style="background:rgb(150, 153, 152)">#969998</td><td style="background:rgb(124, 128, 126)">#7c807e</td><td style="background:rgb(98, 102, 100)">#626664</td><td style="background:rgb(73, 77, 75)">#494d4b</td><td style="background:rgb(47, 51, 50)">#2f3332</td><td style="background:rgb(22, 26, 24)">#161a18</td>
</tr>
<tr>
<td style="background:rgb(0, 255, 170)">160&deg;&nbsp;HSV / 
174&deg;&nbsp;LCh</td>
<td style="background:rgb(250, 255, 253)">#fafffd</td><td style="background:rgb(225, 230, 228)">#e1e6e4</td><td style="background:rgb(200, 204, 203)">#c8cccb</td><td style="background:rgb(175, 179, 177)">#afb3b1</td><td style="background:rgb(148, 153, 151)">#949997</td><td style="background:rgb(124, 128, 126)">#7c807e</td><td style="background:rgb(98, 102, 101)">#626665</td><td style="background:rgb(73, 77, 75)">#494d4b</td><td style="background:rgb(47, 51, 50)">#2f3332</td><td style="background:rgb(22, 26, 24)">#161a18</td>
</tr>
<tr>
<td style="background:rgb(0, 255, 191)">165&deg;&nbsp;HSV / 
180&deg;&nbsp;LCh</td>
<td style="background:rgb(250, 255, 254)">#fafffe</td><td style="background:rgb(225, 230, 228)">#e1e6e4</td><td style="background:rgb(200, 204, 203)">#c8cccb</td><td style="background:rgb(175, 179, 178)">#afb3b2</td><td style="background:rgb(148, 153, 152)">#949998</td><td style="background:rgb(124, 128, 127)">#7c807f</td><td style="background:rgb(98, 102, 101)">#626665</td><td style="background:rgb(73, 77, 76)">#494d4c</td><td style="background:rgb(47, 51, 50)">#2f3332</td><td style="background:rgb(22, 26, 25)">#161a19</td>
</tr>
<tr>
<td style="background:rgb(0, 255, 213)">170&deg;&nbsp;HSV / 
186&deg;&nbsp;LCh</td>
<td style="background:rgb(250, 255, 254)">#fafffe</td><td style="background:rgb(225, 230, 229)">#e1e6e5</td><td style="background:rgb(200, 204, 203)">#c8cccb</td><td style="background:rgb(173, 179, 178)">#adb3b2</td><td style="background:rgb(148, 153, 152)">#949998</td><td style="background:rgb(124, 128, 127)">#7c807f</td><td style="background:rgb(98, 102, 101)">#626665</td><td style="background:rgb(72, 77, 76)">#484d4c</td><td style="background:rgb(47, 51, 50)">#2f3332</td><td style="background:rgb(22, 26, 25)">#161a19</td>
</tr>
<tr>
<td style="background:rgb(0, 255, 234)">175&deg;&nbsp;HSV / 
193&deg;&nbsp;LCh</td>
<td style="background:rgb(250, 255, 255)">#faffff</td><td style="background:rgb(225, 230, 229)">#e1e6e5</td><td style="background:rgb(198, 204, 203)">#c6cccb</td><td style="background:rgb(173, 179, 178)">#adb3b2</td><td style="background:rgb(148, 153, 153)">#949999</td><td style="background:rgb(122, 128, 127)">#7a807f</td><td style="background:rgb(97, 102, 102)">#616666</td><td style="background:rgb(71, 77, 76)">#474d4c</td><td style="background:rgb(46, 51, 51)">#2e3333</td><td style="background:rgb(21, 26, 25)">#151a19</td>
</tr>
<tr>
<td style="background:rgb(0, 255, 255)">180&deg;&nbsp;HSV / 
200&deg;&nbsp;LCh</td>
<td style="background:rgb(247, 255, 255)">#f7ffff</td><td style="background:rgb(223, 230, 230)">#dfe6e6</td><td style="background:rgb(198, 204, 204)">#c6cccc</td><td style="background:rgb(171, 179, 179)">#abb3b3</td><td style="background:rgb(147, 153, 153)">#939999</td><td style="background:rgb(121, 128, 128)">#798080</td><td style="background:rgb(96, 102, 102)">#606666</td><td style="background:rgb(70, 77, 77)">#464d4d</td><td style="background:rgb(45, 51, 51)">#2d3333</td><td style="background:rgb(20, 26, 26)">#141a1a</td>
</tr>
<tr>
<td style="background:rgb(0, 234, 255)">185&deg;&nbsp;HSV / 
207&deg;&nbsp;LCh</td>
<td style="background:rgb(247, 254, 255)">#f7feff</td><td style="background:rgb(220, 229, 230)">#dce5e6</td><td style="background:rgb(196, 203, 204)">#c4cbcc</td><td style="background:rgb(170, 178, 179)">#aab2b3</td><td style="background:rgb(144, 152, 153)">#909899</td><td style="background:rgb(119, 127, 128)">#777f80</td><td style="background:rgb(94, 101, 102)">#5e6566</td><td style="background:rgb(69, 76, 77)">#454c4d</td><td style="background:rgb(43, 50, 51)">#2b3233</td><td style="background:rgb(18, 25, 26)">#12191a</td>
</tr>
<tr>
<td style="background:rgb(0, 213, 255)">190&deg;&nbsp;HSV / 
215&deg;&nbsp;LCh</td>
<td style="background:rgb(242, 253, 255)">#f2fdff</td><td style="background:rgb(218, 228, 230)">#dae4e6</td><td style="background:rgb(192, 202, 204)">#c0cacc</td><td style="background:rgb(168, 177, 179)">#a8b1b3</td><td style="background:rgb(142, 151, 153)">#8e9799</td><td style="background:rgb(117, 126, 128)">#757e80</td><td style="background:rgb(92, 100, 102)">#5c6466</td><td style="background:rgb(67, 75, 77)">#434b4d</td><td style="background:rgb(41, 49, 51)">#293133</td><td style="background:rgb(15, 24, 26)">#0f181a</td>
</tr>
<tr>
<td style="background:rgb(0, 191, 255)">195&deg;&nbsp;HSV / 
224&deg;&nbsp;LCh</td>
<td style="background:rgb(240, 251, 255)">#f0fbff</td><td style="background:rgb(216, 226, 230)">#d8e2e6</td><td style="background:rgb(190, 200, 204)">#bec8cc</td><td style="background:rgb(164, 175, 179)">#a4afb3</td><td style="background:rgb(139, 150, 153)">#8b9699</td><td style="background:rgb(115, 124, 128)">#737c80</td><td style="background:rgb(89, 99, 102)">#596366</td><td style="background:rgb(64, 73, 77)">#40494d</td><td style="background:rgb(39, 48, 51)">#273033</td><td style="background:rgb(12, 22, 26)">#0c161a</td>
</tr>
<tr>
<td style="background:rgb(0, 170, 255)">200&deg;&nbsp;HSV / 
233&deg;&nbsp;LCh</td>
<td style="background:rgb(240, 250, 255)">#f0faff</td><td style="background:rgb(213, 224, 230)">#d5e0e6</td><td style="background:rgb(188, 199, 204)">#bcc7cc</td><td style="background:rgb(162, 173, 179)">#a2adb3</td><td style="background:rgb(138, 148, 153)">#8a9499</td><td style="background:rgb(112, 122, 128)">#707a80</td><td style="background:rgb(88, 97, 102)">#586166</td><td style="background:rgb(63, 72, 77)">#3f484d</td><td style="background:rgb(38, 47, 51)">#262f33</td><td style="background:rgb(11, 21, 26)">#0b151a</td>
</tr>
<tr>
<td style="background:rgb(0, 149, 255)">205&deg;&nbsp;HSV / 
242&deg;&nbsp;LCh</td>
<td style="background:rgb(237, 248, 255)">#edf8ff</td><td style="background:rgb(213, 223, 230)">#d5dfe6</td><td style="background:rgb(188, 197, 204)">#bcc5cc</td><td style="background:rgb(162, 172, 179)">#a2acb3</td><td style="background:rgb(138, 147, 153)">#8a9399</td><td style="background:rgb(112, 121, 128)">#707980</td><td style="background:rgb(87, 96, 102)">#576066</td><td style="background:rgb(62, 70, 77)">#3e464d</td><td style="background:rgb(37, 45, 51)">#252d33</td><td style="background:rgb(8, 18, 26)">#08121a</td>
</tr>
<tr>
<td style="background:rgb(0, 128, 255)">210&deg;&nbsp;HSV / 
251&deg;&nbsp;LCh</td>
<td style="background:rgb(235, 245, 255)">#ebf5ff</td><td style="background:rgb(211, 220, 230)">#d3dce6</td><td style="background:rgb(186, 195, 204)">#bac3cc</td><td style="background:rgb(161, 170, 179)">#a1aab3</td><td style="background:rgb(135, 144, 153)">#879099</td><td style="background:rgb(110, 119, 128)">#6e7780</td><td style="background:rgb(85, 93, 102)">#555d66</td><td style="background:rgb(60, 68, 77)">#3c444d</td><td style="background:rgb(36, 43, 51)">#242b33</td><td style="background:rgb(9, 17, 26)">#09111a</td>
</tr>
<tr>
<td style="background:rgb(0, 106, 255)">215&deg;&nbsp;HSV / 
259&deg;&nbsp;LCh</td>
<td style="background:rgb(237, 245, 255)">#edf5ff</td><td style="background:rgb(211, 219, 230)">#d3dbe6</td><td style="background:rgb(186, 193, 204)">#bac1cc</td><td style="background:rgb(161, 168, 179)">#a1a8b3</td><td style="background:rgb(136, 143, 153)">#888f99</td><td style="background:rgb(111, 118, 128)">#6f7680</td><td style="background:rgb(87, 93, 102)">#575d66</td><td style="background:rgb(61, 68, 77)">#3d444d</td><td style="background:rgb(37, 43, 51)">#252b33</td><td style="background:rgb(10, 17, 26)">#0a111a</td>
</tr>
<tr>
<td style="background:rgb(0, 85, 255)">220&deg;&nbsp;HSV / 
266&deg;&nbsp;LCh</td>
<td style="background:rgb(237, 243, 255)">#edf3ff</td><td style="background:rgb(213, 219, 230)">#d5dbe6</td><td style="background:rgb(188, 193, 204)">#bcc1cc</td><td style="background:rgb(162, 168, 179)">#a2a8b3</td><td style="background:rgb(138, 143, 153)">#8a8f99</td><td style="background:rgb(112, 117, 128)">#707580</td><td style="background:rgb(88, 92, 102)">#585c66</td><td style="background:rgb(63, 67, 77)">#3f434d</td><td style="background:rgb(38, 42, 51)">#262a33</td><td style="background:rgb(12, 16, 26)">#0c101a</td>
</tr>
<tr>
<td style="background:rgb(0, 64, 255)">225&deg;&nbsp;HSV / 
272&deg;&nbsp;LCh</td>
<td style="background:rgb(240, 244, 255)">#f0f4ff</td><td style="background:rgb(213, 217, 230)">#d5d9e6</td><td style="background:rgb(190, 193, 204)">#bec1cc</td><td style="background:rgb(164, 168, 179)">#a4a8b3</td><td style="background:rgb(139, 143, 153)">#8b8f99</td><td style="background:rgb(113, 117, 128)">#717580</td><td style="background:rgb(89, 92, 102)">#595c66</td><td style="background:rgb(63, 67, 77)">#3f434d</td><td style="background:rgb(39, 42, 51)">#272a33</td><td style="background:rgb(13, 16, 26)">#0d101a</td>
</tr>
<tr>
<td style="background:rgb(0, 42, 255)">230&deg;&nbsp;HSV / 
278&deg;&nbsp;LCh</td>
<td style="background:rgb(242, 244, 255)">#f2f4ff</td><td style="background:rgb(216, 218, 230)">#d8dae6</td><td style="background:rgb(190, 192, 204)">#bec0cc</td><td style="background:rgb(166, 168, 179)">#a6a8b3</td><td style="background:rgb(139, 142, 153)">#8b8e99</td><td style="background:rgb(115, 117, 128)">#737580</td><td style="background:rgb(90, 92, 102)">#5a5c66</td><td style="background:rgb(65, 67, 77)">#41434d</td><td style="background:rgb(40, 42, 51)">#282a33</td><td style="background:rgb(14, 16, 26)">#0e101a</td>
</tr>
<tr>
<td style="background:rgb(0, 21, 255)">235&deg;&nbsp;HSV / 
282&deg;&nbsp;LCh</td>
<td style="background:rgb(242, 243, 255)">#f2f3ff</td><td style="background:rgb(218, 219, 230)">#dadbe6</td><td style="background:rgb(192, 193, 204)">#c0c1cc</td><td style="background:rgb(166, 167, 179)">#a6a7b3</td><td style="background:rgb(141, 142, 153)">#8d8e99</td><td style="background:rgb(116, 117, 128)">#747580</td><td style="background:rgb(91, 92, 102)">#5b5c66</td><td style="background:rgb(66, 67, 77)">#42434d</td><td style="background:rgb(41, 42, 51)">#292a33</td><td style="background:rgb(15, 16, 26)">#0f101a</td>
</tr>
<tr>
<td style="background:rgb(0, 0, 255)">240&deg;&nbsp;HSV / 
286&deg;&nbsp;LCh</td>
<td style="background:rgb(242, 242, 255)">#f2f2ff</td><td style="background:rgb(218, 218, 230)">#dadae6</td><td style="background:rgb(192, 192, 204)">#c0c0cc</td><td style="background:rgb(168, 168, 179)">#a8a8b3</td><td style="background:rgb(142, 142, 153)">#8e8e99</td><td style="background:rgb(117, 117, 128)">#757580</td><td style="background:rgb(92, 92, 102)">#5c5c66</td><td style="background:rgb(67, 67, 77)">#43434d</td><td style="background:rgb(42, 42, 51)">#2a2a33</td><td style="background:rgb(16, 16, 26)">#10101a</td>
</tr>
<tr>
<td style="background:rgb(21, 0, 255)">245&deg;&nbsp;HSV / 
289&deg;&nbsp;LCh</td>
<td style="background:rgb(243, 242, 255)">#f3f2ff</td><td style="background:rgb(219, 218, 230)">#dbdae6</td><td style="background:rgb(193, 192, 204)">#c1c0cc</td><td style="background:rgb(169, 168, 179)">#a9a8b3</td><td style="background:rgb(143, 142, 153)">#8f8e99</td><td style="background:rgb(118, 117, 128)">#767580</td><td style="background:rgb(93, 92, 102)">#5d5c66</td><td style="background:rgb(67, 67, 77)">#43434d</td><td style="background:rgb(43, 42, 51)">#2b2a33</td><td style="background:rgb(16, 16, 26)">#10101a</td>
</tr>
<tr>
<td style="background:rgb(43, 0, 255)">250&deg;&nbsp;HSV / 
292&deg;&nbsp;LCh</td>
<td style="background:rgb(247, 245, 255)">#f7f5ff</td><td style="background:rgb(220, 218, 230)">#dcdae6</td><td style="background:rgb(196, 194, 204)">#c4c2cc</td><td style="background:rgb(170, 168, 179)">#aaa8b3</td><td style="background:rgb(144, 142, 153)">#908e99</td><td style="background:rgb(119, 117, 128)">#777580</td><td style="background:rgb(94, 92, 102)">#5e5c66</td><td style="background:rgb(68, 67, 77)">#44434d</td><td style="background:rgb(43, 42, 51)">#2b2a33</td><td style="background:rgb(17, 16, 26)">#11101a</td>
</tr>
<tr>
<td style="background:rgb(64, 0, 255)">255&deg;&nbsp;HSV / 
296&deg;&nbsp;LCh</td>
<td style="background:rgb(247, 245, 255)">#f7f5ff</td><td style="background:rgb(223, 220, 230)">#dfdce6</td><td style="background:rgb(196, 194, 204)">#c4c2cc</td><td style="background:rgb(172, 170, 179)">#acaab3</td><td style="background:rgb(146, 144, 153)">#929099</td><td style="background:rgb(121, 119, 128)">#797780</td><td style="background:rgb(95, 93, 102)">#5f5d66</td><td style="background:rgb(70, 68, 77)">#46444d</td><td style="background:rgb(45, 43, 51)">#2d2b33</td><td style="background:rgb(19, 17, 26)">#13111a</td>
</tr>
<tr>
<td style="background:rgb(85, 0, 255)">260&deg;&nbsp;HSV / 
299&deg;&nbsp;LCh</td>
<td style="background:rgb(250, 247, 255)">#faf7ff</td><td style="background:rgb(223, 220, 230)">#dfdce6</td><td style="background:rgb(199, 196, 204)">#c7c4cc</td><td style="background:rgb(173, 170, 179)">#adaab3</td><td style="background:rgb(148, 145, 153)">#949199</td><td style="background:rgb(122, 120, 128)">#7a7880</td><td style="background:rgb(97, 94, 102)">#615e66</td><td style="background:rgb(71, 69, 77)">#47454d</td><td style="background:rgb(46, 44, 51)">#2e2c33</td><td style="background:rgb(20, 18, 26)">#14121a</td>
</tr>
<tr>
<td style="background:rgb(106, 0, 255)">265&deg;&nbsp;HSV / 
303&deg;&nbsp;LCh</td>
<td style="background:rgb(251, 247, 255)">#fbf7ff</td><td style="background:rgb(225, 223, 230)">#e1dfe6</td><td style="background:rgb(200, 198, 204)">#c8c6cc</td><td style="background:rgb(174, 171, 179)">#aeabb3</td><td style="background:rgb(149, 147, 153)">#959399</td><td style="background:rgb(124, 121, 128)">#7c7980</td><td style="background:rgb(98, 96, 102)">#626066</td><td style="background:rgb(73, 70, 77)">#49464d</td><td style="background:rgb(48, 45, 51)">#302d33</td><td style="background:rgb(22, 19, 26)">#16131a</td>
</tr>
<tr>
<td style="background:rgb(128, 0, 255)">270&deg;&nbsp;HSV / 
306&deg;&nbsp;LCh</td>
<td style="background:rgb(252, 250, 255)">#fcfaff</td><td style="background:rgb(227, 225, 230)">#e3e1e6</td><td style="background:rgb(201, 198, 204)">#c9c6cc</td><td style="background:rgb(176, 173, 179)">#b0adb3</td><td style="background:rgb(151, 148, 153)">#979499</td><td style="background:rgb(125, 122, 128)">#7d7a80</td><td style="background:rgb(99, 97, 102)">#636166</td><td style="background:rgb(74, 72, 77)">#4a484d</td><td style="background:rgb(49, 46, 51)">#312e33</td><td style="background:rgb(23, 21, 26)">#17151a</td>
</tr>
<tr>
<td style="background:rgb(149, 0, 255)">275&deg;&nbsp;HSV / 
309&deg;&nbsp;LCh</td>
<td style="background:rgb(253, 250, 255)">#fdfaff</td><td style="background:rgb(228, 225, 230)">#e4e1e6</td><td style="background:rgb(202, 200, 204)">#cac8cc</td><td style="background:rgb(176, 173, 179)">#b0adb3</td><td style="background:rgb(151, 148, 153)">#979499</td><td style="background:rgb(125, 122, 128)">#7d7a80</td><td style="background:rgb(100, 98, 102)">#646266</td><td style="background:rgb(75, 72, 77)">#4b484d</td><td style="background:rgb(49, 47, 51)">#312f33</td><td style="background:rgb(24, 21, 26)">#18151a</td>
</tr>
<tr>
<td style="background:rgb(170, 0, 255)">280&deg;&nbsp;HSV / 
312&deg;&nbsp;LCh</td>
<td style="background:rgb(253, 250, 255)">#fdfaff</td><td style="background:rgb(228, 225, 230)">#e4e1e6</td><td style="background:rgb(203, 200, 204)">#cbc8cc</td><td style="background:rgb(177, 173, 179)">#b1adb3</td><td style="background:rgb(151, 148, 153)">#979499</td><td style="background:rgb(126, 124, 128)">#7e7c80</td><td style="background:rgb(101, 98, 102)">#656266</td><td style="background:rgb(75, 72, 77)">#4b484d</td><td style="background:rgb(50, 47, 51)">#322f33</td><td style="background:rgb(24, 22, 26)">#18161a</td>
</tr>
<tr>
<td style="background:rgb(191, 0, 255)">285&deg;&nbsp;HSV / 
315&deg;&nbsp;LCh</td>
<td style="background:rgb(254, 250, 255)">#fefaff</td><td style="background:rgb(228, 225, 230)">#e4e1e6</td><td style="background:rgb(203, 200, 204)">#cbc8cc</td><td style="background:rgb(178, 175, 179)">#b2afb3</td><td style="background:rgb(152, 148, 153)">#989499</td><td style="background:rgb(127, 124, 128)">#7f7c80</td><td style="background:rgb(101, 98, 102)">#656266</td><td style="background:rgb(76, 73, 77)">#4c494d</td><td style="background:rgb(50, 47, 51)">#322f33</td><td style="background:rgb(25, 22, 26)">#19161a</td>
</tr>
<tr>
<td style="background:rgb(212, 0, 255)">290&deg;&nbsp;HSV / 
318&deg;&nbsp;LCh</td>
<td style="background:rgb(254, 250, 255)">#fefaff</td><td style="background:rgb(229, 225, 230)">#e5e1e6</td><td style="background:rgb(203, 200, 204)">#cbc8cc</td><td style="background:rgb(178, 175, 179)">#b2afb3</td><td style="background:rgb(152, 148, 153)">#989499</td><td style="background:rgb(127, 124, 128)">#7f7c80</td><td style="background:rgb(101, 98, 102)">#656266</td><td style="background:rgb(76, 73, 77)">#4c494d</td><td style="background:rgb(50, 47, 51)">#322f33</td><td style="background:rgb(25, 22, 26)">#19161a</td>
</tr>
<tr>
<td style="background:rgb(234, 0, 255)">295&deg;&nbsp;HSV / 
321&deg;&nbsp;LCh</td>
<td style="background:rgb(255, 250, 255)">#fffaff</td><td style="background:rgb(229, 225, 230)">#e5e1e6</td><td style="background:rgb(204, 200, 204)">#ccc8cc</td><td style="background:rgb(178, 175, 179)">#b2afb3</td><td style="background:rgb(153, 148, 153)">#999499</td><td style="background:rgb(127, 124, 128)">#7f7c80</td><td style="background:rgb(102, 98, 102)">#666266</td><td style="background:rgb(76, 73, 77)">#4c494d</td><td style="background:rgb(51, 47, 51)">#332f33</td><td style="background:rgb(25, 22, 26)">#19161a</td>
</tr>
<tr>
<td style="background:rgb(255, 0, 255)">300&deg;&nbsp;HSV / 
323&deg;&nbsp;LCh</td>
<td style="background:rgb(255, 250, 255)">#fffaff</td><td style="background:rgb(230, 225, 230)">#e6e1e6</td><td style="background:rgb(204, 200, 204)">#ccc8cc</td><td style="background:rgb(179, 175, 179)">#b3afb3</td><td style="background:rgb(153, 148, 153)">#999499</td><td style="background:rgb(128, 124, 128)">#807c80</td><td style="background:rgb(102, 98, 102)">#666266</td><td style="background:rgb(77, 73, 77)">#4d494d</td><td style="background:rgb(51, 47, 51)">#332f33</td><td style="background:rgb(26, 22, 26)">#1a161a</td>
</tr>
<tr>
<td style="background:rgb(255, 0, 234)">305&deg;&nbsp;HSV / 
326&deg;&nbsp;LCh</td>
<td style="background:rgb(255, 250, 255)">#fffaff</td><td style="background:rgb(230, 225, 229)">#e6e1e5</td><td style="background:rgb(204, 200, 204)">#ccc8cc</td><td style="background:rgb(179, 175, 178)">#b3afb2</td><td style="background:rgb(153, 148, 153)">#999499</td><td style="background:rgb(128, 124, 127)">#807c7f</td><td style="background:rgb(102, 98, 102)">#666266</td><td style="background:rgb(77, 73, 76)">#4d494c</td><td style="background:rgb(51, 47, 51)">#332f33</td><td style="background:rgb(26, 22, 25)">#1a1619</td>
</tr>
<tr>
<td style="background:rgb(255, 0, 212)">310&deg;&nbsp;HSV / 
329&deg;&nbsp;LCh</td>
<td style="background:rgb(255, 250, 254)">#fffafe</td><td style="background:rgb(230, 225, 229)">#e6e1e5</td><td style="background:rgb(204, 200, 203)">#ccc8cb</td><td style="background:rgb(179, 173, 178)">#b3adb2</td><td style="background:rgb(153, 148, 152)">#999498</td><td style="background:rgb(128, 122, 127)">#807a7f</td><td style="background:rgb(102, 98, 101)">#666265</td><td style="background:rgb(77, 72, 76)">#4d484c</td><td style="background:rgb(51, 47, 50)">#332f32</td><td style="background:rgb(26, 22, 25)">#1a1619</td>
</tr>
<tr>
<td style="background:rgb(255, 0, 191)">315&deg;&nbsp;HSV / 
332&deg;&nbsp;LCh</td>
<td style="background:rgb(255, 250, 254)">#fffafe</td><td style="background:rgb(230, 225, 228)">#e6e1e4</td><td style="background:rgb(204, 200, 203)">#ccc8cb</td><td style="background:rgb(179, 173, 177)">#b3adb1</td><td style="background:rgb(153, 148, 152)">#999498</td><td style="background:rgb(128, 122, 126)">#807a7e</td><td style="background:rgb(102, 97, 101)">#666165</td><td style="background:rgb(77, 72, 75)">#4d484b</td><td style="background:rgb(51, 46, 50)">#332e32</td><td style="background:rgb(26, 21, 24)">#1a1518</td>
</tr>
<tr>
<td style="background:rgb(255, 0, 170)">320&deg;&nbsp;HSV / 
335&deg;&nbsp;LCh</td>
<td style="background:rgb(255, 250, 253)">#fffafd</td><td style="background:rgb(230, 225, 228)">#e6e1e4</td><td style="background:rgb(204, 198, 202)">#ccc6ca</td><td style="background:rgb(179, 173, 177)">#b3adb1</td><td style="background:rgb(153, 148, 151)">#999497</td><td style="background:rgb(128, 122, 126)">#807a7e</td><td style="background:rgb(102, 97, 100)">#666164</td><td style="background:rgb(77, 72, 75)">#4d484b</td><td style="background:rgb(51, 46, 49)">#332e31</td><td style="background:rgb(26, 21, 24)">#1a1518</td>
</tr>
<tr>
<td style="background:rgb(255, 0, 149)">325&deg;&nbsp;HSV / 
340&deg;&nbsp;LCh</td>
<td style="background:rgb(255, 250, 253)">#fffafd</td><td style="background:rgb(230, 225, 228)">#e6e1e4</td><td style="background:rgb(204, 198, 201)">#ccc6c9</td><td style="background:rgb(179, 173, 176)">#b3adb0</td><td style="background:rgb(153, 147, 150)">#999396</td><td style="background:rgb(128, 122, 125)">#807a7d</td><td style="background:rgb(102, 97, 100)">#666164</td><td style="background:rgb(77, 71, 74)">#4d474a</td><td style="background:rgb(51, 46, 49)">#332e31</td><td style="background:rgb(26, 21, 24)">#1a1518</td>
</tr>
<tr>
<td style="background:rgb(255, 0, 128)">330&deg;&nbsp;HSV / 
344&deg;&nbsp;LCh</td>
<td style="background:rgb(255, 250, 252)">#fffafc</td><td style="background:rgb(230, 223, 226)">#e6dfe2</td><td style="background:rgb(204, 198, 201)">#ccc6c9</td><td style="background:rgb(179, 173, 176)">#b3adb0</td><td style="background:rgb(153, 147, 150)">#999396</td><td style="background:rgb(128, 121, 124)">#80797c</td><td style="background:rgb(102, 96, 99)">#666063</td><td style="background:rgb(77, 71, 74)">#4d474a</td><td style="background:rgb(51, 46, 48)">#332e30</td><td style="background:rgb(26, 20, 23)">#1a1417</td>
</tr>
<tr>
<td style="background:rgb(255, 0, 106)">335&deg;&nbsp;HSV / 
349&deg;&nbsp;LCh</td>
<td style="background:rgb(255, 250, 252)">#fffafc</td><td style="background:rgb(230, 223, 225)">#e6dfe1</td><td style="background:rgb(204, 198, 200)">#ccc6c8</td><td style="background:rgb(179, 173, 175)">#b3adaf</td><td style="background:rgb(153, 147, 149)">#999395</td><td style="background:rgb(128, 121, 124)">#80797c</td><td style="background:rgb(102, 96, 98)">#666062</td><td style="background:rgb(77, 71, 73)">#4d4749</td><td style="background:rgb(51, 45, 48)">#332d30</td><td style="background:rgb(26, 20, 22)">#1a1416</td>
</tr>
<tr>
<td style="background:rgb(255, 0, 85)">340&deg;&nbsp;HSV / 
355&deg;&nbsp;LCh</td>
<td style="background:rgb(255, 247, 250)">#fff7fa</td><td style="background:rgb(230, 223, 225)">#e6dfe1</td><td style="background:rgb(204, 198, 200)">#ccc6c8</td><td style="background:rgb(179, 171, 174)">#b3abae</td><td style="background:rgb(153, 147, 149)">#999395</td><td style="background:rgb(128, 121, 123)">#80797b</td><td style="background:rgb(102, 96, 98)">#666062</td><td style="background:rgb(77, 70, 72)">#4d4648</td><td style="background:rgb(51, 45, 47)">#332d2f</td><td style="background:rgb(26, 20, 22)">#1a1416</td>
</tr>
<tr>
<td style="background:rgb(255, 0, 64)">345&deg;&nbsp;HSV / 
1&deg;&nbsp;LCh</td>
<td style="background:rgb(255, 247, 249)">#fff7f9</td><td style="background:rgb(230, 223, 224)">#e6dfe0</td><td style="background:rgb(204, 198, 199)">#ccc6c7</td><td style="background:rgb(179, 171, 173)">#b3abad</td><td style="background:rgb(153, 147, 148)">#999394</td><td style="background:rgb(128, 121, 123)">#80797b</td><td style="background:rgb(102, 96, 97)">#666061</td><td style="background:rgb(77, 70, 72)">#4d4648</td><td style="background:rgb(51, 45, 47)">#332d2f</td><td style="background:rgb(26, 20, 21)">#1a1415</td>
</tr>
<tr>
<td style="background:rgb(255, 0, 43)">350&deg;&nbsp;HSV / 
7&deg;&nbsp;LCh</td>
<td style="background:rgb(255, 247, 249)">#fff7f9</td><td style="background:rgb(230, 223, 224)">#e6dfe0</td><td style="background:rgb(204, 198, 199)">#ccc6c7</td><td style="background:rgb(179, 171, 173)">#b3abad</td><td style="background:rgb(153, 147, 148)">#999394</td><td style="background:rgb(128, 121, 122)">#80797a</td><td style="background:rgb(102, 96, 97)">#666061</td><td style="background:rgb(77, 70, 71)">#4d4647</td><td style="background:rgb(51, 45, 46)">#332d2e</td><td style="background:rgb(26, 20, 21)">#1a1415</td>
</tr>
<tr>
<td style="background:rgb(255, 0, 21)">355&deg;&nbsp;HSV / 
13&deg;&nbsp;LCh</td>
<td style="background:rgb(255, 247, 248)">#fff7f8</td><td style="background:rgb(230, 223, 223)">#e6dfdf</td><td style="background:rgb(204, 198, 198)">#ccc6c6</td><td style="background:rgb(179, 171, 172)">#b3abac</td><td style="background:rgb(153, 145, 146)">#999192</td><td style="background:rgb(128, 121, 122)">#80797a</td><td style="background:rgb(102, 96, 96)">#666060</td><td style="background:rgb(77, 70, 71)">#4d4647</td><td style="background:rgb(51, 45, 45)">#332d2d</td><td style="background:rgb(26, 20, 20)">#1a1414</td>
</tr>
<tr>
<td style="background:rgb(255, 0, 0)">360&deg;&nbsp;HSV / 
20&deg;&nbsp;LCh</td>
<td style="background:rgb(255, 247, 247)">#fff7f7</td><td style="background:rgb(230, 223, 223)">#e6dfdf</td><td style="background:rgb(204, 198, 198)">#ccc6c6</td><td style="background:rgb(179, 171, 171)">#b3abab</td><td style="background:rgb(153, 147, 147)">#999393</td><td style="background:rgb(128, 121, 121)">#807979</td><td style="background:rgb(102, 96, 96)">#666060</td><td style="background:rgb(77, 70, 70)">#4d4646</td><td style="background:rgb(51, 45, 45)">#332d2d</td><td style="background:rgb(26, 20, 20)">#1a1414</td>
</tr>
</tbody>
</table>
</div>

<p>Looking pretty good to me!</p>

<h3 id="color-palettes-classified-as-gray">Color palettes classified as gray</h3>

<h4 id="bootstrap-grays-">Bootstrap grays ✅</h4>

<ul style="display: grid; grid-template-columns: repeat(auto-fit, minmax(13em, 1fr));">
<li><code>gray-100: <span data-color="">#f8f9fa</span></code></li>
<li><code>gray-200: <span data-color="">#e9ecef</span></code></li>
<li><code>gray-300: <span data-color="">#dee2e6</span></code></li>
<li><code>gray-400: <span data-color="">#ced4da</span></code></li>
<li><code>gray-500: <span data-color="">#adb5bd</span></code></li>
<li><code>gray-600: <span data-color="">#6c757d</span></code></li>
<li><code>gray-700: <span data-color="">#495057</span></code></li>
<li><code>gray-800: <span data-color="">#343a40</span></code></li>
<li><code>gray-900: <span data-color="">#212529</span></code></li>
</ul>

<h4 id="pantone-cool-grays-">PANTONE cool grays ✅</h4>

<ul style="display: grid; grid-template-columns: repeat(auto-fit, minmax(13em, 1fr));">
<li>1C: <code data-color="">#d9d9d6</code></li>
<li>2C: <code data-color="">#d0d0ce</code></li>
<li>3C: <code data-color="">#c8c9c7</code></li>
<li>4C: <code data-color="">#bbbcbc</code></li>
<li>5C: <code data-color="">#b1b3b3</code></li>
<li>6C: <code data-color="">#a7a8a9</code></li>
<li>7C: <code data-color="">#97999b</code></li>
<li>8C: <code data-color="">#888b8d</code></li>
<li>9C: <code data-color="">#75787b</code></li>
<li>10C: <code data-color="">#63666a</code></li>
<li>11C: <code data-color="">#53565a</code></li>
</ul>

<h4 id="pantone-warm-grays-">PANTONE warm grays ✅</h4>

<ul style="display: grid; grid-template-columns: repeat(auto-fit, minmax(13em, 1fr));">
<li>1C: <code data-color="">#d7d2cb</code></li>
<li>2C: <code data-color="">#cbc4bc</code></li>
<li>3C: <code data-color="">#bfb8af</code></li>
<li>4C: <code data-color="">#b6ada5</code></li>
<li>5C: <code data-color="">#aca39a</code></li>
<li>6C: <code data-color="">#a59c94</code></li>
<li>7C: <code data-color="">#968c83</code></li>
<li>8C: <code data-color="">#8c8279</code></li>
<li>9C: <code data-color="">#83786f</code></li>
<li>10C: <code data-color="">#796e65</code></li>
<li>11C: <code data-color="">#6e6259</code></li>
</ul>

<h4 id="css-colors-">CSS Colors ✅</h4>

<ul style="display: grid; grid-template-columns: repeat(auto-fit, minmax(14em, 1fr));">
<li><code>black <span data-color="">#000000 </span></code></li>
<li><code>silver <span data-color="">#c0c0c0 </span></code></li>
<li><code>gray <span data-color="">#808080 </span></code></li>
<li><code>white <span data-color="">#ffffff </span></code></li>
<li><code>dimgray <span data-color="">#696969 </span></code></li>
<li><code>dimgrey <span data-color="">#696969 </span></code></li>
<li><code>darkgray <span data-color="">#a9a9a9 </span></code></li>
<li><code>darkgrey <span data-color="">#a9a9a9 </span></code></li>
<li><code>grey <span data-color="">#808080 </span></code></li>
<li><code>lightgray <span data-color="">#d3d3d3 </span></code></li>
<li><code>lightgrey <span data-color="">#d3d3d3 </span></code></li>
<li><code>aliceblue <span data-color="">#f0f8ff </span></code></li>
<li><code>gainsboro <span data-color="">#dcdcdc </span></code></li>
<li><code>ghostwhite <span data-color="">#f8f8ff </span></code></li>
<li><code>snow <span data-color="">#fffafa </span></code></li>
<li><code>whitesmoke <span data-color="">#f5f5f5 </span></code></li>
<li><code>seashell <span data-color="">#fff5ee </span></code></li>
<li><code>linen <span data-color="">#faf0e6 </span></code></li>
</ul>

<p>(I slightly disagree with <code class="language-plaintext highlighter-rouge">aliceblue</code> but gonna leave it for now)</p>

<h3 id="color-palettes-not-classified-as-gray">Color palettes <em>not</em> classified as gray</h3>

<p>A lot of these are blues with chroma values way above what I’m willing to match
with the current implementation of the function.
I could treat blues as special, but I’m gonna leave it be for now.</p>

<h4 id="css-slate-gray-">CSS “Slate Gray” ❌</h4>

<p>I would like to match these guys eventually.</p>

<ul style="display: grid; grid-template-columns: repeat(auto-fit, minmax(14em, 1fr));">
<li><code>lightslategray: <span data-color="">#778899</span></code></li>
<li><code>lightslategrey: <span data-color="">#778899</span></code></li> 
<li><code>slategray: <span data-color="">#708090</span>     </code></li> 
<li><code>slategrey: <span data-color="">#708090</span>     </code></li> 
</ul>

<h4 id="light-css-colors-">Light CSS Colors ❌</h4>

<ul style="display: grid; grid-template-columns: repeat(auto-fit, minmax(14em, 1fr));">
<li><code>azure <span data-color="">#f0ffff </span></code></li>
<li><code>ivory <span data-color="">#fffff0 </span></code></li>
<li><code>floralwhite <span data-color="">#fffaf0 </span></code></li>
<li><code>honeydew <span data-color="">#f0fff0 </span></code></li>
<li><code>bisque <span data-color="">#ffe4c4 </span></code></li>
<li><code>blanchedalmond <span data-color="">#ffebcd </span></code></li>
<li><code>burlywood <span data-color="">#deb887 </span></code></li>
<li><code>cornsilk <span data-color="">#fff8dc </span></code></li>
<li><code>beige <span data-color="">#f5f5dc </span></code></li>
<li><code>antiquewhite <span data-color="">#faebd7 </span></code></li>
</ul>

<h4 id="tailwind-css-grays-">Tailwind CSS “grays” ❌</h4>

<p>Matching these made my blue hue edge cases kinda bogus looking. I tend to agree with my function above.
These look like blue colors to me: not gray.</p>

<ul style="display: grid; grid-template-columns: repeat(auto-fit, minmax(13em, 1fr));">
<li>100: <code data-color="">#F7FAFC</code> ✅</li>
<li>200: <code data-color="">#EDF2F7</code> ✅</li>
<li>300: <code data-color="">#E2E8F0</code> ✅</li>
<li>400: <code data-color="">#CBD5E0</code> ❌</li>
<li>500: <code data-color="">#A0AEC0</code> ❌</li>
<li>600: <code data-color="">#718096</code> ❌</li>
<li>700: <code data-color="">#4A5568</code> ❌</li>
<li>800: <code data-color="">#2D3748</code> ❌</li>
<li>900: <code data-color="">#1A202C</code> ❌</li>
</ul>

<p>If you wanted to match these guys you would need to lax your tolerances for blue hues.</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">hueToleranceStopsV2</span> <span class="o">=</span> <span class="p">[</span>
  <span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="mf">3.1</span><span class="p">],</span> <span class="c1">// red</span>
  <span class="p">[</span><span class="mi">20</span><span class="p">,</span> <span class="mf">3.1</span><span class="p">],</span> <span class="c1">// orange</span>
  <span class="p">[</span><span class="mf">61.8</span><span class="p">,</span> <span class="mi">8</span><span class="p">],</span> <span class="c1">// orange</span>
  <span class="p">[</span><span class="mi">66</span><span class="p">,</span> <span class="mf">7.49</span><span class="p">],</span> <span class="c1">// orange</span>
  <span class="p">[</span><span class="mi">75</span><span class="p">,</span> <span class="mf">6.8</span><span class="p">],</span> <span class="c1">// orange</span>
  <span class="p">[</span><span class="mi">100</span><span class="p">,</span> <span class="mi">2</span><span class="p">],</span> <span class="c1">// yellow-green</span>
  <span class="p">[</span><span class="mi">180</span><span class="p">,</span> <span class="mi">2</span><span class="p">],</span> <span class="c1">// green</span>
  <span class="p">[</span><span class="mi">240</span><span class="p">,</span> <span class="mf">5.4</span><span class="p">],</span> <span class="c1">// blue</span>
  <span class="p">[</span><span class="mi">262</span><span class="p">,</span> <span class="mi">14</span><span class="p">],</span> <span class="c1">// blue</span>
  <span class="p">[</span><span class="mi">263</span><span class="p">,</span> <span class="mi">14</span><span class="p">],</span> <span class="c1">// blue</span>
  <span class="p">[</span><span class="mi">290</span><span class="p">,</span> <span class="mf">6.5</span><span class="p">],</span>
  <span class="p">[</span><span class="mi">310</span><span class="p">,</span> <span class="mf">3.1</span><span class="p">],</span> <span class="c1">// red</span>
  <span class="p">[</span><span class="mi">360</span><span class="p">,</span> <span class="mf">3.1</span><span class="p">],</span>
<span class="p">];</span>
</code></pre></div></div>

<p>Which looks like a much different curve:</p>

<blockquote>
  <p><img src="/assets/gray/hue-curve-tw.svg" alt="tailwind hue curve" /></p>
</blockquote>

<h2 id="what-is-this-useful-for">What is this useful for?</h2>

<p>You could use this as a post-processing step to classify things as “gray”.</p>

<blockquote>
  <p><img src="/assets/gray/Screen Capture_select-area_20200525223933.png" alt="google images" /></p>
</blockquote>

<p><a href="https://github.com/roots/palette-webpack-plugin">roots/palette-webpack-plugin</a> uses a simplified
version of this technique to automatically sort colors in a color palette picker.</p>

<hr />

<h2 id="further-reading">Further Reading</h2>

<ul>
  <li><a href="http://www.huevaluechroma.com/index.php">David Briggs. “Dimensions of Colour”</a></li>
  <li><a href="https://www.boronine.com/2012/03/26/Color-Spaces-for-Human-Beings/">Alexei Boronine. “Color Spaces for Human Beings”</a></li>
  <li><a href="http://basecase.org/env/on-rainbows">Charlie Loyd. “On Rainbows”</a></li>
  <li><a href="http://persci.mit.edu/">Perceptual Science Group @ MIT</a>
    <ul>
      <li><a href="http://persci.mit.edu/gallery/checkershadow">Adelson, E.H. “Checkershadow Illusion”</a></li>
      <li><a href="http://persci.mit.edu/pub_pdfs/gazzan.pdf">Adelson, E.H. Lightness Perception and Lightness Illusions. (2000).</a></li>
    </ul>
  </li>
</ul>

<script src="/assets/gray/2020-05-25-detecting-grayscale-colors.js" type="module"></script>]]></content><author><name>Austin Pray</name></author><category term="algorithms" /><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">Learn to Code</title><link href="https://austinpray.com/coding/2017/05/31/learn-to-code.html" rel="alternate" type="text/html" title="Learn to Code" /><published>2017-05-31T00:00:00+00:00</published><updated>2017-05-31T00:00:00+00:00</updated><id>https://austinpray.com/coding/2017/05/31/learn-to-code</id><content type="html" xml:base="https://austinpray.com/coding/2017/05/31/learn-to-code.html"><![CDATA[<blockquote>
  <p>Hey austin. I have been interested into getting into coding, specifically back end coding. I was just wondering what would be the best way about getting started? And I need to buy a new laptop and was wondering what hardware would be best for me given all that. Also keep in mind all of this is on tha backdrop of zero program experience. Very novice at computers period really. Any advice you got would be much appreciated</p>
</blockquote>

<p>In order to make learning to code fun and rewarding, I recommend first finding a problem that interests you. Learn how to code until you can solve your problem.</p>

<p>I began learning how to build websites in 2005. I was learning how to make basic flash animations and wanted to upload them to my own website like some of the other animators were doing. My success was measured by: “can people watch my little animations yet?” I learned how to successfully code and publish a website purely by trying to answer my own questions.</p>

<ul>
  <li>“How do people get a domain name and how does that work?”</li>
  <li>“A website needs hosting, where do I get hosting?”</li>
  <li>“How do I make text show up on a web page?”</li>
  <li>“How do I change the color of the text on the page?”</li>
  <li>“How do I put a video on a web page?”</li>
</ul>

<p>After I answered all of those questions (and a million more probably), I was successful in publishing my little flash animations to the web! People could watch the little clips I uploaded to the website. That only led to more questions.</p>

<ul>
  <li>“How can I let people leave comments on my animations?”</li>
  <li>“How can I make blog posts on my website to let people know that I am working on more animations?”</li>
  <li>“How can I let other animators upload their own animations to my website without me having to manually publish them?”</li>
</ul>

<p>By around 2008 or so I was able to do all the things I mentioned above. I was basically programming literate. I knew how to build simple database-backed web applications. Although discipline was indeed a factor in teaching myself programming, I attribute my success more to having clear end-goals. I wasn’t learning how to write a “for loop” was because someone told me it was important. I was learning how to write a “for loop” because I had an idea that I could use it to display a list of my videos without having to copy paste the list items 10 times.</p>

<p>The whole thing is very much like learning a spoken language. I took 5 years of spanish in school and never got anywhere close to fluent. I was consuming the class content as it was doled out and didn’t have any desire to go out and practice. You hear anecdotes all the time of people taking trips to Mexico or Spain or Costa Rica and coming back with twice the Spanish competency compared to when they left. It is obvious the success can be attributed to immersion as a motivating factor. You are far more likely to half-ass learning how to order at a restaurant out of a workbook compared to when you are in front of a real waiter at a foreign restaurant.</p>

<h2 id="what-can-programming-do">“What Can Programming Do?”</h2>

<p>My little flash animation showcase website was my project that got me into programming. Yours should be something you care about as well. It doesn’t have to be a website, it could be a program that reads a spreadsheet and outputs some helpful text. It could be a program that tracks expiration dates on your groceries. It could be a program that tracks how often you have sex. You could write a program to calculate how much you need to save up month over month to buy something. You could build a goofy chat bot that says funny things when you chat to it. You could even just build your own to-do list app. It doesn’t matter if it’s a novel idea or that there is an existing solution, you are trying to learn.</p>

<p>My recommendation would be to think of a project that deals with numbers or text as data. At least to start with. Dealing with images, audio, and video isn’t rocket science, but introduces concepts that might confuse a truly novice programmer. If you truly aren’t familiar with what a “program” is, use an online tool like <a href="https://www.codecademy.com/">codeacademy</a> and go through the first couple lessons, I recommend starting with JavaScript. This will teach you the very basics of what programming is. Hopefully this will jog your noggin and maybe you will see how you can write programs to crunch numbers or save text or remember dates or display text. But critical to my point: you should never stray far applying your knowledge. As soon as you know enough to be dangerous, you should try your hand at building something.</p>

<h2 id="what-language-should-i-learn">“What Language Should I Learn?”</h2>

<p>I get this question a lot. My blind answer is usually JavaScript and web technologies. My reasoning is that coding JavaScript and building browser applications is very visually oriented and immediately produces results you can show people. It’s easy to produce a goofy little website and have people visit it. With people able to enjoy your work, you can get feedback and be inspired to add goofier and more complicated features.</p>

<p>However, it depends on what you are trying to do to. Do you want to build programs that read spreadsheets and do statistics, kind of like what an analyst would do in excel? You want to learn Python. Python is a programming language that could benefit most all office workers that crunch data or deal with computer systems.</p>

<p>It can get more complicated. For instance: you can build goofy websites using Python, JavaScript, AND web technologies. Although for a novice, focus on getting your feet on solid ground and feel free to mix and match once you know what you are doing.</p>

<p>Good resources:</p>

<ul>
  <li><a href="https://www.codecademy.com/">https://www.codecademy.com/</a></li>
  <li><a href="http://codewars.com/">code wars</a></li>
  <li><a href="https://egghead.io/courses">egghead.io</a></li>
  <li><a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript">mozilla</a></li>
</ul>

<h2 id="what-should-i-do">“What Should I Do?”</h2>

<p>Go to meetups! So you have a basic idea of what programming is and perhaps you have done some reading online. Now it is time to collaborate with other people. If you go to school: see if your school has any coding meetups. Check <a href="https://www.meetup.com/">Meetup.com</a> for coding meetups in your area. Be a fly on the wall, listen to pros talk about stuff. Take notes. Ask people about things they have built. Ask people how they would build things you want to build. Talk to other newbies and ask what has been working for them.</p>

<p>Maybe take a class! If you need hands on instruction, go for it. You may have heard about coding bootcamps and stuff. I don’t have any recommendations on any specific programs to join but I would definitely recommend at least doing some online courses before paying for a bootcamp. My feeling is if you go in completely ignorant you are not going to get a lot out of it. You want to have an understanding of the basics so you can ask good questions and not fill your time tripping over pedestrian issues.</p>

<h2 id="what-equipment-do-i-need">“What Equipment Do I Need?”</h2>

<p>These days most any decent consumer computer is fine for entry-level programming. You need a full operating system though. Like Windows, Mac OSX, or Linux. Chromebooks and ipads and stuff like that won’t work.</p>

<p>If you are serious: I recommend using a Mac laptop. Any Mac laptop after 2014 should be fine. There are two main reasons I recommend a Mac:</p>

<ul>
  <li>Someone might be nice enough to mentor you. If you have a Mac, you can use both Mac OSX and Windows. Use the same OS as your mentor so your time together is productive rather than spent fixing platform-specific issues.</li>
  <li>Using Mac OSX teaches unix concepts. If you learn how to set up a development environment on Mac OSX, those skills will kinda sorta translate to the linux world. If you learn how to set up a development environment on Windows, those skills are only valid for windows. (Footnote for a discerning reader: I am aware of the Windows 10 linux subsystem.)</li>
</ul>

<h2 id="additional-tips">Additional Tips</h2>

<ul>
  <li><strong>Learn linux.</strong> If you don’t know what linux is: you should learn what linux is. Even just installing it on your computer to mess around with is a valuable learning experience if you are ignorant about computers.</li>
</ul>]]></content><author><name>Austin Pray</name></author><category term="coding" /><category term="career" /><summary type="html"><![CDATA[Hey austin. I have been interested into getting into coding, specifically back end coding. I was just wondering what would be the best way about getting started? And I need to buy a new laptop and was wondering what hardware would be best for me given all that. Also keep in mind all of this is on tha backdrop of zero program experience. Very novice at computers period really. Any advice you got would be much appreciated In order to make learning to code fun and rewarding, I recommend first finding a problem that interests you. Learn how to code until you can solve your problem. I began learning how to build websites in 2005. I was learning how to make basic flash animations and wanted to upload them to my own website like some of the other animators were doing. My success was measured by: “can people watch my little animations yet?” I learned how to successfully code and publish a website purely by trying to answer my own questions. “How do people get a domain name and how does that work?” “A website needs hosting, where do I get hosting?” “How do I make text show up on a web page?” “How do I change the color of the text on the page?” “How do I put a video on a web page?” After I answered all of those questions (and a million more probably), I was successful in publishing my little flash animations to the web! People could watch the little clips I uploaded to the website. That only led to more questions. “How can I let people leave comments on my animations?” “How can I make blog posts on my website to let people know that I am working on more animations?” “How can I let other animators upload their own animations to my website without me having to manually publish them?” By around 2008 or so I was able to do all the things I mentioned above. I was basically programming literate. I knew how to build simple database-backed web applications. Although discipline was indeed a factor in teaching myself programming, I attribute my success more to having clear end-goals. I wasn’t learning how to write a “for loop” was because someone told me it was important. I was learning how to write a “for loop” because I had an idea that I could use it to display a list of my videos without having to copy paste the list items 10 times. The whole thing is very much like learning a spoken language. I took 5 years of spanish in school and never got anywhere close to fluent. I was consuming the class content as it was doled out and didn’t have any desire to go out and practice. You hear anecdotes all the time of people taking trips to Mexico or Spain or Costa Rica and coming back with twice the Spanish competency compared to when they left. It is obvious the success can be attributed to immersion as a motivating factor. You are far more likely to half-ass learning how to order at a restaurant out of a workbook compared to when you are in front of a real waiter at a foreign restaurant. “What Can Programming Do?” My little flash animation showcase website was my project that got me into programming. Yours should be something you care about as well. It doesn’t have to be a website, it could be a program that reads a spreadsheet and outputs some helpful text. It could be a program that tracks expiration dates on your groceries. It could be a program that tracks how often you have sex. You could write a program to calculate how much you need to save up month over month to buy something. You could build a goofy chat bot that says funny things when you chat to it. You could even just build your own to-do list app. It doesn’t matter if it’s a novel idea or that there is an existing solution, you are trying to learn. My recommendation would be to think of a project that deals with numbers or text as data. At least to start with. Dealing with images, audio, and video isn’t rocket science, but introduces concepts that might confuse a truly novice programmer. If you truly aren’t familiar with what a “program” is, use an online tool like codeacademy and go through the first couple lessons, I recommend starting with JavaScript. This will teach you the very basics of what programming is. Hopefully this will jog your noggin and maybe you will see how you can write programs to crunch numbers or save text or remember dates or display text. But critical to my point: you should never stray far applying your knowledge. As soon as you know enough to be dangerous, you should try your hand at building something. “What Language Should I Learn?” I get this question a lot. My blind answer is usually JavaScript and web technologies. My reasoning is that coding JavaScript and building browser applications is very visually oriented and immediately produces results you can show people. It’s easy to produce a goofy little website and have people visit it. With people able to enjoy your work, you can get feedback and be inspired to add goofier and more complicated features. However, it depends on what you are trying to do to. Do you want to build programs that read spreadsheets and do statistics, kind of like what an analyst would do in excel? You want to learn Python. Python is a programming language that could benefit most all office workers that crunch data or deal with computer systems. It can get more complicated. For instance: you can build goofy websites using Python, JavaScript, AND web technologies. Although for a novice, focus on getting your feet on solid ground and feel free to mix and match once you know what you are doing. Good resources: https://www.codecademy.com/ code wars egghead.io mozilla “What Should I Do?” Go to meetups! So you have a basic idea of what programming is and perhaps you have done some reading online. Now it is time to collaborate with other people. If you go to school: see if your school has any coding meetups. Check Meetup.com for coding meetups in your area. Be a fly on the wall, listen to pros talk about stuff. Take notes. Ask people about things they have built. Ask people how they would build things you want to build. Talk to other newbies and ask what has been working for them. Maybe take a class! If you need hands on instruction, go for it. You may have heard about coding bootcamps and stuff. I don’t have any recommendations on any specific programs to join but I would definitely recommend at least doing some online courses before paying for a bootcamp. My feeling is if you go in completely ignorant you are not going to get a lot out of it. You want to have an understanding of the basics so you can ask good questions and not fill your time tripping over pedestrian issues. “What Equipment Do I Need?” These days most any decent consumer computer is fine for entry-level programming. You need a full operating system though. Like Windows, Mac OSX, or Linux. Chromebooks and ipads and stuff like that won’t work. If you are serious: I recommend using a Mac laptop. Any Mac laptop after 2014 should be fine. There are two main reasons I recommend a Mac: Someone might be nice enough to mentor you. If you have a Mac, you can use both Mac OSX and Windows. Use the same OS as your mentor so your time together is productive rather than spent fixing platform-specific issues. Using Mac OSX teaches unix concepts. If you learn how to set up a development environment on Mac OSX, those skills will kinda sorta translate to the linux world. If you learn how to set up a development environment on Windows, those skills are only valid for windows. (Footnote for a discerning reader: I am aware of the Windows 10 linux subsystem.) Additional Tips Learn linux. If you don’t know what linux is: you should learn what linux is. Even just installing it on your computer to mess around with is a valuable learning experience if you are ignorant about computers.]]></summary></entry><entry><title type="html">Make Slackbot Respond to Regex</title><link href="https://austinpray.com/hacks/2015/04/16/using-regex-with-slackbot-responses.html" rel="alternate" type="text/html" title="Make Slackbot Respond to Regex" /><published>2015-04-16T00:00:00+00:00</published><updated>2015-04-16T00:00:00+00:00</updated><id>https://austinpray.com/hacks/2015/04/16/using-regex-with-slackbot-responses</id><content type="html" xml:base="https://austinpray.com/hacks/2015/04/16/using-regex-with-slackbot-responses.html"><![CDATA[<p>I love <a href="https://slack.com/">Slack</a> and <a href="https://slack.zendesk.com/hc/en-us/articles/202026038-Slackbot-your-setup-assistant-personal-notepad-and-programmable-bot">Slackbot</a>.</p>

<p><img src="/assets/slackbotridupset.png" alt="Ridwan getting upset" /></p>

<h2 id="automatic-responses">Automatic responses</h2>

<p>There are a couple tricks you need to know about to get the most out of the
automatic responses:</p>

<blockquote>

  <ul>
    <li>Separate multiple input phrases with commas. For example: <strong>hi, hello, yo</strong></li>
    <li>You can add as many Slackbot responses as you’d like for each input phrase. Put each one on its own line. (A random response will be chosen if there are multiple responses.) For example:
<strong>Well hi there!<br />
Hello yourself<br />
Yo yo yo</strong></li>
  </ul>
</blockquote>

<p>With all of these tricks in mind you can do things like I ask slackbot to make
decisions:</p>

<p><img src="/assets/slackbotflipcoin.png" alt="flip coin" /></p>

<h2 id="regex-input-phrases">Regex Input Phrases</h2>

<p>This is all well and good, but it is not very flexible. For instance if you
wanted Slackbot to respond to “yee”, ”yeeeeeeeee”, and everyting inbetween you
would use a regex such as <code class="language-plaintext highlighter-rouge">/yee{1,8}/</code>. Since slackbot doesn’t read regex, you
have to statically compile the regex permutations and then feed them to
Slackbot. I went ahead and used the <a href="http://search.cpan.org/~bowmanbs/Regexp-Genex-0.07/lib/Regexp/Genex.pm">Genex</a> perl module to do this. I explain
how to install perl modules further down.</p>

<h3 id="example-yee">example: “yee”</h3>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>perl -MRegexp::Genex=:all -le 'print join(",\n", strings(qr/yee{1,8}/, 20))'
</code></pre></div></div>

<p>Will yield the following:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>yeeeeeeeee,
yeeeeeeee,
yeeeeeee,
yeeeeee,
yeeeee,
yeeee,
yeee,
yee
</code></pre></div></div>

<p>Note the <code class="language-plaintext highlighter-rouge">strings</code> method:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>@list = strings( $regex, [ $max_length = 10 ] )
  Produce a list of strings that would match the regex.
</code></pre></div></div>

<h3 id="example-how-good-is-x-at-y">example: “how good is x at y?”</h3>

<p>They can get pretty complex:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>perl -MRegexp::Genex=:all -le 'print join(",\n", strings(qr/How good is (Ridwan|Austin|Darren) at (perl|python|golang)\?/, 30))'
</code></pre></div></div>

<p>Will yield:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>How good is Ridwan at perl?,
How good is Ridwan at python?,
How good is Ridwan at go?,
How good is Austin at perl?,
How good is Austin at python?,
How good is Austin at go?,
How good is Darren at perl?,
How good is Darren at python?,
How good is Darren at go?
</code></pre></div></div>

<h3 id="example-aww-yiss">example “aww yiss”</h3>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>perl -MRegexp::Genex=:all -le 'print join(",\n", strings(qr/aww{1,9} yiss{1,9}/, 50))'
</code></pre></div></div>

<p><a href="https://gist.github.com/7a04e52b8c08973d4c7e">Will yeild this.</a> So basically
anyone can type <a href="http://www.harkavagrant.com/index.php?id=125">awww yisss</a> with
an arbitrary combination of w’s or s’s and still get the desired automatic
slackbot response.</p>

<h2 id="how-to-install-perl-modules">How to Install Perl Modules</h2>

<p>If you’ve never used perl before, you should check out
<a href="http://www.cpan.org/modules/index.html">CPAN</a> and <a href="http://www.cpan.org/modules/INSTALL.html">their install
instructions</a>.</p>

<p>If you are on Mac OSX, you already have <code class="language-plaintext highlighter-rouge">perl</code> and <code class="language-plaintext highlighter-rouge">cpan</code> in your path. So all
you need to do is install <code class="language-plaintext highlighter-rouge">cpanm</code>.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cpan App::cpanminus
</code></pre></div></div>

<p>Which will give you a <code class="language-plaintext highlighter-rouge">cpanm</code> executable at <code class="language-plaintext highlighter-rouge">~/perl5/bin/cpanm</code>. Now install the
<a href="http://search.cpan.org/~bowmanbs/Regexp-Genex-0.07/lib/Regexp/Genex.pm">Genex</a> module:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>~/perl5/bin/cpanm Regexp::Genex
</code></pre></div></div>

<p>If that succeeded you can now play around with <a href="http://search.cpan.org/~bowmanbs/Regexp-Genex-0.07/lib/Regexp/Genex.pm">Genex</a> and impress your
friends.</p>]]></content><author><name>Austin Pray</name></author><category term="hacks" /><category term="slack" /><category term="hacks" /><summary type="html"><![CDATA[I love Slack and Slackbot.]]></summary></entry><entry><title type="html">“None of the Above” Checkbox with jQuery</title><link href="https://austinpray.com/javascript/2015/02/14/jquery-none-of-the-above-checkboxes.html" rel="alternate" type="text/html" title="“None of the Above” Checkbox with jQuery" /><published>2015-02-14T00:00:00+00:00</published><updated>2015-02-14T00:00:00+00:00</updated><id>https://austinpray.com/javascript/2015/02/14/jquery-none-of-the-above-checkboxes</id><content type="html" xml:base="https://austinpray.com/javascript/2015/02/14/jquery-none-of-the-above-checkboxes.html"><![CDATA[<h2 id="demo">Demo</h2>

<p data-height="300" data-theme-id="12308" data-slug-hash="KwQQbE" data-default-tab="result" data-user="austinpray" class="codepen">See the Pen <a href="http://codepen.io/austinpray/pen/KwQQbE/">Smart Checkboxes</a> by Austin
  Pray (<a href="http://codepen.io/austinpray">@austinpray</a>) on <a href="http://codepen.io">CodePen</a>.</p>
<script async="" src="//assets.codepen.io/assets/embed/ei.js"></script>

<h2 id="source">Source</h2>

<ul>
  <li><a href="http://codepen.io/austinpray/pen/KwQQbE/">CodePen</a></li>
  <li><a href="https://gist.github.com/austinpray/0f5d6c5179e855892b46">Gist</a></li>
</ul>

<h2 id="problem">Problem</h2>

<p>I had a friend last night ask me:</p>

<blockquote>
  <p>Anyone good at jQuery want to tell me if the following is a convoluted?</p>
</blockquote>

<figure class="highlight"><pre><code class="language-js" data-lang="js"><span class="nx">$</span><span class="p">(</span><span class="dl">'</span><span class="s1">.gfield_checkbox input[value="none"]</span><span class="dl">'</span><span class="p">).</span><span class="nx">change</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
  <span class="nx">$</span><span class="p">(</span><span class="k">this</span><span class="p">).</span><span class="nx">parents</span><span class="p">(</span><span class="dl">'</span><span class="s1">.gfield_checkbox</span><span class="dl">'</span><span class="p">).</span><span class="nx">find</span><span class="p">(</span><span class="dl">'</span><span class="s1">input:checkbox</span><span class="dl">'</span><span class="p">).</span><span class="nx">not</span><span class="p">(</span><span class="k">this</span><span class="p">).</span><span class="nx">prop</span><span class="p">(</span><span class="dl">'</span><span class="s1">checked</span><span class="dl">'</span><span class="p">,</span> <span class="kc">false</span><span class="p">).</span><span class="nx">prop</span><span class="p">(</span><span class="dl">'</span><span class="s1">disabled</span><span class="dl">'</span><span class="p">,</span> <span class="nx">$</span><span class="p">(</span><span class="k">this</span><span class="p">)[</span><span class="mi">0</span><span class="p">].</span><span class="nx">checked</span><span class="p">);</span>
<span class="p">});</span></code></pre></figure>

<p>He had a group of checkboxes:</p>

<ul>
  <li>option 1</li>
  <li>option 2</li>
  <li>option 3</li>
  <li>none of the above</li>
</ul>

<p>He wanted them to behave such that:</p>

<ol>
  <li>When “none of the above” is checked: all the other options become unchecked and disabled.</li>
  <li>When “none of the above” is unchecked: all the other options become enabled again.</li>
</ol>

<p>There is nothing really wrong with the jquery one-liner my friend came up with. However I wanted a solution that didn’t rely on the user having to uncheck “none of the above” before being able to click another option. So:</p>

<ol>
  <li>When “none of the above” is checked: all of the other options become unchecked.</li>
  <li>When an option other than “none of the above” is selected and “none of the above” is already checked: “none of the above” should be automatically unchecked.</li>
</ol>

<h2 id="solution">Solution</h2>

<p><img src="/assets/checkbox.gif" alt="gif of checkboxes" /></p>

<h3 id="approach">Approach</h3>

<p>Each time a checkbox in the group is checked:</p>

<ol>
  <li>Loop through all of the checked boxes and get their values.</li>
  <li>If “none of the above” is among the values: decide which ones to uncheck based on the element that was just clicked.</li>
</ol>

<h3 id="deciding-when-to-uncheck">Deciding when to uncheck</h3>

<p>We loop through all of the checked boxes. For each element in the loop we evaluate this statement to determine if we should uncheck it:</p>

\[A = \text{User checked "none of the above"}\\
B = \text{Current element in the loop is "none of the above"}\\
A \oplus B \equiv (A \rightarrow B) \rightarrow (\neg(B \rightarrow A))\]

<p>or:</p>

<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="k">if</span> <span class="p">(</span><span class="nx">A</span><span class="o">^</span><span class="nx">B</span><span class="p">)</span> <span class="p">{</span>
  <span class="c1">// uncheck everything else</span>
<span class="p">}</span></code></pre></figure>

<p>If you are unfamiliar with logical operators: <a href="https://en.wikipedia.org/wiki/Exclusive_or">XOR</a>. In this case an exclusive OR saves us from:</p>

\[A = \text{User checked "none of the above"}\\
B = \text{Current element in the loop is "none of the above"}\\
(\neg A \wedge B) \vee (A \wedge \neg B)\]

<p>or this confusing mess:</p>

<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="k">if</span> <span class="p">((</span><span class="o">!</span><span class="nx">A</span> <span class="o">&amp;&amp;</span> <span class="nx">B</span><span class="p">)</span> <span class="o">||</span> <span class="p">(</span><span class="nx">A</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="nx">B</span><span class="p">))</span> <span class="p">{</span>
  <span class="c1">// uncheck everything else</span>
<span class="p">}</span></code></pre></figure>

<h2 id="conclusions">Conclusions</h2>

<p>This probably is/should be a jQuery plugin.</p>]]></content><author><name>Austin Pray</name></author><category term="javascript" /><category term="ui" /><summary type="html"><![CDATA[Demo]]></summary></entry><entry><title type="html">Build Steps and Deployment</title><link href="https://austinpray.com/ops/2015/01/15/build-steps-and-deployment.html" rel="alternate" type="text/html" title="Build Steps and Deployment" /><published>2015-01-15T00:00:00+00:00</published><updated>2015-01-15T00:00:00+00:00</updated><id>https://austinpray.com/ops/2015/01/15/build-steps-and-deployment</id><content type="html" xml:base="https://austinpray.com/ops/2015/01/15/build-steps-and-deployment.html"><![CDATA[<p><em>Based on the discussion over here:
<a href="https://github.com/roots/roots/pull/1257#issuecomment-70195437">roots/roots/pull/1257</a>.</em></p>

<p>These days your web project probably has a build step. Your build step takes
your styles, scripts, and other assets and packages them for the browser. Maybe
you are using <a href="http://gulpjs.com/">gulp</a>, <a href="http://gruntjs.com/">grunt</a>, or even <a href="https://www.gnu.org/software/make/">make</a> to orchestrate all your
compilation steps. This post covers a couple methods for deploying projects with
compiled assets and each method’s respective gotchas.</p>

<h2 id="general-rules-of-thumb">General Rules of Thumb</h2>

<ul>
  <li><strong>Never commit compiled files to source control.</strong>
    <ol>
      <li>Compiled files in a repo make it difficult to merge and rebase branches.</li>
      <li>Compiled files muddy the history and increase the file size of the repo.</li>
      <li>It just doesn’t make sense to put “binaries” in your <em>source</em> control unless you have some kind of specific vendoring strategy in mind.</li>
    </ol>
  </li>
  <li><strong>Deployments are completely automated and driven by your version control
  repository.</strong> Whether you are using something like
  <a href="http://capistranorb.com/">Capistrano</a>, a <a href="http://blog.codeclimate.com/blog/2013/10/02/high-speed-rails-deploys-with-git/">hand-rolled git deploy
  process</a>
  or a <a href="https://devcenter.heroku.com/articles/git">PaaS that gives you git deploy
  options</a>: you should have <a href="http://12factor.net/codebase">one
  codebase tracked in revision control and many
  deploys</a>.</li>
  <li><strong>Ideally Dev matches Production exactly.</strong> You should have a reproducible,
  portable development environment which exactly matches the environment your
  production app runs in. The most conventional way to do this is to use
  <a href="https://www.vagrantup.com/">Vagrant</a>. The new hotness is
  <a href="https://www.docker.com/">Docker</a>.</li>
</ul>

<h2 id="deployment-patterns">Deployment Patterns</h2>

<p>So at this point <a href="http://12factor.net/codebase">your source control drives your many deploys</a>. Your
build process outputs compiled files optimized for the browser. You are banned
from checking any of these compiled files into source control. So how do the
compiled files get to the proper place on the application server? It really
depends on who or what is going to be running the build.</p>

<h3 id="1-locally-compile-and-upload-assets">1. Locally Compile and Upload Assets</h3>

<p>As a part of your deployment process on your local development machine:</p>

<ul>
  <li>Build the project</li>
  <li>Copy compiled files to the remote destination</li>
</ul>

<h4 id="where-this-falls-apart">Where this falls apart:</h4>

<ul>
  <li><strong>No single source of truth:</strong> When you have multiple people deploying and
compiling there is a lack of “single source of truth”. This becomes a problem
when working on a team or when you hand off a project. You run the risk of
differences in a particular developer’s setup causing differences in the
compiled output. Although this doesn’t happen often if you are diligent about
locking down dependency versions: when it does it is extremely frustrating and
expensive. It amounts to a ping-pong of intermittent issues based on who is
actually deploying.</li>
  <li><strong>Everyone Requires Credentials:</strong> Anyone who needs to deploy has to have
access credentials for the server. This requires discipline and lots of work
to keep credentials from floating around in the wild.</li>
  <li><strong>Uploading is slow:</strong> uploading a bunch of files, especially over SCP, is
  generally pretty slow.</li>
  <li><strong>Requires Discipline:</strong> You cannot run a deploy while there are uncommitted
  changes in your local source assets, intentional or not. You will have to
  stash any changes before deploying. In addition, you have to be on the exact
  branch your target stage relies on. Otherwise, the assets you upload will not
  match the application files in git. For instance:
    <ol>
      <li>You are trying to deploy to “Staging” which pulls code from the “Develop”
branch.</li>
      <li>You unknowingly find yourself with the master branch checked out while
running the deploy.</li>
      <li>The deploy process will pull the latest code from the “Develop” branch.</li>
      <li>Your local machine will upload the compiled assets from the master
branch.</li>
      <li>Uh oh! Now, among other things, your templates don’t match your
JavaScript or your styles! You done goofed everything.</li>
    </ol>
  </li>
</ul>

<h4 id="recommendation">Recommendation:</h4>

<p>If you are a lone developer with no project handoff in sight, there is probably
nothing wrong with this pattern. It is crude but easy to setup. Also, if you do
not have the ability to install dependencies on the remote box you might be
forced to use this pattern.</p>

<h3 id="2-compile-on-the-application-server">2. Compile on the Application Server</h3>

<p>As a part of your initial server provisioning process:</p>

<ul>
  <li>Install build dependencies and runtimes. If, for instance, you are using gulp
to build your project’s assets you would install Node.js and npm.</li>
</ul>

<p>As a part of your deployment process trigger on the remote application server:</p>

<ul>
  <li>Update, prune, rebuild project build dependencies.
<a href="https://github.com/heroku/heroku-buildpack-nodejs/blob/e227568521c15875d9dd003a2562e885dcff0946/lib/build.sh#L166">(example)</a></li>
  <li>Trigger the build</li>
</ul>

<p>The application server becomes the single source of truth.</p>

<h4 id="where-this-falls-apart-1">Where this falls apart:</h4>

<ul>
  <li><strong>Everyone Requires Credentials</strong></li>
  <li><strong>Additional Dependencies Required on Application Server:</strong> most people prefer
to keep their application servers as lean as possible. Having your
application server “know” about the build process and its dependencies goes
against this principle. Additional dependencies means additional provisioning
time as well.</li>
  <li><strong>Not Suitable for Resource Intensive Build Processes:</strong> If you have a super
duper large build process this can steal resources from app for the duration
of the build process. In all likelihood, your development machine is a quad
core machine with 16GB of memory. Your application servers are probably single
core nodes with 1GB of memory or less. A minute long build process on your
dev machine could take 10 minutes on your lean application server.</li>
</ul>

<h4 id="recommendation-1">Recommendation:</h4>

<p>Generally for web projects this is a safe bet. The setup is cheap and simple.
Due to the nature of web assets having to be consumed by a browser: building
them is usually not a big deal. In my experience, the benefit of reproducible
builds for team environments (especially remote teams) for little to no setup
cost is greater than any hemming and hawing about additional dependencies being
installed on the server.</p>

<h3 id="3-continuous-integration">3. Continuous Integration</h3>

<p>Use something like <a href="http://jenkins-ci.org">Jenkins</a> or a hosted solution like
<a href="https://travis-ci.org">Travis</a>. Your CI server watches your repo for new
commits. When a commit happens the CI server:</p>

<ul>
  <li>Runs any pre-deploy checks (tests, linters, etc.)</li>
  <li>Runs deploy</li>
  <li>compiles and uploads assets</li>
</ul>

<p>So:</p>

<ul>
  <li>Continuous integration server becomes the single source of truth</li>
  <li>Developers only need access to the source code to be able to contribute to a
project. The CI server has all the privileged access under lock and key.</li>
  <li>Application server is as lean as possible, reduce server provisioning time.</li>
  <li>Discipline is enforced by CI server so when building human error is not a
thing. You can make sure tests must pass and style guides must be followed
before code makes it to production.</li>
</ul>

<h4 id="where-this-falls-apart-2">Where this falls apart:</h4>

<p>This doesn’t fall apart unless your CI server falls apart. However, the upfront
cost is significant. A CI server is definitely only worth it in the case of:</p>

<ul>
  <li>Team environment</li>
  <li>Long-lived project or lots of projects/modules to subsidize the upfront cost</li>
  <li>Continuous development</li>
</ul>

<h4 id="recommendation-2">Recommendation:</h4>

<p>This is probably the most ideal deployment setup, but it is definitely only
worth the effort in certain cases. Continuous integration requires continuous
development. If you are working on a long-lived product in a team environment CI
is pretty much required. I can’t imagine doing it any other way. For open source
projects: <a href="https://travis-ci.org/plans">Travis is free</a> and it is awesome. Run
your tests, linting or style guide against any and all contributions.
Automatically build and update your docs.</p>

<h2 id="conclusion">Conclusion</h2>

<p>There are lots of ways to skin this cat. The right solution depends heavily on
the context of the project. The good news is: if you are doing <em>something</em>
resembling any of the above you are probably on the right track.</p>]]></content><author><name>Austin Pray</name></author><category term="ops" /><category term="devops" /><summary type="html"><![CDATA[Based on the discussion over here: roots/roots/pull/1257.]]></summary></entry></feed>