draw2d: one Go drawing API for images, PDF, SVG and OpenGL
2D rendering for different output (raster, pdf, svg)
At a glance
- What is it?
- draw2d is a Go 2D vector graphics library whose point is that the same drawing code can target a raster image, a PDF document, an SVG file or an OpenGL canvas, with the coordinate and angle conventions of the HTML canvas 2D context so the API feels familiar. The samples double as the test suite, and the dependency list shows a project that has not been refreshed in a while.
- Who is it for?
- Adopt draw2d if you generate vector graphics in Go and want the same program to produce a PNG, a PDF, an SVG and an on screen canvas without writing the drawing twice. Do not adopt it for a new project unless you are prepared to take on its dependency versions, and do not use it for work that needs a modern text shaping stack.
- Can I use it commercially?
- Yes. BSD-2-Clause is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Activity is slowing. The repository last received commits 6 months ago.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
One drawing program, four backends
The package description states the design in one sentence. draw2d is a Go 2D vector graphics library with support for multiple outputs such as images, in the draw2d package itself, PDF documents in draw2dpdf, OpenGL in draw2dgl and SVG in draw2dsvg.
That is the entire value proposition. The drawing operations live in one place, and the backend decides where the pixels or the vector commands end up. The README shows this directly, with two sample programs that differ only in their imports and their final save call, one producing a PNG and one producing a PDF, and both described as generating the same drawing.
The core operations are a normal 2D drawing set. Stroking and filling polygons, arcs, Bézier curves, drawing images, and text rendering with truetype fonts. Every drawing operation can be transformed by affine transformations, that is, scale, rotation and translation. There is also a draw2dkit package in the repository, and a draw2dbase package, which are the pieces other backends and the toolkit are built on.
The licence is BSD, the module is github.com/llgcode/draw2d, and the go.mod requires Go 1.24.0 with a toolchain line of go1.24.3. The last push to the repository was on 2026-04-22 and there are no GitHub releases, so the version you get from the Go module proxy is the version record rather than a tag list.
The audience is a Go developer generating diagrams, charts, reports or images on a server, where the same drawing has to end up as a preview and as a printable document, and writing that twice is not acceptable.
The canvas 2D conventions fix the coordinate system
The README says that package draw2d follows the conventions of the HTML Canvas 2D Context for coordinate system, angles and so on, and the acknowledgements section says the library was inspired by Postscript and by HTML5 canvas.
That is a deliberate compatibility choice, and it has two consequences. The good one is that anybody who has written canvas code, or read it, already knows the shape of the API. The path building calls in the quick start, BeginPath, MoveTo, LineTo, QuadCurveTo, Close and FillStroke, are the canvas ones, and the fill and stroke colour setters and the line width setter follow the same naming.
The consequence to plan for is that the canvas model leaks. The coordinates are in device space with the origin at the top left and y growing downwards, angles are measured in the canvas convention, and the drawing model is immediate mode, which is to say that there is no retained scene graph and no notion of a shape you can later modify. If you arrive from a retained mode system, such as a scene graph based vector editor, the mental model has to change.
The affine transform is the other half of the compatibility, and it is where the library is most useful in practice. Because every operation is transformable, a drawing routine can be written in its own coordinate space and then placed, scaled or rotated into the page. That is what makes a chart library or a layout engine able to reuse the same drawing code at different sizes, and it is the reason draw2d is usable for anything that is not a fixed canvas.
The same shape code into a PNG or a PDF
The quick start gives the two programs, and the diff between them is instructive.
The raster version creates an image and a graphic context on it:
package main
import (
"github.com/llgcode/draw2d/draw2dimg"
"image"
"image/color"
)
func main() {
// Initialize the graphic context on an RGBA image
dest := image.NewRGBA(image.Rect(0, 0, 297, 210.0))
gc := draw2dimg.NewGraphicContext(dest)The PDF version replaces the image with a document and changes the save call:
package main
import (
"github.com/llgcode/draw2d/draw2dpdf"
"image/color"
)
func main() {
// Initialize the graphic context on an RGBA image
dest := draw2dpdf.NewPdf("L", "mm", "A4")
gc := draw2dpdf.NewGraphicContext(dest)and at the end, the raster program calls draw2dimg.SaveToPngFile with a file name and the image, while the PDF program calls draw2dpdf.SaveToPdfFile with a file name and the document. Everything between, the colour setters, the line width, and then the drawing itself, is identical:
// Draw a closed shape
gc.BeginPath() // Initialize a new path
gc.MoveTo(10, 10) // Move to a position to start the new path
gc.LineTo(100, 50)
gc.QuadCurveTo(100, 10, 10, 10)
gc.Close()
gc.FillStroke()The PDF constructor takes a page size, a unit and a format, in this case portrait, millimetres and A4, which is the sort of detail that shows the backends are not a toy. And the SVG and OpenGL backends are documented in the same breath, with drawing on opengl provided by the draw2dgl package.
The library is also what the separate Postscript reader uses, and the README makes a point of that: the samples include a postscript sample, and the second image in the README is the result of samples/postscript, which demonstrates that draw2d can draw Postscript files into images or PDF documents with the ps package.
The samples are the test suite
The testing section explains an arrangement that is worth copying. The samples are run as tests from the root package folder draw2d, by:
go test ./...or, if you want to run with test coverage:
go test -cover ./... | grep -v "no test"This will generate output by the different backends in the output folder.
So the sample programs are the tests. Each sample draws something, the test run executes them, and the output folder collects what the backends produced. The repository makes that visible: there is an output/ directory at the top level, a samples/ directory with a README of its own and a samples.go file, and a set of sample programs including geometry, gopher, gopher2, line, linecapjoin, helloworld, helloworldgl, frameimage, postscript and postscriptgl, plus android and appengine directories for platform specific cases.
The consequence is that the backends are compared against each other by construction, since the same sample runs through each of them. The README's own illustration is exactly that comparison, with a geometry sample rendered to a PNG and to a PDF, both clickable.
The test files at the repository root follow the same idea. There are draw2d_types_test.go, font_test.go, matrix_test.go, path_test.go, samples_test.go, sync_test.go, test_test.go, and two files with names that suggest a deliberate policy: bug_exposure_test.go and known_issues_test.go, alongside a KNOWN_ISSUES.md. A library that keeps a known issues document and a test named after it is telling you it would rather you knew than found out.
Dependency versions that date the project
The go.mod is short, and two entries in it are worth reading carefully.
module github.com/llgcode/draw2d
go 1.24.0
toolchain go1.24.3The language requirement is current, with Go 1.24.0 and a toolchain of go1.24.3, which is what you would expect from a project that has been kept buildable on a modern toolchain. The dependencies are a different story. The text rendering dependency is github.com/golang/freetype pinned to a commit from 2017, and the PostScript support comes from github.com/llgcode/ps also pinned to a 2021 commit.
The freetype pin is the one to think about. It is the package that rasterises glyphs for the text rendering, and it is fixed at a decade old commit rather than tracking a release. The graphics stack is in better shape, with github.com/go-gl/gl and github.com/go-gl/glfw at recent pseudo versions, the PDF backend uses jung-kurt/gofpdf at 1.16.2, and the image handling uses golang.org/x/image at 0.36.0.
So the library builds on current Go and current OpenGL bindings, but its text rendering is a decade old. For a library whose features list includes text rendering with truetype fonts, that is the dependency to evaluate first, because the age of the rasteriser is what will decide whether your typography looks the way you expect, and it is also where a security or correctness fix would not be picked up.
The acknowledgements explain the lineage. Laurent Le Goff wrote the library, inspired by Postscript and HTML5 canvas, implemented the image and OpenGL backend with the freetype-go package, and also created the pure Go Postscript interpreter ps. Stani Michiels implemented the PDF backend with the gofpdf package. The acknowledgements credit Stani Michiels with the PDF backend, built on the gofpdf package.
Who uses it, and where the wrong tool is
The README lists the projects that use draw2d, and they are a good guide to what the library is actually good at.
The list is ps, the Postscript interpreter written in Go, and go.uik, a concurrent UI kit written in pure Go. Then smartcrop, for content aware image cropping, karta, for drawing Voronoi diagrams, chart, for basic charts in Go, and hilbert, the Google package for drawing Hilbert curves.
Look at that list. Every entry is a generator. Cropping, Voronoi diagrams, charts, Hilbert curves, a Postscript interpreter, a UI toolkit. None of them is an interactive editor or a display layer, and that is the shape of the library: it is a drawing engine for programs that produce geometry as output, not a canvas for a program that displays it.
That framing tells you where it is the wrong tool. If you need an interactive drawing surface, a widget set or a document editor, this is not it, and the go.uik entry in that list is a better pointer. If you need to produce a figure in a report generator, a chart, a diagram in a build pipeline, or a thumbnail on a server, then a backend that can emit a PDF and a PNG from the same code is exactly the right shape.
The second place it is the wrong tool is text. The truetype handling is a decade old, the shaping is not modern, and the README does not claim otherwise. A project whose output is typographically demanding should test the text rendering early rather than at the end.
And the third is the maintenance shape. There are no GitHub releases in the repository, the last push was on 2026-04-22, and the samples double as the tests, which means regressions are caught for drawing behaviour but not for a new Go release. The KNOWN_ISSUES.md file is the closest thing to a status page, and it is worth reading before you depend on the library.
Editorial conclusion
Adopt draw2d if you generate vector graphics in Go and want the same program to produce a PNG, a PDF, an SVG and an on screen canvas without writing the drawing twice. Do not adopt it for a new project unless you are prepared to take on its dependency versions, and do not use it for work that needs a modern text shaping stack. Verify first that the graphics driver packages build on your platform, that the truetype font handling covers the scripts you need, and that the known issues file does not describe the behaviour you are relying on.
Frequently asked questions
How do I install draw2d?
Install Go first, then run go get -u gopkg.in/llgcode/draw2d.v1 for the stable release or go get -u github.com/llgcode/draw2d for the current release. The package is released under the BSD licence and the module requires Go 1.24.0.
Which output formats does draw2d support?
The library supports multiple outputs: images through the draw2d package, PDF documents through draw2dpdf, OpenGL through draw2dgl and SVG through draw2dsvg. The same drawing code can be rendered to any of them, which the README demonstrates with one program producing a PNG and another producing a PDF.
How do I run the draw2d tests?
The samples are run as tests from the root package folder draw2d with go test ./..., or with coverage using go test -cover ./... | grep -v "no test". This generates output by the different backends in the output folder.
Does draw2d follow the HTML canvas API?
Yes. The README says the package follows the conventions of the HTML Canvas 2D Context for coordinate system, angles and similar, and the library was inspired by Postscript and HTML5 canvas, so the path building calls and the colour and line width setters use the familiar names.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/llgcode-draw2d)Was this useful?