Does anyone here knows how to turn a PGN, let's say for example

1.e4 c5 2.Nf3 ( 2.c3 Nf6 ( 2...f6 3.Na3 ) 3.e5 Nd5 ) Nc6 3.d4 cxd4 4.Nxd4 ( 4.c3 ) g6

into an image? I preferably would like to put out a *.dot-file (Graphviz) that manually created looks like this in the example

digraph chess_openings {
        node [shape=box];
        n1 [label="e4"]; n1-> n2;
        n2 [label="c5", style=filled]; n2 -> n3; n2 -> n4;
        n3 [label="Nf3"]; n3 -> n8;
        n4 [label="c3"]; n4 -> n5;
        n5 [label="Nf6", style=filled]; n5 -> n6;
        n6 [label="e5"]; n6 -> n7;
        n7 [label="d5", style=filled];
        n8 [label="Nc6", style=filled]; n8 -> n9;
        n9 [label="d4"]; n9 -> n10;
        n10 [label="cxd4", style=filled]; n10 -> n11;
        n11 [label="Nxd4"]; n11 -> n12;
        n12 [label="g6", style=filled];

which Graphviz turns into that kind of image

enter image description here

So I'm looking for either a simple and free software that can create such graphs automatically or a script that converts a pgn-file into a dot-file. Can anyone help?

  • Since .dot Graphviz is plaintext, I suggest you write a little Python script yourself. (Depending on the number of bracket depth it shall be able to cope with, this is a hour's work at most for a halfways experienced programmer.) Commented Feb 15, 2023 at 8:23

3 Answers 3


The free program DiagTransfer version 3.0.1 will make a position diagram from a pgn or epd file. Works well on Windows 10. I've used it hundreds of times to insert chess positions into word processor files or print diagrams on file cards. Has several options including adding arrows.

  • I'm looking to turn a png into a tree-like graph. Probably it doesn't do this?
    – Tiru84
    Commented Feb 12, 2023 at 20:11

OMG guys! I did it. With my limited skills it took me two days to come up with a script that turn PGN of an opening to a nice graph. All you need to do is insert the PGN and run Graphviz with "dot -Tsvg graph.dot > output.svg" after running the python script.

import ast

pgn = "1.e4 c5 2.Nf3 ( 2.c3 Nf6 ( 2...f6 3.Na3 ) 3.e5 Nd5 ) Nc6 3.d4 cxd4 4.Nxd4 ( 4.c3 ) g6"

hauptzaehler = 0
knoten = []

class Node:
  def __init__(self, name, move, parent):
    self.name = name
    self.move = move
    self.is_white = None
    self.parent = parent

def pgn_to_list(pgn):
  pgn = pgn[pgn.find("1."):]
  pgn = pgn.split("*", 1)[0]
  for i in range(11,26):
    pgn = pgn.replace(str(i) + ".", "")
  for i in range(1,11):
    pgn = pgn.replace(str(i) + ".", "")
  pgn = pgn.replace(".", "")
  pgn = pgn.rstrip()
  pgn = pgn.replace(" ", "\", \"")
  pgn = pgn.replace("\"(\", ", "[")
  pgn = pgn.replace(", \")\"", "]")
  pgn = "[\"" + pgn + "\"]"
  pgn = pgn.replace(", \")\"", "]")
  feld = ast.literal_eval(pgn)
  return feld

pgn = pgn_to_list(pgn)

def mache_dicts(feld):
  global hauptzaehler
  for i in feld:
    if type(i) != list:
      feld[feld.index(i)] = {"index": hauptzaehler, "zug": i, "parent": None}
      hauptzaehler += 1
  return feld

def parentify1(feld):
  for i in range(len(feld)-1,-1,-1):
    if type(feld[i]) != list:
      if (i == 0):
        feld[0]["parent"] = None
        for x in range(i-1,-1,-1):
          if type(feld[x]) != list:
            eltern = feld[x]["index"]
            feld[i]["parent"] = eltern
  return None

def parentify2(feld):
  eltern0 = None
  eltern1 = None
  for i in range(0,len(feld)):
    if type(feld[i]) != list:
      eltern0 = eltern1
      eltern1 = feld[i]["index"]
      feld[i][0]["parent"] = eltern0
  return None

def knotenliste(pgn):
  global knoten
  for i in pgn:
    if type(i) != list:
  return None

def get_parent(feld, parent):
  for i in feld:
    if (i.name == parent):
      return i

def einfaerben(knoten):
  for i in knoten:
    if (i.parent == None):
      i.is_white = True
    elif (get_parent(knoten, i.parent).is_white == True):
      i.is_white = False
    elif (get_parent(knoten, i.parent).is_white == False):
      i.is_white = True
  return None

def get_kinder(feld, index):
  kinder = []
  for i in feld:
    if (i.parent == index):
  kinderstring = ""
  for x in kinder:
    kinderstring = kinderstring + " n" + str(index) + " -> n" + str(x) + ";"
  return kinderstring

def write_dot(knotenfeld):
  open("graph.dot", "w").close()
  dot = open("graph.dot", "a")
  dot.write("digraph chess_openings {\n  {\n    node [shape=box];\n")
  for i in knotenfeld:
    schwarz = ""
    if (i.is_white == False):
      schwarz = ", style=filled"
    dot.write("    n" + str(i.name) + "[label=\"" + i.move + "\"" + schwarz + "];" + get_kinder(knotenfeld, i.name) + "\n")
  dot.write("  }\n}")
  return None

pgn = mache_dicts(pgn)
for i in range(0,len(knoten)):

The website Chess Tree allows to import pgn and show them as an horizontal tree, with the moves inside circles. I have not seen an option to graphically export the tree or print it. Perhaps the author trust the print option in web explorer menu for this purpose. It allows also to activate an overlay with move statistics of the in site tree data base.

Another one that could catch your interest, comparing your repertoire with your games, is Chess Lens. A description of the app by its programer is in this article.

  • It would be trivial to take a screenshot of it
    – Darren H
    Commented Feb 15, 2023 at 16:34

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.