diff --git a/__tests__/integration/mirador/companion_windows.test.js b/__tests__/integration/mirador/companion_windows.test.js
index f17e1d6261ee598090e14dc001b74196db7eff47..19eb7b1679f53ed15d29e1191f7f6d1f695b666f 100644
--- a/__tests__/integration/mirador/companion_windows.test.js
+++ b/__tests__/integration/mirador/companion_windows.test.js
@@ -8,7 +8,8 @@ describe('Companion Windows', () => {
     await expect(page).toClick('button[aria-label="Toggle window sidebar"]');
 
     await page.waitFor(1000);
-    await expect(page).toClick('button[aria-label="Open information companion window"]');
+    await expect(page).toMatchElement('.mirador-companion-window-left.mirador-window-sidebar-info-panel');
+    await expect(page).toMatchElement('button[aria-label="Open information companion window"][aria-selected="true"]');
 
     await expect(page).not.toMatchElement('.mirador-companion-window-right.mirador-window-sidebar-info-panel');
 
diff --git a/__tests__/src/actions/canvas.test.js b/__tests__/src/actions/canvas.test.js
index da7344e256c27d6d604272c0fe27493df9fed5e6..571c3c6b40ee5c24dfd6f3010c7d41abc91a9c1c 100644
--- a/__tests__/src/actions/canvas.test.js
+++ b/__tests__/src/actions/canvas.test.js
@@ -1,7 +1,7 @@
 import * as actions from '../../../src/state/actions';
 import ActionTypes from '../../../src/state/actions/action-types';
 
-const debounceTime = 300;
+const debounceTime = 100;
 
 describe('canvas actions', () => {
   describe('setCanvas', () => {
diff --git a/__tests__/src/actions/window.test.js b/__tests__/src/actions/window.test.js
index ba983c88c09d7677974b24a3b83fe2e69a6bf527..c144d7d2d94b9bdef18563b8e30ba7ccc04fd26c 100644
--- a/__tests__/src/actions/window.test.js
+++ b/__tests__/src/actions/window.test.js
@@ -15,20 +15,24 @@ describe('window actions', () => {
           id: 'helloworld',
           canvasIndex: 1,
           collectionIndex: 0,
-          companionWindowIds: [],
           manifestId: null,
           maximized: false,
           rangeId: null,
           thumbnailNavigationPosition: 'bottom',
           x: 2700,
           y: 2700,
+          sideBarPanel: 'info',
           width: 400,
           height: 400,
           rotation: null,
           view: 'single',
         },
+        companionWindows: [{ position: 'left', content: 'info' }],
       };
-      expect(actions.addWindow(options)).toEqual(expectedAction);
+      const action = actions.addWindow(options);
+      expect(action).toMatchObject(expectedAction);
+      expect(action.window.companionWindowIds.length).toEqual(1);
+      expect(action.window.companionWindowIds[0]).toEqual(action.companionWindows[0].id);
     });
   });
 
diff --git a/__tests__/src/components/OpenSeadragonViewer.test.js b/__tests__/src/components/OpenSeadragonViewer.test.js
index 6368e2e050d1ea528cf051d5649d4c9359955943..8113ca8e9bfbda73b9526d85e757ff14710bd62f 100644
--- a/__tests__/src/components/OpenSeadragonViewer.test.js
+++ b/__tests__/src/components/OpenSeadragonViewer.test.js
@@ -2,15 +2,20 @@ import React from 'react';
 import { shallow } from 'enzyme';
 import OpenSeadragon from 'openseadragon';
 import { OpenSeadragonViewer } from '../../../src/components/OpenSeadragonViewer';
+import OpenSeadragonCanvasOverlay from '../../../src/lib/OpenSeadragonCanvasOverlay';
+import Annotation from '../../../src/lib/Annotation';
 import ZoomControls from '../../../src/containers/ZoomControls';
 
 jest.mock('openseadragon');
+jest.mock('../../../src/lib/OpenSeadragonCanvasOverlay');
+
 
 describe('OpenSeadragonViewer', () => {
   let wrapper;
   let updateViewport;
   beforeEach(() => {
     OpenSeadragon.mockClear();
+    OpenSeadragonCanvasOverlay.mockClear();
 
     updateViewport = jest.fn();
 
@@ -126,6 +131,16 @@ describe('OpenSeadragonViewer', () => {
         0.5, { x: 1, y: 0, zoom: 0.5 }, false,
       );
     });
+
+    it('sets up a OpenSeadragonCanvasOverlay', () => {
+      wrapper.instance().componentDidMount();
+      expect(OpenSeadragonCanvasOverlay).toHaveBeenCalledTimes(1);
+    });
+
+    it('sets up a listener on update-viewport', () => {
+      wrapper.instance().componentDidMount();
+      expect(addHandler).toHaveBeenCalledWith('update-viewport', expect.anything());
+    });
   });
 
   describe('componentDidUpdate', () => {
@@ -153,6 +168,49 @@ describe('OpenSeadragonViewer', () => {
         0.5, { x: 1, y: 0, zoom: 0.5 }, false,
       );
     });
+
+    it('sets up canvasUpdate to add annotations to the canvas', () => {
+      const clear = jest.fn();
+      const resize = jest.fn();
+      const canvasUpdate = jest.fn();
+      wrapper.instance().osdCanvasOverlay = {
+        clear,
+        resize,
+        canvasUpdate,
+      };
+
+      wrapper.setProps(
+        {
+          annotations: [
+            new Annotation(
+              { '@id': 'foo', resources: [{ foo: 'bar' }] },
+            ),
+          ],
+        },
+      );
+      wrapper.setProps(
+        {
+          annotations: [
+            new Annotation(
+              { '@id': 'foo', resources: [{ foo: 'bar' }] },
+            ),
+          ],
+        },
+      );
+      wrapper.setProps(
+        {
+          annotations: [
+            new Annotation(
+              { '@id': 'bar', resources: [{ foo: 'bar' }] },
+            ),
+          ],
+        },
+      );
+      wrapper.instance().updateCanvas();
+      expect(clear).toHaveBeenCalledTimes(1);
+      expect(resize).toHaveBeenCalledTimes(1);
+      expect(canvasUpdate).toHaveBeenCalledTimes(1);
+    });
   });
 
   describe('onViewportChange', () => {
@@ -173,4 +231,35 @@ describe('OpenSeadragonViewer', () => {
       );
     });
   });
+
+  describe('onUpdateViewport', () => {
+    it('fires updateCanvas', () => {
+      const updateCanvas = jest.fn();
+      wrapper.instance().updateCanvas = updateCanvas;
+      wrapper.instance().onUpdateViewport();
+      expect(updateCanvas).toHaveBeenCalledTimes(1);
+    });
+  });
+
+  describe('annotationsToContext', () => {
+    it('converts the annotations to canvas', () => {
+      const strokeRect = jest.fn();
+      wrapper.instance().osdCanvasOverlay = {
+        context2d: {
+          strokeRect,
+        },
+      };
+
+      const annotations = [
+        new Annotation(
+          { '@id': 'foo', resources: [{ on: 'www.example.com/#xywh=10,10,100,200' }] },
+        ),
+      ];
+      wrapper.instance().annotationsToContext(annotations);
+      const context = wrapper.instance().osdCanvasOverlay.context2d;
+      expect(context.strokeStyle).toEqual('yellow');
+      expect(context.lineWidth).toEqual(10);
+      expect(strokeRect).toHaveBeenCalledWith(10, 10, 100, 200);
+    });
+  });
 });
