diff --git a/README.md b/README.md
index ff91c5929796aadc6739e6f4658befb2f50e8b27..9f69cc338d1395e216aa6192ff6a93bd47ef3b67 100644
--- a/README.md
+++ b/README.md
@@ -139,7 +139,7 @@ Pardon my at-the-moment brief explanation.
 
 # Writing New Modules 
 
-Whenever a menu is requested, the system searches ```modules/ * ``` for *anything*. include your .js of a new module there.
+Whenever a menu is requested, the system searches ```modules/ * ``` for *anything*. When you write a new module, just include your newmodule.js there, and the program should find it.
 
 These modules are written with objects inherited from ```lib/jsunit.js``` 
 
@@ -217,6 +217,52 @@ module.exports = uiNum
 
 Hardware Modules are identical to other modules, but they inherit a class that can be found and examined at ```lib/atkunit.js``` which subsequently calls hardware-interfacing classes from ```lib/atkroute.js``` and often use tools from ```lib/packets.js``` (packets.js is mostly about packing 64-bit information into byte-size units).
 
+# Programatically Connecting and Loading Modules
+
+We can also write and manipulate modules as software objects. This is sometimes very nice.
+
+I.E. here is 'main.js' as configured to run the small program described.
+
+```javascript
+
+// business 
+const Reps = require('./reps.js')
+const Programs = require('./programs.js')
+
+// the program object: real simple, just has a description, and a 'modules' 
+var program = Programs.new('new program')
+
+/* example program-like-an-api */
+// load some modules
+var multiline = Programs.loadModuleFromSource(program, './modules/ui/multiline.js')
+var gcode = Programs.loadModuleFromSource(program, './modules/parsing/gcode.js')
+
+// attaching: always like outputs to inputs
+multiline.outputs.lineOut.attach(gcode.inputs.lineIn)
+
+// we can move things around here as well
+multiline.description.position = {
+    left: 50,
+    top: 50
+}
+
+gcode.description.position = {
+    left: 500,
+    top: 100
+}
+
+// if I have a public function in a module, I can also use that
+multiline.load('./files/dogbone.gcode')
+
+// UI 
+const View = require('./views.js')
+View.startHttp() 
+View.startWs() 
+
+Programs.assignSocket(View.uiSocket)
+View.assignProgram(program)
+```
+
 # Development Notes 
 
 ## For MW
diff --git a/main.js b/main.js
index 4bf43423ea9d2c16f432bd3a3d4e576c2ec4a7d5..20d41bd1a242e9f4cdc0c3f30332cb0eeca21971 100644
--- a/main.js
+++ b/main.js
@@ -27,9 +27,31 @@ programs are assemblies of modules
 const Reps = require('./reps.js')
 const Programs = require('./programs.js')
 
+// the program object: real simple, just has a description, and a 'modules' 
 var program = Programs.new('new program')
 
-// var step = Programs.loadModuleFromSource(program, './modules/hardware/atkstepper.js')
+/* example program-like-an-api 
+// load some modules
+var multiline = Programs.loadModuleFromSource(program, './modules/ui/multiline.js')
+var gcode = Programs.loadModuleFromSource(program, './modules/parsing/gcode.js')
+
+// attaching: always like outputs to inputs
+multiline.outputs.lineOut.attach(gcode.inputs.lineIn)
+
+// we can move things around here as well
+multiline.description.position = {
+	left: 50,
+	top: 50
+}
+
+gcode.description.position = {
+	left: 500,
+	top: 100
+}
+
+// if I have a public function in a module, I can also use that
+multiline.load('./files/dogbone.gcode')
+*/
 
 // UI 
 const View = require('./views.js')
