diff --git a/algviz/parser/json_objects.py b/algviz/parser/json_objects.py
index 10619ee..cd36f94 100644
--- a/algviz/parser/json_objects.py
+++ b/algviz/parser/json_objects.py
@@ -191,6 +191,11 @@ def decode_json(text):
         snapshots.append(decode_snapshot(*raw_snapshot))
     return snapshots
 
+def decode_snapshot_text(text):
+    raw_snapshot = parse(text)
+    validate_snapshot(raw_snapshot)
+    return decode_snapshot(*raw_snapshot)
+
 def decode_snapshot(*objects):
     sd = SnapshotDecoder()
     for raw_obj in objects:
@@ -207,6 +212,9 @@ def validate(json_stuff):
         if json_stuff.get(Tokens.TYPE) == Tokens.NULL_T:
             validate_null(json_stuff)
 
+def validate_snapshot(snapshot):
+    pass  # TODO
+
 def parse(text):
     return json.loads(text, object_hook=fix_aliases)
 
diff --git a/algviz/parser/structures.py b/algviz/parser/structures.py
index 499de37..5efe694 100644
--- a/algviz/parser/structures.py
+++ b/algviz/parser/structures.py
@@ -29,6 +29,12 @@ class ObjectTable(dict):
             if hasattr(obj, 'untablify'):
                 obj.untablify(self)
 
+    def getuid(self, uid):
+        """Convenience method to return the object with the given uid (`str` type)"""
+        if not isinstance(uid, str):
+            raise TypeError("uid must be a string, not {}".format(uid))
+        return self[ObjectTableReference(uid=uid)]
+
 ObjectTableReference = collections.namedtuple("ObjectTableReference", ("uid",))
 
 Snapshot = collections.namedtuple("Snapshot", ("names", "obj_table"))
