diff --git a/package-lock.json b/package-lock.json
index 68fb17365403b32de0fe116d3096507f569d8d48..7096688c7817e9f74c7e5e0aa2870a7fa7b00616 100644
Binary files a/package-lock.json and b/package-lock.json differ
diff --git a/package.json b/package.json
index 9e02ef7ae4361e444b771ee6c1c5b859bc848763..da05723d224acdbb60d28c43967eba40a4f25fda 100644
--- a/package.json
+++ b/package.json
@@ -63,29 +63,29 @@
   "devDependencies": {
     "benchmark": "^2.1.4",
     "circular-dependency-plugin": "^5.0.2",
-    "concurrently": "^4.1.0",
+    "concurrently": "^4.1.1",
     "cpx": "^1.5.0",
-    "css-loader": "^3.0.0",
+    "css-loader": "^3.1.0",
     "extra-watch-webpack-plugin": "^1.0.3",
-    "file-loader": "^4.0.0",
-    "fs-extra": "^8.0.1",
+    "file-loader": "^4.1.0",
+    "fs-extra": "^8.1.0",
     "graphql-code-generator": "^0.18.2",
     "graphql-codegen-time": "^0.18.2",
     "graphql-codegen-typescript-template": "^0.18.2",
     "jest": "^24.8.0",
     "jest-raw-loader": "^1.0.1",
-    "mini-css-extract-plugin": "^0.7.0",
+    "mini-css-extract-plugin": "^0.8.0",
     "node-sass": "^4.12.0",
-    "raw-loader": "^3.0.0",
+    "raw-loader": "^3.1.0",
     "resolve-url-loader": "^3.1.0",
     "sass-loader": "^7.1.0",
-    "simple-git": "^1.116.0",
+    "simple-git": "^1.122.0",
     "style-loader": "^0.23.1",
     "ts-jest": "^24.0.2",
     "tslint": "^5.18.0",
-    "typescript": "^3.5.2",
-    "webpack": "^4.35.0",
-    "webpack-cli": "^3.3.4"
+    "typescript": "^3.5.3",
+    "webpack": "^4.36.1",
+    "webpack-cli": "^3.3.6"
   },
   "dependencies": {
     "@types/argparse": "^1.0.36",
@@ -93,22 +93,22 @@
     "@types/compression": "0.0.36",
     "@types/express": "^4.17.0",
     "@types/jest": "^24.0.15",
-    "@types/node": "^12.0.10",
+    "@types/node": "^12.6.8",
     "@types/node-fetch": "^2.3.7",
-    "@types/react": "^16.8.22",
+    "@types/react": "^16.8.23",
     "@types/react-dom": "^16.8.4",
     "@types/swagger-ui-dist": "3.0.0",
-    "@types/webgl2": "0.0.4",
+    "@types/webgl2": "0.0.5",
     "argparse": "^1.0.10",
     "compression": "^1.7.4",
     "express": "^4.17.1",
-    "graphql": "^14.3.1",
+    "graphql": "^14.4.2",
     "immutable": "^3.8.2",
     "node-fetch": "^2.6.0",
     "react": "^16.8.6",
     "react-dom": "^16.8.6",
     "rxjs": "^6.5.2",
-    "swagger-ui-dist": "^3.22.3",
+    "swagger-ui-dist": "^3.23.1",
     "util.promisify": "^1.0.0",
     "xhr2": "^0.2.0"
   }
diff --git a/src/examples/proteopedia-wrapper/changelog.md b/src/examples/proteopedia-wrapper/changelog.md
index b9d8bd109b9c5e063795fe46f0cc3d03297e06aa..e8e8cb5a36a6716a6b0c00b497000109ceac6e5b 100644
--- a/src/examples/proteopedia-wrapper/changelog.md
+++ b/src/examples/proteopedia-wrapper/changelog.md
@@ -1,3 +1,7 @@
+== v3.3 ==
+
+* Camera Clipping.
+
 == v3.2 ==
 
 * Fixed assembly loading.
