diff --git a/graphics.go b/graphics.go
deleted file mode 100644
index 438167198c9fef294401c3dfc309f17f27d0f21f..0000000000000000000000000000000000000000
--- a/graphics.go
+++ /dev/null
@@ -1,58 +0,0 @@
-package pixel
-
-import "github.com/faiface/pixel/pixelgl"
-
-// Warning: technical stuff below.
-
-// VertexFormat is an internal format of the OpenGL vertex data.
-//
-// You can actually change this and all of the Pixel's functions will use the new format.
-// Only change when you're implementing an OpenGL effect or something similar.
-var VertexFormat = DefaultVertexFormat()
-
-// DefaultVertexFormat returns the default vertex format used by Pixel.
-func DefaultVertexFormat() pixelgl.VertexFormat {
-	return pixelgl.VertexFormat{
-		{Purpose: pixelgl.Position, Size: 2},
-		{Purpose: pixelgl.Color, Size: 4},
-		{Purpose: pixelgl.TexCoord, Size: 2},
-	}
-}
-
-// ConvertVertexData converts data in the oldFormat to the newFormat. Vertex attributes in the new format
-// will be copied from the corresponding vertex attributes in the old format. If a vertex attribute in the new format
-// has no corresponding attribute in the old format, it will be filled with zeros.
-func ConvertVertexData(oldFormat, newFormat pixelgl.VertexFormat, data []float64) []float64 {
-	// calculate the mapping between old and new format
-	// if i is a start of a vertex attribute in the new format, then mapping[i] returns
-	// the index where the same attribute starts in the old format
-	mapping := make(map[int]int)
-	i := 0
-	for _, newAttr := range newFormat {
-		j := 0
-		for _, oldAttr := range oldFormat {
-			if newAttr == oldAttr {
-				mapping[i] = j
-				break
-			}
-			j += oldAttr.Size
-		}
-		i += newAttr.Size
-	}
-
-	oldData, newData := data, []float64{}
-
-	for i := 0; i < len(oldData); i += oldFormat.Size() {
-		j := 0
-		for _, attr := range newFormat {
-			if oldIndex, ok := mapping[j]; ok { // the attribute was found in the old format
-				newData = append(newData, oldData[i+oldIndex:i+oldIndex+attr.Size]...)
-			} else { // the attribute wasn't found in the old format, so fill with zeros
-				newData = append(newData, make([]float64, attr.Size)...)
-			}
-			j += attr.Size
-		}
-	}
-
-	return newData
-}
diff --git a/pixelgl/interface.go b/pixelgl/interface.go
index 86ab87f333bac46e613962b394b77434f8fa4ae6..2b7fbd613dff93ee36cd924bd04e60ce5fb03e58 100644
--- a/pixelgl/interface.go
+++ b/pixelgl/interface.go
@@ -12,10 +12,10 @@ package pixelgl
 // The recommended way to implement a Doer is to wrap another Doer (vertex array wraps texture and so on), let's call
 // it parent. Then the Do method will look like this:
 //
-//   func (o *MyObject) Do(sub func()) {
-//       o.parent.Do(func() {
+//   func (o *MyObject) Do(sub func(Context)) {
+//       o.parent.Do(func(ctx Context) {
 //	     // enter the object's state
-//           sub()
+//           sub(ctx)
 //           // leave the object's state
 //       })
 //   }
@@ -23,6 +23,45 @@ package pixelgl
 // It might seem difficult to grasp this kind of recursion at first, but it's really simple. What it's basically saying
 // is: "Hey parent, enter your state, then let me enter mine, then I'll do whatever I'm supposed to do in the middle.
 // After that I'll leave my state and please leave your state too parent."
