diff --git a/src/apps/canvas/assembly-symmetry.ts b/src/apps/canvas/assembly-symmetry.ts
index 69fee7dd07fd6acfb60c87a354f719dd556a2493..ee4d6832753ef4a2f379f5004ede22078ff26eab 100644
--- a/src/apps/canvas/assembly-symmetry.ts
+++ b/src/apps/canvas/assembly-symmetry.ts
@@ -7,10 +7,10 @@
 import { AssemblySymmetry } from 'mol-model-props/rcsb/symmetry';
 import { Table } from 'mol-data/db';
 import { Color, ColorScale } from 'mol-util/color';
-import { MeshBuilder } from 'mol-geo/mesh/mesh-builder';
+import { MeshBuilder } from 'mol-geo/geometry/mesh/mesh-builder';
 import { Tensor } from 'mol-math/linear-algebra';
-import { addSphere } from 'mol-geo/mesh/builder/sphere';
-import { addCylinder } from 'mol-geo/mesh/builder/cylinder';
+import { addSphere } from 'mol-geo/geometry/mesh/builder/sphere';
+import { addCylinder } from 'mol-geo/geometry/mesh/builder/cylinder';
 import { Shape } from 'mol-model/shape';
 import { ColorTheme } from 'mol-view/theme/color';
 import { Location } from 'mol-model/location';
diff --git a/src/apps/canvas/component/structure-representation.tsx b/src/apps/canvas/component/structure-representation.tsx
index d81cc3f3d1a179dcd825217bddb3de60403dfad6..d282e8e41fd53d705dd00990bd519263f80daf8a 100644
--- a/src/apps/canvas/component/structure-representation.tsx
+++ b/src/apps/canvas/component/structure-representation.tsx
@@ -7,10 +7,10 @@
 import * as React from 'react'
 import { StructureRepresentation, StructureProps } from 'mol-geo/representation/structure';
 import Viewer from 'mol-view/viewer';
-import { VisualQuality, VisualQualityNames } from 'mol-geo/representation/util';
 import { ColorThemeProps, ColorThemeName, ColorThemeNames, ColorTheme } from 'mol-view/theme/color';
 import { Color } from 'mol-util/color';
 import { Progress } from 'mol-task';
