diff --git a/README.md b/README.md
index 244fec46e3208aa8285cb5b4e9bf179f64b41bdc..5272066afd7c3d1f2758f713af7cbe5a05aa80cd 100644
--- a/README.md
+++ b/README.md
@@ -14,6 +14,8 @@ Cuttlefish runs programs that are made of 'hunks' - each hunk is a small block o
 
 Hunks interface with the rest of the world through data ports - inputs and outputs - and their state variables. Each of these interfaces *is typed* - this helps us when we serialize messages (Cuttlefish is made for networks) - and helps everyone understand what hunks are meant to do.
 
+Typing is rendered in `/typeset.js` - we can only connect outputs to inputs when a function exists here to copy data from the output-type to the input-type. These functions are directional! 
+
 ### Flowcontrolled Runtime
 
 When Cuttlefish is running, it ferries data between hunk outputs and outputs, and gives each hunk some time to operate on those inputs, producing outputs. By connecting different hunks in graphs, we can develop programs.
diff --git a/hunks/hunks.js b/hunks/hunks.js
index f77d7ced84df42e88dc308f633a9c5036edce923..1cf8a745d392c03836144b7526045ab5b6861cc3 100644
--- a/hunks/hunks.js
+++ b/hunks/hunks.js
@@ -14,7 +14,7 @@ function Hunkify(hunk) {
   // we set this during load, only once, this avoids some confusion
   hunk.type = null
   hunk.name = null
-  // we keep a copy of our position-in-the-array ...
+  // we keep a copy of our position-in-the-array ... of our parent ... our pip 
   hunk.ind = null
 
   hunk.log = function(msg) {
diff --git a/hunks/image/displayimagedata.js b/hunks/image/displayimagedata.js
new file mode 100644
index 0000000000000000000000000000000000000000..15227c013b6f90d321f60110175c72f9063648a8
--- /dev/null
+++ b/hunks/image/displayimagedata.js
@@ -0,0 +1,45 @@
+import { Hunkify, Input, Output, State } from '../hunks.js'
+
+export default function DisplayImageData() {
+  Hunkify(this)
+
+  const imageIn = new Input('ImageData', 'image', this)
+  this.inputs.push(imageIn)
+
+  this.init = () => {
+    this.dom = $('div').get(0)
+  }
+
+  let uid = `${this.name}_canvas_dom`
+
+  this.onload = () => {
+    // I would say these implementations could use some clarity / nice-ness
+    // there's a real hairball to dig up in the hunks-getting-dom-elements 'system'
+    // that is fair game to anyone willing to wrastle with the dom and force-layout for 1wk
+    const target = $('<div>').get(0);
+    $(this.dom).append(target);
+    // these methods are rad !
+    const view = html `
+      <style>
+        #${uid} {
+          border: 1px solid black;
+          margin: 5px;
+        }
+      </style>
+      <canvas
+        id=${uid}
+        width=100
+        height=100>
+      </canvas>
+    `
+    render(view, target);
+  }
+
+  this.loop = () => {
+    if(imageIn.io()){
+      let imageData = imageIn.get()
+      console.log($(this.dom).children(`#${uid}`))
+    }
+  }
+
+}
diff --git a/hunks/image/readpng.js b/hunks/image/readpng.js
index 2ba2af8a8fdccc1024b9183466aa66d8a2d26011..478edac7a489baadec894dbc1ebbd4f46d91dce5 100644
--- a/hunks/image/readpng.js
+++ b/hunks/image/readpng.js
@@ -1,78 +1,119 @@
-/*
-
-hunk template
-
-*/
+/* hunk template */
 
 // these are ES6 modules
-import {
-  Hunkify,
-  Input,
-  Output,
-  State
-} from '../hunks.js'
+import { Hunkify, Input, Output, State } from '../hunks.js'
+
+import { html, svg, render } from 'https://unpkg.com/lit-html?module';
 
-function ReadPNG() {
+export default function UploadPNG() {
   // this fn attaches handles to our function-object,
   Hunkify(this)
 
-  let imageOutput = new Output('rgba', 'image', this)
-  let buttonOutput = new Output('string', 'button', this)
-  this.outputs.push(imageOutput, buttonOutput)
+  // keeping these local globals,
+  let idealWidth = 400
+  let localImageInfo = null
+  let imageUpdated = false
 
-  let xSize = new State('number', 'xSize', 400)
-  let ySize = new State('number', 'ySize', 400)
-  this.states.push(xSize, ySize)
+  // it looks like 'RGBA' types are actually ImageData objects,
+  // https://developer.mozilla.org/en-US/docs/Web/API/ImageData
+  // since we will be using these in canvas-contexts, makes sense to just use these types
+  const imageOut = new Output('ImageData', 'image', this)
+  this.outputs.push(imageOut)
+
+  // as a hack, we can use boolean state variables as a button: they have handy callbacks...
+  const trig = new State('boolean', 'release', false)
+  this.states.push(trig)
+  trig.onChange = (value) => {
+    // I'll set this flag so that we will release image info on the next loop
+    // if any is available,
+    imageUpdated = true
+  }
 
-  // State items also have change handlers,
-  xSize.onChange = (value) => {
-    // at this point, something external (probably a human)
-    // has requested that we change this state variable,
-    // we can reject that, by doing nothing here, or we can
-    stateItem.set(value)
+  this.init = () => {
+    this.dom = $('<div>').get(0)
   }
 
-  // hunks can choose to- or not- have init code.
-  // at init, the module has been loaded and state variables have been
-  // recalled from any program save - so this is a good point
-  // to check any of those, and setup accordingly ...
-  // as is tradition,
-  this.dom = {}
+  //import
+  const png_read_handler = (e) => {
+    const reader = new FileReader();
 
-  let button
+    const files = e.target.files;
+    const file = files[0];
 
-  this.init = () => {
-      // manager calls this once
-      // it is loaded and state is updated (from program)
-      console.log('HELLO Read PNG')
-      this.dom = document.createElement('div')
-      // 
+    const img = new Image();
+    img.file = file;
+
+    const loader = (aImg) => (e) => {
+      aImg.src = e.target.result;
+      aImg.onload = () => {
+        // OK: sometimes this image is *way too big* to dump into the DOM,
+        // so we write it into a new and virtual canvas that we won't render:
+        let virtualCanvas = $('<canvas>').get(0)
+        virtualCanvas.width = aImg.width
+        virtualCanvas.height = aImg.height
+        let vContext = virtualCanvas.getContext("2d")
+        vContext.drawImage(aImg, 0, 0)
+        // great, now we can use this to pull normalized ImageData information, this is what we pass around:
+        localImageInfo = vContext.getImageData(0, 0, aImg.width, aImg.height)
+        imageUpdated = true
+
+        // now we can draw something:
+        const canvas = document.getElementById(`${this.name}_canvas_dom`);
+        // we'll want to shrink the whole thing ... or enlarge,
+        let scale = idealWidth / aImg.width
+        // the height and width of the actual image,
+        canvas.width = aImg.width * scale
+        canvas.height = aImg.height * scale
+        // and then we draw into it,
+        const ctx = canvas.getContext("2d");
+        ctx.clearRect(0, 0, canvas.width, canvas.height);
+        ctx.scale(scale, scale)
+        ctx.drawImage(aImg, 0, 0);
+      }
+    };
+    reader.onload = loader(img);
+    reader.readAsDataURL(file);
   }
 
   this.onload = () => {
-    let contact = $('<div>').addClass('btn').append('! read png !').get(0)
-    $(this.dom).append(contact)
-    contact.addEventListener('click', (evt) => {
-        buttonOutput.put('anything')
-    })
+    const target = $('<div>').get(0);
+    $(this.dom).append(target);
+    // to name unique DOM elements, if we want to access them later using 'get element' methods,
+    // we can prepend the hunk's name (which is unique)
+    const view = html `
+      <style>
+        #${this.name}_canvas_dom {
+          border: 1px solid black;
+          margin: 5px;
+        }
+        #${this.name}_dom {
+          position: absolute;
+          left: 10px;
+          top: 350px;
+        }
+      </style>
+      <div id=${this.name}_canvas_wrappper>
+        <canvas
+          id=${this.name}_canvas_dom
+          width=100
+          height=100>
+        </canvas>
+      </div>
+      <input
+        id=${this.name}_dom
+        type="file"
+        accept="image/png"
+        @input=${ (e) => png_read_handler(e)}></input>
+    `
+    render(view, target);
   }
 
-  // to divide time between hunks, each has a loop function
-  // this is the hunks' runtime: a manager calls this once-per-round
-  // here is where we check inputs, put to outputs, do work, etc
   this.loop = () => {
-    // typically we check inputs and outputs first,
-    // making sure we are clear to run,
-    /*
-    if (inA.io() && !outB.io()) {
-      // an input is occupied, and the exit path is empty
-      let output = internalFunc(this.inputs.a.get())
-      // put 'er there
-      outB.put(output)
+    // we release data if we have any, if it's been updated, and if the output is clear:
+    if(localImageInfo && imageUpdated && !imageOut.io()){
+      console.log(`${this.name} puts:` , localImageInfo)
+      imageUpdated = false
+      imageOut.put(localImageInfo)
     }
-    */
   }
 }
-
-// the hunk is also an ES6 module, this is how we export those:
-export default ReadPNG
diff --git a/hunks/image/readpng_leo.js b/hunks/image/readpng_leo.js
deleted file mode 100644
index 2426ce644dad34f3fcf591cb48bac7bb538e620b..0000000000000000000000000000000000000000
--- a/hunks/image/readpng_leo.js
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
-
-hunk template
-
-*/
-
-// these are ES6 modules
-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)
-
-  const imageOut = new Output('RGBA', 'Image', this)
-  this.outputs.push(imageOut)
-
-  this.init = () => {
-    this.dom = $('<div>').get(0)
-  }
-
-  //import
-  const png_read_handler = (e) => {
-    const reader = new FileReader();
-
-    const files = e.target.files;
-    const file = files[0];
-
-    const img = new Image();
-    img.file = file;
-
-    const loader = (aImg) => (e) => {
-      aImg.src = e.target.result;
-
-      aImg.onload = () => {
-        const canvas = document.getElementById("wackyAndTotallyUniqueID2");
-        const ctx = canvas.getContext("2d");
-        ctx.clearRect(0, 0, canvas.width, canvas.height);
-        ctx.drawImage(aImg, 0, 0);
-
-        // string other functions here
-        const data = getImageDataRGBA();
-        // console.log("data", data);
-
-        imageOut.put(data)
-        // console.log(data);
-        // console.log("copied", JSON.stringify(data));
-      }
-
-    };
-    reader.onload = loader(img);
-    reader.readAsDataURL(file);
-  }
-
-  // get image data: RGBA
-  const getImageDataRGBA = () => { //should add dpi term here
-    const canvas = document.getElementById("wackyAndTotallyUniqueID2");
-    const ctx = canvas.getContext("2d");
-    const imageRGBA = ctx.getImageData(0, 0, canvas.width, canvas.height);
-    return imageRGBA;
-  }
-
-  this.onload = () => {
-    const target = $('<div>').get(0);
-    $(this.dom).append(target);
-
-    const view = html `
-      <style>
-        #wackyAndTotallyUniqueID2 {
-          border: 1px solid black;
-          margin: 5px;
-        }
-
-        #wackyAndTotallyUniqueID {
-          position: absolute;
-          left: 10px;
-          top: 350px;
-        }
-      </style>
-      <div>Upload!</div>
-      <canvas
-        id="wackyAndTotallyUniqueID2"
-        width=300
-        height=300>
-      </canvas>
-      <input
-        id="wackyAndTotallyUniqueID"
-        type="file"
-        accept="image/png"
-        @input=${(e) => png_read_handler(e)}></input>
-    `
-    render(view, target);
-  }
-
-
-  this.loop = () => {}
-}
diff --git a/hunks/image/thresholdrgba_leo.js b/hunks/image/thresholdrgba.js
similarity index 100%
rename from hunks/image/thresholdrgba_leo.js
rename to hunks/image/thresholdrgba.js
diff --git a/hunks/link.js b/hunks/link.js
index bcd415f062a62a8d223d10ed2341ceff5ab29932..dc1e86b6775e3f039813d6cd1ed7b84ad546138a 100644
--- a/hunks/link.js
+++ b/hunks/link.js
@@ -16,7 +16,8 @@ import {
 import {
   TSET,
   LK,
-  MSGS
+  MSGS,
+  findPhy
 } from '../typeset.js'
 
 function Link() {
@@ -93,9 +94,15 @@ function Link() {
       } else {
         // the object doesn't already exist,
         if (input) {
-          this.inputs[kp + 1] = new Input(nks[kp].typeKey, nks[kp].nameKey, this, true)
+          // we can only make types we have serialization routines for:
+          let phy = findPhy(nks[kp].typeKey)
+          if(phy.key && phy.read){
+            this.inputs[kp + 1] = new Input(nks[kp].typeKey, nks[kp].nameKey, this, true)
+          }
         } else {
-          this.outputs[kp + 1] = new Output(nks[kp].typeKey, nks[kp].nameKey, this, true)
+          if(phy.key && phy.write){
+            this.outputs[kp + 1] = new Output(nks[kp].typeKey, nks[kp].nameKey, this, true)
+          }
         }
       }
     }
@@ -135,9 +142,6 @@ function Link() {
   /* ---------------------------    ---------------------------- */
 
   this.init = () => {
-    // manager calls this once
-    // it is loaded and state is updated (from program)
-    this.log('LINK INIT BEGINS ->')
     // since we know this needs to default to nc, and many programs
     // will save with these states set 'true', we reset them now.
     //otherLink.set(0)
@@ -147,12 +151,16 @@ function Link() {
     // just add in order
     let ipKeys = getTypeAndNameKeys(inputList.value)
     for (let kp of ipKeys) {
-      this.inputs.push(new Input(kp.typeKey, kp.nameKey, this, true))
+      if(getPhy(kp.typeKey).key && getPhy(kp.typeKey).read){
+        this.inputs.push(new Input(kp.typeKey, kp.nameKey, this, true))
+      }
     }
 
     let opKeys = getTypeAndNameKeys(outputList.value)
     for (let kp of opKeys) {
-      this.outputs.push(new Output(kp.typeKey, kp.nameKey, this, true))
+      if(getPhy(kp.typeKey).key && getPhy(kp.typeKey).write){
+        this.outputs.push(new Output(kp.typeKey, kp.nameKey, this, true))
+      }
     }
 
   }
diff --git a/hunks/manager.js b/hunks/manager.js
index ca360414a5db71a9e9b20360a334f87bbb3bad6f..2839aaa8b5b459137c602457e506b8d9435425bd 100644
--- a/hunks/manager.js
+++ b/hunks/manager.js
@@ -301,7 +301,7 @@ function Manager() {
     for (let ind in hunks) {
       // alright look, we keep a copy of this
       if (hunks[ind].ind !== parseInt(ind)) {
-        console.log(`swapping ${hunks[ind].ind} for ${parseInt(ind)}`)
+        // console.log(`swapping ${hunks[ind].ind} for ${parseInt(ind)}`)
         hunks[ind].ind = parseInt(ind)
       }
     }
diff --git a/hunks/view.js b/hunks/view.js
index 871bed98fefeb415a4f4da681854a40e1050db49..fef8a94a130c3bfd74f3f63771231c6cdf58a118 100644
--- a/hunks/view.js
+++ b/hunks/view.js
@@ -504,7 +504,7 @@ function View() {
   /* ---------------------------    ---------------------------- */
 
   let removeDef = (index) => {
-    console.log('removeDef, also many floop likely errors')
+    // console.log('removeDef, also many floop likely errors')
     // ok ok ok
     let od = defs[index]
     if (od === undefined) throw new Error('no hunk to delete!')
@@ -526,7 +526,7 @@ function View() {
   let onDefReorder = () => {
     for (let ind in defs) {
       if (defs[ind].ind !== parseInt(ind)) {
-        console.log(`swapping ${defs[ind].ind} for ${parseInt(ind)}`)
+        // console.log(`swapping ${defs[ind].ind} for ${parseInt(ind)}`)
         defs[ind].newInd(parseInt(ind))
       }
     }
diff --git a/processes/vfpts.js b/processes/vfpts.js
index b11ee8fd34b58209e43f88b1c850fc49b081df38..03895cf5c70a939c7ccb12bd444ce570368d48d0 100644
--- a/processes/vfpts.js
+++ b/processes/vfpts.js
@@ -193,3 +193,12 @@ let openPort = () => {
 }
 
 findSerialPort()
+
+// this causes node to req. time from the OS more often (as often as possible)
+// meaning that our events are handled more often, and we drop ring times by some ms
+// does burn cycles though, 
+
+let reminders = () => {
+  setImmediate(reminders)
+}
+reminders()
diff --git a/scratch/readpng.js b/scratch/readpng.js
new file mode 100644
index 0000000000000000000000000000000000000000..2ba2af8a8fdccc1024b9183466aa66d8a2d26011
--- /dev/null
+++ b/scratch/readpng.js
@@ -0,0 +1,78 @@
+/*
+
+hunk template
+
+*/
+
+// these are ES6 modules
+import {
+  Hunkify,
+  Input,
+  Output,
+  State
+} from '../hunks.js'
+
+function ReadPNG() {
+  // this fn attaches handles to our function-object,
+  Hunkify(this)
+
+  let imageOutput = new Output('rgba', 'image', this)
+  let buttonOutput = new Output('string', 'button', this)
+  this.outputs.push(imageOutput, buttonOutput)
+
+  let xSize = new State('number', 'xSize', 400)
+  let ySize = new State('number', 'ySize', 400)
+  this.states.push(xSize, ySize)
+
+  // State items also have change handlers,
+  xSize.onChange = (value) => {
+    // at this point, something external (probably a human)
+    // has requested that we change this state variable,
+    // we can reject that, by doing nothing here, or we can
+    stateItem.set(value)
+  }
+
+  // hunks can choose to- or not- have init code.
+  // at init, the module has been loaded and state variables have been
+  // recalled from any program save - so this is a good point
+  // to check any of those, and setup accordingly ...
+  // as is tradition,
+  this.dom = {}
+
+  let button
+
+  this.init = () => {
+      // manager calls this once
+      // it is loaded and state is updated (from program)
+      console.log('HELLO Read PNG')
+      this.dom = document.createElement('div')
+      // 
+  }
+
+  this.onload = () => {
+    let contact = $('<div>').addClass('btn').append('! read png !').get(0)
+    $(this.dom).append(contact)
+    contact.addEventListener('click', (evt) => {
+        buttonOutput.put('anything')
+    })
+  }
+
+  // to divide time between hunks, each has a loop function
+  // this is the hunks' runtime: a manager calls this once-per-round
+  // here is where we check inputs, put to outputs, do work, etc
+  this.loop = () => {
+    // typically we check inputs and outputs first,
+    // making sure we are clear to run,
+    /*
+    if (inA.io() && !outB.io()) {
+      // an input is occupied, and the exit path is empty
+      let output = internalFunc(this.inputs.a.get())
+      // put 'er there
+      outB.put(output)
+    }
+    */
+  }
+}
+
+// the hunk is also an ES6 module, this is how we export those:
+export default ReadPNG
diff --git a/hunks/image/threshold.js b/scratch/threshold.js
similarity index 100%
rename from hunks/image/threshold.js
rename to scratch/threshold.js
diff --git a/test_files/ATP.8E5.traces.png b/test_files/ATP.8E5.traces.png
new file mode 100644
index 0000000000000000000000000000000000000000..aeda5d3836e904dbd2694caf869c6e35f2f0fa2d
Binary files /dev/null and b/test_files/ATP.8E5.traces.png differ
diff --git a/scratch/test.png b/test_files/test.png
similarity index 100%
rename from scratch/test.png
rename to test_files/test.png
diff --git a/typeset.js b/typeset.js
index c4ad6148a1ab27d1d2051628fa38d7464c65ecbc..fe4d9748262403f9782a06bdad33184bbc9d2705 100644
--- a/typeset.js
+++ b/typeset.js
@@ -431,6 +431,19 @@ const TSET = [
         return intBounds(num, -2147483647, 2147483647)
       }
     }
+  },
+  { // cuttlefish only, so no key, read or write fn's
+    // this is : https://developer.mozilla.org/en-US/docs/Web/API/ImageData
+    name: 'ImageData',
+    copy: {
+      ImageData: function(imageData){
+        return new ImageData(
+          new Uint8ClampedArray(imageData.data),
+          imageData.width,
+          imageData.height
+        ) //
+      }
+    }
   }
   // etc
 ] // end TSET