diff --git a/GLUtil.cabal b/GLUtil.cabal
--- a/GLUtil.cabal
+++ b/GLUtil.cabal
@@ -1,11 +1,11 @@
 Name:                GLUtil
-Version:             0.6.1.2
+Version:             0.6.2
 Synopsis:            Miscellaneous OpenGL utilities.
 License:             BSD3
 License-file:        LICENSE
 Author:              Anthony Cowley
 Maintainer:          acowley@gmail.com
-Copyright:           (c) 2012 Anthony Cowley
+Copyright:           (c) 2012,2013 Anthony Cowley
 Category:            Graphics
 Build-type:          Simple
 Description:         Helpers for working with shaders, buffer objects, and 
@@ -22,7 +22,7 @@
 
 source-repository this
   type:     git
-  tag:      0.6.1.2
+  tag:      0.6.2
   location: http://github.com/acowley/GLUtil.git
 
 Library
@@ -31,6 +31,9 @@
                        Graphics.GLUtil.Shaders,
                        Graphics.GLUtil.ShaderProgram,
                        Graphics.GLUtil.BufferObjects,
+                       Graphics.GLUtil.Camera2D,
+                       Graphics.GLUtil.Camera3D,
+                       Graphics.GLUtil.Drawing,
                        Graphics.GLUtil.Textures,
                        Graphics.GLUtil.JuicyTextures,
                        Graphics.GLUtil.VertexArrayObjects,
diff --git a/src/Graphics/GLUtil.hs b/src/Graphics/GLUtil.hs
--- a/src/Graphics/GLUtil.hs
+++ b/src/Graphics/GLUtil.hs
@@ -1,6 +1,7 @@
 -- |The main import that simply re-exports the various modules that
 -- make up the @GLUtil@ library.
 module Graphics.GLUtil (module Graphics.GLUtil.BufferObjects,
+                        module Graphics.GLUtil.Drawing,
                         module Graphics.GLUtil.Shaders,
                         module Graphics.GLUtil.Textures,
                         readTexture,
@@ -12,6 +13,7 @@
                         module Graphics.GLUtil.Viewport) where
 
 import Graphics.GLUtil.BufferObjects
+import Graphics.GLUtil.Drawing
 import Graphics.GLUtil.Shaders
 import Graphics.GLUtil.Textures
 import Graphics.GLUtil.GLError
