In this quick tutorial, we’re going to build a real chess game that you can play with a friend in real-time.

We’re going to utilize chessboardjs which is a pretty simple library that can render chessboard along with the figures on it. In order to be able to know what moves are allowed and what are not, we will use another library called chess.js. As we will see, those two play very nice together.

For a backend, we’re going to utilize the fantastic Firebase database (a real-time data JSON database now owned by Google). We’ll scaffold the app using the simple React app and will use React Router to handle the routing.

The end result to play with can be found here. The source code is on GitHub.

Let’s get started.

Setting up

Let’s create a new project using create-react-app. This is a handy project from the React team that aims to simplify the process of setting up a React-ready project. It gives us a lot great tools and configuration settings by default. That’s just what we need.

create-react-app chess && cd $_

Now lean back in your chair and wait until it installs all the required packages. To start your newly created application run:

npm run start

OK, now we’re ready to rock.

Adding a router

Our app is going to have two components. The first one, called Home , is going to have just one button for creating a game. The second component called Game is an actual view of the chess game and that we want it to render when the address is “/:token”. Every player will have their own unique token and thus a unique url they can access to play the game.

As you probably guessed, we now need some kind of a router here.

Luckily for us, there is one in React world and a very solid one at that. Behold, the React Router. Let’s install it:

yarn add react-router-dom

Open “src/App.js” and replace its content with the following:

import React, { Component } from 'react'; import { HashRouter, Route } from 'react-router-dom'; import './App.css'; import Home from './Home'; import Game from './Game'; export default class App extends Component { render() { return ( ); } }

Router listens to url changes and renders the corresponding components. What component to render in each particular case is defined by Route component and its path attribute. If the url is matched to this path, then the component for this route will be rendered.

In our case, it will render Home component when the path is “/”. And if the path is something like “/12345” it will render the Game component.

Note, that we use HashRouter and this is just because it allows the app to be hosted on AWS S3. In general, it doesn’t really matter and you can replace it with more commonly used BrowserRouter .

Intro to Firebase Database

Firebase is a fantastic tool which can loosely be described as backend as a service.

Basically, it lets you store the data, and have multiple clients connected simultaneously and receive the updates in the real-time. So you can synchronize different clients. It can also work offline, has a very thoughtful authentication mechanism and lots of SDKs for every possible platform and a clear documentation.

Sign in to Firebase and create an application. Then you will get a snippet to paste into your html page that looks like this:

<script src="https://www.gstatic.com/firebasejs/3.7.0/firebase.js"></script> <script> // Initialize Firebase var config = { apiKey: "<your api key>", authDomain: "<your app name>.firebaseapp.com", databaseURL: "https://<your app name>.firebaseio.com", storageBucket: "<your app name>.appspot.com", messagingSenderId: "<messaging id>" }; firebase.initializeApp(config); </script>

That’s it. Now you can read and write to your real-time database. But before we went too far let’s set some restrictions.

Firebase stores your data as a tree. You can assign different access rules and validations to any node of it. By default, any authenticated user can write anywhere. Which doesn’t really sounds like a nice idea, given that our API key will be get exposed as part of our frontend application.

Also, we’re not going to use authentication, so we are going to change the rules a little bit.

Go to Firebase Console > Database > Rules.

Then add this set of rules:

{ "rules": { ".read": false, ".write": false, "games": { ".read": true, ".write": true } } }

What it’s saying is, disallow any read and write, except for the games node. This is where we are going to store our games.

Creating a new Game

Let’s create our Home component. The only purpose for it will be to create a new game and redirect to the corresponding url.

Our Home component is a function which returns some JSX:

