Area17 Recreated: Beautiful Scaling Circles Mask

Area 17 won a 2022 Honorable Mention in Awwwards Site of the Year Contest. The hero section is a gorgeous animated reveal using a mask of circles that shows a hidden SVG as a grid of circles grows. I tip my hat to their designers and developers, and show you how to build it with Webflow, GSAP, and Paper.js in this video!

Watch the Tutorial on YouTubeGet the Project Cloneable

Inside <head> tag

<script defer src="https://cdnjs.cloudflare.com/ajax/libs/paper.js/0.12.17/paper-full.min.js"></script>
<script defer src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.11.4/gsap.min.js"></script>

Inside </body> tag

<script>
const init = () => {
  // Select elements we need from HTML
  const canvas = document.querySelector("#paper-canvas");
  const twoTwoEl = document.querySelector("#twotwo-svg");
  const twoThreeEl = document.querySelector("#twothree-svg");

  // exit our code if elements aren't found
  if (!canvas || !twoTwoEl || !twoThreeEl) return;

  // initialize canvas and important values
  paper.setup(canvas);
  const w = canvas.clientWidth;
  const h = canvas.clientHeight;
  const NUM_COLS = 14;

  // import white 2022 SVG
  const twoTwoGroup = paper.project.importSVG(twoTwoEl);
  twoTwoGroup.fillColor = new paper.Color(1, 1, 1);

  // import black 2023 SVG
  const twoThreeGroup = paper.project.importSVG(twoThreeEl);
  twoThreeGroup.fillColor = new paper.Color(0, 0, 0);

  // create blue rectangle
  const rect = new paper.Path.Rectangle({
    point: new paper.Point(0, 0),
    size: [w, h],
  });
  rect.fillColor = new paper.Color("#0D9AFF");

  // size SVGs to the viewport
  twoTwoGroup.fitBounds(paper.view.bounds);
  twoThreeGroup.fitBounds(paper.view.bounds);

  // group the blue rectangle and 2023 SVG together
  const frontGroup = new paper.Group([rect, ...twoThreeGroup.children]);

  // create circlePaths, which will be used to create our clipping mask and animation
  const circlePaths = createCircles(w, h, NUM_COLS);

  // create a clipping mask from our circlePaths, blue rectangle, and 2023 SVG
  const circleGroup = new paper.Group([...circlePaths]);
  let clipGroup = new paper.Group([circleGroup, frontGroup]);
  clipGroup.clipped = true;

  gsap.fromTo(
    circlePaths,
    { scaling: 0.001 }, // don't use 0!
    {
      scaling: 2,
      duration: 2,
      repeat: -1,
      yoyo: true,
      stagger: 0.01,
    }
  );
};

function createCircles(w, h, numCols) {
  let circles = [];

  // determine size of each circle
  let space = w / numCols;

  // loop through width and height, incrementing by circle diameter each time.
  for (let x = 0; x < w; x += space) {
    for (let y = 0; y < h; y += space) {
      let circle = new paper.Path.Circle({
        radius: space / 2,
        center: new paper.Point(x + space / 2, y + space / 2),
        applyMatrix: false, // skip recalculations that we don't need.
      });
      circles.push(circle);
    }
  }
  return circles;
}

document.addEventListener("DOMContentLoaded", init);
</script>