diff --git a/src/examples/proteopedia-wrapper/index.html b/src/examples/proteopedia-wrapper/index.html
index 632496100c0c3df2f7d6238dbfbd8f84da98f45f..b7383173937cd7c1f8c700611fa7a875c7c52031 100644
--- a/src/examples/proteopedia-wrapper/index.html
+++ b/src/examples/proteopedia-wrapper/index.html
@@ -130,7 +130,11 @@
             addSeparator();
 
             addHeader('Camera');
-            addControl('Toggle Spin', () => PluginWrapper.toggleSpin());
+            addControl('Reset Position', () => PluginWrapper.camera.resetPosition());
+            addControl('Toggle Spin', () => PluginWrapper.camera.toggleSpin());
+            // Same as "wheel icon" and Viewport options
+            addControl('Clip', () => PluginWrapper.viewport.setSettings({ clip: [33, 66] }));
+            addControl('Reset Clip', () => PluginWrapper.viewport.setSettings({ clip: [1, 100] }));
             
             addSeparator();
 
diff --git a/src/examples/proteopedia-wrapper/index.ts b/src/examples/proteopedia-wrapper/index.ts
index 750503e5ecad5a08eaed151f6e5ec3b963c7b1c7..1aa4f7c2a3bb521618a28da4e80d80c592546267 100644
--- a/src/examples/proteopedia-wrapper/index.ts
+++ b/src/examples/proteopedia-wrapper/index.ts
@@ -29,6 +29,7 @@ import { BuiltInSizeThemes } from '../../mol-theme/size';
 import { ColorNames } from '../../mol-util/color/tables';
 import { InitVolumeStreaming, CreateVolumeStreamingInfo } from '../../mol-plugin/behavior/dynamic/volume-streaming/transformers';
 import { ParamDefinition } from '../../mol-util/param-definition';
+import { DefaultCanvas3DParams, Canvas3DProps } from '../../mol-canvas3d/canvas3d';
 // import { Vec3 } from 'mol-math/linear-algebra';
 // import { ParamDefinition } from 'mol-util/param-definition';
 // import { Text } from 'mol-geo/geometry/text/text';
