diff --git a/src/components/paint-editor/paint-editor.css b/src/components/paint-editor/paint-editor.css
index 9b7a508f..f10ef7b4 100644
--- a/src/components/paint-editor/paint-editor.css
+++ b/src/components/paint-editor/paint-editor.css
@@ -19,7 +19,7 @@
 }
 
 .top-align-row {
-    display: flex; 
+    display: flex;
     padding-top: calc(5 * $grid-unit);
     flex-direction: row;
 }
@@ -29,7 +29,7 @@
 }
 
 .mod-dashed-border {
-    border-right: 1px dashed $ui-pane-border; 
+    border-right: 1px dashed $ui-pane-border;
     padding-right: calc(3 * $grid-unit);
 }
 
@@ -92,3 +92,8 @@ $border-radius: 0.25rem;
     align-content: flex-start;
     justify-content: space-between;
 }
+
+.zoom-controls {
+    display: flex;
+    flex-direction: row-reverse;
+}
diff --git a/src/components/paint-editor/paint-editor.jsx b/src/components/paint-editor/paint-editor.jsx
index 0f3c115b..cb262c80 100644
--- a/src/components/paint-editor/paint-editor.jsx
+++ b/src/components/paint-editor/paint-editor.jsx
@@ -39,6 +39,9 @@ import sendForwardIcon from './send-forward.svg';
 import sendFrontIcon from './send-front.svg';
 import undoIcon from './undo.svg';
 import ungroupIcon from './ungroup.svg';
