diff --git a/src/mol-canvas3d/camera.ts b/src/mol-canvas3d/camera.ts
index 553225dc30bd4f29b7aa05aa79ead926cbe7a389..2f6f5781c2266c748c0cb0985fb30658d8ae1f1a 100644
--- a/src/mol-canvas3d/camera.ts
+++ b/src/mol-canvas3d/camera.ts
@@ -127,6 +127,23 @@ class Camera implements ICamera {
         return state;
     }
 
+    getInvariantFocus(target: Vec3, radius: number, up: Vec3, dir: Vec3): Partial<Camera.Snapshot> {
+        const r = Math.max(radius, 0.01);
+        const targetDistance = this.getTargetDistance(r);
+
+        Vec3.copy(this.deltaDirection, dir);
+        Vec3.setMagnitude(this.deltaDirection, this.deltaDirection, targetDistance);
+        Vec3.sub(this.newPosition, target, this.deltaDirection);
+
+        const state = Camera.copySnapshot(Camera.createDefaultSnapshot(), this.state);
+        state.target = Vec3.clone(target);
+        state.radius = r;
+        state.position = Vec3.clone(this.newPosition);
+        Vec3.copy(state.up, up);
+
+        return state;
+    }
+
     focus(target: Vec3, radius: number, durationMs?: number, up?: Vec3, dir?: Vec3) {
         if (radius > 0) {
             this.setState(this.getFocus(target, radius, up, dir), durationMs);
diff --git a/src/mol-canvas3d/canvas3d.ts b/src/mol-canvas3d/canvas3d.ts
index b35321002134afda10e3997870ba5840edc8f5cb..f2f817cdce7cb7c0890dbf089210575d24c3fe12 100644
--- a/src/mol-canvas3d/canvas3d.ts
+++ b/src/mol-canvas3d/canvas3d.ts
@@ -305,7 +305,11 @@ namespace Canvas3D {
             let loci: Loci = EmptyLoci;
             let repr: Representation.Any = Representation.Empty;
             if (pickingId) {
+                const cameraHelperLoci = helper.camera.getLoci(pickingId);
+                if (cameraHelperLoci !== EmptyLoci) return { loci: cameraHelperLoci, repr };
+
                 loci = helper.handle.getLoci(pickingId);
+
                 reprRenderObjects.forEach((_, _repr) => {
                     const _loci = _repr.getLoci(pickingId);
                     if (!isEmptyLoci(_loci)) {
@@ -467,7 +471,7 @@ namespace Canvas3D {
                 const duration = nextCameraResetDuration === undefined ? p.cameraResetDurationMs : nextCameraResetDuration;
                 const focus = camera.getFocus(center, radius);
                 const next = typeof nextCameraResetSnapshot === 'function' ? nextCameraResetSnapshot(scene, camera) : nextCameraResetSnapshot;
-                const snapshot = next ? { ...focus, ...nextCameraResetSnapshot } : focus;
+                const snapshot = next ? { ...focus, ...next } : focus;
                 camera.setState({ ...snapshot, radiusMax: scene.boundingSphere.radius }, duration);
             }
 
diff --git a/src/mol-canvas3d/helper/camera-helper.ts b/src/mol-canvas3d/helper/camera-helper.ts
index 757446edca24303323cf2c5d83c13ccbb2a5e230..2c0d08aecc0804f6ec87780513638483653d1c76 100644
--- a/src/mol-canvas3d/helper/camera-helper.ts
+++ b/src/mol-canvas3d/helper/camera-helper.ts
@@ -4,27 +4,29 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { WebGLContext } from '../../mol-gl/webgl/context';
-import { Scene } from '../../mol-gl/scene';
-import { Camera, ICamera } from '../camera';
-import { MeshBuilder } from '../../mol-geo/geometry/mesh/mesh-builder';
-import { Vec3, Mat4 } from '../../mol-math/linear-algebra';
+import produce from 'immer';
+import { addCylinder } from '../../mol-geo/geometry/mesh/builder/cylinder';
 import { addSphere } from '../../mol-geo/geometry/mesh/builder/sphere';
-import { GraphicsRenderObject } from '../../mol-gl/render-object';
 import { Mesh } from '../../mol-geo/geometry/mesh/mesh';
-import { ColorNames } from '../../mol-util/color/names';
-import { addCylinder } from '../../mol-geo/geometry/mesh/builder/cylinder';
-import { Viewport } from '../camera/util';
+import { MeshBuilder } from '../../mol-geo/geometry/mesh/mesh-builder';
+import { PickingId } from '../../mol-geo/geometry/picking';
+import { GraphicsRenderObject } from '../../mol-gl/render-object';
+import { Scene } from '../../mol-gl/scene';
+import { WebGLContext } from '../../mol-gl/webgl/context';
 import { Sphere3D } from '../../mol-math/geometry';
-import { ParamDefinition as PD } from '../../mol-util/param-definition';
-import produce from 'immer';
+import { Mat4, Vec3 } from '../../mol-math/linear-algebra';
+import { DataLoci, EmptyLoci, Loci } from '../../mol-model/loci';
 import { Shape } from '../../mol-model/shape';
+import { ColorNames } from '../../mol-util/color/names';
+import { ParamDefinition as PD } from '../../mol-util/param-definition';
+import { Camera, ICamera } from '../camera';
+import { Viewport } from '../camera/util';
 
 // TODO add scale line/grid
 
 const AxesParams = {
     ...Mesh.Params,
-    alpha: { ...Mesh.Params.alpha, defaultValue: 0.33 },
+    alpha: { ...Mesh.Params.alpha, defaultValue: 0.51 },
     ignoreLight: { ...Mesh.Params.ignoreLight, defaultValue: true },
     colorX: PD.Color(ColorNames.red, { isEssential: true }),
     colorY: PD.Color(ColorNames.green, { isEssential: true }),
@@ -87,6 +89,12 @@ export class CameraHelper {
         return this.props.axes.name === 'on';
     }
 
+    getLoci(pickingId: PickingId) {
+        const { objectId, groupId, instanceId } = pickingId;
+        if (!this.renderObject || objectId !== this.renderObject.id || groupId === CameraHelperAxis.None) return EmptyLoci;
+        return CameraAxesLoci(this, groupId, instanceId);
+    }
+
     update(camera: ICamera) {
         if (!this.renderObject) return;
 
@@ -102,6 +110,38 @@ export class CameraHelper {
     }
 }
 
+export const enum CameraHelperAxis {
+    None = 0,
+    X,
+    Y,
+    Z,
+    XY,
+    XZ,
+    YZ
+}
+
+function getAxisLabel(axis: number) {
+    switch (axis) {
+        case CameraHelperAxis.X: return 'X Axis';
+        case CameraHelperAxis.Y: return 'Y Axis';
+        case CameraHelperAxis.Z: return 'Z Axis';
+        case CameraHelperAxis.XY: return 'XY Plane';
+        case CameraHelperAxis.XZ: return 'XZ Plane';
+        case CameraHelperAxis.YZ: return 'YZ Plane';
+        default: return 'Axes';
+    }
+}
+
+function CameraAxesLoci(cameraHelper: CameraHelper, groupId: number, instanceId: number) {
+    return DataLoci('camera-axes', cameraHelper, [{ groupId, instanceId }],
+        void 0 /** bounding sphere */,
+        () => getAxisLabel(groupId));
+}
+export type CameraAxesLoci = ReturnType<typeof CameraAxesLoci>
+export function isCameraAxesLoci(x: Loci): x is CameraAxesLoci {
+    return x.kind === 'data-loci' && x.tag === 'camera-axes';
+}
+
 function updateCamera(camera: Camera, viewport: Viewport, viewOffset: Camera.ViewOffset) {
     const { near, far } = camera;
 
@@ -134,27 +174,52 @@ function updateCamera(camera: Camera, viewport: Viewport, viewOffset: Camera.Vie
 
 function createAxesMesh(scale: number, mesh?: Mesh) {
     const state = MeshBuilder.createState(512, 256, mesh);
-    const radius = 0.05 * scale;
+    const radius = 0.075 * scale;
     const x = Vec3.scale(Vec3(), Vec3.unitX, scale);
     const y = Vec3.scale(Vec3(), Vec3.unitY, scale);
     const z = Vec3.scale(Vec3(), Vec3.unitZ, scale);
     const cylinderProps = { radiusTop: radius, radiusBottom: radius, radialSegments: 32 };
 
-    state.currentGroup = 0;
+    state.currentGroup = CameraHelperAxis.None;
     addSphere(state, Vec3.origin, radius, 2);
 
-    state.currentGroup = 1;
+    state.currentGroup = CameraHelperAxis.X;
     addSphere(state, x, radius, 2);
     addCylinder(state, Vec3.origin, x, 1, cylinderProps);
 
-    state.currentGroup = 2;
+    state.currentGroup = CameraHelperAxis.Y;
     addSphere(state, y, radius, 2);
     addCylinder(state, Vec3.origin, y, 1, cylinderProps);
 
-    state.currentGroup = 3;
+    state.currentGroup = CameraHelperAxis.Z;
     addSphere(state, z, radius, 2);
     addCylinder(state, Vec3.origin, z, 1, cylinderProps);
 
+    Vec3.scale(x, x, 0.5);
+    Vec3.scale(y, y, 0.5);
+    Vec3.scale(z, z, 0.5);
+
+    state.currentGroup = CameraHelperAxis.XY;
+    MeshBuilder.addTriangle(state, Vec3.origin, x, y);
+    MeshBuilder.addTriangle(state, Vec3.origin, y, x);
+    const xy = Vec3.add(Vec3(), x, y);
+    MeshBuilder.addTriangle(state, xy, x, y);
+    MeshBuilder.addTriangle(state, xy, y, x);
+
+    state.currentGroup = CameraHelperAxis.XZ;
+    MeshBuilder.addTriangle(state, Vec3.origin, x, z);
+    MeshBuilder.addTriangle(state, Vec3.origin, z, x);
+    const xz = Vec3.add(Vec3(), x, z);
+    MeshBuilder.addTriangle(state, xz, x, z);
+    MeshBuilder.addTriangle(state, xz, z, x);
+
+    state.currentGroup = CameraHelperAxis.YZ;
+    MeshBuilder.addTriangle(state, Vec3.origin, y, z);
+    MeshBuilder.addTriangle(state, Vec3.origin, z, y);
+    const yz = Vec3.add(Vec3(), y, z);
+    MeshBuilder.addTriangle(state, yz, y, z);
+    MeshBuilder.addTriangle(state, yz, z, y);
+
     return MeshBuilder.getMesh(state);
 }
 
diff --git a/src/mol-canvas3d/passes/pick.ts b/src/mol-canvas3d/passes/pick.ts
index a694af7127cef0925f64cbc21591e34656845722..946f93ef09583279185a4367785fd0001faa3326 100644
--- a/src/mol-canvas3d/passes/pick.ts
+++ b/src/mol-canvas3d/passes/pick.ts
@@ -66,14 +66,20 @@ export class PickPass {
     private renderVariant(renderer: Renderer, camera: ICamera, scene: Scene, helper: Helper, variant: GraphicsRenderVariant) {
         const depth = this.drawPass.depthTexturePrimitives;
         renderer.clear(false);
+
+        renderer.update(camera);
         renderer.renderPick(scene.primitives, camera, variant, null);
         renderer.renderPick(scene.volumes, camera, variant, depth);
         renderer.renderPick(helper.handle.scene, camera, variant, null);
+
+        if (helper.camera.isEnabled) {
+            helper.camera.update(camera);
+            renderer.update(helper.camera.camera);
+            renderer.renderPick(helper.camera.scene, helper.camera.camera, variant, null);
+        }
     }
 
     render(renderer: Renderer, camera: ICamera, scene: Scene, helper: Helper) {
-        renderer.update(camera);
-
         this.objectPickTarget.bind();
         this.renderVariant(renderer, camera, scene, helper, 'pickObject');
 
diff --git a/src/mol-math/linear-algebra/3d/vec3.ts b/src/mol-math/linear-algebra/3d/vec3.ts
index 4e0cd46cf29e8f6ae9bf3d82b14f8c1287451795..f6d4774ff9b316eb7df5359a3845db96a630c10d 100644
--- a/src/mol-math/linear-algebra/3d/vec3.ts
+++ b/src/mol-math/linear-algebra/3d/vec3.ts
@@ -590,6 +590,9 @@ namespace Vec3 {
     export const unitX: ReadonlyVec3 = create(1, 0, 0);
     export const unitY: ReadonlyVec3 = create(0, 1, 0);
     export const unitZ: ReadonlyVec3 = create(0, 0, 1);
+    export const negUnitX: ReadonlyVec3 = create(-1, 0, 0);
+    export const negUnitY: ReadonlyVec3 = create(0, -1, 0);
+    export const negUnitZ: ReadonlyVec3 = create(0, 0, -1);
 }
 
 export { Vec3 };
\ No newline at end of file
diff --git a/src/mol-model/loci.ts b/src/mol-model/loci.ts
index 6a808bbe176ec16e07d7444ad8998ddb8d94d4b9..87b7d63608f53483842fd21bb76baf36807b784a 100644
--- a/src/mol-model/loci.ts
+++ b/src/mol-model/loci.ts
@@ -37,9 +37,10 @@ export interface DataLoci<T = unknown, E = unknown> {
     readonly kind: 'data-loci',
     readonly tag: string
     readonly data: T,
-    readonly elements: ReadonlyArray<E>
+    readonly elements: ReadonlyArray<E>,
 
-    getBoundingSphere(boundingSphere: Sphere3D): Sphere3D
+    /** if undefined, won't zoom */
+    getBoundingSphere?(boundingSphere: Sphere3D): Sphere3D
     getLabel(): string
 }
 export function isDataLoci(x?: Loci): x is DataLoci {
@@ -159,7 +160,7 @@ namespace Loci {
         } else if (loci.kind === 'group-loci') {
             return ShapeGroup.getBoundingSphere(loci, boundingSphere);
         } else if (loci.kind === 'data-loci') {
-            return loci.getBoundingSphere(boundingSphere);
+            return loci.getBoundingSphere?.(boundingSphere);
         } else if (loci.kind === 'volume-loci') {
             return Volume.getBoundingSphere(loci.volume, boundingSphere);
         } else if (loci.kind === 'isosurface-loci') {
diff --git a/src/mol-plugin/behavior/dynamic/camera.ts b/src/mol-plugin/behavior/dynamic/camera.ts
index 2d114aeaf0b3bc82ddeccd94d3137ef5d81a9a85..eb38500d4bb1ea404c794a34513d13b03d0e06f9 100644
--- a/src/mol-plugin/behavior/dynamic/camera.ts
+++ b/src/mol-plugin/behavior/dynamic/camera.ts
@@ -11,6 +11,8 @@ import { PluginBehavior } from '../behavior';
 import { ButtonsType, ModifiersKeys } from '../../../mol-util/input/input-observer';
 import { Binding } from '../../../mol-util/binding';
 import { PluginCommands } from '../../commands';
+import { CameraHelperAxis, isCameraAxesLoci } from '../../../mol-canvas3d/helper/camera-helper';
+import { Vec3 } from '../../../mol-math/linear-algebra';
 
 const B = ButtonsType;
 const M = ModifiersKeys;
@@ -62,4 +64,58 @@ export const FocusLoci = PluginBehavior.create<FocusLociProps>({
     },
     params: () => FocusLociParams,
     display: { name: 'Camera Focus Loci on Canvas' }
+});
+
+export const CameraAxisHelper = PluginBehavior.create<{}>({
+    name: 'camera-axis-helper',
+    category: 'interaction',
+    ctor: class extends PluginBehavior.Handler<{}> {
+        register(): void {
+
+            let lastPlane = CameraHelperAxis.None;
+            let state = 0;
+
+            this.subscribeObservable(this.ctx.behaviors.interaction.click, ({ current }) => {
+                if (!this.ctx.canvas3d || !isCameraAxesLoci(current.loci)) return;
+
+                const axis = current.loci.elements[0].groupId;
+                if (axis === CameraHelperAxis.None) {
+                    lastPlane = CameraHelperAxis.None;
+                    state = 0;
+                    return;
+                } else if (axis >= CameraHelperAxis.X && axis <= CameraHelperAxis.Z) {
+                    lastPlane = CameraHelperAxis.None;
+                    state = 0;
+                    const up = Vec3();
+                    up[axis - 1] = 1;
+                    this.ctx.canvas3d.requestCameraReset({ snapshot: { up } });
+                } else {
+                    if (lastPlane === axis) {
+                        state = (state + 1) % 2;
+                    } else {
+                        lastPlane = axis;
+                        state = 0;
+                    }
+
+                    let up: Vec3, dir: Vec3;
+                    if (axis === CameraHelperAxis.XY) {
+                        up = state ? Vec3.unitX : Vec3.unitY;
+                        dir = Vec3.negUnitZ;
+                    } else if (axis === CameraHelperAxis.XZ) {
+                        up = state ? Vec3.unitX : Vec3.unitZ;
+                        dir = Vec3.negUnitY;
+                    } else {
+                        up = state ? Vec3.unitY : Vec3.unitZ;
+                        dir = Vec3.negUnitX;
+                    }
+
+                    this.ctx.canvas3d.requestCameraReset({
+                        snapshot: (scene, camera) => camera.getInvariantFocus(scene.boundingSphereVisible.center, scene.boundingSphereVisible.radius, up, dir)
+                    });
+                }
+            });
+        }
+    },
+    params: () => ({}),
+    display: { name: 'Camera Axis Helper' }
 });
\ No newline at end of file
diff --git a/src/mol-plugin/spec.ts b/src/mol-plugin/spec.ts
index 0ea667b5e0289f90ecf776407df52d11147c739e..67c9c5885101769154b7dd4d4c653ccb9e428235 100644
--- a/src/mol-plugin/spec.ts
+++ b/src/mol-plugin/spec.ts
@@ -111,6 +111,7 @@ export const DefaultPluginSpec = (): PluginSpec => ({
         PluginSpec.Behavior(PluginBehaviors.Representation.DefaultLociLabelProvider),
         PluginSpec.Behavior(PluginBehaviors.Representation.FocusLoci),
         PluginSpec.Behavior(PluginBehaviors.Camera.FocusLoci),
+        PluginSpec.Behavior(PluginBehaviors.Camera.CameraAxisHelper),
         PluginSpec.Behavior(StructureFocusRepresentation),
 
         PluginSpec.Behavior(PluginBehaviors.CustomProps.StructureInfo),