diff --git a/ghc-vis.cabal b/ghc-vis.cabal
--- a/ghc-vis.cabal
+++ b/ghc-vis.cabal
@@ -1,5 +1,5 @@
 name:               ghc-vis
-version:            0.4
+version:            0.5
 license:            BSD3
 license-file:       LICENSE
 category:           GHC, Debug, Development
@@ -20,7 +20,7 @@
                     how it can be used with GHCi's debugger:
                     <http://felsin9.de/nnis/ghc-vis/#combined-debugger>
 
-tested-with: GHC == 7.4.1, GHC == 7.4.2
+tested-with: GHC == 7.4.1, GHC == 7.4.2, GHC == 7.6.1
 data-files: ghci
 Extra-source-files: nonghci-test.hs
 
@@ -40,7 +40,7 @@
                  transformers,
                  gtk == 0.12.*,
                  cairo == 0.12.*,
-                 ghc-heap-view >= 0.3.0.2
+                 ghc-heap-view >= 0.3.0.3
   Hs-source-dirs: src/
   Ghc-options: -Wall -fno-warn-unused-do-bind
 
diff --git a/src/GHC/Vis.hs b/src/GHC/Vis.hs
--- a/src/GHC/Vis.hs
+++ b/src/GHC/Vis.hs
@@ -44,7 +44,11 @@
   )
   where
 
+#if __GLASGOW_HASKELL__ < 706
 import Prelude hiding (catch, error)
+#else
+import Prelude hiding (error)
+#endif
 
 import Graphics.UI.Gtk hiding (Box, Signal)
 import qualified Graphics.UI.Gtk.Gdk.Events as E
@@ -92,6 +96,15 @@
 defaultSize :: (Int, Int)
 defaultSize = (640, 480)
 
+zoomIncrement :: Double
+zoomIncrement = 1.25
+
+positionIncrement :: Double
+positionIncrement = 50
+
+bigPositionIncrement :: Double
+bigPositionIncrement = 200
+
 signalTimeout :: Int
 signalTimeout = 1000000
 
@@ -168,15 +181,139 @@
     return True
 
   onMotionNotify canvas False $ \e -> do
+    state <- readIORef visState
     modifyIORef visState (\s -> s {mousePos = (E.eventX e, E.eventY e)})
-    runCorrect move >>= \f -> f canvas
+
+    if dragging state
+    then do
+      let (oldX, oldY) = mousePos state
+          (deltaX, deltaY) = (E.eventX e - oldX, E.eventY e - oldY)
+          (oldPosX, oldPosY) = position state
+      modifyIORef visState (\s -> s {position = (oldPosX + deltaX, oldPosY + deltaY)})
+      widgetQueueDraw canvas
+    else
+      runCorrect move >>= \f -> f canvas
+
     return True
 
   onButtonPress canvas $ \e -> do
-    cf <- runCorrect click
-    when (E.eventButton e == LeftButton && E.eventClick e == SingleClick) cf
+    when (E.eventButton e == LeftButton && E.eventClick e == SingleClick) $
+      join $ runCorrect click
+
+    when (E.eventButton e == RightButton && E.eventClick e == SingleClick) $
+      modifyIORef visState (\s -> s {dragging = True})
+
+    when (E.eventButton e == MiddleButton && E.eventClick e == SingleClick) $ do
+      modifyIORef visState (\s -> s {zoomRatio = 1, position = (0, 0)})
+      widgetQueueDraw canvas
+
     return True
 
