lichess.org
Donate

How to Lichess Developer

Software DevelopmentLichess
Learn to Internet with Lichess PGN Viewer

Right now if you want to embed a high quality PGN viewer for chess games into a website, you have at least two options. One is to use lichess.org’s free open source pgn-viewer library. The other is to ask ChatGPT nicely. But since OpenAI wants your phone number to sign up, let's do the former.

In this blog, we'll learn a bit of web programming and use PGN Viewer to make a simple web page with a field to paste pgn text and a button to render it as a game. You should recognize the chessboard that results - it’s the same code lichess uses to auto-link game URLs in blogs and forums. Behold:

https://lichess.org/x5e0B4dE#77

Basic familiarity with HTML, CSS, Javascript, and shell commands will be helpful but if you don’t mind a bit of internet searching, you can pick some of that up as we go. This tutorial links to many good documentation sources so obey the first rule of internet we all learned as children - if it's blue and you can click on it, then you definitely should. (When everything stops working, daddy will fix Minecraft).

Tools Needed

You'll need to use a terminal application to complete this tutorial. I'll refer to it as the shell from this point forward. If you're unfamiliar, Windows users can watch this video. Mac users can watch this one. Linux peeps, you already know this stuff.

Note - having watched a video on terminals, you may now put Systems Administrator on your resume. This makes you an IT professional. Way to go! Let's go ahead and launch the shell.

First you'll want npm (short for Node Package Manager) - a command line tool for installing, managing, and building Javascript packages. While it is not actually needed to use lichess pgn-viewer, all web programmers should know npm. It's installed alongside Node.js - a program that runs Javascript code just like Chrome and Firefox but provides a different set of functions than your browser. If you already have npm you can skip to the next section.

There are many ways to install Node.js & npm. I use nvm and nvm-windows, but feel free to just choose a method from the install Node.js & npm link and take the plunge.