export default function() { return ( Create a New Game Create a New Game

We use a function here instead of classes because we don’t really need to store any state.

When a user clicks the button we create a new game object:

const newGame = { p1_token: Utils.token(), p2_token: Utils.token() };

Note that we generate a unique token for two players (in chess, the player who moves first is referred to as “White”).

Then we say to Firebase that we want to create a new node (at this point it will generate a unique hash id for us):

const game = firebase.database().ref("games").push();

And now we save it to database:

game.set(newGame).then(() => { window.location = `/${newGame.p1_token}`; }, (err) => { throw err; });

The Firebase API is Promise-based. If you’re not familiar with promises it’s never too late to start learning.

If the game is successfully created, we redirect to the game view url by simply overwriting the location url.

Game component

OK, now this is the cornerstone of our game. Here we’re going to build a chessboard (with the help of our friend chessboardjs), we’re going to let user move the figures (but only according to the game rules — chess.js will help with that), and of cause we are going to store the moves into our Firebase database, so that both clients can have their data in sync and be able to play in real-time!

Isn’t it exciting!

Our component is a class because we’re going to need some state to manage. Let’s look at the JSX.

export default class Game extends React.Component { // ... render() { return ( Player 1: {domain()}/{this.state.p1_token} Player 2: {domain()}/{this.state.p2_token} { this.state.turnText } { this.state.statusText } { history(this.state.moves) }

The screen is split into two parts here. In the first, there’s a board ( <div id="game-board"></div> ), in the second there is some additional information, like the links for players, history of the moves, etc.

Let’s initialize our state in the constructor:

constructor({ match: { params: { token } } }) { super(); this.state = { token }; this.engine = new Chess(); }

We get the match variable from the router. It contains our token specified in url and I’m using that fancy ES6 destructuring feature to get it.

Now as you probably know there are two important callbacks in React world: componentWillMount and componentDidMount . We’re going to use the later because we need to do something when after the component is rendered.

listenForUpdates(this.state.token, (id, game) => { this._updateBoard(id, game); this._updateInfo(game); });

Since we don’t really know what token is that, does it belong to the first user or the second, we have to listen to both games.

function function listenForUpdates(token, cb) { const db = firebase.database().ref("/games"); ["p1_token", "p2_token"].forEach((name) => { // getting the ref to node const ref = db.orderByChild(name).equalTo(token); // if the value is changed, fire the callback ref.on('value', (ref) => { const [id, game] = parse(ref.val()); if (!id) return; cb(id, game); }); }); }

Note that we only calling the callback here when the game exists. Now let’s look at _updateBoard method.

_updateBoard(id, game) { const playerNum = figurePlayer(this.state.token, game); this.engine.load(game.fen || INITIAL_FEN); if (!this.board) { this.board = this._initBoard(id, game); this.board.position(this.engine.fen()); } else if (isMyTurn(playerNum, this.engine.turn())) { this.board.position(this.engine.fen()); } }

First we load so-called fen into our engine which we’re gonna use later to tell us which moves are allowed and which are not.

Next, if we didn’t create the board yet, we create it. Otherwise, we update the existing board by setting position .

And that’s how we create a new board:

_initBoard(id, game) { // ... const playerNum = figurePlayer(token, game); const config = { draggable: true, pieceTheme: "https://s3-us-west-2.amazonaws.com/chessimg/{piece}.png", onDragStart: onDragStart, onDrop: onDrop, onSnapEnd: onSnapEnd }; const board = ChessBoard('game-board', config); if (playerNum === 2) board.orientation('black'); return board; // ... }

We provide an id of the element, where we want to place it ( game-board ) and also several callbacks, the most important of which is onDrop because that’s when the user made his move and we need to update the database:

function onDrop(source, target) { // prepare move object const m = engine.move({ from: source, to: target, promotion: 'q' }); // if this move is no allowed, tell the board to revert if (m === null) return "snapback"; // otherwise update game... game.fen = engine.fen(); game.moves = pushMove(game.moves, `${m['from']}-${m['to']}`); // ... and save it to database games(id).set(game); }

Of cause there’s some more to it, feel free to make yourself familiar with the full code here.

Conclusion

So, here it is. We’ve walked through this simple tutorial, created a real-time chess game, and learned to use Firebase Database along the way.

This is of cause doesn’t have to be chess. You can program any kind of game with any number of players. And the nice part is that you can even make it cross-platform. Firebase has lots of SDKs for platforms like Android, iOS, and others.

Firebase is a great way to prototype real-time applications. There’s much more to it in terms of authentication, data validation, working offline, etc. And I hope to cover some of it in coming write-ups.