diff --git a/app/Main.hs b/app/Main.hs
--- a/app/Main.hs
+++ b/app/Main.hs
@@ -17,8 +17,9 @@
 import Network.WebSockets
 import System.Environment (getArgs)
 import System.FilePath
-import System.INotify
+import System.FSNotify
 import System.Process
+
 -- import System.Random
 
 main :: IO ()
@@ -27,25 +28,21 @@
   _ -> error "Name a file to watch!"
 
 main' :: FilePath ->  IO ()
-main' pathToWatch = do
+main' pathToWatch = withManager $ \mgr -> do
   _ <- forkIO $ serveIndex
-  runServer "127.0.0.1" 8080 $ handleConnection pathToWatch
+  runServer "127.0.0.1" 8080 $ handleConnection pathToWatch mgr
 
-handleConnection :: FilePath -> PendingConnection -> IO ()
-handleConnection pathToWatch pending = do
+handleConnection :: FilePath -> WatchManager -> PendingConnection -> IO ()
+handleConnection pathToWatch mgr pending = do
    let (dirToWatch, fileToWatch) = splitFileName pathToWatch
-   inotify <- initINotify
-   print inotify
    connection <- acceptRequest pending
+
    (sendTextData connection . T.pack) =<< getNewSource pathToWatch
-   -- withINotify $ \inotify ->
-   _ <- addWatch inotify [Modify] dirToWatch $ \case
-      Modified False (Just f) | f == fileToWatch ->
-         (sendTextData connection . T.pack) =<< getNewSource pathToWatch
-      _ -> return ()
---  print wd
---  removeWatch wd
---  receiveDataMessage connection
+
+   let onChange e = case e of
+         Modified _ _ -> (sendTextData connection . T.pack) =<< getNewSource pathToWatch
+         _ -> return ()
+   _ <- watchDir mgr dirToWatch (const True) onChange
    _ <- getLine -- temp hack to keep the socket open
    return ()
 
diff --git a/app/index.html b/app/index.html
--- a/app/index.html
+++ b/app/index.html
@@ -85,34 +85,31 @@
 
     <canvas id="canvas"></canvas>
     <script>
-     const vsSource = `
-attribute vec3 aPosition;
-varying vec2 uvN;
-void main() {
-  gl_Position = vec4(aPosition, 1.0);
-  uvN = aPosition.xy;
-}
-`;
-     const fsHeader=`
-precision mediump float;
-const float pi = 3.141592653589793238462643383;
-uniform float time;
-uniform vec2 mouse;
-uniform vec4 audio;
-uniform sampler2D backBuffer;
-varying vec2 uvN;
-
-vec2 uv() {
-  return 0.5 * uvN  + 0.5;
-}
+     var vsSource = [ "attribute vec3 aPosition;"
+                    , "varying vec2 uvN;"
+                    , "void main() {"
+                    , "  gl_Position = vec4(aPosition, 1.0);"
+                    , "  uvN = aPosition.xy;"
+                    , "}"
+     ].join("\n");
 
-`
+     var fsHeader= [ "precision mediump float;"
+                   , "const float pi = 3.141592653589793238462643383;"
+                   , "uniform float time;"
+                   , "uniform vec2 mouse;"
+                   , "uniform vec2 resolution;"
+                   , "uniform vec4 audio;"
+                   , "uniform sampler2D backBuffer;"
+                   , "varying vec2 uvN;"
+                   , "vec2 uv() {"
+                   , "  return 0.5 * uvN  + 0.5;"
+                   , "}"
+     ].join("\n");
 