diff --git a/__tests__/src/lib/AnnotationResource.test.js b/__tests__/src/lib/AnnotationResource.test.js
index fcf2eb5b5cc091169dce49e1659ab0c1156631ab..64d7777efb21228a8aa5593c01fc4a7b059e846d 100644
--- a/__tests__/src/lib/AnnotationResource.test.js
+++ b/__tests__/src/lib/AnnotationResource.test.js
@@ -53,11 +53,11 @@ describe('AnnotationResource', () => {
   describe('fragmentSelector', () => {
     it('simple string', () => {
       expect(new AnnotationResource({ on: 'www.example.com/#xywh=10,10,100,200' })
-        .fragmentSelector).toEqual(['10', '10', '100', '200']);
+        .fragmentSelector).toEqual([10, 10, 100, 200]);
     });
     it('more complex selector', () => {
       expect(new AnnotationResource({ on: { selector: { value: 'www.example.com/#xywh=10,10,100,200' } } })
-        .fragmentSelector).toEqual(['10', '10', '100', '200']);
+        .fragmentSelector).toEqual([10, 10, 100, 200]);
     });
   });
 });
diff --git a/__tests__/src/lib/OpenSeadragonCanvasOverlay.test.js b/__tests__/src/lib/OpenSeadragonCanvasOverlay.test.js
new file mode 100644
index 0000000000000000000000000000000000000000..8663b652be436724cd7a52bb9191aa530e688a4e
--- /dev/null
+++ b/__tests__/src/lib/OpenSeadragonCanvasOverlay.test.js
@@ -0,0 +1,122 @@
+import OpenSeadragon from 'openseadragon';
+import OpenSeadragonCanvasOverlay from '../../../src/lib/OpenSeadragonCanvasOverlay';
+
+jest.mock('openseadragon');
+
+describe('OpenSeadragonCanvasOverlay', () => {
+  let canvasOverlay;
+  beforeEach(() => {
+    document.body.innerHTML = '<div id="canvas"></div>';
+    OpenSeadragon.mockClear();
+    OpenSeadragon.mockImplementation(() => ({
+      canvas: document.getElementById('canvas'),
+      container: {
+        clientHeight: 100,
+        clientWidth: 200,
+      },
+      viewport: {
+        getBounds: jest.fn(() => ({
+          x: 40, y: 80, width: 200, height: 300,
+        })),
+        getZoom: jest.fn(() => (0.75)),
+      },
+      world: {
+        getItemAt: jest.fn(() => ({
+          source: {
+            dimensions: {
+              x: 1000,
+              y: 2000,
+            },
+          },
+          viewportToImageZoom: jest.fn(() => (0.075)),
+        })),
+      },
+    }));
+    canvasOverlay = new OpenSeadragonCanvasOverlay(new OpenSeadragon());
+  });
+  describe('constructor', () => {
+    it('sets up initial values and canvas', () => {
+      expect(canvasOverlay.containerHeight).toEqual(0);
+      expect(canvasOverlay.containerWidth).toEqual(0);
+      expect(canvasOverlay.canvasDiv.outerHTML).toEqual(
+        '<div style="position: absolute; left: 0px; top: 0px; width: 100%; height: 100%;"><canvas></canvas></div>',
+      );
+    });
+  });
+  describe('context2d', () => {
+    it('calls getContext on canvas', () => {
+      const contextMock = jest.fn();
+      canvasOverlay.canvas = {
+        getContext: contextMock,
+      };
+      canvasOverlay.context2d; // eslint-disable-line no-unused-expressions
+      expect(contextMock).toHaveBeenCalledTimes(1);
+    });
+  });
+  describe('clear', () => {
+    it('calls getContext and clearRect on canvas', () => {
+      const clearRect = jest.fn();
+      const contextMock = jest.fn(() => ({
+        clearRect,
+      }));
+      canvasOverlay.canvas = {
+        getContext: contextMock,
+      };
+      canvasOverlay.clear();
+      expect(contextMock).toHaveBeenCalledTimes(1);
+      expect(clearRect).toHaveBeenCalledTimes(1);
+    });
+  });
+  describe('resize', () => {
+    it('sets various values based off of image and container sizes', () => {
+      canvasOverlay.resize();
+      expect(canvasOverlay.containerHeight).toEqual(100);
+      expect(canvasOverlay.containerWidth).toEqual(200);
+      expect(canvasOverlay.imgAspectRatio).toEqual(0.5);
+    });
+    it('when image is undefined returns early', () => {
+      OpenSeadragon.mockClear();
+      OpenSeadragon.mockImplementation(() => ({
+        canvas: document.getElementById('canvas'),
+        container: {
+          clientHeight: 100,
+          clientWidth: 200,
+        },
+        viewport: {
+          getBounds: jest.fn(() => (new OpenSeadragon.Rect(0, 0, 200, 200))),
+        },
+        world: {
+          getItemAt: jest.fn(),
+        },
+      }));
+      canvasOverlay = new OpenSeadragonCanvasOverlay(new OpenSeadragon());
+      canvasOverlay.resize();
+      expect(canvasOverlay.imgHeight).toEqual(undefined);
+      expect(canvasOverlay.imgWidth).toEqual(undefined);
+    });
+  });
+  describe('canvasUpdate', () => {
+    it('sets appropriate sizes and calls update argument', () => {
+      const scale = jest.fn();
+      const setAttribute = jest.fn();
+      const setTransform = jest.fn();
+      const translate = jest.fn();
+      const contextMock = jest.fn(() => ({
+        scale,
+        setTransform,
+        translate,
+      }));
+      canvasOverlay.canvas = {
+        getContext: contextMock,
+        setAttribute,
+      };
+      const update = jest.fn();
+      canvasOverlay.resize();
+      canvasOverlay.canvasUpdate(update);
+      expect(update).toHaveBeenCalledTimes(1);
+      expect(scale).toHaveBeenCalledWith(0.075, 0.075);
+      expect(translate).toHaveBeenCalledWith(-39.96, -26.65333333333333);
+      expect(setTransform).toHaveBeenCalledWith(1, 0, 0, 1, 0, 0);
+    });
+  });
+});
diff --git a/__tests__/src/reducers/companionWindows.test.js b/__tests__/src/reducers/companionWindows.test.js
index 68775833910068ef0252fdf95084980790c37c1a..9f33e9ecf64c6c1aa05470a9149c470e913678af 100644
--- a/__tests__/src/reducers/companionWindows.test.js
+++ b/__tests__/src/reducers/companionWindows.test.js
@@ -20,6 +20,22 @@ describe('companionWindowsReducer', () => {
     });
   });
 
