diff --git a/README b/README
new file mode 100644
--- /dev/null
+++ b/README
@@ -0,0 +1,12 @@
+hsc3-data - data functions for SC3 related work
+-----------------------------------------------
+
+[hs]: http://haskell.org/
+[sc3]: http://audiosynth.com/
+[hsc3]: http://rd.slavepianos.org/?t=hsc3
+[hsc3-data]: http://rd.slavepianos.org/?t=hsc3-data
+
+© [rohan drape][rd], 2013-2014, [gpl]
+
+[rd]: http://rd.slavepianos.org/
+[gpl]: http://gnu.org/copyleft/
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,3 @@
+#!/usr/bin/env runhaskell
+import Distribution.Simple
+main = defaultMain
diff --git a/Sound/SC3/Data/Math/Bourke.hs b/Sound/SC3/Data/Math/Bourke.hs
new file mode 100644
--- /dev/null
+++ b/Sound/SC3/Data/Math/Bourke.hs
@@ -0,0 +1,86 @@
+-- | <http://paulbourke.net/>
+module Sound.SC3.Data.Math.Bourke where
+
+-- | 2-element /h/ transform.
+h_transform_2 :: Num t => t -> ((t,t) -> (t,t)) -> (t,t) -> (t,t)
+h_transform_2 h f (x,y) =
+    let (x',y') = f (x,y)
+    in (x + h * x',y + h * y')
+
+-- | 3-element /h/ transform.
+h_transform_3 :: Num t => t -> ((t,t,t) -> (t,t,t)) -> (t,t,t) -> (t,t,t)
+h_transform_3 h f (x,y,z) =
+    let (x',y',z') = f (x,y,z)
+    in (x + h * x',y + h * y',z + h * z')
+
+-- | <http://paulbourke.net/fractals/lorenz/>
+lorenz :: Num t => t -> t -> t -> (t,t,t) -> (t,t,t)
+lorenz a b c (x,y,z) =
+    (a * (y - x)
+    ,x * (b - z) - y
+    ,x * y - c * z)
+
+{- | <http://paulbourke.net/fractals/lorenz/>
+
+> import Sound.SC3.Plot {- hsc3-plot -}
+
+> let l = iterate (lorenz_h 0.01 10 28 (8/3)) (0.1,0.0,0.0)
+> in plot_p3_ln [take 5000 l]
+
+> let {l = iterate (lorenz_h 0.01 10 28 (8/3)) (0.1,0.0,0.0)
+>     ;f (x,_,z) = (x,z)}
+> in plot_p2_ln [take 15000 (map f l)]
+
+-}
+lorenz_h :: Num t => t -> t -> t -> t -> (t,t,t) -> (t,t,t)
+lorenz_h h a b c = h_transform_3 h (lorenz a b c)
+
+-- | <http://paulbourke.net/fractals/rossler/>
+rossler :: Num t => t -> t -> t -> (t, t, t) -> (t, t, t)
+rossler a b c (x,y,z) =
+    (negate y - z
+    ,x + a * y
+    ,b + z * (x - c))
+
+{- | <http://paulbourke.net/fractals/rossler/>
+
+> plot_p3_ln [take 5000 (iterate (rossler_h 0.02 0.2 0.2 5.7) (0.1,0,0))]
+
+-}
+rossler_h :: Num t => t -> t -> t -> t -> (t,t,t) -> (t,t,t)
+rossler_h h a b c = h_transform_3 h (rossler a b c)
+
+{- | <http://paulbourke.net/fractals/peterdejong/>
+
+> let pdj a b c d =
+>   let vw x = plot_p2_pt [take 15000 x]
+>   in vw (iterate (peter_de_jong a b c d) (-0.72,-0.64))
+
+> pdj 1.4 (-2.3) 2.4 (-2.1)
+> pdj 2.01 (-2.53) 1.61 (-0.33)
+> pdj (-2.7) (-0.09) (-0.86) (-2.2)
+> pdj (-2.24) 0.43 (-0.65) (-2.43)
+> pdj (-2.0) (-2.0) (-1.2) 2.0
+
+-}
+peter_de_jong :: Floating t => t -> t -> t -> t -> (t, t) -> (t, t)
+peter_de_jong a b c d (x,y) =
+    (sin (a * y) - cos (b * x)
+    ,sin (c * x) - cos (d * y))
+
+{- | <http://paulbourke.net/fractals/clifford/>
+
+> let clf a b c d =
+>   let vw x = plot_p2_pt [take 12500 x]
+>   in vw (iterate (clifford a b c d) (-0.72,-0.64))
+
+> clf (-1.4) (1.6) (1.0) (0.7)
+> clf (1.1) (-1.0) (1.0) (1.5) {- not as pb indicates -}
+> clf (1.6) (-0.6) (-1.2) (1.6)
+> clf (1.7) (1.7) (0.6) (1.2)
+
+-}
+clifford :: Floating t => t -> t -> t -> t -> (t, t) -> (t, t)
+clifford a b c d (x,y) =
+    (sin (a * y) + c * cos (a * x)
+    ,sin (b * x) + d * cos (b * y))
diff --git a/Sound/SC3/Data/Math/Loudness.hs b/Sound/SC3/Data/Math/Loudness.hs
new file mode 100644
--- /dev/null
+++ b/Sound/SC3/Data/Math/Loudness.hs
@@ -0,0 +1,121 @@
+-- | Perceptual loudness functions.
+module Sound.SC3.Data.Math.Loudness where
+
+-- * A Weighting
+
+{- | A-weighting curve multiplier function, ie. for linear magnitude
+-- value.  See <http://en.wikipedia.org/wiki/A-weighting>
+
+> import Sound.SC3.Plot
+
+> let {f w = map w [20::Double,50 .. 20000]
+>     ;r = [a_weighting_R,b_weighting_R,c_weighting_R,d_weighting_R]}
+> in plotTable (map f r)
+
+> let {f w = zip (map log [1..]) (map w [25,50 .. 20000])
+>     ;r = [a_weighting_R,b_weighting_R,c_weighting_R,d_weighting_R]}
+> in plot_p2_ln (map f r)
+
+-}
+a_weighting_R :: Floating a => a -> a
+a_weighting_R f =
+    let sq x = x * x
+        dot = foldl1 (*)
+        n = dot [sq 12200
+                ,f ** 4]
+        d = dot [sq f + sq 20.6
+                ,sqrt ((sq f + sq 107.7) * (sq f + sq 737.9))
+                ,sq f + sq 12200]
+    in n / d
+
+-- | A-weighting curve Db offset (additive) function, ie. for
+-- un-weighted Db readings.
+--
+-- > plotTable1 (map a_weighting [20,50 .. 20000])
+-- > plot_p2_ln [zip (map log [1..]) (map a_weighting [25,50 .. 20000])]
+a_weighting :: Floating a => a -> a
+a_weighting f = 2 + 20 * logBase 10 (a_weighting_R f)
+
+-- * B Weighting
+
+-- | B-weighting curve multiplier function, ie. for linear magnitude
+-- value.  See <http://en.wikipedia.org/wiki/A-weighting>
+--
+-- > import Sound.SC3.Plot
+-- > plotTable1 (map b_weighting_R [20,50 .. 20000])
+-- > plot_p2_ln [zip (map log [1..]) (map b_weighting_R [25,50 .. 20000])]
+b_weighting_R :: Floating a => a -> a
+b_weighting_R f =
+    let sq x = x * x
+        dot = foldl1 (*)
+        n = dot [sq 12200
+                ,f ** 3]
+        d = dot [sq f + sq 20.6
+                ,sqrt (sq f + sq 158.5)
+                ,sq f + sq 12200]
+    in n / d
+
+-- | B-weighting curve Db offset (additive) function, ie. for
+-- un-weighted Db readings.
+--
+-- > plotTable1 (map b_weighting [20,50 .. 20000])
+-- > plot_p2_ln [zip (map log [1..]) (map b_weighting [25,50 .. 20000])]
+b_weighting :: Floating a => a -> a
+b_weighting f = 0.17 + 20 * logBase 10 (b_weighting_R f)
+
+-- * C Weighting
+
+-- | C-weighting curve multiplier function, ie. for linear magnitude
+-- value.  See <http://en.wikipedia.org/wiki/A-weighting>
+--
+-- > import Sound.SC3.Plot
+-- > plotTable1 (map c_weighting_R [20,50 .. 20000])
+-- > plot_p2_ln [zip (map log [1..]) (map c_weighting_R [25,50 .. 20000])]
+c_weighting_R :: Floating a => a -> a
+c_weighting_R f =
+    let sq x = x * x
+        dot = foldl1 (*)
+        n = dot [sq 12200
+                ,f ** 2]
+        d = dot [sq f + sq 20.6
+                ,sq f + sq 12200]
+    in n / d
+
+-- | C-weighting curve Db offset (additive) function, ie. for
+-- un-weighted Db readings.
+--
+-- > plotTable1 (map c_weighting [20,50 .. 20000])
+-- > plot_p2_ln [zip (map log [1..]) (map c_weighting [25,50 .. 20000])]
+c_weighting :: Floating a => a -> a
+c_weighting f = 0.06 + 20 * logBase 10 (c_weighting_R f)
+
+-- * D Weighting
+
+-- | /h/ function for D weighting.
+d_h_function :: Fractional a => a -> a
+d_h_function f =
+    let sq x = x * x
+        n = sq (1037918.48 - sq f) + (1080768.16 * sq f)
+        d = sq (9837328 - sq f) + (11723776 * sq f)
+    in n / d
+
+-- | D-weighting curve multiplier function, ie. for linear magnitude
+-- value.  See <http://en.wikipedia.org/wiki/A-weighting>
+--
+-- > import Sound.SC3.Plot
+-- > plotTable1 (map d_weighting_R [20,50 .. 20000])
+-- > plot_p2_ln [zip (map log [1..]) (map d_weighting_R [25,50 .. 20000])]
+d_weighting_R :: Floating a => a -> a
+d_weighting_R f =
+    let sq x = x * x
+        a = f / (6.8966888496476 * (10 ** (-5)))
+        b = sqrt (d_h_function f / ((sq f + 79919.29) * (sq f + 1345600)))
+    in a * b
+
+-- | D-weighting curve Db offset (additive) function, ie. for
+-- un-weighted Db readings.
+--
+-- > plotTable1 (map d_weighting [20,50 .. 20000])
+-- > plot_p2_ln [zip (map log [1..]) (map d_weighting [25,50 .. 20000])]
+d_weighting :: Floating a => a -> a
+d_weighting f = 20 * logBase 10 (d_weighting_R f)
diff --git a/Sound/SC3/Data/Math/Sprott_1993a.hs b/Sound/SC3/Data/Math/Sprott_1993a.hs
new file mode 100644
--- /dev/null
+++ b/Sound/SC3/Data/Math/Sprott_1993a.hs
@@ -0,0 +1,304 @@
+-- | <http://sprott.physics.wisc.edu/sa.htm>
+module Sound.SC3.Data.Math.Sprott_1993a where
+
+import Data.Maybe {- base -}
+
+import Sound.SC3.Plot {- hsc3-plot -}
+
+-- * Coding Table
+
+-- | Table 2-1. ASCII character set and associated coefficient values (p.28)
+sprott_tbl_2_1 :: [(Char,Int,Double)]
+sprott_tbl_2_1 =
+    let i = [32 .. 127]
+        n = map (/ 10) [-45,-44 .. 50]
+        c = map toEnum i
+    in zip3 c i n
+
+-- | Lookup coeffient at 'sprott_tbl_2_1'.
+--
+-- > sprott_coef 'M' == Just 0
+sprott_coef :: Char -> Maybe Double
+sprott_coef c =
+    let (ch,_,cf) = unzip3 sprott_tbl_2_1
+    in lookup c (zip ch cf)
+
+-- | 'fromJust' of 'sprott_coef'.
+sprott_coef_err :: Char -> Double
+sprott_coef_err = fromJust . sprott_coef
+
+-- * General forms
+
+-- | Pair each elements with the element /n/ places further along.
+--
+-- > with_delayed 3 [1..9] == [(1,4),(2,5),(3,6),(4,7),(5,8),(6,9)]
+with_delayed :: Int -> [t] -> [(t,t)]
+with_delayed n l =
+    case l of
+      [] -> []
+      e0:l' -> case drop n l of
+                 [] -> []
+                 e1:_ -> (e0,e1) : with_delayed n l'
+
+{- hsc3-plot...
+
+-- | /n/ '*' /n/.
+square :: Num a => a -> a
+square x = x * x
+
+-}
+
+-- | General one-dimensional quadratic iterated map (Equation 2A, p.25)
+quadratic_1 :: Num a => a -> a -> a -> a -> a
+quadratic_1 a1 a2 a3 x = a1 + a2 * x + a3 * square x
+
+-- | List (/l/) variant of 'quadratic_1'.
+quadratic_1l :: Num a => [a] -> a -> a
+quadratic_1l l =
+    case l of
+      [a1,a2,a3] -> quadratic_1 a1 a2 a3
+      _ -> error "quadratic_1l"
+
+-- | General one-dimensional quintic iterated map (Equation 2E, p.41)
+quintic_1 :: Num a => a -> a -> a -> a -> a -> a -> a -> a
+quintic_1 a1 a2 a3 a4 a5 a6 x =
+    let f :: Num a => Integer -> a -> a
+        f k n = if k == 1 then n else n * f (k - 1) n
+    in a1 + a2 * x + a3 * f 2 x + a4 * f 3 x + a5 * f 4 x + a6 * f 5 x
+
+-- | List (/l/) variant of 'quintic_1'.
+quintic_1l :: Num a => [a] -> a -> a
+quintic_1l l =
+    case l of
+      [a1,a2,a3,a4,a5,a6] -> quintic_1 a1 a2 a3 a4 a5 a6
+      _ -> error "quintic_1l"
+
+-- | Generalised one-dimensional iterated map.
+general_1l :: Num a => [a] -> Maybe (a -> a)
+general_1l l =
+     case length l of
+       3 -> Just (quadratic_1l l)
+       4 -> Just (quintic_1l (l ++ [0,0]))
+       5 -> Just (quintic_1l (l ++ [0]))
+       6 -> Just (quintic_1l l)
+       _ -> Nothing
+
+-- | General two-dimensional iterated quadratic map (Equation 3B, p.53)
+quadratic_2 :: Num t => t->t->t->t->t->t->t->t->t->t->t->t->(t,t)->(t,t)
+quadratic_2 a1 a2 a3 a4 a5 a6 a7 a8 a9 aA aB aC (x,y) =
+    let sq n = n * n
+        x' = a1 + a2 * x + a3 * sq x + a4 * x * y + a5 * y + a6 * sq y
+        y' = a7 + a8 * x + a9 * sq x + aA * x * y + aB * y + aC * sq y
+    in (x',y')
+
+-- | List (/l/) variant of 'quadratic_2'.
+quadratic_2l :: Num t => [t] -> (t,t) -> (t,t)
+quadratic_2l l =
+    case l of
+      [a1,a2,a3,a4,a5,a6,a7,a8,a9,aA,aB,aC] ->
+        quadratic_2 a1 a2 a3 a4 a5 a6 a7 a8 a9 aA aB aC
+      _ -> error "quadratic_2l"
+
+-- | General two-dimensional iterated cubic map (Equation 3F, p.80)
+cubic_2 :: Num t => t->t->t->t->t->t->t->t->t->t->t->t->t->t->t->t->t->t->t->t->(t,t)->(t,t)
+cubic_2 a1 a2 a3 a4 a5 a6 a7 a8 a9 aA aB aC aD aE aF aG aH aI aJ aK (x,y) =
+    let sq n = n * n
+        cb n = n * n * n
+        x' = a1 + a2 * x + a3 * sq x + a4 * cb x + a5 * sq x * y +
+             a6 * x * y + a7 * x * sq y + a8 * y + a9 * sq y + aA * cb y
+        y' = aB + aC * x + aD * sq x + aE * cb x + aF * sq x * y +
+             aG * x * y + aH * x * sq y + aI * y + aJ * sq y + aK * cb y
+    in (x',y')
+
+-- | List (/l/) variant of 'cubic_2'.
+cubic_2l :: Num t => [t] -> (t,t) -> (t,t)
+cubic_2l l =
+    case l of
+      [a1,a2,a3,a4,a5,a6,a7,a8,a9,aA,aB,aC,aD,aE,aF,aG,aH,aI,aJ,aK] ->
+        cubic_2 a1 a2 a3 a4 a5 a6 a7 a8 a9 aA aB aC aD aE aF aG aH aI aJ aK
+      _ -> error "cubic_2l"
+
+
+-- | Generalised two-dimensional iterated map.
+general_2l :: Num t => [t] -> Maybe ((t,t) -> (t,t))
+general_2l l =
+    case length l of
+      12 -> Just (quadratic_2l l)
+      20 -> Just (cubic_2l l)
+      _ -> Nothing
+
+-- | General three-dimensional iterated quadratic map (Equation 4A, p.147)
+quadratic_3 :: Num t => t -> t -> t -> t -> t -> t -> t -> t -> t -> t -> t -> t -> t -> t -> t -> t -> t -> t -> t -> t -> t -> t -> t -> t -> t -> t -> t -> t -> t -> t -> (t, t, t) -> (t, t, t)
+quadratic_3 a1 a2 a3 a4 a5 a6 a7 a8 a9 a10
+            a11 a12 a13 a14 a15 a16 a17 a18 a19 a20
+            a21 a22 a23 a24 a25 a26 a27 a28 a29 a30 (x,y,z) =
+    let sq n = n * n
+        x' = a1 + a2 * x + a3 * sq x + a4 * x * y + a5 * x * z +
+             a6 * y + a7 * sq y + a8 * y * z + a9 * z + a10 * sq z
+        y' = a11 + a12 * x + a13 * sq x + a14 * x * y + a15 * x * z +
+             a16 * y + a17 * sq y + a18 * y * z + a19 * z + a20 * sq z
+        z' = a21 + a22 * x + a23 * sq x + a24 * x * y + a25 * x * z +
+             a26 * y + a27 * sq y + a28 * y * z + a29 * z + a30 * sq z
+    in (x',y',z')
+
+-- | List (/l/) variant of 'quadratic_3'.
+quadratic_3l :: Num t => [t] -> (t, t, t) -> (t, t, t)
+quadratic_3l l =
+    case l of
+      [a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,
+       a11,a12,a13,a14,a15,a16,a17,a18,a19,a20,
+       a21,a22,a23,a24,a25,a26,a27,a28,a29,a30] ->
+        quadratic_3 a1 a2 a3 a4 a5 a6 a7 a8 a9 a10
+                a11 a12 a13 a14 a15 a16 a17 a18 a19 a20
+                a21 a22 a23 a24 a25 a26 a27 a28 a29 a30
+      _ -> error "quadratic_3l"
+
+-- | Generalised three-dimensional iterated map.
+general_3l :: Num t => [t] -> Maybe ((t,t,t) -> (t,t,t))
+general_3l l =
+    case length l of
+      30 -> Just (quadratic_3l l)
+      _ -> Nothing
+
+-- * Projections
+
+-- | Projection onto sphere (Equation 3G, p.107)
+sphere_proj :: (Floating t,Ord t) => [(t,t)] -> [(t,t)]
+sphere_proj l =
+    let (xs,ys) = unzip l
+        (x0,x1) = minmax xs
+        (y0,y1) = minmax ys
+        xa = (x0 + x1) / 2
+        ya = (y0 + y1) / 2
+        tt = pi / (x1 - x0)
+        pt = pi / (y1 - y0)
+        prj (x,y) = let th = tt * (x1 - x)
+                        ph = pt * (y1 - y)
+                    in (xa + 0.36 * (x1 - x0) * cos th * sin ph
+                       ,ya + 0.5 * (y1 - y0) * cos ph)
+    in map prj l
+
+-- * Code Plotting
+
+-- | Plot one-dimensional code, /m/ is delay, /n/ is iteration degree, /i/ is initial value.
+plot_code_1 :: Int -> Int -> Double -> (Code,Annotation) -> IO ()
+plot_code_1 m n i (c,_) =
+    case c of
+      c0:c' -> if c0 `elem` "ABCD"
+               then case general_1l (map sprott_coef_err c') of
+                      Just f -> plot_p2_pt [with_delayed m (take n (iterate f i))]
+                      Nothing -> error "plot_code_1: ill-formed coef"
+               else error "plot_code_1: not type {A,B,C,D}"
+      _ -> error "plot_code_1: ill-formed code"
+
+-- | Plot two-dimensional code, /n/ is iteration degree, /i/ is initial value.
+plot_code_2 :: Bool -> Int -> (Double,Double) -> (Code,Annotation) -> IO ()
+plot_code_2 sph n i (c,_) =
+    case c of
+      c0:c' -> if c0 `elem` "EF"
+               then case general_2l (map sprott_coef_err c') of
+                      Just f -> let prj = if sph then sphere_proj else id
+                                in plot_p2_pt [prj (take n (iterate f i))]
+                      Nothing -> error "plot_code_2: ill-formed coef"
+               else error "plot_code_2: not type {E,F}"
+      _ -> error "plot_code_2: ill-formed code"
+
+-- | Plot three-dimensional code, /n/ is iteration degree, /i/ is initial value.
+plot_code_3 :: Int -> (Double,Double,Double) -> (Code,Annotation) -> IO ()
+plot_code_3 n i (c,_) =
+    case c of
+      c0:c' -> if c0 `elem` "I"
+               then case general_3l (map sprott_coef_err c') of
+                      Just f -> plot_p3_pt [take n (iterate f i)]
+                      Nothing -> error "plot_code_3: ill-formed coef"
+               else error "plot_code_3: not type {I}"
+      _ -> error "plot_code_3: ill-formed code"
+
+-- * Co-efficient codes.
+
+-- | A code is a string.  The set of book codes are at:
+-- <http://sprott.physics.wisc.edu/fractals/bookdisk/BOOKFIGS.DIC> and
+-- there is a further set of codes at:
+-- <http://sprott.physics.wisc.edu/fractals/bookdisk/SELECTED.DIC>
+type Code = String
+type Annotation = String
+
+-- | One-dimensional codes.
+--
+-- > plot_code_1 5 12500 0.1 (codes_1 !! 7)
+codes_1 :: [(Code,Annotation)]
+codes_1 =
+    [("AMu%","Fig 1-4")
+    ,("AXBH","Fig 2-1")
+    ,("ABDU","Fig 2-2")
+    ,("ACAV","Fig 2-3")
+    ,("AXDA","Fig 2-4")
+    ,("BZEZK","Fig 2-5")
+    ,("CBLCTX","Fig 2-6")
+    ,("CUTXJE","Fig 2-7")
+    ,("DBOGIZI","Fig 2-8")
+    ,("DFBIEVV","Fig 2-9")
+    ,("DOOYRIL","Fig 2-10")]
+
+-- | Two-dimensional codes.
+--
+-- > plot_code_2 False 25000 (0.1,0) (codes_2 !! 25)
+codes_2 :: [(Code,Annotation)]
+codes_2 =
+    [("EWM?MPMMWMMMM","Fig 3-1") -- 0
+    ,("EAGHNFODVNJCP","Fig 3-2")
+    ,("EBCQAFMFVPXKQ","Fig 3-3")
+    ,("EDSYUECINGQNV","Fig 3-4")
+    ,("EELXAPXMPQOBT","Fig 3-5")
+    ,("EEYYMKTUMXUVC","Fig 3-6")
+    ,("EJTTSMBOGLLQF","Fig 3-7")
+    ,("ENNMJRCTVVTYG","Fig 3-8")
+    ,("EOUGFJKDHSAJU","Fig 3-9")
+    ,("EQKOCSIDVTPGY","Fig 3-10")
+    ,("EQLOIARXYGHAJ","Fig 3-11") -- 10
+    ,("ETJUBWEDNRORR","Fig 3-12")
+    ,("ETSILUNDQSIFA","Fig 3-13")
+    ,("EUEBJLCDISIIQ","Fig 3-14")
+    ,("EVDUOTLRBKTJD","Fig 3-15")
+    ,("EWLKWPSMOGIGS","Fig 3-16")
+    ,("EZPMSGCNFRENG","Fig 3-17")
+    ,("FIRPGVTFIDGCSXMFPKIDJ","Fig 3-18")
+    ,("FISMHQCHPDFKFBKEALIFD","Fig 3-19")
+    ,("FJYCBMNFNYOEPYUGHHESU","Fig 3-20")
+    ,("FNUYLCURDUHQUQMRZQWQB","Fig 3-24") -- 20
+    ,("ECSRKVVQLGFFS","Fig 3-42")
+    ,("ECVQKGHQTPHTE","Fig 3-43")
+    ,("EKPNERVOTBYCM","Fig 3-44")
+    ,("EUWACXDQIGKHF","Fig 3-45")
+    ,("ECMMMEWHXRMMM","Fig 3-58")
+    ,("EMVWMGCMaMaRM","Fig 8-6 (Tinkerbell)")
+    ,("EAEUBNVIAHERQ","SELECTED.DIC #1 (p. 583)")
+    ,("EAHSVIGTJKOTB","SELECTED.DIC #1")
+    ]
+
+-- | Three-dimensional codes.
+--
+-- > plot_code_3 15000 (0.1,0,0) (codes_3 !! 1)
+codes_3 :: [(Code,Annotation)]
+codes_3 =
+    [("IJKRADSXGDBHIJTQJJDICEJKYSTXFNU","Fig 4-1")
+    ,("ILURCEGOHOIQFJKBSNYGSNRUKKIKIHW","Fig 4-2")
+    ,("INRRXLCEYLFHYAPFSTPHHJMYRYJFBNM","Fig 4-4")
+    ,("IWDWOGDGWGORJOBTUHFQBPRNTCBYQHP","Fig 4-8")]
+
+-- > let cf = map sprott_coef_err "FUXRRRUIRDYKDUBPHHHOMOBRIRBINCS"
+-- > plot_p2_pt [take 25000 (iterate (quartic_2l cf) (0.1,0))]
+
+-- * Specialised forms
+
+-- | Sprott p.9 (Equation 1C)
+--
+-- > plot_p2_pt [with_delayed 1 (take 5000 (iterate (logistic 4) 0.05))]
+logistic :: Num a => a -> a -> a
+logistic r x = r * x * (1 - x)
+
+-- | Figure 3-1. The Hénon map (p.52)
+--
+-- > plot_p2_pt [take 5000 (iterate (henon (-1.4) 0.3) (0.1,0))]
+henon :: Num t => t -> t -> (t, t) -> (t, t)
+henon a b (x,y) = (1 + a * square x + b * y, x)
diff --git a/Sound/SC3/Data/SVG.hs b/Sound/SC3/Data/SVG.hs
new file mode 100644
--- /dev/null
+++ b/Sound/SC3/Data/SVG.hs
@@ -0,0 +1,51 @@
+-- | Load linearised path data from SVG files.
+module Sound.SC3.Data.SVG where
+
+import Data.Maybe {- base -}
+import System.IO.Unsafe {- base -}
+import qualified Text.XML.Light as X {- xml -}
+
+import Graphics.SVG.ReadPath as P {- SVGPath -}
+
+import Data.CG.Minus {- hcg-minus -}
+import Sound.SC3.Plot {- hsc3-plot -}
+
+-- | Make 'X.QName' with @svg@ 'X.qURI'.
+svg_name :: String -> X.QName
+svg_name nm =
+    X.blank_name
+         {X.qName = nm
+         ,X.qURI = Just "http://www.w3.org/2000/svg"}
+
+pathFromString' :: String -> Either String [PathCommand]
+pathFromString' = Right . unsafePerformIO . P.pathFromString
+
+parse_path :: String -> [P.PathCommand]
+parse_path str =
+    case pathFromString' str of
+      Left err -> error err
+      Right cmd -> cmd
+
+svg_read_path_d :: String -> [[P.PathCommand]]
+svg_read_path_d s =
+    let p = case X.parseXMLDoc s of
+              Nothing -> error "svg_read_path_d: no parse"
+              Just e -> X.findElements (svg_name "path") e
+        d = mapMaybe (X.findAttr (X.unqual "d")) p
+    in map parse_path d
+
+subpaths_to_ls :: (Double,Double) -> [P.PathCommand] -> [Ls Double]
+subpaths_to_ls (dx,dy) r =
+    case P.commandsToPoints r (dx,dy) (0,0) of
+      [] -> error "subpaths_to_ls: no sub-paths"
+      p -> map (map pt') p
+
+svg_load_ls :: (Double, Double) -> FilePath -> IO [Ls Double]
+svg_load_ls rs fn = do
+  s <- readFile fn
+  let d = svg_read_path_d s
+      p = concatMap (subpaths_to_ls rs) d
+  return p
+
+plot_ls :: PNum t => [Ls t] -> IO ()
+plot_ls = plotCoord . map (map pt_xy)
diff --git a/Sound/SC3/Data/Trace.hs b/Sound/SC3/Data/Trace.hs
new file mode 100644
--- /dev/null
+++ b/Sound/SC3/Data/Trace.hs
@@ -0,0 +1,307 @@
+module Sound.SC3.Data.Trace where
+
+import Control.Monad {- base -}
+import Data.Bifunctor {- bifunctor -}
+import Data.List {- base -}
+import Data.List.Split {- split -}
+import Data.Maybe {- base -}
+import Safe {- safe -}
+import System.FilePath.Glob {- glob -}
+
+import Data.CG.Minus {- hcg-minus -}
+import qualified Music.Theory.List as T {- hmt -}
+import qualified Music.Theory.Tuple as T {- hmt -}
+import qualified Sound.File.HSndFile as F {- hsc3-sf-hsndfile -}
+import Sound.SC3.Lang.Core {- hsc3-lang -}
+import Sound.SC3.Plot {- hsc3-plot -}
+
+{- | Traces are sequences @Ord t => [(t,a)]@ where t is ascending.
+
+Ordinarily t is a time-point, and traces are temporal.
+
+However /t/ may be, for instance, distance traversed so that line
+segments (sequences of cartesian points) can be transformed into
+Traces by associating each point with the distance along the line.
+
+If there is an interpolation function (linear or otherwise) for the type /a/ we can lookup a value for any index /t/ in the window of the trace.
+
+Traces can be both more accurate and more compact than sampled data streams.
+
+Break-point envelopes are Traces where /a/ is a scalar
+@(interpolation-type,value)@.
+
+Traces are /normal/ if t0 is >= 0 and tn is <= 1.
+
+Traces are /strictly normal/ if t0 == 0 and tn == 1.
+
+-}
+type Trace t a = [(t,a)]
+
+-- | Start time of trace, or zero for null trace.
+trace_start_time :: Num t => Trace t a -> t
+trace_start_time = maybe 0 fst . headMay
+
+-- | End time of trace, or zero for null trace.
+trace_end_time :: Num t => Trace t a -> t
+trace_end_time = maybe 0 fst . lastMay
+
+-- | A trace window is a pait (t0,t1) indicating the begin and end
+-- time points.
+type Window t = (t,t)
+
+-- | Start and end times of trace, or (0,0) for null trace.
+trace_window :: Num t => Trace t a -> Window t
+trace_window t = (trace_start_time t,trace_end_time t)
+
+-- | Interpolation function type.
+type Lerp_F t a b = (t -> a -> a -> b)
+
+-- | Synonym for real valued time point.
+type Time = R
+
+-- * IO
+
+-- | Load real valued trace stored as a sound file.
+--
+-- The temporal data is in the first channel, subsequent channels are
+-- associated data points.  If set /nc/ is set it requires the file
+-- have precisely the indicated number of _data_ channels, ie. /nc/
+-- does not include the _temporal_ channel.
+trace_load_sf :: Maybe Int -> FilePath -> IO (Trace Time [R])
+trace_load_sf nc fn = do
+  (h,t:d) <- F.read fn
+  let nc' = F.channelCount h
+  when (maybe False (/= (nc' - 1)) nc) (error "trace_load_sf: incorrect nc")
+  return (zip t (transpose d))
+
+-- | Variant for loading two-channel trace file.
+trace_load_sf2 :: FilePath -> IO (Trace Time (R,R))
+trace_load_sf2 =
+    let f = map (bimap id T.t2)
+    in fmap f . trace_load_sf (Just 2)
+
+-- | Variant for set of traces given by 'glob' pattern'.
+trace_load_sf_dir :: Maybe Int -> String -> IO [Trace Time [R]]
+trace_load_sf_dir n p = do
+  nm <- glob p
+  mapM (trace_load_sf n) nm
+
+trace_load_sf2_dir :: String -> IO [Trace Time (R,R)]
+trace_load_sf2_dir p = do
+  nm <- glob p
+  mapM trace_load_sf2 nm
+
+-- * Functor
+
+-- | Map over trace times.
+trace_map_t :: (t -> t') -> Trace t a -> Trace t' a
+trace_map_t f = map (\(t,a) -> (f t,a))
+
+-- | Map over trace values.
+trace_map :: (a -> b) -> Trace t a -> Trace t b
+trace_map f = map (\(t,a) -> (t,f a))
+
+-- * Lookup
+
+-- | Trace nodes that bracket time /t/, and trace starting from left neighbour.
+--
+-- > map (trace_locate (zip [0..9] ['a'..])) [-1,3.5,10]
+trace_locate :: (Ord t,Fractional t) => Trace t a -> t -> Either String (((t,a),(t,a)),Trace t a)
+trace_locate tr tm =
+    case tr of
+      p0:p1:r -> let (t0,_) = p0
+                     (t1,_) = p1
+                 in if tm < t0
+                    then Left "trace_locate: time point before trace window"
+                    else if tm <= t1
+                         then Right ((p0,p1),tr)
+                         else trace_locate (p1:r) tm
+      _ -> Left "trace_locate: time point after trace window"
+
+-- | 'fst' of 'trace_locate'
+--
+-- > trace_neighbours (zip [0..9] ['a'..]) 3.5 == Just ((3.0,'d'),(4.0,'e'))
+trace_neighbours :: (Ord t,Fractional t) => Trace t a -> t -> Maybe ((t,a),(t,a))
+trace_neighbours = either (const Nothing) (Just . fst) .: trace_locate
+
+-- | 'fromJust' of 'trace_neighbours'.
+trace_neighbours_err :: (Fractional t,Ord t) => Trace t a -> t -> ((t,a),(t,a))
+trace_neighbours_err = fromJust .: trace_neighbours
+
+-- | Interpolate between to trace points using given interpolation function.
+trace_lerp :: Fractional t => Lerp_F t a b -> t -> (t,a) -> (t,a) -> (t,b)
+trace_lerp lerp_f n (t0,d0) (t1,d1) =
+    let i = (n - t0) / (t1 - t0)
+    in (n,lerp_f i d0 d1)
+
+-- | Linear interpolating lookup, ie. 'trace_lerp' of 'trace_neighbours'.
+--
+-- > t <- trace_load_sf2_dir "/home/rohan/sw/hsc3-data/help/au/*.txy.au"
+-- > map (\z -> trace_lookup lerpn2 z 0.5) t
+trace_lookup :: (Ord t,Fractional t) => Lerp_F t a b -> Trace t a -> t -> Maybe (t,b)
+trace_lookup lerp_f t n =
+    let f (p0,p1) = trace_lerp lerp_f n p0 p1
+    in fmap f (trace_neighbours t n)
+
+-- | 'trace_lookup' with default value.
+trace_lookup_def :: (Ord t,Fractional t) => b -> Lerp_F t a b -> Trace t a -> t -> (t,b)
+trace_lookup_def def lerp_f t n = maybe (n,def) id (trace_lookup lerp_f t n)
+
+-- | 'fromJust' of 'trace_lookup'.
+trace_lookup_err :: (Ord t,Fractional t) => Lerp_F t a b -> Trace t a -> t -> (t,b)
+trace_lookup_err = fromJust .:: trace_lookup
+
+trace_lookup_seq_asc :: (Ord t,Fractional t) => Lerp_F t a b -> Trace t a -> [t] -> Trace t b
+trace_lookup_seq_asc lerp_f =
+    let loop tr n = case n of
+                      n0:n' -> case trace_locate tr n0 of
+                                 Right ((p0,p1),tr') -> trace_lerp lerp_f n0 p0 p1 : loop tr' n'
+                                 Left err -> error err
+                      _ -> []
+    in loop
+
+-- * Operate
+
+-- | Normalise so that 'trace_window' is (0,1).
+--
+-- > let r = [(0,'a'),(0.2,'b'),(1,'c')]
+-- > in trace_normalise_t [(0,'a'),(1,'b'),(5,'c')] == r
+trace_normalise_t :: Fractional t => Trace t a -> Trace t a
+trace_normalise_t trace =
+    let (t0,t1) = trace_window trace
+        d = t1 - t0
+        f t = ((t - t0) / d)
+    in trace_map_t f trace
+
+-- | Transform trace to an /n/-point linear form (time-points are
+-- equi-distant) over indicated 'Window' (which must be ascending, ie
+-- /t0/ < /t1/).
+trace_linearise :: (Ord t,Fractional t) => Int -> Lerp_F t a b -> Trace t a -> Window t -> Trace t b
+trace_linearise n lerp_f t (t0,t1) = trace_lookup_seq_asc lerp_f t (iota t0 t1 n)
+
+-- | Variant where the range is derived implicity from input trace
+-- ('trace_window').
+--
+-- > t <- trace_load_sf2_dir "/home/rohan/sw/hsc3-data/help/au/*.txy.au"
+-- > plotCoord (map (trace_linearise_w 1024 lerpn . trace_map fst) t)
+-- > plotCoord (map (trace_map fst) t)
+-- > trace2_plot_tbl t
+trace_linearise_w :: (Ord t,Fractional t) => Int -> Lerp_F t a b -> Trace t a -> Trace t b
+trace_linearise_w n lerp_f t = trace_linearise n lerp_f t (trace_window t)
+
+-- | Values only of 'trace_linearise_w'.
+--
+-- > plotTable (map (trace_table 1024 lerpn . trace_map fst) t)
+trace_table :: (Ord t,Fractional t) => Int -> Lerp_F t a b -> Trace t a -> [b]
+trace_table = map snd .:: trace_linearise_w
+
+-- | Variant of 'trace_linearize' assuming /t/ is normalised.
+--
+-- > trace_rescale lerpd [(0,[1]),(2,[2])] 3 == [(0,[1]),(0.5,[1.25]),(1,[1.5])]
+trace_rescale :: (Eq t,Ord t,Fractional t) => Lerp_F t a b -> Trace t a -> Int -> Trace t b
+trace_rescale lerp_f t = map (trace_lookup_err lerp_f t) . iota 0 1
+
+-- | Interpolate maintaining temporal shape, divide each step in half.
+--
+-- > let r = [(0,[0]),(0.5,[0.5]),(1,[1]),(2.5,[2.5]),(4,[4])]
+-- > in trace_expand lerpd [(0,[0]),(1,[1]),(4,[4])] == r
+--
+-- > trace2_plot_3d (map (trace_expand lerpn2) t)
+trace_expand :: (Fractional t) => Lerp_F t a a -> Trace t a -> Trace t a
+trace_expand lerp_f t =
+    let f p0 p1 = trace_lerp lerp_f (h p0 p1) p0 p1
+        h (t0,_) (t1,_) = ((t1 - t0) / 2.0) + t0
+        t' = zipWith f t (tail t)
+    in interleave2 (t,t')
+
+-- | Recursive expansion
+--
+-- > length (trace_expand_n lerpd [(0,[0]),(1,[1]),(4,[4])] 3) == 17
+trace_expand_n :: (Fractional t,Integral n) => Lerp_F t a a -> Trace t a -> n -> Trace t a
+trace_expand_n f t n =
+    if n == 1
+    then trace_expand f t
+    else trace_expand_n f (trace_expand f t) (n - 1)
+
+-- * Interpolation
+
+-- | Linear interpolation.
+--
+-- > zipWith (lerpn 0.25) [4,5] [6,9] == [4.5,6.0]
+lerpn :: Num a => a -> a -> a -> a
+lerpn i a b = a + ((b - a) * i)
+
+-- | Variant at uniform 2-tuple.
+--
+-- > lerpn2 0.25 (4,5) (6,9) == (4.5,6.0)
+lerpn2 :: Num n => n -> (n,n) -> (n,n) -> (n,n)
+lerpn2 i = T.t2_zipWith (lerpn i)
+
+-- | Pointwise linear interpolation at lists.
+--
+-- > lerp_pw lerpn 0.25 [4,5] [6,9] == [4.5,6]
+lerp_pw :: Lerp_F t a b -> t -> [a] -> [a] -> [b]
+lerp_pw lerp_f i = zipWith (lerp_f i)
+
+-- | 'lerp_pw' of 'lerpn'.
+--
+-- > lerpd 0.25 [4,5] [6,9] == [4.5,6]
+lerpd :: Num c => c -> [c] -> [c] -> [c]
+lerpd = lerp_pw lerpn
+
+-- * Geometry
+
+-- | Transform 'Ls' to 'Trace', /t/ is distance along line.
+ls_with_distance :: (Eq t,Floating t) => Ls t -> Trace t (Pt t)
+ls_with_distance p =
+    let d = T.dx_d 0 (zipWith pt_distance p (tail p))
+    in zip d p
+
+-- * List
+
+-- | Generic iota function (name courtesy scheme language) with
+-- explicit increment.  The last value is the given end-point
+-- regardless of accumulated errors.
+--
+-- > iota' 0 1 0.25 5 == [0,0.25,0.5,0.75,1]
+iota' :: (Eq n,Num n,Eq m,Num m) => n -> n -> n -> m -> [n]
+iota' a b i n =
+    case n of
+      0 -> []
+      1 -> [b]
+      _ -> a : iota' (a + i) b i (n - 1)
+
+-- | Fractional iota function with implicit increment.
+--
+-- > iota 0 1 5 == [0,0.25,0.5,0.75,1]
+iota :: (Integral m,Eq n,Fractional n) => n -> n -> m -> [n]
+iota a b n = iota' a b ((b - a) / fromIntegral (n - 1)) n
+
+-- | Alternate elements of two lists.
+--
+-- > interleave2 ("one","two") == "otnweo"
+-- > interleave2 ("long","short") == "lsohnogrt"
+interleave2 :: ([t],[t]) -> [t]
+interleave2 = concat . transpose . T.t2_list
+
+-- | Inverse of 'interleave2'.
+--
+-- > interleave2 ("abcd","ABCD") == "aAbBcCdD"
+-- > deinterleave2 "aAbBcCdD" == ("abcd","ABCD")
+deinterleave2 :: [a] -> ([a],[a])
+deinterleave2 = T.t2 . transpose . chunksOf 2
+
+-- * Plotting
+
+-- | Three-dimensional plot of two-dimensional traces (/time/ on @x@ axis), ie. 'plotPath'.
+trace2_plot_3d :: [Trace R (R,R)] -> IO ()
+trace2_plot_3d = plotPath . map (map (\(t,(p,q)) -> (t,p,q)))
+
+-- | Two-dimensional plot of two-dimensional traces (/time/ not drawn), ie. 'plotCoord'.
+trace2_plot_2d :: [Trace R (R,R)] -> IO ()
+trace2_plot_2d = plotCoord . map (map snd)
+
+trace2_plot_tbl :: [Trace R (R,R)] -> IO ()
+trace2_plot_tbl =
+    let f t = [trace_map fst t,trace_map snd t]
+    in plotCoord . concatMap f
diff --git a/hsc3-data.cabal b/hsc3-data.cabal
new file mode 100644
--- /dev/null
+++ b/hsc3-data.cabal
@@ -0,0 +1,40 @@
+Name:              hsc3-data
+Version:           0.15
+Synopsis:          haskell supercollider data
+Description:       Data related functions useful when working with SC3.
+License:           GPL
+Category:          Sound
+Copyright:         (c) Rohan Drape, 2013-2014
+Author:            Rohan Drape
+Maintainer:        rd@slavepianos.org
+Stability:         Experimental
+Homepage:          http://rd.slavepianos.org/t/hsc3-data
+Tested-With:       GHC == 7.8.2
+Build-Type:        Simple
+Cabal-Version:     >= 1.8
+
+Data-files:        README
+
+Library
+  Build-Depends:   base == 4.*,
+                   bifunctors,
+                   Glob,
+                   hcg-minus == 0.15.*,
+                   hmt == 0.15.*,
+                   hsc3-lang == 0.15.*,
+                   hsc3-plot == 0.15.*,
+                   hsc3-sf-hsndfile == 0.15.*,
+                   safe,
+                   split,
+                   SVGPath >= 1.1.1,
+                   xml
+  GHC-Options:     -Wall -fwarn-tabs
+  Exposed-modules: Sound.SC3.Data.Math.Bourke
+                   Sound.SC3.Data.Math.Loudness
+                   Sound.SC3.Data.Math.Sprott_1993a
+                   Sound.SC3.Data.SVG
+                   Sound.SC3.Data.Trace
+
+Source-Repository  head
+  Type:            darcs
+  Location:        http://rd.slavepianos.org/sw/hsc3-data
