diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,18 @@
+# 0.6
+
+#### Breaking changes
+
+- Added a new function to the `createProgram` and `createProgram_` uniform interface builder
+  argument. That function can now be used to retrieve `U (Region rw (UB a))`, which is a *UBO*.
+- The uniform interface creation is not performed in a arbitrary, user-defined monad anymore. A
+  dedicated type was introduced for that very purpose – `UniformInterface` – constraining the user
+  to only use the uniformize functions to map semantics to `U` values.
+
+#### Non-breaking changes
+
+- Added `UB`, which can be used along with `Buffer` to create *UBO* buffers and pass them to
+  shaders.
+
 ### 0.5.2.1
 
 - Relaxed lower bound of `linear` to accept `linear-1.19.*`. That changes should enable `lumimance`
@@ -6,7 +21,7 @@
 
 ## 0.5.2
 
-#### Minor changes
+#### Non-breaking changes
 
 - Added texture arrays:
     + `Texture1DArray`
@@ -15,7 +30,7 @@
 
 ## 0.5.1
 
-#### Minor changes
+#### Non-breaking changes
 
 - Added several `Uniform` instances for [linear](http://hackage.haskell.org/package/linear).
 
@@ -26,12 +41,12 @@
 
 ### 0.5
 
-#### Major changes
+#### Breaking changes
 
 - Changed the interface of texels transfer and filling. We dropped the `Foldable` instance and now
   require a `Data.Vector.Storable.Vector` for performance purposes.
 
-#### Minor changes
+#### Non-breaking changes
 
 - Added `MirrorRepeat` constructor to `Wrap`.
 
diff --git a/luminance.cabal b/luminance.cabal
--- a/luminance.cabal
+++ b/luminance.cabal
@@ -1,5 +1,5 @@
 name:                luminance
-version:             0.5.2.1
+version:             0.6
 synopsis:            Type-safe, dependently-typed and stateless graphics framework
 description:         This package exposes several modules to work with /GPUs/ in a stateless and
                      type-safe way. Currently, it uses OpenGL as backend hardware technology but
@@ -86,6 +86,7 @@
                      , Graphics.Luminance.Core.Shader.Program
                      , Graphics.Luminance.Core.Shader.Stage
                      , Graphics.Luminance.Core.Shader.Uniform
+                     , Graphics.Luminance.Core.Shader.UniformBlock
                      , Graphics.Luminance.Core.Texture
                      , Graphics.Luminance.Core.Texture1D
                      , Graphics.Luminance.Core.Texture1DArray
diff --git a/src/Graphics/Luminance/Core/Shader/Program.hs b/src/Graphics/Luminance/Core/Shader/Program.hs
--- a/src/Graphics/Luminance/Core/Shader/Program.hs
+++ b/src/Graphics/Luminance/Core/Shader/Program.hs
@@ -14,14 +14,17 @@
 import Control.Monad.Except ( MonadError(throwError) )
 import Control.Monad.IO.Class ( MonadIO(..) )
 import Control.Monad.Trans.Resource ( MonadResource, register )
+import Control.Monad.Trans.State ( StateT, evalStateT, gets, modify )
 import Data.Foldable ( traverse_ )
 import Foreign.C ( peekCString, withCString )
 import Foreign.Marshal.Alloc ( alloca )
 import Foreign.Marshal.Array ( allocaArray )
 import Foreign.Ptr ( castPtr, nullPtr )
-import Foreign.Storable ( peek )
+import Foreign.Storable ( Storable(peek, sizeOf) )
+import Graphics.Luminance.Core.Buffer ( Region(..), bufferID )
 import Graphics.Luminance.Core.Shader.Stage ( Stage(..) )
-import Graphics.Luminance.Core.Shader.Uniform ( U, Uniform(..) )
+import Graphics.Luminance.Core.Shader.Uniform ( U(..), Uniform(..) )
+import Graphics.Luminance.Core.Shader.UniformBlock ( UB, UniformBlock )
 import Graphics.GL
 import Numeric.Natural ( Natural )
 
@@ -44,7 +47,7 @@
 -- the function you pass as argument. You can use that value to gather uniforms for instance.
 createProgram :: (HasProgramError e,MonadError e m,MonadIO m,MonadResource m)
               => [Stage]
-              -> ((forall a. (Uniform a) => Either String Natural -> m (U a)) -> m i)
+              -> ((forall a. (Uniform a) => Either String Natural -> UniformInterface m (U a)) -> (forall a. (Storable a,UniformBlock a) => String -> UniformInterface m (U (Region rw (UB a)))) -> UniformInterface m i)
               -> m (Program,i)
 createProgram stages buildIface = do
   (pid,linked,cl) <- liftIO $ do
@@ -56,11 +59,11 @@
     cl <- clog ll pid
     pure (pid,linked,cl)
   if
-    | linked    -> do
+    | linked -> do
         _ <- register $ glDeleteProgram pid
         let prog = Program pid
-        iface <- buildIface $ ifaceWith prog
-        pure (prog,iface)
+        a <- runUniformInterface $ buildIface (uniformize prog) (uniformizeBlock prog)
+        pure (prog,a)
     | otherwise -> throwError . fromProgramError $ LinkFailed cl
 
 -- |A simpler version of 'createProgram'. That function assumes you don’t need a uniform interface
@@ -68,7 +71,7 @@
 createProgram_ :: (HasProgramError e,MonadError e m,MonadIO m,MonadResource m)
                 => [Stage]
                 -> m Program
-createProgram_ stages = fmap fst $ createProgram stages (\_ -> pure ())
+createProgram_ stages = fmap fst $ createProgram stages (\_ _ -> pure ())
 
 -- |Is a shader program linked?
 isLinked :: GLuint -> IO Bool
@@ -89,25 +92,62 @@
     liftA2 (*>) (glGetProgramInfoLog pid (fromIntegral l) nullPtr)
       (peekCString . castPtr)
 
+--------------------------------------------------------------------------------
+-- Uniform interface -----------------------------------------------------------
+
+newtype UniformInterface m a = UniformInterface {
+    runUniformInterface' :: StateT UniformInterfaceCtxt m a
+  } deriving (Applicative,Functor,Monad)
+
+runUniformInterface :: (Monad m) => UniformInterface m a -> m a
+runUniformInterface ui = evalStateT (runUniformInterface' ui) emptyUniformInterfaceCtxt
+
+newtype UniformInterfaceCtxt = UniformInterfaceCtxt {
+    uniformInterfaceBufferBinding :: GLuint
+  } deriving (Eq,Show)
+
+emptyUniformInterfaceCtxt :: UniformInterfaceCtxt
+emptyUniformInterfaceCtxt = UniformInterfaceCtxt {
+    uniformInterfaceBufferBinding = 0
+  }
+
 -- |Either map a 'String' or 'Natural' to a uniform.
-ifaceWith :: (HasProgramError e,MonadError e m,MonadIO m,Uniform a)
-          => Program
-          -> Either String Natural
-          -> m (U a)
-ifaceWith prog access = case access of
-    Left name -> do
-      location <- liftIO . withCString name $ glGetUniformLocation pid
-      if
-        | isActive location -> pure $ toU pid location
-        | otherwise         -> throwError . fromProgramError $ InactiveUniform access
-    Right sem
-      | isActive sem -> pure $ toU pid (fromIntegral sem)
-      | otherwise    -> throwError . fromProgramError $ InactiveUniform access
-  where
-    pid = programID prog
-    isActive :: (Ord a,Num a) => a -> Bool
-    isActive = (> -1)
+uniformize :: (HasProgramError e,MonadError e m,MonadIO m,Uniform a)
+           => Program
+           -> Either String Natural
+           -> UniformInterface m (U a)
+uniformize Program{programID = pid} access = UniformInterface $ case access of
+  Left name -> do
+    location <- liftIO . withCString name $ glGetUniformLocation pid
+    if
+      | location /= -1 -> pure $ toU pid location
+      | otherwise         -> throwError . fromProgramError $ InactiveUniform access
+  Right sem
+    | sem /= -1 -> pure $ toU pid (fromIntegral sem)
+    | otherwise    -> throwError . fromProgramError $ InactiveUniform access
 
+-- |Map a 'String' to a uniform block.
+uniformizeBlock :: forall a e m rw. (HasProgramError e,MonadError e m,MonadIO m,Storable a,UniformBlock a)
+                => Program
+                -> String
+                -> UniformInterface m (U (Region rw (UB a)))
+uniformizeBlock Program{programID = pid} name = UniformInterface $ do
+    index <- liftIO . withCString name $ glGetUniformBlockIndex pid
+    if
+      | index /= GL_INVALID_INDEX -> do
+          -- retrieve a new binding value and use it
+          binding <- gets uniformInterfaceBufferBinding
+          modify $ \ctxt -> ctxt { uniformInterfaceBufferBinding = succ $ uniformInterfaceBufferBinding ctxt }
+          liftIO (glUniformBlockBinding pid index binding)
+          pure . U $ \r -> do
+            glBindBufferRange
+              GL_UNIFORM_BUFFER
+              binding
+              (bufferID $ regionBuffer r)
+              (fromIntegral $ regionOffset r)
+              (fromIntegral $ regionSize r * sizeOf (undefined :: a))
+      | otherwise -> throwError . fromProgramError $ InactiveUniformBlock name
+
 --------------------------------------------------------------------------------
 -- Shader program errors -------------------------------------------------------
 
@@ -120,6 +160,7 @@
 data ProgramError
   = LinkFailed String
   | InactiveUniform (Either String Natural)
+  | InactiveUniformBlock String
     deriving (Eq,Show)
 
 -- |Types that can handle 'ProgramError' – read as, “have”.
diff --git a/src/Graphics/Luminance/Core/Shader/Uniform.hs b/src/Graphics/Luminance/Core/Shader/Uniform.hs
--- a/src/Graphics/Luminance/Core/Shader/Uniform.hs
+++ b/src/Graphics/Luminance/Core/Shader/Uniform.hs
@@ -39,6 +39,8 @@
 -- handle a lot of uniform types. However, you should have a look at the 'U' documentation for
 -- further information about how to augment the scope of the types you can send down to shaders.
 class Uniform a where
+  -- |@'toU' prog l@ creates a new 'U a' by mapping it to the 'Program' @prog@ and using the
+  -- location 'l'.
   toU :: GLuint -> GLint -> U a
 
 -- |A shader uniform. @'U' a@ doesn’t hold any value. It’s more like a mapping between the host
diff --git a/src/Graphics/Luminance/Core/Shader/UniformBlock.hs b/src/Graphics/Luminance/Core/Shader/UniformBlock.hs
new file mode 100644
--- /dev/null
+++ b/src/Graphics/Luminance/Core/Shader/UniformBlock.hs
@@ -0,0 +1,163 @@
+{-# LANGUAGE DefaultSignatures #-}
+{-# LANGUAGE DeriveTraversable #-}
+
+-----------------------------------------------------------------------------
+-- |
+-- Copyright   : (C) 2015 Dimitri Sabadie
+-- License     : BSD3
+--
+-- Maintainer  : Dimitri Sabadie <dimitri.sabadie@gmail.com>
+-- Stability   : experimental
+-- Portability : portable
+--
+-----------------------------------------------------------------------
+
+module Graphics.Luminance.Core.Shader.UniformBlock where
+
+import Control.Monad.IO.Class ( MonadIO(..) ) 
+import Data.Int ( Int32 )
+import Data.Proxy ( Proxy(..) )
+import Data.Word ( Word32 )
+import Foreign.Ptr ( Ptr )
+import Foreign.Storable ( Storable(..), peekByteOff, pokeByteOff )
+import GHC.Generics
+import Linear.V2 ( V2 )
+import Linear.V3 ( V3 )
+import Linear.V4 ( V4 )
+
+--------------------------------------------------------------------------------
+-- UB wrapper type -------------------------------------------------------------
+
+newtype UB a = UB { unUB :: a } deriving (Eq,Foldable,Functor,Ord,Show,Traversable)
+
+instance (UniformBlock a) => Storable (UB a) where
+  alignment _ = alignmentSTD140 (Proxy :: Proxy a)
+  sizeOf _ = sizeOfSTD140 (Proxy :: Proxy a)
+  peekByteOff p o = fmap UB (peekSTD140 p o)
+  pokeByteOff p o = pokeSTD140 p o . unUB
+
+--------------------------------------------------------------------------------
+-- Uniform block ---------------------------------------------------------------
+
+class UniformBlock a where
+  alignmentSTD140 :: proxy a -> Int
+  default alignmentSTD140 :: (Generic a,GUniformBlock (Rep a)) => proxy a -> Int
+  alignmentSTD140 _ = galignmentSTD140 (Proxy :: Proxy (Rep a))
+
+  sizeOfSTD140 :: proxy a -> Int
+  default sizeOfSTD140 :: (Generic a,GUniformBlock (Rep a)) => proxy a -> Int
+  sizeOfSTD140 _ = gsizeOfSTD140 (Proxy :: Proxy (Rep a))
+
+  peekSTD140 :: (MonadIO m) => Ptr b -> Int -> m a
+  default peekSTD140 :: (Generic a,GUniformBlock (Rep a),MonadIO m) => Ptr b -> Int -> m a
+  peekSTD140 p o = liftIO $ fmap to (gpeekSTD140 p o)
+
+  pokeSTD140 :: (MonadIO m) => Ptr b -> Int -> a -> m ()
+  default pokeSTD140 :: (Generic a,GUniformBlock (Rep a),MonadIO m) => Ptr b -> Int -> a -> m ()
+  pokeSTD140 p o a = liftIO $ gpokeSTD140 p o (from a)
+
+roundUp :: Int -> Int -> Int
+roundUp u a = a + mod (u - a) u
+
+--------------------------------------------------------------------------------
+-- Generic UniformBlock --------------------------------------------------------
+
+class GUniformBlock f where
+  galignmentSTD140 :: proxy f -> Int
+  gsizeOfSTD140 :: proxy f -> Int
+  gpeekSTD140 :: (MonadIO m) => Ptr b -> Int -> m (f a)
+  gpokeSTD140 :: (MonadIO m) => Ptr b -> Int -> f a -> m ()
+
+instance GUniformBlock U1 where
+  galignmentSTD140 _ = 1
+  gsizeOfSTD140 _ = 0
+  gpeekSTD140 _ _ = pure U1
+  gpokeSTD140 _ _ _ = pure ()
+
+instance (GUniformBlock f,GUniformBlock g) => GUniformBlock (f :*: g) where
+  galignmentSTD140 _ = galignmentSTD140 (Proxy :: Proxy f) `max` galignmentSTD140 (Proxy :: Proxy g)
+  gsizeOfSTD140 _ = gsizeOfSTD140 (Proxy :: Proxy f) + gsizeOfSTD140 (Proxy :: Proxy g)
+  gpeekSTD140 p o = liftIO $
+        (:*:)
+    <$> gpeekSTD140 p o
+    <*> gpeekSTD140 p (o + roundUp 32 (gsizeOfSTD140 (Proxy :: Proxy f)))
+  gpokeSTD140 p o (f :*: g) = liftIO $ do
+    gpokeSTD140 p o f
+    gpokeSTD140 p (o + roundUp 32 (gsizeOfSTD140 (Proxy :: Proxy f))) g
+
+instance (GUniformBlock f) => GUniformBlock (D1 c f) where
+  galignmentSTD140 _ = galignmentSTD140 (Proxy :: Proxy f)
+  gsizeOfSTD140 _ = gsizeOfSTD140 (Proxy :: Proxy f)
+  gpeekSTD140 p o = fmap M1 (gpeekSTD140 p o)
+  gpokeSTD140 p o (M1 a) = gpokeSTD140 p o a
+
+instance (GUniformBlock f) => GUniformBlock (C1 c f) where
+  galignmentSTD140 _ = roundUp 32 (galignmentSTD140 (Proxy :: Proxy f))
+  gsizeOfSTD140 _ = roundUp 32 (gsizeOfSTD140 (Proxy :: Proxy f))
+  gpeekSTD140 p o = fmap M1 (gpeekSTD140 p o)
+  gpokeSTD140 p o (M1 a) = gpokeSTD140 p o a
+
+instance (GUniformBlock f) => GUniformBlock (S1 c f) where
+  galignmentSTD140 _ = galignmentSTD140 (Proxy :: Proxy f)
+  gsizeOfSTD140 _ = gsizeOfSTD140 (Proxy :: Proxy f)
+  gpeekSTD140 p o = fmap M1 (gpeekSTD140 p o)
+  gpokeSTD140 p o (M1 a) = gpokeSTD140 p o a
+
+instance (UniformBlock c) => GUniformBlock (K1 i c) where
+  galignmentSTD140 _ = alignmentSTD140 (Proxy :: Proxy c)
+  gsizeOfSTD140 _ = sizeOfSTD140 (Proxy :: Proxy c)
+  gpeekSTD140 p o = fmap K1 (peekSTD140 p o)
+  gpokeSTD140 p o (K1 a) = pokeSTD140 p o a
+
+--------------------------------------------------------------------------------
+-- Basic instances -------------------------------------------------------------
+
+instance UniformBlock Int32 where
+  alignmentSTD140 _ = 4
+  sizeOfSTD140 _ = 4
+  peekSTD140 p o = liftIO (peekByteOff p o)
+  pokeSTD140 p o a = liftIO (pokeByteOff p o a)
+
+instance UniformBlock Word32 where
+  alignmentSTD140 _ = 4
+  sizeOfSTD140 _ = 4
+  peekSTD140 p o = liftIO (peekByteOff p o)
+  pokeSTD140 p o a = liftIO (pokeByteOff p o a)
+
+instance UniformBlock Float where
+  alignmentSTD140 _ = 4
+  sizeOfSTD140 _ = 4
+  peekSTD140 p o = liftIO (peekByteOff p o)
+  pokeSTD140 p o a = liftIO (pokeByteOff p o a)
+
+instance UniformBlock Bool where
+  alignmentSTD140 _ = 4
+  sizeOfSTD140 _ = 4
+  peekSTD140 p o = liftIO (fmap toBool $ peekByteOff p o)
+  pokeSTD140 p o a = liftIO (pokeByteOff p o $ fromBool a)
+
+instance (Storable a,UniformBlock a) => UniformBlock (V2 a) where
+  alignmentSTD140 _ = alignmentSTD140 (Proxy :: Proxy a) * 2
+  sizeOfSTD140 _ = sizeOfSTD140 (Proxy :: Proxy a) * 2
+  peekSTD140 p o = liftIO (peekByteOff p o)
+  pokeSTD140 p o a = liftIO (pokeByteOff p o a)
+
+instance (Storable a,UniformBlock a) => UniformBlock (V3 a) where
+  alignmentSTD140 _ = alignmentSTD140 (Proxy :: Proxy a) * 4
+  sizeOfSTD140 _ = sizeOfSTD140 (Proxy :: Proxy a) * 4
+  peekSTD140 p o = liftIO (peekByteOff p o)
+  pokeSTD140 p o a = liftIO (pokeByteOff p o a)
+
+instance (Storable a,UniformBlock a) => UniformBlock (V4 a) where
+  alignmentSTD140 _ = alignmentSTD140 (Proxy :: Proxy a) * 4
+  sizeOfSTD140 _ = sizeOfSTD140 (Proxy :: Proxy a) * 4
+  peekSTD140 p o = liftIO (peekByteOff p o)
+  pokeSTD140 p o a = liftIO (pokeByteOff p o a)
+
+fromBool :: Bool -> Int32
+fromBool False = 0
+fromBool True = 1
+
+toBool :: Int32 -> Bool
+toBool 0 = False
+toBool _ = True
diff --git a/src/Graphics/Luminance/Shader/Uniform.hs b/src/Graphics/Luminance/Shader/Uniform.hs
--- a/src/Graphics/Luminance/Shader/Uniform.hs
+++ b/src/Graphics/Luminance/Shader/Uniform.hs
@@ -12,6 +12,10 @@
     -- * Uniform
     Uniform
   , U
+    -- * Uniform block
+  , UniformBlock
+  , UB(..)
   ) where
 
 import Graphics.Luminance.Core.Shader.Uniform
+import Graphics.Luminance.Core.Shader.UniformBlock
