diff --git a/src/mol-state/context.ts b/src/mol-state/context.ts
index bb04a1de101777951c6c67e0cdb8b5f88908c3fe..4ab3b9d3239d5fd48143ae4bfbb01fdd07ab55d6 100644
--- a/src/mol-state/context.ts
+++ b/src/mol-state/context.ts
@@ -6,7 +6,7 @@
 
 import { Subject } from 'rxjs'
 import { StateObject } from './object';
-import { Transform } from './tree/transform';
+import { Transform } from './transform';
 
 interface StateContext {
     events: {
diff --git a/src/mol-state/index.ts b/src/mol-state/index.ts
index 0ebc69a6980415c57a8dcfe28fab11e0ab8d66ff..89b5ea1a56aad8fe5352bfeb50649b20119bc8b5 100644
--- a/src/mol-state/index.ts
+++ b/src/mol-state/index.ts
@@ -9,5 +9,5 @@ export * from './state'
 export * from './transformer'
 export * from './tree'
 export * from './context'
-export * from './tree/transform'
-export * from './tree/selection'
\ No newline at end of file
+export * from './transform'
+export * from './selection'
\ No newline at end of file
diff --git a/src/mol-state/object.ts b/src/mol-state/object.ts
index 61f9142cf8edb433173664ec844742edc2f5b744..c721b58264f3049020943b1ecd5b1ec98ca853ee 100644
--- a/src/mol-state/object.ts
+++ b/src/mol-state/object.ts
@@ -5,7 +5,7 @@
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 
-import { Transform } from './tree/transform';
+import { Transform } from './transform';
 
 /** A mutable state object */
 export interface StateObject<P = unknown, D = unknown> {
@@ -47,6 +47,7 @@ export namespace StateObject {
     }
 
     export interface Node {
+        ref: Transform.Ref,
         state: StateType,
         props: unknown,
         errorText?: string,
diff --git a/src/mol-state/selection.ts b/src/mol-state/selection.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b442e562354a24c4bd31ebc418d527d8d4ee4ba4
--- /dev/null
+++ b/src/mol-state/selection.ts
@@ -0,0 +1,178 @@
+/**
+ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author David Sehnal <david.sehnal@gmail.com>
+ */
+
+import { StateObject } from './object';
+import { State } from './state';
+import { ImmutableTree } from './util/immutable-tree';
+
+namespace StateSelection {
+    export type Selector = Query | Builder | string | StateObject.Node;
+    export type NodeSeq = StateObject.Node[]
+    export type Query = (state: State) => NodeSeq;
+
+    export function select(s: Selector, state: State) {
+        return compile(s)(state);
+    }
+
+    export function compile(s: Selector): Query {
+        const selector = s ? s : root();
+        let query: Query;
+        if (isBuilder(selector)) query = (selector as any).compile();
+        else if (isObj(selector)) query = (byValue(selector) as any).compile();
+        else if (isQuery(selector)) query = selector;
+        else query = (byRef(selector as string) as any).compile();
+        return query;
+    }
+
+    function isObj(arg: any): arg is StateObject.Node {
+        return (arg as StateObject.Node).version !== void 0;
+    }
+
+    function isBuilder(arg: any): arg is Builder {
+        return arg.compile !== void 0;
+    }
+
+    function isQuery(arg: any): arg is Query {
+        return typeof arg === 'function';
+    }
+
+    export interface Builder {
+        flatMap(f: (n: Node) => Node[]): Builder;
+        mapEntity(f: (n: Node) => Node): Builder;
+        unique(): Builder;
+
+        parent(): Builder;
+        first(): Builder;
+        filter(p: (n: Node) => boolean): Builder;
+        subtree(): Builder;
+        children(): Builder;
+        ofType(t: StateObject.Type): Builder;
+        ancestorOfType(t: StateObject.Type): Builder;
+    }
+
+    const BuilderPrototype: any = {};
+
+    function registerModifier(name: string, f: Function) {
+        BuilderPrototype[name] = function (this: any, ...args: any[]) { return f.call(void 0, this, ...args) };
+    }
+
+    function build(compile: () => Query): Builder {
+        return Object.create(BuilderPrototype, { compile: { writable: false, configurable: false, value: compile } });
+    }
+
+    export function root() { return build(() => (state: State) => [state.objects.get(state.tree.rootRef)!]) }
+
+
+    export function byRef(...refs: string[]) {
+        return build(() => (state: State) => {
+            const ret: StateObject.Node[] = [];
+            for (const ref of refs) {
+                const n = state.objects.get(ref);
+                if (!n) continue;
+                ret.push(n);
+            }
+            return ret;
+        });
+    }
+
+    export function byValue(...objects: StateObject.Node[]) { return build(() => (state: State) => objects); }
+
+    registerModifier('flatMap', flatMap);
+    export function flatMap(b: Selector, f: (obj: StateObject.Node, state: State) => NodeSeq) {
+        const q = compile(b);
+        return build(() => (state: State) => {
+            const ret: StateObject.Node[] = [];
+            for (const n of q(state)) {
+                for (const m of f(n, state)) {
+                    ret.push(m);
+                }
+            }
+            return ret;
+        });
+    }
+
+    registerModifier('mapEntity', mapEntity);
+    export function mapEntity(b: Selector, f: (n: StateObject.Node, state: State) => StateObject.Node | undefined) {
+        const q = compile(b);
+        return build(() => (state: State) => {
+            const ret: StateObject.Node[] = [];
+            for (const n of q(state)) {
+                const x = f(n, state);
+                if (x) ret.push(x);
+            }
+            return ret;
+        });
+    }
+
+    registerModifier('unique', unique);
+    export function unique(b: Selector) {
+        const q = compile(b);
+        return build(() => (state: State) => {
+            const set = new Set<string>();
+            const ret: StateObject.Node[] = [];
+            for (const n of q(state)) {
+                if (!set.has(n.ref)) {
+                    set.add(n.ref);
+                    ret.push(n);
+                }
+            }
+            return ret;
+        })
+    }
+
+    registerModifier('first', first);
+    export function first(b: Selector) {
+        const q = compile(b);
+        return build(() => (state: State) => {
+            const r = q(state);
+            return r.length ? [r[0]] : [];
+        });
+    }
+
+    registerModifier('filter', filter);
+    export function filter(b: Selector, p: (n: StateObject.Node) => boolean) { return flatMap(b, n => p(n) ? [n] : []); }
+
+    registerModifier('subtree', subtree);
+    export function subtree(b: Selector) {
+        return flatMap(b, (n, s) => {
+            const nodes = [] as string[];
+            ImmutableTree.doPreOrder(s.tree, s.tree.nodes.get(n.ref), nodes, (x, _, ctx) => { ctx.push(x.ref) });
+            return nodes.map(x => s.objects.get(x)!);
+        });
+    }
+
+    registerModifier('children', children);
+    export function children(b: Selector) {
+        return flatMap(b, (n, s) => {
+            const nodes: StateObject.Node[] = [];
+            s.tree.nodes.get(n.ref)!.children.forEach(c => nodes.push(s.objects.get(c!)!));
+            return nodes;
+        });
+    }
+
+    registerModifier('ofType', ofType);
+    export function ofType(b: Selector, t: StateObject.Type) { return filter(b, n => n.obj ? n.obj.type === t : false); }
+
+    registerModifier('ancestorOfType', ancestorOfType);
+    export function ancestorOfType(b: Selector, t: StateObject.Type) { return unique(mapEntity(b, (n, s) => findAncestorOfType(s, n.ref, t))); }
+
+    registerModifier('parent', parent);
+    export function parent(b: Selector) { return unique(mapEntity(b, (n, s) => s.objects.get(s.tree.nodes.get(n.ref)!.parent))); }
+
+    function findAncestorOfType({ tree, objects }: State, root: string, type: StateObject.Type): StateObject.Node | undefined {
+        let current = tree.nodes.get(root)!;
+        while (true) {
+            current = tree.nodes.get(current.parent)!;
+            if (current.ref === tree.rootRef) {
+                return objects.get(tree.rootRef);
+            }
+            const obj = objects.get(current.ref)!.obj!;
+            if (obj.type === type) return objects.get(current.ref);
+        }
+    }
+}
+
+export { StateSelection }
\ No newline at end of file
diff --git a/src/mol-state/state.ts b/src/mol-state/state.ts
index b586e5c2cc4de70b4115bf2bfe505e398f451160..873579731e6a35b211abb495835b2e115dea54bb 100644
--- a/src/mol-state/state.ts
+++ b/src/mol-state/state.ts
@@ -6,7 +6,7 @@
 
 import { StateObject } from './object';
 import { StateTree } from './tree';
-import { Transform } from './tree/transform';
+import { Transform } from './transform';
 import { ImmutableTree } from './util/immutable-tree';
 import { Transformer } from './transformer';
 import { StateContext } from './context';
@@ -23,13 +23,20 @@ export namespace State {
     export type Ref = Transform.Ref
     export type Objects = Map<Ref, StateObject.Node>
 
-    export function create(params?: { globalContext?: unknown, defaultObjectProps: unknown }) {
+    export function create(rootObject: StateObject, params?: { globalContext?: unknown, defaultObjectProps: unknown }) {
         const tree = StateTree.create();
         const objects: Objects = new Map();
         const root = tree.getValue(tree.rootRef)!;
         const defaultObjectProps = (params && params.defaultObjectProps) || { }
 
-        objects.set(tree.rootRef, { obj: void 0 as any, state: StateObject.StateType.Ok, version: root.version, props: { ...defaultObjectProps } });
+        rootObject.ref = tree.rootRef;
+        objects.set(tree.rootRef, {
+            ref: tree.rootRef,
+            obj: rootObject,
+            state: StateObject.StateType.Ok,
+            version: root.version,
+            props: { ...defaultObjectProps }
+        });
 
         return {
             tree,
@@ -126,7 +133,7 @@ export namespace State {
             obj.state = state;
             obj.errorText = errorText;
         } else {
-            const obj = { state, version: UUID.create(), errorText, props: { ...ctx.stateCtx.defaultObjectProps } };
+            const obj: StateObject.Node = { ref, state, version: UUID.create(), errorText, props: { ...ctx.stateCtx.defaultObjectProps } };
             ctx.objects.set(ref, obj);
             changed = true;
         }
@@ -159,7 +166,7 @@ export namespace State {
         }
     }
 
-    function findParent(tree: StateTree, objects: Objects, root: Ref, types: { type: StateObject.Type }[]): StateObject {
+    function findAncestor(tree: StateTree, objects: Objects, root: Ref, types: { type: StateObject.Type }[]): StateObject {
         let current = tree.nodes.get(root)!;
         while (true) {
             current = tree.nodes.get(current.parent)!;
@@ -200,13 +207,14 @@ export namespace State {
     async function updateNode(ctx: UpdateContext, currentRef: Ref) {
         const { oldTree, tree, objects } = ctx;
         const transform = tree.getValue(currentRef)!;
-        const parent = findParent(tree, objects, currentRef, transform.transformer.definition.from);
+        const parent = findAncestor(tree, objects, currentRef, transform.transformer.definition.from);
         // console.log('parent', transform.transformer.id, transform.transformer.definition.from[0].type, parent ? parent.ref : 'undefined')
         if (!oldTree.nodes.has(currentRef) || !objects.has(currentRef)) {
             // console.log('creating...', transform.transformer.id, oldTree.nodes.has(currentRef), objects.has(currentRef));
             const obj = await createObject(ctx, transform.transformer, parent, transform.params);
             obj.ref = currentRef;
             objects.set(currentRef, {
+                ref: currentRef,
                 obj,
                 state: StateObject.StateType.Ok,
                 version: transform.version,
@@ -222,6 +230,7 @@ export namespace State {
                     const obj = await createObject(ctx, transform.transformer, parent, transform.params);
                     obj.ref = currentRef;
                     objects.set(currentRef, {
+                        ref: currentRef,
                         obj,
                         state: StateObject.StateType.Ok,
                         version: transform.version,
diff --git a/src/mol-state/tree/transform.ts b/src/mol-state/transform.ts
similarity index 78%
rename from src/mol-state/tree/transform.ts
rename to src/mol-state/transform.ts
index 6e494e9d2309cc4928d258c8b48e3994c2604a08..f80137241fc821a9cf9dc49e3759da3a0ae2040d 100644
--- a/src/mol-state/tree/transform.ts
+++ b/src/mol-state/transform.ts
@@ -4,8 +4,8 @@
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 
-import { StateObject } from '../object';
-import { Transformer } from '../transformer';
+import { StateObject } from './object';
+import { Transformer } from './transformer';
 import { UUID } from 'mol-util';
 
 export interface Transform<A extends StateObject = StateObject, B extends StateObject = StateObject, P = unknown> {
@@ -19,22 +19,16 @@ export interface Transform<A extends StateObject = StateObject, B extends StateO
 export namespace Transform {
     export type Ref = string
 
-    export interface Props {
-        ref: Ref
-    }
-
-    export enum Flags {
-        // Indicates that the transform was generated by a behaviour and should not be automatically updated
-        Generated
-    }
+    export interface Options { ref?: Ref, defaultProps?: unknown }
 
-    export function create<A extends StateObject, B extends StateObject, P>(transformer: Transformer<A, B, P>, params?: P, props?: Partial<Props>): Transform<A, B, P> {
-        const ref = props && props.ref ? props.ref : UUID.create() as string as Ref;
+    export function create<A extends StateObject, B extends StateObject, P>(transformer: Transformer<A, B, P>, params?: P, options?: Options): Transform<A, B, P> {
+        const ref = options && options.ref ? options.ref : UUID.create() as string as Ref;
         return {
             transformer,
-            params: params || { } as any,
+            params: params || {} as any,
             ref,
-            version: UUID.create()
+            version: UUID.create(),
+            defaultProps: options && options.defaultProps
         }
     }
 
diff --git a/src/mol-state/transformer.ts b/src/mol-state/transformer.ts
index 701085fff0adf0b6ca0d63357ed78e2fd9f354b0..972d2183315d6f21faee3d50ce5626c805a4a373 100644
--- a/src/mol-state/transformer.ts
+++ b/src/mol-state/transformer.ts
@@ -6,10 +6,10 @@
 
 import { Task } from 'mol-task';
 import { StateObject } from './object';
-import { Transform } from './tree/transform';
+import { Transform } from './transform';
 
 export interface Transformer<A extends StateObject = StateObject, B extends StateObject = StateObject, P = unknown> {
-    apply(params?: P, props?: Partial<Transform.Props>): Transform<A, B, P>,
+    apply(params?: P, props?: Partial<Transform.Options>): Transform<A, B, P>,
     readonly namespace: string,
     readonly id: Transformer.Id,
     readonly definition: Transformer.Definition<A, B, P>
diff --git a/src/mol-state/tree.ts b/src/mol-state/tree.ts
index 06e83f0ae4236d22443b8774b249e0498088a528..4656b98f6367397d42f07e45e08e696ee2a45b58 100644
--- a/src/mol-state/tree.ts
+++ b/src/mol-state/tree.ts
@@ -4,7 +4,7 @@
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 
-import { Transform } from './tree/transform';
+import { Transform } from './transform';
 import { ImmutableTree } from './util/immutable-tree';
 import { Transformer } from './transformer';
 import { StateObject } from './object';
@@ -59,7 +59,7 @@ namespace StateTree {
         }
 
         export class To<A extends StateObject> implements Builder {
-            apply<T extends Transformer<A, any, any>>(tr: T, params?: Transformer.Params<T>, props?: Partial<Transform.Props>): To<Transformer.To<T>> {
+            apply<T extends Transformer<A, any, any>>(tr: T, params?: Transformer.Params<T>, props?: Partial<Transform.Options>): To<Transformer.To<T>> {
                 const t = tr.apply(params, props);
                 this.state.tree.add(this.ref, t);
                 return new To(this.state, t.ref);
diff --git a/src/mol-state/tree/selection.ts b/src/mol-state/tree/selection.ts
deleted file mode 100644
index 84130cdc3dfe2d525171ec6063abbfe696bc67cc..0000000000000000000000000000000000000000
--- a/src/mol-state/tree/selection.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-/**
- * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
- *
- * @author David Sehnal <david.sehnal@gmail.com>
- */
-
-namespace StateTreeSelection {
-
-}
-
-export { StateTreeSelection }
\ No newline at end of file
diff --git a/src/perf-tests/state.ts b/src/perf-tests/state.ts
index 0344b8dc4ff318ab8c32d9cba4d381f06407f31f..183063a9ad7d1c15ad84f0595a11f4d2fbfff4cb 100644
--- a/src/perf-tests/state.ts
+++ b/src/perf-tests/state.ts
@@ -1,4 +1,4 @@
-import { State, StateObject, StateTree, Transformer } from 'mol-state';
+import { State, StateObject, StateTree, Transformer, StateSelection } from 'mol-state';
 import { Task } from 'mol-task';
 import * as util from 'util';
 
@@ -74,7 +74,7 @@ function hookEvents(state: State) {
 }
 
 export async function testState() {
-    const state = State.create();
+    const state = State.create(new Root({ label: 'Root' }, { }));
     hookEvents(state);
 
     const tree = state.tree;
@@ -105,6 +105,12 @@ export async function testState() {
     console.log('----------------');
     const state2 = await State.update(state1, treeFromJson).run();
     console.log(util.inspect(state2.objects, true, 3, true));
+
+    console.log('----------------');
+
+    const q = StateSelection.byRef('square').parent();
+    const sel = StateSelection.select(q, state2);
+    console.log(sel);
 }
 
 testState();