JavaScript integration for chess Mérida font displays
In my previous post here, I discussed using the Chess Mérida font and the CSS required to display chess diagrams. The JavaScript implementation builds on this approach by allowing chess positions to be specified with standard Forsyth-Edwards Notation (FEN).
The board reader provides a common interface for displaying chessboards, while the selected renderer determines how the position is represented. This allows the same HTML data attributes to be used with different board renderers, including the Chess Mérida renderer.
Displaying a board from FEN
The basic idea is to provide a FEN string through the data-fen attribute and select the desired renderer with data-renderer.
For example:
<div
class="chessboard-fen-js"
data-renderer="merida"
data-theme="primary"
data-coordinates="true"
data-fen="rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
></div>
The common board reader reads the data-* attributes and creates the requested board. In this example, data-renderer="merida" selects the Chess Mérida renderer, while data-theme, data-coordinates, and data-fen configure the board.
The complete list of available data-* attributes and their specifications is documented here.
How the renderer works
The common reader is responsible for interpreting the board configuration and selecting the renderer. The renderer provides the operations needed to create and update the board.
For the Mérida renderer, the process is approximately:
- Read the FEN position supplied through
data-fen. - Extract the piece-placement section of the FEN.
- Split the position into eight ranks.
- Expand each rank into eight individual squares.
- Map each FEN piece character to its corresponding Mérida glyph.
- Determine the appropriate glyph case from the color of each square.
- Generate the Mérida characters for empty squares.
- Add the appropriate borders and optional coordinate characters.
- Insert the resulting character stream into the Mérida board element.
The renderer works directly with the Mérida character stream. The individual characters are not wrapped in separate elements because the font itself provides the visual appearance of the board.
FEN to Mérida glyph mapping
The renderer uses a mapping between FEN piece characters and the base Mérida glyphs:
const MERIDA_FEN_TO_BASE = {
P: 'p',
N: 'n',
B: 'b',
R: 'r',
Q: 'q',
K: 'k',
p: 'o',
n: 'm',
b: 'v',
r: 't',
q: 'w',
k: 'l'
};
The required case of the glyph is determined from the square position. This is necessary because the Mérida font uses different glyph variants to represent pieces on the two square colors.
function meridaRequiresUppercase(rankIndex, fileIndex) {
const boardRank = 7 - rankIndex;
return ((boardRank + fileIndex) % 2) === 0;
}
The corresponding piece glyph is then generated from the FEN character and its board position:
function meridaGetPieceChar(fenChar, rankIndex, fileIndex) {
const base = MERIDA_FEN_TO_BASE[fenChar];
if (!base) {
return '?';
}
return meridaRequiresUppercase(rankIndex, fileIndex)
? base.toUpperCase()
: base.toLowerCase();
}
Empty squares are generated in the same way, using the two Mérida characters required for the alternating square colors:
function meridaGetEmptySquareChar(rankIndex, fileIndex) {
const boardRank = 7 - rankIndex;
return ((boardRank + fileIndex) % 2) === 0
? '+'
: '*';
}
Board coordinates
The Mérida renderer uses the coordinate glyphs supplied by the font.
const MERIDA_RANK_CHARS = [
'«', '∆', '≈', 'ƒ', '√', '¬', '¡', '¿'
];
const MERIDA_FILE_CHARS = [
'»', '…', 'Ú', 'À', 'Ã', 'Õ', 'Œ', 'œ'
];
const MERIDA_COORD_LEFT = {
white: MERIDA_RANK_CHARS,
black: [...MERIDA_RANK_CHARS].reverse()
};
const MERIDA_COORD_BOTTOM = {
white: `7${MERIDA_FILE_CHARS.join('')}9`,
black: `7${[...MERIDA_FILE_CHARS].reverse().join('')}9`
};
When data-coordinates="true" is specified, the appropriate coordinate characters are added to the board. When the orientation is changed to Black, the coordinate order is reversed automatically.
Orientation
The board orientation is controlled through the common data-orientation attribute:
data-orientation="black"
White is the default orientation.
The renderer changes the order in which ranks and files are written to the character stream. The underlying FEN position remains unchanged.
For example:
<div
class="chessboard-fen-js"
data-renderer="merida"
data-orientation="black"
data-coordinates="true"
data-fen="rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
></div>
Themes
Themes are handled by the common chessboard CSS rather than by renderer-specific classes in the HTML.
For example:
<div
class="chessboard-fen-js"
data-renderer="merida"
data-theme="primary"
data-fen="rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
></div>
For the Mérida renderer, the theme changes the foreground color of the character stream. The font itself provides the visual appearance of the alternating squares.
Rendering the position
The Mérida renderer generates the complete character stream from the FEN position. The resulting content contains the board borders, eight ranks, and the bottom border.
A simplified version of the rendering process is:
function renderMeridaPosition(board, fen) {
const placement = fen.split(' ')[0];
const ranksFen = placement.split('/');
// Expand the FEN ranks and generate the Mérida character stream.
// Add the top border.
// Add each board rank.
// Add the bottom border and optional coordinates.
board.element.innerHTML = html.join('');
board.fen = fen;
}
The renderer therefore does not require manually constructed rank elements in the source HTML. The page only needs the board placeholder and its data-* configuration.
Loading a series of PGN games
The same reader can also load a series of PGN games from a JSON file using the data-pgn-json attribute.
<div
class="chessboard-pgns-js"
data-renderer="merida"
data-theme="primary"
data-orientation="white"
data-coordinates="false"
data-pgn-json="/files/chess/pgn/examples/automatic-load-multiple.min.json"
></div>
The reader creates the board and provides navigation between the games contained in the PGN collection.
Here are some examples:
For more insights into this topic, you can find the details here.