diff --git a/musicw.cabal b/musicw.cabal
--- a/musicw.cabal
+++ b/musicw.cabal
@@ -1,8 +1,8 @@
 name:                musicw
-version:             0.3.0
+version:             0.3.2
 synopsis:            Sound synthesis library, to be used with GHCJS and Web Audio API
 description:         A library for sound synthesis, currently targeting GHCJS and the Web Audio API. Used in the Inner Ear and Estuary projects.
-homepage:            http://github.com/d0kt0r0/reflex-synth/blob/master/README.md
+homepage:            https://github.com/dktr0/musicw/blob/master/README.md
 license:             BSD3
 license-file:        LICENSE
 author:              David Ogborn, Jamie Beverley, Luis Navarro del Angel, Spencer Park and others
@@ -11,14 +11,15 @@
 category:            Web
 build-type:          Simple
 cabal-version:       >=1.10
+tested-with:         GHCJS == 8.6.0.1
 extra-source-files:
 
 Library
   hs-source-dirs:  src
   js-sources:      MusicW.js
   Build-Depends:
-      base >= 4.11.1.0 && < 4.12,
-      containers >= 0.5.11.0 && < 0.6,
+      base >= 4.11.1.0 && < 4.13,
+      containers >= 0.5.11.0 && < 0.7,
       safe >= 0.3.17 && < 0.4,
       ghcjs-base >= 0.2.0.0 && < 0.3,
       ghcjs-dom >= 0.9.2.0 && < 0.10,
diff --git a/src/Sound/MusicW/AudioContext.hs b/src/Sound/MusicW/AudioContext.hs
--- a/src/Sound/MusicW/AudioContext.hs
+++ b/src/Sound/MusicW/AudioContext.hs
@@ -35,7 +35,7 @@
 foreign import javascript unsafe "\"closed\"" js_acClosed :: JSVal
 
 foreign import javascript safe
-  "new (window.AudioContext || window.webkitAudioContext)()"
+  "new (window.AudioContext || window.webkitAudioContext)({ latencyHint: \"playback\", sampleRate: 48000 })"
   newAudioContext :: IO AudioContext
 
 foreign import javascript unsafe
@@ -44,7 +44,9 @@
 
 foreign import javascript safe
   "if (window.___ac == null) { \
-  \    window.___ac = new (window.AudioContext || window.webkitAudioContext)();\
+  \    window.___ac = new (window.AudioContext || window.webkitAudioContext)(\
+  \      { latencyHint: \"playback\", sampleRate: 48000 } \
+  \      );\
   \} $r = window.___ac;"
   getGlobalAudioContext :: IO AudioContext
 
diff --git a/src/Sound/MusicW/Node.hs b/src/Sound/MusicW/Node.hs
--- a/src/Sound/MusicW/Node.hs
+++ b/src/Sound/MusicW/Node.hs
@@ -179,6 +179,14 @@
   setNodeField node "isSink" True
   setNodeField node "startable" False
 
+createChannelSplitter :: AudioIO m => Int -> m Node
+createChannelSplitter nChnls = do
+  ctx <- audioContext
+  node <- liftIO $ js_createChannelSplitter ctx nChnls
+  setNodeField node "isSource" True
+  setNodeField node "isSink" True
+  setNodeField node "startable" False
+
 data OversampleAmount
   = NoOversampling
   | X2Oversampling
@@ -275,6 +283,12 @@
   | not (js_isSink toNode) = error $ (show toNode) ++ " can't be connect target."
   | otherwise   = liftIO $ js_connect' fromNode toNode fromChannel toChannel
 
+connectNodes'' :: MonadIO m => Node -> Int -> Node -> m ()
+connectNodes'' fromNode fromChannel toNode
+  | not (js_isSource fromNode) = error $ (show fromNode) ++ " can't be connect source."
+  | not (js_isSink toNode) = error $ (show toNode) ++ " can't be connect target."
+  | otherwise   = liftIO $ js_connect'' fromNode toNode fromChannel
+
 disconnectNodes :: MonadIO m => Node -> Node -> m ()
 disconnectNodes from to
   | not (js_isSource from) = error $ (show from) ++ " can't be disconnect source."
@@ -396,6 +410,10 @@
   js_createChannelMerger :: AudioContext -> Int -> IO Node
 
 foreign import javascript unsafe
+  "$1.createChannelSplitter($2)"
+  js_createChannelSplitter :: AudioContext -> Int -> IO Node
+
+foreign import javascript unsafe
   "$1.createWaveShaper()"
   js_createWaveShaper :: AudioContext -> IO Node
 
@@ -424,6 +442,10 @@
 foreign import javascript unsafe
   "$1.connect($2,$3,$4);"
   js_connect' :: Node -> Node -> Int -> Int -> IO ()
+
+foreign import javascript unsafe
+  "$1.connect($2,$3);"
+  js_connect'' :: Node -> Node -> Int -> IO ()
 
 foreign import javascript unsafe
   "$1.disconnect($2);"
diff --git a/src/Sound/MusicW/Synth.hs b/src/Sound/MusicW/Synth.hs
--- a/src/Sound/MusicW/Synth.hs
+++ b/src/Sound/MusicW/Synth.hs
@@ -64,6 +64,8 @@
 makeConnections dest ns (NodeRef from (_,_)) DestinationRef = connectNodes (ns!!from) dest
 makeConnections _ ns (NodeRef from (_,_)) (NodeRef to (_,_)) = connectNodes (ns!!from) (ns!!to)
 makeConnections _ ns (NodeRef from (_,_)) (ParamRef to pType) = createParameter (ns!!to) pType >>= connectNodes (ns!!from)
