diff --git a/src/mol-model/structure/query/utils/structure-set.ts b/src/mol-model/structure/query/utils/structure-set.ts
index 85867419505a8e3494e2f7a9e70838f140c0a00c..0ab6a9bbfafe296799320aa3f5d22e7f5d6b28b8 100644
--- a/src/mol-model/structure/query/utils/structure-set.ts
+++ b/src/mol-model/structure/query/utils/structure-set.ts
@@ -94,7 +94,7 @@ export function structureSubtract(a: Structure, b: Structure): Structure {
         const u = aU[i];
         if (!bU.has(u.id)) continue;
         const v = bU.get(u.id);
-        const sub = SortedArray.intersect(u.elements, v.elements);
+        const sub = SortedArray.subtract(u.elements, v.elements);
         if (sub.length > 0) {
             units[units.length] = u.getChild(sub);
         }
diff --git a/src/mol-plugin/behavior/behavior.ts b/src/mol-plugin/behavior/behavior.ts
index 5aa091a68f86ba3b9e726f810fb8d7b1962aae32..bdfb750c2b83beaff52c5d9d8fef4cf39c6bda94 100644
--- a/src/mol-plugin/behavior/behavior.ts
+++ b/src/mol-plugin/behavior/behavior.ts
@@ -46,7 +46,6 @@ namespace PluginBehavior {
         label?: (params: P) => { label: string, description?: string },
         display: {
             name: string,
-            group: string,
             description?: string
         },
         params?(a: Root, globalCtx: PluginContext): { [K in keyof P]: ParamDefinition.Any }
diff --git a/src/mol-plugin/behavior/dynamic/animation.ts b/src/mol-plugin/behavior/dynamic/animation.ts
index a2c863ed388f5a86bd2bf3be4d56a4243435fb97..73dab53e6a8b6fecc0b609f0af81aa3f183bc6b4 100644
--- a/src/mol-plugin/behavior/dynamic/animation.ts
+++ b/src/mol-plugin/behavior/dynamic/animation.ts
@@ -30,7 +30,7 @@ type StructureAnimationProps = PD.Values<typeof StructureAnimationParams>
 export const StructureAnimation = PluginBehavior.create<StructureAnimationProps>({
     name: 'structure-animation',
     category: 'representation',
-    display: { name: 'Structure Animation', group: 'Animation' },
+    display: { name: 'Structure Animation' },
     canAutoUpdate: () => true,
     ctor: class extends PluginBehavior.Handler<StructureAnimationProps> {
         private tmpMat = Mat4.identity()
diff --git a/src/mol-plugin/behavior/dynamic/camera.ts b/src/mol-plugin/behavior/dynamic/camera.ts
index 34199a4fdad3a1cf3e18165075ada53e5169ce0c..34da03e8705134cc1b76bd649453105624e3f207 100644
--- a/src/mol-plugin/behavior/dynamic/camera.ts
+++ b/src/mol-plugin/behavior/dynamic/camera.ts
@@ -27,5 +27,5 @@ export const FocusLociOnSelect = PluginBehavior.create<{ minRadius: number, extr
         minRadius: ParamDefinition.Numeric(10, { min: 1, max: 50, step: 1 }),
         extraRadius: ParamDefinition.Numeric(4, { min: 1, max: 50, step: 1 }, { description: 'Value added to the boundning sphere radius of the Loci.' })
     }),
-    display: { name: 'Focus Loci on Select', group: 'Camera' }
+    display: { name: 'Focus Loci on Select' }
 });
\ No newline at end of file
diff --git a/src/mol-plugin/behavior/dynamic/custom-props/pdbe/structure-quality-report.ts b/src/mol-plugin/behavior/dynamic/custom-props/pdbe/structure-quality-report.ts
index 110ec57b823b70cada7d330ce14a68677cc18cf3..5b8bfdc183f3632ac6dccdc41119bb11b86d7663 100644
--- a/src/mol-plugin/behavior/dynamic/custom-props/pdbe/structure-quality-report.ts
+++ b/src/mol-plugin/behavior/dynamic/custom-props/pdbe/structure-quality-report.ts
@@ -17,7 +17,7 @@ import { CustomPropertyRegistry } from 'mol-model-props/common/custom-property-r
 export const PDBeStructureQualityReport = PluginBehavior.create<{ autoAttach: boolean }>({
     name: 'pdbe-structure-quality-report-prop',
     category: 'custom-props',
-    display: { name: 'PDBe Structure Quality Report', group: 'Custom Props' },
+    display: { name: 'PDBe Structure Quality Report' },
     ctor: class extends PluginBehavior.Handler<{ autoAttach: boolean }> {
         private attach = StructureQualityReport.createAttachTask(
             m => `https://www.ebi.ac.uk/pdbe/api/validation/residuewise_outlier_summary/entry/${m.label.toLowerCase()}`,
diff --git a/src/mol-plugin/behavior/dynamic/custom-props/rcsb/assembly-symmetry.ts b/src/mol-plugin/behavior/dynamic/custom-props/rcsb/assembly-symmetry.ts
index e156dd20d54e7f3b8af6dda004005ba23920e398..0d65bbc92b7823b7d2eea29ebc2274198f06e665 100644
--- a/src/mol-plugin/behavior/dynamic/custom-props/rcsb/assembly-symmetry.ts
+++ b/src/mol-plugin/behavior/dynamic/custom-props/rcsb/assembly-symmetry.ts
@@ -17,7 +17,7 @@ import { CustomPropertyRegistry } from 'mol-model-props/common/custom-property-r
 export const RCSBAssemblySymmetry = PluginBehavior.create<{ autoAttach: boolean }>({
     name: 'rcsb-assembly-symmetry-prop',
     category: 'custom-props',
-    display: { name: 'RCSB Assembly Symmetry', group: 'Custom Props' },
+    display: { name: 'RCSB Assembly Symmetry' },
     ctor: class extends PluginBehavior.Handler<{ autoAttach: boolean }> {
         private attach = AssemblySymmetry.createAttachTask(this.ctx.fetch);
 
diff --git a/src/mol-plugin/behavior/dynamic/labels.ts b/src/mol-plugin/behavior/dynamic/labels.ts
index f3f17ea828a20e69695883d059d8597bca38a0bf..be54651502d57168464b9a568c7a69eb7043cec6 100644
--- a/src/mol-plugin/behavior/dynamic/labels.ts
+++ b/src/mol-plugin/behavior/dynamic/labels.ts
@@ -74,7 +74,7 @@ function getLabelsText(data: LabelsData, props: PD.Values<Text.Params>, text?: T
 export const SceneLabels = PluginBehavior.create<SceneLabelsProps>({
     name: 'scene-labels',
     category: 'representation',
-    display: { name: 'Scene Labels', group: 'Labels' },
+    display: { name: 'Scene Labels' },
     canAutoUpdate: () => true,
     ctor: class extends PluginBehavior.Handler<SceneLabelsProps> {
         private data: LabelsData = {
diff --git a/src/mol-plugin/behavior/dynamic/representation.ts b/src/mol-plugin/behavior/dynamic/representation.ts
index c9126e2d0b440e8764ec6442b6cb878098301bc3..42b448182cdde3057733b463792c18e4a6767ea7 100644
--- a/src/mol-plugin/behavior/dynamic/representation.ts
+++ b/src/mol-plugin/behavior/dynamic/representation.ts
@@ -50,7 +50,7 @@ export const HighlightLoci = PluginBehavior.create({
             });
         }
     },
-    display: { name: 'Highlight Loci on Canvas', group: 'Representation' }
+    display: { name: 'Highlight Loci on Canvas' }
 });
 
 export const SelectLoci = PluginBehavior.create({
@@ -104,7 +104,7 @@ export const SelectLoci = PluginBehavior.create({
             });
         }
     },
-    display: { name: 'Select Loci on Canvas', group: 'Representation' }
+    display: { name: 'Select Loci on Canvas' }
 });
 
 export const DefaultLociLabelProvider = PluginBehavior.create({
@@ -116,7 +116,7 @@ export const DefaultLociLabelProvider = PluginBehavior.create({
         unregister() { this.ctx.lociLabels.removeProvider(this.f); }
         constructor(protected ctx: PluginContext) { }
     },
-    display: { name: 'Provide Default Loci Label', group: 'Representation' }
+    display: { name: 'Provide Default Loci Label' }
 });
 
 
diff --git a/src/mol-plugin/behavior/dynamic/selection/structure-representation-interaction.ts b/src/mol-plugin/behavior/dynamic/selection/structure-representation-interaction.ts
new file mode 100644
index 0000000000000000000000000000000000000000..365d51eb6536f9347ce6e2577cb0b79fe46be812
--- /dev/null
+++ b/src/mol-plugin/behavior/dynamic/selection/structure-representation-interaction.ts
@@ -0,0 +1,133 @@
+/**
+ * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author David Sehnal <david.sehnal@gmail.com>
+ */
+
+import { Structure, StructureElement } from 'mol-model/structure';
+import { PluginBehavior } from 'mol-plugin/behavior';
+import { PluginCommands } from 'mol-plugin/command';
+import { PluginContext } from 'mol-plugin/context';
+import { PluginStateObject } from 'mol-plugin/state/objects';
+import { StateTransforms } from 'mol-plugin/state/transforms';
+import { StructureRepresentation3DHelpers } from 'mol-plugin/state/transforms/representation';
+import { BuiltInStructureRepresentations } from 'mol-repr/structure/registry';
+import { MolScriptBuilder as MS } from 'mol-script/language/builder';
+import { StateObjectCell, StateSelection } from 'mol-state';
+import { BuiltInColorThemes } from 'mol-theme/color';
+import { BuiltInSizeThemes } from 'mol-theme/size';
+import { ColorNames } from 'mol-util/color/tables';
+import { ButtonsType } from 'mol-util/input/input-observer';
+
+type Params = { }
+
+enum Tags {
+    Group = 'structure-interaction-group',
+    ResidueSel = 'structure-interaction-residue-sel',
+    ResidueRepr = 'structure-interaction-residue-repr',
+    SurrSel = 'structure-interaction-surr-sel',
+    SurrRepr = 'structure-interaction-surr-repr'
+}
+
+const TagSet: Set<Tags> = new Set([Tags.Group, Tags.ResidueSel, Tags.ResidueRepr, Tags.SurrSel, Tags.SurrRepr])
+
+export class StructureRepresentationInteractionBehavior extends PluginBehavior.WithSubscribers<Params> {
+
+    private createResVisualParams(s: Structure) {
+        return StructureRepresentation3DHelpers.createParams(this.plugin, s, {
+            repr: BuiltInStructureRepresentations['ball-and-stick']
+        });
+    }
+
+    private createSurVisualParams(s: Structure) {
+        return StructureRepresentation3DHelpers.createParams(this.plugin, s, {
+            repr: BuiltInStructureRepresentations['ball-and-stick'],
+            color: [BuiltInColorThemes.uniform, () => ({ value: ColorNames.gray })],
+            size: [BuiltInSizeThemes.uniform, () => ({ value: 0.2} )]
+        });
+    }
+
+    private ensureShape(cell: StateObjectCell<PluginStateObject.Molecule.Structure>) {
+        const state = this.plugin.state.dataState, tree = state.tree;
+        const builder = state.build();
+        const refs = StateSelection.findUniqueTagsInSubtree(tree, cell.transform.ref, TagSet);
+
+        if (!refs['structure-interaction-group']) {
+            refs['structure-interaction-group'] = builder.to(cell.transform.ref).group(StateTransforms.Misc.CreateGroup,
+                { label: 'Current Interaction' }, { props: { tag: Tags.Group } }).ref;
+        }
+
+        // Selections
+        if (!refs[Tags.ResidueSel]) {
+            refs[Tags.ResidueSel] = builder.to(refs['structure-interaction-group']).apply(StateTransforms.Model.StructureSelection,
+                { query: { } as any, label: 'Residue' }, { props: { tag: Tags.ResidueSel } }).ref;
+        }
+
+        if (!refs[Tags.SurrSel]) {
+            refs[Tags.SurrSel] = builder.to(refs['structure-interaction-group']).apply(StateTransforms.Model.StructureSelection,
+                { query: { } as any, label: 'Surroundings' }, { props: { tag: Tags.SurrSel } }).ref;
+        }
+
+        // Representations
+        // TODO: ability to customize how it looks in the behavior params
+        if (!refs[Tags.ResidueRepr]) {
+            refs[Tags.ResidueRepr] = builder.to(refs['structure-interaction-residue-sel']!).apply(StateTransforms.Representation.StructureRepresentation3D,
+                this.createResVisualParams(cell.obj!.data), { props: { tag: Tags.ResidueRepr } }).ref;
+        }
+
+        if (!refs[Tags.SurrRepr]) {
+            refs[Tags.SurrRepr] = builder.to(refs['structure-interaction-surr-sel']!).apply(StateTransforms.Representation.StructureRepresentation3D,
+                this.createSurVisualParams(cell.obj!.data), { props: { tag: Tags.SurrRepr } }).ref;
+        }
+
+        return { state, builder, refs };
+    }
+
+    register(ref: string): void {
+        // this.ref = ref;
+
+        this.subscribeObservable(this.plugin.events.canvas3d.click, ({ current, buttons }) => {
+            if (buttons !== ButtonsType.Flag.Secondary) return;
+            // TODO: support link loci as well?
+            if (!StructureElement.isLoci(current.loci)) return;
+
+            const parent = this.plugin.helpers.substructureParent.get(current.loci.structure);
+            if (!parent || !parent.obj) return;
+
+            const core = MS.struct.modifier.wholeResidues([
+                StructureElement.Loci.toScriptExpression(current.loci)
+            ]);
+
+            const surroundings = MS.struct.modifier.exceptBy({
+                0: MS.struct.modifier.includeSurroundings({
+                    0: core,
+                    radius: 5,
+                    'as-whole-residues': true
+                }),
+                by: core
+            });
+
+            const { state, builder, refs } = this.ensureShape(parent);
+
+            builder.to(refs[Tags.ResidueSel]!).update(StateTransforms.Model.StructureSelection, old => ({ ...old, query: core }));
+            builder.to(refs[Tags.SurrSel]!).update(StateTransforms.Model.StructureSelection, old => ({ ...old, query: surroundings }));
+
+            PluginCommands.State.Update.dispatch(this.plugin, { state, tree: builder, options: { doNotLogTiming: true, doNotUpdateCurrent: true } });
+        });
+    }
+
+    async update(params: Params) {
+        return false;
+    }
+
+    constructor(public plugin: PluginContext) {
+        super(plugin);
+    }
+}
+
+export const StructureRepresentationInteraction = PluginBehavior.create({
+    name: 'create-structure-representation-interaction',
+    display: { name: 'Structure Representation Interaction' },
+    category: 'interaction',
+    ctor: StructureRepresentationInteractionBehavior
+});
\ No newline at end of file
diff --git a/src/mol-plugin/behavior/dynamic/volume-streaming/transformers.ts b/src/mol-plugin/behavior/dynamic/volume-streaming/transformers.ts
index e3aa09b470dc0900c6fd36442dedc19d4ca6b582..0e4a8173a651e5a9de25f579b5f49f16310b88b8 100644
--- a/src/mol-plugin/behavior/dynamic/volume-streaming/transformers.ts
+++ b/src/mol-plugin/behavior/dynamic/volume-streaming/transformers.ts
@@ -127,7 +127,6 @@ const CreateVolumeStreamingBehavior = PluginStateTransform.BuiltIn({
     }
 });
 
-
 export { VolumeStreamingVisual }
 type VolumeStreamingVisual = typeof VolumeStreamingVisual
 const VolumeStreamingVisual = PluginStateTransform.BuiltIn({
diff --git a/src/mol-plugin/behavior/static/representation.ts b/src/mol-plugin/behavior/static/representation.ts
index bf58413b59e2d4d75998175dc1a5867944148e32..37e0d8bee139897cb4513e7c892324dd8caa72fe 100644
--- a/src/mol-plugin/behavior/static/representation.ts
+++ b/src/mol-plugin/behavior/static/representation.ts
@@ -15,14 +15,17 @@ export function registerDefault(ctx: PluginContext) {
 }
 
 export function SyncRepresentationToCanvas(ctx: PluginContext) {
+    let reprCount = 0;
+
     const events = ctx.state.dataState.events;
     events.object.created.subscribe(e => {
         if (!SO.isRepresentation3D(e.obj)) return;
         updateVisibility(e, e.obj.data);
         e.obj.data.setState({ syncManually: true });
         ctx.canvas3d.add(e.obj.data);
-        // TODO: only do this if there were no representations previously
-        ctx.canvas3d.resetCamera();
+
+        if (reprCount === 0) ctx.canvas3d.resetCamera();
+        reprCount++;
     });
     events.object.updated.subscribe(e => {
         if (e.oldObj && SO.isRepresentation3D(e.oldObj)) {
@@ -31,7 +34,9 @@ export function SyncRepresentationToCanvas(ctx: PluginContext) {
             e.oldObj.data.destroy();
         }
 
-        if (!SO.isRepresentation3D(e.obj)) return;
+        if (!SO.isRepresentation3D(e.obj)) {
+            return;
+        }
 
         updateVisibility(e, e.obj.data);
         if (e.action === 'recreate') {
@@ -44,6 +49,7 @@ export function SyncRepresentationToCanvas(ctx: PluginContext) {
         ctx.canvas3d.remove(e.obj.data);
         ctx.canvas3d.requestDraw(true);
         e.obj.data.destroy();
+        reprCount--;
     });
 }
 
diff --git a/src/mol-plugin/index.ts b/src/mol-plugin/index.ts
index a81f06d2ab99c29dacf10305c5c80be9a2b79d1c..784a246ed493a10de1a09f96379d46603483b74f 100644
--- a/src/mol-plugin/index.ts
+++ b/src/mol-plugin/index.ts
@@ -16,6 +16,7 @@ import { PluginBehaviors } from './behavior';
 import { AnimateModelIndex } from './state/animation/built-in';
 import { StateActions } from './state/actions';
 import { InitVolumeStreaming } from './behavior/dynamic/volume-streaming/transformers';
+import { StructureRepresentationInteraction } from './behavior/dynamic/selection/structure-representation-interaction';
 
 function getParam(name: string, regex: string): string {
     let r = new RegExp(`${name}=(${regex})[&]?`, 'i');
@@ -59,6 +60,7 @@ export const DefaultPluginSpec: PluginSpec = {
         // PluginSpec.Behavior(PluginBehaviors.Labels.SceneLabels),
         PluginSpec.Behavior(PluginBehaviors.CustomProps.PDBeStructureQualityReport, { autoAttach: true }),
         PluginSpec.Behavior(PluginBehaviors.CustomProps.RCSBAssemblySymmetry, { autoAttach: true }),
+        PluginSpec.Behavior(StructureRepresentationInteraction)
     ],
     animations: [
         AnimateModelIndex
diff --git a/src/mol-plugin/state/transforms.ts b/src/mol-plugin/state/transforms.ts
index 6b1c5884c181eb5cc64d4d8661e3de66780f199a..1058f7ee58a74546b4cbd0810bcac92034393d9f 100644
--- a/src/mol-plugin/state/transforms.ts
+++ b/src/mol-plugin/state/transforms.ts
@@ -5,12 +5,14 @@
  */
 
 import * as Data from './transforms/data'
+import * as Misc from './transforms/misc'
 import * as Model from './transforms/model'
 import * as Volume from './transforms/volume'
 import * as Representation from './transforms/representation'
 
 export const StateTransforms = {
     Data,
+    Misc,
     Model,
     Volume,
     Representation
diff --git a/src/mol-plugin/state/transforms/representation.ts b/src/mol-plugin/state/transforms/representation.ts
index 389308f6b2b2f955da1c8d98650afbd9c6d85547..78e98145ebc6748422b3df6174de97765bfd60b3 100644
--- a/src/mol-plugin/state/transforms/representation.ts
+++ b/src/mol-plugin/state/transforms/representation.ts
@@ -5,22 +5,22 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { StateTransformer } from 'mol-state';
-import { Task } from 'mol-task';
-import { PluginStateTransform } from '../objects';
-import { PluginStateObject as SO } from '../objects';
+import { Structure } from 'mol-model/structure';
+import { VolumeData, VolumeIsoValue } from 'mol-model/volume';
+import { ExplodeRepresentation3D } from 'mol-plugin/behavior/dynamic/representation';
 import { PluginContext } from 'mol-plugin/context';
-import { ParamDefinition as PD } from 'mol-util/param-definition';
-import { createTheme } from 'mol-theme/theme';
+import { RepresentationProvider } from 'mol-repr/representation';
 import { BuiltInStructureRepresentationsName } from 'mol-repr/structure/registry';
-import { Structure } from 'mol-model/structure';
 import { StructureParams } from 'mol-repr/structure/representation';
-import { ExplodeRepresentation3D } from 'mol-plugin/behavior/dynamic/representation';
-import { VolumeData, VolumeIsoValue } from 'mol-model/volume';
 import { BuiltInVolumeRepresentationsName } from 'mol-repr/volume/registry';
 import { VolumeParams } from 'mol-repr/volume/representation';
+import { StateTransformer } from 'mol-state';
+import { Task } from 'mol-task';
 import { BuiltInColorThemeName, ColorTheme } from 'mol-theme/color';
 import { BuiltInSizeThemeName, SizeTheme } from 'mol-theme/size';
+import { createTheme, ThemeRegistryContext } from 'mol-theme/theme';
+import { ParamDefinition as PD } from 'mol-util/param-definition';
+import { PluginStateObject as SO, PluginStateTransform } from '../objects';
 
 export namespace StructureRepresentation3DHelpers {
     export function getDefaultParams(ctx: PluginContext, name: BuiltInStructureRepresentationsName, structure: Structure, structureParams?: Partial<PD.Values<StructureParams>>): StateTransformer.Params<StructureRepresentation3D> {
@@ -37,6 +37,43 @@ export namespace StructureRepresentation3DHelpers {
         })
     }
 
+    export function createParams<R extends RepresentationProvider<Structure, any, any>, C extends ColorTheme.Provider<any>, S extends SizeTheme.Provider<any>>(
+            ctx: PluginContext, structure: Structure, params: {
+            repr?: R | [R, (r: R, ctx: ThemeRegistryContext, s: Structure) => RepresentationProvider.ParamValues<R>],
+            color?: C | [C, (c: C, ctx: ThemeRegistryContext) => ColorTheme.ParamValues<C>],
+            size?: S | [S, (c: S, ctx: ThemeRegistryContext) => SizeTheme.ParamValues<S>]
+        }): StateTransformer.Params<StructureRepresentation3D> {
+
+        const themeCtx = ctx.structureRepresentation.themeCtx
+
+        const repr = params.repr
+            ? params.repr instanceof Array ? params.repr[0] : params.repr
+            : ctx.structureRepresentation.registry.default.provider;
+        const reprParams = params.repr instanceof Array
+            ? params.repr[1](repr as R, themeCtx, structure)
+            : PD.getDefaultValues(repr.getParams(themeCtx, structure));
+
+        const color = params.color
+            ? params.color instanceof Array ? params.color[0] : params.color
+            : themeCtx.colorThemeRegistry.get(repr.defaultColorTheme);
+        const colorParams = params.color instanceof Array
+            ? params.color[1](color as C, themeCtx)
+            : PD.getDefaultValues(color.getParams(themeCtx));
+
+        const size = params.size
+            ? params.size instanceof Array ? params.size[0] : params.size
+            : themeCtx.sizeThemeRegistry.get(repr.defaultSizeTheme);
+        const sizeParams = params.size instanceof Array
+            ? params.size[1](size as S, themeCtx)
+            : PD.getDefaultValues(size.getParams(themeCtx));
+
+        return ({
+            type: { name: ctx.structureRepresentation.registry.getName(repr), params: reprParams },
+            colorTheme: { name: themeCtx.colorThemeRegistry.getName(color), params: colorParams },
+            sizeTheme: { name: themeCtx.sizeThemeRegistry.getName(size), params: sizeParams }
+        })
+    }
+
     export function getDefaultParamsWithTheme(ctx: PluginContext, reprName: BuiltInStructureRepresentationsName, colorName: BuiltInColorThemeName | undefined, structure: Structure, structureParams?: Partial<PD.Values<StructureParams>>): StateTransformer.Params<StructureRepresentation3D> {
         const type = ctx.structureRepresentation.registry.get(reprName);
 
@@ -63,7 +100,9 @@ export namespace StructureRepresentation3DHelpers {
         })
     }
 }
-export { StructureRepresentation3D }
+export { StructureRepresentation3D };
+export { ExplodeStructureRepresentation3D };
+export { VolumeRepresentation3D };
 type StructureRepresentation3D = typeof StructureRepresentation3D
 const StructureRepresentation3D = PluginStateTransform.BuiltIn({
     name: 'structure-representation-3d',
@@ -131,14 +170,13 @@ const StructureRepresentation3D = PluginStateTransform.BuiltIn({
         return Task.create('Structure Representation', async ctx => {
             if (newParams.type.name !== oldParams.type.name) return StateTransformer.UpdateResult.Recreate;
             const props = { ...b.data.props, ...newParams.type.params }
-            b.data.setTheme(createTheme(plugin.structureRepresentation.themeCtx, { structure: a.data }, newParams))
+            b.data.setTheme(createTheme(plugin.structureRepresentation.themeCtx, { structure: a.data }, newParams));
             await b.data.createOrUpdate(props, a.data).runInContext(ctx);
             return StateTransformer.UpdateResult.Updated;
         });
     }
 });
 
-export { ExplodeStructureRepresentation3D }
 type ExplodeStructureRepresentation3D = typeof ExplodeStructureRepresentation3D
 const ExplodeStructureRepresentation3D = PluginStateTransform.BuiltIn({
     name: 'explode-structure-representation-3d',
@@ -194,7 +232,6 @@ export namespace VolumeRepresentation3DHelpers {
         return props.isoValue && VolumeIsoValue.toString(props.isoValue)
     }
 }
-export { VolumeRepresentation3D }
 type VolumeRepresentation3D = typeof VolumeRepresentation3D
 const VolumeRepresentation3D = PluginStateTransform.BuiltIn({
     name: 'volume-representation-3d',
diff --git a/src/mol-repr/representation.ts b/src/mol-repr/representation.ts
index b959ed7f1d20885fc2f5068c7a4ec288777cb9e7..6809928fcc04efeec18c1d729228bf1b1d69af6e 100644
--- a/src/mol-repr/representation.ts
+++ b/src/mol-repr/representation.ts
@@ -46,6 +46,14 @@ export interface RepresentationProvider<D, P extends PD.Params, S extends Repres
     readonly defaultSizeTheme: string
 }
 
+export namespace RepresentationProvider {
+    export type ParamValues<R extends RepresentationProvider<any, any, any>> = R extends RepresentationProvider<any, infer P, any> ? PD.Values<P> : never;
+
+    export function getDetaultParams<R extends RepresentationProvider<D, any, any>, D>(r: R, ctx: ThemeRegistryContext, data: D) {
+        return PD.getDefaultValues(r.getParams(ctx, data));
+    }
+}
+
 export type AnyRepresentationProvider = RepresentationProvider<any, {}, Representation.State>
 
 export const EmptyRepresentationProvider = {
@@ -59,6 +67,7 @@ export const EmptyRepresentationProvider = {
 export class RepresentationRegistry<D, S extends Representation.State> {
     private _list: { name: string, provider: RepresentationProvider<D, any, any> }[] = []
     private _map = new Map<string, RepresentationProvider<D, any, any>>()
+    private _name = new Map<RepresentationProvider<D, any, any>, string>()
 
     get default() { return this._list[0]; }
     get types(): [string, string][] {
@@ -70,11 +79,21 @@ export class RepresentationRegistry<D, S extends Representation.State> {
     add<P extends PD.Params>(name: string, provider: RepresentationProvider<D, P, S>) {
         this._list.push({ name, provider })
         this._map.set(name, provider)
+        this._name.set(provider, name)
+    }
+
+    getName(provider: RepresentationProvider<D, any, S>): string {
+        if (!this._name.has(provider)) throw new Error(`'${provider.label}' is not a registered represenatation provider.`);
+        return this._name.get(provider)!;
     }
 
     remove(name: string) {
         this._list.splice(this._list.findIndex(e => e.name === name), 1)
-        this._map.delete(name)
+        const p = this._map.get(name);
+        if (p) {
+            this._map.delete(name);
+            this._name.delete(p);
+        }
     }
 
     get<P extends PD.Params>(name: string): RepresentationProvider<D, P, S> {
diff --git a/src/mol-script/runtime/query/table.ts b/src/mol-script/runtime/query/table.ts
index bbe87566e7d98737f182a7f0ee1d4dd78d08d283..7e48327940d58dd3913730897b2a514eb9a566eb 100644
--- a/src/mol-script/runtime/query/table.ts
+++ b/src/mol-script/runtime/query/table.ts
@@ -207,6 +207,7 @@ const symbols = [
     })(ctx)),
     D(MolScript.structureQuery.modifier.wholeResidues, (ctx, xs) => Queries.modifiers.wholeResidues(xs[0] as any)(ctx)),
     D(MolScript.structureQuery.modifier.expandProperty, (ctx, xs) => Queries.modifiers.expandProperty(xs[0] as any, xs['property'])(ctx)),
+    D(MolScript.structureQuery.modifier.exceptBy, (ctx, xs) => Queries.modifiers.exceptBy(xs[0] as any, xs['by'] as any)(ctx)),
 
     // ============= ATOM PROPERTIES ================
 
diff --git a/src/mol-state/state.ts b/src/mol-state/state.ts
index 88a67e840fa882c393e7e0a850c84ba3a3d8470d..e94bbbc3d886d9eb2baf6075608db7e2d971e11d 100644
--- a/src/mol-state/state.ts
+++ b/src/mol-state/state.ts
@@ -567,7 +567,9 @@ async function updateNode(ctx: UpdateContext, currentRef: Ref): Promise<UpdateNo
         return { action: 'none' };
     }
 
-    const parentCell = StateSelection.findAncestorOfType(tree, ctx.cells, currentRef, transform.transformer.definition.from);
+    let parentCell = transform.transformer.definition.from.length === 0
+        ? ctx.cells.get(current.transform.parent)
+        : StateSelection.findAncestorOfType(tree, ctx.cells, currentRef, transform.transformer.definition.from);
     if (!parentCell) {
         throw new Error(`No suitable parent found for '${currentRef}'`);
     }
diff --git a/src/mol-state/state/selection.ts b/src/mol-state/state/selection.ts
index 97807c000147850d4ff33a34a5bcdaa08a8219d8..a01d5ab7e08815dd40af73dba379c2d7314cfe23 100644
--- a/src/mol-state/state/selection.ts
+++ b/src/mol-state/state/selection.ts
@@ -241,6 +241,29 @@ namespace StateSelection {
         }
         return parent;
     }
+
+    export function findUniqueTagsInSubtree<K extends string = string>(tree: StateTree, root: StateTransform.Ref, tags: Set<K>): { [name in K]?: StateTransform.Ref } {
+        return StateTree.doPreOrder(tree, tree.transforms.get(root), { refs: { }, tags }, _findUniqueTagsInSubtree).refs;
+    }
+
+    function _findUniqueTagsInSubtree(n: StateTransform, _: any, s: { refs: { [name: string]: StateTransform.Ref }, tags: Set<string> }) {
+        if (n.props.tag && s.tags.has(n.props.tag)) {
+            s.refs[n.props.tag] = n.ref;
+        }
+        return true;
+    }
+
+    export function findTagInSubtree(tree: StateTree, root: StateTransform.Ref, tag: string): StateTransform.Ref | undefined {
+        return StateTree.doPreOrder(tree, tree.transforms.get(root), { ref: void 0, tag }, _findTagInSubtree).tag;
+    }
+
+    function _findTagInSubtree(n: StateTransform, _: any, s: { ref: string | undefined, tag: string }) {
+        if (n.props.tag === s.tag) {
+            s.ref = n.ref;
+            return false;
+        }
+        return true;
+    }
 }
 
 export { StateSelection }
\ No newline at end of file
diff --git a/src/mol-theme/color.ts b/src/mol-theme/color.ts
index 33f4c9e3c93fc5544682676f33d55a30f9f8c863..a76970c184b1b8e2bb52c1f2f6b7b21f2bc58ae3 100644
--- a/src/mol-theme/color.ts
+++ b/src/mol-theme/color.ts
@@ -56,6 +56,8 @@ namespace ColorTheme {
     export function createRegistry() {
         return new ThemeRegistry(BuiltInColorThemes as { [k: string]: Provider<any> }, EmptyProvider)
     }
+
+    export type ParamValues<C extends ColorTheme.Provider<any>> = C extends ColorTheme.Provider<infer P> ? PD.Values<P> : never
 }
 
 export const BuiltInColorThemes = {
diff --git a/src/mol-theme/size.ts b/src/mol-theme/size.ts
index c0dbbe3e39e03b5ec59e7296ce5b21eb3d4c00c1..84adfd7667e8b0ee6de8e63ff7251f23b46a644f 100644
--- a/src/mol-theme/size.ts
+++ b/src/mol-theme/size.ts
@@ -37,6 +37,8 @@ namespace SizeTheme {
     export function createRegistry() {
         return new ThemeRegistry(BuiltInSizeThemes as { [k: string]: Provider<any> }, EmptyProvider)
     }
+
+    export type ParamValues<C extends SizeTheme.Provider<any>> = C extends SizeTheme.Provider<infer P> ? PD.Values<P> : never
 }
 
 export const BuiltInSizeThemes = {
diff --git a/src/mol-theme/theme.ts b/src/mol-theme/theme.ts
index b5d224268575463da7436c48865ddead95b9b27d..bdf11bfed8a356b97705d896a69371f4d403ef0c 100644
--- a/src/mol-theme/theme.ts
+++ b/src/mol-theme/theme.ts
@@ -64,6 +64,7 @@ function getTypes(list: { name: string, provider: ThemeProvider<any, any> }[]) {
 export class ThemeRegistry<T extends ColorTheme<any> | SizeTheme<any>> {
     private _list: { name: string, provider: ThemeProvider<T, any> }[] = []
     private _map = new Map<string, ThemeProvider<T, any>>()
+    private _name = new Map<ThemeProvider<T, any>, string>()
 
     get default() { return this._list[0]; }
     get list() { return this._list }
@@ -76,18 +77,28 @@ export class ThemeRegistry<T extends ColorTheme<any> | SizeTheme<any>> {
     add<P extends PD.Params>(name: string, provider: ThemeProvider<T, P>) {
         this._list.push({ name, provider })
         this._map.set(name, provider)
+        this._name.set(provider, name)
     }
 
     remove(name: string) {
         this._list.splice(this._list.findIndex(e => e.name === name), 1)
-        this._map.delete(name)
-        console.log('removed', name, this._list, this._map)
+        const p = this._map.get(name);
+        if (p) {
+            this._map.delete(name);
+            this._name.delete(p);
+        }
     }
 
     get<P extends PD.Params>(name: string): ThemeProvider<T, P> {
         return this._map.get(name) || this.emptyProvider
     }
 
+    getName(provider: ThemeProvider<T, any>): string {
+        if (!this._name.has(provider)) throw new Error(`'${provider.label}' is not a registered represenatation provider.`);
+        return this._name.get(provider)!;
+    }
+
+
     create(name: string, ctx: ThemeDataContext, props = {}) {
         const provider = this.get(name)
         return provider.factory(ctx, { ...PD.getDefaultValues(provider.getParams(ctx)), ...props })