diff --git a/algviz/tools/__init__.py b/algviz/tools/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/algviz/tools/graph_drawing_mockup.py b/algviz/tools/graph_drawing_mockup.py
new file mode 100644
index 0000000..352cd89
--- /dev/null
+++ b/algviz/tools/graph_drawing_mockup.py
@@ -0,0 +1,75 @@
+#!/usr/bin/env python3
+
+"""
+This is a toy script that reads a snapshot (with objects in algviz's JSON data
+format) and directly uses PyGraphViz to create an SVG image of a graph from the
+snapshot.
+
+Run with --help for usage information.
+"""
+
+import argparse
+import sys
+import pygraphviz as pgv
+
+from algviz.parser import json_objects, structures
+
+def graph_to_pgv(graph):
+    """Make a `pygraphviz.AGraph` from the given `algviz.structures.Graph`"""
+    G = pgv.AGraph(directed=True)
+    # It's a shortcoming of pygraphviz that the nodes must be labeled with
+    # their UID and not with their contents, since adding two nodes with the
+    # same label is an error.  (I.e., graphviz makes more assumptions about
+    # nodes' labels than we do.  It assumes they will be unique identifiers.)
+    G.add_nodes_from(node.uid for node in graph.nodes)
+    for edge in graph.edges:
+        G.add_edge(edge.orig.uid, edge.dest.uid)
+    return G
+
+def main():
+    """Run this script with --help for documenation"""
+    parser = argparse.ArgumentParser(
+        "Read from graph in algviz JSON format and write SVG using PyGraphViz")
+    parser.add_argument("infile", type=argparse.FileType("r"),
+                        help="input file.  - for stdin")
+    parser.add_argument("outfile", type=argparse.FileType("wb"),
+                        help="output file (to be overwritten).  - for stdout")
+    parser.add_argument("--prog", "-p", type=str, default="neato", choices=[
+        'neato', 'dot', 'twopi', 'circo', 'fdp', 'sfdp'],
+                        help="A GraphViz graph-drawing algorithm to use")
+    parser.add_argument("--uid", "-u", type=str, default=None,
+                        help=("uid of graph to be drawn, if there is more than"
+                              " one graph in the snapshot."))
+    parser.add_argument("--var", "-r", default=None, type=str,
+                        help="var name of graph.  Takes precedence over UID.")
+    args = parser.parse_args()
+
+    # Even though we asked for args.infile to be opened in binary mode, stdin
+    # will be opened in text mode...
+    if 'b' in args.outfile.mode:
+        outfile = args.outfile
+    else:
+        # ... So we use the use the underlying buffer to write binary data to stdout
+        outfile = args.outfile.buffer
+    # Now we can do the actual decoding and drawing
+    snapshot = json_objects.decode_snapshot_text(args.infile.read())
+    if args.var:
+        graph = snapshot.names[args.var]
+    elif args.uid:
+        graph = snapshot.obj_table.getuid(args.uid)
+    else:
+        # Just search for the first graph we find in the snapshot
+        graph = None
+        for obj in snapshot.obj_table.values():
+            if isinstance(obj, structures.Graph):
+                graph = obj
+                break
+        if graph is None:
+            raise Exception("No graph found in JSON input")
+
+    gv_graph = graph_to_pgv(graph)
+    gv_graph.layout(prog=args.prog)
+    gv_graph.draw(path=outfile, format="svg")
+
+if __name__ == "__main__":
+    main()
diff --git a/example_objects/README.md b/example_objects/README.md
new file mode 100644
index 0000000..1902d09
--- /dev/null
+++ b/example_objects/README.md
@@ -0,0 +1,1 @@
+This directory is for examples of JSON object specification files that may be useful for tests or just fun to play with.
diff --git a/example_objects/example_graph.json b/example_objects/example_graph.json
new file mode 100644
index 0000000..35bfd48
--- /dev/null
+++ b/example_objects/example_graph.json
@@ -0,0 +1,37 @@
+[
+    {"T": "graph", "uid": "mygraph", "var": "G",
+     "nodes": [
+         {"T": "node", "uid": "n0", "data": 0},
+         {"T": "node", "uid": "n1", "data": 1},
+         {"T": "node", "uid": "n2", "data": 2},
+         {"T": "node", "uid": "n3", "data": 3},
+         {"T": "node", "uid": "n4", "data": 4},
+         {"T": "node", "uid": "n5", "data": 5},
+         {"T": "node", "uid": "n6", "data": 6},
+         {"T": "node", "uid": "n7", "data": 7},
+         {"T": "node", "uid": "n8", "data": 8}
+     ],
+     "edges": [
+         {"T": "edge", "uid": "e0", "from": "n0", "to": "n1"},
+         {"T": "edge", "uid": "e1", "from": "n3", "to": "n5"},
+         {"T": "edge", "uid": "e2", "from": "n0", "to": "n2"},
+         {"T": "edge", "uid": "e3", "from": "n2", "to": "n1"},
+         {"T": "edge", "uid": "e4", "from": "n0", "to": "n5"},
+         {"T": "edge", "uid": "e5", "from": "n5", "to": "n7"},
+         {"T": "edge", "uid": "e6", "from": "n6", "to": "n7"},
+         {"T": "edge", "uid": "e7", "from": "n8", "to": "n1"},
+         {"T": "edge", "uid": "e8", "from": "n8", "to": "n7"}
+     ]
+    },
+    {"T": "graph", "uid": "othergraph", "var": "H",
+     "nodes": [
+         "n0", "n1", "n2", "n3", "n4", "n5", "n6", "n7", "n8"
+     ],
+     "edges": [
+         "e0", "e1", "e2", "e3", "e4", "e5", "e6", "e7", "e8",
+         {"T": "edge", "uid": "e9", "from": "n4", "to": "n7"},
+         {"T": "edge", "uid": "e10", "from": "n1", "to": "n4"}
+     ]
+    }
+
+]
diff --git a/setup.py b/setup.py
index 276195b..e6337ee 100644
--- a/setup.py
+++ b/setup.py
@@ -6,4 +6,10 @@ setup(name='algviz',
       version=version,
       author=['Anna Gorbenko', 'Jonathan Homburg', 'John McGowan', 'Doni Ivanov', 'Eyal Minsky-Fenick', 'Oliver Kisielius'],  # feel free to change this, too
       url=r'https://github.com/Agizin/Algorithm-Visualization',
-      packages=find_packages(exclude=['ez_setup',]))
+      packages=find_packages(exclude=['ez_setup',]),
+      entry_points={
+          "console_scripts": [
+              "algviz_graph_mockup=algviz.tools.graph_drawing_mockup:main",
+          ]},
+      install_requires=['pygraphviz'],
+)