+makeConnections _ ns (NodeOutputRef fromNode fromChannel) (NodeRef to (_,_)) = connectNodes'' (ns!!fromNode) fromChannel (ns!!to)
+makeConnections _ ns (NodeOutputRef fromNode fromChannel) (ParamRef to pType) = createParameter (ns!!to) pType >>= connectNodes'' (ns!!fromNode) fromChannel
 makeConnections _ ns (NodeOutputRef fromNode fromChannel) (NodeInputRef toNode toChannel) = connectNodes' (ns!!fromNode) fromChannel (ns!!toNode) toChannel
 makeConnections _ _ _ _ = error "Malformed graph structure."
 
diff --git a/src/Sound/MusicW/SynthDef.hs b/src/Sound/MusicW/SynthDef.hs
--- a/src/Sound/MusicW/SynthDef.hs
+++ b/src/Sound/MusicW/SynthDef.hs
@@ -126,13 +126,14 @@
   zipWithM_ (\x i -> connect' x 0 y i) xs [0..]
   return y
 
--- note: channelSplitter does not actually create any new synthesis nodes
--- it just turns all of the output channels of a node with outputs into
--- individual node references.
 channelSplitter :: AudioIO m => NodeRef -> SynthDef m [NodeRef]
-channelSplitter (NodeOutputRef n o) = return [NodeOutputRef n o]
-channelSplitter (NodeRef n (_,oChnls)) = return $ fmap (NodeOutputRef n) [0 .. (oChnls-1)]
-channelSplitter _ = error "MusicW: channelSplitter called on NodeInputRef ParamRef or DestinationRef"
+channelSplitter input | nodeRefOutputCount input < 1 = error "MusicW: channelSplitter called on NodeRef not representing a node output"
+                      | otherwise = do
+  let nchnls = nodeRefOutputCount input
+  y <- addNodeBuilder (1,nchnls) $ createChannelSplitter nchnls
+  let (NodeRef n _) = y
+  connect input y
+  return $ fmap (NodeOutputRef n) [0 .. (nchnls-1)]
 
 convolver :: AudioIO m => Either Float32Array FloatArraySpec -> Bool -> NodeRef -> SynthDef m NodeRef
 convolver spec normalize input = do
diff --git a/src/Sound/MusicW/Worklets.hs b/src/Sound/MusicW/Worklets.hs
--- a/src/Sound/MusicW/Worklets.hs
+++ b/src/Sound/MusicW/Worklets.hs
@@ -56,7 +56,15 @@
 powWorklet :: AudioIO m => NodeRef -> NodeRef -> SynthDef m NodeRef
 powWorklet in1 in2 = audioWorklet "pow-processor" [in1,in2]
 
+floorWorklet :: AudioIO m => NodeRef -> SynthDef m NodeRef
+floorWorklet x = audioWorklet "floor-processor" [x]
 
+fractWorklet :: AudioIO m => NodeRef -> SynthDef m NodeRef
+fractWorklet x = audioWorklet "fract-processor" [x]
+
+clipWorklet :: AudioIO m => NodeRef -> NodeRef -> NodeRef -> SynthDef m NodeRef
+clipWorklet lo hi input = audioWorklet "clip-processor" [lo,hi,input]
+
 audioWorklet :: AudioIO m => String -> [NodeRef] -> SynthDef m NodeRef
 audioWorklet workletName inputs = do
   let iChnls = length inputs -- NOTE limiting assumption that each input NodeRef provides only one channel of input
@@ -324,4 +332,48 @@
 \    return true;\
 \  }\
 \ }\
-\ registerProcessor('pow-processor',PowProcessor);"
+\ registerProcessor('pow-processor',PowProcessor);\
+\ \
+\ class FloorProcessor extends AudioWorkletProcessor {\
+\  static get parameterDescriptors() { return []; }\
+\  constructor() { super(); }\
+\  process(inputs,outputs,parameters) {\
+\    const input1 = inputs[0];\
+\    const output = outputs[0];\
+\    for(let i = 0; i < input1[0].length; i++) {\
+\      output[0][i] = Math.floor(input1[0][i]);\
+\    }\
+\    return true;\
+\  }\
+\ }\
+\ registerProcessor('floor-processor',FloorProcessor);\
+\ \
+\ class FractProcessor extends AudioWorkletProcessor {\
+\  static get parameterDescriptors() { return []; }\
+\  constructor() { super(); }\
+\  process(inputs,outputs,parameters) {\
+\    const input1 = inputs[0];\
+\    const output = outputs[0];\
+\    for(let i = 0; i < input1[0].length; i++) {\
+\      output[0][i] = input1[0][i] % 1;\
+\    }\
+\    return true;\
+\  }\
+\ }\
+\ registerProcessor('fract-processor',FractProcessor);\
+\ \
+\ class ClipProcessor extends AudioWorkletProcessor {\
+\  static get parameterDescriptors() { return []; }\
+\  constructor() { super(); }\
+\  process(inputs,outputs,parameters) {\
+\    const min = inputs[0];\
+\    const max = inputs[1];\
+\    const input = inputs[2];\
+\    const output = outputs[0];\
+\    for(let i = 0; i < input[0].length; i++) {\
+\      output[0][i] = Math.min(Math.max(input[0][i], min[0][i]), max[0][i]);\
+\    }\
+\    return true;\
+\  }\
+\ }\
+\ registerProcessor('clip-processor',ClipProcessor);"
