diff --git a/hunks/adhoc/center.js b/hunks/adhoc/center.js
new file mode 100644
index 0000000000000000000000000000000000000000..8fbc5c5d14987e872aac26885a120876d12c5d1c
--- /dev/null
+++ b/hunks/adhoc/center.js
@@ -0,0 +1,99 @@
+/*
+
+find pixel space hotspot
+
+*/
+
+import {
+  Hunkify,
+  Input,
+  Output,
+  State
+} from '../hunks.js'
+
+import REG from '../../libs/regression.js'
+
+export default function HotSpotHunter(){
+  Hunkify(this)
+  // will assume this is grayscale'd, and max-dr'd already
+  let ipImg = this.input('ImageData', 'image')
+
+  let opX = this.output('number', 'x')
+  let opY = this.output('number', 'y')
+
+  console.log('REG?', REG)
+
+  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
+  // and ah virtual friend:
+  let vc = $('<canvas>').get(0)
+  let vctx = vc.getContext('2d')
+
+  this.onload = () => {
+    $(this.dom).append(canvas)
+  }
+
+  this.loop = () => {
+    if(ipImg.io() && !opX.io() && !opY.io()){
+      let img = ipImg.get()
+      // first, just hunt for abs. peak:
+      // draw in to virtual,
+      //ctx.putImageData(img, 0, 0)
+      vc.height = img.height
+      vc.width = img.width
+      let scale = canvas.width / img.width
+      vctx.putImageData(img, 0, 0)
+      canvas.height = canvas.width * (img.height / img.width)
+      ctx.drawImage(vc, 0, 0, canvas.width, canvas.height)
+      // ok, that was just drawing into the canvas - we
+      // want to find the center by inspecting the actual image data...
+      let max = -Infinity
+      let mp = {x: 0, y: 0}
+      for(let i = 0; i < img.data.length; i += 4){
+        if(img.data[i] > max){
+          max = img.data[i]
+          mp.x = i % (img.width * 4) / 4
+          mp.y = Math.floor(i / (img.width * 4))
+        }
+      }
+      // if we try to fit a quadratic across some domain, centered here... I need to collect
+      // *the appropriate data*
+      // this is easiest in the x-dir bf oc the data shape
+      let xSamples = []
+      let ySamples = []
+      let xCI = mp.y * (img.width * 4) + (mp.x * 4)// center index
+      for(let i = -10; i < 11; i ++){
+        xSamples.push([i, img.data[xCI + i * 4]])
+        ySamples.push([i, img.data[xCI + i * 4 * img.width]])
+      }
+      //console.log('samp', xSamples)
+      let regX = REG.polynomial(xSamples, {order: 2, precision: 6})
+      let regY = REG.polynomial(ySamples, {order: 2, precision: 6})
+      //console.log('ret', ret)
+      // vertex is -b / 2a
+      let xVert = -regX.equation[1] / (2*regX.equation[0])
+      let yVert = -regY.equation[1] / (2*regY.equation[0])
+      mp.x += xVert
+      mp.y += yVert
+      //console.log('mp.x, .y', mp.x, mp.y)
+      // that's great, let's draw it:
+      ctx.beginPath()
+      ctx.moveTo(mp.x * scale, 0)
+      ctx.lineTo(mp.x * scale, canvas.height)//ctx.height)
+      ctx.stroke()
+      ctx.beginPath()
+      ctx.moveTo(0, mp.y * scale)
+      ctx.lineTo(canvas.width, mp.y * scale)
+      ctx.stroke()
+      //ctx.arc(mp.x * scale, mp.y * scale, 8, 0, 2*Math.PI)
+      //ctx.stroke()
+      // and ship em
+      opX.put(mp.x)
+      opY.put(mp.y)
+    }
+  }
+}
diff --git a/hunks/adhoc/correlate.js b/hunks/adhoc/correlate.js
index 601ebbf317d68bd6db748dcf137f6283d8adf812..648f6f66e934f370eb68ce347e00a57df922b75d 100644
--- a/hunks/adhoc/correlate.js
+++ b/hunks/adhoc/correlate.js
@@ -1,6 +1,6 @@
 /*
 
-attempt of fft cross correlation for fast tracking / subpixel measurements
+attempt for barebones template matching w/ cross correlation dot product
 
 */
 