Toy tool to make pictures of graphs
Please implement a tool to parse JSON descriptions of graphs (using the `parsers.json_objects` module) and produce pictures of graphs directly with pygraphviz.
It's about time we had some fun.
diff --git a/algviz/parser/test_structures.py b/algviz/parser/test_structures.py
index 581f220..303f843 100644
--- a/algviz/parser/test_structures.py
+++ b/algviz/parser/test_structures.py
@@ -12,5 +12,25 @@ class DataStructuresTestCase(unittest.TestCase):
         self.assertFalse(structures.Null)
         self.assertEqual(hash(structures.Null), hash(structures.Null))
 
+class ObjectTableTestCase(unittest.TestCase):
+
+    def setUp(self):
+        self.obj_tab = structures.ObjectTable()
+
+    def test_null_always_in_table(self):
+        self.assertIn(structures.ObjectTableReference(structures.Null.uid),
+                      self.obj_tab)
+
+    def test_keys_must_be_object_table_references(self):
+        obj = structures.Widget(uid="some_kinda_widget")
+        with self.assertRaises(TypeError):
+            self.obj_tab[obj.uid] = obj
+        # make sure the key didn't go in before the error got thrown
+        self.assertNotIn(obj.uid, self.obj_tab)
+
+    def test_getuid_convenience_method(self):
+        self.assertEqual(self.obj_tab.getuid(structures.Null.uid),
+                         structures.Null)
+
 if __name__ == "__main__":
     unittest.main()

pip install -e .
parse_log_pytest
requirements.txt
pytest
3.9
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
-e git+https://github.com/Agizin/Algorithm-Visualization.git@c846f54fae8bf5981bde0df534781ac3db0dcc78#egg=algviz
exceptiongroup==1.2.2
iniconfig==2.1.0
packaging==24.2
pluggy==1.5.0
pytest==8.3.5
tomli==2.2.1

name: Algorithm-Visualization
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=3.0.16=h5eee18b_0
  - pip=25.0=py39h06a4308_0
  - python=3.9.21=he870216_1
  - readline=8.2=h5eee18b_0
  - setuptools=75.8.0=py39h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - tzdata=2025a=h04d1e81_0
  - wheel=0.45.1=py39h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - exceptiongroup==1.2.2
      - iniconfig==2.1.0
      - packaging==24.2
      - pluggy==1.5.0
      - pytest==8.3.5
      - tomli==2.2.1
prefix: /opt/conda/envs/Algorithm-Visualization

algviz/parser/test_structures.py::ObjectTableTestCase::test_getuid_convenience_method
algviz/parser/test_structures.py::DataStructuresTestCase::test_Null algviz/parser/test_structures.py::ObjectTableTestCase::test_keys_must_be_object_table_references algviz/parser/test_structures.py::ObjectTableTestCase::test_null_always_in_table
setup.py algviz/parser/structures.py example_objects/README.md algviz/tools/__init__.py algviz/tools/graph_drawing_mockup.py example_objects/example_graph.json algviz/parser/json_objects.py
setup.py algviz/parser/structures.py example_objects/README.md algviz/tools/__init__.py algviz/tools/graph_drawing_mockup.py example_objects/example_graph.json algviz/parser/json_objects.py