+  onButtonRelease canvas $ \e -> do
+    when (E.eventButton e == RightButton) $
+      modifyIORef visState (\s -> s {dragging = False})
+
+    return True
+
+  onScroll canvas $ \e -> do
+    state <- readIORef visState
+
+    when (E.eventDirection e == ScrollUp) $ do
+      let newZoomRatio = zoomRatio state * zoomIncrement
+      newPos <- zoomImage canvas state newZoomRatio (mousePos state)
+      modifyIORef visState (\s -> s {zoomRatio = newZoomRatio, position = newPos})
+
+    when (E.eventDirection e == ScrollDown) $ do
+      let newZoomRatio = zoomRatio state / zoomIncrement
+      newPos <- zoomImage canvas state newZoomRatio (mousePos state)
+      modifyIORef visState (\s -> s {zoomRatio = newZoomRatio, position = newPos})
+
+    widgetQueueDraw canvas
+    return True
+
+  onKeyPress window $ \e -> do
+    --putStrLn $ E.eventKeyName e
+
+    state <- readIORef visState
+
+    when (E.eventKeyName e `elem` ["plus", "Page_Up", "KP_Add"]) $ do
+      let newZoomRatio = zoomRatio state * zoomIncrement
+          (oldX, oldY) = position state
+          newPos = (oldX*zoomIncrement, oldY*zoomIncrement)
+      modifyIORef visState (\s -> s {zoomRatio = newZoomRatio, position = newPos})
+
+    when (E.eventKeyName e `elem` ["minus", "Page_Down", "KP_Subtract"]) $ do
+      let newZoomRatio = zoomRatio state / zoomIncrement
+          (oldX, oldY) = position state
+          newPos = (oldX/zoomIncrement, oldY/zoomIncrement)
+      modifyIORef visState (\s -> s {zoomRatio = newZoomRatio, position = newPos})
+
+    when (E.eventKeyName e `elem` ["0", "equal"]) $
+      modifyIORef visState (\s -> s {zoomRatio = 1, position = (0, 0)})
+
+    when (E.eventKeyName e `elem` ["Left", "h", "a"]) $
+      modifyIORef visState (\s ->
+        let (x,y) = position s
+            newX  = x + positionIncrement
+        in s {position = (newX, y)})
+
+    when (E.eventKeyName e `elem` ["Right", "l", "d"]) $
+      modifyIORef visState (\s ->
+        let (x,y) = position s
+            newX  = x - positionIncrement
+        in s {position = (newX, y)})
+
+    when (E.eventKeyName e `elem` ["Up", "k", "w"]) $
+      modifyIORef visState (\s ->
+        let (x,y) = position s
+            newY  = y + positionIncrement
+        in s {position = (x, newY)})
+
+    when (E.eventKeyName e `elem` ["Down", "j", "s"]) $
+      modifyIORef visState (\s ->
+        let (x,y) = position s
+            newY  = y - positionIncrement
+        in s {position = (x, newY)})
+
+    when (E.eventKeyName e `elem` ["H", "A"]) $
+      modifyIORef visState (\s ->
+        let (x,y) = position s
+            newX  = x + bigPositionIncrement
+        in s {position = (newX, y)})
+
+    when (E.eventKeyName e `elem` ["L", "D"]) $
+      modifyIORef visState (\s ->
+        let (x,y) = position s
+            newX  = x - bigPositionIncrement
+        in s {position = (newX, y)})
+
+    when (E.eventKeyName e `elem` ["K", "W"]) $
+      modifyIORef visState (\s ->
+        let (x,y) = position s
+            newY  = y + bigPositionIncrement
+        in s {position = (x, newY)})
+
+    when (E.eventKeyName e `elem` ["J", "S"]) $
+      modifyIORef visState (\s ->
+        let (x,y) = position s
+            newY  = y - bigPositionIncrement
+        in s {position = (x, newY)})
+
+    when (E.eventKeyName e `elem` ["space", "Return", "KP_Enter"]) $
+      join $ runCorrect click
+
+    when (E.eventKeyName e `elem` ["v"]) $
+      put SwitchSignal
+
+    when (E.eventKeyName e `elem` ["c"]) $
+      put ClearSignal
+
+    when (E.eventKeyName e `elem` ["u"]) $
+      put UpdateSignal
+
+    widgetQueueDraw canvas
+    return True
+
   widgetShowAll window
 
   reactThread <- forkIO $ react canvas window
@@ -235,7 +372,7 @@
 
 #ifdef GRAPH_VIEW
   where doSwitch = isGraphvizInstalled >>= \gvi -> if gvi
-          then modifyIORef visState (\s -> s {T.view = succN (T.view s)})
+          then modifyIORef visState (\s -> s {T.view = succN (T.view s), zoomRatio = 1, position = (0, 0)})
           else putStrLn "Cannot switch view: Graphviz not installed"
 
         succN GraphView = ListView
@@ -248,3 +385,25 @@
 runCorrect f = do
   s <- readIORef visState
   return $ f $ views !! fromEnum (T.view s)
