This post is part of a series on building Connect Four with Vue.js, SVG, Elixir, and the Phoenix framework.

One of my recent side projects to learn new technologies has been to build a browser-based rendition of Connect Four. The fully-functional version of the game will have both a backend and frontend component with some good challenges including animating checkers falling into place and connecting two players over the network.

In this post, we'll demonstrate rendering the static board with SVG, including the use of pattern masking to emulate a game board wall with portholes through which to view the checkers.

Let's talk about SVG

SVG feels scary and confusing to the uninitiated, myself included. The good news is that we can take a progressive approach to adopting SVG without understanding everything there is to know about it at first.

Here are a few reasons why using SVG is a good fit to render the elements of a Connect Four game board:

Pattern masking

A first pass at the game board might be to explicitly render circles for all 42 game cells (6 rows, 7 columns) and set their fill colors based on game state to red, black, or as the background color to fake an empty cell. What if, instead, we could render the game board with portholes punched into it? That way, the only <circle> elements we need to render explicitly are the checkers themselves.

This is where SVG pattern masking comes in. A powerful feature of desktop visual editing tools Adobe Photoshop/Illustrator is available on the web. Masking allows for a graphic (or set of graphics) to act as a transparent overlay to reveal background elements. Applied to our game, it provides a mechanism by which we can see checkers falling through holes in the game board wall. To my knowledge, there's no (easy) way with typical HTML/CSS to accomplish this other than, perhaps, creating a transparent png, something we won't be able to manipulate easily programmatically.

Let's start with a demonstration of pattern masking by rendering a single game board cell and checker.

Within a containing <svg> element with a viewBox of 100x100 units, we'll start by adding a <circle> to represent a checker positioned slightly offscreen to mimic it falling into place.

<svg viewBox="0 0 100 100" xmlns="">
  <circle cx="50" cy="25" r="45" fill="#254689"></circle>

The game wall is simply a <rect> positioned over the circle; once added, we can't see the circle anymore.

<svg viewBox="0 0 100 100" xmlns="">
  <circle cx="50" cy="25" r="45" fill="#254689"></circle>
  <rect col="0" width="100" height="600" fill="cadetblue" mask="url(#cell-mask)"></rect>

Now to create the hole with a pattern mask. To accomplish this, we use (surprise) both a <pattern> and <mask> elements. These elements are not graphical, meaning, they won't be directly rendered in the view box. Instead, we'll later add the resulting mask as an attribute of our <rect> game wall—the element we want to see through. The <pattern> and <mask> elements are nested inside a <defs> element so they can be available for reuse.

  <pattern id="cell-pattern" patternUnits="userSpaceOnUse" width="100" height="100">
    <circle cx="50" cy="50" r="45" fill="black"></circle>
  <mask id="cell-mask">
    <rect width="100" height="100" fill="white"></rect>
    <rect width="100" height="100" fill="url(#cell-pattern)"></rect>

The <pattern> is simply matches the size of a cell, 100x100, and it contains a <circle>, representing the hole, that matches the size of the checker. The <circle> gets a fill color of "black"; when applied the to <mask>, this this means the absence of space, or full transparency, as opposed to literal black.

The <mask> is composed of two <rect> elements that match the game wall size; the first gets a fill color of "white" (opposite of "black" in a mask) to represent the part of the wall we want to be opaque. The second <rect> sits on top of the first and has a fill of url(#cell-pattern) which refers to the pattern we created above.

Now, we can set the mask attribute for our game wall <rect> by referencing the <mask> element by id.

<rect width="100" height="100" fill="cadetblue" mask="url(#cell-mask)"></rect>

This punches a hole through the wall to reveal the checker underneath.

Here's what we have so far on CodePen:

See the Pen SVG mask demo by Ross Kaffenberger (@rossta) on CodePen.

A nice feature of the <pattern> element is that it repeats itself based on the height/width attributes we've provided. This means we can extend the dimensions of the view box and our game wall <rect> to reveal the seven rows of a single column—we don't have to add each circular hole to the DOM explicitly! To build multiple columns, we'll simply, for each column, add a nested <svg> element at the correct x position to wrap each masked <rect>. This allows us to position each column relative to the container <svg> without needed to specify x coordinates for each child <rect> and <circle>.

Here's the full demo of a static Connect Four SVG game board on CodePen:

See the Pen Connect Four board in SVG by Ross Kaffenberger (@rossta) on CodePen.

Finally, here's a sneak preview of the how the falling checker will look behind the pattern mask:

See the Pen SVG mask demo by Ross Kaffenberger (@rossta) on CodePen.

For more related info, check out the following resources:

In the next post, we'll take a look at using Vue.js to render the board dynamically and add checkers based on user interaction.

Discuss it on Twitter · Part of the Connect Four series. Published on Jan 10, 2018

More posts

Building basic Connect Four with Vue.js

Continuing our Connect Four series, we will take a look at converting a static HTML representation of a Connect Four board and add functionality and dynamic rendering with Vue.js.

These Rails apps are overpacking their JavaScript bundles

A case study of Rails applications making a common Webpacker mistake of rendering modules multiple times on a single page. We'll describe why the problem happens and present a Webpacker Packing Checklist for proper code-splitting.

A visual guide to Webpacker

Navigate the world of Webpacker and webpack on Rails with confidence using this collection of mental maps I put together.

Background Photo by Matthew Kane on Unsplash