+import zoomInIcon from './zoom-in.svg';
+import zoomOutIcon from './zoom-out.svg';
+import zoomResetIcon from './zoom-reset.svg';
 
 const BufferedInput = BufferedInputHOC(Input);
 const messages = defineMessages({
@@ -97,7 +100,7 @@ class PaintEditorComponent extends React.Component {
                                         onClick={this.props.onUndo}
                                     >
                                         <img
-                                            alt="Undo Icon"
+                                            alt="Undo"
                                             className={styles.buttonGroupButtonIcon}
                                             src={undoIcon}
                                         />
@@ -115,7 +118,7 @@ class PaintEditorComponent extends React.Component {
                                         onClick={this.props.onRedo}
                                     >
                                         <img
-                                            alt="Redo Icon"
+                                            alt="Redo"
                                             className={styles.buttonGroupButtonIcon}
                                             src={redoIcon}
                                         />
@@ -127,14 +130,14 @@ class PaintEditorComponent extends React.Component {
                             <InputGroup className={styles.modDashedBorder}>
                                 <LabeledIconButton
                                     disabled={!shouldShowGroup()}
-                                    imgAlt="Group Icon"
+                                    imgAlt="Group"
                                     imgSrc={groupIcon}
                                     title="Group"
                                     onClick={this.props.onGroup}
                                 />
                                 <LabeledIconButton
                                     disabled={!shouldShowUngroup()}
-                                    imgAlt="Ungroup Icon"
+                                    imgAlt="Ungroup"
                                     imgSrc={ungroupIcon}
                                     title="Ungroup"
                                     onClick={this.props.onUngroup}
@@ -145,14 +148,14 @@ class PaintEditorComponent extends React.Component {
                             <InputGroup className={styles.modDashedBorder}>
                                 <LabeledIconButton
                                     disabled={!shouldShowBringForward()}
-                                    imgAlt="Send Forward Icon"
+                                    imgAlt="Send Forward"
                                     imgSrc={sendForwardIcon}
                                     title="Forward"
                                     onClick={this.props.onSendForward}
                                 />
                                 <LabeledIconButton
                                     disabled={!shouldShowSendBackward()}
-                                    imgAlt="Send Backward Icon"
+                                    imgAlt="Send Backward"
                                     imgSrc={sendBackwardIcon}
                                     title="Backward"
                                     onClick={this.props.onSendBackward}
@@ -163,14 +166,14 @@ class PaintEditorComponent extends React.Component {
                             <InputGroup>
                                 <LabeledIconButton
                                     disabled={!shouldShowBringForward()}
-                                    imgAlt="Send to Front Icon"
+                                    imgAlt="Send to Front"
                                     imgSrc={sendFrontIcon}
                                     title="Front"
                                     onClick={this.props.onSendToFront}
                                 />
                                 <LabeledIconButton
                                     disabled={!shouldShowSendBackward()}
-                                    imgAlt="Send to Back Icon"
+                                    imgAlt="Send to Back"
                                     imgSrc={sendBackIcon}
                                     title="Back"
                                     onClick={this.props.onSendToBack}
@@ -180,7 +183,7 @@ class PaintEditorComponent extends React.Component {
                             {/* To be rotation point */}
                             {/* <InputGroup>
                                 <LabeledIconButton
-                                    imgAlt="Rotation Point Icon"
+                                    imgAlt="Rotation Point"
                                     imgSrc={rotationPointIcon}
                                     title="Rotation Point"
                                     onClick={function () {}}
@@ -222,20 +225,16 @@ class PaintEditorComponent extends React.Component {
                                 onUpdateSvg={this.props.onUpdateSvg}
                             />
                             <BrushMode
-                                canvas={this.state.canvas}
                                 onUpdateSvg={this.props.onUpdateSvg}
                             />
                             <EraserMode
-                                canvas={this.state.canvas}
                                 onUpdateSvg={this.props.onUpdateSvg}
                             />
                             <PenMode
-                                canvas={this.state.canvas}
                                 onUpdateSvg={this.props.onUpdateSvg}
                             />
                             {/* Text mode will go here */}
                             <LineMode
-                                canvas={this.state.canvas}
                                 onUpdateSvg={this.props.onUpdateSvg}
                             />
                             <OvalMode
@@ -257,6 +256,41 @@ class PaintEditorComponent extends React.Component {
                             svgId={this.props.svgId}
                             onUpdateSvg={this.props.onUpdateSvg}
                         />
+                        {/* Zoom controls */}
+                        <InputGroup className={styles.zoomControls}>
+                            <ButtonGroup>
+                                <Button
+                                    className={styles.buttonGroupButton}
+                                    onClick={this.props.onZoomIn}
+                                >
+                                    <img
+                                        alt="Zoom In"
+                                        className={styles.buttonGroupButtonIcon}
+                                        src={zoomInIcon}
+                                    />
+                                </Button>
+                                <Button
+                                    className={styles.buttonGroupButton}
+                                    onClick={this.props.onZoomReset}
+                                >
+                                    <img
+                                        alt="Zoom Reset"
+                                        className={styles.buttonGroupButtonIcon}
+                                        src={zoomResetIcon}
+                                    />
+                                </Button>
+                                <Button
+                                    className={styles.buttonGroupButton}
+                                    onClick={this.props.onZoomOut}
+                                >
+                                    <img
+                                        alt="Zoom Out"
+                                        className={styles.buttonGroupButtonIcon}
+                                        src={zoomOutIcon}
+                                    />
+                                </Button>
+                            </ButtonGroup>
+                        </InputGroup>
                     </div>
                 </div>
             </div>
@@ -279,6 +313,9 @@ PaintEditorComponent.propTypes = {
     onUngroup: PropTypes.func.isRequired,
     onUpdateName: PropTypes.func.isRequired,
     onUpdateSvg: PropTypes.func.isRequired,
+    onZoomIn: PropTypes.func.isRequired,
+    onZoomOut: PropTypes.func.isRequired,
+    onZoomReset: PropTypes.func.isRequired,
     rotationCenterX: PropTypes.number,
     rotationCenterY: PropTypes.number,
     svg: PropTypes.string,
diff --git a/src/components/paint-editor/zoom-in.svg b/src/components/paint-editor/zoom-in.svg
new file mode 100644
index 00000000..4b804c1f
--- /dev/null
+++ b/src/components/paint-editor/zoom-in.svg
@@ -0,0 +1,15 @@
+<?xml version="1.0"?>
+<svg xmlns="http://www.w3.org/2000/svg" id="Layer_1" data-name="Layer 1" viewBox="6 6 24 24">
+<defs>
+    <style>
+        .cls-4{fill:none;stroke:#575e75;stroke-linecap:round;stroke-linejoin:round;stroke-width:1.5px;}
+    </style>
+</defs>
+<title>zoom-in</title>
+<g class="cls-3">
+    <circle class="cls-4" cx="18" cy="18" r="7"/>
+    <line class="cls-4" x1="23" y1="23" x2="26" y2="26"/>
+    <line class="cls-4" x1="16" y1="18" x2="20" y2="18"/>
+    <line class="cls-4" x1="18" y1="16" x2="18" y2="20"/>
+</g>
+</svg>
diff --git a/src/components/paint-editor/zoom-out.svg b/src/components/paint-editor/zoom-out.svg
new file mode 100644
index 00000000..16846628
--- /dev/null
+++ b/src/components/paint-editor/zoom-out.svg
@@ -0,0 +1,14 @@
+<?xml version="1.0"?>
+<svg xmlns="http://www.w3.org/2000/svg" id="Layer_1" data-name="Layer 1" viewBox="6 6 24 24">
+<defs>
+    <style>
+        .cls-4{fill:none;stroke:#575e75;stroke-linecap:round;stroke-linejoin:round;stroke-width:1.5px;}
+    </style>
+</defs>
+<title>zoom-out</title>
+<g class="cls-3">
+    <circle class="cls-4" cx="18" cy="18" r="7"/>
+    <line class="cls-4" x1="23" y1="23" x2="26" y2="26"/>
+    <line class="cls-4" x1="16" y1="18" x2="20" y2="18"/>
+</g>
+</svg>
diff --git a/src/components/paint-editor/zoom-reset.svg b/src/components/paint-editor/zoom-reset.svg
new file mode 100644
index 00000000..66d8040c
--- /dev/null
+++ b/src/components/paint-editor/zoom-reset.svg
@@ -0,0 +1,13 @@
+<?xml version="1.0"?>
+<svg xmlns="http://www.w3.org/2000/svg" id="Layer_1" data-name="Layer 1" viewBox="6 6 24 24">
+<defs>
+    <style>
+        .cls-4{fill:#575e75;}
+    </style>
+</defs>
+<title>zoom-reset</title>
+<g class="cls-3">
+    <rect class="cls-4" x="13" y="14" width="10" height="2" rx="1" ry="1"/>
+    <rect class="cls-4" x="13" y="20" width="10" height="2" rx="1" ry="1"/>
+</g>
+</svg>
diff --git a/src/containers/brush-mode.jsx b/src/containers/brush-mode.jsx
index a9401f38..adf0f603 100644
--- a/src/containers/brush-mode.jsx
+++ b/src/containers/brush-mode.jsx
@@ -22,8 +22,7 @@ class BrushMode extends React.Component {
         super(props);
         bindAll(this, [
             'activateTool',
-            'deactivateTool',
-            'onScroll'
+            'deactivateTool'
         ]);
         this.blob = new Blobbiness(
             this.props.onUpdateSvg, this.props.clearSelectedItems);
@@ -53,15 +52,11 @@ class BrushMode extends React.Component {
         // TODO: Instead of clearing selection, consider a kind of "draw inside"
         // analogous to how selection works with eraser
         clearSelection(this.props.clearSelectedItems);
-
         // Force the default brush color if fill is MIXED or transparent
         const {fillColor} = this.props.colorState;
         if (fillColor === MIXED || fillColor === null) {
             this.props.onChangeFillColor(BrushMode.DEFAULT_COLOR);
         }
-
-        // TODO: This is temporary until a component that provides the brush size is hooked up
-        this.props.canvas.addEventListener('mousewheel', this.onScroll);
         this.blob.activateTool({
             isEraser: false,
             ...this.props.colorState,
@@ -69,17 +64,8 @@ class BrushMode extends React.Component {
         });
     }
     deactivateTool () {
-        this.props.canvas.removeEventListener('mousewheel', this.onScroll);
         this.blob.deactivateTool();
     }
-    onScroll (event) {
-        if (event.deltaY < 0) {
-            this.props.changeBrushSize(this.props.brushModeState.brushSize + 1);
-        } else if (event.deltaY > 0 && this.props.brushModeState.brushSize > 1) {
-            this.props.changeBrushSize(this.props.brushModeState.brushSize - 1);
-        }
-        return true;
-    }
     render () {
         return (
             <BrushModeComponent
@@ -94,8 +80,6 @@ BrushMode.propTypes = {
     brushModeState: PropTypes.shape({
         brushSize: PropTypes.number.isRequired
     }),
-    canvas: PropTypes.instanceOf(Element).isRequired,
-    changeBrushSize: PropTypes.func.isRequired,
     clearSelectedItems: PropTypes.func.isRequired,
     colorState: PropTypes.shape({
         fillColor: PropTypes.string,
diff --git a/src/containers/eraser-mode.jsx b/src/containers/eraser-mode.jsx
index cd7149df..45412f63 100644
--- a/src/containers/eraser-mode.jsx
+++ b/src/containers/eraser-mode.jsx
@@ -14,8 +14,7 @@ class EraserMode extends React.Component {
         super(props);
         bindAll(this, [
             'activateTool',
-            'deactivateTool',
-            'onScroll'
+            'deactivateTool'
         ]);
         this.blob = new Blobbiness(
             this.props.onUpdateSvg, this.props.clearSelectedItems);
@@ -41,22 +40,11 @@ class EraserMode extends React.Component {
         return nextProps.isEraserModeActive !== this.props.isEraserModeActive;
     }
     activateTool () {
-        this.props.canvas.addEventListener('mousewheel', this.onScroll);
-
         this.blob.activateTool({isEraser: true, ...this.props.eraserModeState});
     }
     deactivateTool () {
-        this.props.canvas.removeEventListener('mousewheel', this.onScroll);
         this.blob.deactivateTool();
     }
-    onScroll (event) {
-        event.preventDefault();
-        if (event.deltaY < 0) {
-            this.props.changeBrushSize(this.props.eraserModeState.brushSize + 1);
-        } else if (event.deltaY > 0 && this.props.eraserModeState.brushSize > 1) {
-            this.props.changeBrushSize(this.props.eraserModeState.brushSize - 1);
-        }
-    }
     render () {
         return (
             <EraserModeComponent
@@ -68,8 +56,6 @@ class EraserMode extends React.Component {
 }
 
 EraserMode.propTypes = {
-    canvas: PropTypes.instanceOf(Element).isRequired,
-    changeBrushSize: PropTypes.func.isRequired,
     clearSelectedItems: PropTypes.func.isRequired,
     eraserModeState: PropTypes.shape({
         brushSize: PropTypes.number.isRequired
diff --git a/src/containers/line-mode.jsx b/src/containers/line-mode.jsx
index 476f329d..5c72c089 100644
--- a/src/containers/line-mode.jsx
+++ b/src/containers/line-mode.jsx
@@ -205,7 +205,6 @@ class LineMode extends React.Component {
         }
     }
     deactivateTool () {
-        this.props.canvas.removeEventListener('mousewheel', this.onScroll);
         this.tool.remove();
         this.tool = null;
         if (this.hitResult) {
@@ -227,7 +226,6 @@ class LineMode extends React.Component {
 }
 
 LineMode.propTypes = {
-    canvas: PropTypes.instanceOf(Element).isRequired,
     clearSelectedItems: PropTypes.func.isRequired,
     colorState: PropTypes.shape({
         fillColor: PropTypes.string,
diff --git a/src/containers/paint-editor.jsx b/src/containers/paint-editor.jsx
index 07a214b2..9156b6e6 100644
--- a/src/containers/paint-editor.jsx
+++ b/src/containers/paint-editor.jsx
@@ -11,6 +11,7 @@ import {performUndo, performRedo, performSnapshot, shouldShowUndo, shouldShowRed
 import {bringToFront, sendBackward, sendToBack, bringForward} from '../helper/order';
 import {groupSelection, ungroupSelection} from '../helper/group';
 import {getSelectedLeafItems} from '../helper/selection';
+import {resetZoom, zoomOnSelection} from '../helper/view';
 
 import Modes from '../modes/modes';
 import {connect} from 'react-redux';
@@ -18,6 +19,9 @@ import bindAll from 'lodash.bindall';
 import paper from '@scratch/paper';
 
 class PaintEditor extends React.Component {
+    static get ZOOM_INCREMENT () {
+        return 0.5;
+    }
     constructor (props) {
         super(props);
         bindAll(this, [
@@ -41,6 +45,11 @@ class PaintEditor extends React.Component {
         document.removeEventListener('keydown', this.props.onKeyPress);
     }
     handleUpdateSvg (skipSnapshot) {
+        // Store the zoom/pan and restore it after snapshotting
+        // TODO Only doing this because snapshotting at zoom/pan makes export wrong
+        const oldZoom = paper.project.view.zoom;
+        const oldCenter = paper.project.view.center.clone();
+        resetZoom();
         // Hide guide layer
         const guideLayer = getGuideLayer();
         const backgroundGuideLayer = getBackgroundGuideLayer();
@@ -60,6 +69,10 @@ class PaintEditor extends React.Component {
         paper.project.addLayer(backgroundGuideLayer);
         backgroundGuideLayer.sendToBack();
         paper.project.addLayer(guideLayer);
+        // Restore old zoom
+        paper.project.view.zoom = oldZoom;
+        paper.project.view.center = oldCenter;
+        paper.project.view.update();
     }
     handleUndo () {
         performUndo(this.props.undoState, this.props.onUndo, this.props.setSelectedItems, this.handleUpdateSvg);
@@ -91,6 +104,15 @@ class PaintEditor extends React.Component {
     canRedo () {
         return shouldShowRedo(this.props.undoState);
     }
+    handleZoomIn () {
+        zoomOnSelection(PaintEditor.ZOOM_INCREMENT);
+    }
+    handleZoomOut () {
+        zoomOnSelection(-PaintEditor.ZOOM_INCREMENT);
+    }
+    handleZoomReset () {
+        resetZoom();
+    }
     render () {
         return (
             <PaintEditorComponent
@@ -111,6 +133,9 @@ class PaintEditor extends React.Component {
                 onUngroup={this.handleUngroup}
                 onUpdateName={this.props.onUpdateName}
                 onUpdateSvg={this.handleUpdateSvg}
+                onZoomIn={this.handleZoomIn}
+                onZoomOut={this.handleZoomOut}
+                onZoomReset={this.handleZoomReset}
             />
         );
     }
diff --git a/src/containers/paper-canvas.jsx b/src/containers/paper-canvas.jsx
index 2d84220f..9534fedf 100644
--- a/src/containers/paper-canvas.jsx
+++ b/src/containers/paper-canvas.jsx
@@ -10,6 +10,8 @@ import {undoSnapshot, clearUndoState} from '../reducers/undo';
 import {isGroup, ungroupItems} from '../helper/group';
 import {setupLayers} from '../helper/layer';
 import {deleteSelection, getSelectedLeafItems} from '../helper/selection';
+import {pan, resetZoom, zoomOnFixedPoint} from '../helper/view';
+
 import {setSelectedItems} from '../reducers/selected-items';
 
 import styles from './paper-canvas.css';
@@ -20,7 +22,8 @@ class PaperCanvas extends React.Component {
         bindAll(this, [
             'setCanvas',
             'importSvg',
-            'handleKeyDown'
+            'handleKeyDown',
+            'handleWheel'
         ]);
     }
     componentDidMount () {
@@ -45,7 +48,14 @@ class PaperCanvas extends React.Component {
         }
         this.props.clearUndo();
         if (newProps.svg) {
+            // Store the zoom/pan and restore it after importing a new SVG
+            const oldZoom = paper.project.view.zoom;
+            const oldCenter = paper.project.view.center.clone();
+            resetZoom();
             this.importSvg(newProps.svg, newProps.rotationCenterX, newProps.rotationCenterY);
+            paper.project.view.zoom = oldZoom;
+            paper.project.view.center = oldCenter;
+            paper.project.view.update();
         }
     }
     componentWillUnmount () {
@@ -84,7 +94,7 @@ class PaperCanvas extends React.Component {
                     item.clipped = false;
                     mask.remove();
                 }
-                
+
                 // Reduce single item nested in groups
                 if (item.children && item.children.length === 1) {
                     item = item.reduce();
@@ -114,6 +124,23 @@ class PaperCanvas extends React.Component {
             this.props.canvasRef(canvas);
         }
     }
+    handleWheel (event) {
+        if (event.metaKey) {
+            // Zoom keeping mouse location fixed
+            const canvasRect = this.canvas.getBoundingClientRect();
+            const offsetX = event.clientX - canvasRect.left;
+            const offsetY = event.clientY - canvasRect.top;
+            const fixedPoint = paper.project.view.viewToProject(
+                new paper.Point(offsetX, offsetY)
+            );
+            zoomOnFixedPoint(-event.deltaY / 100, fixedPoint);
+        } else {
+            const dx = event.deltaX / paper.project.view.zoom;
+            const dy = event.deltaY / paper.project.view.zoom;
+            pan(dx, dy);
+        }
+        event.preventDefault();
+    }
     render () {
         return (
             <canvas
@@ -121,6 +148,7 @@ class PaperCanvas extends React.Component {
                 height="400px"
                 ref={this.setCanvas}
                 width="500px"
+                onWheel={this.handleWheel}
             />
         );
     }
diff --git a/src/helper/view.js b/src/helper/view.js
new file mode 100644
index 00000000..750bbea1
--- /dev/null
+++ b/src/helper/view.js
@@ -0,0 +1,70 @@
+import paper from '@scratch/paper';
+import {getSelectedRootItems} from './selection';
+
+const clampViewBounds = () => {
+    const {left, right, top, bottom} = paper.project.view.bounds;
+    if (left < 0) {
+        paper.project.view.scrollBy(new paper.Point(-left, 0));
+    }
+    if (top < 0) {
+        paper.project.view.scrollBy(new paper.Point(0, -top));
+    }
+    if (bottom > 400) {
+        paper.project.view.scrollBy(new paper.Point(0, 400 - bottom));
+    }
+    if (right > 500) {
+        paper.project.view.scrollBy(new paper.Point(500 - right, 0));
+    }
+};
+
+// Zoom keeping a project-space point fixed.
+// This article was helpful http://matthiasberth.com/tech/stable-zoom-and-pan-in-paperjs
+const zoomOnFixedPoint = (deltaZoom, fixedPoint) => {
+    const {view} = paper.project;
+    const preZoomCenter = view.center;
+    const newZoom = Math.max(1, view.zoom + deltaZoom);
+    const scaling = view.zoom / newZoom;
+    const preZoomOffset = fixedPoint.subtract(preZoomCenter);
+    const postZoomOffset = fixedPoint.subtract(preZoomOffset.multiply(scaling))
+        .subtract(preZoomCenter);
+    view.zoom = newZoom;
+    view.translate(postZoomOffset.multiply(-1));
+    clampViewBounds();
+};
+
+// Zoom keeping the selection center (if any) fixed.
+const zoomOnSelection = deltaZoom => {
+    let fixedPoint;
+    const items = getSelectedRootItems();
+    if (items.length > 0) {
+        let rect = null;
+        for (const item of items) {
+            if (rect) {
+                rect = rect.unite(item.bounds);
+            } else {
+                rect = item.bounds;
+            }
+        }
+        fixedPoint = rect.center;
+    } else {
+        fixedPoint = paper.project.view.center;
+    }
+    zoomOnFixedPoint(deltaZoom, fixedPoint);
+};
+
+const resetZoom = () => {
+    paper.project.view.zoom = 1;
+    clampViewBounds();
+};
+
+const pan = (dx, dy) => {
+    paper.project.view.scrollBy(new paper.Point(dx, dy));
+    clampViewBounds();
+};
+
+export {
+    pan,
+    resetZoom,
+    zoomOnSelection,
+    zoomOnFixedPoint
+};