Silhouette of a color image


Thanks to Rene Limón's response , I was able to create a CSS silhouette like this:

#silueta img {
  -webkit-filter: grayscale(100%) brightness(0); /* Safari 6.0 - 9.0 */
  filter: grayscale(100%) brightness(0);
  opacity: 0.6;
<div id="original">
  <img src="" height="150px" width="auto" />
<div id="silueta">
  <img src="" height="150px" width="auto" />

But there is a problem: the silhouette is black / gray and I would like it to be of other colors to superimpose several and to easily differentiate what corresponds to each of them.

I have tried changing the filter values to add hue-rotate , but it has been unsuccessful. I tried combining with the most voted solutions to this question (in English), but They did not work either. I also tried adding different filters to div e img , but I was not lucky.

I imagine that the problem is that when you set the brightness to zero, the silhouette is black and even if you change the value of hue-rotation it will not affect because it will continue to be black.

How could you get a silhouette with color using CSS? What combination of filters would work?

asked by Alvaro Montoro 01.03.2017 в 01:50

3 answers


If you decrease the grasycale , what will happen is that the image will not be "opaque" and you will see the color if you break the hue and if you use contrast and saturation . It would be very simple if there was a "fill" option. One way to get the desired effect is through Canvas. All you have to do is draw the image, get its imageData and overwrite the painted colors.


let palette = {
  orange: {
    red: '243',
    green: '156',
    blue: '18'
  midnight: {
    red: '44',
    green: '62',
    blue: '80'
  greensea: {
    red: '22',
    green: '160',
    blue: '130'

let orange = document.getElementById('orange');
let midnight = document.getElementById('midnight');
let greensea = document.getElementById('greensea');

drawImage(midnight, palette.midnight);
drawImage(greensea, palette.greensea);

function drawImage(canvas, color) {
  let ctx = canvas.getContext('2d');
  let img = new Image();

  img.onload = draw;
    .then(b64 => img.src = b64);

  function draw() {
    let hRatio = canvas.width / img.width;
    let vRatio = canvas.height / img.height;
    let ratio = Math.min(hRatio, vRatio);
    let centerX = (canvas.width - img.width * ratio) / 2;
    let centerY = (canvas.height - img.height * ratio) / 2
      img.width * ratio,
      img.height * ratio

    let imageData = ctx.getImageData(0, 0, img.width, img.height);
    let data =;

    for (let i = 0; i < data.length; i += 4) {
      // rojo
      data[i] =;
      // verde
      data[i + 1] =;
      // azul
      data[i + 2] =;

    // sobreescribe la imagen original
    ctx.putImageData(imageData, 0, 0);

function fetchImage(image) {
  return new Promise((resolve) => {
    let http = new XMLHttpRequest();'GET', image);
    http.responseType = 'arraybuffer';

    http.onload = function() {
      if (this.status === 200 && this.readyState === 4) {
        let blob = new Blob(
          [this.response], {
            type: 'image/png'
        let url = window.URL.createObjectURL(blob);
        let reader = new FileReader();
        reader.onloadend = function() {
*:after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;

body {
  align-items: center;
  display: flex;
  flex-flow: column nowrap;
  justify-content: center;
  padding: 20px;
<!-- Para navegadores que no implementen Promise -->
<script src=""></script>

<canvas id="orange" width="500" height="300"></canvas>
<canvas id="midnight" width="500" height="300"></canvas>
<canvas id="greensea" width="500" height="300"></canvas>

Obviously you can draw the three shapes in a single Canvas stage to be able to move them. If you want to move them, you must associate a handler for the drag event in Canvas, obtain the shape according to the coordinates of the pointer and repaint the shape by updating the position according to the pointer.

Note: The images submitted to this procedure must be hosted locally or via base64, otherwise you will get the error "The canvas has been tainted by cross-origin data . at drawImage ", which means that it can not deal with an image from another domain.

answered by 01.03.2017 / 19:06

In this post suggest the combination of three coloring filters the image:


filter: grayscale(100%) sepia(100%) hue-rotate(360deg);
filter: grayscale(100%) sepia(100%) hue-rotate(0deg);


filter: grayscale(100%) sepia(100%) hue-rotate(100deg);


filter: grayscale(100%) sepia(100%) hue-rotate(200deg);

I think it's the closest thing to what you're looking for in what you can use "filter"

answered by 01.03.2017 в 05:46

Translation of the r answer that chazsolo wrote on the site in English.

Instead of using a gray scale, you could set the contrast to 0 and then play with the other values. A high value of saturate will help to mark more colors.

Here you can see it working:

#silhouette img {
  -webkit-filter: contrast(0) sepia(100%) hue-rotate(190deg) saturate(2000%) brightness(100%);
  filter: contrast(0) sepia(100%) hue-rotate(190deg) saturate(2000%) brightness(100%);
  opacity: 1;
<div id="original">
  <img src="" height="150px" width="auto" />
<div id="silhouette">
  <img src="" height="150px" width="auto" />
answered by 23.05.2017 в 14:39