diff --git a/README.md b/README.md
index 5fa0a45d00fc5aafcacfabcf1ef457ecb1cb1eba..b72a8ea34de2b49f38c0c4378cee93c9864dd1fd 100644
--- a/README.md
+++ b/README.md
@@ -2,15 +2,39 @@
 
 ## oy
 
+- not yet disconnecting events
+
+- some styling 
+ - state 'squishes' when module moved too far right 
+ - state 'input' length / title length for long titles ?
+
+- state context menu or button - like 'fire this listener' on click, or get alt description 
+
+- bridge should be able to look for devices, test devices, and do terminal stuff 
+
+- right-click on title of module, reload 
+
+- for crashes etc, how can we re-start / kick serverside from the browser? some other daemon program ?
+
+- probably shouldn't send *all* of the state every time ... ? 
+ - althought it's nice and foolproof 
+
+- still want more diverse html inputs ... i.e. text input with some size to it ? gcode parser for this ... 
+
+- write in logger functions
+ - i.e. module.log('whatever') is wrapped at load into console.log() and sends serverside log message as well, with ID and name 
+
 - state variables good
 - final steps before writing gcode consumption program 
- - write ui position back to server 
- - nomenclature all over the place is spaget 
- - cleaner replace ? wholistic replace ?
- - classes / pretty code ? find desires by using 
+ - nomenclature all over the place is spaget
+ - rewriting this is a good reintroduction to project 
+  - cleaner replace ? wholistic replace ?
+  - classes / pretty code ? find desires by using 
  - any kind of save / load 
   - still with script-type writable file ? 
 
+ - but largely ready to write planner, serialport and packet parsing modules, do gcode stuff 
+
 - server modules are ground truth
 - to the client, we serve representations of these modules
 - on the client, we keep a copy of this rep , and build a UI for it, ``rep.ui``
@@ -35,6 +59,9 @@ http://backbonejs.org/#Events
 
 ## Desires
  - load / keep state 
+ - run headless with view into 
+ - collaborative program editing would be cool 
+ - program save / load / edit ?
 
 ## Model Consistency
 
