diff --git a/ESP/mqtt_test/mqtt_test.ino b/ESP/mqtt_test/mqtt_test.ino
index 81157d1..007da7e 100644
--- a/ESP/mqtt_test/mqtt_test.ino
+++ b/ESP/mqtt_test/mqtt_test.ino
@@ -8,7 +8,8 @@ const char* mqttServer = "10.1.13.173";
 const int mqttPort = 1883;
 const char* mqttTopic = "test";
 
-const int gpioPin = 25;  // GPIO pin to check for shorting
+const int gpioPins[] = {25, 26, 27, 14};  // GPIO pins to check for shorting
+const unsigned int numPins = sizeof(gpioPins) / sizeof(gpioPins[0]);
 const unsigned long debounceDelay = 50;  // Debounce delay in milliseconds
 
 WiFiClient wifiClient;
@@ -16,9 +17,7 @@ PubSubClient mqttClient(wifiClient);
 
 TaskHandle_t mqttTaskHandle = NULL;
 
-Bounce debouncer = Bounce();
-
-volatile int pressCounter = 0;
+Bounce debouncers[numPins];
 
 void setupWiFi() {
   WiFi.begin(ssid, password);
@@ -54,25 +53,30 @@ void mqttTask(void* parameter) {
     ulTaskNotifyTake(pdTRUE, portMAX_DELAY);  // Wait for notification
 
     if (mqttClient.connected()) {
-      String message = "GPIO pin shorted! Count: " + String(pressCounter);
-      mqttClient.publish(mqttTopic, message.c_str());
-      Serial.println("Message sent to MQTT server");
-      pressCounter++;
+      for (unsigned int i = 0; i < numPins; i++) {
+        if (debouncers[i].fell()) {
+          String message = "Pin shorted: " + String(gpioPins[i]);
+          mqttClient.publish(mqttTopic, message.c_str());
+          Serial.println("Message sent to MQTT server");
+        }
+      }
     }
   }
 }
 
 void setup() {
   Serial.begin(115200);
-  pinMode(gpioPin, INPUT_PULLUP);
-
-  debouncer.attach(gpioPin);
-  debouncer.interval(debounceDelay);
 
   setupWiFi();
   mqttClient.setServer(mqttServer, mqttPort);
   mqttClient.setCallback(callback);
 
+  for (unsigned int i = 0; i < numPins; i++) {
+    pinMode(gpioPins[i], INPUT_PULLUP);
+    debouncers[i].attach(gpioPins[i]);
+    debouncers[i].interval(debounceDelay);
+  }
+
   xTaskCreatePinnedToCore(
       mqttTask,        // Task function
       "mqttTask",      // Task name
@@ -90,8 +94,19 @@ void loop() {
   }
   mqttClient.loop();
 
-  debouncer.update();
-  if (debouncer.fell()) {
+  for (unsigned int i = 0; i < numPins; i++) {
+    debouncers[i].update();
+  }
+
+  bool anyPinShorted = false;
+  for (unsigned int i = 0; i < numPins; i++) {
+    if (debouncers[i].fell()) {
+      anyPinShorted = true;
+      break;
+    }
+  }
+
+  if (anyPinShorted) {
     BaseType_t xHigherPriorityTaskWoken = pdFALSE;
     vTaskNotifyGiveFromISR(mqttTaskHandle, &xHigherPriorityTaskWoken);
     if (xHigherPriorityTaskWoken == pdTRUE) {
diff --git a/faces/prootface3.png b/faces/prootface3.png
new file mode 100644
index 0000000..69b53fd
Binary files /dev/null and b/faces/prootface3.png differ
diff --git a/faces/prootface4.png b/faces/prootface4.png
new file mode 100644
index 0000000..a765db5
Binary files /dev/null and b/faces/prootface4.png differ
diff --git a/minDistance.py b/minDistance.py
new file mode 100644
index 0000000..0bee9b6
--- /dev/null
+++ b/minDistance.py
@@ -0,0 +1,132 @@
+import math
+from PIL import Image
+import numpy as np
+from scipy.optimize import linear_sum_assignment
+
+    
+class Point2D:
+    x = 0
+    y = 0
+    
+    def __init__(self, x, y):
+        self.x = x
+        self.y = y
+        
+    def round(self):
+        self.x = round(self.x)
+        self.y = round(self.y)
+        return self
+    
+    def distance(self, other):
+        dx = self.x - other.x
+        dy = self.y - other.y
+        return math.sqrt(dx**2 + dy**2)
+    
+    def interpolate(self, other, percentage):
+        new_x = self.x + (other.x - self.x) * percentage
+        new_y = self.y + (other.y - self.y) * percentage
+        return Point2D(new_x, new_y)
+    
+    def __eq__(self, other):
+        return (self.x, self.y) == (other.x, other.y)
+
+
+def generate_point_array_from_image(image):
+    image = image.convert("RGB")  # Convert image to RGB color mode
+
+    width, height = image.size
+    point_array = []
+
+    # Iterate over the pixels and generate Point2D instances
+    for y in range(height):
+        for x in range(width):
+            pixel = image.getpixel((x, y))
+            if pixel != (0, 0, 0):  # Assuming white pixels
+                point = Point2D(x, y)
+                point_array.append(point)
+
+    return point_array
+
+
+def generate_image_from_point_array(points, width, height):
+    # Create a new blank image
+    image = Image.new("RGB", (width, height), "black")
+    
+    # Set the pixels corresponding to the points as white
+    pixels = image.load()
+    for point in points:
+        point = point.round()
+        x = point.x
+        y = point.y
+        pixels[x, y] = (255, 255, 255)
+    
+    return image
+
+
+def pair_points(points1, points2):
+    # Determine the size of the point arrays
+    size1 = len(points1)
+    size2 = len(points2)
+    
+    # Create a cost matrix based on the distances between points
+    cost_matrix = np.zeros((size1, size2))
+    for i in range(size1):
+        for j in range(size2):
+            cost_matrix[i, j] = points1[i].distance(points2[j])
+    
+    # Duplicate points in the smaller array to match the size of the larger array
+    if size1 > size2:
+        num_duplicates = size1 - size2
+        duplicated_points = np.random.choice(points2, size=num_duplicates).tolist()
+        points2 += duplicated_points
+    elif size2 > size1:
+        num_duplicates = size2 - size1
+        duplicated_points = np.random.choice(points1, size=num_duplicates).tolist()
+        points1 += duplicated_points
+    
+    # Update the size of the point arrays
+    size1 = len(points1)
+    size2 = len(points2)
+    
+    # Create a new cost matrix with the updated sizes
+    cost_matrix = np.zeros((size1, size2))
+    for i in range(size1):
+        for j in range(size2):
+            cost_matrix[i, j] = points1[i].distance(points2[j])
+    
+    # Solve the assignment problem using the Hungarian algorithm
+    row_ind, col_ind = linear_sum_assignment(cost_matrix)
+    
+    # Create pairs of points based on the optimal assignment
+    pairs = []
+    for i, j in zip(row_ind, col_ind):
+        pairs.append((points1[i], points2[j]))
+    
+    return pairs
+
+
+def interpolate_point_pairs(pairs, percentage):
+    interpolated_points = []
+    for pair in pairs:
+        point1, point2 = pair
+        interpolated_point = point1.interpolate(point2, percentage)
+        interpolated_points.append(interpolated_point)
+    return interpolated_points
+
+
+Image1 = Image.open("CiscoTheProot/faces/prootface3.png")
+Image2 = Image.open("CiscoTheProot/faces/prootface4.png")
+
+pixelArray1 = generate_point_array_from_image(Image1)
+pixelArray2 = generate_point_array_from_image(Image2)
+
+pairs = pair_points(pixelArray1, pixelArray2)
+
+
+generate_image_from_point_array(interpolate_point_pairs(pairs, 0), 128, 32).show()
+generate_image_from_point_array(interpolate_point_pairs(pairs, .25), 128, 32).show()
+generate_image_from_point_array(interpolate_point_pairs(pairs, .5), 128, 32).show()
+generate_image_from_point_array(interpolate_point_pairs(pairs, .75), 128, 32).show()
+generate_image_from_point_array(interpolate_point_pairs(pairs, 1), 128, 32).show()
+
+print(pairs)
\ No newline at end of file
diff --git a/rpi/antRender.py b/rpi/antRender.py
new file mode 100644
index 0000000..873b187
--- /dev/null
+++ b/rpi/antRender.py
@@ -0,0 +1,222 @@
+from rgbmatrix import RGBMatrix, RGBMatrixOptions
+import paho.mqtt.client as mqtt
+import time
+from PIL import Image
+import numpy as np
+
+
+# Configuration for the matrix
+options = RGBMatrixOptions()
+options.rows = 32
+options.cols = 64
+options.chain_length = 2
+options.parallel = 1
+options.hardware_mapping = 'regular'  # If you have an Adafruit HAT: 'adafruit-hat'
+matrix = RGBMatrix(options=options)
+
+
+import math
+from PIL import Image
+import numpy as np
+from scipy.optimize import linear_sum_assignment
+
+    
+class Point2D:
+    x = 0
+    y = 0
+    
+    def __init__(self, x, y):
+        self.x = x
+        self.y = y
+        
+    def round(self):
+        self.x = round(self.x)
+        self.y = round(self.y)
+        return self
+    
+    def distance(self, other):
+        dx = self.x - other.x
+        dy = self.y - other.y
+        return math.sqrt(dx**2 + dy**2)
+    
+    def interpolate(self, other, percentage):
+        new_x = self.x + (other.x - self.x) * percentage
+        new_y = self.y + (other.y - self.y) * percentage
+        return Point2D(new_x, new_y)
+    
+    def __eq__(self, other):
+        return (self.x, self.y) == (other.x, other.y)
+
+
+def generate_point_array_from_image(image):
+    image = image.convert("RGB")  # Convert image to RGB color mode
+
+    width, height = image.size
+    point_array = []
+
+    # Iterate over the pixels and generate Point2D instances
+    for y in range(height):
+        for x in range(width):
+            pixel = image.getpixel((x, y))
+            if pixel != (0, 0, 0):  # Assuming white pixels
+                point = Point2D(x, y)
+                point_array.append(point)
+
+    return point_array
+
+
+def generate_image_from_point_array(points, width, height):
+    # Create a new blank image
+    image = Image.new("RGB", (width, height), "black")
+    
+    # Set the pixels corresponding to the points as white
+    pixels = image.load()
+    for point in points:
+        point = point.round()
+        x = point.x
+        y = point.y
+        pixels[x, y] = (255, 255, 255)
+    
+    return image
+
+
+def pair_points(points1, points2):
+    # Determine the size of the point arrays
+    size1 = len(points1)
+    size2 = len(points2)
+    
+    # Create a cost matrix based on the distances between points
+    cost_matrix = np.zeros((size1, size2))
+    for i in range(size1):
+        for j in range(size2):
+            cost_matrix[i, j] = points1[i].distance(points2[j])
+    
+    # Duplicate points in the smaller array to match the size of the larger array
+    if size1 > size2:
+        num_duplicates = size1 - size2
+        duplicated_points = np.random.choice(points2, size=num_duplicates).tolist()
+        points2 += duplicated_points
+    elif size2 > size1:
+        num_duplicates = size2 - size1
+        duplicated_points = np.random.choice(points1, size=num_duplicates).tolist()
+        points1 += duplicated_points
+    
+    # Update the size of the point arrays
+    size1 = len(points1)
+    size2 = len(points2)
+    
+    # Create a new cost matrix with the updated sizes
+    cost_matrix = np.zeros((size1, size2))
+    for i in range(size1):
+        for j in range(size2):
+            cost_matrix[i, j] = points1[i].distance(points2[j])
+    
+    # Solve the assignment problem using the Hungarian algorithm
+    row_ind, col_ind = linear_sum_assignment(cost_matrix)
+    
+    # Create pairs of points based on the optimal assignment
+    pairs = []
+    for i, j in zip(row_ind, col_ind):
+        pairs.append((points1[i], points2[j]))
+    
+    return pairs
+
+
+def interpolate_point_pairs(pairs, percentage):
+    interpolated_points = []
+    for pair in pairs:
+        point1, point2 = pair
+        interpolated_point = point1.interpolate(point2, percentage)
+        interpolated_points.append(interpolated_point)
+    return interpolated_points
+
+
+Image1 = Image.open("faces/prootface3.png")
+Image2 = Image.open("faces/prootface4.png")
+
+pixelArray1 = generate_point_array_from_image(Image1)
+pixelArray2 = generate_point_array_from_image(Image2)
+
+pairs = pair_points(pixelArray1, pixelArray2)
+
+
+
+
+
+
+DesiredBlinkState = 10
+currentBlinkState = 0
+
+blinkFrameCanvases = []
+
+offscreen_interpolated_canvasA = matrix.CreateFrameCanvas()
+offscreen_interpolated_canvasA.brightness = 50
+offscreen_interpolated_canvasA.SetImage(generate_image_from_point_array(interpolate_point_pairs(pairs, 0), 128, 32), unsafe=False)
+blinkFrameCanvases.append(offscreen_interpolated_canvasA)
+
+for alpha in range(1,10):
+    offscreen_interpolated_canvas = matrix.CreateFrameCanvas()
+    interpolated_image = generate_image_from_point_array(interpolate_point_pairs(pairs, alpha), 128, 32)
+    offscreen_interpolated_canvas.SetImage(interpolated_image, unsafe=False)
+    blinkFrameCanvases.append(offscreen_interpolated_canvas)
+
+offscreen_interpolated_canvasB = matrix.CreateFrameCanvas()
+offscreen_interpolated_canvasB.SetImage(generate_image_from_point_array(interpolate_point_pairs(pairs, 1), 128, 32), unsafe=False)
+blinkFrameCanvases.append(offscreen_interpolated_canvasB)
+
+
+
+def update_screen():
+    global DesiredBlinkState, currentBlinkState, blinkFrameCanvases, matrix, offscreen_interpolated_canvasA
+
+    # open eye again after blink
+    if currentBlinkState == 10:
+        DesiredBlinkState = 0
+        
+    if currentBlinkState == DesiredBlinkState:
+        next_canvas = blinkFrameCanvases[currentBlinkState]
+        return
+    
+    
+    next_canvas = blinkFrameCanvases[currentBlinkState]
+    
+    if currentBlinkState < DesiredBlinkState:
+        currentBlinkState += 1
+    else:
+        currentBlinkState -= 1
+
+    next_canvas = matrix.SwapOnVSync(next_canvas)
+
+
+
+
+# functions called by the MQTT listener
+def on_connect(client, userdata, flags, response_code):
+    print("Connected to MQTT broker with result code " + str(response_code))
+    client.subscribe("test")
+
+
+def on_message(client, userdata, message):
+    print("Received message '" + str(message.payload) + "' on topic '"
+          + message.topic + "' with QoS " + str(message.qos))
+    global DesiredBlinkState
+    DesiredBlinkState = 10
+
+# MQTT broker configuration
+broker_address = "10.1.13.173"  # Replace with your MQTT broker's address
+broker_port = 1883
+broker_keepalive = 60
+
+client = mqtt.Client()
+client.on_connect = on_connect
+client.on_message = on_message
+
+client.connect(broker_address, broker_port, broker_keepalive)
+
+client.loop_start()
+
+
+while True:
+    time.sleep(0.05)
+    update_screen()
+    
\ No newline at end of file
diff --git a/testImg1.png b/testImg1.png
new file mode 100644
index 0000000..b9b6021
Binary files /dev/null and b/testImg1.png differ
diff --git a/testImg2.png b/testImg2.png
new file mode 100644
index 0000000..426285b
Binary files /dev/null and b/testImg2.png differ