diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,16 @@
+A tiny library to write to the terminal using Braille characters, a port of
+Adam Tauber's (asciimoo) work on python drawille to haskell.
+Copyright (C) 2014 Pedro Tacla Yamada
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/drawille.cabal b/drawille.cabal
new file mode 100644
--- /dev/null
+++ b/drawille.cabal
@@ -0,0 +1,45 @@
+Name:                   drawille
+Version:                0.1.0.0
+Category:               System
+Author:                 Pedro Yamada <tacla.yamada@gmail.com>
+Maintainer:             Pedro Yamada <tacla.yamada@gmail.com>
+License:                GPL-3
+License-File:           LICENSE
+Synopsis:               A port of asciimoo's drawille to haskell
+Description:            A tiny library for drawing with braille.
+Cabal-Version:          >= 1.10
+Build-Type:             Simple
+
+Library
+  Default-Language:     Haskell2010
+  HS-Source-Dirs:       src
+  GHC-Options:          -Wall
+  Exposed-Modules:      System.Drawille
+  Build-Depends:        base >=4 && <5
+                      , containers >=0.5 && <0.6
+
+Executable senoid
+  Default-Language:    Haskell2010
+  HS-Source-Dirs:       examples
+                      , src
+  Ghc-Options:          -Wall
+  Main-Is:              Senoid.hs
+  Build-Depends:        base >=4 && <5
+                      , containers >=0.5 && <0.6
+                      , AC-Angle >=1.0 && <1.1
+
+Test-Suite spec
+  Type:                 exitcode-stdio-1.0
+  Default-Language:     Haskell2010
+  Hs-Source-Dirs:       src
+                      , test
+  Ghc-Options:          -Wall
+  Main-Is:              Spec.hs
+  Build-Depends:        base >=4 && <5
+                      , hspec >=1.11 && <1.12
+                      , QuickCheck >=2.6 && <2.7
+                      , containers >=0.5 && <0.6
+
+Source-Repository head
+  Type:                 git
+  Location:             git://github.com/yamadapc/haskell-drawille
diff --git a/examples/Senoid.hs b/examples/Senoid.hs
new file mode 100644
--- /dev/null
+++ b/examples/Senoid.hs
@@ -0,0 +1,24 @@
+import Data.Angle
+import Data.List (union)
+import Control.Arrow ((&&&))
+import qualified System.Drawille as D
+
+main :: IO ()
+main = do
+    let s = D.fromList $ take 1800 senoid
+    putStr $ D.frame s
+
+    let cs = D.fromList $ union (take 1800 senoid) (take 1800 cosenoid)
+    putStr $ D.frame cs
+
+senoid :: Integral a => [(a, a)]
+senoid = map ((`div` 10) &&& (floor . (* 10) . rsine . fromIntegral)) [0..]
+
+cosenoid :: Integral a => [(a, a)]
+cosenoid = map ((`div` 10) &&& (floor . (* 10) . rcosine . fromIntegral)) [0..]
+
+rsine :: Double -> Double
+rsine = sine . radians . Degrees
+
+rcosine :: Double -> Double
+rcosine = cosine . radians . Degrees
diff --git a/src/System/Drawille.hs b/src/System/Drawille.hs
new file mode 100644
--- /dev/null
+++ b/src/System/Drawille.hs
@@ -0,0 +1,74 @@
+module System.Drawille ( empty -- drawing API
+                       , frame
+                       , get
+                       , set
+                       , unset
+                       , toggle
+                       , fromList
+
+                       , toPs -- utility functions
+                       , toPx
+                       , pxMap
+                       , pxOff
+                       ) where
+
+import qualified Data.Map as M (Map, empty, lookup, insertWith, keys)
+import Data.Bits ((.|.), (.&.), complement, xor)
+import Data.Char (chr)
+
+type Canvas = M.Map (Int, Int) Int
+
+pxMap :: Num a => [[a]]
+pxMap = [ [0x01, 0x08]
+        , [0x02, 0x10]
+        , [0x04, 0x20]
+        , [0x40, 0x80]
+        ]
+
+pxOff :: Num a => a
+pxOff = 0x2800
+
+toPx :: (Int, Int) -> Int
+toPx (px, py) = pxMap !! mod py 4 !! mod px 2
+
+toPs :: (Int, Int) -> (Int, Int)
+toPs (x, y) = (x `div` 2, y `div` 4)
+
+empty :: Canvas
+empty = M.empty
+
+frame :: Canvas -> String
+frame c = unlines $ map (row c mX) [minY..maxY]
+    where keys = M.keys c
+          mX = maximumMinimum $ map fst keys
+          (maxY, minY) = maximumMinimum $ map snd keys
+
+row :: Canvas -> (Int, Int) -> Int -> String
+row c (maxX, minX) y = map helper vs
+    where vs = map (\x -> M.lookup (x, y) c) [minX..maxX]
+          helper (Just v) = chr $ v + pxOff
+          helper Nothing  = ' '
+
+set :: Canvas -> (Int, Int) -> Canvas
+set c p = M.insertWith (.|.) (toPs p) (toPx p) c
+
+get :: Canvas -> (Int, Int) -> Bool
+get c p = case M.lookup (toPs p) c of
+            Just x  -> let px = toPx p in x .&. px == px
+            Nothing -> False
+
+unset :: Canvas -> (Int, Int) -> Canvas
+unset c p = M.insertWith (.&.) (toPs p) ((complement . toPx) p) c
+
+toggle :: Canvas -> (Int, Int) -> Canvas
+toggle c p = M.insertWith xor (toPs p) (toPx p) c
+
+fromList :: [(Int, Int)] -> Canvas
+fromList = foldr (flip set) empty
+
+maximumMinimum :: Ord a => [a] -> (a, a)
+maximumMinimum (x:xs) = foldr maxMin (x, x) xs
+maximumMinimum [] = error "Empty list"
+
+maxMin :: Ord a => a -> (a, a) -> (a, a)
+maxMin x (b, s) = (max x b, min x s)
diff --git a/test/Spec.hs b/test/Spec.hs
new file mode 100644
--- /dev/null
+++ b/test/Spec.hs
@@ -0,0 +1,1 @@
+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
