Simple 2D game prototyping framework.

  • By null
  • Last update: Dec 17, 2022
  • Comments: 9


Simply prototype 2D games using an easy, minimal interface that lets you draw simple primitives and images on the screen, easily handle mouse and keyboard events and play sounds.



Install the Go programming language. After clicking the download link you will be referred to the installation instructions for your specific operating system.

Install Git and make it available in the PATH so the go tool can use it.

For Linux and OS X you need a C compiler installed. On Windows this is not necessary.

On Linux there are two backends available, GLFW and SDL2. For GLFW you need these libraries installed (tested on Linux Mint, other distros might be slightly different):

libx11-dev libxrandr-dev libgl1-mesa-dev libxcursor-dev libxinerama-dev libxi-dev

GLFW is used by default. To use SDL2 you need to add -tags sdl2 to your Go builds, e.g. go run -tags sdl2 main.go. Also you need the SDL2 libraries installed:

libsdl2-dev libsdl2-mixer-dev libsdl2-image-dev

Install the library and samples by running the following on your command line:

go get


For a description of all library functions, see the godoc page for this project. Note that most of the functionality is in the Window interface and hence the descriptions are listed as code comments in the source for that type.


package main

import (


func main() {
	draw.RunWindow("Title", 640, 480, update)

func update(window draw.Window) {
	// find the screen center
	w, h := window.Size()
	centerX, centerY := w/2, h/2

	// draw a button in the center of the screen
	mouseX, mouseY := window.MousePosition()
	mouseInCircle := math.Hypot(float64(mouseX-centerX), float64(mouseY-centerY)) < 20
	color := draw.DarkRed
	if mouseInCircle {
		color = draw.Red
	window.FillEllipse(centerX-20, centerY-20, 40, 40, color)
	window.DrawEllipse(centerX-20, centerY-20, 40, 40, draw.White)
	if mouseInCircle {
		window.DrawScaledText("Close!", centerX-40, centerY+25, 1.6, draw.Green)

	// check all mouse clicks that happened during this frame
	for _, click := range window.Clicks() {
		dx, dy := click.X-centerX, click.Y-centerY
		squareDist := dx*dx + dy*dy
		if squareDist <= 20*20 {
			// close the window and end the application

This example displays a window with a round button in the middle to close it. It demonstrates some basic drawing and event handling code.



  • 1

    Is there a way to have the screen size smaller than the window size?

    I want to use this for pixel art games, but I don't want to have a tiny window. So how do I make the screen size smaller than the window size? For example, having the window 960x600 but drawing on it like it's 320x200.

  • 2

    Build errors on Windows w/go 1.15 and 1.16

    > go build .
    go: finding module for package
    go: found in v0.0.0-20210219081453-295a54f2a887
    go: finding module for package
    go: finding module for package
    go: finding module for package
    go: finding module for package
    go: found in v1.0.1
    go: found in v0.0.0-20171129091500-a40121441cc5
    go: found in v0.0.0-20171129091500-a40121441cc5
    go: found in v1.0.0
    go: finding module for package
    go: found in v0.0.0-20170706082326-82969548412b
    ..\..\..\..\..\pkg\mod\\gonutz\protot[email protected]\draw\window_windows.go:93:8: undefined: w32.UnregisterClassAtom
    ..\..\..\..\..\pkg\mod\\gonutz\[email protected]\draw\window_windows.go:442:7: undefined: w32.WM_MOUSEHWHEEL
    ..\..\..\..\..\pkg\mod\\gonutz\[email protected]\draw\window_windows.go:515:10: cannot use style & ^w32.WS_OVERLAPPEDWINDOW (type int32) as type uint32 in argument to w32.SetWindowLong

    What version of go is wanted/desired? It might be worth adding a go.mod file (go mod init) so you can specify the go and dependency versions inline?

    Steps to reproduce:

    I tested with the built-in Windows Sandbox feature (if not enabled, hit start, type enough of 'turn windows features on' to get a completion, and enable 'Windows Sandbox' in the list of optional features). Could also be done with a virtual machine.

    • Install latest git:

    • Install go 1.16:

    • Open a Powershell session (I'm a linux/bash guy, powershell is based on the same spec as bash so it's a little more natural to me) -- hit start -- type 'powershell'

    • ensure git and go are installed (note: powershell supports completion on flags and arguments)

      gcm git -ea stop # short for: get-command git -erroraction stop gcm go -ea stop

    • create a local go project:

      cd (mkdir -f (join-path $env:GOPATH local/scratch))

    • initialize local go module

      go mod init local/scratch

    • create a simple test file

      notepad main.go with the following code package main

      import ""

      func main() { draw.RunWindow("Test", 640, 480, update) }

      func update(window draw.Window) { window.DrawLine(100, 100, 440, 380, draw.Blue) }

    • install dependencies

      go mod tidy

    • attempt to run PS C:\Users\WDAGUtilityAccount\go\local\scratch> go run . # ....\pkg\mod\\gonutz\[email protected]\draw\window_windows.go:93:8: undefined: w32.UnregisterClassAtom ....\pkg\mod\\gonutz\[email protected]\draw\window_windows.go:442:7: undefined: w32.WM_MOUSEHWHEEL ....\pkg\mod\\gonutz\[email protected]\draw\window_windows.go:515:10: cannot use style & ^w32.WS_OVERLAPPEDWINDOW (type int32) as type uint32 in argument to w32.SetWindowLong

  • 3

    Web Assembly?

    Will there be any support for Web Assembly? I want to use this for game jams, but usually people don't want to download games, so I usually put web builds of my games. :smile:

  • 4

    I know this isn't technically an issue but,

    It is an issue that this is so underrated! I've been trying everything everywhere from Vulkan-go to glfw-go and NOTHING worked cross platform! Thank you guys, and I'll probably contribute to this in the future! :smile:

  • 5

    some tips

    hello I am again sorry(I am only student who do not understand many things), but I am working on my bachalor and I would like to share tip how to improve code(if I do found and it is already exist, sorry):

    • I was create interface "shape":
    type Shape interface {
    	GetXY() (int, int)
    	SetXY(x, y int)
    	GetSize() (int, int)
    	SetSize(width, height int)
    	GetIdName() (int, string)
    	WasPressed() bool
    	WasActive() bool
    	In(x int, y int) bool
    	GetFunc() func(window draw.Window)
            SetFunc(function func(window draw.Window))
    	Paint(window draw.Window, scaledText float32)
    	CarryEvent(window draw.Window)
    • I was create several structures("basic", TextField, Label, CheckBox, Button), "basic" is implementing Shape and other have embed it, so all are Shape and can also overwrite method....... .
    type Basic struct {
    	x, y, endX, endY, width, height float64
    	id                              int64
    	name                            string
    	pressed, active                 bool
    	function                        func(draw.Window)
    • you can prepare slice structures:= make([]Shape,...) before "animation loop"(or during first turn - I have "init turn" because I use func "GetScaledText") and your "main code" will look like:
    func DrawControlAll(window draw.Window) {
    	for i := 0; i < len(shapes); i++ {
    		shape := shapes[i]
    		shape.Paint(window, 3)
    • if you would like to control themself, you can also at init make lamda with references to themself like this:
            label := my_package.NewLabel(name)
    	label.SetFunc(func(window draw.Window) {
    		label.DoSomeThingSpecial()//e.g. Hide by name
    • this only tip ............
  • 6

    some errors on linux arm

    hello, i am sorry(I am only begener) but I was trying this library on linux arm(andronix, you can see on Linux Distro on Android) and I found this:

    • build with sdl2:
    shared memfd open() failed: Function not implemented
    shared memfd open() failed: Function not implemented
    • build without tags:
    fatal error: unexpected signal during runtime execution
    [signal SIGSEGV: segmentation violation code=0x2 addr=0x7f7d92a000 pc=0x7f54028800]
    runtime stack:
    runtime.throw(0x5ccad6, 0x2a)
    /usr/local/go/src/runtime/panic.go:1117 +0x54
    /usr/local/go/src/runtime/signal_unix.go:718 +0x29c
    goroutine 1 [syscall, locked to thread]:
    runtime.cgocall(0x580a40, 0x40000bfdb8, 0x4000070000)
    /usr/local/go/src/runtime/cgocall.go:154 +0x50 fp=0x40000bfd80 sp=0x40000bfd40 pc=0x445ba0
    _cgo_gotypes.go:2403 +0x40 fp=0x40000bfdb0 sp=0x40000bfd80 pc=0x54ec10*Window).SwapBuffers.func1(0x40000d0000)
    /root/go/pkg/mod/[email protected]/v3.3/glfw/context.go:41 +0x50 fp=0x40000bfdf0 sp=0x40000bfdb0 pc=0x551580*Window).SwapBuffers(0x40000d0000)
    /root/go/pkg/mod/[email protected]/v3.3/glfw/context.go:41 +0x28 fp=0x40000bfe30 sp=0x40000bfdf0 pc=0x54ef28, 0x5, 0x280, 0x1e0, 0x5ce898, 0x0, 0x0)
    /root/go/pkg/mod/[email protected]/draw/window_glfw.go:125 +0x6a8 fp=0x40000bff20 sp=0x40000bfe30 pc=0x554c58
    /root/go/src/my/my.go:10 +0x50 fp=0x40000bff70 sp=0x40000bff20 pc=0x558fb0
    /usr/local/go/src/runtime/proc.go:225 +0x274 fp=0x40000bffd0 sp=0x40000bff70 pc=0x475bf4
    /usr/local/go/src/runtime/asm_arm64.s:1130 +0x4 fp=0x40000bffd0 sp=0x40000bffd0 pc=0x4a4bc4
  • 7

    Fix/w32 modules ref

    Prototype references which, with modules, fetches the v1.0.0 version that does not build. This change updates prototype to build with go 1.15 and 1.16 in a fresh environment.


    • reimaged a vm, win 10 2020 h2
    • installed git, go,
    • clone into $env:GOPATH/src/ and checked out the fix/w32-modules-ref branch
    • execute the following steps:
        $nutzdir = Join-Path $env:GOPATH src/
        $srcurl = ""
        cd (mkdir (join-path $nutzdir proto.before))
        invoke-webrequest $srcurl -outfile worm.go  # iwr for short
        go run worm.go
        # go 1.16 fails because no modules stuff
        go mod init ; go mod tidy
        go run worm.go


    ..\..\..\..\pkg\mod\\gonutz\[email protected]\draw\window_windows.go:93:8: undefined: w32.UnregisterClassAtom
    ..\..\..\..\pkg\mod\\gonutz\[email protected]\draw\window_windows.go:442:7: undefined: w32.WM_MOUSEHWHEEL
    ..\..\..\..\pkg\mod\\gonutz\[email protected]\draw\window_windows.go:515:10: cannot use style & ^w32.WS_OVERLAPPEDWINDOW (type int32) as type uint32 in argument to w32.SetWindowLong

    then I used go mod edit to switch to using the local on-disk copy of prototype:

        cd (mkdir (Join-Path $testdir proto.after))
        iwr $srcurl -outfile worm.go
        go mod init
        # --vv--
        go mod edit -replace  ## <<
        # --^^--
        go mod tidy
        go run worm.go

    which works


  • 8

    Cannot call RunWindow mutliple times

    Tested on Windows: calling RunWindow twice should open a window and once that is closed should open another one. Currently after closing the first window, no second window appears.

  • 9

    Is it possible to draw triangles or polygons?

    Looking at the source code, there doesn't seem to be any way to draw triangles or polygons.

    Is there any way to access lower-level functions to achieve this, or is this being planned for a future update?