diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,101 @@
+# Change Log
+
+## [v0.4.0.0](https://github.com/diagrams/force-layout/tree/v0.4.0.0) (2015-04-19)
+
+* Release to accompany `diagrams-1.3`
+
+## [v0.3.0.11](https://github.com/diagrams/force-layout/tree/v0.3.0.11) (2015-04-03)
+
+[Full Changelog](https://github.com/diagrams/force-layout/compare/v0.3.0.10...v0.3.0.11)
+
+## [v0.3.0.10](https://github.com/diagrams/force-layout/tree/v0.3.0.10) (2015-03-12)
+
+[Full Changelog](https://github.com/diagrams/force-layout/compare/v0.3.0.9...v0.3.0.10)
+
+**Closed issues:**
+
+- cabal file category should be Physics [\#6](https://github.com/diagrams/force-layout/issues/6)
+
+**Merged pull requests:**
+
+- Bump base upper bound [\#5](https://github.com/diagrams/force-layout/pull/5) ([bgamari](https://github.com/bgamari))
+
+## [v0.3.0.9](https://github.com/diagrams/force-layout/tree/v0.3.0.9) (2015-01-13)
+
+[Full Changelog](https://github.com/diagrams/force-layout/compare/v0.3.0.8...v0.3.0.9)
+
+## [v0.3.0.8](https://github.com/diagrams/force-layout/tree/v0.3.0.8) (2014-11-17)
+
+[Full Changelog](https://github.com/diagrams/force-layout/compare/v0.3.0.7...v0.3.0.8)
+
+**Merged pull requests:**
+
+- Linear [\#3](https://github.com/diagrams/force-layout/pull/3) ([cchalmers](https://github.com/cchalmers))
+
+- Bump lens upper version bounds [\#4](https://github.com/diagrams/force-layout/pull/4) ([RyanGlScott](https://github.com/RyanGlScott))
+
+## [v0.3.0.7](https://github.com/diagrams/force-layout/tree/v0.3.0.7) (2014-08-22)
+
+[Full Changelog](https://github.com/diagrams/force-layout/compare/v0.3.0.6...v0.3.0.7)
+
+**Closed issues:**
+
+- Needs Hackage release for lens-4.3 [\#2](https://github.com/diagrams/force-layout/issues/2)
+
+## [v0.3.0.6](https://github.com/diagrams/force-layout/tree/v0.3.0.6) (2014-08-20)
+
+[Full Changelog](https://github.com/diagrams/force-layout/compare/v0.3.0.5...v0.3.0.6)
+
+## [v0.3.0.5](https://github.com/diagrams/force-layout/tree/v0.3.0.5) (2014-06-12)
+
+[Full Changelog](https://github.com/diagrams/force-layout/compare/0_3_0_4...v0.3.0.5)
+
+**Merged pull requests:**
+
+- Make it compilable with GHC HEAD \(v7.9\) [\#1](https://github.com/diagrams/force-layout/pull/1) ([ggreif](https://github.com/ggreif))
+
+## [v0.3.0.4]() (2 June 2014)
+
+  - Allow lens-4.2
+
+## [v0.3.0.3]() (19 March 2014)
+
+  - Allow lens-4.1
+
+## [v0.3.0.2] () (9 March 2014)
+
+  - Allow vector-space-points-0.2
+
+## [v0.3.0.1] () (12 February 2014)
+
+  - Allow lens-4.0
+
+## [v0.3]() [1 January 2014]
+
+  - Use lenses for ForceLayoutOpts
+
+## [v0.2.0.1] () (22 Dec 20130
+
+  - Allow base-4.7
+
+## [v0.2] () (5 December 2012)
+
+  - Switch from fclabels to lens.
+
+    Note this requires a major version bump because the accessors for
+    Particle and Ensemble are exported, and they have completely
+    different types.  But the API has not otherwise changed.
+
+## [v0.1.0.2:] () (23 August 2012)
+
+  - Bump upper bounds to allow base-4.6 and containers-0.5
+
+## [v0.1.0.1] () (21 December 2011)
+
+  - Bump fclabels upper bound to allow 1.1.0
+
+## [v0.1.0.0] () (20 December 2011)
+
+  - Initial release
+
+\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)*
diff --git a/CHANGES b/CHANGES
deleted file mode 100644
--- a/CHANGES
+++ /dev/null
@@ -1,74 +0,0 @@
-* 0.3.0.11 (2 April 2015)
-
- - allow lens-4.9
- - allow vector-space-0.10
-
-* 0.3.0.10 (12 March 2015)
-
-- change category to Physics
-
-* 0.3.0.9 (13 Jan 2015)
-
-- Allow vector-space-0.9
-- Allow lens-4.7
-
-* 0.3.0.8 (17 November 2014)
-
-- Allow lens-4.6
-
-* 0.3.0.7 (22 August 2014)
-
-- Allow lens-4.4
-
-* 0.3.0.6 (20 August 2014)
-
-  - Allow lens-4.3
-
-* 0.3.0.5 (12 June 2014)
-
-  - Add extension to allow building with GHC HEAD (7.9) (thanks to
-    Gabor Greif)
-
-* 0.3.0.4: 2 June 2014
-
-  - Allow lens-4.2
-
-* 0.3.0.3: 19 March 2014
-
-  - Allow lens-4.1
-
-* 0.3.0.2: 9 March 2014
-
-  - Allow vector-space-points-0.2
-
-* 0.3.0.1: 12 February 2014
-
-  - Allow lens-4.0
-
-* 0.3: 1 January 2014
-
-  - Use lenses for ForceLayoutOpts
-
-* 0.2.0.1: 22 Dec 2013
-
-  - Allow base-4.7
-
-* 0.2: 5 December 2012
-
-  - Switch from fclabels to lens.
-
-    Note this requires a major version bump because the accessors for
-    Particle and Ensemble are exported, and they have completely
-    different types.  But the API has not otherwise changed.
-
-* 0.1.0.2: 23 August 2012
-
-  - Bump upper bounds to allow base-4.6 and containers-0.5
-
-* 0.1.0.1: 21 December 2011
-
-  - Bump fclabels upper bound to allow 1.1.0
-
-* 0.1.0.0: 20 December 2011
-
-  - Initial release
diff --git a/force-layout.cabal b/force-layout.cabal
--- a/force-layout.cabal
+++ b/force-layout.cabal
@@ -1,5 +1,5 @@
 name:                force-layout
-version:             0.3.0.11
+version:             0.4.0.0
 synopsis:            Simple force-directed layout
 description:         Simulation engine for doing simple force-based layout,
                      /e.g./ for trees or graphs.  See the diagrams-contrib package
@@ -13,17 +13,17 @@
 category:            Physics
 build-type:          Simple
 cabal-version:       >=1.10
-extra-source-files:  CHANGES
+extra-source-files:  CHANGELOG.md
+tested-with:         GHC == 7.6.3, GHC == 7.8.4, GHC == 7.10.1
 source-repository head
   type:     git
   location: git://github.com/diagrams/force-layout.git
 
 library
   exposed-modules:     Physics.ForceLayout
-  build-depends:       base >= 4.2 && < 4.8,
-                       vector-space >=0.7 && <0.11,
-                       vector-space-points >= 0.1.1 && < 0.3,
-                       lens >= 3 && < 4.10,
+  build-depends:       base >= 4.2 && < 4.9,
+                       linear >= 1.10 && < 1.19,
+                       lens >= 3.0 && < 4.10,
                        containers >=0.4 && < 0.6,
                        data-default-class >= 0.0.1 && < 0.1
   hs-source-dirs:      src
diff --git a/src/Physics/ForceLayout.hs b/src/Physics/ForceLayout.hs
--- a/src/Physics/ForceLayout.hs
+++ b/src/Physics/ForceLayout.hs
@@ -1,8 +1,6 @@
-{-# LANGUAGE FlexibleContexts     #-}
-{-# LANGUAGE ScopedTypeVariables  #-}
-{-# LANGUAGE TemplateHaskell      #-}
-{-# LANGUAGE TypeFamilies         #-}
-{-# LANGUAGE UndecidableInstances #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE TemplateHaskell     #-}
+{-# LANGUAGE TypeFamilies        #-}
 
 -----------------------------------------------------------------------------
 -- |
@@ -16,12 +14,14 @@
 --
 -- To use, just create an 'Ensemble' like so:
 --
+-- > import           Control.Lens        ((&), (.~))
+-- > import           Data.Default.Class  (def)
+-- > import qualified Data.Map            as M
+-- > import           Linear.Affine
+-- > import           Linear.V2
 -- > import           Physics.ForceLayout
--- > import qualified Data.Map              as M
--- > import           Data.AffineSpace.Point
--- > import           Data.Default (def)
 -- >
--- > e :: Ensemble (Double, Double)
+-- > e :: Ensemble V2 Double
 -- > e = Ensemble [ (edges,    hookeForce 0.05 4)
 -- >              , (allPairs, coulombForce 1)
 -- >              ]
@@ -29,7 +29,7 @@
 -- >   where edges       = [(1,2), (2,3), (2,5), (3,5), (3,4), (4,5)]
 -- >         allPairs    = [(x,y) | x <- [1..4], y <- [x+1..5]]
 -- >         particleMap = M.fromList . zip [1..]
--- >                     . map (initParticle . P)
+-- >                     . map (initParticle . P . uncurry V2)
 -- >                     $ [ (2.0, 3.1), (6.3, 7.2)
 -- >                       , (0.3, 4.2), (1.6, -1.1)
 -- >                       , (4.8, 2.9)
@@ -39,7 +39,7 @@
 -- all intermediate states) or 'forceLayout' (to get only the ending
 -- state):
 --
--- > e' :: Ensemble (Double, Double)
+-- > e' :: Ensemble V2 Double
 -- > e' = forceLayout (def & damping     .~ 0.8
 -- >                       & energyLimit .~ Just 0.001
 -- >                       & stepLimit   .~ Nothing
@@ -83,34 +83,34 @@
 
        ) where
 
-import           Data.AffineSpace
-import           Data.AffineSpace.Point
 import           Data.Default.Class
-import           Data.Foldable          (foldMap)
-import qualified Data.Foldable          as F
-import qualified Data.Map               as M
+import qualified Data.Foldable      as F
+import qualified Data.Map           as M
 import           Data.Monoid
-import           Data.VectorSpace       hiding (Sum)
 
 import           Control.Lens
 
+import           Linear.Affine
+import           Linear.Metric
+import           Linear.Vector
+
 ------------------------------------------------------------
 --  Particles
 ------------------------------------------------------------
 
 -- | A particle has a current position, current velocity, and current
 --   force acting on it.
-data Particle v = Particle { _pos   :: Point v
-                           , _vel   :: v
-                           , _force :: v
-                           }
-  deriving (Eq, Show)
+data Particle v n = Particle { _pos   :: Point v n
+                             , _vel   :: v n
+                             , _force :: v n
+                             }
+  deriving Eq
 
 makeLenses ''Particle
 
 -- | Create an initial particle at rest at a particular location.
-initParticle :: AdditiveGroup v => Point v -> Particle v
-initParticle p = Particle p zeroV zeroV
+initParticle :: (Additive v, Num n) => Point v n -> Particle v n
+initParticle p = Particle p zero zero
 
 ------------------------------------------------------------
 --  Ensembles
@@ -127,9 +127,9 @@
 --   particles, and a list of forces that are operative.  Each force
 --   has a list of edges to which it applies, and is represented by a
 --   function giving the force between any two points.
-data Ensemble v = Ensemble { _forces    :: [([Edge], Point v -> Point v -> v)]
-                           , _particles :: M.Map PID (Particle v)
-                           }
+data Ensemble v n = Ensemble { _forces    :: [([Edge], Point v n -> Point v n -> v n)]
+                             , _particles :: M.Map PID (Particle v n)
+                             }
 
 makeLenses ''Ensemble
 
@@ -139,25 +139,28 @@
 
 -- | Simulate one time step for an entire ensemble, with the given
 --   damping factor.
-ensembleStep :: VectorSpace v => Scalar v -> Ensemble v -> Ensemble v
+ensembleStep :: (Additive v, Num n) => n -> Ensemble v n -> Ensemble v n
 ensembleStep d = (over particles . M.map) (particleStep d) . recalcForces
 
 -- | Simulate one time step for a particle (assuming the force acting
 --   on it has already been computed), with the given damping factor.
-particleStep :: VectorSpace v => Scalar v -> Particle v -> Particle v
+particleStep :: (Additive v, Num n) => n -> Particle v n -> Particle v n
 particleStep d = stepPos . stepVel
   where stepVel p = vel .~ (d *^ (p^.vel ^+^ p^.force)) $ p
         stepPos p = pos %~ (.+^ p^.vel) $ p
 
 -- | Recalculate all the forces acting in the next time step of an
 --   ensemble.
-recalcForces :: forall v. AdditiveGroup v => Ensemble v -> Ensemble v
+recalcForces :: (Additive v, Num n) => Ensemble v n -> Ensemble v n
 recalcForces = calcForces . zeroForces
-  where zeroForces = (particles %~) . M.map $ force .~ zeroV
+  where zeroForces = (particles %~) . M.map $ force .~ zero
         calcForces (Ensemble fs ps)
           = Ensemble fs
-            (ala Endo foldMap (concatMap (\(es, f) -> (map (mkForce f) es)) fs) ps)
-        mkForce :: (Point v -> Point v -> v) -> Edge -> M.Map Int (Particle v) -> M.Map Int (Particle v)
+            (ala Endo F.foldMap (concatMap (\(es, f) -> (map (mkForce f) es)) fs) ps)
+        -- mkForce :: (Point v n -> Point v n -> v n)
+        --         -> Edge
+        --         -> M.Map Int (Particle v n)
+        --         -> M.Map Int (Particle v n)
         mkForce f (i1, i2) m
           = case (M.lookup i1 m, M.lookup i2 m) of
               (Just p1, Just p2) ->
@@ -167,37 +170,37 @@
               _                  -> m
 
 -- | Compute the total kinetic energy of an ensemble.
-kineticEnergy :: (InnerSpace v, Num (Scalar v)) => Ensemble v -> Scalar v
-kineticEnergy = ala Sum F.foldMap . fmap (magnitudeSq . view vel) . view particles
+kineticEnergy :: (Metric v, Num n) => Ensemble v n -> n
+kineticEnergy = ala Sum F.foldMap . fmap (quadrance . view vel) . view particles
 
 ------------------------------------------------------------
 --  Simulation
 ------------------------------------------------------------
 
 -- | Options for customizing a simulation.
-data ForceLayoutOpts v =
+data ForceLayoutOpts n =
   FLOpts
-  { _damping     :: Scalar v           -- ^ Damping factor to be
-                                       --   applied at each step.
-                                       --   Should be between 0 and 1.
-                                       --   The default is 0.8.
-  , _energyLimit :: Maybe (Scalar v)   -- ^ Kinetic energy below which
-                                       --   simulation should stop.
-                                       --   If @Nothing@, pay no
-                                       --   attention to kinetic
-                                       --   energy.  The default is
-                                       --   @Just 0.001@.
-  , _stepLimit   :: Maybe Int          -- ^ Maximum number of
-                                       --   simulation steps.  If
-                                       --   @Nothing@, pay no
-                                       --   attention to the number of
-                                       --   steps.  The default is
-                                       --   @Just 1000@.
+  { _damping     :: n         -- ^ Damping factor to be
+                              --   applied at each step.
+                              --   Should be between 0 and 1.
+                              --   The default is 0.8.
+  , _energyLimit :: Maybe n   -- ^ Kinetic energy below which
+                              --   simulation should stop.
+                              --   If @Nothing@, pay no
+                              --   attention to kinetic
+                              --   energy.  The default is
+                              --   @Just 0.001@.
+  , _stepLimit   :: Maybe Int -- ^ Maximum number of
+                              --   simulation steps.  If
+                              --   @Nothing@, pay no
+                              --   attention to the number of
+                              --   steps.  The default is
+                              --   @Just 1000@.
   }
 
 makeLenses ''ForceLayoutOpts
 
-instance Fractional (Scalar v) => Default (ForceLayoutOpts v) where
+instance Fractional n => Default (ForceLayoutOpts n) where
   def = FLOpts
         { _damping     = 0.8
         , _energyLimit = Just 0.001
@@ -210,8 +213,8 @@
 --   be infinite, if a 'stepLimit' is not specified and either the
 --   kinetic energy never falls below the specified threshold, or no
 --   energy threshold is specified.
-simulate :: (InnerSpace v, Ord (Scalar v), Num (Scalar v))
-         => ForceLayoutOpts v -> Ensemble v -> [Ensemble v]
+simulate :: (Metric v, Num n, Ord n)
+         => ForceLayoutOpts n -> Ensemble v n -> [Ensemble v n]
 simulate opts e
   = (e:)
   . takeWhile (maybe (const True) (<) (opts ^. energyLimit) . kineticEnergy)
@@ -225,8 +228,8 @@
 --   given), or the ensemble that results after a number of steps
 --   equal to the 'stepLimit' (if given), whichever comes first.
 --   Otherwise @forceLayout@ will not terminate.
-forceLayout :: (InnerSpace v, Ord (Scalar v), Num (Scalar v))
-            => ForceLayoutOpts v -> Ensemble v -> Ensemble v
+forceLayout :: (Metric v, Num n, Ord n)
+            => ForceLayoutOpts n -> Ensemble v n -> Ensemble v n
 forceLayout opts = last . simulate opts
 
 ------------------------------------------------------------
@@ -237,21 +240,18 @@
 --   multiple of the unit vector in the direction from @p1@ to @p2@,
 --   given a function @f@ which computes the force's magnitude as a
 --   function of the distance between the points.
-distForce :: (InnerSpace v, Floating (Scalar v))
-          => (Scalar v -> Scalar v) -> Point v -> Point v -> v
+distForce :: (Metric v, Floating n) => (n -> n) -> Point v n -> Point v n -> v n
 distForce f p1 p2 = withLength (f (distance p1 p2)) (p2 .-. p1)
-  where withLength s v = s *^ normalized v
+  where withLength s v = s *^ signorm v
 
 -- | @hookeForce k l p1 p2@ computes the force on @p1@, assuming that
 --   @p1@ and @p2@ are connected by a spring with equilibrium length @l@
 --   and spring constant @k@.
-hookeForce :: (InnerSpace v, Floating (Scalar v))
-           => Scalar v -> Scalar v -> Point v -> Point v -> v
+hookeForce :: (Metric v, Floating n) => n -> n -> Point v n -> Point v n -> v n
 hookeForce k l = distForce (\d -> k * (d - l))
 
 -- | @coulombForce k@ computes the electrostatic repulsive force
 --   between two charged particles, with constant of proportionality
 --   @k@.
-coulombForce :: (InnerSpace v, Floating (Scalar v))
-             => Scalar v -> Point v -> Point v -> v
-coulombForce k = distForce (\d -> -k * 1/(d*d))
+coulombForce :: (Metric v, Floating n) => n -> Point v n -> Point v n -> v n
+coulombForce k = distForce (\d -> -k/(d*d))
