diff --git a/musicw.cabal b/musicw.cabal
--- a/musicw.cabal
+++ b/musicw.cabal
@@ -1,5 +1,5 @@
 name:                musicw
-version:             0.3.2
+version:             0.3.5
 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:            https://github.com/dktr0/musicw/blob/master/README.md
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
@@ -45,10 +45,26 @@
 foreign import javascript safe
   "if (window.___ac == null) { \
   \    window.___ac = new (window.AudioContext || window.webkitAudioContext)(\
-  \      { latencyHint: \"playback\", sampleRate: 48000 } \
+  \      { sampleRate: 48000 } \
   \      );\
   \} $r = window.___ac;"
   getGlobalAudioContext :: IO AudioContext
+
+foreign import javascript safe
+  "if (window.___ac == null) { \
+  \    window.___ac = new (window.AudioContext || window.webkitAudioContext)(\
+  \      { latencyHint: \"playback\", sampleRate: 48000 } \
+  \      );\
+  \} $r = window.___ac;"
+  getGlobalAudioContextPlayback :: IO AudioContext
+
+foreign import javascript safe
+  "if (window.___ac == null) { \
+  \    window.___ac = new (window.AudioContext || window.webkitAudioContext)(\
+  \      { latencyHint: \"interactive\", sampleRate: 48000 } \
+  \      );\
+  \} $r = window.___ac;"
+  getGlobalAudioContextInteractive :: IO AudioContext
 
 foreign import javascript unsafe
   "$1.currentTime"
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
@@ -243,6 +243,14 @@
   setNodeField node "isSink" True
   setNodeField node "startable" False
 
+createMicrophone :: AudioIO m => m Node
+createMicrophone = do
+  ctx <- audioContext
+  node <- liftIO $ js_createMicrophone ctx
+  setNodeField node "isSource" True
+  setNodeField node "isSink" False
+  setNodeField node "startable" False
+
 createMediaStreamDestination :: AudioIO m => m Node
 createMediaStreamDestination = do
   ctx <- audioContext
@@ -420,6 +428,14 @@
 foreign import javascript unsafe
   "$1.createScriptProcessor(void 0, $2, $3)"
   js_createScriptProcessor :: AudioContext -> Int -> Int -> IO Node
+
+foreign import javascript safe
+  "$r = $1.createGain();\
+  \navigator.mediaDevices.getUserMedia({ audio: true, video: false}).then(function(stream) {\
+  \  var x = new MediaStreamAudioSourceNode($1,{mediaStream: stream});\
+  \  x.connect($r);\
+  \});"
+  js_createMicrophone :: AudioContext -> IO Node
 
 foreign import javascript unsafe
   "$1.createMediaStreamDestination()"
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
@@ -181,6 +181,9 @@
 audioIn :: AudioIO m => SynthDef m NodeRef
 audioIn = addNodeBuilder (0,1) $ createGain 1.0 -- TODO: hard-coded single input channel will be problematic later
 
+microphone :: AudioIO m => SynthDef m NodeRef
+microphone = addNodeBuilder (0,1) createMicrophone 
+
 resink :: AudioIO m => NodeRef -> NodeRef -> SynthDef m NodeRef
 resink target input = connect input target >> return target
 
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
@@ -53,6 +53,9 @@
 safeDivideWorklet :: AudioIO m => NodeRef -> NodeRef -> SynthDef m NodeRef
 safeDivideWorklet in1 in2 = audioWorklet "safeDivide-processor" [in1,in2]
 
+unsafeDivideWorklet :: AudioIO m => NodeRef -> NodeRef -> SynthDef m NodeRef
+unsafeDivideWorklet in1 in2 = audioWorklet "unsafeDivide-processor" [in1,in2]
+
 powWorklet :: AudioIO m => NodeRef -> NodeRef -> SynthDef m NodeRef
 powWorklet in1 in2 = audioWorklet "pow-processor" [in1,in2]
 
@@ -65,20 +68,52 @@
 clipWorklet :: AudioIO m => NodeRef -> NodeRef -> NodeRef -> SynthDef m NodeRef
 clipWorklet lo hi input = audioWorklet "clip-processor" [lo,hi,input]
 
