diff --git a/README.md b/README.md
index 61dfc673e497453fe5f06ac238526e049a8c371f..7234f83427e2f6b7045707a54c748553e340a4f9 100644
--- a/README.md
+++ b/README.md
@@ -25,6 +25,11 @@ This project serves the developement environment / api we use to write and repre
   - rm modules
   - change settings 
 
+ - right now
+  - add module 
+  - l for load
+  - s for save
+
  - *don't forget*
   - onload, load state... 
   - when hooking back to server, server.emitChange(key) ... from writeStateObject 
diff --git a/client/client.js b/client/client.js
index 35db7fc2bc104f45e1eb193ad1ae89a9741ac706..e17fda15d216f39221c579991fb1113ef4783e23 100644
--- a/client/client.js
+++ b/client/client.js
@@ -111,11 +111,11 @@ function socketRecv(evt) {
         case 'put module':
             console.log('RECV NEW MODULE')
             heapSendsNewModule(data)
-            break 
+            break
         case 'put state':
             console.log('RECV STATE CHANGE')
             heapSendsNewState(data)
-            break 
+            break
         default:
             console.log('ERR recv with non recognized type', recv)
             break
@@ -144,7 +144,7 @@ function heapSendsModuleMenu(tree) {
             li.id = path
             li.addEventListener('click', function(evt) {
                 var data = this.id
-                socketSend('add module', data)
+                socketSend('put module', data)
                 wrapper.removeChild(document.getElementById('menu'))
             })
             ul.appendChild(li)
@@ -189,15 +189,15 @@ function heapSendsNewProgram(prgm) {
     redrawLinks()
 }
 
-function heapSendsNewModule(mdl){
-    if(program.description == null){
+function heapSendsNewModule(mdl) {
+    if (program.description == null) {
         program.description.name = 'unnamed program'
     }
-    if(program.modules == null){
+    if (program.modules == null) {
         program.modules = {}
     }
     addRepToView(mdl)
-    program.modules[mdl.description.id] = mdl 
+    program.modules[mdl.description.id] = mdl
     redrawLinks()
 }
 
@@ -224,19 +224,30 @@ function addRepToView(rep) {
     title.alt = rep.description.alt
     domElem.appendChild(title)
 
+    var uiSetFlag
     // place in pos if info present 
     // the rep.ui object will store references to the module's related DOM elements
-    if (rep.ui != null) {
-        if (rep.ui.left != null) {
-            domElem.style.left = rep.ui.left + 'px'
+    console.log(rep.description.position)
+    if (rep.description.position != null) {
+        console.log("FOUND POS")
+        uiSetFlag = false
+        if (rep.description.position.left != null) {
+            console.log("FOUND LEFT")
+            domElem.style.left = rep.description.position.left + 'px'
         }
-        if (rep.ui.top != null) {
-            domElem.style.top = rep.ui.top + 'px'
+        if (rep.description.position.top != null) {
+            console.log("FOUND TOP")
+            domElem.style.top = rep.description.position.top + 'px'
         }
     } else {
+        uiSetFlag = true
+        rep.description.position = {}
+        rep.description.position.left = lastPos.x
+        rep.description.position.top = lastPos.y
+    }
+
+    if(rep.ui == null){
         rep.ui = {}
-        rep.ui.left = lastPos.x
-        rep.ui.top = lastPos.y
     }
 
     rep.ui.domElem = domElem
@@ -292,8 +303,8 @@ function addRepToView(rep) {
         document.addEventListener('mousemove', domElemMouseMove)
 
         title.onmouseup = function() {
-            rep.ui.left = parseInt(domElem.style.left, 10)
-            rep.ui.top = parseInt(domElem.style.top, 10)
+            rep.description.position.left = parseInt(domElem.style.left, 10)
+            rep.description.position.top = parseInt(domElem.style.top, 10)
             putUi(rep)
             document.removeEventListener('mousemove', domElemMouseMove)
             title.onmouseup = null
@@ -301,6 +312,9 @@ function addRepToView(rep) {
     }
 
     wrapper.appendChild(rep.ui.domElem)
+    if(uiSetFlag){
+        putUi(rep)
+    }
 }
 
 // update state from server to UI
@@ -308,10 +322,10 @@ function heapSendsNewState(data) {
     console.log('HEAP SENDS CHANGE STATE IN MODULE', data)
     var rep = reps[data.id]
     for (var key in data.state) {
-        if (data.state[key].isButton) {
+        if (data.state[key].type == 'button') {
             console.log('BUTTON UPDATE')
-        } else if (data.state[key].isMultiLine) {
-            //console.log("rep", rep.ui.state[key].value, 'data', data.state[key].value)
+        } else if (data.state[key].type == 'multiline') {
+            console.log('MULTILINE UPDATE')
             rep.ui.state[key].value = data.state[key].value
         } else {
             // two ?
@@ -331,6 +345,7 @@ UI -> HEAP ---------------------------------------------------
 
 // push new state from UI to server 
 function putState(rep) {
+    // ship it all home: not perfect, but hey 
     var data = {
         id: rep.id,
         state: rep.state
@@ -341,10 +356,12 @@ function putState(rep) {
 // save ui position to server for reload
 function putUi(rep) {
     var data = {
-        id: rep.id,
-        ui: {
-            left: rep.ui.left,
-            top: rep.ui.top
+        description: {
+            id: rep.description.id,
+            position: {
+                left: rep.description.position.left,
+                top: rep.description.position.top
+            }
         }
     }
 
@@ -372,9 +389,9 @@ function redrawLinks() {
         svg.removeChild(svg.firstChild)
     }
     // redraw thru all links, just look at reps
-    for(mdlName in program.modules){
+    for (mdlName in program.modules) {
         var mdlRep = program.modules[mdlName]
-        for(key in mdlRep.outputs){
+        for (key in mdlRep.outputs) {
             var output = mdlRep.outputs[key]
             var outputUi = mdlRep.ui.outputs[key]
             for (input in output.calls) {
@@ -421,6 +438,35 @@ oncontextmenu = function(evt) {
     return false
 }
 
+document.onmousemove = function(evt){
+    lastPos.x = evt.pageX 
+    lastPos.y = evt.pageY
+}
+
+document.onkeydown = function(evt){
+    console.log(evt)
+    switch(evt.key){
+        case 'Escape':
+            location.reload()
+            break 
+        case 's':
+            // get path ? 
+            var path = prompt("path? starting at atkapi/programs/")
+            socketSend('save program', path)
+            break
+        case 'l':
+            console.log('LOADID')
+            break 
+        case 'm':
+            socketSend('get module menu', '')
+            break 
+        default:
+            break 
+    }
+}
+
+// input / output click hookups 
+
 var clkState = false
 var oClk = {}
 var tmpBz = {}
diff --git a/main.js b/main.js
index fd762dd08ce1966dfc2647974a5a12c49a9175c7..07ede52c69609cd9ba16c076cf75459e5cff101e 100644
--- a/main.js
+++ b/main.js
@@ -28,7 +28,7 @@ const Programs = require('./programs.js')
 
 var program = {} 
 
-program = Programs.open('save/onesave.json')
+program = Programs.open('programs/default.json')
 
 const View = require('./views.js')
 View.startHttp() 
diff --git a/programs.js b/programs.js
index 9ab2299e30f16379e81a60994eadc0d3a793fdc1..9959f93a988cbe6a749fd6b347672d74ecd3408b 100644
--- a/programs.js
+++ b/programs.js
@@ -1,4 +1,7 @@
 const fs = require('fs')
+
+const Reps = require('./reps.js')
+
 const JSUnit = require('./lib/jsunit.js')
 let isStateKey = JSUnit.isStateKey
 
@@ -16,7 +19,7 @@ function loadModuleFromSource(program, path) {
         }
 
         // make unique name 
-        mod.description.id = mod.description.id = mod.description.name + '-' + program.description.counter
+        mod.description.id = mod.description.name + '-' + program.description.counter
         mod.description.path = path
         
         // add to program object 
@@ -112,11 +115,13 @@ function saveProgram(prgmem, path) {
 
     for(key in mdls){
         var mdl = mdls[key]
-        var og = makeRepFromModule(mdl)
+        var og = Reps.makeFromModule(mdl)
         svprgmem.modules[mdl.description.id] = og 
     }
 
     fs.writeFileSync(path, JSON.stringify(svprgmem, null, 2), 'utf8')
+
+    console.log('PROGRAM SAVED AT', path)
 }
 
 
@@ -146,7 +151,9 @@ function openProgram(path){
     // restore saved state and links 
     for(modName in prgRep.modules){
         // keys should be identical for rep and heap
+        // this is the representation that we're opening up from 
         var mdlRep = prgRep.modules[modName]
+        // this is the actual object, having functions and whatnot 
         var mdl = program.modules[modName]
 
         // hooking outputs -> inputs 
@@ -172,6 +179,13 @@ function openProgram(path){
                 // rename .onChange for 'onUiChange' or something 
             }
         }
+
+        // restore position / UI state
+        if(mdlRep.description.position != null){
+            mdl.description.position = {}
+            mdl.description.position.left = mdlRep.description.position.left
+            mdl.description.position.top = mdlRep.description.position.top 
+        }
     }
 
     // once modules exist, link inputs / outputs / copy state ? 
diff --git a/programs/instron-prep.js b/programs/asif/instron-prep.js
similarity index 100%
rename from programs/instron-prep.js
rename to programs/asif/instron-prep.js
diff --git a/programs/instron.js b/programs/asif/instron.js
similarity index 100%
rename from programs/instron.js
rename to programs/asif/instron.js
diff --git a/programs/mothercnc.js b/programs/asif/mothercnc.js
similarity index 100%
rename from programs/mothercnc.js
rename to programs/asif/mothercnc.js
diff --git a/programs/servopwmtest.js b/programs/asif/servopwmtest.js
similarity index 100%
rename from programs/servopwmtest.js
rename to programs/asif/servopwmtest.js
diff --git a/programs/tstloadprgmem.js b/programs/asif/tstloadprgmem.js
similarity index 100%
rename from programs/tstloadprgmem.js
rename to programs/asif/tstloadprgmem.js
diff --git a/programs/tstprgmem.js b/programs/asif/tstprgmem.js
similarity index 100%
rename from programs/tstprgmem.js
rename to programs/asif/tstprgmem.js
diff --git a/save/twosave.json b/programs/default.json
similarity index 82%
rename from save/twosave.json
rename to programs/default.json
index 97421540da2a85a82027de9155330c52a0a90723..251b0a4023275c536b03576f62f39469500a647d 100644
--- a/save/twosave.json
+++ b/programs/default.json
@@ -9,7 +9,11 @@
         "id": "gate-1",
         "name": "gate",
         "alt": "in ... out",
-        "path": "./src/util/gate.js"
+        "path": "./src/util/gate.js",
+        "position": {
+          "left": 10,
+          "top": 10
+        }
       },
       "inputs": {
         "thru": {
@@ -23,10 +27,6 @@
             {
               "parentId": "delay-2",
               "key": "thru"
-            },
-            {
-              "parentId": "logger-3",
-              "key": "thru"
             }
           ]
         }
@@ -45,7 +45,11 @@
         "id": "delay-2",
         "name": "delay",
         "alt": "in ... out",
-        "path": "./src/util/delay.js"
+        "path": "./src/util/delay.js",
+        "position": {
+          "left": 132,
+          "top": 225
+        }
       },
       "inputs": {
         "thru": {
@@ -72,7 +76,11 @@
         "id": "logger-3",
         "name": "logger",
         "alt": "in ... out to console",
-        "path": "./src/util/log.js"
+        "path": "./src/util/log.js",
+        "position": {
+          "left": 123,
+          "top": 367
+        }
       },
       "inputs": {
         "thru": {
diff --git a/save/onesave.json b/programs/onesave.json
similarity index 100%
rename from save/onesave.json
rename to programs/onesave.json
diff --git a/programs/tst.json b/programs/tst.json
new file mode 100644
index 0000000000000000000000000000000000000000..c2219e53c6a6c3546db5b33e414eec9a0fb66b3c
--- /dev/null
+++ b/programs/tst.json
@@ -0,0 +1,112 @@
+{
+  "description": {
+    "name": "tstprgmem",
+    "counter": 4
+  },
+  "modules": {
+    "gate-1": {
+      "description": {
+        "id": "gate-1",
+        "name": "gate",
+        "alt": "in ... out",
+        "path": "./src/util/gate.js"
+      },
+      "inputs": {
+        "thru": {
+          "accepts": "any"
+        }
+      },
+      "outputs": {
+        "out": {
+          "emits": "any",
+          "calls": [
+            {
+              "parentId": "delay-2",
+              "key": "thru"
+            }
+          ]
+        }
+      },
+      "state": {
+        "toggle": {
+          "type": "button",
+          "isPressed": false,
+          "label": "Open / Close"
+        },
+        "message": "closed"
+      }
+    },
+    "delay-2": {
+      "description": {
+        "id": "delay-2",
+        "name": "delay",
+        "alt": "in ... out",
+        "path": "./src/util/delay.js"
+      },
+      "inputs": {
+        "thru": {
+          "accepts": "any"
+        }
+      },
+      "outputs": {
+        "out": {
+          "emits": "any",
+          "calls": [
+            {
+              "parentId": "logger-3",
+              "key": "thru"
+            }
+          ]
+        }
+      },
+      "state": {
+        "ms": 100
+      }
+    },
+    "logger-3": {
+      "description": {
+        "id": "logger-3",
+        "name": "logger",
+        "alt": "in ... out to console",
+        "path": "./src/util/log.js"
+      },
+      "inputs": {
+        "thru": {
+          "accepts": "any"
+        }
+      },
+      "outputs": {
+        "throughput": {
+          "emits": "any",
+          "calls": []
+        }
+      },
+      "state": {
+        "prefix": "LOGGER:",
+        "message": "---"
+      }
+    },
+    "delay-4": {
+      "description": {
+        "id": "delay-4",
+        "name": "delay",
+        "alt": "in ... out",
+        "path": "./src/util/delay.js"
+      },
+      "inputs": {
+        "thru": {
+          "accepts": "any"
+        }
+      },
+      "outputs": {
+        "out": {
+          "emits": "any",
+          "calls": []
+        }
+      },
+      "state": {
+        "ms": 100
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/reps.js b/reps.js
index 81a4fbeda41ae95ec00ad096a1f8053bf894d222..3f5883d900a6aa45e0499d3583f555cacc3839de 100644
--- a/reps.js
+++ b/reps.js
@@ -11,6 +11,10 @@ function makeRepFromModule(mdl) {
         }
     }
 
+    if(mdl.description.position){
+        rep.description.position = mdl.description.position
+    }
+
     // TODO: making rep. of input / output should be a f'n of that object ...
     // input, outputs, state objs should be known /sysobjects 
     // everything else is free play 
diff --git a/save/mdls.json b/save/mdls.json
deleted file mode 100644
index 167d4ffa273a3b01b6e3f6304b4f4952ac8872f2..0000000000000000000000000000000000000000
--- a/save/mdls.json
+++ /dev/null
@@ -1 +0,0 @@
-[{"description":{"name":"gate","alt":"in ... out"},"state":{"emitters":{},"toggle":{"isButton":true,"isPressed":false,"label":"Open / Close"},"message":"closed","_toggle":{"isButton":true,"isPressed":false,"label":"Open / Close"},"_message":"closed"},"isOpen":false,"inputs":{"thru":{"accepts":"any","parentId":0,"key":"thru"}},"outputs":{"out":{"emits":"any","calls":[{"accepts":"any","parentId":1,"key":"thru"},{"accepts":"any","parentId":2,"key":"thru"},{"accepts":"any","parentId":3,"key":"reset"},{"accepts":"event","parentId":4,"key":"trigger"},{"accepts":"event","parentId":8,"key":"rmtrig"},{"accepts":"event","parentId":9,"key":"rmtrig"}]}},"id":0,"path":"./src/util/gate.js","ui":{"left":160,"top":130}},{"description":{"name":"delay!","alt":"in ... out"},"state":{"emitters":{},"ms":100,"_ms":100},"inputs":{"thru":{"accepts":"any","parentId":1,"key":"thru"}},"outputs":{"out":{"emits":"any","calls":[{"accepts":"any","parentId":3,"key":"A"}]}},"id":1,"path":"./src/util/delay.js","ui":{"left":160,"top":260}},{"description":{"name":"logger!","alt":"in ... out to console"},"state":{"emitters":{},"prefix":"LOGGER:","message":"---","_prefix":"LOGGER:","_message":"---"},"inputs":{"thru":{"accepts":"any","parentId":2,"key":"thru"}},"id":2,"path":"./src/util/log.js","ui":{"left":160,"top":400}},{"description":{"name":"andflow","alt":"in ... out"},"state":{"emitters":{},"toggle":{"isButton":true,"isPressed":false,"label":"Reset"},"A":0,"B":0,"_toggle":{"isButton":true,"isPressed":false,"label":"Reset"},"_A":0,"_B":0},"inputs":{"reset":{"accepts":"any","parentId":3,"key":"reset"},"A":{"accepts":"any","parentId":3,"key":"A"},"B":{"accepts":"any","parentId":3,"key":"B"}},"outputs":{"out":{"emits":"any","calls":[{"accepts":"any","parentId":0,"key":"thru"}]}},"id":3,"path":"./src/flowcontrol/and.js","ui":{"left":700,"top":300}},{"description":{"name":"CAMERA Request","alt":"webcam","isHardware":true},"state":{"emitters":{},"button":{"isButton":true,"isPressed":false,"label":"REQUEST IMAGE"},"counter":0,"_button":{"isButton":true,"isPressed":false,"label":"REQUEST IMAGE"},"_counter":0},"inputs":{"trigger":{"accepts":"event","parentId":4,"key":"trigger"}},"outputs":{"image":{"emits":"image","calls":[]},"callback":{"emits":"event","calls":[{"accepts":"any","parentId":3,"key":"B"}]}},"id":4,"path":"./src/hardware/webcam.js","ui":{"left":500,"top":700}},{"description":{"name":"Breadboard ADC Request","alt":"bbadc","isHardware":true},"state":{"emitters":{},"button":{"isButton":true,"isPressed":false,"label":"REQUEST CONVERSION"},"adcVal":0,"_button":{"isButton":true,"isPressed":false,"label":"REQUEST CONVERSION"},"_adcVal":0},"inputs":{"packet":{"accepts":"headless packet","parentId":5,"key":"packet"},"request":{"accepts":"event","parentId":5,"key":"request"}},"outputs":{"packet":{"emits":"number","calls":[{"accepts":"headless packet","parentId":7,"key":"B"}]}},"id":5,"path":"./src/hardware/adc.js","ui":{"left":1629,"top":180}},{"description":{"name":"Test Packet","alt":"net state","isHardware":true},"state":{"emitters":{},"button":{"isButton":true,"isPressed":false,"label":"TEST"},"message":"no test started","_button":{"isButton":true,"isPressed":false,"label":"TEST"},"_message":"no test started"},"inputs":{"packet":{"accepts":"headless packet","parentId":6,"key":"packet"},"trigger":{"accepts":"event","parentId":6,"key":"trigger"}},"outputs":{"packet":{"emits":"number","calls":[{"accepts":"headless packet","parentId":7,"key":"B"}]}},"id":6,"path":"./src/hardware/test.js","ui":{"left":1060,"top":918}},{"description":{"name":"ATK Hardware Bridge","alt":"talks over serialport","isHardware":true},"state":{"emitters":{},"rA":"0,0","rB":"0,1","rC":"0,2","rD":"0,3","rE":"0,4","rF":"0,5","rG":"0","findPort":{"isButton":true,"isPressed":false,"label":"click to find atk port"},"portName":"---","connect":{"isButton":true,"isPressed":false,"label":"click to connect"},"portStatus":"closed","terminal":"address | key:values","sendRawPacket":{"isButton":true,"isPressed":false,"label":"sendRaw"},"_rA":"0,0","_rB":"0,1","_rC":"0,2","_rD":"0,3","_rE":"0,4","_rF":"0,5","_rG":"0","_findPort":{"isButton":true,"isPressed":false,"label":"click to find atk port"},"_portName":"---","_connect":{"isButton":true,"isPressed":false,"label":"click to connect"},"_portStatus":"closed","_terminal":"address | key:values","_sendRawPacket":{"isButton":true,"isPressed":false,"label":"sendRaw"}},"inputs":{"A":{"accepts":"headless packet","parentId":7,"key":"A"},"B":{"accepts":"headless packet","parentId":7,"key":"B"},"C":{"accepts":"headless packet","parentId":7,"key":"C"},"D":{"accepts":"headless packet","parentId":7,"key":"D"},"E":{"accepts":"headless packet","parentId":7,"key":"E"},"F":{"accepts":"headless packet","parentId":7,"key":"F"},"G":{"accepts":"headless packet","parentId":7,"key":"G"}},"outputs":{"A":{"emits":"headless packet","calls":[{"accepts":"headless packet","parentId":8,"key":"packet"},{"accepts":"headless packet","parentId":10,"key":"packet"}]},"B":{"emits":"headless packet","calls":[{"accepts":"headless packet","parentId":5,"key":"packet"},{"accepts":"headless packet","parentId":6,"key":"packet"}]},"C":{"emits":"headless packet","calls":[]},"D":{"emits":"headless packet","calls":[]},"E":{"emits":"headless packet","calls":[]},"F":{"emits":"headless packet","calls":[{"accepts":"headless packet","parentId":9,"key":"packet"},{"accepts":"headless packet","parentId":11,"key":"packet"}]},"G":{"emits":"headless packet","calls":[]}},"pairs":{"A":{"route":"0,0","output":{"emits":"headless packet","calls":[{"accepts":"headless packet","parentId":8,"key":"packet"},{"accepts":"headless packet","parentId":10,"key":"packet"}]}},"B":{"route":"0,1","output":{"emits":"headless packet","calls":[{"accepts":"headless packet","parentId":5,"key":"packet"},{"accepts":"headless packet","parentId":6,"key":"packet"}]}},"C":{"route":"0,2","output":{"emits":"headless packet","calls":[]}},"D":{"route":"0,3","output":{"emits":"headless packet","calls":[]}},"E":{"route":"0,4","output":{"emits":"headless packet","calls":[]}}},"id":7,"path":"./src/hardware/bridge.js","ui":{"left":1629,"top":890}},{"description":{"name":"ATK Network Stepper Driver","alt":"software representation of stepper","isHardware":true},"state":{"emitters":{},"axis":"Y","spu":75,"rawMove":0.25,"makeMove":{"isButton":true,"isPressed":false,"label":"test move"},"lead":0,"position":0,"_axis":"Y","_spu":75,"_rawMove":0.25,"_makeMove":{"isButton":true,"isPressed":false,"label":"test move"},"_lead":0,"_position":0},"inputs":{"move":{"accepts":"move instruction","parentId":8,"key":"move"},"packet":{"accepts":"headless packet","parentId":8,"key":"packet"},"rmtrig":{"accepts":"event","parentId":8,"key":"rmtrig"}},"outputs":{"ack":{"emits":"move acknowledgement","calls":[]},"q":{"emits":"number","calls":[]},"packet":{"emits":"headless packet","calls":[{"accepts":"headless packet","parentId":7,"key":"A"}]}},"id":8,"path":"./src/hardware/stepper.js","ui":{"left":1629,"top":372}},{"description":{"name":"ATK Network Stepper Driver","alt":"software representation of stepper","isHardware":true},"state":{"emitters":{},"axis":"Y","spu":-75,"rawMove":0.25,"makeMove":{"isButton":true,"isPressed":false,"label":"test move"},"lead":0,"position":0,"_axis":"Y","_spu":-75,"_rawMove":0.25,"_makeMove":{"isButton":true,"isPressed":false,"label":"test move"},"_lead":0,"_position":0},"inputs":{"move":{"accepts":"move instruction","parentId":9,"key":"move"},"packet":{"accepts":"headless packet","parentId":9,"key":"packet"},"rmtrig":{"accepts":"event","parentId":9,"key":"rmtrig"}},"outputs":{"ack":{"emits":"move acknowledgement","calls":[]},"q":{"emits":"number","calls":[]},"packet":{"emits":"headless packet","calls":[{"accepts":"headless packet","parentId":7,"key":"F"}]}},"id":9,"path":"./src/hardware/stepper.js","ui":{"left":1629,"top":626}},{"description":{"name":"Test Packet","alt":"net state","isHardware":true},"state":{"emitters":{},"button":{"isButton":true,"isPressed":false,"label":"TEST"},"message":"no test started","_button":{"isButton":true,"isPressed":false,"label":"TEST"},"_message":"no test started"},"inputs":{"packet":{"accepts":"headless packet","parentId":10,"key":"packet"},"trigger":{"accepts":"event","parentId":10,"key":"trigger"}},"outputs":{"packet":{"emits":"number","calls":[{"accepts":"headless packet","parentId":7,"key":"A"}]}},"id":10,"path":"./src/hardware/test.js","ui":{"left":1060,"top":1050}},{"description":{"name":"Test Packet","alt":"net state","isHardware":true},"state":{"emitters":{},"button":{"isButton":true,"isPressed":false,"label":"TEST"},"message":"no test started","_button":{"isButton":true,"isPressed":false,"label":"TEST"},"_message":"no test started"},"inputs":{"packet":{"accepts":"headless packet","parentId":11,"key":"packet"},"trigger":{"accepts":"event","parentId":11,"key":"trigger"}},"outputs":{"packet":{"emits":"number","calls":[{"accepts":"headless packet","parentId":7,"key":"F"}]}},"id":11,"path":"./src/hardware/test.js","ui":{"left":1060,"top":1170}}]
\ No newline at end of file
diff --git a/save/otms.json b/save/otms.json
deleted file mode 100644
index aa0f88040398157147534212ba8a81523a4361cc..0000000000000000000000000000000000000000
--- a/save/otms.json
+++ /dev/null
@@ -1,104 +0,0 @@
-{
-    "description":
-    {
-        "name": "tstprgmem"
-    },
-    "modules": [
-    {
-        "description":
-        {
-            "id": "gate-1",
-            "name": "gate",
-            "alt": "in ... out",
-            "path": "./src/util/gate.js"
-        },
-        "inputs":
-        {
-            "thru":
-            {
-                "accepts": "any"
-            }
-        },
-        "outputs":
-        {
-            "out":
-            {
-                "emits": "any",
-                "calls": [
-                {
-                    "parentId": "delay-2",
-                    "key": "thru"
-                },
-                {
-                    "parentId": "logger-3",
-                    "key": "thru"
-                }]
-            }
-        },
-        "state":
-        {
-            "toggle":
-            {
-                "isButton": true,
-                "isPressed": false,
-                "label": "Open / Close"
-            },
-            "message": "closed"
-        }
-    },
-    {
-        "description":
-        {
-            "id": "delay-2",
-            "name": "delay",
-            "alt": "in ... out",
-            "path": "./src/util/delay.js"
-        },
-        "inputs":
-        {
-            "thru":
-            {
-                "accepts": "any"
-            }
-        },
-        "outputs":
-        {
-            "out":
-            {
-                "emits": "any",
-                "calls": [
-                {
-                    "parentId": "logger-3",
-                    "key": "thru"
-                }]
-            }
-        },
-        "state":
-        {
-            "ms": 100
-        }
-    },
-    {
-        "description":
-        {
-            "id": "logger-3",
-            "name": "logger",
-            "alt": "in ... out to console",
-            "path": "./src/util/log.js"
-        },
-        "inputs":
-        {
-            "thru":
-            {
-                "accepts": "any"
-            }
-        },
-        "outputs":
-        {},
-        "state":
-        {
-            "prefix": "LOGGER:",
-            "message": "---"
-        }
-    }]
-}
\ No newline at end of file
diff --git a/views.js b/views.js
index 57bfa9d0fa32c409a7c14f7cbac4f484fec3c28c..951bf74180f085dd9cbea1165be8c137570a4223 100644
--- a/views.js
+++ b/views.js
@@ -9,6 +9,7 @@ const http = require('http').Server(app)
 const WebSocket = require('ws')
 
 const Reps = require('./reps.js')
+const Programs = require('./programs.js')
 
 /*
 
@@ -87,8 +88,11 @@ function socketRecv(evt) {
             uiRequestModuleMenu()
             break
         case 'load program':
-            uiRequestLoadProgram()
+            uiRequestLoadProgram(data)
             break
+        case 'save program':
+            uiRequestSaveProgram(data)
+            break 
         case 'put module':
             uiRequestNewModule(data)
             break
@@ -137,7 +141,6 @@ function uiRequestCurrentProgram() {
         },
         modules: {}
     }
-
     for (mdlName in program.modules) {
         var mdlRep = Reps.makeFromModule(program.modules[mdlName])
         prgRep.modules[mdlName] = mdlRep
@@ -160,7 +163,6 @@ function uiRequestModuleMenu() {
             }
         }
     }
-
     socketSend('put module menu', availableSourceRep)
 }
 
@@ -174,13 +176,27 @@ function uiRequestLoadProgram(data){
     console.log('UI REQUEST TO OPEN NEW PROGRAM', data)
 }
 
+function uiRequestSaveProgram(data){
+    console.log('UI REQUEST TO SAVE PROGRAM', data)
+    // is data a path? add .json ? 
+    if(!data.includes('.json')){
+        data = data + '.json'
+    }
+    path = 'programs/' + data 
+    Programs.save(program, path)
+}
+
 function uiRequestNewModule(data) {
     console.log('UI REQUEST ADD MODULE TO PROGRAM', data)
+    Programs.loadModuleFromSource(program, data)
+    // bit of a mess to pick out the last entered module 
+    var keys = Object.keys(program.modules)
+    socketSend('put module', Reps.makeFromModule(program.modules[keys[keys.length - 1]]))
 }
 
 function uiRequestStateChange(data) {
     console.log('UI REQUEST CHANGE STATE IN MODULE', data)
-    // do state.obj.emit 
+    // and don't forget state.obj.emit 
 }
 
 function uiRequestLinkChange(data) {
@@ -189,7 +205,8 @@ function uiRequestLinkChange(data) {
 
 function uiRequestUiChange(data) {
     console.log('UI REQUEST ADD / CHANGE UI INFO TO MODULE', data)
-    // do it in module.description 
+    var mod = program.modules[data.description.id]
+    mod.description.position = data.description.position
 }
 
 /*