     for p in particles
 
         # Angle: Global spin, thne positional mapping to key
-        theta = spinAngle + (Math.PI / 5) * p.note.key
+        theta = midiToAngle p.note.key
 
-        # Positioned in polar coordinates, on unit circle
-        x = Math.cos theta
-        y = Math.sin theta
+        # Radius: Particles spawn in center, fly outward
+        radius = 3.0 * (1 - p.life)
+
+        # Positioned in polar coordinates
+        x = radius * Math.cos theta
+        y = radius * Math.sin theta
+
+        # One rainbow per octave
+        hue = (p.note.key - 60 + 0.1) / 12.0
+        p.color = OPC.hsv hue, 0.3, 0.8
+
+        # Intensity mapped to velocity, nonlinear
+        p.intensity = Math.pow(p.note.velocity / 100, 3.0) * 0.25 * brightness
+
+        # Falloff gets sharper as the note gets higher
+        p.falloff = 20 + (p.note.key - 60) * 20
 
         # Add influence of LFOs
         for key, note of lfoNotes
+            age = now - note.timestamp
+            hz = midiToHz key
+            lfoAngle = midiToAngle key
 
-            # Down several octaves, to useful LFO frequencies
-            transpose = -12 * 5
+            # Amplitude starts with left hand velocity
+            wobbleAmp = Math.pow(note.velocity / 100, 3.0)
 
-            # Midi note to frequency
-            hz = 440 * Math.pow 2, (note.key - 69 + transpose) / 12
+            # Scale based on particle fuzziness
+            wobbleAmp *= 100.0 / p.falloff
 
-            # Wobble amplitude driven by LFO
-            wobbleAmp = Math.sin Math.PI * 2 * now * hz
+            # Fade over time
+            wobbleAmp /= 1 + age
+
+            # Wobble
+            wobbleAmp *= Math.sin(p.life * Math.pow(3, (p.note.key - 35) / 12.0))
 
             # Wobble angle driven by LFO note and particle life
-            wobbleAngle = 10.0 * p.life + (Math.PI / 5) * p.note.key
-
-            x += wobbleAmp * Math.cos wobbleAngle
-            y += wobbleAmp * Math.sin wobbleAngle
-
-        # Radius: Particles spawn in center, fly outward
-        radius = 3.0 * (1 - p.life)
-        x *= radius
-        y *= radius
+            x += wobbleAmp * Math.cos lfoAngle
+            y += wobbleAmp * Math.sin lfoAngle
 
         # Use the XZ plane
         p.point = [x, 0, y]
-
-        # One rainbow per octave
-        hue = (p.note.key - 60) / 12.0
-        p.color = OPC.hsv hue, 0.5, 0.8
-
-        # Intensity mapped to velocity, nonlinear
-        p.intensity = Math.pow(p.note.velocity / 100, 5.0) * brightness
-
-        # Falloff gets sharper as the note gets higher
-        p.falloff = 20 + (p.note.key - 60) * 20
 
         p.life -= timeStep / particleLifetime
 

draw = () ->

    # Time delta calculations
    now = 0.001 * new Date().getTime()
    timeStep = now - previousNow
    previousNow = now


brightness = 1.0
spinRate = 1.0

previousNow = 0
spinAngle = 0

# Time clock in seconds
clock = () -> 0.001 * new Date().getTime()

# Midi to frequency
midiToHz = (key) -> 440 * Math.pow 2, (key - 69) / 12

# Midi note to angle, one rev per octave
midiToAngle = (key) -> (2 * Math.PI / 12) * key


input.on 'message', (deltaTime, message) ->
    console.log message

    switch message[0]
        when 0x80  # Voice 0, note off
