diff --git a/__tests__/src/actions/workspace.test.js b/__tests__/src/actions/workspace.test.js
index e9de722b9d23d88d97ec051510ea6abc0f83fc8f..5dcc6d6819925c1a3e06e81933b43d9c664cf2c8 100644
--- a/__tests__/src/actions/workspace.test.js
+++ b/__tests__/src/actions/workspace.test.js
@@ -31,4 +31,13 @@ describe('workspace actions', () => {
       expect(actions.updateWorkspaceMosaicLayout(options)).toEqual(expectedAction);
     });
   });
+  describe('toggleZoomControls', () => {
+    it('should set the zoom control visibility', () => {
+      const expectedAction = {
+        type: ActionTypes.TOGGLE_ZOOM_CONTROLS,
+        showZoomControls: true,
+      };
+      expect(actions.toggleZoomControls(true)).toEqual(expectedAction);
+    });
+  });
 });
diff --git a/__tests__/src/components/WorkspaceMenu.test.js b/__tests__/src/components/WorkspaceMenu.test.js
index 27c11729c9568368cfb7881ae3c51cbfebe2745c..c7b76b66149c0670d51043329cf8adcab09e7af7 100644
--- a/__tests__/src/components/WorkspaceMenu.test.js
+++ b/__tests__/src/components/WorkspaceMenu.test.js
@@ -6,9 +6,19 @@ import WindowList from '../../../src/containers/WindowList';
 describe('WorkspaceMenu', () => {
   let wrapper;
   let handleClose;
+  const showZoomControls = false;
+  let toggleZoomControls;
+
   beforeEach(() => {
     handleClose = jest.fn();
-    wrapper = shallow(<WorkspaceMenu handleClose={handleClose} />);
+    toggleZoomControls = jest.fn();
+    wrapper = shallow(
+      <WorkspaceMenu
+        handleClose={handleClose}
+        showZoomControls={showZoomControls}
+        toggleZoomControls={toggleZoomControls}
+      />,
+    );
   });
 
   it('renders without an error', () => {
@@ -34,4 +44,11 @@ describe('WorkspaceMenu', () => {
       expect(wrapper.find(WindowList).props().open).toBe(false);
     });
   });
+
+  describe('handleZoomToggleClick', () => {
+    it('resets the anchor state', () => {
+      wrapper.instance().handleZoomToggleClick();
+      expect(toggleZoomControls).toBeCalledWith(true);
+    });
+  });
 });
diff --git a/__tests__/src/components/ZoomControls.test.js b/__tests__/src/components/ZoomControls.test.js
new file mode 100644
index 0000000000000000000000000000000000000000..ad3cc991019370031cdc0b9cc418b56f3d126631
--- /dev/null
+++ b/__tests__/src/components/ZoomControls.test.js
@@ -0,0 +1,75 @@
+import React from 'react';
+import { shallow } from 'enzyme';
+import ZoomControls from '../../../src/components/ZoomControls';
+
+describe('ZoomControls', () => {
+  let wrapper;
+  const viewer = { x: 100, y: 100, zoom: 1 };
+  const showZoomControls = false;
+  let updateViewport;
+
+  beforeEach(() => {
+    updateViewport = jest.fn();
+    wrapper = shallow(
+      <ZoomControls
+        windowId="xyz"
+        viewer={viewer}
+        showZoomControls={showZoomControls}
+        updateViewport={updateViewport}
+      />,
+    ).dive();
+  });
+
+  describe('with showZoomControls=false', () => {
+    it('renders nothing unless asked', () => {
+      expect(wrapper.find('WithStyles(List)').length).toBe(0);
+    });
+  });
+
+
+  describe('with showZoomControls=true', () => {
+    beforeEach(() => {
+      updateViewport = jest.fn();
+      wrapper = shallow(
+        <ZoomControls
+          windowId="xyz"
+          viewer={viewer}
+          showZoomControls
+          updateViewport={updateViewport}
+        />,
+      ).dive();
+    });
+
+    it('renders a couple buttons', () => {
+      expect(wrapper.find('WithStyles(List)').length).toBe(1);
+    });
+
+    it('has a zoom-in button', () => {
+      const button = wrapper.find('WithStyles(IconButton)[aria-label="zoomIn"]');
+      expect(button.simulate('click'));
+      expect(updateViewport).toHaveBeenCalledTimes(1);
+      expect(updateViewport).toHaveBeenCalledWith('xyz', { x: 100, y: 100, zoom: 2 });
+    });
+
+    it('has a zoom-out button', () => {
+      const button = wrapper.find('WithStyles(IconButton)[aria-label="zoomOut"]');
+      expect(button.simulate('click'));
+      expect(updateViewport).toHaveBeenCalledTimes(1);
+      expect(updateViewport).toHaveBeenCalledWith('xyz', { x: 100, y: 100, zoom: 0.5 });
+    });
+
+    it('has a zoom reseet button', () => {
+      const button = wrapper.find('WithStyles(IconButton)[aria-label="zoomReset"]');
+      expect(button.simulate('click'));
+      expect(updateViewport).toHaveBeenCalledTimes(1);
+      expect(updateViewport).toHaveBeenCalledWith('xyz', { x: 100, y: 100, zoom: 1 });
+    });
+  });
+
+  describe('handleZoomInClick', () => {
+    it('increases the zoom value on Zoom-In', () => {
+      wrapper.instance().handleZoomInClick();
+      expect(updateViewport).toHaveBeenCalled();
+    });
+  });
+});
diff --git a/__tests__/src/reducers/workspace.test.js b/__tests__/src/reducers/workspace.test.js
index dd5ebddcb99bc540ffd0ae87f4d99a5dbd8568ab..8b5732259ad8837af4d16d991d40308af4fcfbc7 100644
--- a/__tests__/src/reducers/workspace.test.js
+++ b/__tests__/src/reducers/workspace.test.js
@@ -18,6 +18,14 @@ describe('workspace reducer', () => {
       isFullscreenEnabled: true,
     });
   });