+import { VisualQuality, VisualQualityNames } from 'mol-geo/geometry/geometry';
 
 export interface StructureRepresentationComponentProps {
     viewer: Viewer
@@ -165,7 +165,7 @@ export class StructureRepresentationComponent extends React.Component<StructureR
                     <span>Iso Value </span>
                     <input type='range'
                         defaultValue={this.state.isoValue.toString()}
-                        min='0.1'
+                        min='1'
                         max='3'
                         step='0.1'
                         onInput={(e) => this.update({ isoValue: parseFloat(e.currentTarget.value) })}
diff --git a/src/apps/canvas/structure-view.ts b/src/apps/canvas/structure-view.ts
index d13cfb6ef6852840cf2241900d7d67f5f68cbe32..62ef041ddfb753af90e335284ec85e431ae03c3e 100644
--- a/src/apps/canvas/structure-view.ts
+++ b/src/apps/canvas/structure-view.ts
@@ -25,7 +25,7 @@ import { BehaviorSubject } from 'rxjs';
 import { SpacefillRepresentation } from 'mol-geo/representation/structure/representation/spacefill';
 import { DistanceRestraintRepresentation } from 'mol-geo/representation/structure/representation/distance-restraint';
 import { SurfaceRepresentation } from 'mol-geo/representation/structure/representation/surface';
-import { Progress } from 'mol-task';
+// import { Progress } from 'mol-task';
 
 export interface StructureView {
     readonly viewer: Viewer
@@ -210,7 +210,7 @@ export async function StructureView(viewer: Viewer, models: ReadonlyArray<Model>
             for (const k in structureRepresentations) {
                 if (active[k]) {
                     await structureRepresentations[k].createOrUpdate({}, structure).run(
-                        progress => console.log(Progress.format(progress))
+                        // progress => console.log(Progress.format(progress))
                     )
                     viewer.add(structureRepresentations[k])
                 } else {
diff --git a/src/mol-app/ui/transform/backbone.tsx b/src/mol-app/ui/transform/backbone.tsx
index 76270473768242f05fbe3744541c7aed9243ee49..93a4a3392618673ea30514e515ef0d8f3dff0d6c 100644
--- a/src/mol-app/ui/transform/backbone.tsx
+++ b/src/mol-app/ui/transform/backbone.tsx
@@ -19,8 +19,8 @@ import { ColorThemeProps, ColorThemeNames, ColorThemeName } from 'mol-view/theme
 import { SizeThemeProps } from 'mol-view/theme/size';
 import { Color, ColorNames } from 'mol-util/color';
 import { Slider } from '../controls/slider';
-import { VisualQuality } from 'mol-geo/representation/util';
 import { Unit } from 'mol-model/structure';
+import { VisualQuality } from 'mol-geo/geometry/geometry';
 
 interface BackboneState {
     doubleSided: boolean
diff --git a/src/mol-app/ui/transform/ball-and-stick.tsx b/src/mol-app/ui/transform/ball-and-stick.tsx
index 61d621f988f5d6aa7f89e89fe5309dc2d6c7c63b..38320c2b07341ef93674381c3cb52718b9008af0 100644
--- a/src/mol-app/ui/transform/ball-and-stick.tsx
+++ b/src/mol-app/ui/transform/ball-and-stick.tsx
@@ -19,8 +19,8 @@ import { ColorThemeProps, ColorThemeNames, ColorThemeName } from 'mol-view/theme
 import { SizeThemeProps } from 'mol-view/theme/size';
 import { Color, ColorNames } from 'mol-util/color';
 import { Slider } from '../controls/slider';
-import { VisualQuality } from 'mol-geo/representation/util';
 import { Unit } from 'mol-model/structure';
+import { VisualQuality } from 'mol-geo/geometry/geometry';
 
 interface BallAndStickState {
     doubleSided: boolean
diff --git a/src/mol-app/ui/transform/carbohydrate.tsx b/src/mol-app/ui/transform/carbohydrate.tsx
index 66e4b3a4c02c3a5ad99bb582097252666821a979..e725131474ef63d60261eab264c4c925a3f8ad31 100644
--- a/src/mol-app/ui/transform/carbohydrate.tsx
+++ b/src/mol-app/ui/transform/carbohydrate.tsx
@@ -19,8 +19,8 @@ import { ColorThemeProps, ColorThemeNames, ColorThemeName } from 'mol-view/theme
 import { SizeThemeProps } from 'mol-view/theme/size';
 import { Color, ColorNames } from 'mol-util/color';
 import { Slider } from '../controls/slider';
-import { VisualQuality } from 'mol-geo/representation/util';
 import { Unit } from 'mol-model/structure';
+import { VisualQuality } from 'mol-geo/geometry/geometry';
 
 interface CarbohydrateState {
     doubleSided: boolean
diff --git a/src/mol-app/ui/transform/cartoon.tsx b/src/mol-app/ui/transform/cartoon.tsx
index b78e8c91e8969d254e06e9c877c7ee20a157e9a9..994934214126b6fbf9909791a65ac5777b883fdd 100644
--- a/src/mol-app/ui/transform/cartoon.tsx
+++ b/src/mol-app/ui/transform/cartoon.tsx
@@ -19,8 +19,8 @@ import { ColorThemeProps, ColorThemeNames, ColorThemeName } from 'mol-view/theme
 import { SizeThemeProps } from 'mol-view/theme/size';
 import { Color, ColorNames } from 'mol-util/color';
 import { Slider } from '../controls/slider';
-import { VisualQuality } from 'mol-geo/representation/util';
 import { Unit } from 'mol-model/structure';
+import { VisualQuality } from 'mol-geo/geometry/geometry';
 
 interface CartoonState {
     doubleSided: boolean
diff --git a/src/mol-app/ui/transform/distance-restraint.tsx b/src/mol-app/ui/transform/distance-restraint.tsx
index 981cd83ef69d1b188259428233a782414f37ec52..037532629bf76078090abee3e4600f5cc8698ce2 100644
--- a/src/mol-app/ui/transform/distance-restraint.tsx
+++ b/src/mol-app/ui/transform/distance-restraint.tsx
@@ -19,8 +19,8 @@ import { ColorThemeProps, ColorThemeNames, ColorThemeName } from 'mol-view/theme
 import { SizeThemeProps } from 'mol-view/theme/size';
 import { Color, ColorNames } from 'mol-util/color';
 import { Slider } from '../controls/slider';
-import { VisualQuality } from 'mol-geo/representation/util';
 import { Unit } from 'mol-model/structure';
+import { VisualQuality } from 'mol-geo/geometry/geometry';
 
 interface DistanceRestraintState {
     doubleSided: boolean
diff --git a/src/mol-app/ui/transform/spacefill.tsx b/src/mol-app/ui/transform/spacefill.tsx
index 53c22660e267a2f66cab426cb531bd54b8454478..d9694e10eae9c0b7bd3e392112290a40bd1b85a7 100644
--- a/src/mol-app/ui/transform/spacefill.tsx
+++ b/src/mol-app/ui/transform/spacefill.tsx
@@ -19,8 +19,8 @@ import { ColorThemeProps, ColorThemeNames, ColorThemeName } from 'mol-view/theme
 import { SizeThemeProps } from 'mol-view/theme/size';
 import { Color, ColorNames } from 'mol-util/color';
 import { Slider } from '../controls/slider';
-import { VisualQuality } from 'mol-geo/representation/util';
 import { Unit } from 'mol-model/structure';
+import { VisualQuality } from 'mol-geo/geometry/geometry';
 
 interface SpacefillState {
     doubleSided: boolean
diff --git a/src/mol-geo/geometry/geometry.ts b/src/mol-geo/geometry/geometry.ts
new file mode 100644
index 0000000000000000000000000000000000000000..a2f6f940808f3be306625f02329e422eb05c0712
--- /dev/null
+++ b/src/mol-geo/geometry/geometry.ts
@@ -0,0 +1,125 @@
+/**
+ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import { Mesh } from './mesh/mesh';
+import { Point } from './point/point';
+import { PointValues, MeshValues, RenderableState } from 'mol-gl/renderable';
+import { MeshRenderObject, PointRenderObject } from 'mol-gl/render-object';
+import { ValueCell } from 'mol-util';
+import { BaseValues } from 'mol-gl/renderable/schema';
+
+export type GeometryKindType = { 'mesh': Mesh, 'point': Point }
+export type GeometryKind = keyof GeometryKindType
+export type Geometry = Helpers.ValueOf<GeometryKindType>
+
+export type GeometryDef = {
+    'mesh': { 'props': MeshProps, 'values': MeshValues, 'renderObject': MeshRenderObject }
+    'point': { 'props': PointProps, 'values': PointValues, 'renderObject': PointRenderObject }
+}
+
+export namespace Geometry {
+    export function getDrawCount(geometry: Geometry) {
+        switch (geometry.kind) {
+            case 'mesh': return geometry.triangleCount * 3
+            case 'point': return geometry.vertexCount
+        }
+    }
+}
+
+//
+
+export const VisualQualityInfo = {
+    'custom': {},
+    'auto': {},
+    'highest': {},
+    'high': {},
+    'medium': {},
+    'low': {},
+    'lowest': {},
+}
+export type VisualQuality = keyof typeof VisualQualityInfo
+export const VisualQualityNames = Object.keys(VisualQualityInfo)
+
+//
+
+export const DefaultBaseProps = {
+    alpha: 1,
+    visible: true,
+    depthMask: true,
+    useFog: false,
+    quality: 'auto' as VisualQuality
+}
+export type BaseProps = typeof DefaultBaseProps
+
+export const DefaultMeshProps = {
+    ...DefaultBaseProps,
+    doubleSided: false,
+    flipSided: false,
+    flatShaded: false,
+}
+export type MeshProps = typeof DefaultMeshProps
+
+export const DefaultPointProps = {
+    ...DefaultBaseProps,
+    pointSizeAttenuation: true
+}
+export type PointProps = typeof DefaultPointProps
+
+type Counts = { drawCount: number, groupCount: number, instanceCount: number }
+
+export function createBaseValues(props: BaseProps, counts: Counts) {
+    return {
+        uAlpha: ValueCell.create(props.alpha),
+        uGroupCount: ValueCell.create(counts.groupCount),
+        drawCount: ValueCell.create(counts.drawCount),
+        dUseFog: ValueCell.create(props.useFog),
+    }
+}
+
+export function createMeshValues(props: MeshProps, counts: Counts) {
+    return {
+        ...createBaseValues(props, counts),
+        dDoubleSided: ValueCell.create(props.doubleSided),
+        dFlatShaded: ValueCell.create(props.flatShaded),
+        dFlipSided: ValueCell.create(props.flipSided),
+    }
+}
+
+export function createPointValues(props: PointProps, counts: Counts) {
+    return {
+        ...createBaseValues(props, counts),
+        dPointSizeAttenuation: ValueCell.create(props.pointSizeAttenuation),
+    }
+}
+
+export function createRenderableState(props: BaseProps): RenderableState {
+    return {
+        visible: props.visible,
+        depthMask: props.depthMask
+    }
+}
+
+export function updateBaseValues(values: BaseValues, props: BaseProps) {
+    ValueCell.updateIfChanged(values.uAlpha, props.alpha)
+    ValueCell.updateIfChanged(values.dUseFog, props.useFog)
+}
+
+export function updateMeshValues(values: MeshValues, props: MeshProps) {
+    updateBaseValues(values, props)
+    ValueCell.updateIfChanged(values.dDoubleSided, props.doubleSided)
+    ValueCell.updateIfChanged(values.dFlatShaded, props.flatShaded)
+    ValueCell.updateIfChanged(values.dFlipSided, props.flipSided)
+}
+
+export function updatePointValues(values: PointValues, props: PointProps) {
+    updateBaseValues(values, props)
+    ValueCell.updateIfChanged(values.dPointSizeAttenuation, props.pointSizeAttenuation)
+}
+
+export function updateRenderableState(state: RenderableState, props: BaseProps) {
+    state.visible = props.visible
+    state.depthMask = props.depthMask
+}
\ No newline at end of file
diff --git a/src/mol-geo/geometry/line/line-builder.ts b/src/mol-geo/geometry/line/line-builder.ts
new file mode 100644
index 0000000000000000000000000000000000000000..0ffdd02fcbce683e436c0030ffe0517135c6ceda
--- /dev/null
+++ b/src/mol-geo/geometry/line/line-builder.ts
@@ -0,0 +1 @@
+// TODO
\ No newline at end of file
diff --git a/src/mol-geo/geometry/line/line.ts b/src/mol-geo/geometry/line/line.ts
new file mode 100644
index 0000000000000000000000000000000000000000..0ffdd02fcbce683e436c0030ffe0517135c6ceda
--- /dev/null
+++ b/src/mol-geo/geometry/line/line.ts
@@ -0,0 +1 @@
+// TODO
\ No newline at end of file
diff --git a/src/mol-geo/mesh/builder/bounding-box.ts b/src/mol-geo/geometry/mesh/builder/bounding-box.ts
similarity index 97%
rename from src/mol-geo/mesh/builder/bounding-box.ts
rename to src/mol-geo/geometry/mesh/builder/bounding-box.ts
index ba36360454d6d689d8b392631543a2be861ff9cb..0781953361505721dd31b15aef6279f4a4d6b83a 100644
--- a/src/mol-geo/mesh/builder/bounding-box.ts
+++ b/src/mol-geo/geometry/mesh/builder/bounding-box.ts
@@ -7,7 +7,7 @@
 import { Vec3 } from 'mol-math/linear-algebra';
 import { Box3D } from 'mol-math/geometry';
 import { MeshBuilder } from '../mesh-builder';
-import { CylinderProps } from '../../primitive/cylinder';
+import { CylinderProps } from '../../../primitive/cylinder';
 import { addCylinder } from './cylinder';
 import { addSphere } from './sphere';
 
diff --git a/src/mol-geo/mesh/builder/cylinder.ts b/src/mol-geo/geometry/mesh/builder/cylinder.ts
similarity index 96%
rename from src/mol-geo/mesh/builder/cylinder.ts
rename to src/mol-geo/geometry/mesh/builder/cylinder.ts
index b4538c1c81a0a802392a622ece8199e2f997e77e..a4b978f3fd4e2ed7e8770278034e47d210d136a2 100644
--- a/src/mol-geo/mesh/builder/cylinder.ts
+++ b/src/mol-geo/geometry/mesh/builder/cylinder.ts
@@ -6,8 +6,8 @@
 
 import { Vec3, Mat4 } from 'mol-math/linear-algebra';
 import { MeshBuilder } from '../mesh-builder';
-import { Primitive } from '../../primitive/primitive';
-import { Cylinder, CylinderProps } from '../../primitive/cylinder';
+import { Primitive } from '../../../primitive/primitive';
+import { Cylinder, CylinderProps } from '../../../primitive/cylinder';
 
 const cylinderMap = new Map<string, Primitive>()
 const up = Vec3.create(0, 1, 0)
diff --git a/src/mol-geo/mesh/builder/sheet.ts b/src/mol-geo/geometry/mesh/builder/sheet.ts
similarity index 100%
rename from src/mol-geo/mesh/builder/sheet.ts
rename to src/mol-geo/geometry/mesh/builder/sheet.ts
diff --git a/src/mol-geo/mesh/builder/sphere.ts b/src/mol-geo/geometry/mesh/builder/sphere.ts
similarity index 88%
rename from src/mol-geo/mesh/builder/sphere.ts
rename to src/mol-geo/geometry/mesh/builder/sphere.ts
index d9196c3a2840223e833369e133c79c237b3e4c63..4d04616b642e2819432c4463c5346c409bc618c7 100644
--- a/src/mol-geo/mesh/builder/sphere.ts
+++ b/src/mol-geo/geometry/mesh/builder/sphere.ts
@@ -6,8 +6,8 @@
 
 import { Vec3, Mat4 } from 'mol-math/linear-algebra';
 import { MeshBuilder } from '../mesh-builder';
-import { Primitive } from '../../primitive/primitive';
-import { Sphere } from '../../primitive/sphere';
+import { Primitive } from '../../../primitive/primitive';
+import { Sphere } from '../../../primitive/sphere';
 
 const sphereMap = new Map<number, Primitive>()
 const tmpSphereMat = Mat4.identity()
diff --git a/src/mol-geo/mesh/builder/tube.ts b/src/mol-geo/geometry/mesh/builder/tube.ts
similarity index 100%
rename from src/mol-geo/mesh/builder/tube.ts
rename to src/mol-geo/geometry/mesh/builder/tube.ts
diff --git a/src/mol-geo/mesh/mesh-builder.ts b/src/mol-geo/geometry/mesh/mesh-builder.ts
similarity index 96%
rename from src/mol-geo/mesh/mesh-builder.ts
rename to src/mol-geo/geometry/mesh/mesh-builder.ts
index d7d264bfc777b0a5548aa229e43b620862c6af95..ecd8dfeec52c1d756ff3c5e45b4c9e215084ce75 100644
--- a/src/mol-geo/mesh/mesh-builder.ts
+++ b/src/mol-geo/geometry/mesh/mesh-builder.ts
@@ -8,8 +8,8 @@ import { ValueCell } from 'mol-util/value-cell'
 import { Vec3, Mat4, Mat3 } from 'mol-math/linear-algebra';
 import { ChunkedArray } from 'mol-data/util';
 import { Mesh } from './mesh';
-import { getNormalMatrix } from '../util';
-import { Primitive } from '../primitive/primitive';
+import { getNormalMatrix } from '../../util';
+import { Primitive } from '../../primitive/primitive';
 
 export interface MeshBuilderState {
     readonly currentGroup: number
@@ -67,6 +67,7 @@ export namespace MeshBuilder {
                 const nb = ChunkedArray.compact(normals, true) as Float32Array
                 const gb = ChunkedArray.compact(groups, true) as Float32Array
                 return {
+                    kind: 'mesh',
                     vertexCount: vertices.elementCount,
                     triangleCount: indices.elementCount,
                     vertexBuffer: mesh ? ValueCell.update(mesh.vertexBuffer, vb) : ValueCell.create(vb),
diff --git a/src/mol-geo/mesh/mesh.ts b/src/mol-geo/geometry/mesh/mesh.ts
similarity index 99%
rename from src/mol-geo/mesh/mesh.ts
rename to src/mol-geo/geometry/mesh/mesh.ts
index adb7cae231e800863e5b4d7d5fa53b69d1e4246e..e600eef6370dfc6460926a66853199a5734a010f 100644
--- a/src/mol-geo/mesh/mesh.ts
+++ b/src/mol-geo/geometry/mesh/mesh.ts
@@ -8,9 +8,11 @@ import { Task } from 'mol-task'
 import { ValueCell } from 'mol-util'
 import { Vec3, Mat4 } from 'mol-math/linear-algebra'
 import { Sphere3D } from 'mol-math/geometry'
-import { transformPositionArray/* , transformDirectionArray, getNormalMatrix */ } from '../util';
+import { transformPositionArray/* , transformDirectionArray, getNormalMatrix */ } from '../../util';
 
 export interface Mesh {
+    readonly kind: 'mesh',
+
     /** Number of vertices in the mesh */
     vertexCount: number,
     /** Number of triangles in the mesh */
@@ -39,6 +41,7 @@ export namespace Mesh {
         const nb = mesh ? mesh.normalBuffer.ref.value : new Float32Array(0)
         const gb = mesh ? mesh.groupBuffer.ref.value : new Float32Array(0)
         return {
+            kind: 'mesh',
             vertexCount: 0,
             triangleCount: 0,
             vertexBuffer: mesh ? ValueCell.update(mesh.vertexBuffer, vb) : ValueCell.create(vb),
diff --git a/src/mol-geo/geometry/point/point-builder.ts b/src/mol-geo/geometry/point/point-builder.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e00a9e06e3da840a267c19100e9ffb412242ccdd
--- /dev/null
+++ b/src/mol-geo/geometry/point/point-builder.ts
@@ -0,0 +1,38 @@
+/**
+ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import { ValueCell } from 'mol-util/value-cell'
+import { ChunkedArray } from 'mol-data/util';
+import { Point } from './point';
+
+export interface PointBuilder {
+    add(x: number, y: number, z: number, group: number): void
+    getPoint(): Point
+}
+
+export namespace PointBuilder {
+    export function create(initialCount = 2048, chunkSize = 1024, point?: Point): PointBuilder {
+        const vertices = ChunkedArray.create(Float32Array, 3, chunkSize, point ? point.vertexBuffer.ref.value : initialCount);
+        const groups = ChunkedArray.create(Float32Array, 1, chunkSize, point ? point.groupBuffer.ref.value : initialCount);
+
+        return {
+            add: (x: number, y: number, z: number, group: number) => {
+                ChunkedArray.add3(vertices, x, y, z);
+                ChunkedArray.add(groups, group);
+            },
+            getPoint: () => {
+                const vb = ChunkedArray.compact(vertices, true) as Float32Array
+                const gb = ChunkedArray.compact(groups, true) as Float32Array
+                return {
+                    kind: 'point',
+                    vertexCount: vertices.elementCount,
+                    vertexBuffer: point ? ValueCell.update(point.vertexBuffer, vb) : ValueCell.create(vb),
+                    groupBuffer: point ? ValueCell.update(point.groupBuffer, gb) : ValueCell.create(gb),
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/mol-geo/geometry/point/point.ts b/src/mol-geo/geometry/point/point.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2b5a810e6a95a3bcbc078663da46f21d502c7f9c
--- /dev/null
+++ b/src/mol-geo/geometry/point/point.ts
@@ -0,0 +1,43 @@
+/**
+ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import { ValueCell } from 'mol-util'
+import { Mat4 } from 'mol-math/linear-algebra'
+import { transformPositionArray/* , transformDirectionArray, getNormalMatrix */ } from '../../util';
+
+/** Point cloud */
+export interface Point {
+    readonly kind: 'point',
+    /** Number of vertices in the point cloud */
+    vertexCount: number,
+    /** Vertex buffer as array of xyz values wrapped in a value cell */
+    readonly vertexBuffer: ValueCell<Float32Array>,
+    /** Group buffer as array of group ids for each vertex wrapped in a value cell */
+    readonly groupBuffer: ValueCell<Float32Array>,
+}
+
+export namespace Point {
+    export function createEmpty(point?: Point): Point {
+        const vb = point ? point.vertexBuffer.ref.value : new Float32Array(0)
+        const gb = point ? point.groupBuffer.ref.value : new Float32Array(0)
+        return {
+            kind: 'point',
+            vertexCount: 0,
+            vertexBuffer: point ? ValueCell.update(point.vertexBuffer, vb) : ValueCell.create(vb),
+            groupBuffer: point ? ValueCell.update(point.groupBuffer, gb) : ValueCell.create(gb),
+        }
+    }
+
+    export function transformImmediate(point: Point, t: Mat4) {
+        transformRangeImmediate(point, t, 0, point.vertexCount)
+    }
+
+    export function transformRangeImmediate(point: Point, t: Mat4, offset: number, count: number) {
+        const v = point.vertexBuffer.ref.value
+        transformPositionArray(t, v, offset, count)
+        ValueCell.update(point.vertexBuffer, v);
+    }
+}
\ No newline at end of file
diff --git a/src/mol-geo/representation/shape/index.ts b/src/mol-geo/representation/shape/index.ts
index c5b86178a700bd021cbd759e1dcfe36ee43c5df2..fcb1800524150c6262e0cd3966377c4944456418 100644
--- a/src/mol-geo/representation/shape/index.ts
+++ b/src/mol-geo/representation/shape/index.ts
@@ -10,7 +10,6 @@ import { RepresentationProps, Representation } from '..';
 import { PickingId } from '../../util/picking';
 import { Loci, EmptyLoci, isEveryLoci } from 'mol-model/loci';
 import { MarkerAction, applyMarkerAction, createMarkers } from '../../util/marker-data';
-import { createRenderableState, createMeshValues, DefaultMeshProps } from '../util';
 import { getMeshData } from '../../util/mesh-data';
 import { MeshValues } from 'mol-gl/renderable';
 import { ValueCell } from 'mol-util';
@@ -20,6 +19,7 @@ import { LocationIterator } from '../../util/location-iterator';
 import { createColors } from '../structure/visual/util/common';
 import { OrderedSet, Interval } from 'mol-data/int';
 import { createIdentityTransform } from '../../util/transform-data';
+import { DefaultMeshProps, createMeshValues, createRenderableState } from '../../geometry/geometry';
 
 export interface ShapeRepresentation<P extends RepresentationProps = {}> extends Representation<Shape, P> { }
 
diff --git a/src/mol-geo/representation/structure/complex-visual.ts b/src/mol-geo/representation/structure/complex-visual.ts
index 53a59c6d4ed75890f3a3463971c138f14445192b..e9bf510643a5c30bb7b25fd173b3ce41fc18a0f8 100644
--- a/src/mol-geo/representation/structure/complex-visual.ts
+++ b/src/mol-geo/representation/structure/complex-visual.ts
@@ -7,17 +7,17 @@
 import { Structure } from 'mol-model/structure';
 import { Visual } from '..';
 import { MeshRenderObject } from 'mol-gl/render-object';
-import { Mesh } from '../../mesh/mesh';
+import { Mesh } from '../../geometry/mesh/mesh';
 import { RuntimeContext } from 'mol-task';
 import { LocationIterator } from '../../util/location-iterator';
 import { createComplexMeshRenderObject, createColors } from './visual/util/common';
-import { StructureProps, DefaultStructureMeshProps, MeshUpdateState } from '.';
+import { StructureProps, DefaultStructureMeshProps, VisualUpdateState } from '.';
 import { deepEqual, ValueCell } from 'mol-util';
-import { updateMeshValues, updateRenderableState } from '../util';
 import { PickingId } from '../../util/picking';
 import { Loci, isEveryLoci, EmptyLoci } from 'mol-model/loci';
 import { MarkerAction, applyMarkerAction } from '../../util/marker-data';
 import { Interval } from 'mol-data/int';
+import { updateMeshValues, updateRenderableState } from '../../geometry/geometry';
 
 export interface  ComplexVisual<P extends StructureProps> extends Visual<Structure, P> { }
 
@@ -32,12 +32,12 @@ export interface ComplexMeshVisualBuilder<P extends ComplexMeshProps> {
     createLocationIterator(structure: Structure): LocationIterator
     getLoci(pickingId: PickingId, structure: Structure, id: number): Loci
     mark(loci: Loci, structure: Structure, apply: (interval: Interval) => boolean): boolean,
-    setUpdateState(state: MeshUpdateState, newProps: P, currentProps: P): void
+    setUpdateState(state: VisualUpdateState, newProps: P, currentProps: P): void
 }
 
 export function ComplexMeshVisual<P extends ComplexMeshProps>(builder: ComplexMeshVisualBuilder<P>): ComplexVisual<P> {
     const { defaultProps, createMesh, createLocationIterator, getLoci, mark, setUpdateState } = builder
-    const updateState = MeshUpdateState.create()
+    const updateState = VisualUpdateState.create()
 
     let renderObject: MeshRenderObject | undefined
     let currentProps: P
@@ -65,22 +65,22 @@ export function ComplexMeshVisual<P extends ComplexMeshProps>(builder: ComplexMe
         if (!renderObject) return false
 
         locationIt.reset()
-        MeshUpdateState.reset(updateState)
+        VisualUpdateState.reset(updateState)
         setUpdateState(updateState, newProps, currentProps)
 
         const newConformationHash = Structure.conformationHash(currentStructure)
         if (newConformationHash !== conformationHash) {
             conformationHash = newConformationHash
-            updateState.createMesh = true
+            updateState.createGeometry = true
         }
 
-        if (!deepEqual(newProps.sizeTheme, currentProps.sizeTheme)) updateState.createMesh = true
+        if (!deepEqual(newProps.sizeTheme, currentProps.sizeTheme)) updateState.createGeometry = true
         if (!deepEqual(newProps.colorTheme, currentProps.colorTheme)) updateState.updateColor = true
         // if (!deepEqual(newProps.unitKinds, currentProps.unitKinds)) updateState.createMesh = true // TODO
 
         //
 
-        if (updateState.createMesh) {
+        if (updateState.createGeometry) {
             mesh = await createMesh(ctx, currentStructure, newProps, mesh)
             ValueCell.update(renderObject.values.drawCount, mesh.triangleCount * 3)
             updateState.updateColor = true
diff --git a/src/mol-geo/representation/structure/index.ts b/src/mol-geo/representation/structure/index.ts
index 7628d9e084ba81ff6ef7e9705638532e73ee2c77..e6f6610418cb9fbf08a3669fc0157cc62bcd828a 100644
--- a/src/mol-geo/representation/structure/index.ts
+++ b/src/mol-geo/representation/structure/index.ts
@@ -9,7 +9,7 @@ import { Structure } from 'mol-model/structure';
 import { ColorThemeProps } from 'mol-view/theme/color';
 import { SizeThemeProps } from 'mol-view/theme/size';
 import { Representation, RepresentationProps } from '..';
-import { DefaultBaseProps, DefaultMeshProps } from '../util';
+import { DefaultMeshProps, DefaultBaseProps, DefaultPointProps } from '../../geometry/geometry';
 
 export interface StructureRepresentation<P extends RepresentationProps = {}> extends Representation<Structure, P> { }
 
@@ -26,23 +26,32 @@ export const DefaultStructureMeshProps = {
 }
 export type StructureMeshProps = typeof DefaultStructureMeshProps
 
-export interface MeshUpdateState {
+export const DefaultStructurePointProps = {
+    ...DefaultStructureProps,
+    ...DefaultPointProps
+}
+export type StructurePointProps = typeof DefaultStructurePointProps
+
+export interface VisualUpdateState {
     updateTransform: boolean
     updateColor: boolean
-    createMesh: boolean
+    updateSize: boolean
+    createGeometry: boolean
 }
-export namespace MeshUpdateState {
-    export function create(): MeshUpdateState {
+export namespace VisualUpdateState {
+    export function create(): VisualUpdateState {
         return {
             updateTransform: false,
             updateColor: false,
-            createMesh: false
+            updateSize: false,
+            createGeometry: false
         }
     }
-    export function reset(state: MeshUpdateState) {
+    export function reset(state: VisualUpdateState) {
         state.updateTransform = false
         state.updateColor = false
-        state.createMesh = false
+        state.updateSize = false
+        state.createGeometry = false
     }
 }
 
diff --git a/src/mol-geo/representation/structure/representation/surface.ts b/src/mol-geo/representation/structure/representation/surface.ts
index 7e95466e6ffa51421b7973b8436b2a5ef1e9a6d8..92a0d48e2d610244eb7e0e1dc1e32e326ba3a52c 100644
--- a/src/mol-geo/representation/structure/representation/surface.ts
+++ b/src/mol-geo/representation/structure/representation/surface.ts
@@ -11,9 +11,12 @@ import { Structure } from 'mol-model/structure';
 import { MarkerAction } from '../../../util/marker-data';
 import { Loci } from 'mol-model/loci';
 import { PickingId } from '../../../util/picking';
+import { Task } from 'mol-task';
+import { GaussianDensityPointVisual, DefaultGaussianDensityPointProps } from '../visual/gaussian-density-point';
 
 export const DefaultSurfaceProps = {
     ...DefaultGaussianSurfaceProps,
+    ...DefaultGaussianDensityPointProps,
 }
 export type SurfaceProps = typeof DefaultSurfaceProps
 
@@ -21,27 +24,32 @@ export type SurfaceRepresentation = StructureRepresentation<SurfaceProps>
 
 export function SurfaceRepresentation(): SurfaceRepresentation {
     let currentProps: SurfaceProps
-    const gaussianRepr = UnitsRepresentation('Gaussian surface', GaussianSurfaceVisual)
+    const gaussianSurfaceRepr = UnitsRepresentation('Gaussian surface', GaussianSurfaceVisual)
+    const gaussianPointRepr = UnitsRepresentation('Gaussian point grid', GaussianDensityPointVisual)
     return {
         label: 'Surface',
         get renderObjects() {
-            return [ ...gaussianRepr.renderObjects ]
+            return [ ...gaussianSurfaceRepr.renderObjects, ...gaussianPointRepr.renderObjects ]
         },
         get props() {
-            return { ...gaussianRepr.props }
+            return { ...gaussianSurfaceRepr.props, ...gaussianPointRepr.props }
         },
         createOrUpdate: (props: Partial<SurfaceProps> = {}, structure?: Structure) => {
             currentProps = Object.assign({}, DefaultSurfaceProps, currentProps, props)
-            return gaussianRepr.createOrUpdate(currentProps, structure)
+            return Task.create('Creating SurfaceRepresentation', async ctx => {
+                await gaussianSurfaceRepr.createOrUpdate(currentProps, structure).runInContext(ctx)
+                await gaussianPointRepr.createOrUpdate(currentProps, structure).runInContext(ctx)
+            })
         },
         getLoci: (pickingId: PickingId) => {
-            return gaussianRepr.getLoci(pickingId)
+            return gaussianSurfaceRepr.getLoci(pickingId)
         },
         mark: (loci: Loci, action: MarkerAction) => {
-            return gaussianRepr.mark(loci, action)
+            return gaussianSurfaceRepr.mark(loci, action)
         },
         destroy() {
-            gaussianRepr.destroy()
+            gaussianSurfaceRepr.destroy()
+            gaussianPointRepr.destroy()
         }
     }
 }
\ No newline at end of file
diff --git a/src/mol-geo/representation/structure/units-visual.ts b/src/mol-geo/representation/structure/units-visual.ts
index e017c8494135ebee56678f719378cbd6e4abba51..b7feb79de7a9ed01693317afc8517d9d115857d3 100644
--- a/src/mol-geo/representation/structure/units-visual.ts
+++ b/src/mol-geo/representation/structure/units-visual.ts
@@ -5,20 +5,21 @@
  */
 
 import { Unit, Structure } from 'mol-model/structure';
-import { RepresentationProps, Visual } from '..';
-import { DefaultStructureMeshProps, MeshUpdateState } from '.';
+import { RepresentationProps, Visual } from '../';
+import { DefaultStructureMeshProps, VisualUpdateState, DefaultStructurePointProps } from '.';
 import { RuntimeContext } from 'mol-task';
 import { PickingId } from '../../util/picking';
 import { LocationIterator } from '../../util/location-iterator';
-import { Mesh } from '../../mesh/mesh';
+import { Mesh } from '../../geometry/mesh/mesh';
 import { MarkerAction, applyMarkerAction, createMarkers } from '../../util/marker-data';
 import { Loci, isEveryLoci, EmptyLoci } from 'mol-model/loci';
-import { MeshRenderObject } from 'mol-gl/render-object';
-import { createUnitsMeshRenderObject, createColors } from './visual/util/common';
+import { MeshRenderObject, PointRenderObject } from 'mol-gl/render-object';
+import { createUnitsMeshRenderObject, createColors, createUnitsPointRenderObject } from './visual/util/common';
 import { deepEqual, ValueCell, UUID } from 'mol-util';
-import { updateMeshValues, updateRenderableState } from '../util';
 import { Interval } from 'mol-data/int';
 import { createTransforms } from '../../util/transform-data';
+import { updateMeshValues, updateRenderableState, updatePointValues } from '../../geometry/geometry';
+import { Point } from '../../geometry/point/point';
 
 export type StructureGroup = { structure: Structure, group: Unit.SymmetryGroup }
 
@@ -36,12 +37,12 @@ export interface UnitsMeshVisualBuilder<P extends UnitsMeshProps> {
     createLocationIterator(group: Unit.SymmetryGroup): LocationIterator
     getLoci(pickingId: PickingId, group: Unit.SymmetryGroup, id: number): Loci
     mark(loci: Loci, group: Unit.SymmetryGroup, apply: (interval: Interval) => boolean): boolean
-    setUpdateState(state: MeshUpdateState, newProps: P, currentProps: P): void
+    setUpdateState(state: VisualUpdateState, newProps: P, currentProps: P): void
 }
 
 export function UnitsMeshVisual<P extends UnitsMeshProps>(builder: UnitsMeshVisualBuilder<P>): UnitsVisual<P> {
     const { defaultProps, createMesh, createLocationIterator, getLoci, mark, setUpdateState } = builder
-    const updateState = MeshUpdateState.create()
+    const updateState = VisualUpdateState.create()
 
     let renderObject: MeshRenderObject | undefined
     let currentProps: P
@@ -75,20 +76,20 @@ export function UnitsMeshVisual<P extends UnitsMeshProps>(builder: UnitsMeshVisu
         const unit = currentGroup.units[0]
 
         locationIt.reset()
-        MeshUpdateState.reset(updateState)
+        VisualUpdateState.reset(updateState)
         setUpdateState(updateState, newProps, currentProps)
 
         const newConformationId = Unit.conformationId(unit)
         if (newConformationId !== currentConformationId) {
             currentConformationId = newConformationId
-            updateState.createMesh = true
+            updateState.createGeometry = true
         }
 
         if (currentGroup.units.length !== locationIt.instanceCount) updateState.updateTransform = true
 
-        if (!deepEqual(newProps.sizeTheme, currentProps.sizeTheme)) updateState.createMesh = true
+        if (!deepEqual(newProps.sizeTheme, currentProps.sizeTheme)) updateState.createGeometry = true
         if (!deepEqual(newProps.colorTheme, currentProps.colorTheme)) updateState.updateColor = true
-        if (!deepEqual(newProps.unitKinds, currentProps.unitKinds)) updateState.createMesh = true
+        if (!deepEqual(newProps.unitKinds, currentProps.unitKinds)) updateState.createGeometry = true
 
         //
 
@@ -100,7 +101,7 @@ export function UnitsMeshVisual<P extends UnitsMeshProps>(builder: UnitsMeshVisu
             updateState.updateColor = true
         }
 
-        if (updateState.createMesh) {
+        if (updateState.createGeometry) {
             mesh = newProps.unitKinds.includes(unit.kind)
                 ? await createMesh(ctx, unit, currentStructure, newProps, mesh)
                 : Mesh.createEmpty(mesh)
@@ -112,8 +113,9 @@ export function UnitsMeshVisual<P extends UnitsMeshProps>(builder: UnitsMeshVisu
             await createColors(ctx, locationIt, newProps.colorTheme, renderObject.values)
         }
 
-        updateMeshValues(renderObject.values, newProps)
-        updateRenderableState(renderObject.state, newProps)
+        // TODO why do I need to cast here?
+        updateMeshValues(renderObject.values, newProps as UnitsMeshProps)
+        updateRenderableState(renderObject.state, newProps as UnitsMeshProps)
 
         currentProps = newProps
     }
@@ -177,4 +179,155 @@ function sameGroupConformation(groupA: Unit.SymmetryGroup, groupB: Unit.Symmetry
         groupA.units.length === groupB.units.length &&
         Unit.conformationId(groupA.units[0]) === Unit.conformationId(groupB.units[0])
     )
+}
+
+//
+
+export const DefaultUnitsPointProps = {
+    ...DefaultStructurePointProps,
+    unitKinds: [ Unit.Kind.Atomic, Unit.Kind.Spheres ] as Unit.Kind[]
+}
+export type UnitsPointProps = typeof DefaultUnitsPointProps
+
+export interface UnitsPointVisualBuilder<P extends UnitsPointProps> {
+    defaultProps: P
+    createPoint(ctx: RuntimeContext, unit: Unit, structure: Structure, props: P, point?: Point): Promise<Point>
+    createLocationIterator(group: Unit.SymmetryGroup): LocationIterator
+    getLoci(pickingId: PickingId, group: Unit.SymmetryGroup, id: number): Loci
+    mark(loci: Loci, group: Unit.SymmetryGroup, apply: (interval: Interval) => boolean): boolean
+    setUpdateState(state: VisualUpdateState, newProps: P, currentProps: P): void
+}
+
+export function UnitsPointVisual<P extends UnitsPointProps>(builder: UnitsPointVisualBuilder<P>): UnitsVisual<P> {
+    const { defaultProps, createPoint, createLocationIterator, getLoci, mark, setUpdateState } = builder
+    const updateState = VisualUpdateState.create()
+
+    let renderObject: PointRenderObject | undefined
+    let currentProps: P
+    let point: Point
+    let currentGroup: Unit.SymmetryGroup
+    let currentStructure: Structure
+    let locationIt: LocationIterator
+    let currentConformationId: UUID
+
+    async function create(ctx: RuntimeContext, group: Unit.SymmetryGroup, props: Partial<P> = {}) {
+        currentProps = Object.assign({}, defaultProps, props)
+        currentProps.colorTheme.structure = currentStructure
+        currentGroup = group
+
+        const unit = group.units[0]
+        currentConformationId = Unit.conformationId(unit)
+        point = currentProps.unitKinds.includes(unit.kind)
+            ? await createPoint(ctx, unit, currentStructure, currentProps, point)
+            : Point.createEmpty(point)
+
+        // TODO create empty location iterator when not in unitKinds
+        locationIt = createLocationIterator(group)
+        renderObject = await createUnitsPointRenderObject(ctx, group, point, locationIt, currentProps)
+    }
+
+    async function update(ctx: RuntimeContext, props: Partial<P> = {}) {
+        if (!renderObject) return
+
+        const newProps = Object.assign({}, currentProps, props)
+        newProps.colorTheme.structure = currentStructure
+        const unit = currentGroup.units[0]
+
+        locationIt.reset()
+        VisualUpdateState.reset(updateState)
+        setUpdateState(updateState, newProps, currentProps)
+
+        const newConformationId = Unit.conformationId(unit)
+        if (newConformationId !== currentConformationId) {
+            currentConformationId = newConformationId
+            updateState.createGeometry = true
+        }
+
+        if (currentGroup.units.length !== locationIt.instanceCount) updateState.updateTransform = true
+
+        if (!deepEqual(newProps.sizeTheme, currentProps.sizeTheme)) updateState.createGeometry = true
+        if (!deepEqual(newProps.colorTheme, currentProps.colorTheme)) updateState.updateColor = true
+        if (!deepEqual(newProps.unitKinds, currentProps.unitKinds)) updateState.createGeometry = true
+
+        //
+
+        if (updateState.updateTransform) {
+            locationIt = createLocationIterator(currentGroup)
+            const { instanceCount, groupCount } = locationIt
+            createTransforms(currentGroup, renderObject.values)
+            createMarkers(instanceCount * groupCount, renderObject.values)
+            updateState.updateColor = true
+        }
+
+        if (updateState.createGeometry) {
+            point = newProps.unitKinds.includes(unit.kind)
+                ? await createPoint(ctx, unit, currentStructure, newProps, point)
+                : Point.createEmpty(point)
+            ValueCell.update(renderObject.values.drawCount, point.vertexCount)
+            updateState.updateColor = true
+        }
+
+        if (updateState.updateColor) {
+            await createColors(ctx, locationIt, newProps.colorTheme, renderObject.values)
+        }
+
+        // TODO why do I need to cast here?
+        updatePointValues(renderObject.values, newProps as UnitsPointProps)
+        updateRenderableState(renderObject.state, newProps as UnitsPointProps)
+
+        currentProps = newProps
+    }
+
+    return {
+        get renderObject () { return renderObject },
+        async createOrUpdate(ctx: RuntimeContext, props: Partial<P> = {}, structureGroup?: StructureGroup) {
+            if (structureGroup) currentStructure = structureGroup.structure
+            const group = structureGroup ? structureGroup.group : undefined
+            if (!group && !currentGroup) {
+                throw new Error('missing group')
+            } else if (group && (!currentGroup || !renderObject)) {
+                // console.log('unit-visual first create')
+                await create(ctx, group, props)
+            } else if (group && group.hashCode !== currentGroup.hashCode) {
+                // console.log('unit-visual group.hashCode !== currentGroup.hashCode')
+                await create(ctx, group, props)
+            } else {
+                // console.log('unit-visual update')
+                if (group && !sameGroupConformation(group, currentGroup)) {
+                    // console.log('unit-visual new conformation')
+                    currentGroup = group
+                }
+                await update(ctx, props)
+            }
+        },
+        getLoci(pickingId: PickingId) {
+            return renderObject ? getLoci(pickingId, currentGroup, renderObject.id) : EmptyLoci
+        },
+        mark(loci: Loci, action: MarkerAction) {
+            if (!renderObject) return false
+            const { tMarker } = renderObject.values
+            const { groupCount, instanceCount } = locationIt
+
+            function apply(interval: Interval) {
+                const start = Interval.start(interval)
+                const end = Interval.end(interval)
+                return applyMarkerAction(tMarker.ref.value.array, start, end, action)
+            }
+
+            let changed = false
+            if (isEveryLoci(loci)) {
+                changed = apply(Interval.ofBounds(0, groupCount * instanceCount))
+            } else {
+                changed = mark(loci, currentGroup, apply)
+            }
+            if (changed) {
+                ValueCell.update(tMarker, tMarker.ref.value)
+            }
+            return changed
+        },
+        destroy() {
+            // TODO
+            renderObject = undefined
+        }
+    }
 }
\ No newline at end of file
diff --git a/src/mol-geo/representation/structure/visual/carbohydrate-link-cylinder.ts b/src/mol-geo/representation/structure/visual/carbohydrate-link-cylinder.ts
index 185835c629e4bcca92a85f02008992f9466199df..d4270f02ebad1f6632c44d65ea24c91c1a2ddfa2 100644
--- a/src/mol-geo/representation/structure/visual/carbohydrate-link-cylinder.ts
+++ b/src/mol-geo/representation/structure/visual/carbohydrate-link-cylinder.ts
@@ -5,12 +5,11 @@
  */
 
 import { Unit, Structure, Link, StructureElement } from 'mol-model/structure';
-import { DefaultStructureProps, ComplexVisual, MeshUpdateState } from '..';
+import { DefaultStructureProps, ComplexVisual, VisualUpdateState } from '..';
 import { RuntimeContext } from 'mol-task'
-import { Mesh } from '../../../mesh/mesh';
+import { Mesh } from '../../../geometry/mesh/mesh';
 import { PickingId } from '../../../util/picking';
 import { Loci, EmptyLoci } from 'mol-model/loci';
-import { DefaultMeshProps } from '../../util';
 import { Vec3 } from 'mol-math/linear-algebra';
 import { LocationIterator } from '../../../util/location-iterator';
 import { createLinkCylinderMesh, DefaultLinkCylinderProps, LinkCylinderProps } from './util/link';
@@ -19,6 +18,7 @@ import { ComplexMeshVisual } from '../complex-visual';
 import { SizeThemeProps, SizeTheme } from 'mol-view/theme/size';
 import { LinkType } from 'mol-model/structure/model/types';
 import { BitFlags } from 'mol-util';
+import { DefaultMeshProps } from '../../../geometry/geometry';
 
 // TODO create seperate visual
 // for (let i = 0, il = carbohydrates.terminalLinks.length; i < il; ++i) {
@@ -77,8 +77,8 @@ export function CarbohydrateLinkVisual(): ComplexVisual<CarbohydrateLinkProps> {
         createLocationIterator: CarbohydrateLinkIterator,
         getLoci: getLinkLoci,
         mark: markLink,
-        setUpdateState: (state: MeshUpdateState, newProps: CarbohydrateLinkProps, currentProps: CarbohydrateLinkProps) => {
-            state.createMesh = newProps.radialSegments !== currentProps.radialSegments
+        setUpdateState: (state: VisualUpdateState, newProps: CarbohydrateLinkProps, currentProps: CarbohydrateLinkProps) => {
+            state.createGeometry = newProps.radialSegments !== currentProps.radialSegments
         }
     })
 }
diff --git a/src/mol-geo/representation/structure/visual/carbohydrate-symbol-mesh.ts b/src/mol-geo/representation/structure/visual/carbohydrate-symbol-mesh.ts
index 13d568ccb73cd097c659a36a8281c9e600b4aa85..fd6603b25e7caf6683e571e98638fc56141cbd52 100644
--- a/src/mol-geo/representation/structure/visual/carbohydrate-symbol-mesh.ts
+++ b/src/mol-geo/representation/structure/visual/carbohydrate-symbol-mesh.ts
@@ -5,19 +5,19 @@
  */
 
 import { Unit, Structure, StructureElement } from 'mol-model/structure';
-import { ComplexVisual, MeshUpdateState } from '..';
+import { ComplexVisual, VisualUpdateState } from '..';
 import { RuntimeContext } from 'mol-task'
-import { Mesh } from '../../../mesh/mesh';
+import { Mesh } from '../../../geometry/mesh/mesh';
 import { PickingId } from '../../../util/picking';
 import { Loci, EmptyLoci } from 'mol-model/loci';
-import { MeshBuilder } from '../../../mesh/mesh-builder';
+import { MeshBuilder } from '../../../geometry/mesh/mesh-builder';
 import { Vec3, Mat4 } from 'mol-math/linear-algebra';
 import { getSaccharideShape, SaccharideShapes } from 'mol-model/structure/structure/carbohydrates/constants';
 import { LocationIterator } from '../../../util/location-iterator';
 import { OrderedSet, Interval } from 'mol-data/int';
 import { ComplexMeshVisual, DefaultComplexMeshProps } from '../complex-visual';
 import { SizeThemeProps, SizeTheme } from 'mol-view/theme/size';
-import { addSphere } from '../../../mesh/builder/sphere';
+import { addSphere } from '../../../geometry/mesh/builder/sphere';
 import { Box, PerforatedBox } from '../../../primitive/box';
 import { OctagonalPyramid, PerforatedOctagonalPyramid } from '../../../primitive/pyramid';
 import { Star } from '../../../primitive/star';
@@ -154,8 +154,8 @@ export function CarbohydrateSymbolVisual(): ComplexVisual<CarbohydrateSymbolProp
         createLocationIterator: CarbohydrateElementIterator,
         getLoci: getCarbohydrateLoci,
         mark: markCarbohydrate,
-        setUpdateState: (state: MeshUpdateState, newProps: CarbohydrateSymbolProps, currentProps: CarbohydrateSymbolProps) => {
-            state.createMesh = newProps.detail !== currentProps.detail
+        setUpdateState: (state: VisualUpdateState, newProps: CarbohydrateSymbolProps, currentProps: CarbohydrateSymbolProps) => {
+            state.createGeometry = newProps.detail !== currentProps.detail
         }
     })
 }
diff --git a/src/mol-geo/representation/structure/visual/cross-link-restraint-cylinder.ts b/src/mol-geo/representation/structure/visual/cross-link-restraint-cylinder.ts
index 735499fd290cee6d58f5213079613c20aed9d75b..f36e157eb43682d86e44021bb2ac3d658db7056c 100644
--- a/src/mol-geo/representation/structure/visual/cross-link-restraint-cylinder.ts
+++ b/src/mol-geo/representation/structure/visual/cross-link-restraint-cylinder.ts
@@ -5,10 +5,10 @@
  */
 
 import { Link, Structure, StructureElement } from 'mol-model/structure';
-import { ComplexVisual, MeshUpdateState } from '..';
+import { ComplexVisual, VisualUpdateState } from '..';
 import { RuntimeContext } from 'mol-task'
 import { LinkCylinderProps, DefaultLinkCylinderProps, createLinkCylinderMesh } from './util/link';
-import { Mesh } from '../../../mesh/mesh';
+import { Mesh } from '../../../geometry/mesh/mesh';
 import { PickingId } from '../../../util/picking';
 import { Vec3 } from 'mol-math/linear-algebra';
 import { Loci, EmptyLoci } from 'mol-model/loci';
@@ -65,8 +65,8 @@ export function CrossLinkRestraintVisual(): ComplexVisual<CrossLinkRestraintProp
         createLocationIterator: CrossLinkRestraintIterator,
         getLoci: getLinkLoci,
         mark: markLink,
-        setUpdateState: (state: MeshUpdateState, newProps: CrossLinkRestraintProps, currentProps: CrossLinkRestraintProps) => {
-            state.createMesh = newProps.radialSegments !== currentProps.radialSegments
+        setUpdateState: (state: VisualUpdateState, newProps: CrossLinkRestraintProps, currentProps: CrossLinkRestraintProps) => {
+            state.createGeometry = newProps.radialSegments !== currentProps.radialSegments
         }
     })
 }
diff --git a/src/mol-geo/representation/structure/visual/element-point.ts b/src/mol-geo/representation/structure/visual/element-point.ts
index eac5fb80226aad8dc30f67570d11f9c7469cdfe2..f624d11f10d49c0d3ec3b3009b3e24f421882e12 100644
--- a/src/mol-geo/representation/structure/visual/element-point.ts
+++ b/src/mol-geo/representation/structure/visual/element-point.ts
@@ -2,169 +2,56 @@
  * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
- * @author David Sehnal <david.sehnal@gmail.com>
  */
 
-import { ValueCell } from 'mol-util/value-cell'
-import { PointRenderObject } from 'mol-gl/render-object'
 import { Unit, Structure } from 'mol-model/structure';
 import { RuntimeContext } from 'mol-task'
-import { UnitsVisual, DefaultStructureProps } from '..';
+import { UnitsVisual, VisualUpdateState } from '..';
 import { getElementLoci, StructureElementIterator, markElement } from './util/element';
-import { createColors, createSizes, createUnitsPointRenderObject } from './util/common';
-import { deepEqual, UUID } from 'mol-util';
-import { Interval } from 'mol-data/int';
-import { PickingId } from '../../../util/picking';
-import { Loci, EmptyLoci, isEveryLoci } from 'mol-model/loci';
-import { MarkerAction, createMarkers, applyMarkerAction } from '../../../util/marker-data';
 import { Vec3 } from 'mol-math/linear-algebra';
-import { fillSerial } from 'mol-util/array';
 import { SizeThemeProps } from 'mol-view/theme/size';
-import { LocationIterator } from '../../../util/location-iterator';
-import { createTransforms } from '../../../util/transform-data';
-import { StructureGroup } from '../units-visual';
-import { updateRenderableState, updateBaseValues } from '../../util';
+import { UnitsPointVisual, DefaultUnitsPointProps } from '../units-visual';
+import { Point } from '../../../geometry/point/point';
+import { PointBuilder } from '../../../geometry/point/point-builder';
 
 export const DefaultElementPointProps = {
-    ...DefaultStructureProps,
+    ...DefaultUnitsPointProps,
 
     sizeTheme: { name: 'uniform', value: 0.2 } as SizeThemeProps,
     pointSizeAttenuation: true,
 }
-export type ElementPointProps = Partial<typeof DefaultElementPointProps>
+export type ElementPointProps = typeof DefaultElementPointProps
 
-export async function createElementPointVertices(ctx: RuntimeContext, unit: Unit, vertices?: ValueCell<Float32Array>) {
+// TODO size
+
+export async function createElementPoint(ctx: RuntimeContext, unit: Unit, structure: Structure, props: ElementPointProps, point: Point) {
     const elements = unit.elements
-    const n = elements.length * 3
-    const array = vertices && vertices.ref.value.length >= n ? vertices.ref.value : new Float32Array(n)
+    const n = elements.length
+    const builder = PointBuilder.create(n, n / 10, point)
 
     const pos = unit.conformation.invariantPosition
-
     const p = Vec3.zero()
-    for (let i = 0; i < n; i += 3) {
-        pos(elements[i / 3], p)
-        array[i] = p[0]
-        array[i + 1] = p[1]
-        array[i + 2] = p[2]
+
+    for (let i = 0; i < n; ++i) {
+        pos(elements[i], p)
+        builder.add(p[0], p[1], p[2], i)
 
         if (i % 10000 === 0 && ctx.shouldUpdate) {
-            await ctx.update({ message: 'Creating points', current: i / 3, max: elements.length });
+            await ctx.update({ message: 'Creating points', current: i, max: n });
         }
-        ++i
     }
-    return vertices ? ValueCell.update(vertices, array) : ValueCell.create(array)
+    return builder.getPoint()
 }
 
 export function ElementPointVisual(): UnitsVisual<ElementPointProps> {
-    let renderObject: PointRenderObject | undefined
-    let currentProps = DefaultElementPointProps
-    let currentGroup: Unit.SymmetryGroup
-    let currentStructure: Structure
-    let locationIt: LocationIterator
-    let vertices: ValueCell<Float32Array>
-    let currentConformationId: UUID
-
-    return {
-        get renderObject () { return renderObject },
-        async createOrUpdate(ctx: RuntimeContext, props: ElementPointProps = {}, structureGroup?: StructureGroup) {
-            if (structureGroup) currentStructure = structureGroup.structure
-            const group = structureGroup ? structureGroup.group : undefined
-            if (!group && !currentGroup) {
-                throw new Error('missing group')
-            } else if (group && !currentGroup) {
-                currentProps = Object.assign({}, DefaultElementPointProps, props)
-                currentProps.colorTheme.structure = currentStructure
-                currentGroup = group
-                locationIt = StructureElementIterator.fromGroup(group)
-
-                const unit = group.units[0]
-                currentConformationId = Unit.conformationId(unit)
-                vertices = await createElementPointVertices(ctx, unit, vertices)
-
-                renderObject = await createUnitsPointRenderObject(ctx, group, vertices, locationIt, currentProps)
-            } else if (renderObject) {
-                if (group) currentGroup = group
-
-                const newProps = { ...currentProps, ...props }
-                const unit = currentGroup.units[0]
-
-                let updateTransform = false
-                let createVertices = false
-                let updateColor = false
-                let updateSize = false
-
-                const newConformationId = Unit.conformationId(unit)
-                if (newConformationId !== currentConformationId) {
-                    currentConformationId = newConformationId
-                    createVertices = true
-                }
-
-                if (currentGroup.units.length !== locationIt.instanceCount) updateTransform = true
+    return UnitsPointVisual<ElementPointProps>({
+        defaultProps: DefaultElementPointProps,
+        createPoint: createElementPoint,
+        createLocationIterator: StructureElementIterator.fromGroup,
+        getLoci: getElementLoci,
+        mark: markElement,
+        setUpdateState: (state: VisualUpdateState, newProps: ElementPointProps, currentProps: ElementPointProps) => {
 
-                if (!deepEqual(newProps.sizeTheme, currentProps.sizeTheme)) createVertices = true
-                if (!deepEqual(newProps.colorTheme, currentProps.colorTheme)) updateColor = true
-                if (!deepEqual(newProps.sizeTheme, currentProps.sizeTheme)) updateSize = true
-
-                if (updateTransform) {
-                    locationIt = StructureElementIterator.fromGroup(currentGroup)
-                    const { instanceCount, groupCount } = locationIt
-                    createTransforms(currentGroup, renderObject.values)
-                    createMarkers(instanceCount * groupCount, renderObject.values)
-                    updateColor = true
-                    updateSize = true
-                }
-
-                if (createVertices) {
-                    await createElementPointVertices(ctx, unit, vertices)
-                    ValueCell.update(renderObject.values.aGroup, fillSerial(new Float32Array(locationIt.groupCount))) // TODO reuse array
-                    ValueCell.update(renderObject.values.drawCount, locationIt.groupCount)
-                    updateColor = true
-                    updateSize = true
-                }
-
-                if (updateColor) {
-                    await createColors(ctx, locationIt, newProps.colorTheme, renderObject.values)
-                }
-
-                if (updateSize) {
-                    await createSizes(ctx, locationIt, newProps.sizeTheme, renderObject.values)
-                }
-
-                updateBaseValues(renderObject.values, newProps)
-                updateRenderableState(renderObject.state, newProps)
-
-                currentProps = newProps
-            }
-        },
-        getLoci(pickingId: PickingId) {
-            return renderObject ? getElementLoci(pickingId, currentGroup, renderObject.id) : EmptyLoci
-        },
-        mark(loci: Loci, action: MarkerAction) {
-            if (!renderObject) return false
-            const { tMarker } = renderObject.values
-            const { groupCount, instanceCount } = locationIt
-
-            function apply(interval: Interval) {
-                const start = Interval.start(interval)
-                const end = Interval.end(interval)
-                return applyMarkerAction(tMarker.ref.value.array, start, end, action)
-            }
-
-            let changed = false
-            if (isEveryLoci(loci)) {
-                apply(Interval.ofBounds(0, groupCount * instanceCount))
-                changed = true
-            } else {
-                changed = markElement(loci, currentGroup, apply)
-            }
-            if (changed) {
-                ValueCell.update(tMarker, tMarker.ref.value)
-            }
-            return changed
-        },
-        destroy() {
-            // TODO
-            renderObject = undefined
         }
-    }
-}
+    })
+}
\ No newline at end of file
diff --git a/src/mol-geo/representation/structure/visual/element-sphere.ts b/src/mol-geo/representation/structure/visual/element-sphere.ts
index 523e19e1e3d62d8cc1a0c0ce6c3d1fdc4b699c14..62a59004bb7393a0220bfc8d46c1b5e2e213242f 100644
--- a/src/mol-geo/representation/structure/visual/element-sphere.ts
+++ b/src/mol-geo/representation/structure/visual/element-sphere.ts
@@ -5,7 +5,7 @@
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 
-import { UnitsVisual, MeshUpdateState } from '..';
+import { UnitsVisual, VisualUpdateState } from '..';
 import { createElementSphereMesh, markElement, getElementLoci, StructureElementIterator } from './util/element';
 import { UnitsMeshVisual, DefaultUnitsMeshProps } from '../units-visual';
 
@@ -22,8 +22,8 @@ export function ElementSphereVisual(): UnitsVisual<ElementSphereProps> {
         createLocationIterator: StructureElementIterator.fromGroup,
         getLoci: getElementLoci,
         mark: markElement,
-        setUpdateState: (state: MeshUpdateState, newProps: ElementSphereProps, currentProps: ElementSphereProps) => {
-            state.createMesh = newProps.detail !== currentProps.detail
+        setUpdateState: (state: VisualUpdateState, newProps: ElementSphereProps, currentProps: ElementSphereProps) => {
+            state.createGeometry = newProps.detail !== currentProps.detail
         }
     })
 }
\ No newline at end of file
diff --git a/src/mol-geo/representation/structure/visual/gaussian-density-point.ts b/src/mol-geo/representation/structure/visual/gaussian-density-point.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7bc2d1c9d671bad4c69321708b4b25ea99af4621
--- /dev/null
+++ b/src/mol-geo/representation/structure/visual/gaussian-density-point.ts
@@ -0,0 +1,83 @@
+/**
+ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import { Unit, Structure } from 'mol-model/structure';
+import { RuntimeContext } from 'mol-task'
+import { UnitsVisual, VisualUpdateState } from '..';
+import { StructureElementIterator } from './util/element';
+import { EmptyLoci } from 'mol-model/loci';
+import { Vec3 } from 'mol-math/linear-algebra';
+import { SizeThemeProps } from 'mol-view/theme/size';
+import { UnitsPointVisual, DefaultUnitsPointProps } from '../units-visual';
+import { computeGaussianDensity } from './util/gaussian';
+import { Color } from 'mol-util/color';
+import { ColorThemeProps } from 'mol-view/theme/color';
+import { Point } from '../../../geometry/point/point';
+import { PointBuilder } from '../../../geometry/point/point-builder';
+
+export const DefaultGaussianDensityPointProps = {
+    ...DefaultUnitsPointProps,
+
+    sizeTheme: { name: 'uniform', value: 1.5 } as SizeThemeProps,
+    colorTheme: { name: 'uniform', value: Color(0xAAAAAA) } as ColorThemeProps,
+    pointSizeAttenuation: false,
+
+    resolutionFactor: 7,
+    probeRadius: 0,
+    isoValue: 1.5,
+}
+export type GaussianDensityPointProps = typeof DefaultGaussianDensityPointProps
+
+export interface GaussianDensityProps {
+    sizeTheme: SizeThemeProps
+
+    resolutionFactor: number
+    probeRadius: number
+    isoValue: number
+}
+
+export async function createGaussianDensityPoint(ctx: RuntimeContext, unit: Unit, structure: Structure, props: GaussianDensityProps, point?: Point) {
+    const { transform, field: { space, data } } = await computeGaussianDensity(unit, structure, props).runAsChild(ctx)
+
+    const { dimensions, get } = space
+    const [ xn, yn, zn ] = dimensions
+
+    const n = xn * yn * zn * 3
+    const builder = PointBuilder.create(n, n / 10, point)
+
+    const p = Vec3.zero()
+    let i = 0
+
+    for (let x = 0; x < xn; ++x) {
+        for (let y = 0; y < yn; ++y) {
+            for (let z = 0; z < zn; ++z) {
+                if (get(data, x, y, z) > 0.001) {
+                    Vec3.set(p, x, y, z)
+                    Vec3.transformMat4(p, p, transform)
+                    builder.add(p[0], p[1], p[2], i)
+                }
+                if (i % 100000 === 0 && ctx.shouldUpdate) {
+                    await ctx.update({ message: 'Creating density points', current: i, max: n });
+                }
+                ++i
+            }
+        }
+    }
+    return builder.getPoint()
+}
+
+export function GaussianDensityPointVisual(): UnitsVisual<GaussianDensityPointProps> {
+    return UnitsPointVisual<GaussianDensityPointProps>({
+        defaultProps: DefaultGaussianDensityPointProps,
+        createPoint: createGaussianDensityPoint,
+        createLocationIterator: StructureElementIterator.fromGroup,
+        getLoci: () => EmptyLoci,
+        mark: () => false,
+        setUpdateState: (state: VisualUpdateState, newProps: GaussianDensityPointProps, currentProps: GaussianDensityPointProps) => {
+
+        }
+    })
+}
\ No newline at end of file
diff --git a/src/mol-geo/representation/structure/visual/gaussian-surface-mesh.ts b/src/mol-geo/representation/structure/visual/gaussian-surface-mesh.ts
index f966d5076ce7949b6309f33eea7095e8302b7da6..8966ca864a445e4ae546d338d66a73f151c231af 100644
--- a/src/mol-geo/representation/structure/visual/gaussian-surface-mesh.ts
+++ b/src/mol-geo/representation/structure/visual/gaussian-surface-mesh.ts
@@ -4,17 +4,17 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { Unit, Structure, StructureElement } from 'mol-model/structure';
-import { UnitsVisual, MeshUpdateState } from '..';
+import { Unit, Structure } from 'mol-model/structure';
+import { UnitsVisual, VisualUpdateState } from '..';
 import { RuntimeContext } from 'mol-task'
-import { Mesh } from '../../../mesh/mesh';
+import { Mesh } from '../../../geometry/mesh/mesh';
 import { UnitsMeshVisual, DefaultUnitsMeshProps } from '../units-visual';
 import { StructureElementIterator, getElementLoci, markElement } from './util/element';
 import { computeMarchingCubes } from '../../../util/marching-cubes/algorithm';
-import { Tensor, Vec3, Mat4 } from 'mol-math/linear-algebra';
-import { Box3D } from 'mol-math/geometry';
-import { SizeThemeProps, SizeTheme } from 'mol-view/theme/size';
+import { SizeThemeProps } from 'mol-view/theme/size';
 import { Color } from 'mol-util/color';
+import { computeGaussianDensity } from './util/gaussian';
+import { ColorThemeProps } from 'mol-view/theme/color';
 
 export interface GaussianSurfaceMeshProps {
     sizeTheme: SizeThemeProps
@@ -24,116 +24,22 @@ export interface GaussianSurfaceMeshProps {
     isoValue: number
 }
 
-function getDelta(box: Box3D, resolutionFactor: number) {
-    const extent = Vec3.sub(Vec3.zero(), box.max, box.min)
-
-    const n = Math.pow(Math.pow(2, resolutionFactor), 3)
-    const f = (extent[0] * extent[1] * extent[2]) / n
-    const s = Math.pow(f, 1 / 3)
-    const size = Vec3.zero()
-    // Vec3.scale(size, extent, s)
-    Vec3.ceil(size, Vec3.scale(size, extent, s))
-    const delta = Vec3.div(Vec3.zero(), extent, size)
-    return delta
-}
-
 async function createGaussianSurfaceMesh(ctx: RuntimeContext, unit: Unit, structure: Structure, props: GaussianSurfaceMeshProps, mesh?: Mesh): Promise<Mesh> {
-    const { resolutionFactor, probeRadius, isoValue } = props
-
-    const { elements } = unit;
-    const elementCount = elements.length;
-    const sizeTheme = SizeTheme(props.sizeTheme)
-
-    const v = Vec3.zero()
-    const p = Vec3.zero()
-    const pos = unit.conformation.invariantPosition
-    const l = StructureElement.create()
-    l.unit = unit
-
-    const pad = (probeRadius + 2) * 2 // TODO calculate max radius
-    const box = unit.lookup3d.boundary.box
-    const expandedBox = Box3D.expand(Box3D.empty(), box, Vec3.create(pad, pad, pad));
-    const extent = Vec3.sub(Vec3.zero(), expandedBox.max, expandedBox.min)
-    const min = expandedBox.min
-
-    // const n = Math.pow(128, 3)
-    // const f = (extent[0] * extent[1] * extent[2]) / n
-    // const s = Math.pow(f, 1 / 3)
-    // const size = Vec3.zero()
-    // // Vec3.scale(size, extent, s)
-    // Vec3.ceil(size, Vec3.scale(size, extent, s))
-    // const delta = Vec3.div(Vec3.zero(), extent, size)
-
-    // console.log('extent', extent)
-    // console.log('n', n)
-    // console.log('f', f)
-    // console.log('s', s)
-    // console.log('size', size)
-    // console.log('delta', delta)
-    const delta = getDelta(Box3D.expand(Box3D.empty(), structure.boundary.box, Vec3.create(pad, pad, pad)), resolutionFactor)
-    const dim = Vec3.zero()
-    Vec3.ceil(dim, Vec3.mul(dim, extent, delta))
-    // console.log('dim', dim, dim[0] * dim[1] * dim[2])
-
-    const space = Tensor.Space(dim, [0, 1, 2], Float32Array)
-    const data = space.create()
-    const field = Tensor.create(space, data)
-
-    const c = Vec3.zero()
-
-    const radius2 = Vec3.scale(Vec3.zero(), delta, 2)
-    const alpha = 1.5 // smoothness
+    const { isoValue } = props
 
     console.time('surface density')
-    for (let i = 0; i < elementCount; i++) {
-        l.element = elements[i]
-        pos(elements[i], v)
-
-        Vec3.sub(v, v, min)
-        Vec3.mul(c, v, delta)
-
-        const radius = sizeTheme.size(l) // + probeRadius
-
-        const minX = Math.floor(c[0] - radius2[0]) - 1
-        const minY = Math.floor(c[1] - radius2[1]) - 1
-        const minZ = Math.floor(c[2] - radius2[2]) - 1
-        const maxX = Math.ceil(c[0] + radius2[0]) + 1
-        const maxY = Math.ceil(c[1] + radius2[1]) + 1
-        const maxZ = Math.ceil(c[2] + radius2[2]) + 1
-
-        for (let x = minX; x < maxX; ++x) {
-            for (let y = minY; y < maxY; ++y) {
-                for (let z = minZ; z < maxZ; ++z) {
-                    Vec3.set(p, x, y, z)
-                    Vec3.div(p, p, delta)
-                    const dist = Vec3.distance(p, v)
-                    const density = Math.exp(-alpha * Math.pow((dist) / radius, 2))
-                    space.set(data, x, y, z, space.get(data, x, y, z) + density)
-                }
-            }
-        }
-
-        if (i % 100 === 0 && ctx.shouldUpdate) {
-            await ctx.update({ message: 'Gaussian surface', current: i, max: elementCount });
-        }
-    }
+    const { transform, field } = await computeGaussianDensity(unit, structure, props).runAsChild(ctx)
     console.timeEnd('surface density')
 
-    // console.log('data', data)
-
     console.time('surface mc')
     const surface = await computeMarchingCubes({
-        isoLevel: isoValue * Math.exp(-alpha),
+        isoLevel: Math.exp(-isoValue),
         scalarField: field,
         oldSurface: mesh
     }).runAsChild(ctx)
     console.timeEnd('surface mc')
 
-    const t = Mat4.identity()
-    Mat4.fromScaling(t, Vec3.inverse(Vec3.zero(), delta))
-    Mat4.setTranslation(t, expandedBox.min)
-
-    Mesh.transformImmediate(surface, t)
+    Mesh.transformImmediate(surface, transform)
     Mesh.computeNormalsImmediate(surface)
 
     return surface;
@@ -149,11 +55,11 @@ export const DefaultGaussianSurfaceProps = {
     flipSided: true,
     // flatShaded: true,
     alpha: 0.7,
-    colorTheme: { name: 'uniform' as 'uniform', value: Color(0xDDDDDD) },
+    colorTheme: { name: 'uniform' as 'uniform', value: Color(0xDDDDDD) } as ColorThemeProps,
 
     resolutionFactor: 7,
-    probeRadius: 1.4,
-    isoValue: 1,
+    probeRadius: 0,
+    isoValue: 1.5,
 }
 export type GaussianSurfaceProps = typeof DefaultGaussianSurfaceProps
 
@@ -164,10 +70,10 @@ export function GaussianSurfaceVisual(): UnitsVisual<GaussianSurfaceProps> {
         createLocationIterator: StructureElementIterator.fromGroup,
         getLoci: getElementLoci,
         mark: markElement,
-        setUpdateState: (state: MeshUpdateState, newProps: GaussianSurfaceProps, currentProps: GaussianSurfaceProps) => {
-            if (newProps.resolutionFactor !== currentProps.resolutionFactor) state.createMesh = true
-            if (newProps.probeRadius !== currentProps.probeRadius) state.createMesh = true
-            if (newProps.isoValue !== currentProps.isoValue) state.createMesh = true
+        setUpdateState: (state: VisualUpdateState, newProps: GaussianSurfaceProps, currentProps: GaussianSurfaceProps) => {
+            if (newProps.resolutionFactor !== currentProps.resolutionFactor) state.createGeometry = true
+            if (newProps.probeRadius !== currentProps.probeRadius) state.createGeometry = true
+            if (newProps.isoValue !== currentProps.isoValue) state.createGeometry = true
         }
     })
 }
\ No newline at end of file
diff --git a/src/mol-geo/representation/structure/visual/inter-unit-link-cylinder.ts b/src/mol-geo/representation/structure/visual/inter-unit-link-cylinder.ts
index 44469dacff2b8bc4587e8cdb89ce9da3a15b4bff..91fac0b244bbbe0d56cfe65f1abbb20deacd925d 100644
--- a/src/mol-geo/representation/structure/visual/inter-unit-link-cylinder.ts
+++ b/src/mol-geo/representation/structure/visual/inter-unit-link-cylinder.ts
@@ -5,10 +5,10 @@
  */
 
 import { Link, Structure, StructureElement } from 'mol-model/structure';
-import { ComplexVisual, MeshUpdateState } from '..';
+import { ComplexVisual, VisualUpdateState } from '..';
 import { RuntimeContext } from 'mol-task'
 import { LinkCylinderProps, DefaultLinkCylinderProps, createLinkCylinderMesh, LinkIterator } from './util/link';
-import { Mesh } from '../../../mesh/mesh';
+import { Mesh } from '../../../geometry/mesh/mesh';
 import { PickingId } from '../../../util/picking';
 import { Vec3 } from 'mol-math/linear-algebra';
 import { Loci, EmptyLoci } from 'mol-model/loci';
@@ -63,8 +63,8 @@ export function InterUnitLinkVisual(): ComplexVisual<InterUnitLinkProps> {
         createLocationIterator: LinkIterator.fromStructure,
         getLoci: getLinkLoci,
         mark: markLink,
-        setUpdateState: (state: MeshUpdateState, newProps: InterUnitLinkProps, currentProps: InterUnitLinkProps) => {
-            state.createMesh = newProps.radialSegments !== currentProps.radialSegments
+        setUpdateState: (state: VisualUpdateState, newProps: InterUnitLinkProps, currentProps: InterUnitLinkProps) => {
+            state.createGeometry = newProps.radialSegments !== currentProps.radialSegments
         }
     })
 }
diff --git a/src/mol-geo/representation/structure/visual/intra-unit-link-cylinder.ts b/src/mol-geo/representation/structure/visual/intra-unit-link-cylinder.ts
index a3d6dd8687bc1624fd75fb2b45c2a96e383af1c8..8ec45a83d67947576d6647137b871bcf71471d00 100644
--- a/src/mol-geo/representation/structure/visual/intra-unit-link-cylinder.ts
+++ b/src/mol-geo/representation/structure/visual/intra-unit-link-cylinder.ts
@@ -6,10 +6,10 @@
  */
 
 import { Unit, Link, StructureElement, Structure } from 'mol-model/structure';
-import { UnitsVisual, MeshUpdateState } from '..';
+import { UnitsVisual, VisualUpdateState } from '..';
 import { RuntimeContext } from 'mol-task'
 import { DefaultLinkCylinderProps, LinkCylinderProps, createLinkCylinderMesh, LinkIterator } from './util/link';
-import { Mesh } from '../../../mesh/mesh';
+import { Mesh } from '../../../geometry/mesh/mesh';
 import { PickingId } from '../../../util/picking';
 import { Vec3 } from 'mol-math/linear-algebra';
 import { Loci, EmptyLoci } from 'mol-model/loci';
@@ -77,8 +77,8 @@ export function IntraUnitLinkVisual(): UnitsVisual<IntraUnitLinkProps> {
         createLocationIterator: LinkIterator.fromGroup,
         getLoci: getLinkLoci,
         mark: markLink,
-        setUpdateState: (state: MeshUpdateState, newProps: LinkCylinderProps, currentProps: LinkCylinderProps) => {
-            state.createMesh = newProps.radialSegments !== currentProps.radialSegments
+        setUpdateState: (state: VisualUpdateState, newProps: LinkCylinderProps, currentProps: LinkCylinderProps) => {
+            state.createGeometry = newProps.radialSegments !== currentProps.radialSegments
         }
     })
 }
diff --git a/src/mol-geo/representation/structure/visual/nucleotide-block-mesh.ts b/src/mol-geo/representation/structure/visual/nucleotide-block-mesh.ts
index 82387af83fe0a284eebadd7a38b129219606807e..48733329c73d40cc64bb6eb23416b73826b69375 100644
--- a/src/mol-geo/representation/structure/visual/nucleotide-block-mesh.ts
+++ b/src/mol-geo/representation/structure/visual/nucleotide-block-mesh.ts
@@ -7,14 +7,14 @@
 import { Unit, Structure } from 'mol-model/structure';
 import { UnitsVisual } from '..';
 import { RuntimeContext } from 'mol-task'
-import { Mesh } from '../../../mesh/mesh';
-import { MeshBuilder } from '../../../mesh/mesh-builder';
+import { Mesh } from '../../../geometry/mesh/mesh';
+import { MeshBuilder } from '../../../geometry/mesh/mesh-builder';
 import { Vec3, Mat4 } from 'mol-math/linear-algebra';
 import { Segmentation } from 'mol-data/int';
 import { MoleculeType, isNucleic, isPurinBase, isPyrimidineBase } from 'mol-model/structure/model/types';
 import { getElementIndexForAtomId, getElementIndexForAtomRole } from 'mol-model/structure/util';
 import { DefaultUnitsMeshProps, UnitsMeshVisual } from '../units-visual';
-import { addCylinder } from '../../../mesh/builder/cylinder';
+import { addCylinder } from '../../../geometry/mesh/builder/cylinder';
 import { Box } from '../../../primitive/box';
 import { NucleotideLocationIterator, markNucleotideElement, getNucleotideElementLoci } from './util/nucleotide';
 
diff --git a/src/mol-geo/representation/structure/visual/polymer-backbone-cylinder.ts b/src/mol-geo/representation/structure/visual/polymer-backbone-cylinder.ts
index cd9ca682c0cab90544d44ab88e7958a4858ef956..db7428205695bdcc41335eb12be0c7123fe235d6 100644
--- a/src/mol-geo/representation/structure/visual/polymer-backbone-cylinder.ts
+++ b/src/mol-geo/representation/structure/visual/polymer-backbone-cylinder.ts
@@ -5,10 +5,10 @@
  */
 
 import { Unit, Structure } from 'mol-model/structure';
-import { UnitsVisual, MeshUpdateState } from '..';
+import { UnitsVisual, VisualUpdateState } from '..';
 import { RuntimeContext } from 'mol-task'
-import { Mesh } from '../../../mesh/mesh';
-import { MeshBuilder } from '../../../mesh/mesh-builder';
+import { Mesh } from '../../../geometry/mesh/mesh';
+import { MeshBuilder } from '../../../geometry/mesh/mesh-builder';
 import { PolymerBackboneIterator } from './util/polymer';
 import { getElementLoci, markElement, StructureElementIterator } from './util/element';
 import { Vec3 } from 'mol-math/linear-algebra';
@@ -16,7 +16,7 @@ import { DefaultUnitsMeshProps, UnitsMeshVisual } from '../units-visual';
 import { SizeThemeProps, SizeTheme } from 'mol-view/theme/size';
 import { CylinderProps } from '../../../primitive/cylinder';
 import { OrderedSet } from 'mol-data/int';
-import { addCylinder } from '../../../mesh/builder/cylinder';
+import { addCylinder } from '../../../geometry/mesh/builder/cylinder';
 
 export interface PolymerBackboneCylinderProps {
     sizeTheme: SizeThemeProps
@@ -77,8 +77,8 @@ export function PolymerBackboneVisual(): UnitsVisual<PolymerBackboneProps> {
         createLocationIterator: StructureElementIterator.fromGroup,
         getLoci: getElementLoci,
         mark: markElement,
-        setUpdateState: (state: MeshUpdateState, newProps: PolymerBackboneProps, currentProps: PolymerBackboneProps) => {
-            state.createMesh = newProps.radialSegments !== currentProps.radialSegments
+        setUpdateState: (state: VisualUpdateState, newProps: PolymerBackboneProps, currentProps: PolymerBackboneProps) => {
+            state.createGeometry = newProps.radialSegments !== currentProps.radialSegments
         }
     })
 }
\ No newline at end of file
diff --git a/src/mol-geo/representation/structure/visual/polymer-direction-wedge.ts b/src/mol-geo/representation/structure/visual/polymer-direction-wedge.ts
index ddd31a67d6970f1b0e00416461a4a57fd7d79d40..0b6f96f8844ee94c01e37a7ab5efad3df5fbf4f0 100644
--- a/src/mol-geo/representation/structure/visual/polymer-direction-wedge.ts
+++ b/src/mol-geo/representation/structure/visual/polymer-direction-wedge.ts
@@ -7,8 +7,8 @@
 import { Unit, Structure } from 'mol-model/structure';
 import { UnitsVisual } from '..';
 import { RuntimeContext } from 'mol-task'
-import { Mesh } from '../../../mesh/mesh';
-import { MeshBuilder } from '../../../mesh/mesh-builder';
+import { Mesh } from '../../../geometry/mesh/mesh';
+import { MeshBuilder } from '../../../geometry/mesh/mesh-builder';
 import { PolymerTraceIterator, createCurveSegmentState, interpolateCurveSegment, PolymerLocationIterator, getPolymerElementLoci, markPolymerElement } from './util/polymer';
 import { Vec3, Mat4 } from 'mol-math/linear-algebra';
 import { SecondaryStructureType, isNucleic } from 'mol-model/structure/model/types';
diff --git a/src/mol-geo/representation/structure/visual/polymer-gap-cylinder.ts b/src/mol-geo/representation/structure/visual/polymer-gap-cylinder.ts
index 21ebafc2d3ffd91d4160defa3e9412568df14d86..cab4cd3940e72cad0ea9b5394c1c95435b47aad4 100644
--- a/src/mol-geo/representation/structure/visual/polymer-gap-cylinder.ts
+++ b/src/mol-geo/representation/structure/visual/polymer-gap-cylinder.ts
@@ -5,17 +5,17 @@
  */
 
 import { Unit, Structure } from 'mol-model/structure';
-import { UnitsVisual, MeshUpdateState } from '..';
+import { UnitsVisual, VisualUpdateState } from '..';
 import { RuntimeContext } from 'mol-task'
-import { Mesh } from '../../../mesh/mesh';
-import { MeshBuilder } from '../../../mesh/mesh-builder';
+import { Mesh } from '../../../geometry/mesh/mesh';
+import { MeshBuilder } from '../../../geometry/mesh/mesh-builder';
 import { PolymerGapIterator, PolymerGapLocationIterator, markPolymerGapElement, getPolymerGapElementLoci } from './util/polymer';
 import { Vec3 } from 'mol-math/linear-algebra';
 import { UnitsMeshVisual, DefaultUnitsMeshProps } from '../units-visual';
 import { SizeThemeProps, SizeTheme } from 'mol-view/theme/size';
 import { CylinderProps } from '../../../primitive/cylinder';
-import { addSphere } from '../../../mesh/builder/sphere';
-import { addFixedCountDashedCylinder } from '../../../mesh/builder/cylinder';
+import { addSphere } from '../../../geometry/mesh/builder/sphere';
+import { addFixedCountDashedCylinder } from '../../../geometry/mesh/builder/cylinder';
 
 const segmentCount = 10
 
@@ -84,8 +84,8 @@ export function PolymerGapVisual(): UnitsVisual<PolymerGapProps> {
         createLocationIterator: PolymerGapLocationIterator.fromGroup,
         getLoci: getPolymerGapElementLoci,
         mark: markPolymerGapElement,
-        setUpdateState: (state: MeshUpdateState, newProps: PolymerGapProps, currentProps: PolymerGapProps) => {
-            state.createMesh = newProps.radialSegments !== currentProps.radialSegments
+        setUpdateState: (state: VisualUpdateState, newProps: PolymerGapProps, currentProps: PolymerGapProps) => {
+            state.createGeometry = newProps.radialSegments !== currentProps.radialSegments
         }
     })
 }
\ No newline at end of file
diff --git a/src/mol-geo/representation/structure/visual/polymer-trace-mesh.ts b/src/mol-geo/representation/structure/visual/polymer-trace-mesh.ts
index 186f374e1ad71d661731431b3e9f100b6f690e63..6ecfa05d443b4bd7e725898ad697ca32b0f287be 100644
--- a/src/mol-geo/representation/structure/visual/polymer-trace-mesh.ts
+++ b/src/mol-geo/representation/structure/visual/polymer-trace-mesh.ts
@@ -5,16 +5,16 @@
  */
 
 import { Unit, Structure } from 'mol-model/structure';
-import { UnitsVisual, MeshUpdateState } from '..';
+import { UnitsVisual, VisualUpdateState } from '..';
 import { RuntimeContext } from 'mol-task'
-import { Mesh } from '../../../mesh/mesh';
-import { MeshBuilder } from '../../../mesh/mesh-builder';
+import { Mesh } from '../../../geometry/mesh/mesh';
+import { MeshBuilder } from '../../../geometry/mesh/mesh-builder';
 import { PolymerTraceIterator, createCurveSegmentState, interpolateCurveSegment, PolymerLocationIterator, getPolymerElementLoci, markPolymerElement } from './util/polymer';
 import { SecondaryStructureType, isNucleic } from 'mol-model/structure/model/types';
 import { UnitsMeshVisual, DefaultUnitsMeshProps } from '../units-visual';
 import { SizeThemeProps, SizeTheme } from 'mol-view/theme/size';
-import { addSheet } from '../../../mesh/builder/sheet';
-import { addTube } from '../../../mesh/builder/tube';
+import { addSheet } from '../../../geometry/mesh/builder/sheet';
+import { addTube } from '../../../geometry/mesh/builder/tube';
 
 export interface PolymerTraceMeshProps {
     sizeTheme: SizeThemeProps
@@ -97,8 +97,8 @@ export function PolymerTraceVisual(): UnitsVisual<PolymerTraceProps> {
         createLocationIterator: PolymerLocationIterator.fromGroup,
         getLoci: getPolymerElementLoci,
         mark: markPolymerElement,
-        setUpdateState: (state: MeshUpdateState, newProps: PolymerTraceProps, currentProps: PolymerTraceProps) => {
-            state.createMesh = (
+        setUpdateState: (state: VisualUpdateState, newProps: PolymerTraceProps, currentProps: PolymerTraceProps) => {
+            state.createGeometry = (
                 newProps.linearSegments !== currentProps.linearSegments ||
                 newProps.radialSegments !== currentProps.radialSegments ||
                 newProps.aspectRatio !== currentProps.aspectRatio ||
diff --git a/src/mol-geo/representation/structure/visual/util/common.ts b/src/mol-geo/representation/structure/visual/util/common.ts
index 0dfb1317bd369f48ba0ec9f8590f0811f3cdba6b..1377c9cd97ed38a41c676cf841cac568e5a36463 100644
--- a/src/mol-geo/representation/structure/visual/util/common.ts
+++ b/src/mol-geo/representation/structure/visual/util/common.ts
@@ -8,12 +8,10 @@
 import { Unit, Structure } from 'mol-model/structure';
 import { createUniformColor, ColorData, createGroupColor, createGroupInstanceColor, createInstanceColor, ColorType } from '../../../../util/color-data';
 import { createUniformSize, SizeData, createGroupSize, createGroupInstanceSize, createInstanceSize, SizeType } from '../../../../util/size-data';
-import { ValueCell } from 'mol-util';
 import { LocationIterator } from '../../../../util/location-iterator';
-import { Mesh } from '../../../../mesh/mesh';
+import { Mesh } from '../../../../geometry/mesh/mesh';
 import { MeshValues, PointValues } from 'mol-gl/renderable';
 import { getMeshData } from '../../../../util/mesh-data';
-import { MeshProps, createMeshValues, createRenderableState, createPointValues } from '../../../util';
 import { StructureProps } from '../..';
 import { createMarkers } from '../../../../util/marker-data';
 import { createMeshRenderObject, createPointRenderObject } from 'mol-gl/render-object';
@@ -21,8 +19,10 @@ import { ColorThemeProps, ColorTheme } from 'mol-view/theme/color';
 import { SizeThemeProps, SizeTheme } from 'mol-view/theme/size';
 import { RuntimeContext } from 'mol-task';
 import { PointProps } from 'mol-geo/representation/structure/representation/point';
-import { fillSerial } from 'mol-util/array';
 import { TransformData, createIdentityTransform, createTransforms } from '../../../../util/transform-data';
+import { Point } from '../../../../geometry/point/point';
+import { getPointData } from '../../../../util/point-data';
+import { MeshProps, createMeshValues, createRenderableState, createPointValues } from '../../../../geometry/geometry';
 
 function getGranularity(locationIt: LocationIterator, granularity: ColorType | SizeType) {
     // Always use 'group' granularity for 'complex' location iterators,
@@ -52,7 +52,7 @@ export async function createSizes(ctx: RuntimeContext, locationIt: LocationItera
 
 // mesh
 
-type StructureMeshProps = Required<MeshProps & StructureProps>
+type StructureMeshProps = MeshProps & StructureProps
 
 async function _createMeshValues(ctx: RuntimeContext, transforms: TransformData, mesh: Mesh, locationIt: LocationIterator, props: StructureMeshProps): Promise<MeshValues> {
     const { instanceCount, groupCount } = locationIt
@@ -100,19 +100,18 @@ export async function updateComplexMeshRenderObject(ctx: RuntimeContext, structu
 
 // point
 
-type StructurePointProps = Required<PointProps & StructureProps>
+type StructurePointProps = PointProps & StructureProps
 
-async function _createPointValues(ctx: RuntimeContext, transforms: TransformData, vertices: ValueCell<Float32Array>, locationIt: LocationIterator, props: StructurePointProps): Promise<PointValues> {
+async function _createPointValues(ctx: RuntimeContext, transforms: TransformData, point: Point, locationIt: LocationIterator, props: StructurePointProps): Promise<PointValues> {
     const { instanceCount, groupCount } = locationIt
     const color = await createColors(ctx, locationIt, props.colorTheme)
     const size = await createSizes(ctx, locationIt, props.sizeTheme)
     const marker = createMarkers(instanceCount * groupCount)
 
-    const counts = { drawCount: groupCount, groupCount, instanceCount }
+    const counts = { drawCount: point.vertexCount, groupCount, instanceCount }
 
     return {
-        aPosition: vertices,
-        aGroup: ValueCell.create(fillSerial(new Float32Array(groupCount))),
+        ...getPointData(point),
         ...color,
         ...size,
         ...marker,
@@ -121,13 +120,13 @@ async function _createPointValues(ctx: RuntimeContext, transforms: TransformData
     }
 }
 
-export async function createUnitsPointValues(ctx: RuntimeContext, group: Unit.SymmetryGroup, vertices: ValueCell<Float32Array>, locationIt: LocationIterator, props: StructurePointProps): Promise<PointValues> {
+export async function createUnitsPointValues(ctx: RuntimeContext, group: Unit.SymmetryGroup, point: Point, locationIt: LocationIterator, props: StructurePointProps): Promise<PointValues> {
     const transforms = createTransforms(group)
-    return _createPointValues(ctx, transforms, vertices, locationIt, props)
+    return _createPointValues(ctx, transforms, point, locationIt, props)
 }
 
-export async function createUnitsPointRenderObject(ctx: RuntimeContext, group: Unit.SymmetryGroup, vertices: ValueCell<Float32Array>, locationIt: LocationIterator, props: StructurePointProps) {
-    const values = await createUnitsPointValues(ctx, group, vertices, locationIt, props)
+export async function createUnitsPointRenderObject(ctx: RuntimeContext, group: Unit.SymmetryGroup, point: Point, locationIt: LocationIterator, props: StructurePointProps) {
+    const values = await createUnitsPointValues(ctx, group, point, locationIt, props)
     const state = createRenderableState(props)
     return createPointRenderObject(values, state)
 }
\ No newline at end of file
diff --git a/src/mol-geo/representation/structure/visual/util/element.ts b/src/mol-geo/representation/structure/visual/util/element.ts
index 6887addab337f1956adcb4c1645e2586c9bbd4ab..28d4f1add33e11152269038cf6aedc4052ba5851 100644
--- a/src/mol-geo/representation/structure/visual/util/element.ts
+++ b/src/mol-geo/representation/structure/visual/util/element.ts
@@ -8,14 +8,14 @@ import { Vec3 } from 'mol-math/linear-algebra';
 import { Unit, StructureElement, Structure } from 'mol-model/structure';
 import { RuntimeContext } from 'mol-task';
 import { sphereVertexCount } from '../../../../primitive/sphere';
-import { Mesh } from '../../../../mesh/mesh';
-import { MeshBuilder } from '../../../../mesh/mesh-builder';
+import { Mesh } from '../../../../geometry/mesh/mesh';
+import { MeshBuilder } from '../../../../geometry/mesh/mesh-builder';
 import { Loci, EmptyLoci } from 'mol-model/loci';
 import { Interval, OrderedSet } from 'mol-data/int';
 import { PickingId } from '../../../../util/picking';
 import { SizeTheme, SizeThemeProps } from 'mol-view/theme/size';
 import { LocationIterator } from '../../../../util/location-iterator';
-import { addSphere } from '../../../../mesh/builder/sphere';
+import { addSphere } from '../../../../geometry/mesh/builder/sphere';
 
 export interface ElementSphereMeshProps {
     sizeTheme: SizeThemeProps,
diff --git a/src/mol-geo/representation/structure/visual/util/gaussian.ts b/src/mol-geo/representation/structure/visual/util/gaussian.ts
new file mode 100644
index 0000000000000000000000000000000000000000..1607bbb7fb3f71ce0a4e5d0998da9fa1c05289de
--- /dev/null
+++ b/src/mol-geo/representation/structure/visual/util/gaussian.ts
@@ -0,0 +1,124 @@
+/**
+ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import { Unit, Structure, StructureElement } from 'mol-model/structure';
+import { RuntimeContext, Task } from 'mol-task'
+import { Tensor, Vec3, Mat4 } from 'mol-math/linear-algebra';
+import { Box3D } from 'mol-math/geometry';
+import { SizeThemeProps, SizeTheme } from 'mol-view/theme/size';
+
+export interface GaussianDensityProps {
+    sizeTheme: SizeThemeProps
+
+    resolutionFactor: number
+    probeRadius: number
+    isoValue: number
+}
+
+function getDelta(box: Box3D, resolutionFactor: number) {
+    const extent = Vec3.sub(Vec3.zero(), box.max, box.min)
+
+    const n = Math.pow(Math.pow(2, resolutionFactor), 3)
+    const f = (extent[0] * extent[1] * extent[2]) / n
+    const s = Math.pow(f, 1 / 3)
+    const size = Vec3.zero()
+    // Vec3.scale(size, extent, s)
+    Vec3.ceil(size, Vec3.scale(size, extent, s))
+    const delta = Vec3.div(Vec3.zero(), extent, size)
+    return delta
+}
+
+type Density = { transform: Mat4, field: Tensor }
+
+export function computeGaussianDensity(unit: Unit, structure: Structure, props: GaussianDensityProps) {
+    return Task.create('Gaussian Density', async ctx => {
+        return await GaussianDensity(ctx, unit, structure, props);
+    });
+}
+
+export async function GaussianDensity(ctx: RuntimeContext, unit: Unit, structure: Structure, props: GaussianDensityProps): Promise<Density> {
+    const { resolutionFactor, probeRadius, isoValue } = props
+
+    const { elements } = unit;
+    const elementCount = elements.length;
+    const sizeTheme = SizeTheme(props.sizeTheme)
+
+    const v = Vec3.zero()
+    const p = Vec3.zero()
+    const pos = unit.conformation.invariantPosition
+    const l = StructureElement.create(unit)
+
+    const pad = (probeRadius + 3) * 3 // TODO calculate max radius
+    const box = unit.lookup3d.boundary.box
+    const expandedBox = Box3D.expand(Box3D.empty(), box, Vec3.create(pad, pad, pad));
+    const extent = Vec3.sub(Vec3.zero(), expandedBox.max, expandedBox.min)
+    const min = expandedBox.min
+
+    const delta = getDelta(Box3D.expand(Box3D.empty(), structure.boundary.box, Vec3.create(pad, pad, pad)), resolutionFactor)
+    const dim = Vec3.zero()
+    Vec3.ceil(dim, Vec3.mul(dim, extent, delta))
+
+    const space = Tensor.Space(dim, [0, 1, 2], Float32Array)
+    const data = space.create()
+    const field = Tensor.create(space, data)
+
+    const c = Vec3.zero()
+
+    const alpha = isoValue // smoothness
+
+    const _r2 = (probeRadius + 1.4 * 2)
+    const _radius2 = Vec3.create(_r2, _r2, _r2)
+    Vec3.mul(_radius2, _radius2, delta)
+    const updateChunk = Math.ceil(10000 / (_radius2[0] * _radius2[1] * _radius2[2]))
+
+    const beg = Vec3.zero()
+    const end = Vec3.zero()
+
+    for (let i = 0; i < elementCount; i++) {
+        l.element = elements[i]
+        pos(elements[i], v)
+
+        Vec3.sub(v, v, min)
+        Vec3.mul(c, v, delta)
+
+        const radius = sizeTheme.size(l) + probeRadius
+        const rSq = radius * radius
+
+        const r2 = (probeRadius + radius * 2)
+        const radius2 = Vec3.create(r2, r2, r2)
+        Vec3.mul(radius2, radius2, delta)
+        const r2sq = r2 * r2
+
+        const [ begX, begY, begZ ] = Vec3.floor(beg, Vec3.sub(beg, c, radius2))
+        const [ endX, endY, endZ ] = Vec3.ceil(end, Vec3.add(end, c, radius2))
+
+        for (let x = begX; x < endX; ++x) {
+            for (let y = begY; y < endY; ++y) {
+                for (let z = begZ; z < endZ; ++z) {
+                    Vec3.set(p, x, y, z)
+                    Vec3.div(p, p, delta)
+                    const distSq = Vec3.squaredDistance(p, v)
+                    if (distSq <= r2sq) {
+                        space.add(data, x, y, z, Math.exp(-alpha * (distSq / rSq)))
+                    }
+                }
+            }
+        }
+
+        if (i % updateChunk === 0 && ctx.shouldUpdate) {
+            await ctx.update({ message: 'filling density grid', current: i, max: elementCount });
+        }
+    }
+
+    const t = Mat4.identity()
+    Mat4.fromScaling(t, Vec3.inverse(Vec3.zero(), delta))
+    Mat4.setTranslation(t, expandedBox.min)
+
+    return {
+        field,
+        transform: t
+    }
+}
\ No newline at end of file
diff --git a/src/mol-geo/representation/structure/visual/util/link.ts b/src/mol-geo/representation/structure/visual/util/link.ts
index ca73e55b9cb017173c7cb9af15701e7aff21a1fc..b218c9cf2420e8da986886068bb0bb6349e22d50 100644
--- a/src/mol-geo/representation/structure/visual/util/link.ts
+++ b/src/mol-geo/representation/structure/visual/util/link.ts
@@ -6,15 +6,15 @@
 
 import { Vec3 } from 'mol-math/linear-algebra';
 import { RuntimeContext } from 'mol-task';
-import { Mesh } from '../../../../mesh/mesh';
-import { MeshBuilder } from '../../../../mesh/mesh-builder';
+import { Mesh } from '../../../../geometry/mesh/mesh';
+import { MeshBuilder } from '../../../../geometry/mesh/mesh-builder';
 import { LinkType } from 'mol-model/structure/model/types';
-import { DefaultMeshProps } from '../../../util';
 import { SizeThemeProps } from 'mol-view/theme/size';
 import { CylinderProps } from '../../../../primitive/cylinder';
 import { LocationIterator } from '../../../../util/location-iterator';
 import { Unit, StructureElement, Structure, Link } from 'mol-model/structure';
-import { addFixedCountDashedCylinder, addCylinder, addDoubleCylinder } from '../../../../mesh/builder/cylinder';
+import { addFixedCountDashedCylinder, addCylinder, addDoubleCylinder } from '../../../../geometry/mesh/builder/cylinder';
+import { DefaultMeshProps } from '../../../../geometry/geometry';
 
 export const DefaultLinkCylinderProps = {
     ...DefaultMeshProps,
diff --git a/src/mol-geo/representation/util.ts b/src/mol-geo/representation/util.ts
index 48df84aa75bcdbd5b215c031c7144a7f7b4a330d..407ade0f2311b0472fd15688a57b63b555251444 100644
--- a/src/mol-geo/representation/util.ts
+++ b/src/mol-geo/representation/util.ts
@@ -4,97 +4,9 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { ValueCell } from 'mol-util/value-cell'
-import { BaseValues } from 'mol-gl/renderable/schema';
-import { MeshValues, RenderableState } from 'mol-gl/renderable';
 import { defaults } from 'mol-util';
 import { Structure } from 'mol-model/structure';
-
-export const DefaultBaseProps = {
-    alpha: 1,
-    visible: true,
-    depthMask: true,
-    useFog: false,
-    quality: 'auto' as VisualQuality
-}
-export type BaseProps = typeof DefaultBaseProps
-
-export const DefaultMeshProps = {
-    ...DefaultBaseProps,
-    doubleSided: false,
-    flipSided: false,
-    flatShaded: false,
-}
-export type MeshProps = typeof DefaultMeshProps
-
-export const DefaultPointProps = {
-    ...DefaultBaseProps,
-    pointSizeAttenuation: true
-}
-export type PointProps = typeof DefaultPointProps
-
-type Counts = { drawCount: number, groupCount: number, instanceCount: number }
-
-export function createBaseValues(props: Required<BaseProps>, counts: Counts) {
-    return {
-        uAlpha: ValueCell.create(props.alpha),
-        uGroupCount: ValueCell.create(counts.groupCount),
-        drawCount: ValueCell.create(counts.drawCount),
-        dUseFog: ValueCell.create(props.useFog),
-    }
-}
-
-export function createMeshValues(props: Required<MeshProps>, counts: Counts) {
-    return {
-        ...createBaseValues(props, counts),
-        dDoubleSided: ValueCell.create(props.doubleSided),
-        dFlatShaded: ValueCell.create(props.flatShaded),
-        dFlipSided: ValueCell.create(props.flipSided),
-    }
-}
-
-export function createPointValues(props: Required<PointProps>, counts: Counts) {
-    return {
-        ...createBaseValues(props, counts),
-        dPointSizeAttenuation: ValueCell.create(props.pointSizeAttenuation),
-    }
-}
-
-export function createRenderableState(props: Required<BaseProps>): RenderableState {
-    return {
-        visible: props.visible,
-        depthMask: props.depthMask
-    }
-}
-
-export function updateBaseValues(values: BaseValues, props: Required<BaseProps>) {
-    ValueCell.updateIfChanged(values.uAlpha, props.alpha)
-    ValueCell.updateIfChanged(values.dUseFog, props.useFog)
-}
-
-export function updateMeshValues(values: MeshValues, props: Required<MeshProps>) {
-    updateBaseValues(values, props)
-    ValueCell.updateIfChanged(values.dDoubleSided, props.doubleSided)
-    ValueCell.updateIfChanged(values.dFlatShaded, props.flatShaded)
-    ValueCell.updateIfChanged(values.dFlipSided, props.flipSided)
-}
-
-export function updateRenderableState(state: RenderableState, props: Required<BaseProps>) {
-    state.visible = props.visible
-    state.depthMask = props.depthMask
-}
-
-export const VisualQualityInfo = {
-    'custom': {},
-    'auto': {},
-    'highest': {},
-    'high': {},
-    'medium': {},
-    'low': {},
-    'lowest': {},
-}
-export type VisualQuality = keyof typeof VisualQualityInfo
-export const VisualQualityNames = Object.keys(VisualQualityInfo)
+import { VisualQuality } from '../geometry/geometry';
 
 export interface QualityProps {
     quality: VisualQuality
diff --git a/src/mol-geo/representation/volume/index.ts b/src/mol-geo/representation/volume/index.ts
index 9197885d3c9cebb758d2cd37218437ec3164f561..0ddad3eeaba9ca880578380152e0bf4126026687 100644
--- a/src/mol-geo/representation/volume/index.ts
+++ b/src/mol-geo/representation/volume/index.ts
@@ -11,7 +11,7 @@ import { VolumeData } from 'mol-model/volume';
 import { PickingId } from '../../util/picking';
 import { Loci, EmptyLoci } from 'mol-model/loci';
 import { MarkerAction } from '../../util/marker-data';
-import { DefaultBaseProps } from '../util';
+import { DefaultBaseProps } from '../../geometry/geometry';
 
 export interface VolumeVisual<P extends RepresentationProps = {}> extends Visual<VolumeData, P> { }
 
diff --git a/src/mol-geo/representation/volume/surface.ts b/src/mol-geo/representation/volume/surface.ts
index 46671107fcfff72a825598c02692718e6c1953b7..c621e43ebb6ea16b8a9260f8ac382bc7dfa1309a 100644
--- a/src/mol-geo/representation/volume/surface.ts
+++ b/src/mol-geo/representation/volume/surface.ts
@@ -8,7 +8,7 @@
 import { VolumeData, VolumeIsoValue } from 'mol-model/volume'
 import { Task, RuntimeContext } from 'mol-task'
 import { computeMarchingCubes } from '../../util/marching-cubes/algorithm';
-import { Mesh } from '../../mesh/mesh';
+import { Mesh } from '../../geometry/mesh/mesh';
 import { VolumeVisual } from '.';
 import { createMeshRenderObject, MeshRenderObject } from 'mol-gl/render-object';
 import { ValueCell, defaults } from 'mol-util';
diff --git a/src/mol-geo/util/marching-cubes/algorithm.ts b/src/mol-geo/util/marching-cubes/algorithm.ts
index 6ac77a8ffdb2e3411223ec1f4f0729bcb386547c..19393af7888be054c874c82d37949b5ecf24596e 100644
--- a/src/mol-geo/util/marching-cubes/algorithm.ts
+++ b/src/mol-geo/util/marching-cubes/algorithm.ts
@@ -7,7 +7,7 @@
 import { Task, RuntimeContext } from 'mol-task'
 import { ChunkedArray } from 'mol-data/util'
 import { Tensor } from 'mol-math/linear-algebra'
-import { Mesh } from '../../mesh/mesh'
+import { Mesh } from '../../geometry/mesh/mesh'
 import { Index, EdgeIdInfo, CubeEdges, EdgeTable, TriTable } from './tables'
 import { ValueCell } from 'mol-util'
 
@@ -76,6 +76,7 @@ class MarchingCubesComputation {
         const os = this.parameters.oldSurface
 
         return {
+            kind: 'mesh',
             vertexCount:  this.state.vertexCount,
             triangleCount: this.state.triangleCount,
             vertexBuffer: os ? ValueCell.update(os.vertexBuffer, vb) : ValueCell.create(vb),
diff --git a/src/mol-geo/util/mesh-data.ts b/src/mol-geo/util/mesh-data.ts
index 0451ee98a8bfee2d7e2790c69a40a3144ca6ba69..37ed0291ce8843e72ac7068ddc8c4d23f6f7454d 100644
--- a/src/mol-geo/util/mesh-data.ts
+++ b/src/mol-geo/util/mesh-data.ts
@@ -5,7 +5,7 @@
  */
 
 import { ValueCell } from 'mol-util/value-cell'
-import { Mesh } from '../mesh/mesh';
+import { Mesh } from '../geometry/mesh/mesh';
 
 type MeshData = {
     aPosition: ValueCell<Float32Array>,
diff --git a/src/mol-geo/util/point-data.ts b/src/mol-geo/util/point-data.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ce070cf7e9d34fb6bc418f3efa91b95af428d3e5
--- /dev/null
+++ b/src/mol-geo/util/point-data.ts
@@ -0,0 +1,20 @@
+/**
+ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import { ValueCell } from 'mol-util/value-cell'
+import { Point } from '../geometry/point/point';
+
+type PointData = {
+    aPosition: ValueCell<Float32Array>,
+    aGroup: ValueCell<Float32Array>,
+}
+
+export function getPointData(point: Point): PointData {
+    return {
+        aPosition: point.vertexBuffer,
+        aGroup: point.groupBuffer,
+    }
+}
\ No newline at end of file
diff --git a/src/mol-io/reader/obj/parser.ts b/src/mol-io/reader/obj/parser.ts
index 46cd16ccb151b1fb0ab69afa873c83f33a09e0b8..046143b305b51530b1e8d0e1cad7f8cb097fa50c 100644
--- a/src/mol-io/reader/obj/parser.ts
+++ b/src/mol-io/reader/obj/parser.ts
@@ -6,7 +6,7 @@
 
 import Result from '../result'
 import { Task, RuntimeContext } from 'mol-task'
-import { Mesh } from 'mol-geo/mesh/mesh';
+import { Mesh } from 'mol-geo/geometry/mesh/mesh';
 
 async function parseInternal(data: string, ctx: RuntimeContext): Promise<Result<Mesh>> {
     // TODO
diff --git a/src/mol-math/linear-algebra/tensor.ts b/src/mol-math/linear-algebra/tensor.ts
index 4bcae46f7bd0db8987d0036ed8590ba804cd1d9c..0909730c58111245e1c2cf33ef46c49f648b31f7 100644
--- a/src/mol-math/linear-algebra/tensor.ts
+++ b/src/mol-math/linear-algebra/tensor.ts
@@ -20,6 +20,7 @@ export namespace Tensor {
         create(array?: ArrayCtor): Tensor.Data,
         get(data: Tensor.Data, ...coords: number[]): number
         set(data: Tensor.Data, ...coordsAndValue: number[]): number
+        add(data: Tensor.Data, ...coordsAndValue: number[]): number
     }
 
     interface Layout {
@@ -45,8 +46,8 @@ export namespace Tensor {
 
     export function Space(dimensions: number[], axisOrderSlowToFast: number[], ctor?: ArrayCtor): Space {
         const layout = Layout(dimensions, axisOrderSlowToFast, ctor);
-        const { get, set } = accessors(layout);
-        return { rank: dimensions.length, dimensions, axisOrderSlowToFast, create: creator(layout), get, set };
+        const { get, set, add } = accessors(layout);
+        return { rank: dimensions.length, dimensions, axisOrderSlowToFast, create: creator(layout), get, set, add };
     }
 
     export function Data1(values: ArrayLike<number>): Data { return values as Data; }
@@ -98,52 +99,89 @@ export namespace Tensor {
         return true;
     }
 
-    function accessors(layout: Layout): { get: Space['get'], set: Space['set'] } {
+    function accessors(layout: Layout): { get: Space['get'], set: Space['set'], add: Space['add'] } {
         const { dimensions, axisOrderFastToSlow: ao } = layout;
         switch (dimensions.length) {
-            case 1: return { get: (t, d) => t[d], set: (t, d, x) => t[d] = x };
+            case 1: return {
+                get: (t, d) => t[d],
+                set: (t, d, x) => t[d] = x,
+                add: (t, d, x) => t[d] += x
+            };
             case 2: {
                 // column major
                 if (ao[0] === 0 && ao[1] === 1) {
                     const rows = dimensions[0];
-                    return { get: (t, i, j) => t[j * rows + i], set: (t, i, j, x) => t[j * rows + i] = x };
+                    return {
+                        get: (t, i, j) => t[j * rows + i],
+                        set: (t, i, j, x) => t[j * rows + i] = x,
+                        add: (t, i, j, x) => t[j * rows + i] += x
+                    };
                 }
                 if (ao[0] === 1 && ao[1] === 0) {
                     const cols = dimensions[1];
-                    return { get: (t, i, j) => t[i * cols + j], set: (t, i, j, x) => t[i * cols + j] = x };
+                    return {
+                        get: (t, i, j) => t[i * cols + j],
+                        set: (t, i, j, x) => t[i * cols + j] = x,
+                        add: (t, i, j, x) => t[i * cols + j] += x
+                    };
                 }
                 throw new Error('bad axis order')
             }
             case 3: {
                 if (ao[0] === 0 && ao[1] === 1 && ao[2] === 2) { // 012 ijk
                     const u = dimensions[0], v = dimensions[1], uv = u * v;
-                    return { get: (t, i, j, k) => t[i + j * u + k * uv], set: (t, i, j, k, x ) => t[i + j * u + k * uv] = x };
+                    return {
+                        get: (t, i, j, k) => t[i + j * u + k * uv],
+                        set: (t, i, j, k, x ) => t[i + j * u + k * uv] = x,
+                        add: (t, i, j, k, x ) => t[i + j * u + k * uv] += x
+                    };
                 }
                 if (ao[0] === 0 && ao[1] === 2 && ao[2] === 1) { // 021 ikj
                     const u = dimensions[0], v = dimensions[2], uv = u * v;
-                    return { get: (t, i, j, k) => t[i + k * u + j * uv], set: (t, i, j, k, x ) => t[i + k * u + j * uv] = x };
+                    return {
+                        get: (t, i, j, k) => t[i + k * u + j * uv],
+                        set: (t, i, j, k, x ) => t[i + k * u + j * uv] = x,
+                        add: (t, i, j, k, x ) => t[i + k * u + j * uv] += x
+                    };
                 }
                 if (ao[0] === 1 && ao[1] === 0 && ao[2] === 2) { // 102 jik
                     const u = dimensions[1], v = dimensions[0], uv = u * v;
-                    return { get: (t, i, j, k) => t[j + i * u + k * uv], set: (t, i, j, k, x ) => t[j + i * u + k * uv] = x };
+                    return {
+                        get: (t, i, j, k) => t[j + i * u + k * uv],
+                        set: (t, i, j, k, x ) => t[j + i * u + k * uv] = x,
+                        add: (t, i, j, k, x ) => t[j + i * u + k * uv] += x
+                    };
                 }
                 if (ao[0] === 1 && ao[1] === 2 && ao[2] === 0) { // 120 jki
                     const u = dimensions[1], v = dimensions[2], uv = u * v;
-                    return { get: (t, i, j, k) => t[j + k * u + i * uv], set: (t, i, j, k, x ) => t[j + k * u + i * uv] = x };
+                    return {
+                        get: (t, i, j, k) => t[j + k * u + i * uv],
+                        set: (t, i, j, k, x ) => t[j + k * u + i * uv] = x,
+                        add: (t, i, j, k, x ) => t[j + k * u + i * uv] += x
+                    };
                 }
                 if (ao[0] === 2 && ao[1] === 0 && ao[2] === 1) { // 201 kij
                     const u = dimensions[2], v = dimensions[0], uv = u * v;
-                    return { get: (t, i, j, k) => t[k + i * u + j * uv], set: (t, i, j, k, x ) => t[k + i * u + j * uv] = x };
+                    return {
+                        get: (t, i, j, k) => t[k + i * u + j * uv],
+                        set: (t, i, j, k, x ) => t[k + i * u + j * uv] = x,
+                        add: (t, i, j, k, x ) => t[k + i * u + j * uv] += x
+                    };
                 }
                 if (ao[0] === 2 && ao[1] === 1 && ao[2] === 0) { // 210 kji
                     const u = dimensions[2], v = dimensions[1], uv = u * v;
-                    return { get: (t, i, j, k) => t[k + j * u + i * uv], set: (t, i, j, k, x ) => t[k + j * u + i * uv] = x };
+                    return {
+                        get: (t, i, j, k) => t[k + j * u + i * uv],
+                        set: (t, i, j, k, x ) => t[k + j * u + i * uv] = x,
+                        add: (t, i, j, k, x ) => t[k + j * u + i * uv] += x
+                    };
                 }
                 throw new Error('bad axis order')
             }
             default: return {
                 get: (t, ...c) => t[dataOffset(layout, c)],
-                set: (t, ...c) => t[dataOffset(layout, c)] = c[c.length - 1]
+                set: (t, ...c) => t[dataOffset(layout, c)] = c[c.length - 1],
+                add: (t, ...c) => t[dataOffset(layout, c)] += c[c.length - 1]
             };
         }
     }
diff --git a/src/mol-model/shape/shape.ts b/src/mol-model/shape/shape.ts
index ffc602342db05a6419dd471d38b755ff41c51fa3..b911bd340fa8432444d4253b4fbcbed3608a592e 100644
--- a/src/mol-model/shape/shape.ts
+++ b/src/mol-model/shape/shape.ts
@@ -4,7 +4,7 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { Mesh } from 'mol-geo/mesh/mesh';
+import { Mesh } from 'mol-geo/geometry/mesh/mesh';
 import { Color } from 'mol-util/color';
 import { UUID, ValueCell } from 'mol-util';
 import { OrderedSet } from 'mol-data/int';