diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,31 @@
+Copyright (c) 2009, Eugene Kirpichov
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+
+    * The names of contributors may not be used to endorse or promote
+      products derived from this software without specific prior
+      written permission. 
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,8 @@
+#!/usr/bin/env runghc
+
+module Main where
+
+import Distribution.Simple
+
+main :: IO ()
+main = defaultMain
diff --git a/Tools/StatePlot.hs b/Tools/StatePlot.hs
new file mode 100644
--- /dev/null
+++ b/Tools/StatePlot.hs
@@ -0,0 +1,113 @@
+module Main where
+
+import Control.Monad.Writer
+import Control.Monad.State
+import qualified Data.Map as M
+import Data.List
+import Data.Ord
+import Data.Function
+import Debug.Trace
+import Data.Maybe
+import Data.Time
+import Data.Time.Parse
+import System (getArgs)
+import qualified Data.ByteString.Char8 as B
+import Graphics.Rendering.Chart
+import Graphics.Rendering.Chart.Renderable
+import Graphics.Rendering.Chart.Gtk
+import qualified Graphics.Rendering.Cairo as C
+import Data.Colour
+import Data.Colour.Names
+
+data Event = Event {time :: LocalTime, track :: String, edge :: Edge} deriving (Show)
+data Edge = Begin { color :: String } | End deriving (Show)
+
+getArg :: String -> String -> [String] -> String
+getArg name def args = case [(k,v) | (k,v) <- zip args (tail args), k==("-"++name)] of
+  (_,v):_ -> v
+  _       -> def
+
+parse :: (B.ByteString -> (LocalTime, B.ByteString)) -> B.ByteString -> Event
+parse parseTime s = Event { time = ts, track = B.unpack $ B.tail track', edge = edge }
+  where
+    (ts, s') = parseTime s
+    (track', color) = B.break (==' ') (B.tail s')
+    edge = case (B.head track') of
+      '>' -> Begin (B.unpack (B.tail color))
+      '<' -> End
+
+diffToMillis :: LocalTime -> LocalTime -> Double
+diffToMillis t2 t1 = fromIntegral (truncate (1000000*d)) / 1000
+ where d = diffUTCTime (localTimeToUTC utc t2) (localTimeToUTC utc t1)
+
+renderEvents :: Double -> Double -> [Event] -> Renderable ()
+renderEvents bandHeight ticksIntervalMs es = Renderable {minsize = return (0,0), render = render'}
+  where 
+    events = sortBy (comparing time) es
+    minTime = time $ head events
+    maxTime = time $ last events
+    time2ms t = diffToMillis t minTime
+    rangeMs = time2ms maxTime
+    ticks = takeWhile (<rangeMs) $ iterate (+ticksIntervalMs) 0
+    tracks = sortBy (comparing (time . head)) . groupBy ((==) `on` track) . sortBy (comparing track) $ events
+
+    maybeM :: (Monad m) => (a -> m b) -> Maybe a -> m ()
+    maybeM f Nothing  = return ()
+    maybeM f (Just x) = f x >> return ()
+
+    bars track = execWriter $ evalStateT (mapM step track) Nothing
+      where
+        step (Event t _ (Begin c)) = do
+          get >>= maybeM (\(t0,c0) -> tell [(time2ms t0, time2ms t, c0)])
+          put (Just (t,c))
+        step (Event t _ End      ) = do
+          get >>= maybeM (\(t0,c0) -> tell [(time2ms t0, time2ms t, c0)])
+          put Nothing
+
+    render' (w,h) = do
+      let ms2x ms = 10 + ms / rangeMs * (w - 10)
+      let time2x t = ms2x (time2ms t)
+      let numTracks = length tracks
+      let yStep = (h-10) / fromIntegral (numTracks+1)
+      let track2y i = fromIntegral (i+1) * yStep - bandHeight/2
+      let drawTick ms = do {
+          setLineStyle $ solidLine 1 (opaque black)
+        ; moveTo $ Point (ms2x ms) (h-10)
+        ; lineTo $ Point (ms2x ms) (h-5)
+        ; c $ C.stroke
+        }
+      let drawBar i (ms1, ms2, color) = do {
+          setLineStyle $ solidLine 1 transparent
+        ; setFillStyle $ solidFillStyle $ opaque $ fromMaybe (error "unknown color") (readColourName color)
+        ; fillPath (rectPath $ Rect (Point (ms2x ms1) (track2y i)) (Point (ms2x ms2) (track2y i + bandHeight)))
+        }
+      let drawTrack (i, es) = mapM_ (drawBar i) (bars es)
+
+      setFillStyle $ solidFillStyle (opaque white)
+      fillPath $ rectPath $ Rect (Point 0 0) (Point w h)
+      setLineStyle $ solidLine 1 (opaque black)
+      moveTo (Point 10 (h-10))
+      lineTo (Point w  (h-10))
+      c $ C.stroke
+      moveTo (Point 10 (h-10))
+      lineTo (Point 10 0)
+      c $ C.stroke
+      mapM_ drawTick ticks
+      mapM_ drawTrack $ zip [0..] tracks
+      return nullPickFn
+
+main = do
+  args <- getArgs
+  let (w,h) = (read $ getArg "w" "640" args, read $ getArg "h" "480" args)
+  let bandHeight = read $ getArg "bh" "5" args
+  let ticksIntervalMs = read $ getArg "tickInterval" "10" args
+  let timeFormat = getArg "tf" "%Y-%m-%d %H:%M:%OS" args
+  let parseTime = fromMaybe (error "Invalid time") . strptime (B.pack timeFormat)
+  let outPNG = getArg "o" "" args
+  input <- B.getContents
+  let events = parse parseTime `map` B.lines input
+  let pic = renderEvents bandHeight ticksIntervalMs events
+  case outPNG of
+    "" -> renderableToWindow pic w h
+    f  -> const () `fmap` renderableToPNGFile pic w h outPNG
+
diff --git a/splot.cabal b/splot.cabal
new file mode 100644
--- /dev/null
+++ b/splot.cabal
@@ -0,0 +1,27 @@
+Name: splot
+Version: 0.1.0
+License: BSD3
+License-file: LICENSE
+Copyright: Eugene Kirpichov, 2010
+Author: Eugene Kirpichov <ekirpichov@gmail.com>
+Maintainer: Eugene Kirpichov <ekirpichov@gmail.com>
+Synopsis: A tool for visualizing the lifecycle of many concurrent multi-staged processes.
+Description: A tool for visualizing the lifecycle of many concurrent multi-staged processes.
+  Each process has a name, it starts at a point in time, ends at a point in time, and at some
+  points in time it changes colour.
+Category: Graphics
+Cabal-Version: >= 1.6
+Build-Type: Simple
+
+flag splitbase
+  description: Choose the new smaller, split-up base package.
+
+executable splot
+  if flag(splitbase)
+    Build-Depends: base >= 3 && < 5
+  else
+    Build-Depends: base < 3
+
+  Build-Depends: cairo, bytestring, bytestring-lexing, strptime >= 0.1.5, time, 
+                 containers, colour, haskell98, Chart, mtl
+  Main-Is: Tools/StatePlot.hs
