Cool Profile Picture Animation in HTML, CSS, and JS

I saw this cool pixilation filter in the About Us section of a landing page, but it was static and boring. So, I created this effect using pure HTML, CSS, and JS and animated it on hover to reveal the faces.

The Demo
Photo by Alex Suprun on Unsplash
The Code
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Pixel Profile Pic test</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background-color: #000814;
}
.main_container {
width: 100%;
min-height: 100vh;
overflow: hidden;
display: flex;
justify-content: center;
align-items: center;
}
.outerImage {
width: 400px;
height: 400px;
display: flex;
justify-content: center;
align-items: center;
background-image: url("/pi.png");
background-size: cover;
background-position: center;
overflow: hidden;
border-radius: 20%;
}
.innerImage {
width: 100%;
height: 100%;
background-size: cover;
background-position: center;
clip-path: polygon(20% 20%, 80% 20%, 80% 80%, 20% 80%);
border-radius: 20%;
}
</style>
</head>
<body>
<div class="main_container">
<div
class="outerImage"
onmouseenter="removePixelation()"
onmouseleave="applyPixelation()"
>
<canvas id="canvas" class="innerImage"></canvas>
</div>
</div>
<script>
var img = new Image();
img.src = "/pi.png";
img.onload = function () {
console.log("image loaded");
draw(this);
};
var pixels = [];
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var size = 30; // size of the pixels
var imgData;
function draw(img) {
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0, img.width, img.height);
imgData = ctx.getImageData(0, 0, canvas.width, canvas.height);
applyPixelation();
}
var originalImagel;
function applyPixelation() {
console.log("applying pixelation");
originalImagel = ctx.getImageData(0, 0, canvas.width, canvas.height);
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Create an array of all pixel positions
pixels = [];
for (var i = 0; i < imgData.width; i += size) {
for (var j = 0; j < imgData.height; j += size) {
pixels.push({ x: i, y: j });
}
}
// Shuffle the array
pixels.sort(() => Math.random() - 0.5);
var index = 0;
function drawPixel() {
for (let k = 0; k < 4; k++) {
// Draw 4 pixels at a time
if (index >= pixels.length) {
return; // All pixels have been drawn
}
var pixel = pixels[index++];
var color = getAverageColor(pixel.x, pixel.y, size, imgData);
ctx.fillStyle =
"rgb(" + color.r + "," + color.g + "," + color.b + ")";
ctx.fillRect(pixel.x, pixel.y, size, size);
}
setTimeout(drawPixel, 0); // Call drawPixel again after a delay
}
drawPixel(); // Start the drawing process
}
// function removePixelation() {
// ctx.putImageData(imgData, 0, 0);
// }
function removePixelation() {
if (!pixels || pixels.length === 0) {
// Reinitialize the pixels array if needed
pixels = [];
for (var i = 0; i < imgData.width; i += size) {
for (var j = 0; j < imgData.height; j += size) {
pixels.push({ x: i, y: j });
}
}
// Reverse the order of the pixels to un-pixelate from the last to the first
pixels.reverse();
}
var index = 0;
function revealPixel() {
for (let k = 0; k < 4; k++) {
// Reveal multiple pixels at a time for a faster animation
if (index >= pixels.length) {
return; // All pixels have been revealed
}
var pixel = pixels[index++];
// Clear the specific pixel block
ctx.clearRect(pixel.x, pixel.y, size, size);
// Draw the original pixels within each block from the stored original image
ctx.putImageData(
originalImagel,
-pixel.x,
-pixel.y,
pixel.x,
pixel.y,
size,
size
);
}
setTimeout(revealPixel, 0); // Call revealPixel again after a delay
}
revealPixel(); // Start the revealing process
}
function getAverageColor(x, y, size, imgData) {
var total = { r: 0, g: 0, b: 0, count: 0 };
for (var i = 0; i < size; i++) {
for (var j = 0; j < size; j++) {
var idx = (x + i + (y + j) * imgData.width) * 4;
total.r += imgData.data[idx + 0];
total.g += imgData.data[idx + 1];
total.b += imgData.data[idx + 2];
total.count++;
}
}
return {
r: (total.r / total.count) | 0,
g: (total.g / total.count) | 0,
b: (total.b / total.count) | 0,
};
}
</script>
</body>
</html>
The Conclusion

I added this cool animated pixel effect to my portfolio site. It makes the profile picture come to life when you hover over it. Check it out at daviddodda.com.
and shutout to @domi_kissi and @inCleveri for the inspiration.






