4 min read

How to Easily Create a Mosaic Effect on Images with Simple Web Methods

Table of Contents

Let’s dive straight into the implementation first

The easiest way to create a mosaic effect on images for the web is by using an SVG filter.

It’s simple—just paste the following SVG code anywhere on the page:

<svg>
  <filter id="mosaic">
    <feFlood x="4" y="4" height="2" width="2"/>
    <feComposite width="8" height="8"/>
    <feTile result="a"/>
    <feComposite in="SourceGraphic" in2="a" operator="in"/>
    <feMorphology operator="dilate" radius="4"/>
  </filter>
</svg>

Then, simply apply the following CSS code to the areas where you want the mosaic effect:

img {
  filter: url(#mosaic);
}

Example Illustration

Below is the original image:

caniuse

After applying the filter effect:

caniuse


A Detailed Explanation of the Implementation

If you’re not interested in the effects of SVG filters or how they work in this context, feel free to skip ahead to the third section, where we demonstrate how to achieve the mosaic effect without using SVG filters.

  1. feFlood is used to apply an overlay with transparency. It supports the flood-color and flood-opacity properties. If these properties are not defined, the default is a solid black color. Therefore, the following line of code means:

    <feFlood x="4" y="4" height="2" width="2"/>
    

    Create a black square dot at coordinates (4,4), with width and height both set to 2.

  2. feComposite is used to blend two input image effects, similar to the mask-composite property. It determines how the overlapping portions of the two input images will be displayed, such as whether the intersection is hidden, the non-intersection is hidden, or if they are simply combined. In the code <feComposite width="8" height="8"/>, since no in or in2 attributes are set, it applies to the previous filter effects within the <filter> element, like the feFlood.

    This is equivalent to creating an 8x8 area and placing the feFlood black square inside it.

  3. feTile acts as a repeating background element, naturally tiling. In this case, it tiles the 8x8 area (including the black square in the center) across the entire original image. The effect is shown below:

    p1

  4. As explained earlier, feComposite is used to blend the black dot with the original image. The operator attribute defines the blending mode. operator=“in” means that the overlapping area between the in input image and the in2 input image will display the content of the in image, while the non-overlapping parts will be transparent.

    At this point, the filter effect looks like this:

    p1

  5. Finally, this feMorphology is the erosion and dilation filter. operator=“dilate” represents dilation, and the radius attribute defines the dilation radius. Technically, the dilation radius here should be 3, meaning radius=“3”, because the 8×8 area contains a 2×2 black square, and only 3 pixels of dilation are needed. However, the actual effect is somewhat different from the mosaic effect we want, where the edges have a rough blend. Therefore, I set the radius=“4”, and the effect is fantastic!


Achieving the mosaic effect without using SVG filters

Strategy for Achieving a Mosaic Effect by Enlarging a Small Image

On the web, when we apply the image-rendering: pixelated property to an image, if the original image is small and we increase its display size, the image will automatically become pixelated, creating an effect that closely resembles a mosaic.

For example, I have a profile picture that’s only 90px wide. Now, let’s resize it to 600px in width and apply the pixelated rendering mode. We can see the resulting effect in real-time.

origin:

p2

Here’s how the image looks after applying the mosaic effect.

p3

Strategy for a Large Image

If we only have a large image, we can reduce its size, and naturally, it will support the mosaic effect.

This can be achieved by using a canvas, where the original image is drawn on a smaller canvas, and then the image-rendering: pixelated property is applied to create the mosaic effect.

Here is the code

.mosaic {
  image-rendering: pixelated;
}
<div>
    <img alt="origin" src="https://raw.githubusercontent.com/trevortylerlee/n1/main/n1.jpeg" width="300" height="300">
    <img alt="mosaic" crossOrigin="anonymous" src="https://raw.githubusercontent.com/trevortylerlee/n1/main/n1.jpeg" width="300" height="300" class="mosaic">
</div>
const img = document.querySelector('.mosaic');
img.onload = function () {
    if (!this.src.startsWith('https')) {
        return;
    }
    const { clientWidth, clientHeight } = this;
    const canvas = document.createElement('canvas');
    canvas.width = clientWidth / 6;
    canvas.height = clientHeight / 6;
    const context = canvas.getContext('2d');
    context.drawImage(this, 0, 0, canvas.width, canvas.height);
    this.src = canvas.toDataURL();
};

And is result:

p4

Compatibility of the image-rendering CSS property

p5