Once you've installed Node, verify that everything works. Your shell's $PATH variable (that's %PATH% on Windows) should include the npm executable location so that when you enter npm --version, you get a version number rather than an error message like command not found. Most installation methods will adjust your PATH correctly but you'll need to restart your shell to pick it up. If you need to troubleshoot this, the two PATH links above will help.

[Side note] If you're comfortable with the git version control software developers use to manage changes, you can also git clone https://github.com/lichess-org/pgn-viewer and build things from source. You'll need to learn git eventually to collaborate on projects with others, but I will not mention it again. I am no madman.

Project Setup

Use the shell to create a new folder for our little project. The name should not contain spaces but otherwise doesn't matter. From here on out, when I say create a file called 'filename', you should do so directly inside of this new folder. Otherwise, things probably won't work.

Next, we'll cd into it and run npm init. Here's an example:

mkdir your-project
cd your-project
npm init

Npm will ask you a bunch of questions. Just hit enter to use the default for each. It then previews the contents for a 'package.json' file that it creates in your project folder. Npm will modify this file in the future whenever it is used to install or remove packages.

Now let's enter:

npm install lichess-pgn-viewer

Afterwards when you look at 'package.json' , you'll see a new entry for lichess-pgn-viewer. You may also notice the 'node_modules' folder that npm created to store the library code (and many other packages). Where does npm get the files from? An internet database called the npm public registry contains hundreds of thousands of packages by authors from all over the world. This is the technology behind the boner pill ads.

HTML 101

Let's get acquainted with HTML syntax.

<element attributeName="value"> inner text </element>

In the example above, element is an HTML tag name, <element ...> is the begin tag, and </element> is the end tag (note the forward slash in the end tag). The tag name specifies the element's type. The content is defined by everything between the begin tag and the end tag including any attributes, inner text, or child elements. HTML tags mostly correspond to items you can actually see or regions that contain them on a webpage. Some common ones are <div>, <p>, <a>, and <button>.

Back to the example, attributeName="value" is an attribute name/value pair. An element may contain any number of these and they provide more information about the element to the browser. Some common attributes include id, class, type, href, and action.

Now you know. Time to add Full Stack Web Developer to your resume (congrats!)

CSS Copy Pasta

Let's define some basic layout and visual elements for our project with CSS. Feel free to learn all about it. When you're ready, use a plaintext editor to create a file called 'viewer.css' and paste the following into it:

body {
  background: #f0f0f0;
  font-family: 'Noto Sans';
  --board-color: #bbbbbb;
}
.page {
  display: flex;
  flex-direction: column;
  align-items: center;
}
.pgn {
  margin: 1em;
}
.render {
  padding: .5em 2em;
  background-color: white;
}
.container {
  margin: 1em;
  max-width: 720px;
}

In a css source file, text preceding an open curly brace is called a selector. It identifies (selects) webpage elements that the style properties { everything inside these } are applied to. There are multiple selector flavors, but we select our body element by type and all others by class to keep things simple. The class selectors in 'viewer.css' have a period followed by a name and will match any HTML elements with a class attribute containing that name. Note - CSS classes are just a tagging mechanism to apply style rules and are unrelated to javascript classes.

HTML Copy Pasta

Next we’ll write some HTML to describe the structure of our webpage. Create a plaintext file called 'viewer.html' with:

<!doctype html>
<html>
  <head>
    <link href="viewer.css" rel="stylesheet">
    <link href="node_modules/lichess-pgn-viewer/demo/lichess-pgn-viewer.css" rel="stylesheet">

    <title>Lichess PGN Viewer</title>
  </head>
  <body>
    <div class="page">
      <textarea class="pgn" cols="86" rows="10"></textarea>
      <button class="render">Render</button>
      <div class="container">
        <div class="lpv"></div>
      </div>
    </div>
    <script src="node_modules/lichess-pgn-viewer/demo/lichess-pgn-viewer.js"></script>
    <script src="index.js"></script>
  </body>
</html>

You can see that the head element contains a link to our 'viewer.css' as well as 'node_modules/.../lichess-pgn-viewer.css' which defines styles used by the pgn viewer package. Below that in the body, you can see the visual elements that we match by class in the CSS including the textarea where you will paste pgn, the button you will click to render it, and a placeholder div with the lpv class where the pgn-viewer library will place the game board after the button is clicked and the game board's DOM elements are built.

[Fun facts] The DOM is a browser's internal view of the document described by HTML. There's usually a direct mapping between elements in either domain so the terms HTML and DOM are sometimes interchanged. In a traditional web application, you build the DOM with HTML and you modify it from Javascript using the DOM web APIs provided by the browser (or using libraries that do this for you).

Javascript Copy Pasta

Speaking of Javascript, let's use our plaintext editor to create 'index.js' with:

const pgnTextEl = document.querySelector('.pgn');

document.querySelector('.render').addEventListener('click', render);

function render() {
  LichessPgnViewer(
    document.querySelector('.lpv'), {
      pgn: pgnTextEl.value,
      // optional parameters go here
    }
  );
}

This code contains two top level statements that run when the browser loads the page as well as a render function that is called later. The second statement attaches our render function to the Render button as a click handler. Inside render, we call LichessPgnViewer (defined somewhere in 'node_modules/.../lichess-pgn-viewer.js') with a placeholder element and a configuration object with a pgn property as parameters.

Note that we called document.querySelector('.lpv') directly as the first parameter to LichessPgnViewer. Breaking this down - we used the global document object's querySelector method to obtain the element matching the .lpv selector. This return value is passed to LichessPgnViewer as our placeholder element, followed by the configuration object which is defined by the properties (currently just pgn) within the innermost curlies above. These two parameters to LichessPgnViewer are delineated by that comma after the call to querySelector.

Your browser generates a new 'click' event and calls registered handlers each time that Render button is clicked. If the pgn property of the object parameter is a valid pgn string, the viewer library will construct all the viewer elements and swap them into the DOM for the placeholder element we passed. If it's not valid pgn, you'll see some errors in your browser console. Hey, you are the one who provided the pgn!

At this point, you should be able to locate and double click 'viewer.html' in your file explorer to open it with your browser. Paste some pgn text, click render, and everything should work. If it does not, hold on a sec while I slip out the back. Just kidding - try searching online for any errors you get and if you're still stumped in two years time, there's always the #general-programming-chat channel on Lichess Discord.

Once you've got it working, there are some additional properties besides pgn that can go in that object parameter. Try inserting one or more of these into your 'index.js' alongside or replacing the // comment:

    orientation: 'black',
    showPlayers: false,
    showMoves: false,
    showClocks: false,
    showControls: false,
    initialPly: 42,
    scrollToMove: false,
    drawArrows: true,

OK, I Lied

Making web apps is not quite this easy. We did some bad things up there for expediency. One is that users typically access a webpage through an https url (the network) rather than a file url (your local filesystem). if you look closely at the browser tab running our viewer code, sure enough you'll notice that the address bar starts with file:///

It's easy enough to set up a local webserver. In fact you can do it in a few lines with npm. But to serve pages over the network, you'll want a signed certificate so visitors don't have to click through a series of APOCALYPSE YES/NO? dialogs when connecting to your site.

Another problem is that our Javascript doesn't use modules. In a production system, we would have something like import LichessPgnViewer from 'lichess-pgn-viewer'; at the top of our 'index.js', specify type="module" in lichess-pgn-viewer's script tag in 'viewer.html', and use a bundler such as webpack or esbuild to package all the dependencies together.

Come to think of it, pretty much everything else we did can get you in trouble too. I'm sorry I lied. Hopefully you got a sense of accomplishment and learned some things without having to dive head first into the thorny bits. Note - this does not mean you must remove Full Stack Web Developer from your resume! Rather, now you're aware of additional things you'll need to bullshit your way through for that six figure salary.

Further reading

One day you might get tired of monkeying with the single --board-color css variable and decide you want to customize the viewer further. I hear it's nice when websites have matching colors. As of PGN Viewer 1.5.4, you'll need to use sass variables to do this. There's some pointers in the GitHub readme at https://github.com/lichess-org/pgn-viewer.

There's a lot more you can configure by passing a chessground: {...} configuration property inside the object parameter to the pgn viewer create function. Have a look at some of the options here.

Lichess PGN Viewer is distributed under the GPL-3.0 license (or any later version, at your option). When you use it for your website, your combined work may be distributed only under the GPL. You must release your source code to the users of your website.

Please read more about GPL for JavaScript on greendrake.info.