@@ -99,8 +99,8 @@ function worker(){
     let resY = b.height - a.height
     let numruns = resX * resY
     // the move now is to make an md array of these values,
-    let bArr = packRBGA(b) //packGrayscale(b)
-    let aArr = packRBGA(a) //packGrayscale(a)
+    let bArr = packGrayscale(b) //packRBGA(b) //packGrayscale(b)
+    let aArr = packGrayscale(a) //packGrayscale(a)
     // ok, results array like[x][y]
     let result = []
     for (let x = 0; x < resX; x++) {
@@ -109,7 +109,7 @@ function worker(){
     // now fill,
     for (let x = 0; x < resX; x++) {
       for (let y = 0; y < resY; y++) {
-        result[x][y] = correlateRBGA(aArr, bArr, x, y)
+        result[x][y] = correlateGrayscale(aArr, bArr, x, y)//correlateRBGA(aArr, bArr, x, y)
       }
     }
     // make image from the result,
@@ -160,11 +160,11 @@ export default function Correlate() {
   let resOut = this.output('ImageData', 'correlation')
 
   let canvasA = $('<canvas>').get(0)
-  canvasA.width = 50
-  canvasA.height = 50
+  canvasA.width = 24
+  canvasA.height = 24
   let ctxA = canvasA.getContext('2d')
-  ctxA.width = 50
-  ctxA.height = 50
+  ctxA.width = 24
+  ctxA.height = 24
 
   let canvasB = $('<canvas>').get(0)
   let ctxB = canvasB.getContext('2d')
@@ -214,9 +214,18 @@ export default function Correlate() {
       // now we can pull the imagedata (scaled) from here,
       let b = ctxB.getImageData(0, 0, canvasB.width, canvasB.height)
       // and the thing we want to find, to test, just pick the middle:
-      let a = ctxB.getImageData(b.width / 2, b.height / 2, 50, 50)
+      //let a = ctxB.getImageData(b.width / 2, b.height / 2, 25, 25)
       // and write that out, to debug ...
-      ctxA.putImageData(a, 0, 0)
+      //ctxA.putImageData(a, 0, 0)
+      ctxA.fillStyle = 'white'
+      ctxA.fillRect(0,0,24,24)
+      ctxA.fillStyle = 'black'
+      // 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/libs/regression.js b/libs/regression.js
new file mode 100644
index 0000000000000000000000000000000000000000..7165405f76bec32b58b032797d15cd3dbb990c3a
--- /dev/null
+++ b/libs/regression.js
@@ -0,0 +1,330 @@
+// http://tom-alexander.github.io/regression-js/
+// jake repackages for ES6 browser module 
+
+function _toConsumableArray(arr) {
+  if (Array.isArray(arr)) {
+    for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) {
+      arr2[i] = arr[i];
+    }
+
+    return arr2;
+  } else {
+    return Array.from(arr);
+  }
+}
+
+var DEFAULT_OPTIONS = { order: 2, precision: 2, period: null };
+
+/**
+ * Determine the coefficient of determination (r^2) of a fit from the observations
+ * and predictions.
+ *
+ * @param {Array<Array<number>>} data - Pairs of observed x-y values
+ * @param {Array<Array<number>>} results - Pairs of observed predicted x-y values
+ *
+ * @return {number} - The r^2 value, or NaN if one cannot be calculated.
+ */
+function determinationCoefficient(data, results) {
+  var predictions = [];
+  var observations = [];
+
+  data.forEach(function(d, i) {
+    if (d[1] !== null) {
+      observations.push(d);
+      predictions.push(results[i]);
+    }
+  });
+
+  var sum = observations.reduce(function(a, observation) {
+    return a + observation[1];
+  }, 0);
+  var mean = sum / observations.length;
+
+  var ssyy = observations.reduce(function(a, observation) {
+    var difference = observation[1] - mean;
+    return a + difference * difference;
+  }, 0);
+
+  var sse = observations.reduce(function(accum, observation, index) {
+    var prediction = predictions[index];
+    var residual = observation[1] - prediction[1];
+    return accum + residual * residual;
+  }, 0);
+
+  return 1 - sse / ssyy;
+}
+
+/**
+ * Determine the solution of a system of linear equations A * x = b using
+ * Gaussian elimination.
+ *
+ * @param {Array<Array<number>>} input - A 2-d matrix of data in row-major form [ A | b ]
+ * @param {number} order - How many degrees to solve for
+ *
+ * @return {Array<number>} - Vector of normalized solution coefficients matrix (x)
+ */
+function gaussianElimination(input, order) {
+  var matrix = input;
+  var n = input.length - 1;
+  var coefficients = [order];
+
+  for (var i = 0; i < n; i++) {
+    var maxrow = i;
+    for (var j = i + 1; j < n; j++) {
+      if (Math.abs(matrix[i][j]) > Math.abs(matrix[i][maxrow])) {
+        maxrow = j;
+      }
+    }
+
+    for (var k = i; k < n + 1; k++) {
+      var tmp = matrix[k][i];
+      matrix[k][i] = matrix[k][maxrow];
+      matrix[k][maxrow] = tmp;
+    }
+
+    for (var _j = i + 1; _j < n; _j++) {
+      for (var _k = n; _k >= i; _k--) {
+        matrix[_k][_j] -= matrix[_k][i] * matrix[i][_j] / matrix[i][i];
+      }
+    }
+  }
+
+  for (var _j2 = n - 1; _j2 >= 0; _j2--) {
+    var total = 0;
+    for (var _k2 = _j2 + 1; _k2 < n; _k2++) {
+      total += matrix[_k2][_j2] * coefficients[_k2];
+    }
+
+    coefficients[_j2] = (matrix[n][_j2] - total) / matrix[_j2][_j2];
+  }
+
+  return coefficients;
+}
+
+/**
+ * Round a number to a precision, specificed in number of decimal places
+ *
+ * @param {number} number - The number to round
+ * @param {number} precision - The number of decimal places to round to:
+ *                             > 0 means decimals, < 0 means powers of 10
+ *
+ *
+ * @return {numbr} - The number, rounded
+ */
+function round(number, precision) {
+  var factor = Math.pow(10, precision);
+  return Math.round(number * factor) / factor;
+}
+
+/**
+ * The set of all fitting methods
+ *
+ * @namespace
+ */
+var methods = {
+  linear: function linear(data, options) {
+    var sum = [0, 0, 0, 0, 0];
+    var len = 0;
+
+    for (var n = 0; n < data.length; n++) {
+      if (data[n][1] !== null) {
+        len++;
+        sum[0] += data[n][0];
+        sum[1] += data[n][1];
+        sum[2] += data[n][0] * data[n][0];
+        sum[3] += data[n][0] * data[n][1];
+        sum[4] += data[n][1] * data[n][1];
+      }
+    }
+
+    var run = len * sum[2] - sum[0] * sum[0];
+    var rise = len * sum[3] - sum[0] * sum[1];
+    var gradient = run === 0 ? 0 : round(rise / run, options.precision);
+    var intercept = round(sum[1] / len - gradient * sum[0] / len, options.precision);
+
+    var predict = function predict(x) {
+      return [round(x, options.precision), round(gradient * x + intercept, options.precision)];
+    };
+
+    var points = data.map(function(point) {
+      return predict(point[0]);
+    });
+
+    return {
+      points: points,
+      predict: predict,
+      equation: [gradient, intercept],
+      r2: round(determinationCoefficient(data, points), options.precision),
+      string: intercept === 0 ? 'y = ' + gradient + 'x' : 'y = ' + gradient + 'x + ' + intercept
+    };
+  },
+  exponential: function exponential(data, options) {
+    var sum = [0, 0, 0, 0, 0, 0];
+
+    for (var n = 0; n < data.length; n++) {
+      if (data[n][1] !== null) {
+        sum[0] += data[n][0];
+        sum[1] += data[n][1];
+        sum[2] += data[n][0] * data[n][0] * data[n][1];
+        sum[3] += data[n][1] * Math.log(data[n][1]);
+        sum[4] += data[n][0] * data[n][1] * Math.log(data[n][1]);
+        sum[5] += data[n][0] * data[n][1];
+      }
+    }
+
+    var denominator = sum[1] * sum[2] - sum[5] * sum[5];
+    var a = Math.exp((sum[2] * sum[3] - sum[5] * sum[4]) / denominator);
+    var b = (sum[1] * sum[4] - sum[5] * sum[3]) / denominator;
+    var coeffA = round(a, options.precision);
+    var coeffB = round(b, options.precision);
+    var predict = function predict(x) {
+      return [round(x, options.precision), round(coeffA * Math.exp(coeffB * x), options.precision)];
+    };
+
+    var points = data.map(function(point) {
+      return predict(point[0]);
+    });
+
+    return {
+      points: points,
+      predict: predict,
+      equation: [coeffA, coeffB],
+      string: 'y = ' + coeffA + 'e^(' + coeffB + 'x)',
+      r2: round(determinationCoefficient(data, points), options.precision)
+    };
+  },
+  logarithmic: function logarithmic(data, options) {
+    var sum = [0, 0, 0, 0];
+    var len = data.length;
+
+    for (var n = 0; n < len; n++) {
+      if (data[n][1] !== null) {
+        sum[0] += Math.log(data[n][0]);
+        sum[1] += data[n][1] * Math.log(data[n][0]);
+        sum[2] += data[n][1];
+        sum[3] += Math.pow(Math.log(data[n][0]), 2);
+      }
+    }
+
+    var a = (len * sum[1] - sum[2] * sum[0]) / (len * sum[3] - sum[0] * sum[0]);
+    var coeffB = round(a, options.precision);
+    var coeffA = round((sum[2] - coeffB * sum[0]) / len, options.precision);
+
+    var predict = function predict(x) {
+      return [round(x, options.precision), round(round(coeffA + coeffB * Math.log(x), options.precision), options.precision)];
+    };
+
+    var points = data.map(function(point) {
+      return predict(point[0]);
+    });
+
+    return {
+      points: points,
+      predict: predict,
+      equation: [coeffA, coeffB],
+      string: 'y = ' + coeffA + ' + ' + coeffB + ' ln(x)',
+      r2: round(determinationCoefficient(data, points), options.precision)
+    };
+  },
+  power: function power(data, options) {
+    var sum = [0, 0, 0, 0, 0];
+    var len = data.length;
+
+    for (var n = 0; n < len; n++) {
+      if (data[n][1] !== null) {
+        sum[0] += Math.log(data[n][0]);
+        sum[1] += Math.log(data[n][1]) * Math.log(data[n][0]);
+        sum[2] += Math.log(data[n][1]);
+        sum[3] += Math.pow(Math.log(data[n][0]), 2);
+      }
+    }
+
+    var b = (len * sum[1] - sum[0] * sum[2]) / (len * sum[3] - Math.pow(sum[0], 2));
+    var a = (sum[2] - b * sum[0]) / len;
+    var coeffA = round(Math.exp(a), options.precision);
+    var coeffB = round(b, options.precision);
+
+    var predict = function predict(x) {
+      return [round(x, options.precision), round(round(coeffA * Math.pow(x, coeffB), options.precision), options.precision)];
+    };
+
+    var points = data.map(function(point) {
+      return predict(point[0]);
+    });
+
+    return {
+      points: points,
+      predict: predict,
+      equation: [coeffA, coeffB],
+      string: 'y = ' + coeffA + 'x^' + coeffB,
+      r2: round(determinationCoefficient(data, points), options.precision)
+    };
+  },
+  polynomial: function polynomial(data, options) {
+    var lhs = [];
+    var rhs = [];
+    var a = 0;
+    var b = 0;
+    var len = data.length;
+    var k = options.order + 1;
+
+    for (var i = 0; i < k; i++) {
+      for (var l = 0; l < len; l++) {
+        if (data[l][1] !== null) {
+          a += Math.pow(data[l][0], i) * data[l][1];
+        }
+      }
+
+      lhs.push(a);
+      a = 0;
+
+      var c = [];
+      for (var j = 0; j < k; j++) {
+        for (var _l = 0; _l < len; _l++) {
+          if (data[_l][1] !== null) {
+            b += Math.pow(data[_l][0], i + j);
+          }
+        }
+        c.push(b);
+        b = 0;
+      }
+      rhs.push(c);
+    }
+    rhs.push(lhs);
+
+    var coefficients = gaussianElimination(rhs, k).map(function(v) {
+      return round(v, options.precision);
+    });
+
+    var predict = function predict(x) {
+      return [round(x, options.precision), round(coefficients.reduce(function(sum, coeff, power) {
+        return sum + coeff * Math.pow(x, power);
+      }, 0), options.precision)];
+    };
+
+    var points = data.map(function(point) {
+      return predict(point[0]);
+    });
+
+    var string = 'y = ';
+    for (var _i = coefficients.length - 1; _i >= 0; _i--) {
+      if (_i > 1) {
+        string += coefficients[_i] + 'x^' + _i + ' + ';
+      } else if (_i === 1) {
+        string += coefficients[_i] + 'x + ';
+      } else {
+        string += coefficients[_i];
+      }
+    }
+
+    return {
+      string: string,
+      points: points,
+      predict: predict,
+      equation: [].concat(_toConsumableArray(coefficients)).reverse(),
+      r2: round(determinationCoefficient(data, points), options.precision)
+    };
+  }
+};
+
+export default methods
diff --git a/save/contexts/cuttlefish/correlate.json b/save/contexts/cuttlefish/correlate.json
index e68e608578f5ebce50c91d51870c5d48b172b4db..daeea9d192276a0f4824e830aeea89e9d7387623 100644
--- a/save/contexts/cuttlefish/correlate.json
+++ b/save/contexts/cuttlefish/correlate.json
@@ -89,10 +89,24 @@
             {
               "inHunkIndex": "3",
               "inHunkInput": "0"
+            },
+            {
+              "inHunkIndex": "5",
+              "inHunkInput": "0"
             }
           ]
         }
       ]
+    },
+    {
+      "type": "adhoc/center",
+      "name": "adhoc/center_5",
+      "inputs": [
+        {
+          "name": "image",
+          "type": "ImageData"
+        }
+      ]
     }
   ]
 }
\ No newline at end of file