+//
+// Also notice, that the functions are passing a Context around. This context contains the most important state variables.
+// Usually, you just pass it as you received it. If you want to pass a changed context to your child (e.g. your a shader),
+// use ctx.With* methods.
 type Doer interface {
-	Do(sub func())
+	Do(sub func(Context))
+}
+
+// Context takes state from one object to another. OpenGL is a state machine, so we have to approach it like that.
+// However, global variables are evil, so we have Context, that transfers important OpenGL state from one object to another.
+//
+// This type does *not* represent an OpenGL context in the OpenGL terminology.
+type Context struct {
+	vertexFormat VertexFormat
+	shader       *Shader
+}
+
+// VertexFormat returns the current vertex format.
+func (c Context) VertexFormat() VertexFormat {
+	return c.vertexFormat
+}
+
+// Shader returns the current shader.
+func (c Context) Shader() *Shader {
+	return c.shader
+}
+
+// WithVertexFormat returns a copy of this context with the specified vertex format.
+func (c Context) WithVertexFormat(vf VertexFormat) Context {
+	return Context{
+		vertexFormat: vf,
+		shader:       c.shader,
+	}
+}
+
+// WithShader returns a copy of this context with the specified shader.
+func (c Context) WithShader(s *Shader) Context {
+	return Context{
+		vertexFormat: c.vertexFormat,
+		shader:       s,
+	}
 }
diff --git a/pixelgl/shader.go b/pixelgl/shader.go
index 7c28f786b5292343b51c28283b3b90f646f234a6..134f4c46b3f7bf2aaf19cdc8432b53940411b694 100644
--- a/pixelgl/shader.go
+++ b/pixelgl/shader.go
@@ -22,7 +22,7 @@ func NewShader(parent Doer, vertexShader, fragmentShader string) (*Shader, error
 	}
 
 	var err, glerr error