diff --git a/src/Graphics/GLUtil/BufferObjects.hs b/src/Graphics/GLUtil/BufferObjects.hs
--- a/src/Graphics/GLUtil/BufferObjects.hs
+++ b/src/Graphics/GLUtil/BufferObjects.hs
@@ -1,6 +1,7 @@
 {-# LANGUAGE ScopedTypeVariables #-}
 -- |Utilities for filling 'BufferObject's.
 module Graphics.GLUtil.BufferObjects where
+import Data.Word (Word32)
 import Graphics.Rendering.OpenGL
 import Foreign.ForeignPtr
 import Foreign.Ptr
@@ -80,3 +81,18 @@
 -- |A zero-offset 'Ptr'.
 offset0 :: Ptr a
 offset0 = offsetPtr 0
+
+-- | A class for things we know how to serialize into an OpenGL
+-- buffer.
+class BufferSource v where
+  fromSource :: BufferTarget -> v -> IO BufferObject
+
+instance Storable a => BufferSource [a] where
+  fromSource = makeBuffer
+
+instance Storable a => BufferSource (V.Vector a) where
+  fromSource = fromVector
+
+-- | Create an 'ElementArrayBuffer' from a source of 'Word32's.
+bufferIndices :: BufferSource (v Word32) => v Word32 -> IO BufferObject
+bufferIndices = fromSource ElementArrayBuffer
diff --git a/src/Graphics/GLUtil/Camera2D.hs b/src/Graphics/GLUtil/Camera2D.hs
new file mode 100644
--- /dev/null
+++ b/src/Graphics/GLUtil/Camera2D.hs
@@ -0,0 +1,37 @@
+-- | A camera designed for 2D viewing. The camera may be translated
+-- perpendicular to its view direction, or rolled about its view
+-- direction.
+module Graphics.GLUtil.Camera2D 
+  (-- * Camera movement
+   Camera, track, roll, rollRad,
+   -- * Camera initialization
+   camera2D,
+   -- * Math
+   camMatrix, deg2rad) where
+import Graphics.GLUtil.Camera3D hiding (camMatrix, roll, rollRad)
+import qualified Graphics.GLUtil.Camera3D as C
+import Linear (Conjugate, Epsilon, V2(..), V3(..), V4(..), M33)
+
+-- | Initialize a camera for 2D rendering.
+camera2D :: (Epsilon a, RealFloat a) => Camera a
+camera2D = fpsCamera
+
+-- | Move the camera side-to-side or up-and-down as in a tracking shot.
+track :: (Conjugate a, Epsilon a, RealFloat a) => V2 a -> Camera a -> Camera a
+track (V2 x y) = dolly (V3 x y 0)
+
+-- | Produce a matrix that transforms homogenous 2D points into the
+-- camera's coordinate frame.
+camMatrix :: (Conjugate a, Epsilon a, RealFloat a) => Camera a -> M33 a
+camMatrix = fmap getXYW . getXYW . C.camMatrix
+  where getXYW (V4 x y _ w) = V3 x y w
+
+-- | Roll a camera view about its view direction by an angle given in
+-- degrees.
+roll :: (Epsilon a, RealFloat a) => a -> Camera a -> Camera a
+roll = C.roll
+
+-- | Roll a camera view about its view direction by an angle given in
+-- radians.
+rollRad :: (Epsilon a, RealFloat a) => a -> Camera a -> Camera a
+rollRad = C.rollRad
diff --git a/src/Graphics/GLUtil/Camera3D.hs b/src/Graphics/GLUtil/Camera3D.hs
new file mode 100644
--- /dev/null
+++ b/src/Graphics/GLUtil/Camera3D.hs
@@ -0,0 +1,112 @@
+-- | A 'Camera' represents a coordinate frame into which 3D points may
+-- be transformed. For rendering purposes, it is often helpful to
+-- combine a transformation matrix computed from a 'Camera' by
+-- 'camMatrix' -- that transforms points into the camera's coordinate
+-- frame -- with a perspective projection matrix, as created by
+-- 'projectionMatrix'.
+module Graphics.GLUtil.Camera3D 
+  (-- * Camera movement
+   Camera, panRad, pan, tiltRad, tilt, rollRad, roll, dolly,
+   -- * Camera initialization
+   rosCamera, fpsCamera,
+   -- * Matrices
+   projectionMatrix, camMatrix,
+   -- * Miscellaneous
+   deg2rad) where
+import Linear (Conjugate(conjugate), Epsilon, V3(..), V4(..))
+import Linear.Matrix (mkTransformation, M44)
+import Linear.Quaternion (Quaternion, axisAngle, rotate)
+
+-- | A 'Camera' may be translated and rotated to provide a coordinate
+-- frame into which 3D points may be transformed.
+data Camera a = Camera { forward     :: V3 a
+                       , upward      :: V3 a
+                       , rightward   :: V3 a
+                       , orientation :: Quaternion a
+                       , location    :: V3 a }
+
+-- | Pan a camera view (turn side-to-side) by an angle given in
+-- radians. Panning is about the world's up-axis as captured by the
+-- initial camera state (e.g. the positive Y axis for 'fpsCamera').
+panRad :: (Epsilon a, RealFloat a) => a -> Camera a -> Camera a
+panRad theta c = c { orientation = r * orientation c }
+  where r = axisAngle (upward c) theta
+
+-- | Pan a camera view (turn side-to-side) by an angle given in
+-- degrees. Panning is about the world's up-axis as captured by the
+-- initial camera state (e.g. the positive Y axis for 'fpsCamera').
+pan :: (Epsilon a, RealFloat a) => a -> Camera a -> Camera a
+pan = panRad . deg2rad
+
+-- | Tilt a camera view (up-and-down) by an angle given in
+-- radians. Tilting is about the camera's horizontal axis (e.g. the
+-- positive X axis for 'fpsCamera').
+tiltRad :: (Epsilon a, RealFloat a) => a -> Camera a -> Camera a
+tiltRad theta c = c { orientation = orientation c * r }
+  where r = axisAngle (rightward c) theta
+
+-- | Tilt a camera view (up-and-down) by an angle given in degrees.
+-- Tilting is about the camera's horizontal axis (e.g. the positive X
+-- axis for 'fpsCamera').
+tilt :: (Epsilon a, RealFloat a) => a -> Camera a -> Camera a
+tilt = tiltRad . deg2rad
+
+-- | Roll a camera view about its view direction by an angle given in
+-- radians. Rolling is about the camera's forward axis (e.g. the
+-- negative Z axis for 'fpsCamera').
+rollRad :: (Epsilon a, RealFloat a) => a -> Camera a -> Camera a
+rollRad theta c = c { orientation = orientation c * r }
+  where r = axisAngle (forward c) theta
+
+-- | Roll a camera view about its view direction by an angle given in
+-- degrees. Rolling is about the camera's forward axis (e.g. the
+-- negative Z axis for 'fpsCamera').
+roll :: (Epsilon a, RealFloat a) => a -> Camera a -> Camera a
+roll = rollRad . deg2rad
+
+-- | Translate a camera's position by the given vector.
+dolly :: (Conjugate a, Epsilon a, RealFloat a) => V3 a -> Camera a -> Camera a
+dolly t c = c { location = location c + t' }
+  where t' = orientation c `rotate` t
+
+-- | Convert degrees to radians.
+deg2rad :: RealFloat a => a -> a
+deg2rad x = x * pi / 180
+
+-- | A camera at the origin with its up-axis coincident with the
+-- positive Z axis. This is the convention used by the ROS robotics
+-- platform.
+rosCamera :: (Epsilon a, RealFloat a) => Camera a
+rosCamera = Camera (V3 1 0 0) (V3 0 0 1) (V3 0 1 0) 1 0
+
+-- | A camera at the origin with its up-axis coincident with the
+-- positive Y axis. This is the convention used by "first-person
+-- shooter" (fps) video games.
+fpsCamera :: (Epsilon a, RealFloat a) => Camera a
+fpsCamera = Camera (V3 0 0 (-1)) (V3 0 1 0) (V3 1 0 0) 1 0
+
+-- | @projectionMatrix fov aspect near far@ produces a perspective
+-- projection matrix with the specified vertical field of view (FOV),
+-- given in radians, aspect ratio, and near and far clipping planes.
+projectionMatrix :: (Conjugate a, Epsilon a, RealFloat a)
+                 => a -> a -> a -> a -> M44 a
+projectionMatrix fovy aspect near far = 
+  V4 (V4 (focal / aspect) 0 0 0)
+     (V4 0 focal 0 0)
+     (V4 0 0 ((far+near) / (near - far)) ((2*far*near) / (near - far)))
+     (V4 0 0 (-1) 0)
+  where focal = 1 / tan (fovy * 0.5)
+
+-- | Produce a transformation matrix from a 'Camera'. This matrix
+-- transforms homogenous points into the camera's coordinate frame.
+camMatrix :: (Conjugate a, Epsilon a, RealFloat a) => Camera a -> M44 a
+camMatrix c = mkTransformation q (rotate q . negate . location $ c)
+  where q = conjugate $ orientation c
+
+{-
+-- | A lens for the fourth column of a matrix.
+translation' :: (R3 t, R4 v, Functor f)
+            => (V3 a -> f (V3 a)) -> t (v a) -> f (t (v a))
+translation' f m = fmap (\(V3 x y z) -> m & _x._w .~ x & _y._w .~ y & _z._w .~ z)
+                        (f (fmap (^. _w) (m ^. _xyz)))
+-}
diff --git a/src/Graphics/GLUtil/Drawing.hs b/src/Graphics/GLUtil/Drawing.hs
new file mode 100644
--- /dev/null
+++ b/src/Graphics/GLUtil/Drawing.hs
@@ -0,0 +1,11 @@
+-- | Simplify common drawing commands.
+module Graphics.GLUtil.Drawing where
+import Foreign.Ptr (nullPtr)
+import Graphics.Rendering.OpenGL
+
+-- | @drawIndexedTris n@ draws @n@ 'Triangles' using vertex data from
+-- the currently bound 'ArrayBuffer' and indices from the beginning of
+-- the currently bound 'ElementArrayBuffer'. Note that there must be
+-- at least @n * 3@ indices in the 'ElementArrayBuffer'!
+drawIndexedTris :: GLsizei -> IO ()
+drawIndexedTris n = drawElements Triangles (n*3) UnsignedInt nullPtr
