diff --git a/src/mol-canvas3d/canvas3d.ts b/src/mol-canvas3d/canvas3d.ts
index cc5866ae3e7e702f134d72463e4b3d72c7a6cba3..8e2016f1201826b191aa685b34b060bfb12666f4 100644
--- a/src/mol-canvas3d/canvas3d.ts
+++ b/src/mol-canvas3d/canvas3d.ts
@@ -27,7 +27,7 @@ import { Loci, EmptyLoci, isEmptyLoci } from 'mol-model/loci';
 import { Color } from 'mol-util/color';
 import { Camera } from './camera';
 import { ParamDefinition as PD } from 'mol-util/param-definition';
-import { BoundingSphereHelper } from './helper/bounding-sphere-helper';
+import { BoundingSphereHelper, DebugHelperParams } from './helper/bounding-sphere-helper';
 
 export const Canvas3DParams = {
     // TODO: FPS cap?
@@ -37,9 +37,7 @@ export const Canvas3DParams = {
     clip: PD.Interval([1, 100], { min: 1, max: 100, step: 1 }),
     fog: PD.Interval([50, 100], { min: 1, max: 100, step: 1 }),
     pickingAlphaThreshold: PD.Numeric(0.5, { min: 0.0, max: 1.0, step: 0.01 }, { description: 'The minimum opacity value needed for an object to be pickable.' }),
-    debug: PD.Group({
-        showBoundingSpheres: PD.Boolean(false, { description: 'Show bounding spheres of render objects.' }),
-    })
+    debug: PD.Group(DebugHelperParams)
 }
 export type Canvas3DParams = PD.Values<typeof Canvas3DParams>
 
@@ -48,9 +46,6 @@ export { Canvas3D }
 interface Canvas3D {
     readonly webgl: WebGLContext,
 
-    hide: (repr: Representation.Any) => void
-    show: (repr: Representation.Any) => void
-
     add: (repr: Representation.Any) => void
     remove: (repr: Representation.Any) => void
     update: () => void
@@ -128,7 +123,7 @@ namespace Canvas3D {
         let drawPending = false
         let lastRenderTime = -1
 
-        const boundingSphereHelper = new BoundingSphereHelper(scene, p.debug.showBoundingSpheres)
+        const debugHelper = new BoundingSphereHelper(webgl, scene, p.debug)
 
         function getLoci(pickingId: PickingId) {
             let loci: Loci = EmptyLoci
@@ -192,16 +187,24 @@ namespace Canvas3D {
                 switch (variant) {
                     case 'pick':
                         objectPickTarget.bind();
+                        renderer.clear()
                         renderer.render(scene, 'pickObject');
                         instancePickTarget.bind();
+                        renderer.clear()
                         renderer.render(scene, 'pickInstance');
                         groupPickTarget.bind();
+                        renderer.clear()
                         renderer.render(scene, 'pickGroup');
                         break;
                     case 'draw':
                         webgl.unbindFramebuffer();
                         renderer.setViewport(0, 0, canvas.width, canvas.height);
+                        renderer.clear()
                         renderer.render(scene, variant);
+                        if (debugHelper.isEnabled) {
+                            debugHelper.syncVisibility()
+                            renderer.render(debugHelper.scene, 'draw')
+                        }
                         lastRenderTime = now()
                         pickDirty = true
                         break;
@@ -298,8 +301,8 @@ namespace Canvas3D {
             }
             reprRenderObjects.set(repr, newRO)
             reprCount.next(reprRenderObjects.size)
-            boundingSphereHelper.update()
             scene.update()
+            if (debugHelper.isEnabled) debugHelper.update()
             isUpdating = false
             requestDraw(true)
         }
@@ -309,15 +312,6 @@ namespace Canvas3D {
         return {
             webgl,
 
-            hide: (repr: Representation.Any) => {
-                const renderObjectSet = reprRenderObjects.get(repr)
-                if (renderObjectSet) renderObjectSet.forEach(o => o.state.visible = false)
-            },
-            show: (repr: Representation.Any) => {
-                const renderObjectSet = reprRenderObjects.get(repr)
-                if (renderObjectSet) renderObjectSet.forEach(o => o.state.visible = true)
-            },
-
             add: (repr: Representation.Any) => {
                 add(repr)
                 reprUpdatedSubscriptions.set(repr, repr.updated.subscribe(_ => {
@@ -335,8 +329,8 @@ namespace Canvas3D {
                     renderObjects.forEach(o => scene.remove(o))
                     reprRenderObjects.delete(repr)
                     reprCount.next(reprRenderObjects.size)
-                    boundingSphereHelper.update()
                     scene.update()
+                    if (debugHelper.isEnabled) debugHelper.update()
                     isUpdating = false
                     requestDraw(true)
                 }
@@ -345,6 +339,7 @@ namespace Canvas3D {
             clear: () => {
                 reprRenderObjects.clear()
                 scene.clear()
+                debugHelper.clear()
             },
 
             // draw,
@@ -387,8 +382,8 @@ namespace Canvas3D {
                 if (props.pickingAlphaThreshold !== undefined && props.pickingAlphaThreshold !== renderer.props.pickingAlphaThreshold) {
                     renderer.setPickingAlphaThreshold(props.pickingAlphaThreshold)
                 }
-                if (props.debug && props.debug.showBoundingSpheres !== undefined) {
-                    boundingSphereHelper.visible = props.debug.showBoundingSpheres
+                if (props.debug) {
+                    debugHelper.setProps(props.debug)
                 }
                 requestDraw(true)
             },
@@ -400,9 +395,7 @@ namespace Canvas3D {
                     clip: p.clip,
                     fog: p.fog,
                     pickingAlphaThreshold: renderer.props.pickingAlphaThreshold,
-                    debug: {
-                        showBoundingSpheres: boundingSphereHelper.visible
-                    }
+                    debug: { ...debugHelper.props }
                 }
             },
             get input() {
@@ -413,6 +406,7 @@ namespace Canvas3D {
             },
             dispose: () => {
                 scene.clear()
+                debugHelper.clear()
                 input.dispose()
                 controls.dispose()
                 renderer.dispose()
diff --git a/src/mol-canvas3d/helper/bounding-sphere-helper.ts b/src/mol-canvas3d/helper/bounding-sphere-helper.ts
index 8bf47ddb6d76dc67d5b0166ab214700adab7fe5c..84bba56ef3442a9043305e96e9dbfc7ab4b43aea 100644
--- a/src/mol-canvas3d/helper/bounding-sphere-helper.ts
+++ b/src/mol-canvas3d/helper/bounding-sphere-helper.ts
@@ -4,39 +4,105 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { createMeshRenderObject, MeshRenderObject } from 'mol-gl/render-object'
+import { createMeshRenderObject, RenderObject } from 'mol-gl/render-object'
 import { MeshBuilder } from 'mol-geo/geometry/mesh/mesh-builder';
 import { addSphere } from 'mol-geo/geometry/mesh/builder/sphere';
 import { Mesh } from 'mol-geo/geometry/mesh/mesh';
-import { Geometry } from 'mol-geo/geometry/geometry';
-import { ValueCell } from 'mol-util';
+import { ParamDefinition as PD } from 'mol-util/param-definition';
 import Scene from 'mol-gl/scene';
+import { WebGLContext } from 'mol-gl/webgl/context';
+import { Sphere3D } from 'mol-math/geometry';
+
+export const DebugHelperParams = {
+    sceneBoundingSpheres: PD.Boolean(false, { description: 'Show scene bounding spheres.' }),
+    objectBoundingSpheres: PD.Boolean(false, { description: 'Show bounding spheres of render objects.' }),
+}
+export type DebugHelperParams = typeof DebugHelperParams
+export type DebugHelperProps = PD.Values<DebugHelperParams>
+
+type BoundingSphereData = { boundingSphere: Sphere3D, renderObject: RenderObject }
+
+// TODO per-object-transform bounding spheres
 
 export class BoundingSphereHelper {
-    private mesh: Mesh
-    private renderObject: MeshRenderObject
+    readonly scene: Scene
+    private readonly parent: Scene
+    private _props: DebugHelperProps
+    private objectsData = new Map<RenderObject, BoundingSphereData>()
+    private sceneData: BoundingSphereData | undefined
 
-    constructor(private scene: Scene, visible: boolean) {
-        this.mesh = MeshBuilder.getMesh(MeshBuilder.createState(1024, 512))
-        const values = Mesh.createValuesSimple(this.mesh, { alpha: 0.1, doubleSided: false })
-        this.renderObject = createMeshRenderObject(values, { visible, pickable: false, opaque: false })
-        scene.add(this.renderObject)
+    constructor(ctx: WebGLContext, parent: Scene, props: Partial<DebugHelperProps>) {
+        this.scene = Scene.create(ctx)
+        this.parent = parent
+        this._props = { ...PD.getDefaultValues(DebugHelperParams), ...props }
     }
 
     update() {
-        const builderState = MeshBuilder.createState(1024, 512, this.mesh)
-        if (this.scene.boundingSphere.radius) {
-            addSphere(builderState, this.scene.boundingSphere.center, this.scene.boundingSphere.radius, 2)
-        }
-        this.scene.forEach(r => {
-            if (r.boundingSphere.radius) {
-                addSphere(builderState, r.boundingSphere.center, r.boundingSphere.radius, 2)
+        const newSceneData = updateBoundingSphereData(this.scene, this.parent.boundingSphere, this.sceneData)
+        if (newSceneData) this.sceneData = newSceneData
+
+        const oldRO = new Set<RenderObject>()
+        this.parent.forEach((r, ro) => {
+            let objectData = this.objectsData.get(ro)
+            const newObjectData = updateBoundingSphereData(this.scene, r.boundingSphere, objectData)
+            if (newObjectData) this.objectsData.set(ro, newObjectData)
+            oldRO.delete(ro)
+        })
+        oldRO.forEach(ro => {
+            const objectData = this.objectsData.get(ro)
+            if (objectData) {
+                this.scene.remove(objectData.renderObject)
+                this.objectsData.delete(ro)
             }
         })
-        this.mesh = MeshBuilder.getMesh(builderState)
-        ValueCell.update(this.renderObject.values.drawCount, Geometry.getDrawCount(this.mesh))
     }
 
-    get visible() { return this.renderObject.state.visible }
-    set visible(value: boolean) { this.renderObject.state.visible = value }
+    syncVisibility() {
+        if(this.sceneData) {
+            this.sceneData.renderObject.state.visible = this._props.sceneBoundingSpheres
+        }
+
+        this.parent.forEach((_, ro) => {
+            const objectData = this.objectsData.get(ro)
+            if (objectData) objectData.renderObject.state.visible = ro.state.visible && this._props.objectBoundingSpheres
+            else console.error('expected to have debug render object')
+        })
+    }
+
+    clear() {
+        this.sceneData = undefined
+        this.objectsData.clear()
+        this.scene.clear()
+    }
+
+    get isEnabled() {
+        return this._props.sceneBoundingSpheres || this._props.objectBoundingSpheres
+    }
+    get props() { return this._props as Readonly<DebugHelperProps> }
+
+    setProps (props: Partial<DebugHelperProps>) {
+        Object.assign(this._props, props)
+        if (this.isEnabled) this.update()
+    }
+}
+
+function updateBoundingSphereData(scene: Scene, boundingSphere: Sphere3D, data?: BoundingSphereData) {
+    if (!data || !Sphere3D.exactEquals(data.boundingSphere, boundingSphere)) {
+        if (data) scene.remove(data.renderObject)
+        const renderObject = createBoundingSphereRenderObject(boundingSphere)
+        scene.add(renderObject)
+        return { boundingSphere, renderObject }
+    }
+}
+
+function createBoundingSphereRenderObject(boundingSphere: Sphere3D) {
+    const builderState = MeshBuilder.createState(1024, 512)
+    if (boundingSphere.radius) {
+        addSphere(builderState, boundingSphere.center, boundingSphere.radius, 2)
+    } else if (isNaN(boundingSphere.radius)) {
+        console.warn('boundingSphere.radius is NaN')
+    }
+    const mesh = MeshBuilder.getMesh(builderState)
+    const values = Mesh.createValuesSimple(mesh, { alpha: 0.1, doubleSided: false })
+    return createMeshRenderObject(values, { visible: true, pickable: false, opaque: false })
 }
\ No newline at end of file
diff --git a/src/mol-gl/renderer.ts b/src/mol-gl/renderer.ts
index 691c4ac5e28b089d43348b4b17229bcbd7102ce7..3bf307fc22f671e66f6a67fb9af012c147594b82 100644
--- a/src/mol-gl/renderer.ts
+++ b/src/mol-gl/renderer.ts
@@ -36,6 +36,7 @@ interface Renderer {
     readonly stats: RendererStats
     readonly props: RendererProps
 
+    clear: () => void
     render: (scene: Scene, variant: RenderVariant) => void
     setViewport: (x: number, y: number, width: number, height: number) => void
     setClearColor: (color: Color) => void
@@ -162,9 +163,6 @@ namespace Renderer {
 
             currentProgramId = -1
 
-            gl.depthMask(true)
-            gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
-
             gl.disable(gl.BLEND)
             gl.enable(gl.DEPTH_TEST)
             scene.eachOpaque((r) => renderObject(r, variant))
@@ -177,6 +175,10 @@ namespace Renderer {
         }
 
         return {
+            clear: () => {
+                gl.depthMask(true)
+                gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
+            },
             render,
 
             setClearColor,
diff --git a/src/mol-gl/scene.ts b/src/mol-gl/scene.ts
index 941cf4c56c6392a049709425cb18b90491687004..575bc56cb4480d4accc860212aca9d04cca9b57f 100644
--- a/src/mol-gl/scene.ts
+++ b/src/mol-gl/scene.ts
@@ -111,14 +111,10 @@ namespace Scene {
                 renderableMap.forEach(callbackFn)
             },
             eachOpaque: (callbackFn: (value: Renderable<any>, key: RenderObject) => void) => {
-                renderableMap.forEach((r, o) => {
-                    if (r.state.opaque) callbackFn(r, o)
-                })
+                renderableMap.forEach((r, o) => { if (r.state.opaque) callbackFn(r, o) })
             },
             eachTransparent: (callbackFn: (value: Renderable<any>, key: RenderObject) => void) => {
-                renderableMap.forEach((r, o) => {
-                    if (!r.state.opaque) callbackFn(r, o)
-                })
+                renderableMap.forEach((r, o) => { if (!r.state.opaque) callbackFn(r, o) })
             },
             get count() {
                 return renderableMap.size