diff --git a/examples/lights/main.go b/examples/lights/main.go
index c1a6d3fa82944c65a0d171e10ca5bf58aa598fe8..dd41386b1955cb41e48e169b428eb3fa2f90f58b 100644
--- a/examples/lights/main.go
+++ b/examples/lights/main.go
@@ -27,10 +27,6 @@ func loadPicture(path string) (pixel.Picture, error) {
 	return pixel.PictureDataFromImage(img), nil
 }
 
-type drawer interface {
-	Draw(pixel.Target)
-}
-
 type colorlight struct {
 	color  pixel.RGBA
 	point  pixel.Vec
@@ -43,7 +39,7 @@ type colorlight struct {
 	imd *imdraw.IMDraw
 }
 
-func (cl *colorlight) apply(src, noise drawer, dst pixel.ComposeTarget) {
+func (cl *colorlight) apply(dst pixel.ComposeTarget, center pixel.Vec, src, noise *pixel.Sprite) {
 	// create the light arc if not created already
 	if cl.imd == nil {
 		imd := imdraw.New(nil)
@@ -66,12 +62,12 @@ func (cl *colorlight) apply(src, noise drawer, dst pixel.ComposeTarget) {
 	// draw the noise inside the light
 	dst.SetMatrix(pixel.IM)
 	dst.SetComposeMethod(pixel.ComposeIn)
-	noise.Draw(dst)
+	noise.Draw(dst, pixel.IM.Moved(center))
 
 	// draw an image inside the noisy light
 	dst.SetColorMask(cl.color)
 	dst.SetComposeMethod(pixel.ComposeIn)
-	src.Draw(dst)
+	src.Draw(dst, pixel.IM.Moved(center))
 
 	// draw the light reflected from the dust
 	dst.SetMatrix(pixel.IM.Scaled(0, cl.radius).Rotated(0, cl.angle).Moved(cl.point))
@@ -101,9 +97,7 @@ func run() {
 	}
 
 	panda := pixel.NewSprite(pandaPic, pandaPic.Bounds())
-	panda.SetMatrix(pixel.IM.Moved(win.Bounds().Center()))
 	noise := pixel.NewSprite(noisePic, noisePic.Bounds())
-	noise.SetMatrix(pixel.IM.Moved(win.Bounds().Center()))
 
 	colors := []pixel.RGBA{
 		pixel.RGB(1, 0, 0),
@@ -174,7 +168,7 @@ func run() {
 		// draw the panda visible outside the light
 		win.SetColorMask(pixel.Alpha(0.4))
 		win.SetComposeMethod(pixel.ComposePlus)
-		panda.Draw(win)
+		panda.Draw(win, pixel.IM.Moved(win.Bounds().Center()))
 
 		allLight.Clear(pixel.Alpha(0))
 		allLight.SetComposeMethod(pixel.ComposePlus)
@@ -182,13 +176,13 @@ func run() {
 		// accumulate all the lights
 		for i := range lights {
 			oneLight.Clear(pixel.Alpha(0))
-			lights[i].apply(panda, noise, oneLight)
-			oneLight.Draw(allLight)
+			lights[i].apply(oneLight, oneLight.Bounds().Center(), panda, noise)
+			oneLight.Draw(allLight, pixel.IM.Moved(allLight.Bounds().Center()))
 		}
 
 		// compose the final result
 		win.SetColorMask(pixel.Alpha(1))
-		allLight.Draw(win)
+		allLight.Draw(win, pixel.IM.Moved(win.Bounds().Center()))
 
 		win.Update()
 
diff --git a/examples/platformer/main.go b/examples/platformer/main.go
index ee2971e7d3362089c9922e0f25d5744a5e01a01e..dab362883420f88263188359d91ff31649d9bd39 100644
--- a/examples/platformer/main.go
+++ b/examples/platformer/main.go
@@ -215,7 +215,7 @@ func (ga *gopherAnim) draw(t pixel.Target, phys *gopherPhys) {
 	}
 	// draw the correct frame with the correct position and direction
 	ga.sprite.Set(ga.sheet, ga.frame)
-	ga.sprite.SetMatrix(pixel.IM.
+	ga.sprite.Draw(t, pixel.IM.
 		ScaledXY(0, pixel.V(
 			phys.rect.W()/ga.sprite.Frame().W(),
 			phys.rect.H()/ga.sprite.Frame().H(),
@@ -223,7 +223,6 @@ func (ga *gopherAnim) draw(t pixel.Target, phys *gopherPhys) {
 		ScaledXY(0, pixel.V(-ga.dir, 1)).
 		Moved(phys.rect.Center()),
 	)
-	ga.sprite.Draw(t)
 }
 
 type goal struct {
@@ -385,7 +384,7 @@ func run() {
 				win.Bounds().H()/canvas.Bounds().H(),
 			),
 		).Moved(win.Bounds().Center()))
-		canvas.Draw(win)
+		canvas.Draw(win, pixel.IM.Moved(canvas.Bounds().Center()))
 		win.Update()
 	}
 }
diff --git a/examples/smoke/main.go b/examples/smoke/main.go
index 40007a2f1abfbd192b2212d021274c78cbb12f5d..4b366c2490c73c429aa50bd57c337bc12b123553 100644
--- a/examples/smoke/main.go
+++ b/examples/smoke/main.go
@@ -54,13 +54,14 @@ func (p *particles) DrawAll(t pixel.Target) {
 	for e := p.parts.Front(); e != nil; e = e.Next() {
 		part := e.Value.(*particle)
 
-		part.Sprite.SetMatrix(pixel.IM.
-			Scaled(0, part.Scale).
-			Rotated(0, part.Rot).
-			Moved(part.Pos),
+		part.Sprite.DrawColorMask(
+			t,
+			pixel.IM.
+				Scaled(0, part.Scale).
+				Rotated(0, part.Rot).
+				Moved(part.Pos),
+			part.Mask,
 		)
-		part.Sprite.SetColorMask(part.Mask)
-		part.Sprite.Draw(t)
 	}
 }
 
diff --git a/examples/xor/main.go b/examples/xor/main.go
index 9b4c5c376eb76f1c1112a12eeecca041cfe85b0a..a062de4113f173d0170a8bacdd0ef5faada3b850 100644
--- a/examples/xor/main.go
+++ b/examples/xor/main.go
@@ -66,7 +66,7 @@ func run() {
 		imd.Draw(canvas)
 
 		win.Clear(colornames.Green)
-		canvas.Draw(win)
+		canvas.Draw(win, pixel.IM.Moved(win.Bounds().Center()))
 		win.Update()
 	}
 }
diff --git a/pixelgl/canvas.go b/pixelgl/canvas.go
index 723b544a1234b145f824401eb2ac3cdc1c455e7e..038bcc01c5dd85ae2e7759dba6d68ba94d86fbc2 100644
--- a/pixelgl/canvas.go
+++ b/pixelgl/canvas.go
@@ -123,7 +123,7 @@ func (c *Canvas) SetBounds(bounds pixel.Rect) {
 		c.sprite = pixel.NewSprite(nil, pixel.Rect{})
 	}
 	c.sprite.Set(c, c.Bounds())
-	c.sprite.SetMatrix(pixel.IM.Moved(c.Bounds().Center()))
+	//c.sprite.SetMatrix(pixel.IM.Moved(c.Bounds().Center()))
 }
 
 // Bounds returns the rectangular bounds of the Canvas.
@@ -245,12 +245,18 @@ func (c *Canvas) Pixels() []uint8 {
 	return pixels
 }
 
-// Draw draws a rectangle equal to Canvas's Bounds containing the Canvas's content to another
-// Target.
+// Draw draws the content of the Canvas onto another Target, transformed by the given Matrix, just
+// like if it was a Sprite containing the whole Canvas.
+func (c *Canvas) Draw(t pixel.Target, matrix pixel.Matrix) {
+	c.sprite.Draw(t, matrix)
+}
+
+// DrawColorMask draws the content of the Canvas onto another Target, transformed by the given
+// Matrix and multiplied by the given mask, just like if it was a Sprite containing the whole Canvas.
 //
-// Note, that the matrix and the color mask of this Canvas have no effect here.
-func (c *Canvas) Draw(t pixel.Target) {
-	c.sprite.Draw(t)
+// If the color mask is nil, a fully opaque white mask will be used causing no effect.
+func (c *Canvas) DrawColorMask(t pixel.Target, matrix pixel.Matrix, mask color.Color) {
+	c.sprite.DrawColorMask(t, matrix, mask)
 }
 
 type canvasTriangles struct {
diff --git a/sprite.go b/sprite.go
index 6ac9aebe1c1ba6fb2d1eeee9902ac9ffe6a80304..45989d691d38f7d851cabd8d97964af8c5cc3181 100644
--- a/sprite.go
+++ b/sprite.go
@@ -57,43 +57,35 @@ func (s *Sprite) Frame() Rect {
 	return s.frame
 }
 
-// SetMatrix sets a Matrix that this Sprite will be transformed by. This overrides any previously
-// set Matrix.
+// Draw draws the Sprite onto the provided Target. The Sprite will be transformed by the given Matrix.
 //
-// Note, that this has nothing to do with BasicTarget's SetMatrix method. This only affects this
-// Sprite and is usable with any Target.
-func (s *Sprite) SetMatrix(matrix Matrix) {
-	if s.matrix != matrix {
-		s.matrix = matrix
-		s.calcData()
-	}
+// This method is equivalent to calling DrawColorMask with nil color mask.
+func (s *Sprite) Draw(t Target, matrix Matrix) {
+	s.DrawColorMask(t, matrix, nil)
 }
 
-// Matrix returns the currently set Matrix.
-func (s *Sprite) Matrix() Matrix {
-	return s.matrix
-}
-
-// SetColorMask sets a color that this Sprite will be multiplied by. This overrides any previously
-// set color mask.
+// DrawColorMask draw the Sprite onto the provided Target. The Sprite will be transformed by the
+// given Matrix and all of it's color will be multiplied by the given mask.
 //
-// Note, that this has nothing to do with BasicTarget's SetColorMask method. This only affects this
-// Sprite and is usable with any Target.
-func (s *Sprite) SetColorMask(mask color.Color) {
-	rgba := ToRGBA(mask)
-	if s.mask != rgba {
+// If the mask is nil, a fully opaque white mask will be used, which causes no effect.
+func (s *Sprite) DrawColorMask(t Target, matrix Matrix, mask color.Color) {
+	dirty := false
+	if matrix != s.matrix {
+		s.matrix = matrix
+		dirty = true
+	}
+	if mask == nil {
+		mask = Alpha(1)
+	}
+	if mask != s.mask {
 		s.mask = ToRGBA(mask)
-		s.calcData()
+		dirty = true
 	}
-}
 
-// ColorMask returns the currently set color mask.
-func (s *Sprite) ColorMask() RGBA {
-	return s.mask
-}
+	if dirty {
+		s.calcData()
+	}
 
-// Draw draws the Sprite onto the provided Target.
-func (s *Sprite) Draw(t Target) {
 	s.d.Draw(t)
 }