+maxWorklet :: AudioIO m => NodeRef -> NodeRef -> SynthDef m NodeRef
+maxWorklet in1 in2 = audioWorklet "max-processor" [in1,in2]
+
+minWorklet :: AudioIO m => NodeRef -> NodeRef -> SynthDef m NodeRef
+minWorklet in1 in2 = audioWorklet "min-processor" [in1,in2]
+
+whiteNoiseWorklet :: AudioIO m => SynthDef m NodeRef
+whiteNoiseWorklet = audioWorklet "white-noise-processor" []
+
+sinToSqrWorklet :: AudioIO m => NodeRef -> SynthDef m NodeRef
+sinToSqrWorklet x = audioWorklet "sin-to-sqr-processor" [x]
+
+sinToTriWorklet :: AudioIO m => NodeRef -> SynthDef m NodeRef
+sinToTriWorklet x = audioWorklet "sin-to-tri-processor" [x]
+
+
+-- | sinToSawWorklet is for producing an ideal (ie. not band-limited) sawtooth
+-- wave (a phasor, in other words) from two phase-aligned sine waves. The first
+-- sine wave argument should be at one quarter the desired frequency, and the second
+-- sine wave argument should be at half the desired frequency.
+
+sinToSawWorklet :: AudioIO m => NodeRef -> NodeRef -> SynthDef m NodeRef
+sinToSawWorklet x y = audioWorklet "sin-to-saw-processor" [x,y]
+
+-- | stepWorklet produces an output by selecting from a list of inputs on the basis
+-- of another, "selector" input from from 0-1. The space of 0-1 is divided over however
+-- many inputs are provided in the argument, and the output at any given sample only
+-- comes from one of the inputs (there is no mixing or cross-fading between the inputs).
+
+stepWorklet :: AudioIO m => [NodeRef] -> NodeRef -> SynthDef m NodeRef
+stepWorklet xs y = audioWorklet "step-processor" (y:xs)
+
 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
   let oChnls = 1 -- NOTE limiting assumption that each audio worklet provides only one channel of output
   y <- addNodeBuilder (iChnls,oChnls) $ createAudioWorkletNode iChnls oChnls workletName
-  zipWithM (\x n -> connect' x 0 y n) inputs [0..]
+  zipWithM_ (\x n -> connect' x 0 y n) inputs [0..]
   return y
 
 createAudioWorkletNode :: AudioIO m => Int -> Int -> String -> m Node
 createAudioWorkletNode inChnls outChnls workletName = do
   ctx <- audioContext
   node <- liftIO $ js_createAudioWorkletNode ctx (toJSString workletName) inChnls outChnls
-  setNodeField node "isSource" (outChnls > 0)
-  setNodeField node "isSink" (inChnls > 0)
+  _ <- setNodeField node "isSource" (outChnls > 0)
+  _ <- setNodeField node "isSink" (inChnls > 0)
   setNodeField node "startable" False
 
 foreign import javascript safe
@@ -319,6 +354,21 @@
 \ }\
 \ registerProcessor('safeDivide-processor',SafeDivideProcessor);\
 \ \
+\ class UnsafeDivideProcessor extends AudioWorkletProcessor {\
+\  static get parameterDescriptors() { return []; }\
+\  constructor() { super(); }\
+\  process(inputs,outputs,parameters) {\
+\    const input1 = inputs[0];\
+\    const input2 = inputs[1];\
+\    const output = outputs[0];\
+\    for(let i = 0; i < input1[0].length; i++) {\
+\      output[0][i] = input1[0][i] / input2[0][i];\
+\    }\
+\    return true;\
+\  }\
+\ }\
+\ registerProcessor('unsafeDivide-processor',UnsafeDivideProcessor);\
+\ \
 \ class PowProcessor extends AudioWorkletProcessor {\
 \  static get parameterDescriptors() { return []; }\
 \  constructor() { super(); }\
@@ -376,4 +426,117 @@
 \    return true;\
 \  }\
 \ }\