+
+zoomImage :: WidgetClass w1 => w1 -> State -> Double -> T.Point -> IO T.Point
+zoomImage _canvas s newZoomRatio _mousePos@(_x', _y') = do
+  -- TODO: Mouse must stay at same spot
+
+  --E.Rectangle _ _ rw' rh' <- widgetGetAllocation canvas
+  --let (rw, rh) = (fromIntegral rw', fromIntegral rh')
+  --let (x,y) = (x' - rw / 2, y' - rh / 2)
+
+  let (oldPosX, oldPosY) = position s
+      zoom = newZoomRatio / zoomRatio s
+      newPos = (oldPosX * zoom, oldPosY * zoom)
+
+  return newPos
+
+-- Zoom into mouse, but only working from (0,0)
+-- newPos = ( oldPosX + x * zoomRatio s - x * newZoomRatio
+--          , oldPosY + y * zoomRatio s - y * newZoomRatio )
+
+-- Zoom into center:
+-- newPos = ( oldPosX * zoomIncrement
+--          , oldPosY * zoomIncrement )
diff --git a/src/GHC/Vis/Internal.hs b/src/GHC/Vis/Internal.hs
--- a/src/GHC/Vis/Internal.hs
+++ b/src/GHC/Vis/Internal.hs
@@ -1,4 +1,4 @@
-{-# LANGUAGE MagicHash, DeriveDataTypeable, NoMonomorphismRestriction, RankNTypes #-}
+{-# LANGUAGE CPP, MagicHash, DeriveDataTypeable, NoMonomorphismRestriction, RankNTypes #-}
 
 {- |
    Module      : GHC.Vis.Internal
@@ -16,7 +16,9 @@
   )
   where
 
+#if __GLASGOW_HASKELL__ < 706
 import Prelude hiding (catch)
+#endif
 
 import GHC.Vis.Types
 import GHC.HeapView hiding (name, pkg, modl, fun, arrWords)
@@ -392,13 +394,13 @@
       sPtrs = if null tPtrs then [Unnamed ""] else Unnamed "[" : tPtrs ++ [Unnamed "]"]
   return $ Function (infixFix name) : fPtr ++ sPtrs
 
---parseInternal b (APStackClosure _ fun pl) = do
---  name <- getSetName b
---  fPtr <- contParse fun
---  pPtrs <- mapM contParse $ reverse pl
---  let tPtrs = intercalate [Unnamed ","] pPtrs
---      sPtrs = if null tPtrs then [Unnamed ""] else Unnamed "[" : tPtrs ++ [Unnamed "]"]
---  return $ Thunk (infixFix name) : fPtr ++ sPtrs
+parseInternal b (APStackClosure _ fun pl) = do
+  name <- getSetName b
+  fPtr <- contParse fun
+  pPtrs <- mapM contParse $ reverse pl
+  let tPtrs = intercalate [Unnamed ","] pPtrs
+      sPtrs = if null tPtrs then [Unnamed ""] else Unnamed "[" : tPtrs ++ [Unnamed "]"]
+  return $ Thunk (infixFix name) : fPtr ++ sPtrs
 
 parseInternal _ (MVarClosure _ qHead qTail qValue)
    = do cHead <- liftM mbParens $ contParse qHead
@@ -523,8 +525,8 @@
 showClosure PAPClosure{}
   = "PAP"
 
---showClosure APStackClosure{}
---  = "APStack"
+showClosure APStackClosure{}
+  = "APStack"
 
 showClosure BCOClosure{}
   = "BCO"
diff --git a/src/GHC/Vis/Types.hs b/src/GHC/Vis/Types.hs
--- a/src/GHC/Vis/Types.hs
+++ b/src/GHC/Vis/Types.hs
@@ -7,6 +7,7 @@
 
  -}
 module GHC.Vis.Types (
+  Point,
   DrawFunction,
   Signal(..),
   View(..),
@@ -55,8 +56,12 @@
 
 -- | The global state used for the visualizations.
 data State = State
-  { mousePos :: Point -- ^ Current position of the mouse, updated with every movement
-  , view     :: ViewType -- ^ What kind of visualization is currently running
+  { mousePos   :: Point    -- ^ Current position of the mouse, updated with every movement
+  , view       :: ViewType -- ^ What kind of visualization is currently running
+  , zoomRatio  :: Double   -- ^ How much the window is zoomed in
+  , position   :: Point    -- ^ Current position in the zoom
+  , dragging   :: Bool     -- ^ Whether the mouse is dragging
+  , wasDragged :: Bool     -- ^ Whether the mouse was actually dragged
   }
 
 -- | An entry in a 'HeapMap', consisting of an identifier and a parsed GHC heap entry
diff --git a/src/GHC/Vis/View/Common.hs b/src/GHC/Vis/View/Common.hs
--- a/src/GHC/Vis/View/Common.hs
+++ b/src/GHC/Vis/View/Common.hs
@@ -1,4 +1,4 @@
-{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE CPP, ScopedTypeVariables #-}
 {- |
    Module      : GHC.Vis.View.Common
    Copyright   : (c) Dennis Felsing
@@ -15,7 +15,9 @@
   )
   where
 
+#if __GLASGOW_HASKELL__ < 706
 import Prelude hiding (catch)
+#endif
 
 import Control.Concurrent
 import Control.DeepSeq
@@ -39,7 +41,7 @@
 
 -- | Internal state of the visualization
 visState :: IORef State
-visState = unsafePerformIO $ newIORef $ State (0, 0) ListView
+visState = unsafePerformIO $ newIORef $ State (0, 0) ListView 1 (0, 0) False False
 
 -- | All the visualized boxes
 visBoxes :: MVar [(Box, String)]
diff --git a/src/GHC/Vis/View/Graph.hs b/src/GHC/Vis/View/Graph.hs
--- a/src/GHC/Vis/View/Graph.hs
+++ b/src/GHC/Vis/View/Graph.hs
@@ -1,4 +1,4 @@
-{-# LANGUAGE RankNTypes, ScopedTypeVariables #-}
+{-# LANGUAGE CPP, RankNTypes, ScopedTypeVariables #-}
 {- |
    Module      : GHC.Vis.View.Graph
    Copyright   : (c) Dennis Felsing
@@ -15,7 +15,9 @@
   )
   where
 
+#if __GLASGOW_HASKELL__ < 706
 import Prelude hiding (catch)
+#endif
 
 import Graphics.UI.Gtk hiding (Box, Signal, Rectangle, Object)
 import qualified Graphics.UI.Gtk as Gtk
@@ -73,6 +75,8 @@
 
 draw :: State -> Int -> Int -> Render [(Object Int, Rectangle)]
 draw s rw2 rh2 = do
+  vS <- liftIO $ readIORef visState
+
   -- Line widths don't count to size, let's add a bit
   let rw = 0.97 * fromIntegral rw2
       rh = 0.97 * fromIntegral rh2
@@ -81,10 +85,10 @@
       size@(_,_,sw,sh) = totalSize s
 
   -- Proportional scaling
-      sx = min (rw / sw) (rh / sh)
-      sy = sx
-      ox = 0.5 * fromIntegral rw2
-      oy = 0.5 * fromIntegral rh2
+      (sx,sy) = (zoomRatio vS * min (rw / sw) (rh / sh), sx)
+      (ox1,oy1) = (0.5 * fromIntegral rw2, 0.5 * fromIntegral rh2)
+      (ox2,oy2) = position vS
+      (ox,oy) = (ox1 + ox2, oy1 + oy2)
 
   translate ox oy
   scale sx sy
diff --git a/src/GHC/Vis/View/List.hs b/src/GHC/Vis/View/List.hs
--- a/src/GHC/Vis/View/List.hs
+++ b/src/GHC/Vis/View/List.hs
@@ -119,6 +119,8 @@
   pos <- mapM height objs
   widths <- mapM (mapM width) objs
 
+  vS <- liftIO $ readIORef visState
+
   let rw = 0.98 * fromIntegral rw2
       rh = fromIntegral rh2
 
@@ -128,15 +130,15 @@
       sw = maximum widths2
       sh = sum (map (+ 30) pos) - 15
 
-      sx = min (rw / sw) (rh / sh)
-      sy = sx
-      ox = 0
-      oy = 0
+      (sx,sy) = (zoomRatio vS * min (rw / sw) (rh / sh), sx)
+      (ox2,oy2) = position vS
+      (ox,oy) = (ox2 - (zoomRatio vS - 1) * rw / 2, oy2 - (zoomRatio vS - 1) * rh / 2)
 
+  translate ox oy
   scale sx sy
 
   let rpos = scanl (\a b -> a + b + 30) 30 pos
-  result <- mapM (drawEntry s maxNameWidth) (zip3 objs rpos names)
+  result <- mapM (drawEntry s maxNameWidth 0) (zip3 objs rpos names)
 
   return $ map (\(o, (x,y,w,h)) -> (o, (x*sx+ox,y*sy+oy,w*sx,h*sy))) $ concat result
 
@@ -186,10 +188,10 @@
   let objs = zipWith (\(x,y) z -> (x,y,z)) boxes os
   modifyIORef state (\s -> s {objects = objs, hover = Nothing})
 
-drawEntry :: State -> Double -> ([VisObject], Double, String) -> Render [(String, Rectangle)]
-drawEntry s nameWidth (obj, pos, name) = do
+drawEntry :: State -> Double -> Double -> ([VisObject], Double, String) -> Render [(String, Rectangle)]
+drawEntry s nameWidth xPos (obj, pos, name) = do
   save
-  translate 0 pos
+  translate xPos pos
   moveTo 0 0
   drawBox s $ Unnamed name
   translate nameWidth 0
