npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

chess2img

v0.4.1

Published

Modern chess board image rendering for Node.js

Readme

chess2img

Overview

chess2img renders chessboard PNG, SVG, and JPEG images from FEN, PGN, or board-array inputs with a small Promise-based API for JavaScript and TypeScript users on Node.js.

Supported output formats:

  • PNG
  • SVG
  • JPEG

Features

  • Render PNG, SVG, or JPEG chessboard images from fen, pgn, or board input.
  • Use five bundled built-in themes: merida, alpha, cburnett, cheq, and leipzig.
  • Highlight squares such as the last move or key tactical ideas.
  • Customize board colors, size, padding, border sizing, coordinates, and board orientation.
  • Use either the functional renderChess(...) API or the ChessImageGenerator class API.
  • Register custom themes globally or pass a theme inline for one-off rendering.
  • Consume the package from both JavaScript and TypeScript projects.

Example Output

Opening 1.e4 with a Chess.com-like board palette, highlighted origin and destination squares, and the built-in cburnett piece set.

Quick Start

npm install chess2img
import { writeFile } from "node:fs/promises";
import { renderChess } from "chess2img";

const png = await renderChess({
  fen: "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1",
  size: 480,
  style: "cburnett",
  colors: {
    lightSquare: "#EEEED2",
    darkSquare: "#769656",
    highlight: "rgba(246, 246, 105, 0.6)",
  },
  highlights: ["e2", "e4"],
});

await writeFile("board.png", png);

renderChess(...) returns a Promise<Buffer> containing PNG data.

SVG Output

import { writeFile } from "node:fs/promises";
import { renderSvg } from "chess2img";

const svg = await renderSvg({
  fen: "4k3/8/8/8/8/8/8/4K3 w - - 0 1",
  size: 480,
  style: "merida",
});

await writeFile("board.svg", svg, "utf8");

JPEG Output

import { writeFile } from "node:fs/promises";
import { renderJpeg } from "chess2img";

const jpeg = await renderJpeg({
  fen: "4k3/8/8/8/8/8/8/4K3 w - - 0 1",
  size: 480,
  style: "merida",
});

await writeFile("board.jpg", jpeg);

Installation

npm install chess2img

Node And Canvas Requirements

  • Node.js 18+
  • native build support for canvas
  • common Linux packages usually include Cairo, Pango, libpng, libjpeg, giflib, a C/C++ toolchain, and Python for node-gyp

If canvas fails to install, verify your system packages first. The library ships canvas as a direct dependency, but native prerequisites still need to exist on the host.

Basic Usage

Functional API

import { renderFile } from "chess2img";

await renderFile("board.png", {
  fen: "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1",
  size: 480,
  style: "merida",
});

renderChess(...) and renderFile(...) are the explicit PNG APIs.

Direct PNG, SVG, And JPEG Buffers

import { renderChess, renderJpeg, renderSvg } from "chess2img";

const png = await renderChess({
  fen: "4k3/8/8/8/8/8/8/4K3 w - - 0 1",
  size: 480,
  style: "merida",
});

const svg = await renderSvg({
  fen: "4k3/8/8/8/8/8/8/4K3 w - - 0 1",
  size: 480,
  style: "merida",
});

const jpeg = await renderJpeg({
  fen: "4k3/8/8/8/8/8/8/4K3 w - - 0 1",
  size: 480,
  style: "merida",
});

SVG And JPEG File Helpers

import { renderFile, renderJpegFile, renderSvgFile } from "chess2img";

await renderFile("board.png", {
  fen: "4k3/8/8/8/8/8/8/4K3 w - - 0 1",
  size: 480,
  style: "merida",
});

await renderSvgFile("board.svg", {
  fen: "4k3/8/8/8/8/8/8/4K3 w - - 0 1",
  size: 480,
  style: "merida",
});

await renderJpegFile("board.jpg", {
  fen: "4k3/8/8/8/8/8/8/4K3 w - - 0 1",
  size: 480,
  style: "merida",
});

Automatic Coordinates

import { writeFile } from "node:fs/promises";
import { renderChess } from "chess2img";

const png = await renderChess({
  fen: "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1",
  size: 480,
  style: "cburnett",
  coordinates: true,
});

await writeFile("board-with-auto-coordinates.png", png);

coordinates: true chooses border mode when borderSize > 0, otherwise it falls back to inside mode.

Border Coordinates

import { writeFile } from "node:fs/promises";
import { renderChess } from "chess2img";

const png = await renderChess({
  fen: "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1",
  size: 480,
  style: "cburnett",
  borderSize: 24,
  coordinates: {
    enabled: true,
    position: "border",
    color: "#333",
  },
});

await writeFile("board-with-border-coordinates.png", png);

Inside Coordinates

import { writeFile } from "node:fs/promises";
import { renderChess } from "chess2img";

const png = await renderChess({
  fen: "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1",
  size: 480,
  style: "cburnett",
  coordinates: "inside",
});

await writeFile("board-with-inside-coordinates.png", png);

Circle Highlights

import { writeFile } from "node:fs/promises";
import { renderChess } from "chess2img";

const png = await renderChess({
  fen: "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1",
  size: 480,
  style: "cburnett",
  highlights: [{ square: "e4", style: "circle" }],
});

await writeFile("board-with-circle-highlight.png", png);

Combined Fill And Circle Highlights

import { writeFile } from "node:fs/promises";
import { renderChess } from "chess2img";

const png = await renderChess({
  fen: "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1",
  size: 480,
  style: "cburnett",
  highlights: ["e4", { square: "e4", style: "circle" }],
});

await writeFile("board-with-fill-and-circle-highlights.png", png);

Class API

import { ChessImageGenerator } from "chess2img";

