diff --git a/__tests__/integration/mirador/index.html b/__tests__/integration/mirador/index.html
index c6027129a15b8cc10c50c3aa243521f00d49b318..2bd6b0ed85350f9f19378b220e719f8fed308d01 100644
--- a/__tests__/integration/mirador/index.html
+++ b/__tests__/integration/mirador/index.html
@@ -12,6 +12,9 @@
     <script type="text/javascript">
      var miradorInstance = Mirador.viewer({
        id: 'mirador',
+       workspace: {
+         type: 'elastic'
+       },
        windows: [{
          loadedManifest: 'https://iiif.harvardartmuseums.org/manifests/object/299843',
          canvasIndex: 2,
diff --git a/__tests__/src/reducers/workspace.test.js b/__tests__/src/reducers/workspace.test.js
index 9873e434d0160319f421233d64e25bcfc374382f..f4f863bc428e2b5f23c54bc01498b404edf08483 100644
--- a/__tests__/src/reducers/workspace.test.js
+++ b/__tests__/src/reducers/workspace.test.js
@@ -46,13 +46,11 @@ describe('workspace reducer', () => {
     expect(workspaceReducer([], {
       type: ActionTypes.SET_WORKSPACE_VIEWPORT_POSITION,
       payload: {
-        position: {
-          x: 50,
-          y: 50,
-        },
+        x: 50,
+        y: 50,
       },
     })).toEqual({
-      viewportPosition: {
+      viewport: {
         x: 50,
         y: 50,
       },
diff --git a/package.json b/package.json
index 96013af403b7eb39db645b75c96c5aceb2a7fe68..29c6b41fce690c2c3abc7f3b25d0579bf0972602 100644
--- a/package.json
+++ b/package.json
@@ -55,6 +55,7 @@
     "react-mosaic-component": "^2.1.0",
     "react-placeholder": "^3.0.1",
     "react-redux": "^6.0.0",
+    "react-resize-observer": "1.1.1",
     "react-rnd": "^9.1.1",
     "react-virtualized": "^9.21.0",
     "redux": "4.0.1",
diff --git a/src/components/ElasticMinimap.js b/src/components/ElasticMinimap.js
new file mode 100644
index 0000000000000000000000000000000000000000..f1eb376f248c06ca6c1eeb26503ab085b91d638f
--- /dev/null
+++ b/src/components/ElasticMinimap.js
@@ -0,0 +1,158 @@
+import React, { Component } from 'react';
+import PropTypes from 'prop-types';
+import { Rnd } from 'react-rnd';
+import ns from '../config/css-ns';
+import { getWorkspaceBoundingBox } from '../state/selectors';
+
+const minimapContainer = {
+  width: 200,
+  height: 200,
+  innerPadding: 10,
+};
+
+// There is a minimum bounding box based on the viewport dimensions,
+// and the bounding box is always square
+
+/**
+ * ElasticMinimap
+ */
+const scaledViewport = (workspaceViewport, boundingBox) => {
+  const viewportAspectRatio = workspaceViewport.width / workspaceViewport.height;
+  const viewportScaleFactor = workspaceViewport.width / boundingBox.width;
+
+  const scaledViewportWidth = viewportScaleFactor * minimapContainer.width;
+  const scaledViewportHeight = scaledViewportWidth / viewportAspectRatio;
+  const scaledViewportX = (-workspaceViewport.x / boundingBox.width) * minimapContainer.width;
+  const scaledViewportY = (-workspaceViewport.y / boundingBox.height) * minimapContainer.height;
+
+  return {
+    x: scaledViewportX,
+    y: scaledViewportY,
+    width: scaledViewportWidth,
+    height: scaledViewportHeight,
+  };
+};
+
+/**
+ * ElasticMinimap
+ */
+const minimapToWorkspaceCoordinates = (x, y, boundingBox) => {
+  const newX = -x * boundingBox.width / minimapContainer.width;
+  const newY = -y * boundingBox.height / minimapContainer.height;
+  return {
+    x: newX,
+    y: newY,
+  };
+};
+
+/**
+ * ElasticMinimap
+ */
+const windowStyle = (window, boundingBox) => {
+  const windowAspectRatio = window.width / window.height;
+  const windowScaleFactor = window.width / boundingBox.width;
+
+  const scaledWindowWidth = windowScaleFactor * 100;
+  const scaledWindowX = (window.x / boundingBox.width) * 100;
+  const scaledWindowY = (window.y / boundingBox.height) * 100;
+  const scaledWindowHeight = scaledWindowWidth / windowAspectRatio;
+
+  return {
+    top: `${scaledWindowY}%`,
+    left: `${scaledWindowX}%`,
+    height: `${scaledWindowHeight}%`,
+    width: `${scaledWindowWidth}%`,
+  };
+};
+
+/**
+ * ElasticMinimap
+ */
+const boundingBoxStyle = (windows, boundingBox) => {
+  const windowBoundingBox = getWorkspaceBoundingBox(windows);
+  const windowBBAspectRatio = windowBoundingBox.width / windowBoundingBox.height;
+  const windowBBScaleFactor = windowBoundingBox.width / boundingBox.width;
+
+  const scaledWindowBBWidth = windowBBScaleFactor * 100;
+  const scaledWindowBBX = (windowBoundingBox.x / boundingBox.width) * 100;
+  const scaledWindowBBY = (windowBoundingBox.y / boundingBox.height) * 100;
+  const scaledWindowBBHeight = scaledWindowBBWidth / windowBBAspectRatio;
+
+  return {
+    top: `${scaledWindowBBY}%`,
+    left: `${scaledWindowBBX}%`,
+    height: `${scaledWindowBBHeight}%`,
+    width: `${scaledWindowBBWidth}%`,
+  };
+};
+
+/**
+ * ElasticMinimap
+ */
+export class ElasticMinimap extends Component {
+  /**
+   * render
+   * @return
+   */
+  render() {
+    const boundingBox = {
+      width: 5000,
+      height: 5000,
+    };
+
+    const {
+      windows,
+      workspaceViewport,
+      setWorkspaceViewportPosition,
+    } = this.props;
+
+    const viewport = scaledViewport(
+      workspaceViewport,
+      boundingBox,
+    );
+
+    return (
+      <div className={ns('elastic-minimap')}>
+        {
+          Object.values(windows).map(window => (
+            <div
+              key={window.id}
+              className="minimap-window"
+              style={windowStyle(window, boundingBox)}
+            />
+          ))
+        }
+        <div
+          className="window-bounding-box"
+          style={boundingBoxStyle(windows, boundingBox)}
+        />
+        <Rnd
+          position={{ x: viewport.x, y: viewport.y }}
+          size={{ width: viewport.width, height: viewport.height }}
+          enableResizing={{
+            top: false,
+            right: false,
+            bottom: false,
+            left: false,
+            topRight: false,
+            bottomRight: false,
+            bottomLeft: false,
+            topLeft: false,
+          }}
+          onDragStart={(e, d) => null}
+          onDrag={(e, d) => {
+            const newPosition = minimapToWorkspaceCoordinates(d.x, d.y, boundingBox);
+            setWorkspaceViewportPosition(newPosition.x, newPosition.y);
+          }}
+          className="minimap-viewport"
+        />
+      </div>
+    );
+  }
+}
+
+ElasticMinimap.propTypes = {
+  windows: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
+  setWorkspaceViewportPosition: PropTypes.func.isRequired,
+  workspaceViewport: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
+};
diff --git a/src/components/Workspace.js b/src/components/Workspace.js
index 5f975288249e5ed8544acaaa110d6ed76b127277..d716d67523ffc015e1ff41050fb8abcb772cb3de 100644
--- a/src/components/Workspace.js
+++ b/src/components/Workspace.js
@@ -2,6 +2,7 @@ import React from 'react';
 import PropTypes from 'prop-types';
 import classNames from 'classnames';
 import Typography from '@material-ui/core/Typography';
+import ResizeObserver from 'react-resize-observer';
 import Window from '../containers/Window';
 import WorkspaceMosaic from '../containers/WorkspaceMosaic';
 import WorkspaceElastic from '../containers/WorkspaceElastic';
@@ -61,7 +62,7 @@ export class Workspace extends React.Component {
    * render
    */
   render() {
-    const { isWorkspaceControlPanelVisible, t } = this.props;
+    const { isWorkspaceControlPanelVisible, setWorkspaceViewportDimensions, t } = this.props;
 
     return (
       <div
@@ -74,6 +75,12 @@ export class Workspace extends React.Component {
       >
         <Typography variant="srOnly" component="h1">{t('miradorViewer')}</Typography>
         {this.workspaceByType()}
+
+        <ResizeObserver
+          onResize={(rect) => {
+            setWorkspaceViewportDimensions(rect.width, rect.height);
+          }}
+        />
       </div>
     );
   }
@@ -81,6 +88,7 @@ export class Workspace extends React.Component {
 
 Workspace.propTypes = {
   isWorkspaceControlPanelVisible: PropTypes.bool.isRequired,
+  setWorkspaceViewportDimensions: PropTypes.func.isRequired,
   windows: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
   workspaceType: PropTypes.string.isRequired, // eslint-disable-line react/forbid-prop-types
   t: PropTypes.func.isRequired,
diff --git a/src/components/WorkspaceElastic.js b/src/components/WorkspaceElastic.js
index 65de11c19693ac3567cfcba6aaed850c3d346357..07312ce37d4f0c7e58a1912c4a9b41184f7dc81f 100644
--- a/src/components/WorkspaceElastic.js
+++ b/src/components/WorkspaceElastic.js
@@ -2,6 +2,7 @@ import React from 'react';
 import PropTypes from 'prop-types';
 import { Rnd } from 'react-rnd';
 import Window from '../containers/Window';
+import ElasticMinimap from '../containers/ElasticMinimap';
 import ns from '../config/css-ns';
 
 /**
@@ -21,54 +22,57 @@ class WorkspaceElastic extends React.Component {
       setWindowSize,
     } = this.props;
     return (
-      <Rnd
-        default={{
-          width: 5000,
-          height: 5000,
-        }}
-        position={{ x: workspace.viewportPosition.x, y: workspace.viewportPosition.y }}
-        enableResizing={{
-          top: false,
-          right: false,
-          bottom: false,
-          left: false,
-          topRight: false,
-          bottomRight: false,
-          bottomLeft: false,
-          topLeft: false,
-        }}
-        onDragStop={(e, d) => {
-          setWorkspaceViewportPosition({ x: d.x, y: d.y });
-        }}
-        cancel={`.${ns('window')}`}
-        className={ns('workspace')}
-      >
-        {
-          Object.values(windows).map(window => (
-            <Rnd
-              key={window.id}
-              size={{ width: window.width, height: window.height }}
-              position={{ x: window.x, y: window.y }}
-              bounds="parent"
-              onDragStop={(e, d) => {
-                updateWindowPosition(window.id, { x: d.x, y: d.y });
-              }}
-              onResize={(e, direction, ref, delta, position) => {
-                setWindowSize(window.id, {
-                  width: ref.style.width,
-                  height: ref.style.height,
-                  ...position,
-                });
-              }}
-              dragHandleClassName={ns('window-top-bar')}
-            >
-              <Window
-                window={window}
-              />
-            </Rnd>
-          ))
-        }
-      </Rnd>
+      <>
+        <Rnd
+          default={{
+            width: 5000,
+            height: 5000,
+          }}
+          position={{ x: workspace.viewport.x, y: workspace.viewport.y }}
+          enableResizing={{
+            top: false,
+            right: false,
+            bottom: false,
+            left: false,
+            topRight: false,
+            bottomRight: false,
+            bottomLeft: false,
+            topLeft: false,
+          }}
+          onDrag={(e, d) => {
+            setWorkspaceViewportPosition(d.x, d.y);
+          }}
+          cancel={`.${ns('window')}`}
+          className={ns('workspace')}
+        >
+          {
+            Object.values(windows).map(window => (
+              <Rnd
+                key={window.id}
+                size={{ width: window.width, height: window.height }}
+                position={{ x: window.x, y: window.y }}
+                bounds="parent"
+                onDrag={(e, d) => {
+                  updateWindowPosition(window.id, { x: d.x, y: d.y });
+                }}
+                onResize={(e, direction, ref, delta, position) => {
+                  setWindowSize(window.id, {
+                    width: parseInt(ref.style.width, 10),
+                    height: parseInt(ref.style.height, 10),
+                    ...position,
+                  });
+                }}
+                dragHandleClassName={ns('window-top-bar')}
+              >
+                <Window
+                  window={window}
+                />
+              </Rnd>
+            ))
+          }
+        </Rnd>
+        <ElasticMinimap />
+      </>
     );
   }
 }
diff --git a/src/containers/ElasticMinimap.js b/src/containers/ElasticMinimap.js
new file mode 100644
index 0000000000000000000000000000000000000000..e3fc6efacf4d299d12d26f1114f85ab5463caf14
--- /dev/null
+++ b/src/containers/ElasticMinimap.js
@@ -0,0 +1,39 @@
+import { compose } from 'redux';
+import { connect } from 'react-redux';
+import { withTranslation } from 'react-i18next';
+import miradorWithPlugins from '../lib/miradorWithPlugins';
+import * as actions from '../state/actions';
+import { ElasticMinimap } from '../components/ElasticMinimap';
+
+/**
+ * mapDispatchToProps - used to hook up connect to action creators
+ * @memberof ManifestListItem
+ * @private
+ */
+const mapDispatchToProps = (dispatch, props) => ({
+  setWorkspaceViewportPosition: (x, y) => {
+    dispatch(
+      actions.setWorkspaceViewportPosition(x, y),
+    );
+  },
+});
+
+/**
+ * mapStateToProps - to hook up connect
+ * @memberof WindowViewer
+ * @private
+ */
+const mapStateToProps = state => ({
+  // viewport: state.workspace.viewport,
+  // workspaceBoundingBox: state.workspace.viewport,
+  workspaceViewport: state.workspace.viewport,
+  windows: state.windows,
+});
+
+const enhance = compose(
+  withTranslation(),
+  connect(mapStateToProps, mapDispatchToProps),
+  miradorWithPlugins,
+);
+
+export default enhance(ElasticMinimap);
diff --git a/src/containers/Workspace.js b/src/containers/Workspace.js
index 235dec8d17602fffbb963843d7085eccf8a95342..f06f6b80fc2c1c34a566dcec029ac4f4c80951c1 100644
--- a/src/containers/Workspace.js
+++ b/src/containers/Workspace.js
@@ -1,8 +1,23 @@
 import { compose } from 'redux';
 import { connect } from 'react-redux';
 import { withTranslation } from 'react-i18next';
+import * as actions from '../state/actions';
 import { Workspace } from '../components/Workspace';
 
+
+/**
+ * mapDispatchToProps - used to hook up connect to action creators
+ * @memberof ManifestListItem
+ * @private
+ */
+const mapDispatchToProps = (dispatch, props) => ({
+  setWorkspaceViewportDimensions: (width, height) => {
+    dispatch(
+      actions.setWorkspaceViewportDimensions(width, height),
+    );
+  },
+});
+
 /**
  * mapStateToProps - to hook up connect
  * @memberof Workspace
@@ -18,7 +33,7 @@ const mapStateToProps = state => (
 
 const enhance = compose(
   withTranslation(),
-  connect(mapStateToProps),
+  connect(mapStateToProps, mapDispatchToProps),
   // further HOC go here
 );
 
diff --git a/src/containers/WorkspaceElastic.js b/src/containers/WorkspaceElastic.js
index 8c0b8d714bb1d69c700178fd79aefac3612b1288..8eecedf343fff4d010a55eccfa219ce5c00544e0 100644
--- a/src/containers/WorkspaceElastic.js
+++ b/src/containers/WorkspaceElastic.js
@@ -22,9 +22,9 @@ const mapStateToProps = state => (
  * @private
  */
 const mapDispatchToProps = (dispatch, props) => ({
-  setWorkspaceViewportPosition: (position) => {
+  setWorkspaceViewportPosition: (x, y) => {
     dispatch(
-      actions.setWorkspaceViewportPosition(position),
+      actions.setWorkspaceViewportPosition(x, y),
     );
   },
   toggleWorkspaceExposeMode: size => dispatch(
diff --git a/src/state/actions/action-types.js b/src/state/actions/action-types.js
index 43b6433b04e90c32967155429c7795dd2fa28f46..2a0fcce0fe0676c6cbad7770759c9b1266fe9368 100644
--- a/src/state/actions/action-types.js
+++ b/src/state/actions/action-types.js
@@ -10,6 +10,7 @@ const ActionTypes = {
 
   FOCUS_WINDOW: 'FOCUS_WINDOW',
   SET_WORKSPACE_FULLSCREEN: 'SET_WORKSPACE_FULLSCREEN',
+  SET_WORKSPACE_VIEWPORT_DIMENSIONS: 'SET_WORKSPACE_VIEWPORT_DIMENSIONS',
   SET_WORKSPACE_VIEWPORT_POSITION: 'SET_WORKSPACE_VIEWPORT_POSITION',
   TOGGLE_WORKSPACE_EXPOSE_MODE: 'TOGGLE_WORKSPACE_EXPOSE_MODE',
   ADD_MANIFEST: 'ADD_MANIFEST',
diff --git a/src/state/actions/workspace.js b/src/state/actions/workspace.js
index 6da0e4898d127da4000cf334164bc1f670a71f1d..d09cf4938563c87efa24444dcf472a20e71869e4 100644
--- a/src/state/actions/workspace.js
+++ b/src/state/actions/workspace.js
@@ -45,14 +45,29 @@ export function setWorkspaceAddVisibility(isWorkspaceAddVisible) {
  * @param  {Object} position
  * @memberof ActionCreators
  */
-export function setWorkspaceViewportPosition(position) {
+export function setWorkspaceViewportPosition(x, y) {
+  console.log('x:', x, 'y', y);
   return {
     type: ActionTypes.SET_WORKSPACE_VIEWPORT_POSITION,
     payload: {
-      position: {
-        x: position.x,
-        y: position.y,
-      },
+      x,
+      y,
+    },
+  };
+}
+
+/**
+ * setWorkspaceViewportPosition - action creator
+ *
+ * @param  {Object} position
+ * @memberof ActionCreators
+ */
+export function setWorkspaceViewportDimensions(width, height) {
+  return {
+    type: ActionTypes.SET_WORKSPACE_VIEWPORT_DIMENSIONS,
+    payload: {
+      width,
+      height,
     },
   };
 }
diff --git a/src/state/reducers/workspace.js b/src/state/reducers/workspace.js
index 6abaeb93abc2395bb2cf99343aa8cdaf5b67a3c2..86c12855351b8aa807aaea1cfb494495c6f31984 100644
--- a/src/state/reducers/workspace.js
+++ b/src/state/reducers/workspace.js
@@ -5,9 +5,11 @@ import ActionTypes from '../actions/action-types';
  */
 export const workspaceReducer = (
   state = { // we'll need to abstract this more, methinks.
-    viewportPosition: {
+    viewport: {
       x: -2500,
       y: -2500,
+      width: 800,
+      height: 600,
     },
     exposeModeOn: false,
   },
@@ -25,7 +27,23 @@ export const workspaceReducer = (
     case ActionTypes.SET_WORKSPACE_ADD_VISIBILITY:
       return { ...state, isWorkspaceAddVisible: action.isWorkspaceAddVisible };
     case ActionTypes.SET_WORKSPACE_VIEWPORT_POSITION:
-      return { ...state, viewportPosition: action.payload.position };
+      return {
+        ...state,
+        viewport: {
+          ...state.viewport,
+          x: action.payload.x,
+          y: action.payload.y,
+        },
+      };
+    case ActionTypes.SET_WORKSPACE_VIEWPORT_DIMENSIONS:
+      return {
+        ...state,
+        viewport: {
+          ...state.viewport,
+          width: action.payload.width,
+          height: action.payload.height,
+        },
+      };
     case ActionTypes.TOGGLE_WORKSPACE_EXPOSE_MODE:
       return { ...state, exposeModeOn: !state.exposeModeOn };
     default:
diff --git a/src/state/selectors/index.js b/src/state/selectors/index.js
index f2940ef1181b82bf26602e6f08275a257bd05432..dccefea72df2eaaa4e2a625c1c54982f17415120 100644
--- a/src/state/selectors/index.js
+++ b/src/state/selectors/index.js
@@ -272,3 +272,45 @@ export function getLanguagesFromConfigWithCurrent(state) {
     current: key === language,
   }));
 }
+
+/**
+ * Return the bounding box for all open windows in the elastic workspace
+ * in workspace coordinates
+ * @param {object} state
+ * @return {object}
+ */
+export function getWorkspaceBoundingBox(windows) {
+  const windowObjects = Object.values(windows);
+  const minX = Math.min(...windowObjects.map(win => win.x));
+  const minY = Math.min(...windowObjects.map(win => win.y));
+  const maxX = Math.max(...windowObjects.map((win) => {
+    console.log('win width: ', win.width);
+    console.log(win.x + win.width);
+    return (win.x + win.width);
+  }));
+  const maxY = Math.max(...windowObjects.map(win => (win.y + win.height)));
+  console.log('min X: ', minX);
+  console.log('min Y: ', minY);
+  console.log('max X: ', maxX);
+  console.log('max Y: ', maxY);
+  return {
+    x: minX,
+    y: minY,
+    width: (maxX - minX),
+    height: maxY - minY,
+  };
+
+  // Potential solution to overflow problems.
+  // const minX = Math.min(...windowObjects.map(
+  //   win => Number.parseFloat(win.x).toPrecision(4),
+  // ));
+  // const minY = Math.min(...windowObjects.map(
+  //   win => Number.parseFloat(win.y).toPrecision(4),
+  // ));
+  // const maxX = Math.max(...windowObjects.map(
+  //   win => Number.parseFloat((win.x + win.width)).toPrecision(4),
+  // ));
+  // const maxY = Math.max(...windowObjects.map(
+  //   win => Number.parseFloat((win.y + win.height)).toPrecision(4),
+  // ));
+}
diff --git a/src/styles/index.scss b/src/styles/index.scss
index 62687b6c8f64bc93f6c57a4a976e382c41489e80..0a568005dbc10a9ec2953c6181d65725bc4320bc 100644
--- a/src/styles/index.scss
+++ b/src/styles/index.scss
@@ -43,7 +43,7 @@
   }
 
   &-workspace-with-control-panel {
-    padding-top: 74px; // The height of the control panel
+    margin-top: 74px; // The height of the control panel
   }
 
   &-workspace-maximized-window {
@@ -56,20 +56,57 @@
   &-workspace-add {
     height: 100%;
     overflow: auto;
-    padding-left: 6px;
-    padding-right: 6px;
-    padding-top: 92px;
+    margin-left: 6px;
+    margin-right: 6px;
+    margin-top: 92px;
+  }
+
+  &-elastic-minimap {
+    opacity: 0.8;
+    position: absolute;
+    bottom: 0;
+    right: 0;
+    margin: 20px;
+    width: 200px;
+    height: 200px;
+    border: 2px solid lightgray;
+    background: white;
+    box-sizing: border-box;
+
+    .minimap-window {
+      position: absolute;
+      background: rgba(deepskyblue, .3);
+      border: 1px solid lightgray;
+      box-sizing: border-box;
+    }
+
+    .minimap-viewport {
+      position: absolute;
+      border: 1px solid orangered;
+      box-sizing: border-box;
+      transition: 0.2s box-shadow ease-out;
+      &:hover {
+        transition: 0.2s box-shadow ease-out;
+        box-shadow: 0 0 3px gray;
+      }
+    }
+
+    .window-bounding-box {
+      box-sizing: border-box;
+      position: absolute;
+      border: 1px dotted deeppink;
+    }
   }
 
   @media (min-width: 600px) {
     &-workspace-with-control-panel {
-      padding-left: 100px;
-      padding-top: 0;
+      margin-left: 100px;
+      margin-top: 0;
     }
 
     &-workspace-add {
-      padding-left: 100px;
-      padding-top: 18px;
+      margin-left: 100px;
+      margin-top: 18px;
     }
   }