diff --git a/client/client.js b/client/client.js
index 861d6b04c46b5afd005a44e34c92c24e2466256d..0ed360dfa4e3ca1b8a2fcb314010e928215419d4 100644
--- a/client/client.js
+++ b/client/client.js
@@ -28,11 +28,7 @@ oncontextmenu = function(evt) {
     if (sckt) {
         lastPos.X = evt.pageX
         lastPos.Y = evt.pageY
-        var req = {
-            type: 'get menu',
-            data: ''
-        }
-        sckt.send(JSON.stringify(req))
+        socketSend('get menu', '')
     } else {
         // socket brkn 
         location.reload()
@@ -86,6 +82,7 @@ function addReps(reps) {
 function addRep(rep) {
     // a div to locate it 
     var domElem = document.createElement('div')
+    // dif. color for hardwares ?
     domElem.className = 'block'
     domElem.style.left = lastPos.X + 'px'
     domElem.style.top = lastPos.Y + 'px'
diff --git a/client/divtools.js b/client/divtools.js
index 0ef83161d8222965cb3823aac64c978f28907fe0..119212dc64efc68e051dd731b8b2920422dfb22f 100644
--- a/client/divtools.js
+++ b/client/divtools.js
@@ -1,37 +1,54 @@
 // writing representations
 
-function writeStateRep(container, rep, key){
+function writeStateRep(container, rep, key) {
     var variable = rep.state[key]
-    if(typeof variable == 'string'){
-        var li  = document.createElement('li')
-        li.appendChild(document.createTextNode(key))
-        var input = document.createElement('input')
-        input.type = 'text'
-        input.size = 24
-        input.value = variable
-        input.addEventListener('change', function(){
-            rep.state[key] = input.value
-            putState(rep)
-        })
-        li.appendChild(input)
-        container.appendChild(li)
-        return input
-    } else if (typeof variable == 'number'){
+    if (variable.isButton) {
+        console.log('BUTTON!')
         var li = document.createElement('li')
-        li.appendChild(document.createTextNode(key))
-        var input = document.createElement('input')
-        input.type = 'text'
-        input.size = 24
-        input.value = variable.toString()
-        input.addEventListener('change', function(){
-            rep.state[key] = parseFloat(input.value)
+        li.appendChild(document.createTextNode(variable.label))
+        li.addEventListener('click', function() {
+            // invert 
+            if(rep.state[key].isPressed){
+                rep.state[key].isPressed = false
+            } else {
+                rep.state[key].isPressed = true
+            }
             putState(rep)
         })
-        li.appendChild(input)
         container.appendChild(li)
-        return input
+        return li 
     } else {
-        console.log("unui'd type:", typeof variable)
+        if (typeof variable == 'string') {
+            var li = document.createElement('li')
+            li.appendChild(document.createTextNode(key))
+            var input = document.createElement('input')
+            input.type = 'text'
+            input.size = 24
+            input.value = variable
+            input.addEventListener('change', function() {
+                rep.state[key] = input.value
+                putState(rep)
+            })
+            li.appendChild(input)
+            container.appendChild(li)
+            return input
+        } else if (typeof variable == 'number') {
+            var li = document.createElement('li')
+            li.appendChild(document.createTextNode(key))
+            var input = document.createElement('input')
+            input.type = 'text'
+            input.size = 24
+            input.value = variable.toString()
+            input.addEventListener('change', function() {
+                rep.state[key] = parseFloat(input.value)
+                putState(rep)
+            })
+            li.appendChild(input)
+            container.appendChild(li)
+            return input
+        } else {
+            console.log("unui'd type:", typeof variable)
+        }
     }
 }
 
@@ -90,7 +107,7 @@ function modifyBezierTail(bz, x2, y2) {
     redrawBezier(bz)
 }
 
-function getOutputArrow(div){
+function getOutputArrow(div) {
     var x = div.offsetParent.offsetLeft + div.offsetLeft + div.clientWidth
     var y = div.offsetParent.offsetTop + div.offsetTop + div.clientHeight / 2
     var pos = {
@@ -101,7 +118,7 @@ function getOutputArrow(div){
     return pos
 }
 
-function getInputArrow(div){
+function getInputArrow(div) {
     var x = div.offsetParent.offsetLeft
     var y = div.offsetParent.offsetTop + div.offsetTop + div.clientHeight / 2
     var pos = {
diff --git a/client/style.css b/client/style.css
index 5d557999e50422bb15f9fb0755802666b5355669..0b06c0e286dc5c37b74873e16f8fa6e40447ddaa 100644
--- a/client/style.css
+++ b/client/style.css
@@ -34,9 +34,11 @@ body {
 	color: #eee;
 }
 
+/*
 .state li:hover {
 	background-color: #303030;
 }
+*/
 
 .state input {
 	background-color: #1a1a1a;
@@ -82,7 +84,11 @@ li {
 }
 
 li:hover{
-	background-color: #000;
+	background-color: #969696;
+}
+
+li:active{
+	background-color: #d1d1d1;
 }
 
 #menu {
diff --git a/lib/inout.js b/lib/inout.js
index e0d05f0d39288eb182db2994a7444c38106737ce..a24abf480bd63508894020345b4d376bb16071ab 100644
--- a/lib/inout.js
+++ b/lib/inout.js
@@ -54,10 +54,22 @@ function State(){
 	return state
 }
 
+// within state ... or not ? 
+function Button(label){
+	var button = {
+		isButton: true,
+		isPressed: false,
+		label: label
+	}
+
+	return button 
+}
+
 module.exports = {
 	Input: Input,
 	Output: Output,
-	State: State
+	State: State,
+	Button: Button
 }
 
 /*
diff --git a/package-lock.json b/package-lock.json
index f6b39aee788f79d0344d7a0983766c9a6f8d1b78..b46f78106464b85d6b3e9258ffe61a04d7d7c093 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -356,6 +356,11 @@
       "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
       "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
     },
+    "async-limiter": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz",
+      "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg=="
+    },
     "bindings": {
       "version": "1.3.0",
       "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.3.0.tgz",
@@ -483,6 +488,11 @@
       "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz",
       "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg=="
     },
+    "complex.js": {
+      "version": "2.0.11",
+      "resolved": "https://registry.npmjs.org/complex.js/-/complex.js-2.0.11.tgz",
+      "integrity": "sha512-6IArJLApNtdg1P1dFtn3dnyzoZBEF0MwMnrfF1exSBRpZYoy4yieMkpZhQDC0uwctw48vii0CFVyHfpgZ/DfGw=="
+    },
     "component-emitter": {
       "version": "1.2.1",
       "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
@@ -531,6 +541,11 @@
         "ms": "2.0.0"
       }
     },
+    "decimal.js": {
+      "version": "10.0.1",
+      "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.0.1.tgz",
+      "integrity": "sha512-vklWB5C4Cj423xnaOtsUmAv0/7GqlXIgDv2ZKDyR64OV3OSzGHNx2mk4p/1EKnB5s70k73cIOOEcG9YzF0q4Lw=="
+    },
     "decompress-response": {
       "version": "3.3.0",
       "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz",
@@ -600,6 +615,11 @@
       "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
       "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
     },
+    "escape-latex": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/escape-latex/-/escape-latex-1.1.1.tgz",
+      "integrity": "sha512-N2D6Z2kXh8x/pQNQH+natXDCwrzghhXMRII5dZ518mlTLeuba80NL0LCQyaahqOrAidoLivmmG6GKPnGhHse+A=="
+    },
     "etag": {
       "version": "1.8.1",
       "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
@@ -712,6 +732,11 @@
       "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
       "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ="
     },
+    "fraction.js": {
+      "version": "4.0.9",
+      "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.0.9.tgz",
+      "integrity": "sha512-qP1sNwdrcA+Vs5TTvGETuaaUmz4Tm48V6Jc+8Oh/gqvkb1d42s99w5kvSrZkIATp/mz3rV4CTef6xINkCofu+A=="
+    },
     "fresh": {
       "version": "0.5.2",
       "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
@@ -876,6 +901,11 @@
       "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
       "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8="
     },
+    "javascript-natural-sort": {
+      "version": "0.7.1",
+      "resolved": "https://registry.npmjs.org/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz",
+      "integrity": "sha1-+eIwPUUH9tdDVac2ZNFED7Wg71k="
+    },
     "kind-of": {
       "version": "3.2.2",
       "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
@@ -928,6 +958,21 @@
         "object-visit": "1.0.1"
       }
     },
+    "mathjs": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/mathjs/-/mathjs-5.1.2.tgz",
+      "integrity": "sha512-ewA6m6omkS+VPkU6Qd60T2oPVvACG4r8ajD6M6OaWbO0nMQ4wr0ZHFHRjfuzpTb96Y5HT82rTeNqGGktBa5G/w==",
+      "requires": {
+        "complex.js": "2.0.11",
+        "decimal.js": "10.0.1",
+        "escape-latex": "1.1.1",
+        "fraction.js": "4.0.9",
+        "javascript-natural-sort": "0.7.1",
+        "seed-random": "2.2.0",
+        "tiny-emitter": "2.0.2",
+        "typed-function": "1.1.0"
+      }
+    },
     "media-typer": {
       "version": "0.3.0",
       "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
@@ -1526,6 +1571,11 @@
       "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
       "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
     },
+    "seed-random": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/seed-random/-/seed-random-2.2.0.tgz",
+      "integrity": "sha1-KpsZ4lCoFwmSMaW5mk2vgLf77VQ="
+    },
     "semver": {
       "version": "5.5.0",
       "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz",
@@ -1833,6 +1883,11 @@
       "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-1.1.0.tgz",
       "integrity": "sha1-dkpaEa9QVhkhsTPztE5hhofg9cM="
     },
+    "tiny-emitter": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.0.2.tgz",
+      "integrity": "sha512-2NM0auVBGft5tee/OxP4PI3d8WItkDM+fPnaRAVo6xTDI2knbz9eC5ArWGqtGlYqiH3RU5yMpdyTTO7MguC4ow=="
+    },
     "to-buffer": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz",
@@ -1871,6 +1926,11 @@
         "mime-types": "2.1.19"
       }
     },
+    "typed-function": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/typed-function/-/typed-function-1.1.0.tgz",
+      "integrity": "sha512-TuQzwiT4DDg19beHam3E66oRXhyqlyfgjHB/5fcvsRXbfmWPJfto9B4a0TBdTrQAPGlGmXh/k7iUI+WsObgORA=="
+    },
     "unpipe": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
@@ -1933,6 +1993,14 @@
       "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
       "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
     },
+    "ws": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/ws/-/ws-6.0.0.tgz",
+      "integrity": "sha512-c2UlYcAZp1VS8AORtpq6y4RJIkJ9dQz18W32SpR/qXGfLDZ2jU4y4wKvvZwqbi7U6gxFQTeE+urMbXU/tsDy4w==",
+      "requires": {
+        "async-limiter": "1.0.0"
+      }
+    },
     "xtend": {
       "version": "4.0.1",
       "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz",
diff --git a/package.json b/package.json
index 2fb0e633c3b106b4c28e65247b879618a0f6e6e4..66dba07163d90e70adad0bb1fe82269e1feed12c 100644
--- a/package.json
+++ b/package.json
@@ -9,6 +9,7 @@
   "dependencies": {
     "express": "^4.16.3",
     "http": "0.0.0",
+    "mathjs": "^5.1.2",
     "readline": "^1.3.0",
     "serialport": "^6.2.2",
     "ws": "^6.0.0"
diff --git a/server.js b/server.js
index bd8b953c71d154aee65774fc7da736d7edd58d4b..8ae6379ddd6cab224de9061e25d2abe4145a9f3e 100644
--- a/server.js
+++ b/server.js
@@ -103,38 +103,62 @@ function socketRecv(evt) {
 }
 
 /*
-
+------------------------------------------------------
 PROGRAM AS API
-
+------------------------------------------------------
 */
 
 var modules = new Array()
 
 // wrap require() up, appending path used to object, and giving ID
 // use the same to load from browser 
-var term = addModule('./src/ui/terminal.js')
-var gcode = addModule('./src/parsing/gcode.js')
+//var term = addModule('./src/ui/terminal.js')
+//var gcode = addModule('./src/parsing/gcode.js')
+//var planner = addModule('./src/motion/planner.js')
+var stepper = addModule('./src/hardware/stepper.js')
+var bridge = addModule('./src/hardware/bridge.js')
 
 // attaching an output to an input
-term.outputs.lineOut.attach(gcode.inputs.lineIn)
+// term.outputs.lineOut.attach(gcode.inputs.lineIn)
 
 // setting ui elements / state 
-term.state.uiInput = "G1 F100 X10 Y10"
+// term.state.uiInput = "G1 F100 X10 Y10"
 // gcode.ui.G0.set(1255)
 
+stepper.state.address = '0,2'
+stepper.outputs.packet.attach(bridge.inputs.B)
+bridge.outputs.B.attach(stepper.inputs.packet)
+
+// gcode.outputs.instructionOut.attach(planner.inputs.instruction)
+// planner.outputs.move.attach(stepper.inputs.move)
+
 // setting data w/r/t the representations they serve 
+/*
 term.ui = {}
 term.ui.left = 10
 term.ui.top = 10
 
 gcode.ui = {}
-gcode.ui.left = 200
-gcode.ui.top = 400
+gcode.ui.left = 50
+gcode.ui.top = 100
 
-/*
+planner.ui = {}
+planner.ui.left = 100
+planner.ui.top = 250
+*/
 
-PROGRAM WRITING 
+stepper.ui = {}
+stepper.ui.left = 150
+stepper.ui.top = 450
+
+bridge.ui = {}
+bridge.ui.left = 200
+bridge.ui.top = 650
 
+/*
+------------------------------------------------------
+PROGRAM WRITING 
+------------------------------------------------------
 */
 
 function addModule(path) {
@@ -149,7 +173,7 @@ function addModule(path) {
         mod.path = path
 
         // we need to add some top-level info to the inputs so that we can draw them 
-        for (item in mod.inputs){
+        for (item in mod.inputs) {
             mod.inputs[item].parentId = mod.id
             mod.inputs[item].key = item
         }
@@ -211,7 +235,7 @@ function putReps() {
     socketSend('add reps', reps)
 }
 
-function makeRep(mod){
+function makeRep(mod) {
     // scrape for only things we'll want to represent 
     var rep = {
         id: mod.id,
@@ -222,12 +246,12 @@ function makeRep(mod){
     }
 
     // holds UI information - to load mods at location
-    if(mod.ui != null){
+    if (mod.ui != null) {
         rep.ui = mod.ui
     }
 
-    for(var key in mod.state){
-        if(isStateKey(key)){
+    for (var key in mod.state) {
+        if (isStateKey(key)) {
             rep.state[key] = mod.state[key]
         }
     }
@@ -239,7 +263,7 @@ function putRep(mod) {
     socketSend('add rep', makeRep(mod))
 }
 
-function changeRep(mod){
+function changeRep(mod) {
     socketSend('change rep', makeRep(mod))
 }
 
@@ -252,7 +276,12 @@ function changeState(data) {
     // rep only holds proper key-values w/o emitters, _values etc
     for (var key in newState) {
         if (isStateKey(key)) {
-            if (oldState[key] !== newState[key]) {
+            if (oldState[key].isButton) {
+                if (oldState[key].isPressed != newState[key].isPressed) {
+                    console.log('CHANGE STATE', key, 'to', newState[key], 'in', data.id)
+                    oldState[key] = newState[key]
+                }
+            } else if (oldState[key] !== newState[key]) {
                 console.log('CHANGE STATE', key, 'to', newState[key], 'in', data.id)
                 oldState[key] = newState[key]
             }
@@ -260,9 +289,9 @@ function changeState(data) {
     }
 }
 
-function changeUi(data){
+function changeUi(data) {
     var mod = modules[data.id]
-    if(mod.ui == null){
+    if (mod.ui == null) {
         console.log("NO UI")
         mod.ui = {}
     }
@@ -270,8 +299,8 @@ function changeUi(data){
     console.log('CHANGE UI', mod.id, mod.ui)
 }
 
-function isStateKey(key){
-    if(key.indexOf('_') == 0 || key == 'emitters' || key == 'onChange' || key == 'emitChange'){
+function isStateKey(key) {
+    if (key.indexOf('_') == 0 || key == 'emitters' || key == 'onChange' || key == 'emitChange') {
         return false
     } else {
         return true
@@ -283,8 +312,8 @@ function putState(mod) {
     // push just the state to the individual mod
     // copy only the keys we want to see 
     var state = {}
-    for (var key in mod.state){
-        if(isStateKey(key)){
+    for (var key in mod.state) {
+        if (isStateKey(key)) {
             state[key] = mod.state[key]
         }
     }
diff --git a/src/hardware/bridge.js b/src/hardware/bridge.js
new file mode 100644
index 0000000000000000000000000000000000000000..40f889bb26cc0fa927ff5320b7b520c85ba961ed
--- /dev/null
+++ b/src/hardware/bridge.js
@@ -0,0 +1,174 @@
+// boilerplate atkapi header
+const InOut = require('../../lib/inout.js')
+let Input = InOut.Input
+let Output = InOut.Output
+
+let State = InOut.State
+let Button = InOut.Button
+
+const serialport = require('serialport')
+
+function ATKBridge() {
+    var bridge = {
+        description: {
+            name: 'ATK Hardware Bridge',
+            alt: 'talks over serialport',
+            isHardware: true 
+        }
+    }
+
+    bridge.state = State()
+    var state = bridge.state
+
+    state.rA = '0,0'
+    state.rB = '0,1'
+
+    state.findPort = Button('click to find atk port')
+    state.portName = '---'
+    state.connect = Button('click to connect')
+    state.portStatus = 'closed' // or we hope it will be 
+    state.terminal = 'address | key:values'
+    state.sendRawPacket = Button('sendRaw')
+
+    /*
+	------------------------------------------------------
+	SERIALPORT MANAGEMENT 
+	------------------------------------------------------
+	*/
+
+    port = null
+
+    state.onChange('findPort', findPort)
+
+    function findPort() {
+        serialport.list(function(err, ports) {
+            ports.forEach(function(port) {
+                if (port.manufacturer == 'Silicon Labs') {
+                    console.log('found cp2102 port')
+                    state.portName = port.comName
+                    openPort()
+                }
+            })
+        })
+    }
+
+    state.onChange('connect', openPort)
+
+    function openPort() {
+        if (state.portName == '---') {
+            findPort()
+        } else {
+            if (port == null) {
+                port = new serialport(state.portName, {
+                    baudRate: 750000
+                })
+                port.on('open', function() {
+                    state.portStatus = 'open'
+                })
+                port.on('error', function(err) {
+                    state.portStatus = err.message
+                })
+                port.on('data', onPortData)
+            }
+        }
+    }
+
+    function sendToHardware(pckt){
+    	if(port.writable){
+    		port.write(pckt)
+    		return true 
+    	} else {
+    		return false 
+    	}
+    }
+
+    /*
+	------------------------------------------------------
+	PACKETS TO HARDWARE
+	------------------------------------------------------
+	*/
+
+	// from software output to hardware ... 
+    bridge.inputs = {
+        A: Input('headless packet', function() {
+            onPacketInput('rA')
+        }),
+        B: Input('headless packet', function() {
+            onPacketInput('rB')
+        })
+    }
+
+    state.onChange('sendRawPacket', function() {
+        if (parseTerminal(state.terminal)) {
+            state.sendRawPacket.isPressed = false
+        } else {
+            state.terminal = 'not parsed successfully'
+            state.sendRawPacket.isPressed = false
+        }
+    })
+
+    function parseTerminal(str){
+    	// 
+    	return false 
+    }
+
+    function onSoftwareInput(key) {
+        console.log('packet to send on address', state.key)
+    }
+
+    function parseTerminal(str) {
+        console.log('ready to parse...', str, 'as packet')
+    }
+
+    /*
+	------------------------------------------------------
+	PACKETS FROM HARDWARE 
+	------------------------------------------------------
+	*/
+
+	// from hardware outputs to software ... 
+	bridge.outputs = {
+        A: Output('headless packet'),
+        B: Output('headless packet')
+    }
+
+    var thisPacket = new Array()
+
+    function onPortData(data) {
+        console.log('DATA IN', data)
+        /*
+        // not sure about this, from old code, but sure
+        var dataArray = new Array()
+        for (var i = 0; i < data.length; i ++){
+        	dataArray[i] = data[i]
+        }
+        */
+
+        thisPacket = thisPacket.concat(data)
+        if (thisPacket[0] <= 0) {
+            thisPacket = []
+            console.log('throwing packet with leading zero')
+        }
+
+        while (thisPacket.length >= thisPacket[0]) {
+            if (thisPacket.length == thisPacket[0]) {
+                var packetCopy = thisPacket.slice(0) // copy, deref
+                thisPacket = []
+                onPacket(packetCopy)
+            } else { // rare case of two packets saddling break 
+                var fullPacket = thisPacket.slice(0, thisPacket[0])
+                onPacket(fullPacket)
+                thisPacket = thisPacket.slice(thisPacket[0])
+            }
+
+        }
+    }
+
+    function onPacket(pckt) {
+        console.log('recv full packet', pckt)
+    }
+
+    return bridge
+}
+
+module.exports = ATKBridge
\ No newline at end of file
diff --git a/src/hardware/stepper.js b/src/hardware/stepper.js
new file mode 100644
index 0000000000000000000000000000000000000000..32a68c3396a4396f42ac956a9eea14d4e05bd933
--- /dev/null
+++ b/src/hardware/stepper.js
@@ -0,0 +1,112 @@
+// boilerplate atkapi header
+const InOut = require('../../lib/inout.js')
+let Input = InOut.Input
+let Output = InOut.Output
+
+let State = InOut.State
+let Button = InOut.Button
+
+const math = require ('mathjs')
+
+function Stepper() {
+	var stepper = {
+		description: {
+			name: 'ATK Network Stepper Driver',
+			alt: 'software representation of stepper',
+			isHardware: true 
+		}
+	}
+
+	stepper.state = State()
+	state = stepper.state 
+
+	state.axis = 'X'
+	state.spu = 200 // steps per unit 
+	state.rawMove = -10
+
+	state.makeMove = Button('test move')
+
+	state.onChange('makeMove', function(){
+		onRawMove()
+		state.makeMove.isPressed = false
+	})
+
+	stepper.inputs = {
+		move: Input('move instruction', onNewInstruction),
+		packet: Input('headless packet', onHardwareIn)
+	}
+
+	stepper.outputs = {
+		heft: Output('number'), // how much juice used for last move
+		packet: Output('headless packet')
+	}
+
+	function onHardwareIn(pckt){
+		console.log('packet from stepper', pckt)
+	}
+
+	function onRawMove(){
+		console.log('raw move for', state.rawMove)
+		// finds type of ramp, calculates entry and exit ramp lengths 
+		var length = Math.abs(state.rawMove)
+		var testTrapezoid = calcDiscreteMoves(state.rawMove, 10, 100, length / 2 - length/4, length / 2 + length / 4)
+		console.log(testTrapezoid)
+		/*
+		var machineTrap = calcDiscreteMoves(rawTrap)
+		var packet = makeTrapezoidPacket(machineTrap)
+		// send it 
+		stepper.outputs.packet.emit(machineTrap)
+		*/
+	}
+
+	function onNewInstruction(move){
+		console.log('move to stepper', move)
+		// pick out axis (check if it's a wait move)
+
+		// write trapezoid
+
+		// machine trapezoid 
+
+		// send and setup for ack ? 
+	}
+
+
+	function calcDiscreteMoves(delta, entry, accel, aLength, dLength){
+		// steps, entryspeed, accel, accellength, deccellength
+		var dLength = watchRounding(Math.abs(delta * state.spu))
+		var dDelta = 0
+		if(delta < 0){
+			dDelta = - dLength
+		} else {
+			dDelta = dLength
+		}
+		var dEntry = watchRounding(entry * state.spu)
+		var dAccel = watchRounding(accel * state.spu)
+		var dALength = watchRounding(aLength * state.spu)
+		var dDLength = watchRounding(dLength * state.spu)
+
+		var discreteTrap = {
+			// HERE !
+		}
+		// from float to step conversions, watching for corner (haha) cases 
+	}
+
+	function watchRounding(val){
+		var rounded = Math.round(val)
+		if(rounded < 0){
+			console.log('WATCH ZERO', val, rounded)
+		}
+		var error = Math.abs(val/rounded - 1)
+		if(error > 0.05){
+			console.log('WATCH ROUNDING', val, rounded, error)
+		}
+	}
+
+	function makeTrapezoidPacket(machineTrapezoid){
+		// make a packit
+	}
+
+	return stepper
+}
+
+module.exports = Stepper 
\ No newline at end of file
diff --git a/src/motion/planner.js b/src/motion/planner.js
index ecf537333ed4ba701cc2e09fe57156754130c77c..328e5838bf3d7fab26b29f4d13e3e0a7b06c1120 100644
--- a/src/motion/planner.js
+++ b/src/motion/planner.js
@@ -1,88 +1,67 @@
 // boilerplate atkapi header
-const InOut = require('./lib/inout.js')
+const InOut = require('../../lib/inout.js')
 let Input = InOut.Input
 let Output = InOut.Output
+let State = InOut.State
 
-function Gcode() {
-
-    // state
-    this.state = {
-    	mode: 'G0',
-    	speeds: {
-    		G0: 1200,
-    		G1: 400
-    	}
-    }
-
-    // local functions
-    var getKeyValues = (str) => {
-    	var kv = {}
-        for (var i = 0; i < str.length; i++) {
-            if (str[i].match('[A-Za-z]')) { // regex to match upper case letters
-                var lastIndex = str.indexOf(' ', i)
-                if (lastIndex < 0) {
-                    lastIndex = str.length
-                }
-                var key = str[i].toUpperCase()
-                kv[key] = parseFloat(str.slice(i + 1, lastIndex))
-            }
+function Planner() {
+    var planner = {
+        description: {
+            name: 'Lookahead Motion Planner',
+            alt: 'movements -> acceleration planned moves'
         }
-        return kv
     }
 
-    var parseGcode = (str) => {
-    	var instruction = {
-    		position: {},
-    		hasMove: false,
-    		speed: 0
-    	}
-
-    	kv = getKeyValues(str)
-    	// track modality
-    	if(kv.G == 0 | kv.G == 1){
-    		this.state.mode = 'G' + kv.G.toString()
-    	} else if (kv.G != null) {
-    		// no arcs pls
-    		console.log('unfriendly Gcode mode!', kv)
-    	}
-
-    	for(key in kv){
-    		if(key.match('[A-EX-Z]')){
-    			instruction.position[key] = kv[key]
-    			instruction.hasMove = true
-    		} else if (key.match('[F]')){
-    			this.state.speeds[this.state.mode] = kv.F
-    		}
-    	}
-
-    	instruction.speed = this.state.speeds[this.state.mode]
-    	// and this for help later?
-    	instruction.kv = kv
-
-    	return instruction
+    planner.state = State()
+    var state = planner.state // reference pass attempt?
+
+    state.axisIDs = 'X, Y, Z'
+
+    state.accel = 100 // units/s/s
+    state.jd = 1 // units to arc about
+    state.ms = 10 // units/s
+
+    planner.inputs = {
+        instruction: Input('move instruction', onNewInstruction)
     }
 
-    // input functions
-    var lineIn = (str) => {
-    	var instruction = parseGcode(str)
-    	if (instruction.hasMove){
-    		this.outputs.instructionOut.emit(instruction)
-    	} else {
-    		this.outputs.modeChange.emit(this.state.mode)
-    	}
+    planner.outputs = {
+        move: Output('move instruction')
     }
 
-    // ins and outs
-    this.inputs = {
-    	lineIn: new Input('string input', 'string', lineIn)
+    /*
+    ------------------------------------------------------
+    ENTRY POINTS  
+    ------------------------------------------------------
+    */
+
+    function onNewInstruction(move){
+        console.log('move to planner', move)
     }
 
-    this.outputs = {
-    	instructionOut: new Output('move instruction', 'object'),
-    	modeChange: new Output('mode change', 'string')
+    /*
+    ------------------------------------------------------
+    JUNCTION DEVIATION 
+    ------------------------------------------------------
+    */
+
+
+
+    /*
+    ------------------------------------------------------
+    TRAPEZOIDS FROM JUNCTION DEVIATION ENTRY / EXITS
+    ------------------------------------------------------
+    */
+
+    function calcTrap(p1, p2, entry, accel, cruise, exit){
+        // delta is signed ! important for writing direction later, we'll use delta for maths
+        var length = Math.abs(delta)
+
+        // min and max exits / entries based on entry / exit speeds, accel, respectively 
+        // actually we sould really do this in the planner ? 
     }
-}
 
+    return planner
+}
 
-// export the module 
-module.exports = Gcode
\ No newline at end of file
+module.exports = Planner
\ No newline at end of file