const generator = new ChessImageGenerator({
  size: 800,
  style: "alpha",
});

await generator.loadPGN("1. e4 e5 2. Nf3 Nc6 3. Bb5 a6");
generator.setHighlights(["e4", "e5"]);

const png = await generator.toBuffer();
const svg = await generator.toSvg();
const jpeg = await generator.toJpeg();

await generator.toFile("board.png");
await generator.toSvgFile("board.svg");
await generator.toJpegFile("board.jpg");

Class method summary:

  • toBuffer() -> PNG Buffer
  • toFile(path) -> writes PNG
  • toSvg() -> SVG string
  • toSvgFile(path) -> writes SVG
  • toJpeg() -> JPEG Buffer
  • toJpegFile(path) -> writes JPEG

JavaScript Usage

const { renderChess } = require("chess2img");
const { writeFile } = require("node:fs/promises");

async function main() {
  const png = await renderChess({
    fen: "4k3/8/8/8/8/8/8/4K3 w - - 0 1",
    style: "merida",
  });

  await writeFile("board.png", png);
}

main().catch(console.error);

Built-In Themes

Bundled built-in themes:

  • merida
  • alpha
  • cburnett
  • cheq
  • leipzig

Built-in themes are vendored in-package and render through the same theme pipeline as custom themes.

Custom Piece Packs

Register a reusable theme globally:

import { registerTheme } from "chess2img";

registerTheme({
  name: "custom-theme",
  displayName: "Custom Theme",
  license: "MIT",
  attribution: "Theme author or package source",
  pieces: {
    // 12 canonical pieces required
  },
});

Or pass either:

  • a registered custom theme name through theme: "custom-theme"
  • an inline ThemeDefinition object through theme: { ... }

Custom piece packs may use either:

  • svg assets
  • png assets

Expected piece-pack structure:

my-pack/
  wK.svg
  wQ.svg
  wR.svg
  wB.svg
  wN.svg
  wP.svg
  bK.svg
  bQ.svg
  bR.svg
  bB.svg
  bN.svg
  bP.svg

You can point the theme definition at either SVG or PNG files. Mixing formats is also supported as long as all 12 canonical pieces are present.

Example Third-Party Packs

  • chess.com-boards-and-pieces: https://github.com/GiorgioMegrelli/chess.com-boards-and-pieces

These are third-party repositories. They are not bundled with chess2img, and you should verify each pack's license terms before redistribution or repackaging.

API

Public Exports

  • new ChessImageGenerator(options?)
  • renderChess(options)
  • renderSvg(options)
  • renderJpeg(options)
  • renderFile(path, options)
  • renderSvgFile(path, options)
  • renderJpegFile(path, options)
  • registerTheme(theme)

Functional API

renderChess(...), renderSvg(...), and renderJpeg(...) accept exactly one position source:

  • fen
  • pgn
  • board

Return values:

  • renderChess(...) -> Promise<Buffer> containing PNG data
  • renderSvg(...) -> Promise<string> containing SVG markup
  • renderJpeg(...) -> Promise<Buffer> containing JPEG data

File helpers:

  • renderFile(path, options) -> writes PNG only
  • renderSvgFile(path, options) -> writes SVG only
  • renderJpegFile(path, options) -> writes JPEG only

Class API

Methods:

  • loadFEN(fen: string): Promise<void>
  • loadPGN(pgn: string): Promise<void>
  • loadBoard(board: BoardArray): Promise<void>
  • setHighlights(highlights: HighlightInput[]): void
  • clearHighlights(): void
  • toBuffer(): Promise<Buffer>
  • toFile(filePath: string): Promise<void>
  • toSvg(): Promise<string>
  • toSvgFile(filePath: string): Promise<void>
  • toJpeg(): Promise<Buffer>
  • toJpegFile(filePath: string): Promise<void>

Semantics:

  • setHighlights replaces the current highlight set
  • clearHighlights removes all highlights
  • loading a new position clears highlights
  • constructor defaults persist across position loads

Options

  • size: board size in pixels
  • padding: [top, right, bottom, left]
  • borderSize: inner border size in pixels, from 0 up to Math.floor(size / 8)
  • flipped: render from black's perspective when true
  • style: built-in theme alias
  • theme: built-in theme name, registered custom theme name, or inline ThemeDefinition
  • highlights: array of square strings or highlight objects such as ["e4", { square: "d5", style: "circle" }]
  • highlightSquares: compatibility alias for highlights
  • coordinates: boolean, "border", "inside", or { enabled?: boolean; position?: "border" | "inside"; color?: string }
  • colors.lightSquare
  • colors.darkSquare
  • colors.highlight

coordinates: false or omitting the option disables labels. coordinates: true enables labels and chooses border mode when borderSize > 0, otherwise inside mode. Explicit coordinates: "inside" is always valid. Explicit coordinates: "border" requires borderSize > 0 and throws ValidationError otherwise.

highlights is the preferred API. Each entry may be a square string for a filled highlight, or an object with square, style, color, opacity, lineWidth, and radius. highlightSquares remains available for backward compatibility, but should not be used together with highlights in the same call.

Inside coordinates use automatic light/dark contrast by default, similar to chess.com. If coordinates.color is provided, that exact color is used instead. Border coordinates keep a single-color label style with #333 as the default.

At very small valid sizes, the renderer suppresses coordinates when they cannot fit legibly in the available border band or edge-square area.

Errors

The library exports:

  • ValidationError
  • ParseError
  • ThemeError
  • RenderError
  • IOError

License

chess2img is distributed under the MIT license in package metadata.

Bundled built-in themes are derived from the upstream andyruwruw/chess-image-generator resource packs and are vendored in-package for deterministic installs. Provenance and licensing notes live in ATTRIBUTION.md.