diff --git a/__tests__/src/components/OpenSeadragonViewer.test.js b/__tests__/src/components/OpenSeadragonViewer.test.js
index dc5637a9637dd92cd5e2e2dc2b1649b33d79ac89..18c03a552aecae19163c931456f5255eec8ca221 100644
--- a/__tests__/src/components/OpenSeadragonViewer.test.js
+++ b/__tests__/src/components/OpenSeadragonViewer.test.js
@@ -232,10 +232,13 @@ describe('OpenSeadragonViewer', () => {
     let panTo;
     let zoomTo;
     let addHandler;
+    let innerTracker;
+
     beforeEach(() => {
       panTo = jest.fn();
       zoomTo = jest.fn();
       addHandler = jest.fn();
+      innerTracker = {};
 
       wrapper = shallow(
         <OpenSeadragonViewer
@@ -257,6 +260,7 @@ describe('OpenSeadragonViewer', () => {
       OpenSeadragon.mockImplementation(() => ({
         addHandler,
         addTiledImage: jest.fn().mockResolvedValue('event'),
+        innerTracker,
         viewport: { panTo, zoomTo },
       }));
     });
@@ -279,6 +283,12 @@ describe('OpenSeadragonViewer', () => {
       expect(addHandler).toHaveBeenCalledWith('animation-finish', expect.anything());
       expect(addHandler).toHaveBeenCalledWith('animation-finish', wrapper.instance().onViewportChange);
     });
+
+    it('adds a mouse-move handler', () => {
+      wrapper.instance().componentDidMount();
+
+      expect(innerTracker.moveHandler).toEqual(wrapper.instance().onCanvasMouseMove);
+    });
   });
 
   describe('componentDidUpdate', () => {
@@ -352,4 +362,16 @@ describe('OpenSeadragonViewer', () => {
       );
     });
   });
+
+  describe('onCanvasMouseMove', () => {
+    it('triggers an OSD event', () => {
+      const viewer = { raiseEvent: jest.fn() };
+      wrapper.setState({ viewer });
+
+      wrapper.instance().onCanvasMouseMove('event');
+      wrapper.instance().onCanvasMouseMove.flush();
+
+      expect(viewer.raiseEvent).toHaveBeenCalledWith('mouse-move', 'event');
+    });
+  });
 });
diff --git a/src/components/AnnotationsOverlay.js b/src/components/AnnotationsOverlay.js
index 941e3f2646f45a02849a1a94ff2062a5113aaeae..dad600e2010ff243ed1c36d5e187a9bfb1761fd9 100644
--- a/src/components/AnnotationsOverlay.js
+++ b/src/components/AnnotationsOverlay.js
@@ -130,11 +130,7 @@ export class AnnotationsOverlay extends Component {
     viewer.removeHandler('canvas-click', this.onCanvasClick);
     viewer.removeHandler('canvas-exit', this.onCanvasExit);
     viewer.removeHandler('update-viewport', this.onUpdateViewport);
-
-    if (viewer.innerTracker
-      && viewer.innerTracker.moveHandler === this.onCanvasMouseMove) {
-      viewer.innerTracker.moveHandler = null;
-    }
+    viewer.removeHandler('mouse-move', this.onCanvasMouseMove);
   }
 
   /** */
@@ -263,10 +259,7 @@ export class AnnotationsOverlay extends Component {
     viewer.addHandler('canvas-click', this.onCanvasClick);
     viewer.addHandler('canvas-exit', this.onCanvasExit);
     viewer.addHandler('update-viewport', this.onUpdateViewport);
-
-    if (viewer.innerTracker) {
-      viewer.innerTracker.moveHandler = this.onCanvasMouseMove;
-    }
+    viewer.addHandler('mouse-move', this.onCanvasMouseMove);
 
     this.updateCanvas = this.canvasUpdateCallback();
   }
diff --git a/src/components/OpenSeadragonViewer.js b/src/components/OpenSeadragonViewer.js
index ca1469f3fed2318b822cf08499402bd00f0b8ab9..ff9f32373dd27ebfe4e3088e0ea161272eba4a1b 100644
--- a/src/components/OpenSeadragonViewer.js
+++ b/src/components/OpenSeadragonViewer.js
@@ -1,5 +1,6 @@
 import React, { Component } from 'react';
 import PropTypes from 'prop-types';
+import debounce from 'lodash/debounce';
 import isEqual from 'lodash/isEqual';
 import OpenSeadragon from 'openseadragon';
 import classNames from 'classnames';
@@ -24,6 +25,7 @@ export class OpenSeadragonViewer extends Component {
     this.ref = React.createRef();
     this.apiRef = React.createRef();
     OSDReferences.set(props.windowId, this.apiRef);
+    this.onCanvasMouseMove = debounce(this.onCanvasMouseMove.bind(this), 10);
     this.onViewportChange = this.onViewportChange.bind(this);
     this.zoomToWorld = this.zoomToWorld.bind(this);
     this.osdUpdating = false;
@@ -62,6 +64,10 @@ export class OpenSeadragonViewer extends Component {
     viewer.addHandler('animation-finish', () => {
       this.osdUpdating = false;
     });
+
+    if (viewer.innerTracker) {
+      viewer.innerTracker.moveHandler = this.onCanvasMouseMove;
+    }
   }
 
   /**
@@ -126,10 +132,21 @@ export class OpenSeadragonViewer extends Component {
   componentWillUnmount() {
     const { viewer } = this.state;
 
+    if (viewer.innerTracker
+      && viewer.innerTracker.moveHandler === this.onCanvasMouseMove) {
+      viewer.innerTracker.moveHandler = null;
+    }
     viewer.removeAllHandlers();
     this.apiRef.current = undefined;
   }
 
+  /** Shim to provide a mouse-move event coming from the viewer */
+  onCanvasMouseMove(event) {
+    const { viewer } = this.state;
+
+    viewer.raiseEvent('mouse-move', event);
+  }
+
   /**
    * Forward OSD state to redux
    */