diff --git a/modules/hardware/atkmrobot.js b/modules/hardware/atkmrobot.js
new file mode 100644
index 0000000000000000000000000000000000000000..de127aed0222dfff2dab61c91444201846b65733
--- /dev/null
+++ b/modules/hardware/atkmrobot.js
@@ -0,0 +1,83 @@
+// boilerplate atkapi header
+const InOut = require('../../lib/jsunit.js')
+let Input = InOut.Input
+let Output = InOut.Output
+let State = InOut.State
+let Button = InOut.Button
+
+const Hardware = require('../../lib/atkunit.js')
+const PCKT = require('../../lib/packets.js')
+
+// a constructor, a fn, a javascript mess
+function ATKMathRobot() {
+
+    // we make the module, starting from this base 
+    // '0,1' is the route to our piece of hardware
+    // 'onPacket' is the function that will be called
+    // when data arrives from that port 
+    var atkmr = Hardware()
+
+    // change the name ... 
+    atkmr.description.name = 'atk-math-robot-joint'
+
+    // inputs and outputs as usual 
+    atkmr.inputs = {
+        pc_t: Input('number', onPositionTargetInput)
+    }
+    atkmr.outputs = {
+        ok: Output('nothing-yet')
+    }
+
+    // and state as well 
+    var state = atkmr.state 
+
+    state.message = 'no packet yet'
+    state.pc_t = 2048
+    state.onChange('pc_t', onPositionTargetUserChange)
+    state.walk = 1024
+    state.tickButton = Button('Walk Value', onPositionTickTock)
+
+    // to send things down the well, we can use
+    // atkmr.route.send(packet) 
+    // where packet is a byte array
+
+    function onPositionTargetInput(evt){
+        console.log("INPUTS NOT YET BOUND", evt)
+    }
+
+    function onPositionTargetUserChange() {
+        var pc_t = state.pc_t
+        if (pc_t > 16384) {
+            pc_t = 16384
+        } else if (pc_t < 0) {
+            pc_t = 0
+        }
+
+        var pc_tpack = PCKT.pack32(pc_t)
+        pc_tpack.unshift(143)
+
+        state.message = 'packet out'
+        atkmr.route.send(pc_tpack)
+    }
+
+    function onPositionTickTock(){
+        var pc_t = state.pc_t
+        pc_t += state.walk
+        pc_t %= 16384
+        state.pc_t = pc_t 
+        onPositionTargetUserChange()
+    }
+
+    // to get replies to certain packets, we can
+    // subscribe, where 141 (here) is the 'key' 
+    // we're looking for on the network, and the 
+    // msg is byte array we'll get back
+    atkmr.route.subscribe(143, function(msg){
+        state.message = 'packet ok'
+    })
+
+    return atkmr
+}
+
+// exports 
+module.exports = ATKMathRobot
\ No newline at end of file
diff --git a/modules/hardware/atkseriallink.js b/modules/hardware/atkseriallink.js
index 615acb7449fb7418562f04bb4c71ca513ed9fbdc..f97d49311b297e7d0f9393aab0d5527b18bb3b55 100644
--- a/modules/hardware/atkseriallink.js
+++ b/modules/hardware/atkseriallink.js
@@ -150,7 +150,6 @@ function ATKSerialLink() {
         // we'll make sure it's what we think it will be
         // console.log("PORT DATA")
         var dtArray = new Array()
-        console.log('sp data', data)
         if (Buffer.isBuffer(data)) {
             for (var i = 0; i < data.length; i++) {
                 dtArray[i] = data[i]
diff --git a/views.js b/views.js
index b9886e006f5db54f0db56a1d3c9828e2fea9f788..32d1138a1c6072d6673738eed3b7d3cea20e223a 100644
--- a/views.js
+++ b/views.js
@@ -119,7 +119,7 @@ function socketRecv(evt) {
 }
 
 function socketSend(type, data) {
-    if (sckt.readyState === 1) {
+    if (sckt != null && sckt.readyState === 1) {
         var msg = {
             type: type,
             data: data
@@ -127,7 +127,7 @@ function socketSend(type, data) {
         //console.log('SEND', msg)
         sckt.send(JSON.stringify(msg))
     } else {
-        throw 'on socketSend, ws not ready'
+        console.log('on socketSend, ws not ready')
     }
 }