-	parent.Do(func() {
+	parent.Do(func(ctx Context) {
 		err, glerr = DoErrGLErr(func() error {
 			var vshader, fshader uint32
 
@@ -105,7 +105,7 @@ func NewShader(parent Doer, vertexShader, fragmentShader string) (*Shader, error
 
 // Delete deletes a shader program. Don't use a shader after deletion.
 func (s *Shader) Delete() {
-	s.parent.Do(func() {
+	s.parent.Do(func(ctx Context) {
 		DoNoBlock(func() {
 			gl.DeleteProgram(s.program)
 		})
@@ -113,12 +113,12 @@ func (s *Shader) Delete() {
 }
 
 // Do stars using a shader, executes sub, and stops using it.
-func (s *Shader) Do(sub func()) {
-	s.parent.Do(func() {
+func (s *Shader) Do(sub func(Context)) {
+	s.parent.Do(func(ctx Context) {
 		DoNoBlock(func() {
 			gl.UseProgram(s.program)
 		})
-		sub()
+		sub(ctx.WithShader(s))
 		DoNoBlock(func() {
 			gl.UseProgram(0)
 		})
diff --git a/pixelgl/texture.go b/pixelgl/texture.go
index 29ff28e74128877d398ee6bc38389784d589d470..3f9f8c9020e6c725f82e9f0bbd952b906234b031 100644
--- a/pixelgl/texture.go
+++ b/pixelgl/texture.go
@@ -17,7 +17,7 @@ func NewTexture(parent Doer, width, height int, pixels []uint8) (*Texture, error
 	texture := &Texture{parent: parent}
 
 	var err error
-	parent.Do(func() {
+	parent.Do(func(ctx Context) {
 		err = DoGLErr(func() {
 			gl.GenTextures(1, &texture.tex)
 			gl.BindTexture(gl.TEXTURE_2D, texture.tex)
@@ -48,7 +48,7 @@ func NewTexture(parent Doer, width, height int, pixels []uint8) (*Texture, error
 
 // Delete deletes a texture. Don't use a texture after deletion.
 func (t *Texture) Delete() {
-	t.parent.Do(func() {
+	t.parent.Do(func(ctx Context) {
 		DoNoBlock(func() {
 			gl.DeleteTextures(1, &t.tex)
 		})
@@ -56,12 +56,12 @@ func (t *Texture) Delete() {
 }
 
 // Do bind a texture, executes sub, and unbinds the texture.
-func (t *Texture) Do(sub func()) {
-	t.parent.Do(func() {
+func (t *Texture) Do(sub func(Context)) {
+	t.parent.Do(func(ctx Context) {
 		DoNoBlock(func() {
 			gl.BindTexture(gl.TEXTURE_2D, t.tex)
 		})
-		sub()
+		sub(ctx)
 		DoNoBlock(func() {
 			gl.BindTexture(gl.TEXTURE_2D, 0)
 		})
diff --git a/pixelgl/vertex.go b/pixelgl/vertex.go
index a0ed2f434956698074a1ab1dfdc3d91962048cef..a9c842088db168487f6250083b282f2337e0b2c8 100644
--- a/pixelgl/vertex.go
+++ b/pixelgl/vertex.go
@@ -125,7 +125,7 @@ func NewVertexArray(parent Doer, format VertexFormat, mode VertexDrawMode, usage
 	}
 
 	var err error
-	parent.Do(func() {
+	parent.Do(func(ctx Context) {
 		err = DoGLErr(func() {
 			gl.GenVertexArrays(1, &va.vao)
 			gl.BindVertexArray(va.vao)
@@ -161,7 +161,7 @@ func NewVertexArray(parent Doer, format VertexFormat, mode VertexDrawMode, usage
 
 // Delete deletes a vertex array and it's associated vertex buffer. Don't use a vertex array after deletion.
 func (va *VertexArray) Delete() {
-	va.parent.Do(func() {
+	va.parent.Do(func(ctx Context) {
 		DoNoBlock(func() {
 			gl.DeleteVertexArrays(1, &va.vao)
 			gl.DeleteBuffers(1, &va.vbo)
@@ -193,7 +193,7 @@ func (va *VertexArray) DrawMode() VertexDrawMode {
 
 // Draw draws a vertex array.
 func (va *VertexArray) Draw() {
-	va.Do(func() {})
+	va.Do(func(Context) {})
 }
 
 // Data returns a copy of data inside a vertex array (actually it's vertex buffer).
@@ -233,13 +233,13 @@ func (va *VertexArray) SetVertexAttribute(vertex int, attr VertexAttribute, data
 }
 
 // Do binds a vertex arrray and it's associated vertex buffer, executes sub, and unbinds the vertex array and it's vertex buffer.
-func (va *VertexArray) Do(sub func()) {
-	va.parent.Do(func() {
+func (va *VertexArray) Do(sub func(Context)) {
+	va.parent.Do(func(ctx Context) {
 		DoNoBlock(func() {
 			gl.BindVertexArray(va.vao)
 			gl.BindBuffer(gl.ARRAY_BUFFER, va.vbo)
 		})
-		sub()
+		sub(ctx)
 		DoNoBlock(func() {
 			gl.DrawArrays(uint32(va.mode), 0, int32(va.count))
 			gl.BindBuffer(gl.ARRAY_BUFFER, 0)
diff --git a/window.go b/window.go
index d06c9b23a87f7339254e604ff5168ae041a3ab93..6b5c85a3b355b27c188d77066326f3eca6c5f207 100644
--- a/window.go
+++ b/window.go
@@ -108,7 +108,7 @@ func NewWindow(config WindowConfig) (*Window, error) {
 
 // Delete destroys a window. The window can't be used any further.
 func (w *Window) Delete() {
-	w.Do(func() {
+	w.Do(func(pixelgl.Context) {
 		pixelgl.Do(func() {
 			w.window.Destroy()
 		})
@@ -117,14 +117,14 @@ func (w *Window) Delete() {
 
 // Clear clears the window with a color.
 func (w *Window) Clear(c color.Color) {
-	w.Do(func() {
+	w.Do(func(pixelgl.Context) {
 		pixelgl.Clear(colorToRGBA(c))
 	})
 }
 
 // Update swaps buffers and polls events.
 func (w *Window) Update() {
-	w.Do(func() {
+	w.Do(func(pixelgl.Context) {
 		pixelgl.Do(func() {
 			if w.config.VSync {
 				glfw.SwapInterval(1)
@@ -137,7 +137,7 @@ func (w *Window) Update() {
 
 // SetTitle changes the title of a window.
 func (w *Window) SetTitle(title string) {
-	w.Do(func() {
+	w.Do(func(pixelgl.Context) {
 		pixelgl.Do(func() {
 			w.window.SetTitle(title)
 		})
@@ -147,7 +147,7 @@ func (w *Window) SetTitle(title string) {
 // SetSize resizes a window to the specified size in pixels.
 // In case of a fullscreen window, it changes the resolution of that window.
 func (w *Window) SetSize(width, height float64) {
-	w.Do(func() {
+	w.Do(func(pixelgl.Context) {
 		pixelgl.Do(func() {
 			w.window.SetSize(int(width), int(height))
 		})
@@ -156,7 +156,7 @@ func (w *Window) SetSize(width, height float64) {
 
 // Size returns the size of the client area of a window (the part you can draw on).
 func (w *Window) Size() (width, height float64) {
-	w.Do(func() {
+	w.Do(func(pixelgl.Context) {
 		pixelgl.Do(func() {
 			wi, hi := w.window.GetSize()
 			width = float64(wi)
@@ -168,7 +168,7 @@ func (w *Window) Size() (width, height float64) {
 
 // Show makes a window visible if it was hidden.
 func (w *Window) Show() {
-	w.Do(func() {
+	w.Do(func(pixelgl.Context) {
 		pixelgl.Do(func() {
 			w.window.Show()
 		})
@@ -177,7 +177,7 @@ func (w *Window) Show() {
 
 // Hide hides a window if it was visible.
 func (w *Window) Hide() {
-	w.Do(func() {
+	w.Do(func(pixelgl.Context) {
 		pixelgl.Do(func() {
 			w.window.Hide()
 		})
@@ -191,7 +191,7 @@ func (w *Window) Hide() {
 func (w *Window) SetFullscreen(monitor *Monitor) {
 	if w.Monitor() != monitor {
 		if monitor == nil {
-			w.Do(func() {
+			w.Do(func(pixelgl.Context) {
 				pixelgl.Do(func() {
 					w.window.SetMonitor(
 						nil,
@@ -204,7 +204,7 @@ func (w *Window) SetFullscreen(monitor *Monitor) {
 				})
 			})
 		} else {
-			w.Do(func() {
+			w.Do(func(pixelgl.Context) {
 				pixelgl.Do(func() {
 					w.restore.xpos, w.restore.ypos = w.window.GetPos()
 					w.restore.width, w.restore.height = w.window.GetSize()
@@ -233,7 +233,7 @@ func (w *Window) IsFullscreen() bool {
 // Monitor returns a monitor a fullscreen window is on. If the window is not fullscreen, this function returns nil.
 func (w *Window) Monitor() *Monitor {
 	var monitor *glfw.Monitor
-	w.Do(func() {
+	w.Do(func(pixelgl.Context) {
 		monitor = pixelgl.DoVal(func() interface{} {
 			return w.window.GetMonitor()
 		}).(*glfw.Monitor)
@@ -248,7 +248,7 @@ func (w *Window) Monitor() *Monitor {
 
 // Focus brings a window to the front and sets input focus.
 func (w *Window) Focus() {
-	w.Do(func() {
+	w.Do(func(pixelgl.Context) {
 		pixelgl.Do(func() {
 			w.window.Focus()
 		})
@@ -258,7 +258,7 @@ func (w *Window) Focus() {
 // Focused returns true if a window has input focus.
 func (w *Window) Focused() bool {
 	var focused bool
-	w.Do(func() {
+	w.Do(func(pixelgl.Context) {
 		focused = pixelgl.DoVal(func() interface{} {
 			return w.window.GetAttrib(glfw.Focused) == glfw.True
 		}).(bool)
@@ -268,7 +268,7 @@ func (w *Window) Focused() bool {
 
 // Maximize puts a windowed window to a maximized state.
 func (w *Window) Maximize() {
-	w.Do(func() {
+	w.Do(func(pixelgl.Context) {
 		pixelgl.Do(func() {
 			w.window.Maximize()
 		})
@@ -277,7 +277,7 @@ func (w *Window) Maximize() {
 
 // Restore restores a windowed window from a maximized state.
 func (w *Window) Restore() {
-	w.Do(func() {
+	w.Do(func(pixelgl.Context) {
 		pixelgl.Do(func() {
 			w.window.Restore()
 		})
@@ -290,7 +290,7 @@ var currentWindow struct {
 }
 
 // Do makes the context of this window current, if it's not already, and executes sub.
-func (w *Window) Do(sub func()) {
+func (w *Window) Do(sub func(pixelgl.Context)) {
 	currentWindow.Lock()
 	defer currentWindow.Unlock()
 
@@ -302,5 +302,5 @@ func (w *Window) Do(sub func()) {
 		currentWindow.handler = w
 	}
 
-	sub()
+	sub(pixelgl.Context{})
 }