diff --git a/hunks/debug/logNumbers.js b/hunks/debug/logNumbers.js
index a74ed3391291d8448c28116707be5e00cc023058..814924e2b72d926a4b6aff04c62ecf99556efb69 100644
--- a/hunks/debug/logNumbers.js
+++ b/hunks/debug/logNumbers.js
@@ -24,17 +24,9 @@ export default function NumberLogger() {
   let logToConsole = new State('boolean', 'console', false)
   this.states.push(logToConsole)
 
-  this.dom = {}
-
-  this.init = () => {
-    this.dom = $('<div>').get(0)
-  }
-
-  this.onload = () => {
-    //error here
-    let text = $('<div>').addClass('txt').append('- >').get(0)
-    $(this.dom).append(text)
-  }
+  let dom = this.document()
+  let text = $('<div>').addClass('txt').append('- >').get(0)
+  $(dom).append(text)
 
   this.loop = () => {
     // this will be called once every round turn
@@ -42,7 +34,7 @@ export default function NumberLogger() {
     if (tolog.io()) {
       // an input is occupied, and the exit path is empty
       let val = tolog.get()
-      $(this.dom).children('.txt').html(val)
+      $(text).html(val)
       if (logToConsole.value === true) {
         console.log(`logger ${this.ind}`, val)
       }
diff --git a/hunks/image/imageCapture.js b/hunks/image/imageCapture.js
new file mode 100644
index 0000000000000000000000000000000000000000..099d5a5f5a62f52567219bac321add6d7ba0982f
--- /dev/null
+++ b/hunks/image/imageCapture.js
@@ -0,0 +1,73 @@
+/*
+hunks/image/imagecapture.js
+
+pull image from webcam, use imagecapture interface for fine tuning 
+
+Jake Read at the Center for Bits and Atoms with Neil Gershenfeld and Leo McElroy
+(c) Massachusetts Institute of Technology 2019
+
+This work may be reproduced, modified, distributed, performed, and
+displayed for any purpose, but must acknowledge the squidworks and cuttlefish projects.
+Copyright is retained and must be preserved. The work is provided as is;
+no warranty is provided, and users accept all liability.
+*/
+
+import {
+  Hunkify,
+  Input,
+  Output,
+  State
+} from '../hunks.js'
+
+export default function RTCWebcam(){
+  Hunkify(this)
+
+  let imgOut = this.output('ImageData', 'frame')
+
+  let dom = this.document()
+  let msg = $(`<div>retrieving video...</div>`).get(0)
+  $(dom).append(msg)
+  let video = $('<video>').get(0)
+  let canvas = $('<canvas>').get(0) // invisible canvas 4 us
+
+  let streaming = false
+  let context = canvas.getContext('2d')
+
+  this.init = () => {
+    navigator.mediaDevices.getUserMedia({video: true, audio: false}).then((stream) => {
+      $(dom).append(video)
+      video.srcObject = stream
+      video.play()
+      video.addEventListener('canplay', (evt) => {
+        $(msg).empty()
+        if(!streaming){
+          streaming = true
+          canvas.width = video.videoWidth
+          canvas.height = video.videoHeight
+        }
+        /*
+        let capture = () => {
+          context.drawImage(video, 0, 0, canvas.width, canvas.height)
+          let imgData = context.getImageData(0, 0, canvas.width, canvas.height)
+          console.log(imgData)
+          setTimeout(capture, 2000)
+        }
+        setTimeout(capture, 2000)
+        */
+      })
+    }).catch((err) => {
+      $(msg).text('error retrieving webcam from system, see logs')
+      console.error("Webcam Hunk couldn't retrieve a stream:", err)
+    })
+  }
+
+  this.loop = () => {
+    if(streaming && !imgOut.io()){
+      // capture frames as imagedata, onto the virtual canvas,
+      context.drawImage(video, 0, 0, canvas.width, canvas.height)
+      // pull img data from that vcanvas -> the output,
+      imgOut.put(context.getImageData(0,0, canvas.width, canvas.height))
+    }
+  }
+
+}
diff --git a/hunks/image/imgToPath2D.js b/hunks/image/imgToPath2D.js
index 6206b0f56f05dee0acdc0ae1dffa9aabb93570b0..f0a90fa98614c0131a1b3b4f46c1e076caa19894 100644
--- a/hunks/image/imgToPath2D.js
+++ b/hunks/image/imgToPath2D.js
@@ -1,7 +1,7 @@
 /*
-hunks/image/edgeDetect.js
+hunks/image/imgToPath2D.js
 
-find edges
+threshold, edges, orient, vectorize, z-clearances and cuts,
 
 Jake Read at the Center for Bits and Atoms with Neil Gershenfeld and Leo McElroy
 (c) Massachusetts Institute of Technology 2019
@@ -27,8 +27,8 @@ export default function imgToPath2D() {
 
   let threshValue = this.state("number", "threshold", 0.5)
   let idealWidth = this.state("number", "width", 220) // machine-space width,
-  let zUp = this.state('number', 'clearance', 10)
-  let zDown = this.state('number', 'depth', -5)
+  let zUp = this.state('number', 'clearance', 8)
+  let zDown = this.state('number', 'depth', 3)
 
   this.init = () => {}
 
@@ -904,6 +904,8 @@ const unfPath = (path, pwidth, mmwidth, zu, zd) => {
   let unfp = []
   // expansion,
   let scale = mmwidth / pwidth
+  // first move is to 0,0,clearance
+  unfp.push([0, 0, zu])
   // flatten, adding z-moves
   for(let leg of path){
     // start each leg up top, above the first point,
diff --git a/hunks/image/readPNG.js b/hunks/image/readPNG.js
index 49013c36fa1a3513be939c867e46e646dcad683f..2ea41e9ca9d87e8f653936efc581fb6954427c7c 100644
--- a/hunks/image/readPNG.js
+++ b/hunks/image/readPNG.js
@@ -14,14 +14,14 @@ no warranty is provided, and users accept all liability.
 
 import { Hunkify, Input, Output, State } from "../hunks.js";
 
-import { html, svg, render } from "https://unpkg.com/lit-html?module";
-
 export default function UploadPNG() {
   // this fn attaches handles to our function-object,
   Hunkify(this);
 
   let imgOut = this.output('ImageData', 'image')
   let dpiOut = this.output('number', 'dpi')
+  let widthOut = this.output('number', 'width')
+  let heightOut = this.output('number', 'height')
 
   let trig = this.state('boolean', 'release', false)
   trig.onChange = (value) => {
@@ -37,10 +37,13 @@ export default function UploadPNG() {
   // local datas,
   let imageData = null
   let imageUpdated = false
-  let idealWidth = 395
+  let idealWidth = 285
   // and dpi,
   let imgDpi = 72
   let dpiUpdated = false
+  let whUpdated = false
+  let width = 0
+  let height = 0
 
   // write some startup DOM elements,
   let button = $('<input type="file" accept="image/png">').on('input', (evt) => {
@@ -107,11 +110,13 @@ export default function UploadPNG() {
       dpiUpdated = true
       if (imageData) {
         console.log(imageData)
-        let width = imageData.width * (imgDpi / 1000) / 25.4
-        let height = imageData.height * (imgDpi / 1000) / 25.4
+        // this ?
+        width = (imageData.width / imgDpi) * 25.4
+        height = (imageData.height / imgDpi) * 25.4
+        whUpdated = true
         console.warn(`size in mm is w: ${width.toFixed(3)}, h: ${height.toFixed(3)}`)
       }
-      console.log('dpi', imgDpi)
+      console.warn('dpi is: ', imgDpi)
     }
     rawReader.readAsArrayBuffer(file)
   })
@@ -123,9 +128,14 @@ export default function UploadPNG() {
       imageUpdated = false
       imgOut.put(imageData)
     }
-    if(imgDpi && dpiUpdated && !dpiOut.io()){
+    if (imgDpi && dpiUpdated && !dpiOut.io()) {
       dpiUpdated = false
       dpiOut.put(imgDpi)
     }
+    if (whUpdated && !widthOut.io() && !heightOut.io()) {
+      whUpdated = false
+      widthOut.put(width)
+      heightOut.put(height)
+    }
   }
 }
diff --git a/hunks/interface/number.js b/hunks/interface/number.js
index 0ff5488a014c61d62bf31a6f0e15e3d557ec19e6..49c31c80cb0e905ff03ed674fc9784c38ac61111 100644
--- a/hunks/interface/number.js
+++ b/hunks/interface/number.js
@@ -17,37 +17,26 @@ import {
   State
 } from '../hunks.js'
 
-function Number() {
+export default function Number() {
     Hunkify(this)
 
-    let numout = new Output('number', 'num', this)
-    this.outputs.push(numout)
+    let numout = this.output('number', 'num')
 
-    let numrep = new State('number', 'numrep', 275074)
-    this.states.push(numrep)
+    let numrep = this.state('number', 'numrep', 1000)
 
     // as is tradition,
-    this.dom = {}
-
-    this.init = () => {
-        // manager calls this once
-        // it is loaded and state is updated (from program)
-        console.log('HELLO NUMINPUT')
-        this.dom = $('<div>').get(0)
-        //this.dom = document.createElement('div')
-    }
-
-    this.onload = () => {
-      let contact = $('<div>').addClass('btn').append('! contact !').get(0)
-      $(this.dom).append(contact)
-      contact.addEventListener('click', (evt) => {
-          numout.put(numrep.value)
-      })
-    }
+    let dom = this.document()
+    let contact = $('<div>').addClass('btn').append('! contact !').get(0)
+    contact.addEventListener('click', (evt) => {
+      if(numout.io()){
+        console.warn('number stream occupied, bailing')
+        return
+      }
+      numout.put(numrep.value)
+    })
+    $(dom).append(contact)
 
     this.loop = () => {
       //
     }
 }
-
-export default Number
diff --git a/hunks/interface/threejsPathDisplay.js b/hunks/interface/threejsPathDisplay.js
index 05e4b6ebe22c80bbddc9c68a149f42dc33a42708..9c9e67a389991f06e1910b17267f0b93d2322857 100644
--- a/hunks/interface/threejsPathDisplay.js
+++ b/hunks/interface/threejsPathDisplay.js
@@ -28,9 +28,9 @@ import { MeshLine, MeshLineMaterial } from '../../libs/three_meshline.module.js'
 export default function ThreeGhosts() {
   Hunkify(this)
 
-  let ptin = this.input('reference', 'point')
+  let pathIn = this.input('reference', 'point')
 
-  let lineWidth = this.state('number', 'line width', 5)
+  let lineWidth = this.state('number', 'line width', 2)
 
   let dom = this.document()
 
@@ -38,8 +38,8 @@ export default function ThreeGhosts() {
 
   }
 
-  let width = 1000
-  let height = 1000
+  let width = 600
+  let height = 600
 
   let camera = new THREE.PerspectiveCamera(75, width / height, 1, 10000)
   camera.up.set(0, 0, 1)
@@ -74,9 +74,9 @@ export default function ThreeGhosts() {
   let last = {}
 
   this.loop = () => {
-    if (ptin.io()) {
+    if (pathIn.io()) {
       // local copy,
-      let lp = JSON.parse(JSON.stringify(ptin.get()))
+      let lp = JSON.parse(JSON.stringify(pathIn.get()))
       let lw = lineWidth.value
       // redraw top->bottom every time ?
       let geometry = new THREE.Geometry()
diff --git a/hunks/interface/toggle.js b/hunks/interface/toggle.js
index d276be129c9cdcbdbb46f3c0118c9765d021937d..57b8740081cabf3187a834ef07452a7c4049b2a1 100644
--- a/hunks/interface/toggle.js
+++ b/hunks/interface/toggle.js
@@ -15,38 +15,29 @@ no warranty is provided, and users accept all liability.
 import { Hunkify, Input, Output, State } from '../hunks.js'
 
 export default function Toggle() {
-    Hunkify(this)
-
-    let onclk = this.output('boolean', 'out')
-    let nextOut = this.state('boolean', 'next out', true)
-
-    this.dom = {}
-
-    this.init = () => {
-        // manager calls this once
-        this.dom = $('<div>').get(0)
+  Hunkify(this)
+
+  let onclk = this.output('boolean', 'out')
+  let nextOut = this.state('boolean', 'next out', true)
+
+  let dom = this.document()
+  let contact = $('<div>').addClass('btn').append('! toggle !').get(0)
+  contact.addEventListener('click', (evt) => {
+    if (onclk.io()) {
+      console.warn("button attempts to push to occupied output")
+    } else {
+      if (nextOut.value) {
+        onclk.put(true)
+        nextOut.set(false)
+      } else {
+        onclk.put(false)
+        nextOut.set(true)
+      }
     }
+  })
+  $(dom).append(contact)
 
-    this.onload = (dom) => {
-      // function equivalent, our .dom element is loaded into ~ the d o m ~
-      let contact = $('<div>').addClass('btn').append('! toggle !').get(0)
-      $(this.dom).append(contact)
-      contact.addEventListener('click', (evt) => {
-          if(onclk.io()){
-            console.warn("button attempts to push to occupied output")
-          } else {
-            if(nextOut.value){
-              onclk.put(true)
-              nextOut.set(false)
-            } else {
-              onclk.put(false)
-              nextOut.set(true)
-            }
-          }
-      })
-    }
-
-    this.loop = () => {
-        // ... nah
-    }
+  this.loop = () => {
+    // ... nah
+  }
 }
diff --git a/hunks/statemachines/saturn.js b/hunks/statemachines/saturn.js
index 3edc10c2d12a56cbab163ec2769b361afea2290f..e47e5cdeadcc2b0467c3b6c9d07f424c258db832 100644
--- a/hunks/statemachines/saturn.js
+++ b/hunks/statemachines/saturn.js
@@ -58,17 +58,17 @@ export default function Saturn() {
 
   // here's a case where we might want some type of enum ish thing
   let isIncrementalMode = this.state('boolean', 'incremental mode', false)
-  let accelState = this.state('number', 'acceleration (u/s/s)', 20)
+  let accelState = this.state('number', 'acceleration (u/s/s)', 5)
   accelState.onChange = (val) => {
     if(val > 30){
       val = 30
-    } else if (val < 5){
-      val = 5
+    } else if (val < 2){
+      val = 2
     }
     accelState.set(val)
     accel = accelState.value
   }
-  let speedState = this.state('number', 'speed (u/s)', 100)
+  let speedState = this.state('number', 'speed (u/s)', 50)
   speedState.onChange = (val) => {
     if (val > 200){
       val = 200
diff --git a/hunks/view.js b/hunks/view.js
index 2bee4c445b6dd83f4ddbdb6124c117acaf006daa..0abc3220ba8e26c27f6e293bedd58f50cf8b12ca 100644
--- a/hunks/view.js
+++ b/hunks/view.js
@@ -156,7 +156,7 @@ export default function View() {
   // an oddity, we occasionally change positions,
   this.hasDefPositionUpdate = false
   let setDefReposition = (index) => {
-    window.check = true 
+    window.check = true
     this.hasDefPositionUpdate = true
     if (defs[index]) {
       defs[index].hasPositionUpdate = true
@@ -821,7 +821,7 @@ export default function View() {
   and then handle etcs afterwards, i.e. probably just cleaning up .clean() (ha)
   */
   this.refresh = () => {
-    console.log(`REFRESH ${this.name}`)
+    console.warn(`REFRESH ${this.name}`)
     // well, I won't say it's clean or beautiful, but we are interested in keeping the
     // positions of things intact, so I am here for this:
     let psns = []
@@ -998,6 +998,8 @@ export default function View() {
           if (df.position) hnk.position = df.position
           if (df.type == 'link') {
             if (df.interior()) {
+              // WARN: the link-state hack, needs link work / protocol work 
+              hnk.states[0].value = false
               hnk.contains = await df.interior().save() // this should recurse, yeah ?
             }
           }
@@ -1122,6 +1124,7 @@ export default function View() {
   }
 
   this.reinstate = async (system, debug) => {
+    console.warn(`${this.name} begins reinstate`)
     // ensure we are up-to-date w/ current state,
     await this.refresh()
     // and and,
@@ -1142,6 +1145,7 @@ export default function View() {
             await interior.reinstate(spec.contains)
           }
         }
+        console.warn(`${this.name} finish reinstate`)
         resolve()
       } catch (err) {
         console.error(err)
diff --git a/hunks/x_adhoc/center.js b/hunks/x_adhoc/center.js
index 90a9f02e20c4c1b7a10957b042998655f2db85ee..a1d42679c4dad1ee90b43c00eb3c5e735ccf42d1 100644
--- a/hunks/x_adhoc/center.js
+++ b/hunks/x_adhoc/center.js
@@ -29,21 +29,17 @@ export default function HotSpotHunter(){
   let opX = this.output('number', 'x')
   let opY = this.output('number', 'y')
 
-  console.log('REG?', REG)
+  let dom = this.document()
 
-  this.dom = $('<div>').get(0)
   // one to draw into:
   let canvas = $('<canvas>').get(0)
   let ctx = canvas.getContext('2d')
-  canvas.width = 395
-  canvas.height = 395
+  canvas.width = 285
+  canvas.height = 285
   // and ah virtual friend:
   let vc = $('<canvas>').get(0)
   let vctx = vc.getContext('2d')
-
-  this.onload = () => {
-    $(this.dom).append(canvas)
-  }
+  $(dom).append(canvas)
 
   this.loop = () => {
     if(ipImg.io() && !opX.io() && !opY.io()){
diff --git a/hunks/x_adhoc/correlate.js b/hunks/x_adhoc/correlate.js
index 7b0a93daf12e192cef2a87cf01f42192cde5d031..e00483b8692e56c44abd97f50fce93db8e2fa3b7 100644
--- a/hunks/x_adhoc/correlate.js
+++ b/hunks/x_adhoc/correlate.js
@@ -167,6 +167,8 @@ export default function Correlate() {
   let imgIn = this.input('ImageData', 'frame')
   let resOut = this.output('ImageData', 'correlation')
 
+  let invert = this.state('boolean', 'invert', true)
+
   let canvasA = $('<canvas>').get(0)
   canvasA.width = 24
   canvasA.height = 24
@@ -177,7 +179,9 @@ export default function Correlate() {
   let canvasB = $('<canvas>').get(0)
   let ctxB = canvasB.getContext('2d')
 
-  this.dom = $('<div>').get(0)
+  let dom = this.document()
+  $(dom).append(canvasA)
+  $(dom).append(canvasB)
 
   let webWorker
 
@@ -194,11 +198,6 @@ export default function Correlate() {
     })
   }
 
-  this.onload = () => {
-    $(this.dom).append(canvasA)
-    $(this.dom).append(canvasB)
-  }
-
   this.loop = () => {
     if (imgIn.io() && !running) {
       /*
@@ -225,14 +224,22 @@ export default function Correlate() {
       //let a = ctxB.getImageData(b.width / 2, b.height / 2, 25, 25)
       // and write that out, to debug ...
       //ctxA.putImageData(a, 0, 0)
-      ctxA.fillStyle = 'white'
-      ctxA.fillRect(0,0,24,24)
-      ctxA.fillStyle = 'black'
+      if(invert.value){
+        ctxA.fillStyle = 'black'
+        ctxA.fillRect(0,0,24,24)
+        ctxA.fillStyle = 'white'
+        ctxA.fillRect(0,0,12,12)
+        ctxA.fillRect(12,12,24,24)
+      } else {
+        ctxA.fillStyle = 'white'
+        ctxA.fillRect(0,0,24,24)
+        ctxA.fillStyle = 'black'
+        ctxA.fillRect(0,0,12,12)
+        ctxA.fillRect(12,12,24,24)
+      }
       // ctxA.arc(12,12,8, 0, 2*Math.PI)
       // ctxA.fillStyle = 'black'
       // ctxA.fill()
-      ctxA.fillRect(0,0,12,12)
-      ctxA.fillRect(12,12,24,24)
       let a = ctxA.getImageData(0,0,24,24)
       webWorker.postMessage({a: a, b: b})
       running = true
diff --git a/hunks/x_adhoc/pathGap.js b/hunks/x_adhoc/pathGap.js
new file mode 100644
index 0000000000000000000000000000000000000000..78c2c742b1b57cbbf0da732b52b5981cb60a057f
--- /dev/null
+++ b/hunks/x_adhoc/pathGap.js
@@ -0,0 +1,67 @@
+/*
+hunks/adhoc/tpath.js
+
+canned paths, for testing motion control systems
+
+Jake Read at the Center for Bits and Atoms with Neil Gershenfeld and Leo McElroy
+(c) Massachusetts Institute of Technology 2019
+
+This work may be reproduced, modified, distributed, performed, and
+displayed for any purpose, but must acknowledge the squidworks and cuttlefish projects.
+Copyright is retained and must be preserved. The work is provided as is;
+no warranty is provided, and users accept all liability.
+*/
+
+import {
+  Hunkify,
+  Input,
+  Output,
+  State
+} from '../hunks.js'
+
+export default function PathGap(){
+  Hunkify(this)
+
+  let path = []
+
+  // we intake big paths,
+  let inPath = this.input('reference', 'path')
+  // we output positions one-at-a-time,
+  let outPosn = this.output('array', 'position')
+  // and output the remaining path, in case we would like to re-render ...
+  let outPath = this.output('reference', 'remaining')
+
+  // we have stop / start here, and reset
+  let runner = this.state('boolean', 'running', false)
+  runner.onChange = (val) => {
+    runner.set(val)
+  }
+  let reset = this.state('boolean', 'reset', false)
+  reset.onChange = (val) => {
+    path = []
+    reset.set(false)
+  }
+
+  // copies out, one at a time, fc pressure
+  this.loop = () => {
+    // if no path, and path available, pull
+    if(inPath.io()){
+      if(path.length == 0){
+        path = JSON.parse(JSON.stringify(inPath.get()))
+      } else {
+        inPath.get() // clear it for our friends, if we don't want it...
+      }
+    }
+    // if we have path and can push outputs,
+    if(path.length > 0 && runner.value){
+      if(!outPosn.io()){
+        let op = path.shift()
+        outPosn.put(op)
+        // and update the downstream ref, for rendering ...
+        if(!outPath.io()){
+          outPath.put(path)
+        }
+      }
+    }
+  }
+}
diff --git a/hunks/x_adhoc/tpath.js b/hunks/x_adhoc/tpath.js
index ab7ce883ed84af140185aa8fc0233ee5d5004390..d94682fb567c0f7fd8b024e0349744371f4ea1a8 100644
--- a/hunks/x_adhoc/tpath.js
+++ b/hunks/x_adhoc/tpath.js
@@ -1,7 +1,7 @@
 /*
 hunks/adhoc/tpath.js
 
-canned paths, for testing motion control systems 
+canned paths, for testing motion control systems
 
 Jake Read at the Center for Bits and Atoms with Neil Gershenfeld and Leo McElroy
 (c) Massachusetts Institute of Technology 2019
diff --git a/test_files/the-alchemist.png b/test_files/the-alchemist.png
new file mode 100644
index 0000000000000000000000000000000000000000..68181087f9f81ffca2ff935f0869231502b770a1
Binary files /dev/null and b/test_files/the-alchemist.png differ
diff --git a/view/contextmenu.js b/view/contextmenu.js
index 902a373a4212b44c5e758a601a863904c7676a3f..8423331f2039a234511584e1780398e723fa6e51 100644
--- a/view/contextmenu.js
+++ b/view/contextmenu.js
@@ -34,7 +34,7 @@ let findOrBuild = (evt) => {
         pos.x = (evt.layerX - pt.x) * (1 / pt.s)
         pos.y = (evt.layerY - pt.y) * (1 / pt.s)
       } else {
-        if (!$(evt.target).is('.subplane')) console.error('poorly placed menu cominatcha')
+        if (!$(evt.target).is('.subplane')) return
         plane = evt.target
         pos.x = evt.layerX + 3
         pos.y = evt.layerY - 29
@@ -145,6 +145,7 @@ let changeContextTitle = (text) => {
         }
       }
     }
+    return
   })
 }
 
@@ -163,7 +164,9 @@ let setupForSave = (se, callback) => {
   $(tinput).focus()
   $(tinput).select()
   $(tinput).on('keyup', (ke) => {
+    console.log('save key')
     if (ke.keyCode == 13) {
+      console.log('keycode 13')
       callback(tinput.value)
     }
   })
@@ -340,6 +343,7 @@ function planeMenu(evt, context) {
       console.log('ptch', patch)
       // now we should be ok to ship this home ...
       setupForSave(ce, (name) => {
+        console.log('save return, posting...')
         jQuery.post(`/save/systems/${name}`, patch, (res) => {
           if (!res.success) {
             changeContextTitle('error while saving, see logs')