diff --git a/package-lock.json b/package-lock.json
index eef02eb8afb9f6be4baa39aacd2c15fb8fc6b413..25dccbec517c83159a708ec29ec305d1a34fc3ef 100644
Binary files a/package-lock.json and b/package-lock.json differ
diff --git a/src/apps/canvas/app.ts b/src/apps/canvas/app.ts
index 4da00cdbf9eb242a0913d7b8adbda6b922f53929..862b01b07cb334e986aa927e78d00197ecb2618f 100644
--- a/src/apps/canvas/app.ts
+++ b/src/apps/canvas/app.ts
@@ -5,21 +5,22 @@
  */
 
 import Viewer from 'mol-view/viewer';
-import { getCifFromUrl, getModelsFromMmcif, getCifFromFile, getCcp4FromUrl } from './util';
+import { getCifFromUrl, getModelsFromMmcif, getCifFromFile, getCcp4FromUrl, getVolumeFromCcp4, getCcp4FromFile } from './util';
 import { StructureView } from './structure-view';
 import { BehaviorSubject } from 'rxjs';
 import { CifBlock } from 'mol-io/reader/cif';
-import { volumeFromCcp4 } from 'mol-model/volume/formats/ccp4';
-import { VolumeRepresentation } from 'mol-geo/representation/volume';
-import IsosurfaceVisual from 'mol-geo/representation/volume/isosurface';
+import { VolumeView } from './volume-view';
+import { Ccp4File } from 'mol-io/reader/ccp4/schema';
 
 export class App {
     viewer: Viewer
     container: HTMLDivElement | null = null;
     canvas: HTMLCanvasElement | null = null;
     structureView: StructureView | null = null;
+    volumeView: VolumeView | null = null;
 
-    pdbIdLoaded: BehaviorSubject<StructureView | null> = new BehaviorSubject<StructureView | null>(null)
+    structureLoaded: BehaviorSubject<StructureView | null> = new BehaviorSubject<StructureView | null>(null)
+    volumeLoaded: BehaviorSubject<VolumeView | null> = new BehaviorSubject<VolumeView | null>(null)
 
     initViewer(_canvas: HTMLCanvasElement, _container: HTMLDivElement) {
         this.canvas = _canvas
@@ -63,7 +64,7 @@ export class App {
     async loadMmcif(cif: CifBlock, assemblyId?: string) {
         const models = await this.runTask(getModelsFromMmcif(cif), 'Build models')
         this.structureView = await this.runTask(StructureView(this, this.viewer, models, { assemblyId }), 'Init structure view')
-        this.pdbIdLoaded.next(this.structureView)
+        this.structureLoaded.next(this.structureView)
     }
 
     async loadPdbIdOrMmcifUrl(idOrUrl: string, options?: { assemblyId?: string, binary?: boolean }) {
@@ -82,17 +83,21 @@ export class App {
 
     //
 
-    async loadCcp4File() {
-        const url = 'http://localhost:8091/ngl/data/betaGal.mrc'
-        const ccp4 = await getCcp4FromUrl(url)
-        console.log(ccp4)
-        const volume = await volumeFromCcp4(ccp4).run()
-        const volRepr = VolumeRepresentation(IsosurfaceVisual)
-        await volRepr.createOrUpdate({
-            isoValue: 1
-        }, volume).run()
-        this.viewer.add(volRepr)
-        console.log('volRepr', volRepr)
-        this.viewer.requestDraw(true)
+    async loadCcp4(ccp4: Ccp4File) {
+        const volume = await this.runTask(getVolumeFromCcp4(ccp4), 'Get Volume')
+        this.volumeView = await this.runTask(VolumeView(this, this.viewer, volume), 'Init volume view')
+        this.volumeLoaded.next(this.volumeView)
+    }
+
+    async loadCcp4File(file: File) {
+        if (this.volumeView) this.volumeView.destroy();
+        const ccp4 = await this.runTask(getCcp4FromFile(file), 'Load CCP4 from file')
+        this.loadCcp4(ccp4)
+    }
+
+    async loadCcp4Url(url: string) {
+        if (this.volumeView) this.volumeView.destroy();
+        const ccp4 = await this.runTask(getCcp4FromUrl(url), 'Load CCP4 from URL')
+        this.loadCcp4(ccp4)
     }
 }
\ No newline at end of file
diff --git a/src/apps/canvas/component/app.tsx b/src/apps/canvas/component/app.tsx
index bc15e244bee3d02e4e7d226965cbb1d3513a274f..7f2d390928f589244c393de613d88f7209456de3 100644
--- a/src/apps/canvas/component/app.tsx
+++ b/src/apps/canvas/component/app.tsx
@@ -10,6 +10,8 @@ import { App } from '../app';
 import { Viewport } from './viewport';
 import { StructureViewComponent } from './structure-view';
 import { Examples } from '../examples';
+import { VolumeViewComponent } from './volume-view';
+import { VolumeView } from '../volume-view';
 
 export interface AppProps {
     app: App
@@ -17,25 +19,28 @@ export interface AppProps {
 
 export interface AppState {
     structureView: StructureView | null,
+    volumeView: VolumeView | null,
     binary: boolean
 }
 
 export class AppComponent extends React.Component<AppProps, AppState> {
     state = {
         structureView: this.props.app.structureView,
+        volumeView: this.props.app.volumeView,
         binary: false
     }
 
     componentDidMount() {
-        this.props.app.pdbIdLoaded.subscribe((structureView) => {
-            this.setState({
-                structureView: this.props.app.structureView
-            })
+        this.props.app.structureLoaded.subscribe((structureView) => {
+            this.setState({ structureView: this.props.app.structureView })
+        })
+        this.props.app.volumeLoaded.subscribe((volumeView) => {
+            this.setState({ volumeView: this.props.app.volumeView })
         })
     }
 
     render() {
-        const { structureView } = this.state
+        const { structureView, volumeView } = this.state
 
         return <div style={{width: '100%', height: '100%'}}>
             <div style={{left: '0px', right: '350px', height: '100%', position: 'absolute'}}>
@@ -69,6 +74,16 @@ export class AppComponent extends React.Component<AppProps, AppState> {
                         }}
                     />
                 </div>
+                <div>
+                    <span>Load CCP4/MRC file </span>
+                    <input
+                        accept='*.ccp4,*.mrc, *.map'
+                        type='file'
+                        onChange={e => {
+                            if (e.target.files) this.props.app.loadCcp4File(e.target.files[0])
+                        }}
+                    />
+                </div>
                 <div>
                     <span>Load example </span>
                     <select
@@ -87,6 +102,10 @@ export class AppComponent extends React.Component<AppProps, AppState> {
                 <div style={{marginBottom: '10px'}}>
                     {structureView ? <StructureViewComponent structureView={structureView} /> : ''}
                 </div>
+                <hr/>
+                <div style={{marginBottom: '10px'}}>
+                    {volumeView ? <VolumeViewComponent volumeView={volumeView} /> : ''}
+                </div>
             </div>
         </div>;
     }
diff --git a/src/apps/canvas/component/structure-view.tsx b/src/apps/canvas/component/structure-view.tsx
index 91bbe6cc93365ccf4ab00706fcbfa016ee148205..9518d047904f6f4e200dd3e57988826e81b3b8b8 100644
--- a/src/apps/canvas/component/structure-view.tsx
+++ b/src/apps/canvas/component/structure-view.tsx
@@ -10,17 +10,6 @@ import { StructureRepresentation } from 'mol-geo/representation/structure';
 import { RepresentationComponent } from './representation';
 import { Representation } from 'mol-geo/representation';
 
-// export function FileInput (props: {
-//     accept: string
-//     onChange: (v: FileList | null) => void,
-// }) {
-//     return <input
-//         accept={props.accept || '*.*'}
-//         type='file'
-//         onChange={e => props.onChange.call(null, e.target.files)}
-//     />
-// }
-
 export interface StructureViewComponentProps {
     structureView: StructureView
 }
diff --git a/src/apps/canvas/component/volume-view.tsx b/src/apps/canvas/component/volume-view.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..f816102bbf72f8599e885a1ef1d12c4041e7ff20
--- /dev/null
+++ b/src/apps/canvas/component/volume-view.tsx
@@ -0,0 +1,104 @@
+/**
+ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import * as React from 'react'
+import { RepresentationComponent } from './representation';
+import { Representation } from 'mol-geo/representation';
+import { VolumeView } from '../volume-view';
+import { VolumeRepresentation } from 'mol-geo/representation/volume';
+
+export interface VolumeViewComponentProps {
+    volumeView: VolumeView
+}
+
+export interface VolumeViewComponentState {
+    volumeView: VolumeView
+    label: string
+    active: { [k: string]: boolean }
+    volumeRepresentations: { [k: string]: VolumeRepresentation<any> }
+}
+
+export class VolumeViewComponent extends React.Component<VolumeViewComponentProps, VolumeViewComponentState> {
+    state = this.stateFromVolumeView(this.props.volumeView)
+
+    private stateFromVolumeView(vv: VolumeView) {
+        return {
+            volumeView: vv,
+            label: vv.label,
+            active: vv.active,
+            volumeRepresentations: vv.volumeRepresentations
+        }
+    }
+
+    componentWillMount() {
+        this.setState(this.stateFromVolumeView(this.props.volumeView))
+    }
+
+    componentDidMount() {
+        const vv = this.props.volumeView
+
+        this.props.volumeView.updated.subscribe(() => this.setState({
+            volumeRepresentations: vv.volumeRepresentations
+        }))
+    }
+
+    componentWillReceiveProps(nextProps: VolumeViewComponentProps) {
+        if (nextProps.volumeView !== this.props.volumeView) {
+            this.setState(this.stateFromVolumeView(nextProps.volumeView))
+
+            nextProps.volumeView.updated.subscribe(() => this.setState({
+                volumeRepresentations: nextProps.volumeView.volumeRepresentations
+            }))
+        }
+    }
+
+    // async update(state: Partial<VolumeViewComponentState>) {
+    //     const vv = this.state.volumeView
+    //     this.setState(this.stateFromVolumeView(vv))
+    // }
+
+    render() {
+        const { volumeView, label, active, volumeRepresentations } = this.state
+
+        return <div>
+            <div>
+                <h2>{label}</h2>
+            </div>
+            <div>
+                <div>
+                    <h4>Active</h4>
+                    { Object.keys(active).map((k, i) => {
+                        return <div key={i}>
+                            <input
+                                type='checkbox'
+                                checked={active[k]}
+                                onChange={(e) => {
+                                    volumeView.setVolumeRepresentation(k, e.target.checked)
+                                }}
+                            /> {k}
+                        </div>
+                    } ) }
+                </div>
+                <div>
+                    <h3>Volume Representations</h3>
+                    { Object.keys(volumeRepresentations).map((k, i) => {
+                        if (active[k]) {
+                            return <div key={i}>
+                                <RepresentationComponent
+                                    repr={volumeRepresentations[k] as Representation<any>}
+                                    viewer={volumeView.viewer}
+                                    app={volumeView.app}
+                                />
+                            </div>
+                        } else {
+                            return ''
+                        }
+                    } ) }
+                </div>
+            </div>
+        </div>;
+    }
+}
\ No newline at end of file
diff --git a/src/apps/canvas/util.ts b/src/apps/canvas/util.ts
index 226785421fafbb0e93aa98f1a58cccd0e41534a8..dcbc58697bd99d9897066ba0cd1c120bb9c6b27d 100644
--- a/src/apps/canvas/util.ts
+++ b/src/apps/canvas/util.ts
@@ -4,11 +4,13 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { readUrl, readFile, readUrlAsBuffer } from 'mol-util/read';
+import { readUrl, readFile, readUrlAsBuffer, readFileAsBuffer } from 'mol-util/read';
 import CIF, { CifBlock } from 'mol-io/reader/cif'
 import { Model, Format, StructureSymmetry, Structure } from 'mol-model/structure';
 import CCP4 from 'mol-io/reader/ccp4/parser'
 import { FileHandle } from 'mol-io/common/file-handle';
+import { Ccp4File } from 'mol-io/reader/ccp4/schema';
+import { volumeFromCcp4 } from 'mol-model/volume/formats/ccp4';
 // import { parse as parseObj } from 'mol-io/reader/obj/parser'
 
 // export async function getObjFromUrl(url: string) {
@@ -53,9 +55,17 @@ export async function getCcp4FromUrl(url: string) {
     return getCcp4FromData(await readUrlAsBuffer(url))
 }
 
+export async function getCcp4FromFile(file: File) {
+    return getCcp4FromData(await readFileAsBuffer(file))
+}
+
 export async function getCcp4FromData(data: Uint8Array) {
     const file = FileHandle.fromBuffer(data)
     const parsed = await CCP4(file).run()
     if (parsed.isError) throw parsed
     return parsed.result
+}
+
+export async function getVolumeFromCcp4(ccp4: Ccp4File) {
+    return await volumeFromCcp4(ccp4).run()
 }
\ No newline at end of file
diff --git a/src/apps/canvas/volume-view.ts b/src/apps/canvas/volume-view.ts
new file mode 100644
index 0000000000000000000000000000000000000000..968fca0727000d3d1245596f9f88dced2d730615
--- /dev/null
+++ b/src/apps/canvas/volume-view.ts
@@ -0,0 +1,98 @@
+/**
+ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import Viewer from 'mol-view/viewer';
+import { BehaviorSubject } from 'rxjs';
+import { App } from './app';
+import { Progress } from 'mol-task';
+import { VolumeData } from 'mol-model/volume';
+import { VolumeRepresentation } from 'mol-geo/representation/volume';
+import IsosurfaceVisual from 'mol-geo/representation/volume/isosurface';
+import { Vec3 } from 'mol-math/linear-algebra';
+
+export interface VolumeView {
+    readonly app: App
+    readonly viewer: Viewer
+
+    readonly label: string
+    readonly volume: VolumeData
+
+    readonly active: { [k: string]: boolean }
+    readonly volumeRepresentations: { [k: string]: VolumeRepresentation<any> }
+    readonly updated: BehaviorSubject<null>
+
+    setVolumeRepresentation(name: string, value: boolean): void
+    destroy: () => void
+}
+
+interface StructureViewProps {
+    assemblyId?: string
+    symmetryFeatureId?: number
+}
+
+export async function VolumeView(app: App, viewer: Viewer, volume: VolumeData, props: StructureViewProps = {}): Promise<VolumeView> {
+    const active: { [k: string]: boolean } = {
+        isosurface: true,
+        volume: false,
+    }
+
+    const volumeRepresentations: { [k: string]: VolumeRepresentation<any> } = {
+        isosurface: VolumeRepresentation(IsosurfaceVisual),
+    }
+
+    const updated: BehaviorSubject<null> = new BehaviorSubject<null>(null)
+
+    let label: string = 'Volume'
+
+    async function setVolumeRepresentation(k: string, value: boolean) {
+        active[k] = value
+        await createVolumeRepr()
+    }
+
+    async function createVolumeRepr() {
+        for (const k in volumeRepresentations) {
+            if (active[k]) {
+                await app.runTask(volumeRepresentations[k].createOrUpdate({}, volume).run(
+                    progress => console.log(Progress.format(progress))
+                ), 'Create/update representation')
+                viewer.add(volumeRepresentations[k])
+            } else {
+                viewer.remove(volumeRepresentations[k])
+            }
+        }
+
+        // const center = Vec3.clone(volume.cell.size)
+        // Vec3.scale(center, center, 0.5)
+        // viewer.center(center)
+
+        updated.next(null)
+        viewer.requestDraw(true)
+        console.log('stats', viewer.stats)
+    }
+
+    await createVolumeRepr()
+
+    return {
+        app,
+        viewer,
+
+        get label() { return label },
+        volume,
+
+        active,
+        volumeRepresentations,
+        setVolumeRepresentation,
+        updated,
+
+        destroy: () => {
+            for (const k in volumeRepresentations) {
+                viewer.remove(volumeRepresentations[k])
+                volumeRepresentations[k].destroy()
+            }
+            viewer.requestDraw(true)
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/mol-geo/representation/volume/index.ts b/src/mol-geo/representation/volume/index.ts
index 5f22df51d937b41ce3b94cc69802090748500146..18e9722ddb304299c7f833b4863173dc7bcbae83 100644
--- a/src/mol-geo/representation/volume/index.ts
+++ b/src/mol-geo/representation/volume/index.ts
@@ -5,7 +5,6 @@
  */
 
 import { Task } from 'mol-task'
-import { RenderObject } from 'mol-gl/render-object';
 import { RepresentationProps, Representation, Visual } from '..';
 import { VolumeData } from 'mol-model/volume';
 import { PickingId } from '../../geometry/picking';
@@ -13,32 +12,41 @@ import { Loci, EmptyLoci } from 'mol-model/loci';
 import { MarkerAction } from '../../geometry/marker-data';
 import { Geometry } from '../../geometry/geometry';
 import { paramDefaultValues } from 'mol-view/parameter';
+import { IsosurfaceParams } from './isosurface';
 
 export interface VolumeVisual<P extends RepresentationProps = {}> extends Visual<VolumeData, P> { }
 
 export interface VolumeRepresentation<P extends RepresentationProps = {}> extends Representation<VolumeData, P> { }
 
 export const VolumeParams = {
-    ...Geometry.Params
+    ...Geometry.Params,
+    ...IsosurfaceParams
 }
 export const DefaultVolumeProps = paramDefaultValues(VolumeParams)
 export type VolumeProps = typeof DefaultVolumeProps
 
 export function VolumeRepresentation<P extends VolumeProps>(visualCtor: (volumeData: VolumeData) => VolumeVisual<P>): VolumeRepresentation<P> {
-    const renderObjects: RenderObject[] = []
-    let _volumeData: VolumeData
+    let visual: VolumeVisual<any>
     let _props: P
+    let busy = false
 
     function createOrUpdate(props: Partial<P> = {}, volumeData?: VolumeData) {
         _props = Object.assign({}, DefaultVolumeProps, _props, props)
         return Task.create('VolumeRepresentation.create', async ctx => {
-            if (volumeData) {
-                _volumeData = volumeData
-                const visual = visualCtor(_volumeData)
-                await visual.createOrUpdate(ctx, props, _volumeData)
-                if (visual.renderObject) renderObjects.push(visual.renderObject)
+            // TODO queue it somehow
+            if (busy) return
+
+            if (!visual && !volumeData) {
+                throw new Error('volumeData missing')
+            } else if (volumeData && !visual) {
+                busy = true
+                visual = visualCtor(volumeData)
+                await visual.createOrUpdate(ctx, props, volumeData)
+                busy = false
             } else {
-                throw new Error('missing volumeData')
+                busy = true
+                await visual.createOrUpdate(ctx, props, volumeData)
+                busy = false
             }
         });
     }
@@ -46,7 +54,9 @@ export function VolumeRepresentation<P extends VolumeProps>(visualCtor: (volumeD
     return {
         label: 'Volume mesh',
         params: VolumeParams,
-        get renderObjects () { return renderObjects },
+        get renderObjects() {
+            return visual && visual.renderObject ? [ visual.renderObject ] : []
+        },
         get props () { return _props },
         createOrUpdate,
         getLoci(pickingId: PickingId) {
diff --git a/src/mol-geo/representation/volume/isosurface.ts b/src/mol-geo/representation/volume/isosurface.ts
index cc472eeaba5514af3ea59384a420a0d6cf8c0697..72be9abcf2ec993c42b52be757523a179dbeb20d 100644
--- a/src/mol-geo/representation/volume/isosurface.ts
+++ b/src/mol-geo/representation/volume/isosurface.ts
@@ -17,58 +17,91 @@ import { Loci, EmptyLoci } from 'mol-model/loci';
 import { LocationIterator } from '../../util/location-iterator';
 import { NullLocation } from 'mol-model/location';
 import { createIdentityTransform } from '../../geometry/transform-data';
-import { createRenderableState } from '../../geometry/geometry';
+import { createRenderableState, updateRenderableState } from '../../geometry/geometry';
 import { paramDefaultValues, NumberParam } from 'mol-view/parameter';
+import { ValueCell } from 'mol-util';
 
-export function computeVolumeSurface(volume: VolumeData, isoValue: VolumeIsoValue) {
+export function computeVolumeSurface(volume: VolumeData, isoValue: VolumeIsoValue, mesh?: Mesh) {
     return Task.create<Mesh>('Volume Surface', async ctx => {
         ctx.update({ message: 'Marching cubes...' });
 
-        const mesh = await computeMarchingCubesMesh({
+        const surface = await computeMarchingCubesMesh({
             isoLevel: VolumeIsoValue.toAbsolute(isoValue).absoluteValue,
             scalarField: volume.data
-        }).runAsChild(ctx);
+        }, mesh).runAsChild(ctx);
 
         const transform = VolumeData.getGridToCartesianTransform(volume);
         ctx.update({ message: 'Transforming mesh...' });
-        Mesh.transformImmediate(mesh, transform);
+        Mesh.transformImmediate(surface, transform);
+        Mesh.computeNormalsImmediate(surface)
 
-        return mesh;
+        return surface;
     });
 }
 
 export const IsosurfaceParams = {
     ...Mesh.Params,
-    isoValue: NumberParam('Iso Value', '', 2, -5, 5, 0.01),
+    isoValue: NumberParam('Iso Value', '', 2, -15, 15, 0.01),
 }
 export const DefaultIsosurfaceProps = paramDefaultValues(IsosurfaceParams)
 export type IsosurfaceProps = typeof DefaultIsosurfaceProps
 
 export default function IsosurfaceVisual(): VolumeVisual<IsosurfaceProps> {
-    let renderObject: MeshRenderObject
     let currentProps = DefaultIsosurfaceProps
+    let renderObject: MeshRenderObject
+    let currentVolume: VolumeData
+    let mesh: Mesh
+
+    async function create(ctx: RuntimeContext, volume: VolumeData, props: Partial<IsosurfaceProps> = {}) {
+        currentProps = { ...DefaultIsosurfaceProps, ...props }
+
+        mesh = await computeVolumeSurface(volume,  VolumeIsoValue.relative(volume.dataStats, currentProps.isoValue)).runAsChild(ctx)
+
+        const locationIt = LocationIterator(1, 1, () => NullLocation)
+        const transform = createIdentityTransform()
+
+        const values = await Mesh.createValues(ctx, mesh, transform, locationIt, currentProps)
+        const state = createRenderableState(currentProps)
+
+        renderObject = createMeshRenderObject(values, state)
+    }
+
+    async function update(ctx: RuntimeContext, props: Partial<IsosurfaceProps> = {}) {
+        const newProps = { ...currentProps, ...props }
+
+        let createMesh = false
+
+        if (newProps.isoValue !== currentProps.isoValue) createMesh = true
+
+        if (createMesh) {
+            mesh = await computeVolumeSurface(currentVolume,  VolumeIsoValue.relative(currentVolume.dataStats, currentProps.isoValue), mesh).runAsChild(ctx)
+            ValueCell.update(renderObject.values.drawCount, mesh.triangleCount * 3)
+        }
+
+        Mesh.updateValues(renderObject.values, newProps)
+        updateRenderableState(renderObject.state, newProps)
+
+        currentProps = newProps
+        return true
+    }
 
     return {
         get renderObject () { return renderObject },
         async createOrUpdate(ctx: RuntimeContext, props: Partial<IsosurfaceProps> = {}, volume?: VolumeData) {
-            currentProps = { ...DefaultIsosurfaceProps, ...props }
-
-            console.log('MOINMOIN')
-            if (!volume) return
-
-            const mesh = await computeVolumeSurface(volume,  VolumeIsoValue.relative(volume.dataStats, currentProps.isoValue)).runAsChild(ctx)
-            if (!props.flatShaded) {
-                Mesh.computeNormalsImmediate(mesh)
+            if (!volume && !currentVolume) {
+                throw new Error('missing volume')
+            } else if (volume && (!currentVolume || !renderObject)) {
+                currentVolume = volume
+                await create(ctx, volume, props)
+            } else if (volume && volume !== currentVolume) {
+                currentVolume = volume
+                await create(ctx, volume, props)
+            } else {
+                await update(ctx, props)
             }
 
-            const locationIt = LocationIterator(1, 1, () => NullLocation)
-            const transform = createIdentityTransform()
-
-            const values = await Mesh.createValues(ctx, mesh, transform, locationIt, currentProps)
-            const state = createRenderableState(currentProps)
+            currentProps = { ...DefaultIsosurfaceProps, ...props }
 
-            renderObject = createMeshRenderObject(values, state)
-            console.log('renderObject', renderObject)
         },
         getLoci(pickingId: PickingId) {
             // TODO
diff --git a/src/mol-math/linear-algebra/3d/mat4.ts b/src/mol-math/linear-algebra/3d/mat4.ts
index 442f8e3927a3f2ad3358fabba82a26cbe01096fb..c74acbe2a8f7fdb681880bfa0afe0ac6b63cc3b4 100644
--- a/src/mol-math/linear-algebra/3d/mat4.ts
+++ b/src/mol-math/linear-algebra/3d/mat4.ts
@@ -176,6 +176,72 @@ namespace Mat4 {
         return Mat4.copy(Mat4.zero(), a);
     }
 
+    /**
+     * Returns the translation vector component of a transformation matrix.
+     */
+    export function getTranslation(out: Vec3, mat: Mat4) {
+        out[0] = mat[12];
+        out[1] = mat[13];
+        out[2] = mat[14];
+        return out;
+    }
+
+    /**
+     * Returns the scaling factor component of a transformation matrix.
+     */
+    export function getScaling(out: Vec3, mat: Mat4) {
+        let m11 = mat[0];
+        let m12 = mat[1];
+        let m13 = mat[2];
+        let m21 = mat[4];
+        let m22 = mat[5];
+        let m23 = mat[6];
+        let m31 = mat[8];
+        let m32 = mat[9];
+        let m33 = mat[10];
+        out[0] = Math.sqrt(m11 * m11 + m12 * m12 + m13 * m13);
+        out[1] = Math.sqrt(m21 * m21 + m22 * m22 + m23 * m23);
+        out[2] = Math.sqrt(m31 * m31 + m32 * m32 + m33 * m33);
+        return out;
+    }
+
+    /**
+     * Returns a quaternion representing the rotational component of a transformation matrix.
+     */
+    export function getRotation(out: Quat, mat: Mat4) {
+        // Algorithm taken from http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm
+        let trace = mat[0] + mat[5] + mat[10];
+        let S = 0;
+
+        if (trace > 0) {
+            S = Math.sqrt(trace + 1.0) * 2;
+            out[3] = 0.25 * S;
+            out[0] = (mat[6] - mat[9]) / S;
+            out[1] = (mat[8] - mat[2]) / S;
+            out[2] = (mat[1] - mat[4]) / S;
+        } else if ((mat[0] > mat[5]) && (mat[0] > mat[10])) {
+            S = Math.sqrt(1.0 + mat[0] - mat[5] - mat[10]) * 2;
+            out[3] = (mat[6] - mat[9]) / S;
+            out[0] = 0.25 * S;
+            out[1] = (mat[1] + mat[4]) / S;
+            out[2] = (mat[8] + mat[2]) / S;
+        } else if (mat[5] > mat[10]) {
+            S = Math.sqrt(1.0 + mat[5] - mat[0] - mat[10]) * 2;
+            out[3] = (mat[8] - mat[2]) / S;
+            out[0] = (mat[1] + mat[4]) / S;
+            out[1] = 0.25 * S;
+            out[2] = (mat[6] + mat[9]) / S;
+        } else {
+            S = Math.sqrt(1.0 + mat[10] - mat[0] - mat[5]) * 2;
+            out[3] = (mat[1] - mat[4]) / S;
+            out[0] = (mat[8] + mat[2]) / S;
+            out[1] = (mat[6] + mat[9]) / S;
+            out[2] = 0.25 * S;
+        }
+
+        return out;
+    }
+
     export function transpose(out: Mat4, a: Mat4) {
         // If we are transposing ourselves we can skip a few steps but have to cache some values
         if (out === a) {