+  describe('ADD_WINDOW', () => {
+    it('adds default companion window(s)', () => {
+      const action = {
+        type: ActionTypes.ADD_WINDOW,
+        companionWindows: [{ id: 'banana', position: 'left', content: 'info' }, { id: 'Banane', position: 'right', content: 'canvas_navigation' }],
+      };
+      const beforeState = {};
+      const expectedState = {
+        banana: { id: 'banana', position: 'left', content: 'info' },
+        Banane: { id: 'Banane', position: 'right', content: 'canvas_navigation' },
+      };
+      expect(companionWindowsReducer(beforeState, action)).toEqual(expectedState);
+    });
+  });
+
+
   describe('UPDATE_COMPANION_WINDOW', () => {
     it('updates an existing companion window', () => {
       const action = {
diff --git a/src/components/CompanionWindow.js b/src/components/CompanionWindow.js
index 2615be8ed46326fffa644f43f205af8f933bb3b3..585009d68f2a16b48366b3dc2c0bf90a8255b5c0 100644
--- a/src/components/CompanionWindow.js
+++ b/src/components/CompanionWindow.js
@@ -35,7 +35,7 @@ export class CompanionWindow extends Component {
         component="aside"
         aria-label={title}
       >
-        <Toolbar variant="dense" className={ns('companion-window-header')}>
+        <Toolbar variant="dense" className={[position === 'left' ? classes.leftPadding : undefined, ns('companion-window-header')].join(' ')} disableGutters>
           <Typography variant="h3" className={classes.windowSideBarTitle}>
             {title}
           </Typography>
diff --git a/src/components/OpenSeadragonViewer.js b/src/components/OpenSeadragonViewer.js
index b78231c44454bee293467e6eb46c8f07a46b76b6..50708b5ac3df3f776f1e861e68fa4e0498b30ca0 100644
--- a/src/components/OpenSeadragonViewer.js
+++ b/src/components/OpenSeadragonViewer.js
@@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
 import OpenSeadragon from 'openseadragon';
 import ns from '../config/css-ns';
 import ZoomControls from '../containers/ZoomControls';
+import OpenSeadragonCanvasOverlay from '../lib/OpenSeadragonCanvasOverlay';
 
 /**
  * Represents a OpenSeadragonViewer in the mirador workspace. Responsible for mounting
@@ -16,7 +17,11 @@ export class OpenSeadragonViewer extends Component {
     super(props);
 
     this.viewer = null;
+    this.osdCanvasOverlay = null;
+    // An initial value for the updateCanvas method
+    this.updateCanvas = () => {};
     this.ref = React.createRef();
+    this.onUpdateViewport = this.onUpdateViewport.bind(this);
     this.onViewportChange = this.onViewportChange.bind(this);
   }
 
@@ -36,6 +41,9 @@ export class OpenSeadragonViewer extends Component {
       showNavigationControl: false,
       preserveImageSizeOnResize: true,
     });
+
+    this.osdCanvasOverlay = new OpenSeadragonCanvasOverlay(this.viewer);
+    this.viewer.addHandler('update-viewport', this.onUpdateViewport);
     this.viewer.addHandler('viewport-change', this.onViewportChange);
 
     if (viewer) {
@@ -48,10 +56,21 @@ export class OpenSeadragonViewer extends Component {
 
   /**
    * When the tileSources change, make sure to close the OSD viewer.
+   * When the annotations change, reset the updateCanvas method to make sure
+   * they are added.
    * When the viewport state changes, pan or zoom the OSD viewer as appropriate
    */
   componentDidUpdate(prevProps) {
-    const { tileSources, viewer } = this.props;
+    const { tileSources, viewer, annotations } = this.props;
+    if (!this.annotationsMatch(prevProps.annotations)) {
+      this.updateCanvas = () => {
+        this.osdCanvasOverlay.clear();
+        this.osdCanvasOverlay.resize();
+        this.osdCanvasOverlay.canvasUpdate(() => {
+          this.annotationsToContext(annotations);
+        });
+      };
+    }
     if (!this.tileSourcesMatch(prevProps.tileSources)) {
       this.viewer.close();
       Promise.all(
@@ -81,6 +100,13 @@ export class OpenSeadragonViewer extends Component {
     this.viewer.removeAllHandlers();
   }
 
+  /**
+   * onUpdateViewport - fires during OpenSeadragon render method.
+   */
+  onUpdateViewport(event) {
+    this.updateCanvas();
+  }
+
   /**
    * Forward OSD state to redux
    */
@@ -96,6 +122,20 @@ export class OpenSeadragonViewer extends Component {
     });
   }
 
+  /**
+   * annotationsToContext - converts anontations to a canvas context
+   */
+  annotationsToContext(annotations) {
+    const context = this.osdCanvasOverlay.context2d;
+    annotations.forEach((annotation) => {
+      annotation.resources.forEach((resource) => {
+        context.strokeStyle = 'yellow';
+        context.lineWidth = 10;
+        context.strokeRect(...resource.fragmentSelector);
+      });
+    });
+  }
+
   /**
    * boundsFromTileSources - calculates the overall width/height
    * based on 0 -> n tileSources
@@ -195,6 +235,25 @@ export class OpenSeadragonViewer extends Component {
     });
   }
 
+  /**
+   * annotationsMatch - compares previous annotations to current to determine
+   * whether to add a new updateCanvas method to draw annotations
+   * @param  {Array} prevAnnotations
+   * @return {Boolean}
+   */
+  annotationsMatch(prevAnnotations) {
+    const { annotations } = this.props;
+    return annotations.some((annotation, index) => {
+      if (!prevAnnotations[index]) {
+        return false;
+      }
+      if (annotation.id === prevAnnotations[index].id) {
+        return true;
+      }
+      return false;
+    });
+  }
+
   /**
    * Renders things
    */
@@ -220,6 +279,7 @@ export class OpenSeadragonViewer extends Component {
 }
 
 OpenSeadragonViewer.defaultProps = {
+  annotations: [],
   children: null,
   tileSources: [],
   viewer: null,
@@ -227,6 +287,7 @@ OpenSeadragonViewer.defaultProps = {
 };
 
 OpenSeadragonViewer.propTypes = {
+  annotations: PropTypes.arrayOf(PropTypes.object),
   children: PropTypes.element,
   tileSources: PropTypes.arrayOf(PropTypes.object),
   viewer: PropTypes.object, // eslint-disable-line react/forbid-prop-types
diff --git a/src/components/WindowViewer.js b/src/components/WindowViewer.js
index 9f103823ffff31dfbae01d24b8a8d01267eb0d4b..78b5354560789a897ae934acd703cdb380deb9db 100644
--- a/src/components/WindowViewer.js
+++ b/src/components/WindowViewer.js
@@ -126,6 +126,7 @@ export class WindowViewer extends Component {
       <>
         <OSDViewer
           tileSources={this.tileInfoFetchedFromStore()}
+          currentCanvases={this.currentCanvases()}
           windowId={window.id}
         >
           <ViewerNavigation window={window} canvases={this.canvases} />
diff --git a/src/containers/CompanionWindow.js b/src/containers/CompanionWindow.js
index e2a9615f679cdca7e66489189d21fd19dee07b31..3d7e6724df9b27c38e139c943d1770ec067c1d78 100644
--- a/src/containers/CompanionWindow.js
+++ b/src/containers/CompanionWindow.js
@@ -59,6 +59,10 @@ const styles = theme => ({
   positionButton: {
     order: -100,
   },
+  leftPadding: {
+    ...theme.mixins.gutters(),
+    paddingRight: 0,
+  },
   content: {
     ...theme.mixins.gutters(),
     overflowY: 'auto',
diff --git a/src/containers/OpenSeadragonViewer.js b/src/containers/OpenSeadragonViewer.js
index eb0e1422d496870e33c228ab101a5bd0ffdc238e..7099f30dc6460276366607398747635cf8499a66 100644
--- a/src/containers/OpenSeadragonViewer.js
+++ b/src/containers/OpenSeadragonViewer.js
@@ -7,6 +7,7 @@ import * as actions from '../state/actions';
 import {
   getCanvasLabel,
   getSelectedCanvas,
+  getSelectedCanvasAnnotations,
 } from '../state/selectors';
 
 /**
@@ -14,12 +15,18 @@ import {
  * @memberof Window
  * @private
  */
-const mapStateToProps = ({ viewers, windows, manifests }, { windowId }) => ({
+const mapStateToProps = ({
+  viewers, windows, manifests, annotations,
+}, { windowId, currentCanvases }) => ({
   viewer: viewers[windowId],
   label: getCanvasLabel(
     getSelectedCanvas({ windows, manifests }, windowId),
     windows[windowId].canvasIndex,
   ),
+  annotations: getSelectedCanvasAnnotations(
+    { annotations },
+    currentCanvases.map(canvas => canvas.id),
+  ),
 });
 
 /**
diff --git a/src/lib/AnnotationResource.js b/src/lib/AnnotationResource.js
index 88e38be87073a32e118c63d2d724fe8c99bf6f9c..3e3091a559a22f99c1eebd18ae55e55c08c78c3e 100644
--- a/src/lib/AnnotationResource.js
+++ b/src/lib/AnnotationResource.js
@@ -37,9 +37,9 @@ export default class AnnotationResource {
     const { on } = this.resource;
     switch (typeof on) {
       case 'string':
-        return on.match(/xywh=(.*)$/)[1].split(',');
+        return on.match(/xywh=(.*)$/)[1].split(',').map(str => parseInt(str, 10));
       case 'object':
-        return on.selector.value.match(/xywh=(.*)$/)[1].split(',');
+        return on.selector.value.match(/xywh=(.*)$/)[1].split(',').map(str => parseInt(str, 10));
       default:
         return null;
     }
diff --git a/src/lib/OpenSeadragonCanvasOverlay.js b/src/lib/OpenSeadragonCanvasOverlay.js
new file mode 100644
index 0000000000000000000000000000000000000000..e306f4579b84557503324fb6d9c33676ca56c5b1
--- /dev/null
+++ b/src/lib/OpenSeadragonCanvasOverlay.js
@@ -0,0 +1,100 @@
+import OpenSeadragon from 'openseadragon';
+
+/**
+ * OpenSeadragonCanvasOverlay - adapted from https://github.com/altert/OpenSeadragonCanvasOverlay
+ * used rather than an "onRedraw" function we tap into our own method. Existing
+ * repository is not published as an npm package.
+ * Code ported from https://github.com/altert/OpenSeadragonCanvasOverlay
+ * carries a BSD 3-Clause license originally authored by @altert from
+ * https://github.com/altert/OpenseadragonFabricjsOverlay
+ */
+export default class OpenSeadragonCanvasOverlay {
+  /**
+   * constructor - sets up the Canvas overlay container
+   */
+  constructor(viewer) {
+    this.viewer = viewer;
+
+    this.containerWidth = 0;
+    this.containerHeight = 0;
+
+    this.canvasDiv = document.createElement('div');
+    this.canvasDiv.style.position = 'absolute';
+    this.canvasDiv.style.left = 0;
+    this.canvasDiv.style.top = 0;
+    this.canvasDiv.style.width = '100%';
+    this.canvasDiv.style.height = '100%';
+    this.viewer.canvas.appendChild(this.canvasDiv);
+
+    this.canvas = document.createElement('canvas');
+    this.canvasDiv.appendChild(this.canvas);
+    this.imgAspectRatio = 1;
+  }
+
+  /** */
+  get context2d() {
+    return this.canvas.getContext('2d');
+  }
+
+  /** */
+  clear() {
+    this.canvas.getContext('2d').clearRect(0, 0, this.containerWidth, this.containerHeight);
+  }
+
+  /**
+   * resize - resizes the added Canvas overlay.
+   */
+  resize() {
+    if (this.containerWidth !== this.viewer.container.clientWidth) {
+      this.containerWidth = this.viewer.container.clientWidth;
+      this.canvasDiv.setAttribute('width', this.containerWidth);
+      this.canvas.setAttribute('width', this.containerWidth);
+    }
+
+    if (this.containerHeight !== this.viewer.container.clientHeight) {
+      this.containerHeight = this.viewer.container.clientHeight;
+      this.canvasDiv.setAttribute('height', this.containerHeight);
+      this.canvas.setAttribute('height', this.containerHeight);
+    }
+
+    this.viewportOrigin = new OpenSeadragon.Point(0, 0);
+    const boundsRect = this.viewer.viewport.getBounds(true);
+    this.viewportOrigin.x = boundsRect.x;
+    this.viewportOrigin.y = boundsRect.y * this.imgAspectRatio;
+
+    this.viewportWidth = boundsRect.width;
+    this.viewportHeight = boundsRect.height * this.imgAspectRatio;
+    const image1 = this.viewer.world.getItemAt(0);
+    if (!image1) return;
+    this.imgWidth = image1.source.dimensions.x;
+    this.imgHeight = image1.source.dimensions.y;
+    this.imgAspectRatio = this.imgWidth / this.imgHeight;
+  }
+
+  /**
+   * canvasUpdate - sets up the dimensions for the canvas update to mimick image
+   * 0 dimensions. Then call provided update function.
+   * @param {Function} update
+   */
+  canvasUpdate(update) {
+    const viewportZoom = this.viewer.viewport.getZoom(true);
+    const image1 = this.viewer.world.getItemAt(0);
+    if (!image1) return;
+    const zoom = image1.viewportToImageZoom(viewportZoom);
+
+    const x = (
+      (this.viewportOrigin.x / this.imgWidth - this.viewportOrigin.x) / this.viewportWidth
+    ) * this.containerWidth;
+    const y = (
+      (this.viewportOrigin.y / this.imgHeight - this.viewportOrigin.y) / this.viewportHeight
+    ) * this.containerHeight;
+
+    if (this.clearBeforeRedraw) this.clear();
+    this.canvas.getContext('2d').translate(x, y);
+    this.canvas.getContext('2d').scale(zoom, zoom);
+
+    update();
+
+    this.canvas.getContext('2d').setTransform(1, 0, 0, 1, 0, 0);
+  }
+}
diff --git a/src/state/actions/canvas.js b/src/state/actions/canvas.js
index 68e0e8408d6a4a5d03e7d986b9385c63e17f88f6..5fc9c5f9da83c7fe7f83ef4a3969a93c0f81fba2 100644
--- a/src/state/actions/canvas.js
+++ b/src/state/actions/canvas.js
@@ -22,7 +22,7 @@ export function updateViewport(windowId, payload) {
     meta: {
       debounce: {
         // TODO : set this value in a registry
-        time: 300,
+        time: 100,
       },
     },
     windowId,
diff --git a/src/state/actions/window.js b/src/state/actions/window.js
index 85a1ecf0f5526f3e8ff045fb6ea126a7370e309c..b966c2dca801ab5818894cba3d3a57618c536a46 100644
--- a/src/state/actions/window.js
+++ b/src/state/actions/window.js
@@ -19,6 +19,7 @@ export function focusWindow(windowId) {
  * @memberof ActionCreators
  */
 export function addWindow(options) {
+  const cwDefault = `cw-${uuid()}`;
   const defaultOptions = {
     id: `window-${uuid()}`,
     canvasIndex: 0,
@@ -30,12 +31,13 @@ export function addWindow(options) {
     height: 400,
     x: 2700,
     y: 2700,
-    companionWindowIds: [],
+    companionWindowIds: [cwDefault],
+    sideBarPanel: 'info',
     rotation: null,
     view: 'single',
     maximized: false,
   };
-  return { type: ActionTypes.ADD_WINDOW, window: { ...defaultOptions, ...options } };
+  return { type: ActionTypes.ADD_WINDOW, window: { ...defaultOptions, ...options }, companionWindows: [{ id: cwDefault, position: 'left', content: 'info' }] };
 }
 
 /**
diff --git a/src/state/reducers/companionWindows.js b/src/state/reducers/companionWindows.js
index c5cfc2dcc685b5570d1563974c423124591fcaf9..e3d43b9809e8dbb417b9e4eef182e5ca464f21ca 100644
--- a/src/state/reducers/companionWindows.js
+++ b/src/state/reducers/companionWindows.js
@@ -9,6 +9,12 @@ export function companionWindowsReducer(state = {}, action) {
     case ActionTypes.ADD_COMPANION_WINDOW:
       return setIn(state, [action.id], action.payload);
 
+    case ActionTypes.ADD_WINDOW:
+      return action.companionWindows.reduce((newState, cw) => {
+        newState[cw.id] = cw; // eslint-disable-line no-param-reassign
+        return newState;
+      }, state);
+
     case ActionTypes.UPDATE_COMPANION_WINDOW:
       return updateIn(state, [action.id], orig => merge(orig, action.payload));