@@ -36,7 +37,7 @@ require('../../mol-plugin/skin/light.scss')
 
 class MolStarProteopediaWrapper {
     static VERSION_MAJOR = 3;
-    static VERSION_MINOR = 2;
+    static VERSION_MINOR = 3;
 
     private _ev = RxEventHelper.create();
 
@@ -241,6 +242,38 @@ class MolStarProteopediaWrapper {
         if (!spinning) PluginCommands.Camera.Reset.dispatch(this.plugin, { });
     }
 
+    viewport = {
+        setSettings: (settings?: Canvas3DProps) => {
+            PluginCommands.Canvas3D.SetSettings.dispatch(this.plugin, {
+                settings: settings || DefaultCanvas3DParams
+            });
+        }
+    };
+
+    camera = {
+        toggleSpin: () => this.toggleSpin(),
+        resetPosition: () => PluginCommands.Camera.Reset.dispatch(this.plugin, { }),
+        // setClip: (options?: { distance?: number, near?: number, far?: number }) => {
+        //     if (!options) {
+        //         PluginCommands.Canvas3D.SetSettings.dispatch(this.plugin, {
+        //             settings: {
+        //                 cameraClipDistance: DefaultCanvas3DParams.cameraClipDistance,
+        //                 clip: DefaultCanvas3DParams.clip
+        //             }
+        //         });
+        //         return;
+        //     }
+
+        //     options = options || { };
+        //     const props = this.plugin.canvas3d.props;
+        //     const clipNear = typeof options.near === 'undefined' ? props.clip[0] : options.near;
+        //     const clipFar = typeof options.far === 'undefined' ? props.clip[1] : options.far;
+        //     PluginCommands.Canvas3D.SetSettings.dispatch(this.plugin, {
+        //         settings: { cameraClipDistance: options.distance, clip: [clipNear, clipFar] }
+        //     });
+        // }
+    }
+
     animate = {
         modelIndex: {
             maxFPS: 8,
diff --git a/src/mol-canvas3d/camera.ts b/src/mol-canvas3d/camera.ts
index 64fc8773c80d89cedfff633a3d20a75571ea83a3..43b390255697572aa8cc33efb32a9ddb2c39a195 100644
--- a/src/mol-canvas3d/camera.ts
+++ b/src/mol-canvas3d/camera.ts
@@ -90,25 +90,30 @@ class Camera implements Object3D {
         return ret;
     }
 
-    getFocus(target: Vec3, radius: number): Partial<Camera.Snapshot> {
+    getFocus(target: Vec3, radius: number, dir?: Vec3): Partial<Camera.Snapshot> {
         const fov = this.state.fov
         const { width, height } = this.viewport
         const aspect = width / height
         const aspectFactor = (height < width ? 1 : aspect)
         const currentDistance = Vec3.distance(this.state.position, target)
         const targetDistance = Math.abs((radius / aspectFactor) / Math.sin(fov / 2))
+
         const deltaDistance = Math.abs(currentDistance - targetDistance)
 
-        Vec3.sub(this.deltaDirection, this.state.position, target)
-        Vec3.setMagnitude(this.deltaDirection, this.state.direction, deltaDistance)
-        if (currentDistance < targetDistance) Vec3.negate(this.deltaDirection, this.deltaDirection)
-        Vec3.add(this.newPosition, this.state.position, this.deltaDirection)
+        if (dir) {
+            Vec3.setMagnitude(this.deltaDirection, dir, targetDistance)
+            Vec3.add(this.newPosition, target, this.deltaDirection)
+        } else {
+            Vec3.setMagnitude(this.deltaDirection, this.state.direction, deltaDistance)
+            if (currentDistance < targetDistance) Vec3.negate(this.deltaDirection, this.deltaDirection)
+            Vec3.add(this.newPosition, this.state.position, this.deltaDirection)
+        }
 
         return { target, position: Vec3.clone(this.newPosition) };
     }
 
-    focus(target: Vec3, radius: number) {
-        if (radius > 0) this.setState(this.getFocus(target, radius));
+    focus(target: Vec3, radius: number, dir?: Vec3) {
+        if (radius > 0) this.setState(this.getFocus(target, radius, dir));
     }
 
     // lookAt(target: Vec3) {
diff --git a/src/mol-canvas3d/canvas3d.ts b/src/mol-canvas3d/canvas3d.ts
index 8805de61b50af90bcc8c3819392338ab7e59e997..3ac440ca0d2214305cf0578fe580c3aa20c63059 100644
--- a/src/mol-canvas3d/canvas3d.ts
+++ b/src/mol-canvas3d/canvas3d.ts
@@ -47,6 +47,7 @@ export const Canvas3DParams = {
     trackball: PD.Group(TrackballControlsParams),
     debug: PD.Group(DebugHelperParams)
 }
+export const DefaultCanvas3DParams = PD.getDefaultValues(Canvas3DParams);
 export type Canvas3DProps = PD.Values<typeof Canvas3DParams>
 
 export { Canvas3D }
@@ -106,7 +107,7 @@ namespace Canvas3D {
     }
 
     export function create(gl: GLRenderingContext, input: InputObserver, props: Partial<Canvas3DProps> = {}, runTask = DefaultRunTask): Canvas3D {
-        const p = { ...PD.getDefaultValues(Canvas3DParams), ...props }
+        const p = { ...DefaultCanvas3DParams, ...props }
 
         const reprRenderObjects = new Map<Representation.Any, Set<GraphicsRenderObject>>()
         const reprUpdatedSubscriptions = new Map<Representation.Any, Subscription>()
@@ -351,11 +352,12 @@ namespace Canvas3D {
             getLoci,
 
             handleResize,
-            resetCamera: () => {
+            resetCamera: (dir?: Vec3) => {
                 if (scene.isCommiting) {
+                    // TODO handle `dir`
                     cameraResetRequested = true
                 } else {
-                    camera.focus(scene.boundingSphere.center, scene.boundingSphere.radius)
+                    camera.focus(scene.boundingSphere.center, scene.boundingSphere.radius, dir)
                     requestDraw(true);
                 }
             },