-\ registerProcessor('clip-processor',ClipProcessor);"
+\ registerProcessor('clip-processor',ClipProcessor);\
+\ \
+\ class MaxProcessor extends AudioWorkletProcessor {\
+\  static get parameterDescriptors() { return []; }\
+\  constructor() { super(); }\
+\  process(inputs,outputs,parameters) {\
+\    const in1 = inputs[0];\
+\    const in2 = inputs[1];\
+\    const output = outputs[0];\
+\    for(let i = 0; i < in1[0].length; i++) {\
+\      output[0][i] = Math.max(in1[0][i],in2[0][i]);\
+\    }\
+\    return true;\
+\  }\
+\ }\
+\ registerProcessor('max-processor',MaxProcessor);\
+\ \
+\ class MinProcessor extends AudioWorkletProcessor {\
+\  static get parameterDescriptors() { return []; }\
+\  constructor() { super(); }\
+\  process(inputs,outputs,parameters) {\
+\    const in1 = inputs[0];\
+\    const in2 = inputs[1];\
+\    const output = outputs[0];\
+\    for(let i = 0; i < in1[0].length; i++) {\
+\      output[0][i] = Math.min(in1[0][i],in2[0][i]);\
+\    }\
+\    return true;\
+\  }\
+\ }\
+\ registerProcessor('min-processor',MinProcessor);\
+\ \
+\ class WhiteNoiseProcessor extends AudioWorkletProcessor {\
+\ \
+\  static get parameterDescriptors() { return []; }\
+\  constructor() { super(); }\
+\  process(inputs,outputs,parameters) {\
+\    const output = outputs[0];\
+\    for(let i = 0; i < output[0].length; i++) {\
+\      output[0][i] = Math.random()*2-1;\
+\    }\
+\    return true;\
+\  }\
+\ }\
+\ registerProcessor('white-noise-processor',WhiteNoiseProcessor);\
+\ \
+\ class SinToSqrProcessor extends AudioWorkletProcessor {\
+\ \
+\  static get parameterDescriptors() { return []; }\
+\  constructor() { super(); }\
+\  process(inputs,outputs,parameters) {\
+\    const input = inputs[0];\
+\    const output = outputs[0];\
+\    for(let i = 0; i < output[0].length; i++) {\
+\      output[0][i] = (input[0][i] >= 0) ? 1 : -1;\
+\    }\
+\    return true;\
+\  }\
+\ }\
+\ registerProcessor('sin-to-sqr-processor',SinToSqrProcessor);\
+\ \
+\ class SinToTriProcessor extends AudioWorkletProcessor {\
+\ \
+\  static get parameterDescriptors() { return []; }\
+\  constructor() { super(); this.MULT = 4/Math.PI; }\
+\  process(inputs,outputs,parameters) {\
+\    const input = inputs[0];\
+\    const output = outputs[0];\
+\    for(let i = 0; i < output[0].length; i++) {\
+\      output[0][i] = Math.abs(Math.asin(input[0][i]))*this.MULT-1;\
+\    }\
+\    return true;\
+\  }\
+\ }\
+\ registerProcessor('sin-to-tri-processor',SinToTriProcessor);\
+\ \
+\ class SinToSawProcessor extends AudioWorkletProcessor {\
+\  static get parameterDescriptors() { return []; }\
+\  constructor() { super(); this.MULT = 4/Math.PI; }\
+\  process(inputs,outputs,parameters) {\
+\    const input1 = inputs[0];\
+\    const input2 = inputs[1];\
+\    const output = outputs[0];\
+\    var a;\
+\    for(let i = 0; i < output[0].length; i++) {\
+\      a = Math.abs(Math.asin(input1[0][i]))*this.MULT;\
+\      output[0][i] = (input2[0][i] >= 0) ? (a-1) : (1-a);\
+\    }\
+\    return true;\
+\  }\
+\ }\
+\ registerProcessor('sin-to-saw-processor',SinToSawProcessor);\
+\ \
+\ class StepProcessor extends AudioWorkletProcessor {\
+\  static get parameterDescriptors() { return []; }\
+\  constructor() { super(); }\
+\  process(inputs,outputs,parameters) {\
+\    const output = outputs[0];\
+\    if(inputs.length < 2) { for(let i = 0; i < output[0].length; i++) output[0][i] = 0;}\
+\    else if(inputs.length == 2) { for(let i = 0; i < output[0].length; i++) output[0][i] = inputs[1][i];}\
+\    else {\
+\      var a,n = inputs.length - 1;\
+\      for(let i = 0; i < output[0].length; i++) {\
+\        a = inputs[0][0][i] * 0.5 + 0.5;\
+\        a = Math.floor ((a - Math.trunc(a)) * n);\
+\        a = (a >= 0) ? a : 0;\
+\        a = (a < n) ? a : (n-1);\
+\        output[0][i] = inputs[1+a][0][i];\
+\      }\
+\    }\
+\    return true;\
+\  }\
+\ }\
+\ registerProcessor('step-processor',StepProcessor);"
