For new mountains
reload the page

[Part 2] Building mountains: Building the first version

On the first part of this series, we learnt how we could create polygons using CSS’s clip-path. Now, on the second part of this series we will explore how we can leverage clip-path to create polygons that resemble mountains on a retro game.

Visualizing mountains

On a surface level, mountains can be described as

a large natural elevation of the earth’s surface rising abruptly from the surrounding level; a large steep hill.

Oxford Languages

The idea is to use an algorithm that builds a shape step by step from left to right. These steps are then used as input for clip-path, creating the visual effect of a mountain.

Animation depicting the visual representation of a mountain’s slope

Now that we have broken down the first step we can start to manually code a polygon that resembles this very shape:

.polygon {
    clip-path: polygon(
        0% 100%,
        0% 90%,
        10% 90%,
        10% 80%,
        20% 80%,
        20% 70%,
        30% 70%,
        30% 60%,
        40% 60%,
        40% 50%,
        50% 50%,
        50% 40%,
        60% 40%,
        60% 30%,
        70% 30%,
        70% 20%,
        80% 20%,
        80% 10%,
        90% 10%,
        90% 0%,
        100% 0%,
        100% 100%
    );
}
Mountain’s slope illustration going from left to right and going up

Now it’s a matter of adjusting our percentages to create our very first mountain looking like shape:

Retro illustration of a shape that looks like the peak of a mountain

Adjusting how many “pixels” are in a polygon

The shape above gives an impression of a mountain but looks more like steps than a mountain. The main reason for that is the “pixel density” we are packing in this polygon. The polygon above has 5 “pixels” horizontally. In order to make it look more like a mountain we need to be able to pack more “pixels” in it horizontally.

Illustration showing a pixelated mountain packing more “pixels” in it and giving a better impression of a mountain

What we have done is essentially add more vertices to our polygon:

.polygon {
    clip-path: polygon(0% 100%, 0% 90%, 4% 90%, 4% 86%, 8% 86%, 8% 82%, 12% 82%, 12% 78%, 16% 78%, 16% 74%, 20% 74%, 20% 70%, 24% 70%, 24% 66%, 28% 66%, 28% 62%, 32% 62%, 32% 58%, 36% 58%, 36% 54%, 40% 54%, 40% 50%, 44% 50%, 44% 46%, 48% 46%, 48% 42%, 52% 42%, 52% 46%, 56% 46%, 56% 50%, 60% 50%, 60% 54%, 64% 54%, 64% 58%, 68% 58%, 68% 62%, 72% 62%, 72% 66%, 76% 66%, 76% 70%, 80% 70%, 80% 74%, 84% 74%, 84% 78%, 88% 78%, 88% 82%, 92% 82%, 92% 86%, 96% 86%, 96% 90%, 100% 90%, 100% 94%, 100% 100%);
}

but you will quickly notice that this might be tedious and very prone to error to create this manually. Since this is a deterministic path we can create an algorithm to create such “steps”:

Playable illustration of a mountain where it allows you to pick the number of steps (or “pixels”)
Pixel size
function generateZigZagPolygon(
  elementWidth: number,
  elementHeight: number,
  pixelSize = 10,
): string {
  const points: string[] = [];
  const midX = elementWidth / 2;

  let y = elementHeight;

  points.push("0% 100%");
  points.push(`0% ${(y / elementHeight) * 100}%`);

  for (let x = 0; x < elementWidth; x += pixelSize) {
    const nextX = x + pixelSize;
    const yPercent = (y / elementHeight) * 100;
    const xPercent = (nextX / elementWidth) * 100;

    points.push(`${xPercent}% ${yPercent}%`);

    if (nextX <= midX) {
      y -= pixelSize;
    } else {
      y += pixelSize;
    }

    points.push(`${xPercent}% ${(y / elementHeight) * 100}%`);
  }

  points.push("100% 100%");

  return `polygon(${points.join(", ")})`;
}

In this first version, we have understood what we understand by “mountains” and we could recreate a retro look mountain using clip-path. On the first step we have created a simple version where we essentially create a “step” looking like shape going from left to right. Then, we adapted this shape to create what it looks like a retro mountain. Then, we take this concept and create an algorithm to generate the mountains dynamically based on the container’s width, height and desired pixel size.

Next step will be to create a dynamically generated terrain with multiple mountains on it. You will notice that the terrain will look almost the same and have unnatural look to it, we will then introduce the concept of a random flatness to the terrain.