diff --git a/package.json b/package.json
index 81603ca..568ac33 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "gdq-viewport-assign",
-  "version": "0.9.1",
+  "version": "0.9.2",
   "description": "",
   "main": "dist/index.js",
   "scripts": {
diff --git a/src/icons.ts b/src/icons.ts
index 3c15660..b79b90d 100644
--- a/src/icons.ts
+++ b/src/icons.ts
@@ -30,7 +30,8 @@ const icons = {
   left: `data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20fill%3D%27none%27%20stroke%3D%27%23d2d2d2%27%20stroke-linecap%3D%27round%27%20stroke-linejoin%3D%27round%27%20stroke-width%3D%272%27%20viewBox%3D%270%200%2024%2024%27%3E%3Cpath%20d%3D%27m6%209%206%206%206-6%27%20transform%3D%27matrix(0%20-1.7072%20-1.7538%200%2032.495%2032.495)%27%2F%3E%3C%2Fsvg%3E`,
   right: `data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20fill%3D%27none%27%20stroke%3D%27%23d2d2d2%27%20stroke-linecap%3D%27round%27%20stroke-linejoin%3D%27round%27%20stroke-width%3D%272%27%20viewBox%3D%270%200%2024%2024%27%3E%3Cpath%20d%3D%27m6%209%206%206%206-6%27%20transform%3D%27matrix(0%201.7072%201.7538%200%20-8.519%20-8.48)%27%2F%3E%3C%2Fsvg%3E`,
 	addRow: `data:image/svg+xml,%3Csvg%20fill%3D%27none%27%20stroke-linejoin%3D%27round%27%20stroke-linecap%3D%27round%27%20stroke-width%3D%272%27%20viewBox%3D%270%200%2024%2024%27%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%3E%3Cdefs%3E%3Cmask%20id%3D%27a%27%3E%3Crect%20width%3D%27100%25%27%20height%3D%27100%25%27%20fill%3D%27%23fff%27%2F%3E%3Ccircle%20r%3D%278%27%20cx%3D%2712%27%20cy%3D%2716%27%20fill%3D%27%23000%27%2F%3E%3C%2Fmask%3E%3Cmask%20id%3D%27b%27%3E%3Crect%20width%3D%27100%25%27%20height%3D%27100%25%27%20fill%3D%27%23fff%27%2F%3E%3Cpath%20d%3D%27M9%2016h6m-3%203v-6%27%20stroke%3D%27%23000%27%2F%3E%3C%2Fmask%3E%3C%2Fdefs%3E%3Cpath%20d%3D%27M1%202v12h22V2Z%27%20stroke%3D%27%23d2d2d2%27%20mask%3D%27url%28%23a%29%27%2F%3E%3Ccircle%20r%3D%276%27%20cx%3D%2712%27%20cy%3D%2716%27%20fill%3D%27%23d2d2d2%27%20mask%3D%27url%28%23b%29%27%2F%3E%3C%2Fsvg%3E`,
-	removeRow: 'data:image/svg+xml,%3Csvg%20fill%3D%27none%27%20stroke-linejoin%3D%27round%27%20stroke-linecap%3D%27round%27%20stroke-width%3D%272%27%20viewBox%3D%270%200%2024%2024%27%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%3E%3Cdefs%3E%3Cmask%20id%3D%27a%27%3E%3Crect%20width%3D%27100%25%27%20height%3D%27100%25%27%20fill%3D%27%23fff%27%2F%3E%3Ccircle%20r%3D%278%27%20cx%3D%2712%27%20cy%3D%2716%27%20fill%3D%27%23000%27%2F%3E%3C%2Fmask%3E%3Cmask%20id%3D%27b%27%3E%3Crect%20width%3D%27100%25%27%20height%3D%27100%25%27%20fill%3D%27%23fff%27%2F%3E%3Cpath%20d%3D%27M9%2016h6%27%20stroke%3D%27%23000%27%2F%3E%3C%2Fmask%3E%3C%2Fdefs%3E%3Cpath%20d%3D%27M1%202v12h22V2Z%27%20stroke%3D%27%23d2d2d2%27%20mask%3D%27url%28%23a%29%27%2F%3E%3Ccircle%20r%3D%276%27%20cx%3D%2712%27%20cy%3D%2716%27%20fill%3D%27%23d2d2d2%27%20mask%3D%27url%28%23b%29%27%2F%3E%3C%2Fsvg%3E'
+	removeRow: 'data:image/svg+xml,%3Csvg%20fill%3D%27none%27%20stroke-linejoin%3D%27round%27%20stroke-linecap%3D%27round%27%20stroke-width%3D%272%27%20viewBox%3D%270%200%2024%2024%27%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%3E%3Cdefs%3E%3Cmask%20id%3D%27a%27%3E%3Crect%20width%3D%27100%25%27%20height%3D%27100%25%27%20fill%3D%27%23fff%27%2F%3E%3Ccircle%20r%3D%278%27%20cx%3D%2712%27%20cy%3D%2716%27%20fill%3D%27%23000%27%2F%3E%3C%2Fmask%3E%3Cmask%20id%3D%27b%27%3E%3Crect%20width%3D%27100%25%27%20height%3D%27100%25%27%20fill%3D%27%23fff%27%2F%3E%3Cpath%20d%3D%27M9%2016h6%27%20stroke%3D%27%23000%27%2F%3E%3C%2Fmask%3E%3C%2Fdefs%3E%3Cpath%20d%3D%27M1%202v12h22V2Z%27%20stroke%3D%27%23d2d2d2%27%20mask%3D%27url%28%23a%29%27%2F%3E%3Ccircle%20r%3D%276%27%20cx%3D%2712%27%20cy%3D%2716%27%20fill%3D%27%23d2d2d2%27%20mask%3D%27url%28%23b%29%27%2F%3E%3C%2Fsvg%3E',
+  blank: `data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20fill%3D%27%23d2d2d2%27%20viewBox%3D%270%200%208%208%27%3E%3C%2Fsvg%3E`,
 };
 
 export { icons };
\ No newline at end of file
diff --git a/src/main.ts b/src/main.ts
index 0f81265..d41f779 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -9,8 +9,10 @@ const gdqGreen2 = [0, 255, 0];
 let screenshotBase64 = '';
 let connectedToOBS = false;
 let obsConnectionError = '';
-let cropItem: null | (sceneItemRef & crop & { width: number; height: number }) =
-  null;
+let obsUpdateTimeout: NodeJS.Timeout | null = null;
+let cropItem:
+  | null
+  | (sceneItemRef & crop & { width: number; height: number }) = null;
 let cropSide: 'left' | 'right' | 'top' | 'bottom' | null = null;
 let targetCrop: crop | null = null;
 let initialCrop: crop | null = null;
@@ -398,7 +400,14 @@ const reInitEvents = [
 function subscribeToChanges() {
   if (!subscribed)
     for (let i = 0; i < reInitEvents.length; i++) {
-      obs.on(reInitEvents[i], initOBS);
+      obs.on(reInitEvents[i], () => {
+        if (obsUpdateTimeout) {
+          clearTimeout(obsUpdateTimeout);
+        }
+        obsUpdateTimeout = setTimeout(() => {
+          initOBS();
+        }, 200);
+      });
     }
   subscribed = true;
 }
@@ -543,6 +552,7 @@ function populateViewportsFromActiveFeed() {
     .send('GetSceneItemList', { sceneName: selectedFeedsScene })
     .then(async (data) => {
       sceneItemList = data.sceneItems;
+      const viewportFeeds: viewport['assignedFeeds'] = [];
       for (let i = 0; i < data.sceneItems.length; i++) {
         await obs
           .send('GetSceneItemProperties', {
@@ -550,7 +560,7 @@ function populateViewportsFromActiveFeed() {
             item: { id: data.sceneItems[i].itemId },
           })
           .then((data) => {
-            const viewportFeed: viewport['assignedFeeds'][number] = {
+            viewportFeeds.push({
               'scene-name': selectedFeedsScene,
               item: { id: data.itemId, name: data.name },
               type: sceneItemList[i].sourceKind,
@@ -560,61 +570,97 @@ function populateViewportsFromActiveFeed() {
               bottom: data.crop.bottom,
               width: data.sourceWidth,
               height: data.sourceHeight,
-            };
-            let assigned = false;
-            for (let i = 0; i < currentSceneViewports.length; i++) {
+              x: data.position.x,
+              y: data.position.y,
+              boundsWidth: data.bounds.x,
+              boundsHeight: data.bounds.y,
+            });
+          })
+          .catch(obsError);
+      }
+      let i = 0;
+      let productiveLoop = false;
+      while (i < viewportFeeds.length) {
+        let assigned = false;
+        for (let j = 0; j < currentSceneViewports.length; j++) {
+          if (
+            viewportSearchBoxes[j].x == viewportFeeds[i].x &&
+            viewportSearchBoxes[j].y == viewportFeeds[i].y
+          ) {
+            if (
+              viewportSearchBoxes[j].width == viewportFeeds[i].boundsWidth &&
+              viewportSearchBoxes[j].height == viewportFeeds[i].boundsHeight
+            ) {
+              currentSceneViewports[j].assignedFeeds.push(viewportFeeds[i]);
               if (
-                viewportSearchBoxes[i].x == data.position.x &&
-                viewportSearchBoxes[i].y == data.position.y
-              ) {
-                if (
-                  viewportSearchBoxes[i].width == data.bounds.x &&
-                  viewportSearchBoxes[i].height == data.bounds.y
-                ) {
-                  currentSceneViewports[i].assignedFeeds.push(viewportFeed);
-                  if (
-                    currentSceneViewports[i].rows > 1 ||
-                    currentSceneViewports[i].columns > 1
-                  )
-                    viewportSearchBoxes[i] = getViewPortBoundingBoxes(
-                      currentSceneViewports[i]
-                    )[currentSceneViewports[i].assignedFeeds.length];
+                currentSceneViewports[j].rows > 1 ||
+                currentSceneViewports[j].columns > 1
+              )
+                viewportSearchBoxes[j] = getViewPortBoundingBoxes(
+                  currentSceneViewports[j]
+                )[currentSceneViewports[j].assignedFeeds.length];
+              if (!viewportSearchBoxes[j])
+                viewportSearchBoxes[j] = {
+                  x: -1,
+                  y: -1,
+                  width: -1,
+                  height: -1,
+                };
+              productiveLoop = true;
+              assigned = true;
+              viewportFeeds.splice(i, 1);
+              break;
+            } else if (currentSceneViewports[j].assignedFeeds.length == 0) {
+              const possibleWidth: number[] = [NaN]; //array index corresponds to number of columns, which can't be zero
+              for (let k = 1; k <= 4; k++) {
+                possibleWidth.push(
+                  Math.round(currentSceneViewports[j].width / k)
+                );
+              }
+              const columns = possibleWidth.indexOf(
+                viewportFeeds[i].boundsWidth
+              );
+              if (columns > 0) {
+                const possibleHeight: number[] = [NaN]; //array index corresponds to number of rows, which can't be zero
+                for (let k = 1; k <= 4; k++) {
+                  possibleHeight.push(
+                    Math.round(currentSceneViewports[j].height / k)
+                  );
+                }
+                const rows = possibleHeight.indexOf(
+                  viewportFeeds[i].boundsHeight
+                );
+                if (rows > 0) {
+                  currentSceneViewports[j].rows = rows;
+                  currentSceneViewports[j].columns = columns;
+                  currentSceneViewports[j].assignedFeeds.push(viewportFeeds[i]);
+                  viewportSearchBoxes[j] = getViewPortBoundingBoxes(
+                    currentSceneViewports[j]
+                  )[currentSceneViewports[j].assignedFeeds.length];
+                  if (!viewportSearchBoxes[j])
+                    viewportSearchBoxes[j] = {
+                      x: -1,
+                      y: -1,
+                      width: -1,
+                      height: -1,
+                    };
+                  productiveLoop = true;
                   assigned = true;
+                  viewportFeeds.splice(i, 1);
                   break;
-                } else if (currentSceneViewports[i].assignedFeeds.length == 0) {
-                  const possibleWidth: number[] = [NaN];
-                  for (let j = 1; j <= 4; j++) {
-                    possibleWidth.push(
-                      Math.round(currentSceneViewports[i].width / j)
-                    );
-                  }
-                  const columns = possibleWidth.indexOf(data.bounds.x);
-                  if (columns > 0) {
-                    const possibleHeight: number[] = [NaN];
-                    for (let j = 1; j <= 4; j++) {
-                      possibleHeight.push(
-                        Math.round(currentSceneViewports[i].height / j)
-                      );
-                    }
-                    const rows = possibleHeight.indexOf(data.bounds.y);
-                    if (rows > 0) {
-                      currentSceneViewports[i].rows = rows;
-                      currentSceneViewports[i].columns = columns;
-                      currentSceneViewports[i].assignedFeeds.push(viewportFeed);
-                      viewportSearchBoxes[i] = getViewPortBoundingBoxes(
-                        currentSceneViewports[i]
-                      )[currentSceneViewports[i].assignedFeeds.length];
-                      assigned = true;
-                      break;
-                    }
-                  }
                 }
               }
             }
-            if (!assigned) unassignedFeeds.push(viewportFeed);
-          })
-          .catch(obsError);
+          }
+        }
+        //if (!assigned) unassignedFeeds.push(viewportFeeds[i]);
+        if (!assigned) i++;
+        if (i == viewportFeeds.length && productiveLoop == true) {
+          i = 0;
+          productiveLoop = false;
+        }
       }
+      unassignedFeeds = viewportFeeds;
     })
     .catch(obsError);
 }
@@ -759,7 +805,8 @@ async function refreshViewportsDiv() {
             if (
               currentSceneViewports[i] &&
               currentSceneViewports[i].rows >
-                currentSceneViewports[i].assignedFeeds.length
+                currentSceneViewports[i].assignedFeeds.length &&
+              currentSceneViewports[i].assignedFeeds.length > 0
             )
               currentSceneViewports[i].rows =
                 currentSceneViewports[i].assignedFeeds.length;
@@ -781,6 +828,26 @@ async function refreshViewportsDiv() {
         refreshCropDiv();
       };
       sourceDiv.appendChild(cropIcon);
+      const upIcon = document.createElement('img');
+      upIcon.classList.add('icon');
+      upIcon.style.float = 'right';
+      if (j > 0) {
+        upIcon.src = icons.up;
+        upIcon.onclick = () => {
+          swapFeeds(viewportFeeds, j, j - 1);
+        };
+      } else upIcon.src = icons.blank;
+      sourceDiv.appendChild(upIcon);
+      if (j < viewportFeeds.length - 1) {
+        const downIcon = document.createElement('img');
+        downIcon.classList.add('icon');
+        downIcon.style.float = 'right';
+        downIcon.src = icons.down;
+        downIcon.onclick = () => {
+          swapFeeds(viewportFeeds, j, j + 1);
+        };
+        sourceDiv.appendChild(downIcon);
+      }
       viewportSourcesDiv.appendChild(sourceDiv);
     }
     listDiv.appendChild(viewportSourcesDiv);
@@ -788,6 +855,61 @@ async function refreshViewportsDiv() {
   refreshFooter();
 }
 
+function swapFeeds(
+  viewportFeeds: typeof unassignedFeeds,
+  index1: number,
+  index2: number
+) {
+  const feed1 = viewportFeeds[index1];
+  const feed2 = viewportFeeds[index2];
+  const swapX = feed2.x;
+  const swapY = feed2.y;
+  const swapWidth = feed2.boundsWidth;
+  const swapHeight = feed2.boundsHeight;
+  feed2.x = feed1.x;
+  feed2.y = feed1.y;
+  feed2.boundsWidth = feed1.boundsWidth;
+  feed2.boundsHeight = feed1.boundsHeight;
+  feed1.x = swapX;
+  feed1.y = swapY;
+  feed1.boundsWidth = swapWidth;
+  feed1.boundsHeight = swapHeight;
+  unsubscribeToChanges();
+  obs
+    .send('SetSceneItemProperties', {
+      'scene-name': selectedFeedsScene,
+      item: feed1.item,
+      position: { x: feed1.x, y: feed1.y },
+      scale: {},
+      crop: {},
+      bounds: {
+        x: feed1.boundsWidth,
+        y: feed1.boundsHeight,
+      },
+    })
+    .then(() => {
+      return obs.send('SetSceneItemProperties', {
+        'scene-name': selectedFeedsScene,
+        item: feed2.item,
+        position: { x: feed2.x, y: feed2.y },
+        scale: {},
+        crop: {},
+        bounds: {
+          x: feed2.boundsWidth,
+          y: feed2.boundsHeight,
+        },
+      });
+    })
+    .then(() => {
+      const swap = viewportFeeds[index2];
+      viewportFeeds[index2] = viewportFeeds[index1];
+      viewportFeeds[index1] = swap;
+      refreshViewportsDiv();
+      subscribeToChanges();
+    })
+    .catch(console.error);
+}
+
 async function removeUnassignedSources() {
   unsubscribeToChanges();
   for (let i = 0; i < unassignedFeeds.length; i++) {
@@ -840,6 +962,12 @@ async function arrangeViewportFeeds(viewport: viewport) {
           y: boxes[i].height,
         },
       })
+      .then(() => {
+        feed.x = boxes[i].x;
+        feed.y = boxes[i].y;
+        feed.boundsWidth = boxes[i].width;
+        feed.boundsHeight = boxes[i].height;
+      })
       .catch(console.error);
   }
   subscribeToChanges();
@@ -1167,6 +1295,10 @@ async function addSourceToViewport(
         bottom: 0,
         width: source.source_cx,
         height: source.source_cy,
+        x: viewport.x,
+        y: viewport.y,
+        boundsWidth: viewport.width,
+        boundsHeight: viewport.height,
       };
       return obs.send('SetSceneItemProperties', {
         'scene-name': scene,
diff --git a/types/index.d.ts b/types/index.d.ts
index 5896991..7eb9700 100644
--- a/types/index.d.ts
+++ b/types/index.d.ts
@@ -28,7 +28,7 @@ type viewport = {
   height: number;
 	rows: number;
 	columns: number;
-  assignedFeeds: (sceneItemRef & crop & { type: string, width: number, height: number })[];
+  assignedFeeds: (sceneItemRef & crop & { type: string, width: number, height: number, x: number, y: number, boundsWidth: number, boundsHeight: number })[];
 };
 type obsSceneItems = {
   itemId: number;