diff --git a/pixelgl/canvas.go b/pixelgl/canvas.go
index 0439bb07133b18fac45397b487b77b3765421d0d..825c06063312807f33944c65abd52b5eba58cc9b 100644
--- a/pixelgl/canvas.go
+++ b/pixelgl/canvas.go
@@ -17,20 +17,15 @@ import (
 //
 // It supports TrianglesPosition, TrianglesColor, TrianglesPicture and PictureColor.
 type Canvas struct {
-	// these should **only** be accessed through orig
-	f       *glhf.Frame
-	borders pixel.Rect
-	pixels  []uint8
-	dirty   bool
-
-	// these should **never** be accessed through orig
+	f      *glhf.Frame
 	s      *glhf.Shader
 	bounds pixel.Rect
+	pixels []uint8
+	dirty  bool
+
 	mat    mgl32.Mat3
 	col    mgl32.Vec4
 	smooth bool
-
-	orig *Canvas
 }
 
 // NewCanvas creates a new empty, fully transparent Canvas with given bounds. If the smooth flag is
@@ -41,7 +36,6 @@ func NewCanvas(bounds pixel.Rect, smooth bool) *Canvas {
 		mat:    mgl32.Ident3(),
 		col:    mgl32.Vec4{1, 1, 1, 1},
 	}
-	c.orig = c
 
 	mainthread.Call(func() {
 		var err error
@@ -171,27 +165,16 @@ func (c *Canvas) SetColorMask(col color.Color) {
 }
 
 // SetBounds resizes the Canvas to the new bounds. Old content will be preserved.
-//
-// If the new Bounds fit into the Original borders, no new Canvas will be allocated.
 func (c *Canvas) SetBounds(bounds pixel.Rect) {
-	c.bounds = bounds
-
-	// if this bounds fit into the original bounds, no need to reallocate
-	if c.orig.borders.Contains(bounds.Min) && c.orig.borders.Contains(bounds.Max) {
-		return
-	}
-
 	mainthread.Call(func() {
-		oldF := c.orig.f
+		oldF := c.f
 
 		_, _, w, h := intBounds(bounds)
 		c.f = glhf.NewFrame(w, h, c.smooth)
 
 		// preserve old content
 		if oldF != nil {
-			relBounds := bounds
-			relBounds = relBounds.Moved(-c.orig.borders.Min)
-			ox, oy, ow, oh := intBounds(relBounds)
+			ox, oy, ow, oh := intBounds(bounds)
 			oldF.Blit(
 				c.f,
 				ox, oy, ox+ow, oy+oh,
@@ -200,11 +183,9 @@ func (c *Canvas) SetBounds(bounds pixel.Rect) {
 		}
 	})
 
-	// detach from orig
-	c.borders = bounds
+	c.bounds = bounds
 	c.pixels = nil
 	c.dirty = true
-	c.orig = c
 }
 
 // Bounds returns the rectangular bounds of the Canvas.
@@ -226,15 +207,13 @@ func (c *Canvas) Smooth() bool {
 
 // must be manually called inside mainthread
 func (c *Canvas) setGlhfBounds() {
-	bounds := c.bounds
-	bounds.Moved(c.orig.borders.Min)
-	bx, by, bw, bh := intBounds(bounds)
+	bx, by, bw, bh := intBounds(c.bounds)
 	glhf.Bounds(bx, by, bw, bh)
 }
 
 // Clear fills the whole Canvas with a single color.
 func (c *Canvas) Clear(color color.Color) {
-	c.orig.dirty = true
+	c.dirty = true
 
 	nrgba := pixel.ToNRGBA(color)
 
@@ -248,39 +227,39 @@ func (c *Canvas) Clear(color color.Color) {
 
 	mainthread.CallNonBlock(func() {
 		c.setGlhfBounds()
-		c.orig.f.Begin()
+		c.f.Begin()
 		glhf.Clear(
 			float32(nrgba.R),
 			float32(nrgba.G),
 			float32(nrgba.B),
 			float32(nrgba.A),
 		)
-		c.orig.f.End()
+		c.f.End()
 	})
 }
 
 // Color returns the color of the pixel over the given position inside the Canvas.
 func (c *Canvas) Color(at pixel.Vec) pixel.NRGBA {
-	if c.orig.dirty {
+	if c.dirty {
 		mainthread.Call(func() {
-			tex := c.orig.f.Texture()
+			tex := c.f.Texture()
 			tex.Begin()
-			c.orig.pixels = tex.Pixels(0, 0, tex.Width(), tex.Height())
+			c.pixels = tex.Pixels(0, 0, tex.Width(), tex.Height())
 			tex.End()
 		})
-		c.orig.dirty = false
+		c.dirty = false
 	}
 	if !c.bounds.Contains(at) {
 		return pixel.NRGBA{}
 	}
-	bx, by, bw, _ := intBounds(c.orig.borders)
+	bx, by, bw, _ := intBounds(c.bounds)
 	x, y := int(at.X())-bx, int(at.Y())-by
 	off := y*bw + x
 	return pixel.NRGBA{
-		R: float64(c.orig.pixels[off*4+0]) / 255,
-		G: float64(c.orig.pixels[off*4+1]) / 255,
-		B: float64(c.orig.pixels[off*4+2]) / 255,
-		A: float64(c.orig.pixels[off*4+3]) / 255,
+		R: float64(c.pixels[off*4+0]) / 255,
+		G: float64(c.pixels[off*4+1]) / 255,
+		B: float64(c.pixels[off*4+2]) / 255,
+		A: float64(c.pixels[off*4+3]) / 255,
 	}
 }
 
@@ -291,7 +270,7 @@ type canvasTriangles struct {
 }
 
 func (ct *canvasTriangles) draw(tex *glhf.Texture, bounds pixel.Rect) {
-	ct.dst.orig.dirty = true
+	ct.dst.dirty = true
 
 	// save the current state vars to avoid race condition
 	mat := ct.dst.mat
@@ -299,7 +278,7 @@ func (ct *canvasTriangles) draw(tex *glhf.Texture, bounds pixel.Rect) {
 
 	mainthread.CallNonBlock(func() {
 		ct.dst.setGlhfBounds()
-		ct.dst.orig.f.Begin()
+		ct.dst.f.Begin()
 		ct.dst.s.Begin()
 
 		ct.dst.s.SetUniformAttr(canvasBounds, mgl32.Vec4{
@@ -337,7 +316,7 @@ func (ct *canvasTriangles) draw(tex *glhf.Texture, bounds pixel.Rect) {
 		}
 
 		ct.dst.s.End()
-		ct.dst.orig.f.End()
+		ct.dst.f.End()
 	})
 }
 
@@ -401,7 +380,7 @@ func (ccp *canvasCanvasPicture) Draw(t pixel.TargetTriangles) {
 	if ccp.dst != ct.dst {
 		panic(fmt.Errorf("(%T).Draw: TargetTriangles generated by different Canvas", ccp))
 	}
-	ct.draw(ccp.src.orig.f.Texture(), ccp.Bounds())
+	ct.draw(ccp.src.f.Texture(), ccp.Bounds())
 }
 
 const (
@@ -445,7 +424,6 @@ out vec2 Texture;
 out float Intensity;
 
 uniform mat3 transform;
-uniform vec4 borders;
 uniform vec4 bounds;
 
 void main() {
@@ -468,7 +446,6 @@ in float Intensity;
 out vec4 color;
 
 uniform vec4 colorMask;
-uniform vec4 texBorders;
 uniform vec4 texBounds;
 uniform sampler2D tex;