title: How to transform black into any given color using only CSS filters
body: <p>My question is: given a target RGB color, what is the formula to recolor black (<code>#000</code>) into that color using only <a href="https://developer.mozilla.org/en/docs/Web/CSS/filter" rel="noreferrer">CSS filters</a>?</p>
  
  <p>For an answer to be accepted, it would need to provide a function (in any language) that would accept the target color as an argument and return the corresponding CSS <code>filter</code> string.</p>
  
  <p>The context for this is the need to recolor an SVG inside a <code>background-image</code>. In this case, it is to support certain TeX math features in KaTeX:  <a href="https://github.com/Khan/KaTeX/issues/587" rel="noreferrer">https://github.com/Khan/KaTeX/issues/587</a>.</p>
  
  <h3>Example</h3>
  
  <p>If the target color is <code>#ffff00</code> (yellow), one correct solution is:</p>
  
  <pre><code>filter: invert(100%) sepia() saturate(10000%) hue-rotate(0deg)
  </code></pre>
  
  <p>(<a href="http://jsbin.com/xetefa/1/edit?html,css,js,output" rel="noreferrer">demo</a>)</p>
  
  <h3>Non-goals</h3>
  
  <ul>
  <li>Animation.</li>
  <li>Non CSS-filter solutions.</li>
  <li>Starting from a color other than black.</li>
  <li>Caring about what happens to colors other than black.</li>
  </ul>
  
  <h2>Results so far</h2>
  
  <ul>
  <li><p>Brute-force search for parameters of a fixed filter list: <a href="https://stackoverflow.com/a/43959856/181228">https://stackoverflow.com/a/43959856/181228</a><br>
  Cons: inefficient, only generates some of the 16,777,216 possible colors (676,248 with <code>hueRotateStep=1</code>).</p></li>
  <li><p>A faster search solution using <a href="https://en.wikipedia.org/wiki/Simultaneous_perturbation_stochastic_approximation" rel="noreferrer">SPSA</a>:
  <a href="https://stackoverflow.com/a/43960991/181228">https://stackoverflow.com/a/43960991/181228</a>
  <strong>Bounty awarded</strong></p></li>
  <li><p>A <code>drop-shadow</code> solution:
  <a href="https://stackoverflow.com/a/43959853/181228">https://stackoverflow.com/a/43959853/181228</a><br>
  Cons: Does not work on Edge. Requires non-<code>filter</code> CSS changes and minor HTML changes.</p></li>
  </ul>
  
  <p>You can still get an <em>Accepted</em> answer by submitting a non brute-force solution!</p>
  
  <h3>Resources</h3>
  
  <ul>
  <li><p>How <code>hue-rotate</code> and <code>sepia</code> are calculated:
  <a href="https://stackoverflow.com/a/29521147/181228">https://stackoverflow.com/a/29521147/181228</a>
  Example Ruby implementation:</p>
  
  <pre><code>LUM_R = 0.2126; LUM_G = 0.7152; LUM_B = 0.0722
  HUE_R = 0.1430; HUE_G = 0.1400; HUE_B = 0.2830
  
  def clamp(num)
    [0, [255, num].min].max.round
  end
  
  def hue_rotate(r, g, b, angle)
    angle = (angle % 360 + 360) % 360
    cos = Math.cos(angle * Math::PI / 180)
    sin = Math.sin(angle * Math::PI / 180)
    [clamp(
       r * ( LUM_R  +  (1 - LUM_R) * cos  -  LUM_R * sin       ) +
       g * ( LUM_G  -  LUM_G * cos        -  LUM_G * sin       ) +
       b * ( LUM_B  -  LUM_B * cos        +  (1 - LUM_B) * sin )),
     clamp(
       r * ( LUM_R  -  LUM_R * cos        +  HUE_R * sin       ) +
       g * ( LUM_G  +  (1 - LUM_G) * cos  +  HUE_G * sin       ) +
       b * ( LUM_B  -  LUM_B * cos        -  HUE_B * sin       )),
     clamp(
       r * ( LUM_R  -  LUM_R * cos        -  (1 - LUM_R) * sin ) +
       g * ( LUM_G  -  LUM_G * cos        +  LUM_G * sin       ) +
       b * ( LUM_B  +  (1 - LUM_B) * cos  +  LUM_B * sin       ))]
  end
  
  def sepia(r, g, b)
    [r * 0.393 + g * 0.769 + b * 0.189,
     r * 0.349 + g * 0.686 + b * 0.168,
     r * 0.272 + g * 0.534 + b * 0.131]
  end
  </code></pre>
  
  <p>Note that the <code>clamp</code> above makes the <code>hue-rotate</code> function non-linear.</p>
  
  <p>Browser implementations: <a href="https://chromium.googlesource.com/chromium/blink/+/master/Source/platform/graphics/filters/FEColorMatrix.cpp" rel="noreferrer">Chromium</a>, <a href="https://github.com/mozilla/gecko-dev/blob/96f61369bfe520ed99d553a6378dd205978ed62e/gfx/src/FilterSupport.cpp#L423-L448" rel="noreferrer">Firefox</a>.</p></li>
  <li><p>Demo: Getting to a non-grayscale color from a grayscale color:
  <a href="https://stackoverflow.com/a/25524145/181228">https://stackoverflow.com/a/25524145/181228</a></p></li>
  <li><p>A formula that <strong>almost</strong> works (from a <a href="https://stackoverflow.com/questions/29037023">similar question</a>):<br>
  <a href="https://stackoverflow.com/a/29958459/181228">https://stackoverflow.com/a/29958459/181228</a></p>
  
  <p>A detailed explanation of why the formula above is wrong (CSS <code>hue-rotate</code> is not a true hue rotation but a linear approximation):<br>
  <a href="https://stackoverflow.com/a/19325417/2441511">https://stackoverflow.com/a/19325417/2441511</a></p></li>
  </ul>