Stitching Together a Chess Game Load and View Program (with Annotations) in Javascript

By Matt Lavin

February 08, 2019

If you're a chess enthusiast like me, you've probably noticed no shortage of web-based ways to view the moves of previous games. In the Age of Glut, the bigger problem is how to work your way through a mind bogglingly large archive of historic games. And if you're keen to improve, you may have heard that studying significant games of the past--really working through each move as if you must decide what's next--can be an effective way to grow, especially if you are able to notice patterns and themes. (See How To Study Master Chess Games.)

Chessgames.com and chess.com are my favorite destinations for historic games and playthrough lessons (Nothing against https://database.chessbase.com). Chess.com also provides a WYSIWYG editor for your user blog where you can load a chess game from a variety of formats, choose board and piece styles, and add your own annotations. For a long while now, I've been curious if I could code something similar in javascript/jquery to do the same thing on any static website (my blog, Github Pages, etc.). Implementing my own solution seemed like an especially interesting way to learn more about how chess is made machine readable, and how online chess games are coded.

At first I thought the task would be trivial. To mimic the playthrough controls for How To Study Master Chess Games, I imagined, you'd just have to load the right library and write something like this:

var annotated = Chessgame(source='game-data', target='#div-1'); 

I soon found out that there isn't just one annotated game play-through library in javascript. There's a chess library for "chess move generation/validation, piece placement/movement, and check/checkmate/stalemate detection" (chess.js) and a chessboard generator library (chessboard.js), but neither implements annotations or play-through controls. Sam Koblenski's blog post on the subject, A Barely Adequate Guide to Displaying Chess Games with JavaScript, covers most of this in much greater detail. He doesn't get into representing commentary, but I'll return to that in a moment.

Before saying more about representing chess in javascript, I want to describe two of the moast common machien readable chess game notation formats: FEN and PGN. FEN (Forsyth-Edwards Notation) provides all the information needed to describe a particular position, including the positions of all the pieces, whose turn comes next, each player's current access to castling (which gets spoiled, for example, if you've moved your king), the number of times either player has moved since the last capture or pawn advance, and the current move number. In theory, with all this information, you could start from any position and play out the rest of the game that led to that position. FEN doesn't contain a move history, or annotations. Here's an example of an FEN string:

r2q1rk1/p4pbp/2p1n1p1/1p2p1P1/4P2P/2PP1Q2/PPBN4/2K3RR b - - 2 18

There's a logic to all of this, but the important thing to note is that the notation formula isn't especially reader friendly.

PGN (Portable Game Notation), meanwhile, is a flat text format that represents the moves of game using algebraic notation, as well as metadat about the game such as the names of the players, who won, and the game's location. PGN can also support move-by-move annotations. For example, the following PGN text represents Jose Capablanca vs. T.A. Carter, Dec. 8, 1909. The game lasted 30 moves and ended with Capablanca (white) checkmating his opponent:

[Event "Simul, 23b"]
[Site "St. Louis, MO USA"]
[Date "1909.12.08"]
[EventDate "1909.??.??"]
[Round "?"]
[Result "1-0"]
[White "Jose Raul Capablanca"]
[Black "T A Carter"]
[ECO "C30"]
[WhiteElo "?"]
[BlackElo "?"]
[PlyCount "59"]

1. e4 e5 2. f4 Bc5 3. Nf3 d6 4. c3 Bg4 5. fxe5 Bxf3 6. Qxf3
dxe5 7. Bc4 Nf6 8. d3 O-O 9. Bg5 Nbd7 10. Nd2 c6 11. O-O-O b5
12. Bb3 Be7 13. h4 Nc5 14. Bc2 Ne6 15. g4 g6 16. Bxf6 Bxf6
17. g5 Bg7 18. Rdg1 Nf4 19. Nf1 Kh8 20. Ne3 Qc7 21. h5 gxh5
22. Rxh5 Nxh5 23. Qxh5 Rae8 24. Nf5 Re6 25. Rh1 h6 26. gxh6
Bf6 27. Qg4 Rg8 28. Qg7+ Rxg7 { if bxg7, 29. hxg7# } 29. hxg7+ Kg8 30. Rh8# 1-0

The PGN also tells us that this was a simultaneous exposition in St Louis, so we can infer that Carter probably wasn't a very strong player. I downloaded the PGN from chessgames.com (this was the "Game of the Day" on February 6) and added one annotation after move 28 to note that, if Carter had taken the Queen with his bishop instead of his rook, mate would have come one move sooner, since pawn takes bishop leads to an immediate mate.

PGN is the accepted standard for digitally archiving chess games. It's easy to read, low memory, and cross-platform compatible. Software like Fritz, Rybka, and Chessmaster can all load a PGN file, and sites like chessgames.com and chess.com provide various means for users to generate, download, or upload this format. I want an interface that begins with a PGN file and displays a chess board that can be played through like chess.com or chessgames.com, with annotations preserved and displayed only after the relevant move has been played. As I mentioned, Sam Koblenski describes the initial steps I followed: (1) integrating chess.js and generating a chessboard with chessboard.js). Koblenski's post goes so far as to program buttons for the first move, the last move, the next move, and the previous move, and his version displays the full PGN below the game board.

Here's a live example of the code from Koblenski's post (with locally hosted versions of all the dependencies).

Koblenski converts a PGN string to a javascript object using chess.js, but this process removes the annotations from the source data rather than attempting to parse them. At least one chess.js enthusiast has coded a solution if you want to keep your annotations, but it hasn't been merged back to the the official chess.js source. MakG10's edit creates a new method

Chess.comments()

that works largely the same as the

Chess.history()

method Koblenski uses in his post. Using a tweaked version of the same pattern that Koblenski uses to display the previous or next move, I wrote some code to fetch and display the correct annotation at each click of "Next" and "Previous."

 

// 3. If Next button clicked, move forward one
  $('#nextBtn').on('click', function() {
    game.move(history[i]);
    board.position(game.fen());
    i += 1;
    
    // this code targets the container with an id of 'annotation-window'
    // it empties the annotation window on every click of the 'next' button and fills in the correct annotation if there is one
    // comments[i-1] is because we always want the first annotation to come after the first move. Using i-1 after setting i will allow me to use the same code for next, previous, first, and last buttons 
    
    $('#annotation-window').html("");
    if (comments[i-1] !== null) { $('#annotation-window').html(comments[i-1]);}
    
    if (i > history.length) {
      i = history.length;
    }
  });

Here's a link to view this code snippet in action.

It works! Now we can take a moment and think about style. I use the Bootstrap library on my site, so I want to design the board with that framework in mind. In particular, I want to make the board's play buttons, borders, and annotation area more fetching.

Take a look at my basic bootstrap integration.

Finally, a quick look at a chessboard.js example suggests that some of the board's display comes from an outside CSS file. Overwriting some of those rules in a local stylesheet will allow me to edit the board color, the highlight color, the grid letter and number sizes, and more.

Here's the semi-final product, minus real annotations. In the future, I may add a few more bells and whistles, such as highlighting in the move list for the current move, or the ability to click on any move to jump ahead. For now, though, I'm feeling good about my ability to publish annotated games to a static website.