+  it('should handle TOGGLE_ZOOM_CONTROLS', () => {
+    expect(reducer([], {
+      type: ActionTypes.TOGGLE_ZOOM_CONTROLS,
+      showZoomControls: true,
+    })).toEqual({
+      showZoomControls: true,
+    });
+  });
   it('should handle UPDATE_WORKSPACE_MOSAIC_LAYOUT', () => {
     expect(reducer([], {
       type: ActionTypes.UPDATE_WORKSPACE_MOSAIC_LAYOUT,
diff --git a/locales/en/translation.json b/locales/en/translation.json
index 3ca92e21328345b5afbeff4ef4d8c37ddc80a007..a5e657c224f8a77e7a64b9fef968aeb3af4ba36d 100644
--- a/locales/en/translation.json
+++ b/locales/en/translation.json
@@ -23,6 +23,9 @@
     "theme": "Theme",
     "thumbnails": "Thumbnails",
     "toggleWindowSideBar": "Toggle window sidebar",
-    "untitled": "[Untitled]"
+    "untitled": "[Untitled]",
+    "zoomIn": "Zoom in",
+    "zoomOut": "Zoom out",
+    "zoomReset": "Reset zoom"
   }
 }
diff --git a/src/components/OpenSeadragonViewer.js b/src/components/OpenSeadragonViewer.js
index e28b87182dd4a77c31d216921901f3541356ffd8..6477a27df66e52888ef5a52aea569c98c5933a29 100644
--- a/src/components/OpenSeadragonViewer.js
+++ b/src/components/OpenSeadragonViewer.js
@@ -2,6 +2,7 @@ import React, { Component } from 'react';
 import PropTypes from 'prop-types';
 import OpenSeadragon from 'openseadragon';
 import ns from '../config/css-ns';
+import ZoomControls from '../containers/ZoomControls';
 
 /**
  * Represents a OpenSeadragonViewer in the mirador workspace. Responsible for mounting
@@ -154,6 +155,7 @@ class OpenSeadragonViewer extends Component {
         >
           { children }
         </div>
+        <ZoomControls windowId={window.id} />
       </>
     );
   }
diff --git a/src/components/WorkspaceMenu.js b/src/components/WorkspaceMenu.js
index 9f37eff027313e9c5279c2fa5ea040a1cf5fc2d9..d7a0020463503a0710788f8e25571b9231bcf90e 100644
--- a/src/components/WorkspaceMenu.js
+++ b/src/components/WorkspaceMenu.js
@@ -2,6 +2,7 @@ import React, { Component } from 'react';
 import Menu from '@material-ui/core/Menu';
 import Divider from '@material-ui/core/Divider';
 import ListItemIcon from '@material-ui/core/ListItemIcon';
+import LoupeIcon from '@material-ui/icons/Loupe';
 import MenuItem from '@material-ui/core/MenuItem';
 import Typography from '@material-ui/core/Typography';
 import SaveAltIcon from '@material-ui/icons/SaveAlt';
@@ -22,6 +23,7 @@ class WorkspaceMenu extends Component {
     super(props);
     this.state = {
       windowList: {},
+      toggleZoom: {},
       settings: {},
       exportWorkspace: {},
     };
@@ -53,13 +55,29 @@ class WorkspaceMenu extends Component {
     };
   }
 
+  /**
+   * @private
+   */
+  handleZoomToggleClick() {
+    const { toggleZoomControls, showZoomControls } = this.props;
+    toggleZoomControls(!showZoomControls);
+  }
+
   /**
    * render
    * @return
    */
   render() {
-    const { handleClose, anchorEl, t } = this.props;
-    const { windowList, settings, exportWorkspace } = this.state;
+    const {
+      handleClose, anchorEl, t, showZoomControls,
+    } = this.props;
+
+    const {
+      windowList,
+      toggleZoom,
+      settings,
+      exportWorkspace,
+    } = this.state;
 
     return (
       <>
@@ -74,6 +92,16 @@ class WorkspaceMenu extends Component {
             </ListItemIcon>
             <Typography varient="inherit">{t('listAllOpenWindows')}</Typography>
           </MenuItem>
+          <MenuItem
+            aria-haspopup="true"
+            onClick={(e) => { this.handleZoomToggleClick(e); handleClose(e); }}
+            aria-owns={toggleZoom.anchorEl ? 'toggle-zoom-menu' : undefined}
+          >
+            <ListItemIcon>
+              <LoupeIcon />
+            </ListItemIcon>
+            <Typography varient="inherit">{ showZoomControls ? 'Hide zoom controls' : 'Show Zoom Controls' }</Typography>
+          </MenuItem>
           <Divider />
           <MenuItem
             aria-haspopup="true"
@@ -101,6 +129,10 @@ class WorkspaceMenu extends Component {
           open={Boolean(windowList.anchorEl)}
           handleClose={this.handleMenuItemClose('windowList')}
         />
+        <WorkspaceSettings
+          open={Boolean(toggleZoom.open)}
+          handleClose={this.handleMenuItemClose('toggleZoom')}
+        />
         <WorkspaceSettings
           open={Boolean(settings.open)}
           handleClose={this.handleMenuItemClose('settings')}
@@ -116,6 +148,8 @@ class WorkspaceMenu extends Component {
 
 WorkspaceMenu.propTypes = {
   handleClose: PropTypes.func.isRequired,
+  toggleZoomControls: PropTypes.func,
+  showZoomControls: PropTypes.bool,
   anchorEl: PropTypes.object, // eslint-disable-line react/forbid-prop-types
   t: PropTypes.func,
 };
@@ -123,6 +157,8 @@ WorkspaceMenu.propTypes = {
 WorkspaceMenu.defaultProps = {
   anchorEl: null,
   t: key => key,
+  showZoomControls: false,
+  toggleZoomControls: () => {},
 };
 
 export default WorkspaceMenu;
diff --git a/src/components/ZoomControls.js b/src/components/ZoomControls.js
new file mode 100644
index 0000000000000000000000000000000000000000..77903229042d79f1454bb191801da8ac29bd0fc6
--- /dev/null
+++ b/src/components/ZoomControls.js
@@ -0,0 +1,134 @@
+import React, { Component } from 'react';
+import { withStyles } from '@material-ui/core/styles';
+import IconButton from '@material-ui/core/IconButton';
+import List from '@material-ui/core/List';
+import ListItem from '@material-ui/core/ListItem';
+import AddCircleIcon from '@material-ui/icons/AddCircle';
+import RemoveCircleIcon from '@material-ui/icons/RemoveCircle';
+import RefreshIcon from '@material-ui/icons/Refresh';
+import PropTypes from 'prop-types';
+
+/**
+ */
+class ZoomControls extends Component {
+  /**
+   * constructor -
+   */
+  constructor(props) {
+    super(props);
+
+    this.handleZoomInClick = this.handleZoomInClick.bind(this);
+    this.handleZoomOutClick = this.handleZoomOutClick.bind(this);
+    this.handleZoomResetClick = this.handleZoomResetClick.bind(this);
+  }
+
+  /**
+   * @private
+   */
+  handleZoomInClick() {
+    const { windowId, updateViewport, viewer } = this.props;
+
+    updateViewport(windowId, {
+      x: viewer.x,
+      y: viewer.y,
+      zoom: viewer.zoom * 2,
+    });
+  }
+
+  /**
+   * @private
+   */
+  handleZoomOutClick() {
+    const { windowId, updateViewport, viewer } = this.props;
+
+    updateViewport(windowId, {
+      x: viewer.x,
+      y: viewer.y,
+      zoom: viewer.zoom / 2,
+    });
+  }
+
+  /**
+   * @private
+   */
+  handleZoomResetClick() {
+    const { windowId, updateViewport, viewer } = this.props;
+
+    updateViewport(windowId, {
+      x: viewer.x,
+      y: viewer.y,
+      zoom: 1,
+    });
+  }
+
+  /**
+   * render
+   * @return
+   */
+  render() {
+    const { showZoomControls, classes, t } = this.props;
+
+    if (!showZoomControls) {
+      return (
+        <>
+        </>
+      );
+    }
+    return (
+      <List className={classes.zoom_controls}>
+        <ListItem>
+          <IconButton aria-label={t('zoomIn')} onClick={this.handleZoomInClick}>
+            <AddCircleIcon />
+          </IconButton>
+        </ListItem>
+        <ListItem>
+          <IconButton aria-label={t('zoomOut')} onClick={this.handleZoomOutClick}>
+            <RemoveCircleIcon />
+          </IconButton>
+        </ListItem>
+        <ListItem>
+          <IconButton aria-label={t('zoomReset')} onClick={this.handleZoomResetClick}>
+            <RefreshIcon />
+          </IconButton>
+        </ListItem>
+      </List>
+    );
+  }
+}
+
+ZoomControls.propTypes = {
+  windowId: PropTypes.string,
+  showZoomControls: PropTypes.bool,
+  viewer: PropTypes.shape({
+    x: PropTypes.number,
+    y: PropTypes.number,
+    zoom: PropTypes.number,
+  }),
+  updateViewport: PropTypes.func,
+  classes: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
+  t: PropTypes.func,
+};
+
+ZoomControls.defaultProps = {
+  windowId: '',
+  showZoomControls: false,
+  viewer: {},
+  updateViewport: () => {},
+  t: key => key,
+};
+
+/**
+ * @private
+ */
+const styles = theme => ({
+  zoom_controls: {
+    position: 'absolute',
+    right: 0,
+  },
+  ListItem: {
+    paddingTop: 0,
+    paddingBottom: 0,
+  },
+});
+
+export default withStyles(styles)(ZoomControls);
diff --git a/src/containers/WorkspaceMenu.js b/src/containers/WorkspaceMenu.js
index 866358d4fede4ed20468237fbf47e8cb0288c2b6..aa1bdd2e34d4b48d2c46818ff79d0551fafc1a73 100644
--- a/src/containers/WorkspaceMenu.js
+++ b/src/containers/WorkspaceMenu.js
@@ -1,9 +1,28 @@
 import { compose } from 'redux';
+import { connect } from 'react-redux';
 import { withNamespaces } from 'react-i18next';
 import miradorWithPlugins from '../lib/miradorWithPlugins';
+import * as actions from '../state/actions';
 import WorkspaceMenu from '../components/WorkspaceMenu';
 
+/**
+ * mapDispatchToProps - used to hook up connect to action creators
+ * @memberof ManifestListItem
+ * @private
+ */
+const mapDispatchToProps = { toggleZoomControls: actions.toggleZoomControls };
+
+/**
+ * mapStateToProps - to hook up connect
+ * @memberof WindowViewer
+ * @private
+ */
+const mapStateToProps = state => (
+  { showZoomControls: state.workspace.showZoomControls }
+);
+
 const enhance = compose(
+  connect(mapStateToProps, mapDispatchToProps),
   withNamespaces(),
   miradorWithPlugins,
   // further HOC
diff --git a/src/containers/ZoomControls.js b/src/containers/ZoomControls.js
new file mode 100644
index 0000000000000000000000000000000000000000..9c99d6cb906280e0c429f97fb2da449eb59bf7ba
--- /dev/null
+++ b/src/containers/ZoomControls.js
@@ -0,0 +1,33 @@
+import { compose } from 'redux';
+import { connect } from 'react-redux';
+import { withNamespaces } from 'react-i18next';
+import * as actions from '../state/actions';
+import ZoomControls from '../components/ZoomControls';
+
+/**
+ * mapStateToProps - to hook up connect
+ * @memberof Workspace
+ * @private
+ */
+const mapStateToProps = (state, props) => (
+  {
+    showZoomControls: state.workspace.showZoomControls,
+    viewer: state.windows[props.windowId].viewer,
+  }
+);
+
+
+/**
+ * mapDispatchToProps - used to hook up connect to action creators
+ * @memberof Workspace
+ * @private
+ */
+const mapDispatchToProps = { updateViewport: actions.updateViewport };
+
+const enhance = compose(
+  connect(mapStateToProps, mapDispatchToProps),
+  withNamespaces(),
+  // further HOC go here
+);
+
+export default enhance(ZoomControls);
diff --git a/src/state/actions/action-types.js b/src/state/actions/action-types.js
index 946d7ec8df813a86d04a53452c175f1e50015444..bd11ddcb9324722bc910e6fbfa1933522d952e8b 100644
--- a/src/state/actions/action-types.js
+++ b/src/state/actions/action-types.js
@@ -15,6 +15,7 @@ const ActionTypes = {
   SET_WINDOW_THUMBNAIL_POSITION: 'SET_WINDOW_THUMBNAIL_POSITION',
   TOGGLE_WINDOW_SIDE_BAR: 'TOGGLE_WINDOW_SIDE_BAR',
   TOGGLE_WINDOW_SIDE_BAR_PANEL: 'TOGGLE_WINDOW_SIDE_BAR_PANEL',
+  TOGGLE_ZOOM_CONTROLS: 'TOGGLE_ZOOM_CONTROLS',
   UPDATE_CONFIG: 'UPDATE_CONFIG',
   REMOVE_MANIFEST: 'REMOVE_MANIFEST',
   REQUEST_INFO_RESPONSE: 'REQUEST_INFO_RESPONSE',
diff --git a/src/state/actions/workspace.js b/src/state/actions/workspace.js
index 5bda3ff7df4303e90799ecce67194cd74ea4f3bf..a5aeb61a668b814de123ce13b9fff7ef50bc3ef0 100644
--- a/src/state/actions/workspace.js
+++ b/src/state/actions/workspace.js
@@ -11,6 +11,15 @@ export function setWorkspaceFullscreen(isFullscreenEnabled) {
   return { type: ActionTypes.SET_WORKSPACE_FULLSCREEN, isFullscreenEnabled };
 }
 
+/**
+ * toggleZoomControls - action creator
+ * @param {Boolean} showZoomControls
+ * @memberof ActionCreators
+*/
+export function toggleZoomControls(showZoomControls) {
+  return { type: ActionTypes.TOGGLE_ZOOM_CONTROLS, showZoomControls };
+}
+
 /**
  * updateWorkspaceMosaicLayout - action creator
  *
diff --git a/src/state/reducers/workspace.js b/src/state/reducers/workspace.js
index 1bfa069ad2d15d1448033771ee55c1c8e2da122a..47ccdf7de83f3e17b1c1063abbf5d1e7f45284bd 100644
--- a/src/state/reducers/workspace.js
+++ b/src/state/reducers/workspace.js
@@ -9,6 +9,8 @@ const workspaceReducer = (state = {}, action) => {
       return { ...state, focusedWindowId: action.windowId };
     case ActionTypes.SET_WORKSPACE_FULLSCREEN:
       return { ...state, isFullscreenEnabled: action.isFullscreenEnabled };
+    case ActionTypes.TOGGLE_ZOOM_CONTROLS:
+      return { ...state, showZoomControls: action.showZoomControls };
     case ActionTypes.UPDATE_WORKSPACE_MOSAIC_LAYOUT:
       return { ...state, layout: action.layout };
     default: