diff --git a/src/mol-model/structure/query/queries/internal.ts b/src/mol-model/structure/query/queries/internal.ts
index 23b495f72a26920add9f871533c740b841b7242d..fecbf7d10bc2a533ed62e0d50912799ea1d5c568 100644
--- a/src/mol-model/structure/query/queries/internal.ts
+++ b/src/mol-model/structure/query/queries/internal.ts
@@ -4,7 +4,7 @@
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 
-import { Segmentation } from '../../../../mol-data/int';
+import { Segmentation, SortedArray } from '../../../../mol-data/int';
 import StructureElement from '../../../../mol-model/structure/structure/element';
 import { StructureProperties as P, Unit } from '../../structure';
 import Structure from '../../structure/structure';
@@ -12,6 +12,8 @@ import { StructureQuery } from '../query';
 import { StructureSelection } from '../selection';
 import { QueryContext } from '../context';
 import { LinkType } from '../../model/types';
+import { BundleElement, Bundle } from '../../structure/element/bundle';
+import { UnitIndex } from '../../structure/element/element';
 
 export function defaultLinkTest(ctx: QueryContext) {
     return LinkType.isCovalent(ctx.atomicLink.type);
@@ -105,4 +107,23 @@ export function spheres(): StructureQuery {
         }
         return StructureSelection.Singletons(inputStructure, new Structure(units, { parent: inputStructure }));
     };
+}
+
+export function bundleElementImpl(groupedUnits: number[][], ranges: number[], set: number[]): BundleElement {
+    return {
+        groupedUnits: groupedUnits as any as SortedArray<number>[],
+        ranges: ranges as any as SortedArray<UnitIndex>,
+        set: set as any as SortedArray<UnitIndex>
+    };
+}
+
+export function bundleGenerator(elements: BundleElement[]): StructureQuery {
+    return ctx => {
+        const bundle: Bundle = {
+            hash: ctx.inputStructure.hashCode,
+            elements
+        };
+
+        return StructureSelection.Sequence(ctx.inputStructure, [Bundle.toStructure(bundle, ctx.inputStructure)]);
+    };
 }
\ No newline at end of file
diff --git a/src/mol-model/structure/structure/element/bundle.ts b/src/mol-model/structure/structure/element/bundle.ts
index b750308df22518ca0aaa64309c95bde947ea2e05..b222894267daaf4f7958d1aceea3efb3728dd989 100644
--- a/src/mol-model/structure/structure/element/bundle.ts
+++ b/src/mol-model/structure/structure/element/bundle.ts
@@ -12,8 +12,10 @@ import { hashFnv32a, hash2 } from '../../../../mol-data/util';
 import SortedRanges from '../../../../mol-data/int/sorted-ranges';
 import { UnitIndex } from './element';
 import { Loci } from './loci';
+import Expression from '../../../../mol-script/language/expression';
+import { MolScriptBuilder as MS } from '../../../../mol-script/language/builder';
 
-interface BundleElement {
+export interface BundleElement {
     /**
      * Array (sorted by first element in sub-array) of
      * arrays of `Unit.id`s that share the same `Unit.invariantId`
@@ -192,6 +194,21 @@ export namespace Bundle {
         return Structure.create(units, { parent })
     }
 
+
+    function elementToExpression(e: BundleElement): Expression {
+        return MS.internal.generator.bundleElement({
+            groupedUnits: MS.core.type.list(e.groupedUnits.map(u => MS.core.type.list(u))),
+            ranges: MS.core.type.list(e.ranges),
+            set: MS.core.type.list(e.set),
+        })
+    }
+
+    export function toExpression(bundle: Bundle): Expression {
+        return MS.internal.generator.bundle({
+            elements: MS.core.type.list(bundle.elements.map(elementToExpression))
+        });
+    }
+
     export function areEqual(a: Bundle, b: Bundle) {
         if (a.elements.length !== b.elements.length) return false
         for (let i = 0, il = a.elements.length; i < il; ++i) {
diff --git a/src/mol-script/language/builder.ts b/src/mol-script/language/builder.ts
index c114c0ff6307c5c2ed36ad44a35ff7a01a82c288..16038e3837ddc1f39468220af151a7a1dc252197 100644
--- a/src/mol-script/language/builder.ts
+++ b/src/mol-script/language/builder.ts
@@ -11,6 +11,7 @@ import { MolScriptSymbolTable as SymbolTable } from './symbol-table'
 export namespace MolScriptBuilder {
     export const core = SymbolTable.core;
     export const struct = SymbolTable.structureQuery;
+    export const internal = SymbolTable.internal;
 
     /** Atom-name constructor */
     export function atomName(s: string) { return struct.type.atomName([s]); }
diff --git a/src/mol-script/language/symbol-table.ts b/src/mol-script/language/symbol-table.ts
index 3dec8616fedebe9973a69385ca78da09092d2020..b8fde22ab30653331273d04ba027a6a649221489 100644
--- a/src/mol-script/language/symbol-table.ts
+++ b/src/mol-script/language/symbol-table.ts
@@ -6,10 +6,11 @@
 
 import core from './symbol-table/core'
 import structureQuery from './symbol-table/structure-query'
+import internal from './symbol-table/internal'
 import { normalizeTable, symbolList } from './helpers'
 import { MSymbol } from './symbol'
 
-const MolScriptSymbolTable = { core, structureQuery };
+const MolScriptSymbolTable = { core, structureQuery, internal };
 
 normalizeTable(MolScriptSymbolTable);
 
diff --git a/src/mol-script/language/symbol-table/internal.ts b/src/mol-script/language/symbol-table/internal.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d034334841a2b14989588e189195b3893e7cc809
--- /dev/null
+++ b/src/mol-script/language/symbol-table/internal.ts
@@ -0,0 +1,30 @@
+/**
+ * Copyright (c) 2019 Mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author David Sehnal <david.sehnal@gmail.com>
+ */
+
+import Type from '../type'
+import * as Struct from './structure-query'
+import { Arguments, Argument } from '../symbol'
+import { symbol } from '../helpers'
+
+const generator = {
+    '@header': 'Generators',
+
+    bundleElement: symbol(Arguments.Dictionary({
+        // TODO: should we use more universal unit keys? (i.e. based on chain and "operator name")
+        groupedUnits: Argument(Type.Any), // SortedArray<number>[],
+        set: Argument(Type.Any), // SortedArray<UnitIndex>
+        ranges: Argument(Type.Any) // SortedArray<UnitIndex>
+    }), Type.Any), // returns BundleElement
+
+    bundle: symbol(Arguments.Dictionary({
+        elements: Argument(Type.Any) // BundleElement[]
+    }), Struct.Types.ElementSelectionQuery, 'A selection with single structure containing represented by the bundle.'),
+}
+
+export default {
+    '@header': 'Internal Queries',
+    generator
+}
\ No newline at end of file
diff --git a/src/mol-script/runtime/query/table.ts b/src/mol-script/runtime/query/table.ts
index bf45a8836896209acf9bb1f08b4e8fc490cc2d0c..6c1bcf4414f50cc422320c9ce90e5eb0b7f89677 100644
--- a/src/mol-script/runtime/query/table.ts
+++ b/src/mol-script/runtime/query/table.ts
@@ -15,6 +15,7 @@ import { VdwRadius, AtomWeight, AtomNumber } from '../../../mol-model/structure/
 import { cantorPairing } from '../../../mol-data/util';
 import C = QuerySymbolRuntime.Const
 import D = QuerySymbolRuntime.Dynamic
+import { bundleElementImpl, bundleGenerator } from '../../../mol-model/structure/query/queries/internal';
 
 const symbols = [
     // ============= TYPES =============
@@ -320,6 +321,12 @@ const symbols = [
     // ============= BOND PROPERTIES ================
     D(MolScript.structureQuery.linkProperty.order, (ctx, xs) => ctx.atomicLink.order),
     D(MolScript.structureQuery.linkProperty.flags, (ctx, xs) => ctx.atomicLink.type),
+
+
+    ////////////////////////////////////
+    // Internal
+    D(MolScript.internal.generator.bundleElement, (ctx, xs) => bundleElementImpl(xs.groupedUnits(ctx), xs.ranges(ctx), xs.set(ctx))),
+    D(MolScript.internal.generator.bundle, (ctx, xs) => bundleGenerator(xs.elements(ctx))),
 ];
 
 function atomProp(p: (e: StructureElement.Location) => any): (ctx: QueryContext, _: any) => any {