-     const initialFsSource = `
-void main() {
-    gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);
-}
-`;
+     var initialFsSource = [ "void main() {"
+                           , "    gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);"
+                           , "}"
+     ].join("\n");
 
 
      function loadProgram (gl, state, vsSource, fsSource) {
@@ -123,13 +120,13 @@
        function compileShader (gl, source, shaderType) {
          //assert(shaderType === gl.FRAGMENT_SHADER || shaderType === g.VERTEXT_SHADER);
 
-         let shader = gl.createShader(shaderType);
+         var shader = gl.createShader(shaderType);
 
          gl.shaderSource(shader, source);
          gl.compileShader(shader);
 
 
-         let success = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
+         var success = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
          if (!success) {
            console.log(source);
            throw "could not compile shader:" + gl.getShaderInfoLog(shader);
@@ -140,18 +137,18 @@
 
 
 
-       let vs = compileShader(gl, vsSource, gl.VERTEX_SHADER);
-       let fs = compileShader(gl, fsSource, gl.FRAGMENT_SHADER);
+       var vs = compileShader(gl, vsSource, gl.VERTEX_SHADER);
+       var fs = compileShader(gl, fsSource, gl.FRAGMENT_SHADER);
 
 
-       let program = gl.createProgram();
+       var program = gl.createProgram();
 
        gl.attachShader(program, vs);
        gl.attachShader(program, fs);
 
        gl.linkProgram(program);
 
-       let success = gl.getProgramParameter(program, gl.LINK_STATUS);
+       var success = gl.getProgramParameter(program, gl.LINK_STATUS);
        if (!success) {
          throw ("program failed to link:" + gl.getProgramInfoLog(program));
        }
@@ -171,7 +168,7 @@
 
        // backBuffer stuff
        function createTarget() {
-         let target = {
+         var target = {
            texture: gl.createTexture(),
            framebuffer: gl.createFramebuffer()
          };
@@ -201,18 +198,20 @@
        gl.time = gl.getUniformLocation(program, "time");
        gl.mouse = gl.getUniformLocation(program, "mouse");
        gl.audio = gl.getUniformLocation(program, "audio");
+       gl.resolution = gl.getUniformLocation(program, "resolution");
        gl.backBuffer = gl.getUniformLocation(program, "backBuffer");
 
        draw(gl, state);
 
        if (state.animationFrameRequest === null) {
-         state.animationFrameRequest = requestAnimationFrame(() => animate(gl, state));
+         state.animationFrameRequest = requestAnimationFrame(function() {animate(gl, state);});
        }
      }
 
      function draw (gl, state) {
        gl.uniform1f(gl.time, (new Date().getTime() / 1000 - state.time0));
        gl.uniform2f(gl.mouse, state.mouse.x, state.mouse.y);
+       gl.uniform2f(gl.resolution, WIDTH, HEIGHT);
        gl.uniform4f(gl.audio, state.audio.low, state.audio.mid, state.audio.upper, state.audio.high);
 
        gl.uniform1i(gl.backBuffer, 0); // Do I need to check for null?
@@ -235,7 +234,7 @@
 
      function animate (gl, state) {
        draw(gl, state);
-       state.animationFrameRequest = requestAnimationFrame(() => animate(gl, state));
+       state.animationFrameRequest = requestAnimationFrame(function() { animate(gl, state);});
      };
 
 
@@ -250,14 +249,14 @@
 
 
        function setMouse (event) {
-         let r = event.target.getBoundingClientRect();
+         var r = event.target.getBoundingClientRect();
          state.mouse.x = (event.clientX - r.left) / (r.right - r.left) * 2 -1;
          state.mouse.y = (event.clientY - r.bottom) / (r.top - r.bottom) * 2 - 1;
        };
        /* canvas.onmousedown = (event) => setMouse(event, 1); */
        /* canvas.onmouseup = (event) => setMouse(event, 0); */
 
-       canvas.onmousemove = (event) => setMouse(event);
+       canvas.onmousemove = setMouse;
        state.mouse = {x: 0, y: 0};
 
 
@@ -265,14 +264,14 @@
        state.audioAgain = null;
 
        function onAccept (stream) {
-         let audioCtx = new (window.AudioContext || window.webkitAudioContext)();
-         let source = audioCtx.createMediaStreamSource(stream);
-         let analyser = audioCtx.createAnalyser();
+         var audioCtx = new (window.AudioContext || window.webkitAudioContext)();
+         var source = audioCtx.createMediaStreamSource(stream);
+         var analyser = audioCtx.createAnalyser();
          source.connect(analyser);
 
          analyser.fftSize = 512;
-         let bufferLength = analyser.frequencyBinCount;
-         let dataArray = new Uint8Array(bufferLength);
+         var bufferLength = analyser.frequencyBinCount;
+         var dataArray = new Uint8Array(bufferLength);
 
 
          function toAudio() {
@@ -280,8 +279,8 @@
            analyser.getByteFrequencyData(dataArray);
 
            // Taken from The_Force
-           let k = 0, f = 0.0;
-           let a = 5, b = 11, c = 24, d = bufferLength, i = 0;
+           var k = 0, f = 0.0;
+           var a = 5, b = 11, c = 24, d = bufferLength, i = 0;
 
            for(; i < a; i++) {
              f += dataArray[i];
@@ -332,20 +331,20 @@
 
      // stateful variables
 
-     const canvas = document.getElementById("canvas");
-     let WIDTH = Math.max(window.innerHeight, window.innerWidth);
-     let HEIGHT =  Math.max(window.innerHeight, window.innerWidth);
+     var canvas = document.getElementById("canvas");
+     var WIDTH = Math.max(window.innerHeight, window.innerWidth);
+     var HEIGHT =  Math.max(window.innerHeight, window.innerWidth);
      canvas.width = WIDTH;
      canvas.height = HEIGHT;
 
-     const gl = canvas.getContext("webgl");
+     var gl = canvas.getContext("webgl");
 
-     const state = {};
+     var state = {};
 
      setupState(state, canvas);
 
 
-     loadProgram (gl, state,  vsSource, fsHeader + initialFsSource);
+     loadProgram (gl, state,  vsSource, fsHeader + "\n" + initialFsSource);
 
 
      function fadeOut(elem) {
@@ -356,13 +355,13 @@
      }
 
      function connectToHylogen() {
-       const wsConn = new WebSocket("ws://localhost:8080/");
+       var wsConn = new WebSocket("ws://localhost:8080/");
 
        wsConn.onopen = function() {
-         const landing = document.getElementById("landing");
+         var landing = document.getElementById("landing");
          if (landing) {
            fadeOut(document.getElementById("bg"));
-           window.setTimeout(() => {fadeOut(landing);}, 500);
+           window.setTimeout(function() {fadeOut(landing);}, 500);
          }
          console.log('websocket opened');
 
@@ -375,7 +374,7 @@
 
        wsConn.onmessage = function (m) {
          console.log(m.data);
-         loadProgram(gl, state,  vsSource, fsHeader + m.data);
+         loadProgram(gl, state,  vsSource, fsHeader + "\n" + m.data);
        };
      }
 
diff --git a/hylogen.cabal b/hylogen.cabal
--- a/hylogen.cabal
+++ b/hylogen.cabal
@@ -1,5 +1,5 @@
 name:                hylogen
-version:             0.1.0.6
+version:             0.1.0.8
 synopsis:            an EDSL for live-coding fragment shaders
 description:         an EDSL for live-coding fragment shaders
 homepage:            https://github.com/sleexyz/hylogen
@@ -28,7 +28,7 @@
   build-depends:       base
                      , bytestring
                      , filepath
-                     , hinotify
+                     , fsnotify
                      , network
                      , process
                      , random
diff --git a/src/Hylogen/Globals.hs b/src/Hylogen/Globals.hs
--- a/src/Hylogen/Globals.hs
+++ b/src/Hylogen/Globals.hs
@@ -3,8 +3,17 @@
 module Hylogen.Globals where
 
 import Hylogen.Types
+import Data.VectorSpace
 
+vec2 :: (Show tc, ConstructFrom tc Vec2) => tc -> Vec2
+vec2 = Vec2
 
+vec3 :: (Show tc, ConstructFrom tc Vec3) => tc -> Vec3
+vec3 = Vec3
+
+vec4 :: (Show tc, ConstructFrom tc Vec4) => tc -> Vec4
+vec4 = Vec4
+
 inverseSqrt :: (HyloPrim a) => a -> a
 inverseSqrt = vuop "inversesqrt"
 
@@ -43,6 +52,9 @@
 uvN :: Vec2
 uvN = V2u "uvN"
 
+resolution :: Vec2
+resolution = V2u "resolution"
+
 mouse :: Vec2
 mouse = V2u "mouse"
 
@@ -52,6 +64,9 @@
 
 backBuffer :: Texture
 backBuffer = Tu "backBuffer"
+
+mix :: Vec1 -> Vec4 -> Vec4 -> Vec4
+mix p a b = p *^ a + (1 - p) *^ b
 
 -- | Booly's
 
diff --git a/src/Hylogen/Types.hs b/src/Hylogen/Types.hs
--- a/src/Hylogen/Types.hs
+++ b/src/Hylogen/Types.hs
@@ -1,25 +1,41 @@
 {-# LANGUAGE GADTs #-}
 {-# LANGUAGE FlexibleInstances #-}
-{-# LANGUAGE NoMonomorphismRestriction #-}
-{-# LANGUAGE NoMonoLocalBinds #-}
+{-# LANGUAGE FlexibleContexts #-}
 {-# LANGUAGE ExtendedDefaultRules #-}
 {-# LANGUAGE ScopedTypeVariables #-}
 {-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE ConstraintKinds #-}
+{-# LANGUAGE TypeOperators #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
 
 module Hylogen.Types where
 
+
 import Data.Monoid
 import Data.VectorSpace
+import GHC.Exts (Constraint)
 
--- | Darn I need injective type families.. waiting for GHC8
-type family HyloConstructor v where
-  HyloConstructor Vec1 = Float
-  HyloConstructor Vec2 = (Vec1, Vec1)
-  HyloConstructor Vec3 = (Vec1, Vec1, Vec1)
-  HyloConstructor Vec4 = (Vec1, Vec1, Vec1, Vec1)
 
+type family (ConstructFrom tc hprim) :: Constraint where
+  ConstructFrom a Vec1 = a ~ Float
+  ConstructFrom (a, b) Vec2 = (a ~ Vec1, b ~ Vec1)
+
+  ConstructFrom (a, b, c) Vec3 = (a ~ Vec1, b ~ Vec1, c ~ Vec1)
+  ConstructFrom (Vec2, b) Vec3 = (b ~ Vec1)
+  ConstructFrom (a, Vec2) Vec3 = (a ~ Vec1)
+
+  ConstructFrom (a, b, c, d) Vec4 = (a ~ Vec1, b ~ Vec1, c ~ Vec1, d ~ Vec1)
+  ConstructFrom (Vec3, b) Vec4 = (b ~ Vec1)
+  ConstructFrom (Vec2, b) Vec4 = (b ~ Vec2)
+  ConstructFrom (a, Vec3) Vec4 = (a ~ Vec1)
+  ConstructFrom (a, Vec2, c) Vec4 = (a ~ Vec1, c ~ Vec1)
+  ConstructFrom (Vec2, b, c) Vec4 = (b ~ Vec1, c ~ Vec1)
+  ConstructFrom (a, b, Vec2) Vec4 = (a ~ Vec1, b ~ Vec1)
+
+
 class (Show v) => HyloPrim v where
-  vec :: HyloConstructor v -> v
+  vec :: (Show tc, ConstructFrom tc v) => tc -> v
   vu :: String -> v
   vuop :: String -> v -> v
   vuoppre :: String -> v -> v
@@ -27,15 +43,16 @@
   vboppre :: String -> v -> v -> v
   select :: Booly -> v -> v -> v
   fromVec1 :: Vec1 -> v
+  toList :: v -> [Vec1]
 
 
+
 class Show a => HasX a
 class HasX a => HasY a
 class HasY a => HasZ a
 class HasZ a => HasW a
 
 
-
 data Vec1 where
   Vec1 :: Float -> Vec1
   V1u :: String -> Vec1
@@ -44,6 +61,7 @@
   V1bop :: String -> Vec1 -> Vec1 -> Vec1
   V1boppre :: String -> Vec1 -> Vec1 -> Vec1
   V1select :: Booly -> Vec1 -> Vec1 -> Vec1
+  Dot :: (HyloPrim a) => a -> a -> Vec1
   X :: (HasX a) => a -> Vec1
   Y :: (HasY a) => a -> Vec1
   Z :: (HasZ a) => a -> Vec1
@@ -60,6 +78,7 @@
     V1bop b x y -> "(" <> show x <> " " <> b <> " " <> show y <> ")"
     V1boppre b x y -> b <> "(" <> show x <> ", " <> show y <> ")"
     V1select b x y -> "( " <> show b <> " ? " <> show x <> " : " <> show y <> ")"
+    Dot x y -> "dot(" <> show x <> ", " <> show y <> ")"
     X x ->  show x <> ".x"
     Y x ->  show x <> ".y"
     Z x ->  show x <> ".z"
@@ -75,6 +94,7 @@
   vboppre = V1boppre
   select = V1select
   fromVec1 = id
+  toList x = [x]
 
 instance Num Vec1 where
   (+) = vbop "+"
@@ -119,10 +139,13 @@
   type Scalar Vec1 = Vec1
   a *^ b = a * b
 
+instance InnerSpace Vec1 where
+  (<.>) = Dot
+
 -- | Vec2:
 
 data Vec2 where
-  Vec2 :: (Vec1, Vec1) -> Vec2
+  Vec2 :: (Show tc, ConstructFrom tc Vec2) => tc -> Vec2
   V2u :: String -> Vec2
   V2uop :: String -> Vec2 -> Vec2
   V2uoppre :: String -> Vec2 -> Vec2
@@ -140,11 +163,12 @@
   vboppre = V2boppre
   select = V2select
   fromVec1 x = Vec2 (x, x)
+  toList x = [X x, Y x]
 
 
 instance Show Vec2 where
   show expr = case expr of
-    Vec2 (x, y) -> "vec2(" <> show x <> ", " <> show y <> ")"
+    Vec2 tc -> "vec2" <> show tc
     V2u x -> x
     V2uop u x -> u <> "(" <> show x <> ")"
     V2uoppre u x -> "(" <> u <> show x <> ")"
@@ -196,6 +220,9 @@
   type Scalar Vec2 = Vec1
   a *^ b = V2bops "*" a b
 
+instance InnerSpace Vec2 where
+  (<.>) = Dot
+
 instance HasX Vec2
 instance HasY Vec2
 
@@ -203,7 +230,7 @@
 -- | Vec3:
 
 data Vec3 where
-  Vec3 :: (Vec1, Vec1, Vec1) -> Vec3
+  Vec3 :: (Show tc, ConstructFrom tc Vec3) => tc -> Vec3
   V3u :: String -> Vec3
   V3uop :: String -> Vec3 -> Vec3
   V3uoppre :: String -> Vec3 -> Vec3
@@ -221,10 +248,11 @@
   vboppre = V3boppre
   select = V3select
   fromVec1 x = Vec3 (x, x, x)
+  toList x = [X x, Y x, Z x]
 
 instance Show Vec3 where
   show expr = case expr of
-    Vec3 (x, y, z) -> "vec3(" <> show x <> ", " <> show y <> ", " <> show z <> ")"
+    Vec3 tc -> "vec3" <> show tc
     V3u x -> x
     V3uop u x -> u <> "(" <> show x <> ")"
     V3uoppre u x -> "(" <> u <> show x <> ")"
@@ -276,6 +304,9 @@
   type Scalar Vec3 = Vec1
   a *^ b = V3bops "*" a b
 
+instance InnerSpace Vec3 where
+  (<.>) = Dot
+
 instance HasX Vec3
 instance HasY Vec3
 instance HasZ Vec3
@@ -285,7 +316,7 @@
 -- | Vec4:
 
 data Vec4 where
-  Vec4 :: (Vec1, Vec1, Vec1, Vec1) -> Vec4
+  Vec4 :: (Show tc, ConstructFrom tc Vec4) => tc -> Vec4
   V4u :: String -> Vec4
   V4uop :: String -> Vec4 -> Vec4
   V4uoppre :: String -> Vec4 -> Vec4
@@ -305,10 +336,11 @@
   vboppre = V4boppre
   select = V4select
   fromVec1 x = Vec4 (x, x, x, x)
+  toList x = [X x, Y x, Z x, W x]
 
 instance Show Vec4 where
   show expr = case expr of
-    Vec4 (x, y, z, w) -> "vec4(" <> show x <> ", " <> show y <> ", " <> show z <> ", " <> show w <> ")"
+    Vec4 tc -> "vec4" <> show tc
     V4u x -> x
     V4uop u x -> u <> "(" <> show x <> ")"
     V4uoppre u x -> "(" <> u <> show x <> ")"
@@ -361,6 +393,9 @@
   type Scalar Vec4 = Vec1
   a *^ b = V4bops "*" a b
 
+instance InnerSpace Vec4 where
+  (<.>) = Dot
+
 instance HasX Vec4
 instance HasY Vec4
 instance HasZ Vec4
@@ -381,6 +416,7 @@
   Buoppre :: String -> Booly -> Booly
   Bbop :: String -> Booly -> Booly -> Booly
   Bcomp :: (HyloPrim a) => String -> a -> a -> Booly
+  Bcomp_ :: String -> Vec1 -> Vec1 -> Booly
 
 instance Show Booly where
   show expr = case expr of
@@ -388,7 +424,8 @@
     Buop u x -> u <> "(" <> show x <> ")"
     Buoppre u x -> "(" <> u <> show x <> ")"
     Bbop u x y -> "(" <> show x <> " " <> u <> " " <>  show y <> ")"
-    Bcomp u x y -> "(" <> show x <> " " <> u <> " " <>  show y <> ")"
+    Bcomp u x y -> show . product $ zipWith (Bcomp_ u) (toList x) (toList y)
+    Bcomp_ u x y -> "(" <> show x <> " " <> u <> " " <>  show y <> ")"
 
 instance Num Booly where
   (+) = Bbop "||"
