Use an image as a mask for another image

I’m making a website that I wanted to be a white page that you could stamp to make another image appear under. So when you click, you make a holepunch. Like this exemple : enter image description here

So I managed to have a randomized image in the background as I click which is fine for what I want, and to be able to .append() the holepunches. But I don’t know how to do the mask thing I’ve been digging online for a few things and help, and managed to make it work in certain cases but not that one…

It should be like that (I guess) :

  • image in the background
  • white shape in front
  • the star shape is making a holepunch in the white shape

For now, the only thing I managed to do is to have the picture besides a bigger holepunch (which is my original img) but when I click it doesn’t make any holepunch, it justs add the stamp.

Here is the code :

var images = ["https://icatcare.org/app/uploads/2018/07/Thinking-of-getting-a-cat.png", "https://ichef.bbci.co.uk/news/1024/cpsprodpb/151AB/production/_111434468_gettyimages-1143489763.jpg"];


$(document.body).click(function(c) {
  var tw = 100 / 2;
  var th = 30 / 2;
  var x = Math.floor((Math.random() * images.length));
    document.getElementById('random').src = images[x];
  $("#random").css({
    position: 'absolute',
    display: "block",
    left: 0,
    top: 0
  });
    var tw = 50 / 2;
    var th = tw;
  $('#holepunch:last').clone().appendTo(this).css({
    position: 'absolute',
    display: "block",

    left: c.pageX - tw - $(this).position().left,
    top: c.pageY - th + $(this).scrollTop()
});
});

     
body{
  background: lightgrey;
  width: 100vw;
  height: 100vh;
  z-index: 1;
  opacity: 1;
}
.fauxbody{
  z-index: 100;
  position: fixed;
  width: 100vw;
  height: 100vh;
  background-color: white;
  top: 0;
  left: 0;

  -webkit-mask:
    -moz-element(#holepunch) 1vw 1vh no-repeat,
    linear-gradient(#fff 0 0);
  mask-composite:exclude;
}

#random{
   z-index: -100;
  width: 100vw;
  height: auto;
}

#holepunch{
  width: 50px;
  height: auto;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<body>
  <img id="random">
  <div class ="fauxbody">

    <img id="holepunch" src="https://oshi.at/iimtXg/Jqtz.png">
    </div>
</body>

Answer

Here is an idea using multiple mask and CSS variables. The trick is to add an extra layer on each click. I removed the code related to background generation since it’s irrelevant and quite easy to be added

var mask = "";
w = 60;
h = 60;

document.documentElement.addEventListener("click", function (c) {
    mask+="url(https://i.ibb.co/FzmCjLL/Jqtz.png)"+(c.pageX-w/2)+"px "+(c.pageY-h/2)+"px/"+w+"px "+h+"px no-repeat,";
   document.documentElement.style.setProperty("--mask", mask)
});
html {
  background:url(https://picsum.photos/800/800) center/cover;
}

html::before {
  content:"";
  position: fixed;
  background-color: #f3f3f3;
  top: 0;
  left: 0;
  right: 0;
  bottom:0;
  -webkit-mask: 
     var(--mask)
     linear-gradient(#fff 0 0);
  -webkit-mask-reepat: no-repeat;
  -webkit-mask-composite: destination-out;
  mask-composite: exclude;
}

Also like below without mask-composite:

var mask = "";
w = 60;
h = 60;

document.documentElement.addEventListener("click", function (c) {
    if(mask!="")
      mask+=",";
    mask+="url(https://i.ibb.co/FzmCjLL/Jqtz.png)"+(c.pageX-w/2)+"px "+(c.pageY-h/2)+"px/"+w+"px "+h+"px no-repeat";
   document.documentElement.style.setProperty("--mask", mask)
});
html::before {
  content:"";
  position: fixed;
  background:url(https://picsum.photos/800/800) center/cover;
  top: 0;
  left: 0;
  right: 0;
  bottom:0;
  -webkit-mask:var(--mask,linear-gradient(transparent 0 0));
}