Small games are a fun way to learn more about a new technology. This
article walks you through your own implementation of Conway's Game of Life
using CoffeeScript and the HTML5
element. Though Conway's Game of Life is probably not exactly a game, it's
a great little task that is very manageable.
Regarding technology, you'll need:
- A decent web browser that can handle the HTML5
- The CoffeeScript compiler, in order to program CoffeeScript. It is recommended that you install Node.js and then use Node Package Manager (NPM) to install it (see Resources). The example in this article uses Version 1.3.3 of the CoffeeScript compiler.
- Your favorite text editor.
You can download the source code for the examples used in this article.
Conway's Game of Life
Conway's Game of Life is basically a simulation that takes place on a two-dimensional orthogonal grid of square cells. Every cell is in one of two possible states: dead or alive. The game consists of periodic updates, which are also called ticks. With every tick the current generation of cells evolves into the next generation. During a tick, the following rules get applied to every single cell:
- A living cell will die due to under-population if it has fewer than two living neighbors.
- A living cell with two or three living neighbors lives on to the next generation.
- A living cell with more than three living neighbors dies due to overcrowding.
- A dead cell with exactly three living neighbors becomes a living cell due to reproduction.
The first generation of cells is created randomly. After that, the simulation runs until all cells are dead or patterns emerge. See Resources for more information about the different patterns and the history of Conway's Game of Life.
Figure 1. Example Conway's Game of Life implementation
In this article, the Conway's Game of Life implementation consists of two parts: the first part is the HTML5 markup and CSS, which is the groundwork required for the second part; the second part is the actual CoffeeScript implementation of the game.
HTML5 markup and CSS
The first step is to create a directory named game-of-life where you'll save all of the example files. The markup and CSS need a place to stay, so create a new index.html file within the new game-of-life directory. Listing 1 shows the HTML5 markup and CSS needed for the Conway's Game of Life implementation.
Listing 1. Markup and CSS of the index.html file
Let's add a little style so this Game of Life looks pretty. The body element gets a dark background and you add a border to the canvas element. With the CSS attributes margin and display, you ensure that the canvas is nicely positioned in the center of the screen.
To start the Game of Life, the code adds a little script block to the body
of the markup and creates a new
Game of Life CoffeeScript implementation
The example implementation consists of only one class, so you need to create only one corresponding file. The game_of_life.coffee file, which will contain all the CoffeeScript code, is located in the coffeescripts directory.
To run this command, you have to navigate to the game-of-life directory in
your favorite command line tool. Use the
example specifies the output folder with the
--output flag. Then the
--compile flags on the coffeescripts directory
What does all this mean? Whenever a file in the coffeescripts directory
gets modified, the
coffee command will pick it
directory. Now you know why we never created the game_of_life.js file that
was included in Listing 1.
When you write CoffeeScript code in the game_of_life.coffee file, it will
into the game_of_life.js file.
Initializing the game
Now that the compile issue is addressed, you can start programming the
example version of Game of Life. Open the file game_of_life.coffee in your
text editor. As shown in Listing 2, there's a single
GameOfLife class with several attributes.
GameOfLife class and attributes
class GameOfLife currentCellGeneration: null cellSize: 7 numberOfRows: 50 numberOfColumns: 50 seedProbability: 0.5 tickLength: 100 canvas: null drawingContext: null
Self-explaining variable and method names make reading source code easy. In Listing 2:
- Since Conway's Game of Life consists of a two-dimensional grid of
cells, the example also needs something like that. The
currentCellGenerationattribute will hold all cells in a two-dimensional array.
cellSizespecifies the width and height of a single cell—in our case, seven pixels.
- The attributes
numberOfColumnsdetermine the size of the grid.
- Conway's Game of Life needs an initial pattern of cells, which is also
called the seed. When creating the seed, the
seedProbabilityattribute is used to determine if a cell is dead or alive.
tickLengthattribute specifies in which interval the game gets updated. In the example, the game updates every one hundred milliseconds.
canvasattribute will save the canvas element you're going to create.
- To draw graphics on the canvas, you need the drawing context, which is
stored in the
The constructor of the
GameOfLife class is
responsible for setting up the game. As shown in Listing 3, you have to create a canvas before
you can resize it to the correct dimensions. Then, you can use the newly
created canvas to create the drawing context. After that, you're ready to
create the initial seed pattern and kick off the game loop by starting the
first tick. But first, let's create the canvas.
Listing 3. Constructor for the
constructor: -> @createCanvas() @resizeCanvas() @createDrawingContext() @seed() @tick()
Because modern browsers provide a great API to manipulate the document
object model (DOM), and the example doesn't require anything fancy; you
can ditch external frameworks like jQuery. Use the
document.createElement method to create a new
canvas element, which you then store in the equally named attribute.
Append the newly created element to the body of the page. All of this
happens in the
createCanvas method, as shown in
Listing 4. Setting up the canvas element
createCanvas: -> @canvas = document.createElement 'canvas' document.body.appendChild @canvas resizeCanvas: -> @canvas.height = @cellSize * @numberOfRows @canvas.width = @cellSize * @numberOfColumns createDrawingContext: -> @drawingContext = @canvas.getContext '2d'
resizeCanvas method uses the
numberOfColumns to calculate the width and
height of the canvas element. The third method in Listing 4,
createDrawingContext, gets the 2d context from
the canvas and stores it for future use.
Apart from these three methods, the constructor in Listing 4 invokes two additional methods:
cover a fairly big part of the code and are discussed in the following
Creating the initial seed pattern
Conway's Game of Life requires an initial seed pattern. Based on the initial seed pattern, the cells on the grid evolve every tick into the next generation. To create the seed, you have to decide randomly for every cell on the grid if it is alive or dead using the seed method, as shown in Listing 5. Two nested for loops allow you to visit every cell on the grid.
The outer loop loops over all rows, accomplished with a CoffeeScript
feature called ranges. The three periods
for row in
[0...@numberOfRows] indicate that
the range is exclusive. If
numberOfRows has the
value 3, the iterator (in this case the row variable) will have the values
ranging from 0 to 2. This lets you create the two-dimensional array
The inner loop loops over all columns and creates a new
seedCell for every cell. It invokes the
createSeedCell method with the current row and
column. After creating the seed cell, it stores it at the correct position
Listing 5. Initial seed pattern
seed: -> @currentCellGeneration =  for row in [0...@numberOfRows] @currentCellGeneration[row] =  for column in [0...@numberOfColumns] seedCell = @createSeedCell row, column @currentCellGeneration[row][column] = seedCell createSeedCell: (row, column) -> isAlive: Math.random() < @seedProbability row: row column: column
Creating a new seed cell is easy. A cell is a simple object and consists of
three attributes. The
createSeedCell method in
Listing 5 simply passes
the row and column arguments to the cell object. The
isAlive attribute determines if the cell is
dead or alive. With the help of the
method and the
seedProbability attribute, you
randomly create dead or alive cells. You might have noticed you don't have
to use the return keyword since CoffeeScript methods automatically return
their final value.
The game loop
Now that you've created the initial seed pattern, it's time to bring the
Game of Life to life. You have to draw the current generation of cells to
the canvas and evolve the generation into the next one. And all of this
needs to happen in a regular interval. As shown in Listing 6, start this interval by invoking
tick method. The
tick method, in Listing 6, does three things. It:
- Draws the current cell generation by invoking the
- Evolves the current cell generation into the next one.
- Sets a timeout to keep the game loop running.
setTimeout method with two arguments.
The first argument is the method that should be invoked—in this
tick method itself. The second
argument defines the number of milliseconds that should pass before it
gets invoked. You can control the speed of the game loop with the
You might have noticed the difference between the
tick method and all other methods. The tick
method uses the fat arrow (
=>) feature of
CoffeeScript. The fat arrow binds the method to the current context. The
context will always be correct. Without this, the timeout would not
Listing 6. The game loops tick method
tick: => @drawGrid() @evolveCellGeneration() setTimeout @tick, @tickLength
Drawing the grid is easy. The
drawGrid method in
Listing 7 uses two nested
loops to visit every cell on the grid and then passes the cell to the
drawCell method. The
drawCell method calculates the x and y position
on the grid using the
cellSize as well as the
row and column attributes of the cell. Depending on the
isAlive attribute, you set the fill style of
the cell. Set the
fillStyle attributes of the canvas before using
the canvas methods
fillRect to draw the cell.
Listing 7. Drawing the grid
drawGrid: -> for row in [0...@numberOfRows] for column in [0...@numberOfColumns] @drawCell @currentCellGeneration[row][column] drawCell: (cell) -> x = cell.column * @cellSize y = cell.row * @cellSize if cell.isAlive fillStyle = 'rgb(242, 198, 65)' else fillStyle = 'rgb(38, 38, 38)' @drawingContext.strokeStyle = 'rgba(242, 198, 65, 0.1)' @drawingContext.strokeRect x, y, @cellSize, @cellSize @drawingContext.fillStyle = fillStyle @drawingContext.fillRect x, y, @cellSize, @cellSize
Evolving the current cell generation consists of three methods. The
evolveCellGeneration method is shown in Listing 8. Similar to the
seed method, use two nested loops to create a
two-dimensional array called
which will store the evolved cell generation. The inner loop passes the
cell to the
evolveCell method, which will
return the evolved cell. The evolved cell then gets stored at the right
position in the
newCellGeneration array. After
you've evolved every single cell of the current cell generation, you can
Listing 8. Evolve the current cell generation
evolveCellGeneration: -> newCellGeneration =  for row in [0...@numberOfRows] newCellGeneration[row] =  for column in [0...@numberOfColumns] evolvedCell = @evolveCell @currentCellGeneration[row][column] newCellGeneration[row][column] = evolvedCell @currentCellGeneration = newCellGeneration
evolveCell method in Listing 9 starts by creating an
evolvedCell variable with the same attributes
as the passed cell. In order to decide if the cell dies, reproduces, or
stays alive, you have to know how many neighbor cells are alive. To get
this number, invoke the
method with the cell. This method counts and returns the number of living
After you have the number of living neighbors, you can use the rules of
Game of Life to update the
isAlive attribute of
the evolved cell. After updating that attribute, simply return the
Listing 9. Evolving a single cell
evolveCell: (cell) -> evolvedCell = row: cell.row column: cell.column isAlive: cell.isAlive numberOfAliveNeighbors = @countAliveNeighbors cell if cell.isAlive or numberOfAliveNeighbors is 3 evolvedCell.isAlive = 1 < numberOfAliveNeighbors < 4 evolvedCell
countAliveNeighbors method in Listing 10 takes a single cell
as an argument and returns the number of living cells. Typically, a cell
on the grid has eight neighbors. However, if the cell is located at the
edges of the grid, the neighbor count is smaller. Counting the living
neighbors is a slightly complicated task.
For a nice, readable solution to this problem, you have to calculate the area in which you're searching for living neighbors. For a cell in the middle of the grid, it's easy to calculate the bounds within the search. A cell located at row 4 and column 5 has neighbors in rows 3, 4, 5 and columns 4, 5, 6.
It's a different case for a cell located at row 0 and column 0. The
neighbor cells range from row 0 to 1 and column 0 to 1. The lower bound
for the rows is the row number of the cell minus one, but the minimum is
zero. You can implement this using the
method, as in Listing 10.
The calculation for the lower bound for the columns is done the same
The upper bound is calculated with the
method. Make sure that the cell row plus one is not bigger than the last
row index. After you have the upper and lower bounds for the rows and
columns, you can loop over them in two nested loops. In this case, the
example uses the implicit range operator of CoffeeScript to ensure that
upperColumnBound values are also used.
You don't want to count the cell itself, so you have to place a continue
statement in the inner loop, which happens when the loops row and column
variables match the cell's attributes. After that, increment the
numberOfAliveNeighbors counter by one if the
currently visited cell is alive. In the end, you only have to return this
Listing 10. Count the living neighbors of a cell
countAliveNeighbors: (cell) -> lowerRowBound = Math.max cell.row - 1, 0 upperRowBound = Math.min cell.row + 1, @numberOfRows - 1 lowerColumnBound = Math.max cell.column - 1, 0 upperColumnBound = Math.min cell.column + 1, @numberOfColumns - 1 numberOfAliveNeighbors = 0 for row in [lowerRowBound..upperRowBound] for column in [lowerColumnBound..upperColumnBound] continue if row is cell.row and column is cell.column if @currentCellGeneration[row][column].isAlive numberOfAliveNeighbors++ numberOfAliveNeighbors
Since CoffeeScript wraps every file into its own closure, you need to
GameOfLife class so you can use it
outside of its own file. Add a
attribute to the window object, as follows:
window.GameOfLife = GameOfLife.
And that's it! You're finished with the example Conway's Game of Life implementation. If you open the index.html file in your browser, you should see your own version of Game of Life, as illustrated in Figure 1. If something went wrong, you can compare your version with the author's complete source code (see Resources).
Though Conway's Game of Life is a small game with simple rules, there are some tricky problems you have to solve. It's a great example to use to learn a new programming language or improve your skills.
|Article example source code||game-of-life.zip||4KB|
- The Little Book on CoffeeScript (O'Reilly): Read about getting started with CoffeeScript.
- Conway's Game of Life: Read about the history of the game and different patterns on Wikipedia.
- Author's version of Conway's Game of Life.
- HTML5 Canvas cheat sheet: Handy information for this element.
- "Create great graphics with the HTML5 canvas" (developerWorks, February 2011): Explore how to enhance your web pages with Canvas, a simple HTML5 element that packs a punch.
- "HTML5 fundamentals, Part 4: The final touch: The Canvas" (developerWorks, July 2011): Read this introductory article about the HTML5 Canvas element, which includes several examples to demonstrate functions.
- WHATWG: Learn about this community of developers working with the W3C to fine-tune HTML5.
- Canvas tutorial: Use the canvas element in this tutorial from the Mozilla developers.
- HTML5 Canvas reference: Explore several useful exercises to help hone your canvas knowledge.
- developerWorks Web development zone: Find articles covering various web-based solutions. See the Web development technical library for a wide range of technical articles and tips, tutorials, standards, and IBM Redbooks.
- developerWorks technical events and webcasts: Stay current with technology in these sessions.
- developerWorks Live! briefings: Get up to speed quickly on IBM products and tools as well as IT industry trends.
- developerWorks on-demand demos: Watch demos ranging from product installation and setup for beginners, to advanced functionality for experienced developers.
- developerWorks on Twitter: Join today to follow developerWorks tweets.
Get products and technologies
- Author's implementation of Conway's Game of Life: Get the example used in this article.
- Node.js: Download and install this prerequisite for CoffeeScript.
- Node Package Manager (NPM): Use this with Node.js to install CoffeeScript.
- IBM product evaluation versions: Download or explore the online trials in the IBM SOA Sandbox and get your hands on application development tools and middleware products from DB2, Lotus, Rational, Tivoli, and WebSphere.
- developerWorks community: Connect with other developerWorks users while exploring the developer-driven blogs, forums, groups, and wikis.
- Find other developerWorks members interested in web development.