diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,5 @@
+# Revision history for prettyprinter-graphviz
+
+## 0.1.0.0 -- YYYY-mm-dd
+
+* First version. Released on an unsuspecting world.
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright (c) 2020, George Thomas
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+
+    * Neither the name of George Thomas nor the names of other
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,6 @@
+pretty-printing for GraphViz labels
+===================================
+
+Just some simple utility functions for hooking up the Haskell libraries [graphviz](https://hackage.haskell.org/package/graphviz) and [prettyprinter](https://hackage.haskell.org/package/prettyprinter).
+
+Given a [Doc](https://hackage.haskell.org/package/prettyprinter/docs/Data-Text-Prettyprint-Doc.html#t:Doc), you can use [render](https://hackage.haskell.org/package/prettyprinter-graphviz/docs/Data-Text-Prettyprint-Doc-Render-GraphViz.html#v:render) to transform it to a GraphViz [Label](https://hackage.haskell.org/package/graphviz/docs/Data-GraphViz-Attributes-Complete.html#t:Label). If you are using a different annotation type (eg. something more abstract), then you can define a mapping to GraphViz HTML attributes, and `fmap` it over your `Doc`.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/prettyprinter-graphviz.cabal b/prettyprinter-graphviz.cabal
new file mode 100644
--- /dev/null
+++ b/prettyprinter-graphviz.cabal
@@ -0,0 +1,29 @@
+cabal-version:       2.4
+name:                prettyprinter-graphviz
+version:             0.1.0.0
+synopsis:            a prettyprinter backend for graphviz
+description:         Contains utility functions for rendering pretty GraphViz labels.
+homepage:            https://github.com/georgefst/prettyprinter-graphviz
+license:             BSD-3-Clause
+license-file:        LICENSE
+author:              George Thomas
+maintainer:          georgefsthomas@gmail.com
+category:            Graphics
+extra-source-files:  CHANGELOG.md
+                     README.md
+
+source-repository head
+  type:     git
+  location: git://github.com/georgefst/prettyprinter-graphviz.git
+
+library
+  exposed-modules:     Data.Text.Prettyprint.Doc.Render.GraphViz
+  build-depends:       base >= 4.11 && < 5
+                      ,graphviz ^>= 2999.20.0.3
+                      ,prettyprinter ^>= 1.5.1
+                      ,text ^>= 1.2.3.1
+  hs-source-dirs:      src
+  default-language:    Haskell2010
+  ghc-options:         -Wall
+  default-extensions:  LambdaCase
+                       OverloadedStrings
diff --git a/src/Data/Text/Prettyprint/Doc/Render/GraphViz.hs b/src/Data/Text/Prettyprint/Doc/Render/GraphViz.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Text/Prettyprint/Doc/Render/GraphViz.hs
@@ -0,0 +1,68 @@
+module Data.Text.Prettyprint.Doc.Render.GraphViz (
+    -- * Rendering functions
+    render,
+    render',
+
+    -- * Error handling
+    GraphVizRenderError(..),
+) where
+
+import qualified Data.Text.Lazy as TL
+import qualified Data.Text as T
+
+import Control.Exception (
+        Exception,
+        displayException,
+        throw,
+    )
+import Data.GraphViz.Attributes.Complete (
+        Label (HtmlLabel),
+    )
+import qualified Data.GraphViz.Attributes.HTML as H
+import Data.Text.Prettyprint.Doc (
+        Doc,
+        SimpleDocStream (..),
+        defaultLayoutOptions,
+        layoutPretty,
+    )
+
+-- | Render a document as a GraphViz label, using 'defaultLayoutOptions'.
+render :: Doc H.Attribute -> Label
+render = HtmlLabel . H.Text . render' . layoutPretty defaultLayoutOptions
+
+-- | Render a document stream as HTML text for GraphViz. This provides more fine-grained control than 'render'.
+render' :: SimpleDocStream H.Attribute -> H.Text
+render' =
+    let go cs = \case
+            SFail           -> throw GVDocStreamFail
+            SEmpty          -> []
+            SChar c ds      -> renderText cs (T.singleton c) : go cs ds
+            SText _ txt ds  -> renderText cs txt : go cs ds
+            SLine n ds      -> H.Newline [] : renderText cs (T.replicate n " ") : go cs ds
+            SAnnPush col ds -> go (col : cs) ds
+            SAnnPop ds      -> go (tailDef (throw GVEmptyStack) cs) ds
+    in  go []
+
+
+-- | The functions in this module can throw errors, given a malformed document stream.
+-- The average user is very unlikely to run into this,
+-- but error handling functionality is provided for completeness.
+data GraphVizRenderError
+    = GVDocStreamFail
+    | GVEmptyStack
+    deriving (Eq, Ord, Read, Show)
+instance Exception GraphVizRenderError where
+    displayException = \case
+        GVDocStreamFail -> t ++ "encountered failure in document stream"
+        GVEmptyStack    -> t ++ "attempted to pop empty attribute stack"
+        where t = "Failed to render HTML for GraphViz: "
+
+-- | Equal to the funciton of the same name from [safe](https://hackage.haskell.org/package/safe).
+tailDef :: [a] -> [a] -> [a]
+tailDef e = \case
+    []     -> e
+    _ : xs -> xs
+
+-- | Helper for rendering an individual 'TextItem'.
+renderText :: H.Attributes -> T.Text -> H.TextItem
+renderText cs = H.Font cs . pure . H.Str . TL.fromStrict
