diff --git a/src/apps/canvas/index.ts b/src/apps/canvas/index.ts
index b02f1c33fb14b4b5942ea0ab9e4fa00a960749c4..aa7a8ff5d933c61659139aa35aaa3c7c3360bfa6 100644
--- a/src/apps/canvas/index.ts
+++ b/src/apps/canvas/index.ts
@@ -23,4 +23,4 @@ const assemblyId = urlQueryParameter('assembly')
 const pdbId = urlQueryParameter('pdb')
 if (pdbId) app.loadPdbIdOrMmcifUrl(pdbId, { assemblyId })
 
-// app.loadCcp4File()
\ No newline at end of file
+app.loadCcp4Url('http://localhost:8091/ngl/data/betaGal.mrc')
\ No newline at end of file
diff --git a/src/apps/canvas/structure-view.ts b/src/apps/canvas/structure-view.ts
index d13cae50b250b4e2fb848589bb89f14efbce3030..4224874470d1465db4880d83f58f226d5ee3fe25 100644
--- a/src/apps/canvas/structure-view.ts
+++ b/src/apps/canvas/structure-view.ts
@@ -70,7 +70,7 @@ export async function StructureView(app: App, viewer: Viewer, models: ReadonlyAr
     const active: { [k: string]: boolean } = {
         cartoon: true,
         point: false,
-        surface: true,
+        surface: false,
         ballAndStick: false,
         carbohydrate: false,
         spacefill: false,
diff --git a/src/apps/canvas/volume-view.ts b/src/apps/canvas/volume-view.ts
index 968fca0727000d3d1245596f9f88dced2d730615..422934fdf4e1db5ee695d3d1524f869027a823cb 100644
--- a/src/apps/canvas/volume-view.ts
+++ b/src/apps/canvas/volume-view.ts
@@ -10,8 +10,8 @@ import { App } from './app';
 import { Progress } from 'mol-task';
 import { VolumeData } from 'mol-model/volume';
 import { VolumeRepresentation } from 'mol-geo/representation/volume';
-import IsosurfaceVisual from 'mol-geo/representation/volume/isosurface';
-import { Vec3 } from 'mol-math/linear-algebra';
+import { IsosurfaceRepresentation } from 'mol-geo/representation/volume/isosurface-mesh';
+import { DirectVolumeRepresentation } from 'mol-geo/representation/volume/direct-volume';
 
 export interface VolumeView {
     readonly app: App
@@ -28,19 +28,19 @@ export interface VolumeView {
     destroy: () => void
 }
 
-interface StructureViewProps {
-    assemblyId?: string
-    symmetryFeatureId?: number
+interface VolumeViewProps {
+
 }
 
-export async function VolumeView(app: App, viewer: Viewer, volume: VolumeData, props: StructureViewProps = {}): Promise<VolumeView> {
+export async function VolumeView(app: App, viewer: Viewer, volume: VolumeData, props: VolumeViewProps = {}): Promise<VolumeView> {
     const active: { [k: string]: boolean } = {
         isosurface: true,
-        volume: false,
+        directVolume: false,
     }
 
     const volumeRepresentations: { [k: string]: VolumeRepresentation<any> } = {
-        isosurface: VolumeRepresentation(IsosurfaceVisual),
+        isosurface: IsosurfaceRepresentation(),
+        directVolume: DirectVolumeRepresentation(),
     }
 
     const updated: BehaviorSubject<null> = new BehaviorSubject<null>(null)
diff --git a/src/apps/structure-info/volume.ts b/src/apps/structure-info/volume.ts
index 2f5e6441f3c4a563089e9ff56df85a0c17fd6f4f..d59034bca4a4fedd5783b5b49405861f16e4996e 100644
--- a/src/apps/structure-info/volume.ts
+++ b/src/apps/structure-info/volume.ts
@@ -13,8 +13,9 @@ import { downloadCif } from './helpers'
 import CIF from 'mol-io/reader/cif'
 import { DensityServer_Data_Database } from 'mol-io/reader/cif/schema/density-server';
 import { Table } from 'mol-data/db';
-import { computeVolumeSurface } from 'mol-geo/representation/volume/isosurface';
+import { createVolumeSurface } from 'mol-geo/representation/volume/isosurface-mesh';
 import { StringBuilder } from 'mol-util';
+import { Task } from 'mol-task';
 
 require('util.promisify').shim();
 const writeFileAsync = util.promisify(fs.writeFile);
@@ -37,7 +38,7 @@ function print(data: Volume) {
 }
 
 async function doMesh(data: Volume, filename: string) {
-    const mesh = await computeVolumeSurface(data.volume, VolumeIsoValue.relative(data.volume.dataStats, 1.5)).run();
+    const mesh = await Task.create('', ctx => createVolumeSurface(ctx, data.volume, VolumeIsoValue.relative(data.volume.dataStats, 1.5))).run();
     console.log({ vc: mesh.vertexCount, tc: mesh.triangleCount });
 
     // Export the mesh in OBJ format.
diff --git a/src/mol-geo/geometry/direct-volume/direct-volume.ts b/src/mol-geo/geometry/direct-volume/direct-volume.ts
index ddf9be4d65b25094f31202e4f4dbfa5a7829f7ed..63cde8d1fb947bc76c6403ba397cb33383ba14d8 100644
--- a/src/mol-geo/geometry/direct-volume/direct-volume.ts
+++ b/src/mol-geo/geometry/direct-volume/direct-volume.ts
@@ -85,6 +85,7 @@ export namespace DirectVolume {
     }
 
     export function updateValues(values: DirectVolumeValues, props: Props) {
+        console.log('DirectVolumeValues', props, values)
         ValueCell.updateIfChanged(values.uIsoValue, props.isoValue)
         ValueCell.updateIfChanged(values.uAlpha, props.alpha)
         ValueCell.updateIfChanged(values.dUseFog, props.useFog)
diff --git a/src/mol-geo/geometry/direct-volume/transfer-function.ts b/src/mol-geo/geometry/direct-volume/transfer-function.ts
index 9c0d1679c9cebe2d9d24074f964b24ba78c4b8c8..f236bc9f66cd9d0b9f9fdea9091d7d28d3d8ee6e 100644
--- a/src/mol-geo/geometry/direct-volume/transfer-function.ts
+++ b/src/mol-geo/geometry/direct-volume/transfer-function.ts
@@ -18,7 +18,7 @@ export function getControlPointsFromString(s: string): ControlPoint[] {
         return { x: parseFloat(ps[0]), alpha: parseFloat(ps[1]) }
     })
 }
-
+// TODO move core function to mol-view/color
 export function createTransferFunctionTexture(controlPoints: ControlPoint[], texture?: ValueCell<TextureImage>): ValueCell<TextureImage> {
     const cp = [
         { x: 0, alpha: 0 },
diff --git a/src/mol-geo/representation/volume/direct-volume.ts b/src/mol-geo/representation/volume/direct-volume.ts
new file mode 100644
index 0000000000000000000000000000000000000000..860a7eaf2517a172a02d87fd70a538c2e328fadc
--- /dev/null
+++ b/src/mol-geo/representation/volume/direct-volume.ts
@@ -0,0 +1,221 @@
+/**
+ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import { VolumeData } from 'mol-model/volume'
+import { RuntimeContext } from 'mol-task'
+import { VolumeVisual, VolumeRepresentation } from '.';
+import { DirectVolumeRenderObject, createDirectVolumeRenderObject } from 'mol-gl/render-object';
+import { PickingId } from '../../geometry/picking';
+import { MarkerAction } from '../../geometry/marker-data';
+import { Loci, EmptyLoci } from 'mol-model/loci';
+import { createRenderableState, updateRenderableState, Geometry } from '../../geometry/geometry';
+import { paramDefaultValues, RangeParam } from 'mol-view/parameter';
+import { ValueCell } from 'mol-util';
+import { DirectVolume } from '../../geometry/direct-volume/direct-volume';
+import { Vec2, Vec3, Tensor } from 'mol-math/linear-algebra';
+import { Box3D } from 'mol-math/geometry';
+import { createImageData } from 'mol-gl/webgl/context';
+import { debugTexture } from 'mol-gl/util';
+
+function getFlattedVolumeLayout(dim: Vec3, maxTextureSize = 4096) {
+    let width = 0
+    let height = dim[1]
+    let rows = 1
+    let columns = dim[0]
+    if (maxTextureSize < dim[0] * dim[2]) {
+        columns =  Math.floor(maxTextureSize / dim[0])
+        rows = Math.ceil(dim[2] / columns)
+        width = columns * dim[0]
+        height *= rows
+    } else {
+        width = dim[0] * dim[2]
+    }
+    width += columns // horizontal padding
+    height += rows // vertical padding
+    return { width, height, columns, rows }
+}
+
+// let foo = 0
+
+function createFlattendVolumeTexture(tensor: Tensor, itemSize = 4) {
+    const { space, data } = tensor
+    const dim = space.dimensions as Vec3
+    const { get } = space
+    const { width, height, columns, rows } = getFlattedVolumeLayout(dim)
+
+    const array = new Uint8Array(width * height * itemSize)
+    const textureImage = { array, width, height }
+
+    const [ xl, yl, zl ] = dim
+    const xlp = xl + 1
+    const ylp = yl + 1
+
+    function setTex(value: number, x: number, y: number, z: number) {
+        const column = Math.floor(((z * xlp) % width) / xlp)
+        const row = Math.floor((z * xlp) / width)
+        const px = column * xlp + x
+        // const py = row * ylp + y
+        const index = itemSize * ((row * ylp * width) + (y * width) + px);
+        array[index] = value * 255;
+        array[index + 1] = value * 255;
+        array[index + 3] = value;
+        // if (foo % 1000 === 0) {
+        //     console.log(value * 255, x, y, z, index, '|', column, row);
+        // }
+        // ++foo;
+    }
+
+    console.log('dim', dim)
+    console.log('layout', { width, height, columns, rows })
+
+    for (let z = 0; z < zl; ++z) {
+        for (let y = 0; y < yl; ++y) {
+            for (let x = 0; x < xl; ++x) {
+                setTex(get(data, x, y, z), x, y, z)
+            }
+        }
+    }
+
+    return textureImage
+}
+
+export function createDirectVolume(ctx: RuntimeContext, volume: VolumeData, directVolume?: DirectVolume) {
+    const gridDimension = volume.data.space.dimensions as Vec3
+    // const textureImage = createTextureImage(1, 4)
+    const textureImage = createFlattendVolumeTexture(volume.data)
+    const transform = VolumeData.getGridToCartesianTransform(volume)
+
+    console.log('textureImage', textureImage)
+    debugTexture(createImageData(textureImage.array, textureImage.width, textureImage.height), 1/3)
+
+    const bbox = Box3D.empty()
+    Box3D.add(bbox, gridDimension)
+    Box3D.transform(bbox, bbox, transform)
+
+    const dim = Vec3.create(gridDimension[0] + 1, gridDimension[1] + 1, gridDimension[2])
+
+    if (directVolume) {
+        ValueCell.update(directVolume.gridDimension, dim)
+        ValueCell.update(directVolume.gridTexture, textureImage)
+        ValueCell.update(directVolume.gridTextureDim, Vec2.set(directVolume.gridTextureDim.ref.value, textureImage.width, textureImage.height))
+        ValueCell.update(directVolume.bboxMin, bbox.min)
+        ValueCell.update(directVolume.bboxMax, bbox.max)
+        ValueCell.update(directVolume.bboxSize, Vec3.sub(directVolume.bboxSize.ref.value, bbox.max, bbox.min))
+        ValueCell.update(directVolume.transform, transform)
+    } else {
+        directVolume = {
+            kind: 'direct-volume' as 'direct-volume',
+            gridDimension: ValueCell.create(dim),
+            gridTexture: ValueCell.create(textureImage),
+            gridTextureDim: ValueCell.create(Vec2.create(textureImage.width, textureImage.height)),
+            bboxMin: ValueCell.create(bbox.min),
+            bboxMax: ValueCell.create(bbox.max),
+            bboxSize: ValueCell.create(Vec3.sub(Vec3.zero(), bbox.max, bbox.min)),
+            transform: ValueCell.create(transform),
+        }
+    }
+
+    console.log('gridDimension', dim)
+    console.log('gridTextureDim', textureImage.width, textureImage.height)
+    console.log('boundingBox', bbox)
+    console.log('transform', transform)
+
+    return directVolume;
+}
+
+export const DirectVolumeParams = {
+    ...Geometry.Params,
+    ...DirectVolume.Params,
+    isoValue: RangeParam('Iso Value', '', 2, -15, 15, 0.01),
+}
+export const DefaultDirectVolumeProps = paramDefaultValues(DirectVolumeParams)
+export type DirectVolumeProps = typeof DefaultDirectVolumeProps
+
+export function DirectVolumeVisual(): VolumeVisual<DirectVolumeProps> {
+    let currentProps = DefaultDirectVolumeProps
+    let renderObject: DirectVolumeRenderObject
+    let currentVolume: VolumeData
+    let directVolume: DirectVolume
+
+    async function create(ctx: RuntimeContext, volume: VolumeData, props: Partial<DirectVolumeProps> = {}) {
+        currentProps = { ...DefaultDirectVolumeProps, ...props }
+
+        directVolume = await createDirectVolume(ctx, volume, directVolume)
+
+        const values = await DirectVolume.createValues(ctx, directVolume, currentProps)
+        const state = createRenderableState(currentProps)
+
+        renderObject = createDirectVolumeRenderObject(values, state)
+    }
+
+    async function update(ctx: RuntimeContext, props: Partial<DirectVolumeProps> = {}) {
+        console.log('props', props)
+        const newProps = { ...currentProps, ...props }
+
+        DirectVolume.updateValues(renderObject.values, newProps)
+        updateRenderableState(renderObject.state, newProps)
+
+        currentProps = newProps
+    }
+
+    return {
+        get renderObject () { return renderObject },
+        async createOrUpdate(ctx: RuntimeContext, props: Partial<DirectVolumeProps> = {}, volume?: VolumeData) {
+            if (!volume && !currentVolume) {
+                throw new Error('missing volume')
+            } else if (volume && (!currentVolume || !renderObject)) {
+                currentVolume = volume
+                await create(ctx, volume, props)
+            } else if (volume && volume !== currentVolume) {
+                currentVolume = volume
+                await create(ctx, volume, props)
+            } else {
+                await update(ctx, props)
+            }
+
+            currentProps = { ...DefaultDirectVolumeProps, ...props }
+        },
+        getLoci(pickingId: PickingId) {
+            // TODO
+            return EmptyLoci
+        },
+        mark(loci: Loci, action: MarkerAction) {
+            // TODO
+            return false
+        },
+        destroy() {
+            // TODO
+        }
+    }
+}
+
+export function DirectVolumeRepresentation(): VolumeRepresentation<DirectVolumeProps> {
+    let currentProps: DirectVolumeProps
+    const volumeRepr = VolumeRepresentation(DirectVolumeVisual)
+    return {
+        label: 'Direct Volume',
+        params: DirectVolumeParams,
+        get renderObjects() {
+            return [ ...volumeRepr.renderObjects ]
+        },
+        get props() {
+            return { ...volumeRepr.props }
+        },
+        createOrUpdate: (props: Partial<DirectVolumeProps> = {}, volume?: VolumeData) => {
+            currentProps = Object.assign({}, DefaultDirectVolumeProps, currentProps, props)
+            return volumeRepr.createOrUpdate(currentProps, volume)
+        },
+        getLoci: (pickingId: PickingId) => {
+            return volumeRepr.getLoci(pickingId)
+        },
+        mark: (loci: Loci, action: MarkerAction) => {
+            return volumeRepr.mark(loci, action)
+        },
+        destroy() {
+            volumeRepr.destroy()
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/mol-geo/representation/volume/index.ts b/src/mol-geo/representation/volume/index.ts
index 18e9722ddb304299c7f833b4863173dc7bcbae83..2bbceed74d941ac56213cf044a60f8129d5e322e 100644
--- a/src/mol-geo/representation/volume/index.ts
+++ b/src/mol-geo/representation/volume/index.ts
@@ -12,7 +12,6 @@ import { Loci, EmptyLoci } from 'mol-model/loci';
 import { MarkerAction } from '../../geometry/marker-data';
 import { Geometry } from '../../geometry/geometry';
 import { paramDefaultValues } from 'mol-view/parameter';
-import { IsosurfaceParams } from './isosurface';
 
 export interface VolumeVisual<P extends RepresentationProps = {}> extends Visual<VolumeData, P> { }
 
@@ -20,7 +19,6 @@ export interface VolumeRepresentation<P extends RepresentationProps = {}> extend
 
 export const VolumeParams = {
     ...Geometry.Params,
-    ...IsosurfaceParams
 }
 export const DefaultVolumeProps = paramDefaultValues(VolumeParams)
 export type VolumeProps = typeof DefaultVolumeProps
@@ -52,7 +50,7 @@ export function VolumeRepresentation<P extends VolumeProps>(visualCtor: (volumeD
     }
 
     return {
-        label: 'Volume mesh',
+        label: 'Volume',
         params: VolumeParams,
         get renderObjects() {
             return visual && visual.renderObject ? [ visual.renderObject ] : []
diff --git a/src/mol-geo/representation/volume/isosurface.ts b/src/mol-geo/representation/volume/isosurface-mesh.ts
similarity index 63%
rename from src/mol-geo/representation/volume/isosurface.ts
rename to src/mol-geo/representation/volume/isosurface-mesh.ts
index 72be9abcf2ec993c42b52be757523a179dbeb20d..982dc3261ef6a86556284cee35a428ec5b7e36b0 100644
--- a/src/mol-geo/representation/volume/isosurface.ts
+++ b/src/mol-geo/representation/volume/isosurface-mesh.ts
@@ -6,10 +6,10 @@
  */
 
 import { VolumeData, VolumeIsoValue } from 'mol-model/volume'
-import { Task, RuntimeContext } from 'mol-task'
+import { RuntimeContext } from 'mol-task'
 import { computeMarchingCubesMesh } from '../../util/marching-cubes/algorithm';
 import { Mesh } from '../../geometry/mesh/mesh';
-import { VolumeVisual } from '.';
+import { VolumeVisual, VolumeRepresentation } from '.';
 import { createMeshRenderObject, MeshRenderObject } from 'mol-gl/render-object';
 import { PickingId } from '../../geometry/picking';
 import { MarkerAction } from '../../geometry/marker-data';
@@ -21,22 +21,20 @@ import { createRenderableState, updateRenderableState } from '../../geometry/geo
 import { paramDefaultValues, NumberParam } from 'mol-view/parameter';
 import { ValueCell } from 'mol-util';
 
-export function computeVolumeSurface(volume: VolumeData, isoValue: VolumeIsoValue, mesh?: Mesh) {
-    return Task.create<Mesh>('Volume Surface', async ctx => {
-        ctx.update({ message: 'Marching cubes...' });
+export async function createVolumeSurface(ctx: RuntimeContext, volume: VolumeData, isoValue: VolumeIsoValue, mesh?: Mesh) {
+    ctx.update({ message: 'Marching cubes...' });
 
-        const surface = await computeMarchingCubesMesh({
-            isoLevel: VolumeIsoValue.toAbsolute(isoValue).absoluteValue,
-            scalarField: volume.data
-        }, mesh).runAsChild(ctx);
+    const surface = await computeMarchingCubesMesh({
+        isoLevel: VolumeIsoValue.toAbsolute(isoValue).absoluteValue,
+        scalarField: volume.data
+    }, mesh).runAsChild(ctx);
 
-        const transform = VolumeData.getGridToCartesianTransform(volume);
-        ctx.update({ message: 'Transforming mesh...' });
-        Mesh.transformImmediate(surface, transform);
-        Mesh.computeNormalsImmediate(surface)
+    const transform = VolumeData.getGridToCartesianTransform(volume);
+    ctx.update({ message: 'Transforming mesh...' });
+    Mesh.transformImmediate(surface, transform);
+    Mesh.computeNormalsImmediate(surface)
 
-        return surface;
-    });
+    return surface;
 }
 
 export const IsosurfaceParams = {
@@ -46,7 +44,7 @@ export const IsosurfaceParams = {
 export const DefaultIsosurfaceProps = paramDefaultValues(IsosurfaceParams)
 export type IsosurfaceProps = typeof DefaultIsosurfaceProps
 
-export default function IsosurfaceVisual(): VolumeVisual<IsosurfaceProps> {
+export function IsosurfaceVisual(): VolumeVisual<IsosurfaceProps> {
     let currentProps = DefaultIsosurfaceProps
     let renderObject: MeshRenderObject
     let currentVolume: VolumeData
@@ -55,7 +53,7 @@ export default function IsosurfaceVisual(): VolumeVisual<IsosurfaceProps> {
     async function create(ctx: RuntimeContext, volume: VolumeData, props: Partial<IsosurfaceProps> = {}) {
         currentProps = { ...DefaultIsosurfaceProps, ...props }
 
-        mesh = await computeVolumeSurface(volume,  VolumeIsoValue.relative(volume.dataStats, currentProps.isoValue)).runAsChild(ctx)
+        mesh = await createVolumeSurface(ctx, volume,  VolumeIsoValue.relative(volume.dataStats, currentProps.isoValue))
 
         const locationIt = LocationIterator(1, 1, () => NullLocation)
         const transform = createIdentityTransform()
@@ -74,7 +72,7 @@ export default function IsosurfaceVisual(): VolumeVisual<IsosurfaceProps> {
         if (newProps.isoValue !== currentProps.isoValue) createMesh = true
 
         if (createMesh) {
-            mesh = await computeVolumeSurface(currentVolume,  VolumeIsoValue.relative(currentVolume.dataStats, currentProps.isoValue), mesh).runAsChild(ctx)
+            mesh = await createVolumeSurface(ctx, currentVolume,  VolumeIsoValue.relative(currentVolume.dataStats, currentProps.isoValue), mesh)
             ValueCell.update(renderObject.values.drawCount, mesh.triangleCount * 3)
         }
 
@@ -82,7 +80,6 @@ export default function IsosurfaceVisual(): VolumeVisual<IsosurfaceProps> {
         updateRenderableState(renderObject.state, newProps)
 
         currentProps = newProps
-        return true
     }
 
     return {
@@ -101,7 +98,6 @@ export default function IsosurfaceVisual(): VolumeVisual<IsosurfaceProps> {
             }
 
             currentProps = { ...DefaultIsosurfaceProps, ...props }
-
         },
         getLoci(pickingId: PickingId) {
             // TODO
@@ -116,3 +112,31 @@ export default function IsosurfaceVisual(): VolumeVisual<IsosurfaceProps> {
         }
     }
 }
+
+export function IsosurfaceRepresentation(): VolumeRepresentation<IsosurfaceProps> {
+    let currentProps: IsosurfaceProps
+    const volumeRepr = VolumeRepresentation(IsosurfaceVisual)
+    return {
+        label: 'Isosurface',
+        params: IsosurfaceParams,
+        get renderObjects() {
+            return [ ...volumeRepr.renderObjects ]
+        },
+        get props() {
+            return { ...volumeRepr.props }
+        },
+        createOrUpdate: (props: Partial<IsosurfaceProps> = {}, volume?: VolumeData) => {
+            currentProps = Object.assign({}, DefaultIsosurfaceProps, currentProps, props)
+            return volumeRepr.createOrUpdate(currentProps, volume)
+        },
+        getLoci: (pickingId: PickingId) => {
+            return volumeRepr.getLoci(pickingId)
+        },
+        mark: (loci: Loci, action: MarkerAction) => {
+            return volumeRepr.mark(loci, action)
+        },
+        destroy() {
+            volumeRepr.destroy()
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/mol-gl/renderable/direct-volume.ts b/src/mol-gl/renderable/direct-volume.ts
index 1827bcc5205b669e40056e20034eda36c2848d2d..a19d26f32a9708c85947543547f367cd8b4eca85 100644
--- a/src/mol-gl/renderable/direct-volume.ts
+++ b/src/mol-gl/renderable/direct-volume.ts
@@ -7,7 +7,7 @@
 import { Renderable, RenderableState, createRenderable } from '../renderable'
 import { Context } from '../webgl/context';
 import { createRenderItem } from '../webgl/render-item';
-import { AttributeSpec, Values, UniformSpec, GlobalUniformSchema, InternalSchema, TextureSpec, ValueSpec, ElementsSpec, DefineSpec } from './schema';
+import { AttributeSpec, Values, UniformSpec, GlobalUniformSchema, InternalSchema, TextureSpec, ValueSpec, ElementsSpec, DefineSpec, InternalValues } from './schema';
 import { DirectVolumeShaderCode } from '../shader-code';
 import { ValueCell } from 'mol-util';
 
@@ -37,7 +37,8 @@ export type DirectVolumeValues = Values<DirectVolumeSchema>
 
 export function DirectVolumeRenderable(ctx: Context, id: number, values: DirectVolumeValues, state: RenderableState): Renderable<DirectVolumeValues> {
     const schema = { ...GlobalUniformSchema, ...InternalSchema, ...DirectVolumeSchema }
-    const internalValues = {
+    const internalValues: InternalValues = {
+        dWebGL2: ValueCell.create(ctx.isWebGL2),
         uObjectId: ValueCell.create(id)
     }
     const shaderCode = DirectVolumeShaderCode
diff --git a/src/mol-gl/renderable/gaussian-density.ts b/src/mol-gl/renderable/gaussian-density.ts
index dd1c88e88a8decd33a176f11b38c024671c74ad1..2b4b51e696c1a8c6441f8a05835206aa05ef6354 100644
--- a/src/mol-gl/renderable/gaussian-density.ts
+++ b/src/mol-gl/renderable/gaussian-density.ts
@@ -7,10 +7,12 @@
 import { Renderable, RenderableState, createRenderable } from '../renderable'
 import { Context } from '../webgl/context';
 import { createRenderItem } from '../webgl/render-item';
-import { AttributeSpec, Values, UniformSpec, ValueSpec } from './schema';
+import { AttributeSpec, Values, UniformSpec, ValueSpec, DefineSpec } from './schema';
 import { GaussianDensityShaderCode } from '../shader-code';
 
 export const GaussianDensitySchema = {
+    dWebGL2: DefineSpec('boolean'),
+
     drawCount: ValueSpec('number'),
     instanceCount: ValueSpec('number'),
 
@@ -32,7 +34,7 @@ export type GaussianDensityValues = Values<GaussianDensitySchema>
 export function GaussianDensityRenderable(ctx: Context, id: number, values: GaussianDensityValues, state: RenderableState): Renderable<GaussianDensityValues> {
     const schema = { ...GaussianDensitySchema }
     const shaderCode = GaussianDensityShaderCode
-    const renderItem = createRenderItem(ctx, 'points', shaderCode, schema, { ...values })
+    const renderItem = createRenderItem(ctx, 'points', shaderCode, schema, values)
 
     return createRenderable(renderItem, values, state);
 }
\ No newline at end of file
diff --git a/src/mol-gl/renderable/lines.ts b/src/mol-gl/renderable/lines.ts
index 854eb0f4a80622c54b6e3c85cc056814f2dda260..9f35feb42f45ae049f724971dc2a63b6981ec77b 100644
--- a/src/mol-gl/renderable/lines.ts
+++ b/src/mol-gl/renderable/lines.ts
@@ -7,7 +7,7 @@
 import { Renderable, RenderableState, createRenderable } from '../renderable'
 import { Context } from '../webgl/context';
 import { createRenderItem } from '../webgl/render-item';
-import { GlobalUniformSchema, BaseSchema, AttributeSpec, DefineSpec, Values, InternalSchema, SizeSchema, ElementsSpec } from './schema';
+import { GlobalUniformSchema, BaseSchema, AttributeSpec, DefineSpec, Values, InternalSchema, SizeSchema, ElementsSpec, InternalValues } from './schema';
 import { ValueCell } from 'mol-util';
 import { LinesShaderCode } from '../shader-code';
 
@@ -27,7 +27,8 @@ export type LinesValues = Values<LinesSchema>
 
 export function LinesRenderable(ctx: Context, id: number, values: LinesValues, state: RenderableState): Renderable<LinesValues> {
     const schema = { ...GlobalUniformSchema, ...InternalSchema, ...LinesSchema }
-    const internalValues = {
+    const internalValues: InternalValues = {
+        dWebGL2: ValueCell.create(ctx.isWebGL2),
         uObjectId: ValueCell.create(id)
     }
     const shaderCode = LinesShaderCode
diff --git a/src/mol-gl/renderable/mesh.ts b/src/mol-gl/renderable/mesh.ts
index 5a4d243949ee644f5cd74614ef6dcc84fcab9ed2..b40670a74d9987017bba3453034d846e0d0ab6b6 100644
--- a/src/mol-gl/renderable/mesh.ts
+++ b/src/mol-gl/renderable/mesh.ts
@@ -7,7 +7,7 @@
 import { Renderable, RenderableState, createRenderable } from '../renderable'
 import { Context } from '../webgl/context';
 import { createRenderItem } from '../webgl/render-item';
-import { GlobalUniformSchema, BaseSchema, AttributeSpec, ElementsSpec, DefineSpec, Values, InternalSchema } from './schema';
+import { GlobalUniformSchema, BaseSchema, AttributeSpec, ElementsSpec, DefineSpec, Values, InternalSchema, InternalValues } from './schema';
 import { MeshShaderCode } from '../shader-code';
 import { ValueCell } from 'mol-util';
 
@@ -25,7 +25,8 @@ export type MeshValues = Values<MeshSchema>
 
 export function MeshRenderable(ctx: Context, id: number, values: MeshValues, state: RenderableState): Renderable<MeshValues> {
     const schema = { ...GlobalUniformSchema, ...InternalSchema, ...MeshSchema }
-    const internalValues = {
+    const internalValues: InternalValues = {
+        dWebGL2: ValueCell.create(ctx.isWebGL2),
         uObjectId: ValueCell.create(id)
     }
     const shaderCode = MeshShaderCode
diff --git a/src/mol-gl/renderable/points.ts b/src/mol-gl/renderable/points.ts
index de003f0dda63986c956ec49022e3a52809eafd46..6270b9a58cde33b0c048c7f71499f4a18fc10995 100644
--- a/src/mol-gl/renderable/points.ts
+++ b/src/mol-gl/renderable/points.ts
@@ -7,7 +7,7 @@
 import { Renderable, RenderableState, createRenderable } from '../renderable'
 import { Context } from '../webgl/context';
 import { createRenderItem } from '../webgl/render-item';
-import { GlobalUniformSchema, BaseSchema, AttributeSpec, UniformSpec, DefineSpec, Values, InternalSchema, SizeSchema } from './schema';
+import { GlobalUniformSchema, BaseSchema, AttributeSpec, UniformSpec, DefineSpec, Values, InternalSchema, SizeSchema, InternalValues } from './schema';
 import { PointsShaderCode } from '../shader-code';
 import { ValueCell } from 'mol-util';
 
@@ -24,7 +24,8 @@ export type PointsValues = Values<PointsSchema>
 
 export function PointsRenderable(ctx: Context, id: number, values: PointsValues, state: RenderableState): Renderable<PointsValues> {
     const schema = { ...GlobalUniformSchema, ...InternalSchema, ...PointsSchema }
-    const internalValues = {
+    const internalValues: InternalValues = {
+        dWebGL2: ValueCell.create(ctx.isWebGL2),
         uObjectId: ValueCell.create(id)
     }
     const shaderCode = PointsShaderCode
diff --git a/src/mol-gl/renderable/schema.ts b/src/mol-gl/renderable/schema.ts
index ed784e524c2185d78723f9fa8a07e7e0b65624a7..c29f51f09bb50ba29c442015321e9f3700fc8e36 100644
--- a/src/mol-gl/renderable/schema.ts
+++ b/src/mol-gl/renderable/schema.ts
@@ -141,8 +141,11 @@ export type GlobalUniformSchema = typeof GlobalUniformSchema
 export type GlobalUniformValues = { [k in keyof GlobalUniformSchema]: ValueCell<any> }
 
 export const InternalSchema = {
+    dWebGL2: DefineSpec('boolean'),
     uObjectId: UniformSpec('i'),
 }
+export type InternalSchema = typeof InternalSchema
+export type InternalValues = { [k in keyof InternalSchema]: ValueCell<any> }
 
 export const ColorSchema = {
     aColor: AttributeSpec('float32', 3, 0),
diff --git a/src/mol-gl/shader/direct-volume.frag b/src/mol-gl/shader/direct-volume.frag
index 379a27ee369822adeb0bbe9f61d2f5000e894340..adf44b4208524b08d4ab33df324eda8a0cd7e2d6 100644
--- a/src/mol-gl/shader/direct-volume.frag
+++ b/src/mol-gl/shader/direct-volume.frag
@@ -84,13 +84,13 @@ vec3 palette1(in float t) {
 vec4 textureVal(vec3 pos) {
     float zSlice0 = floor(pos.z * uGridDim.z);
     float column0 = myMod(zSlice0 * uGridDim.x, uGridTexDim.x) / uGridDim.x;
-    float row0 = floor(myDiv((zSlice0 * uGridDim.x), uGridTexDim.x));
+    float row0 = floor(myDiv(zSlice0 * uGridDim.x, uGridTexDim.x));
     vec2 coord0 = (vec2(column0 * uGridDim.x, row0 * uGridDim.y) + (pos.xy * uGridDim.xy)) / uGridTexDim;
     vec4 color0 = texture2D(tGridTex, coord0);
 
     float zSlice1 = zSlice0 + 1.0;
     float column1 = myMod(zSlice1 * uGridDim.x, uGridTexDim.x) / uGridDim.x;
-    float row1 = floor(myDiv((zSlice1 * uGridDim.x), uGridTexDim.x));
+    float row1 = floor(myDiv(zSlice1 * uGridDim.x, uGridTexDim.x));
     vec2 coord1 = (vec2(column1 * uGridDim.x, row1 * uGridDim.y) + (pos.xy * uGridDim.xy)) / uGridTexDim;
     vec4 color1 = texture2D(tGridTex, coord1);
 
diff --git a/src/mol-gl/util.ts b/src/mol-gl/util.ts
index 49dd6101573bc181801a9e8962bcea93a89ce64a..b05766f4b89376a434123f9db72fdf6213f8765d 100644
--- a/src/mol-gl/util.ts
+++ b/src/mol-gl/util.ts
@@ -4,7 +4,7 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-function debugTexture(imageData: ImageData, scale = 1) {
+export function debugTexture(imageData: ImageData, scale = 1) {
     const canvas = document.createElement('canvas')
     canvas.width = imageData.width
     canvas.height = imageData.height
@@ -20,6 +20,7 @@ function debugTexture(imageData: ImageData, scale = 1) {
         img.style.position = 'absolute'
         img.style.top = '0px'
         img.style.left = '0px'
+        img.style.border = 'solid grey'
         document.body.appendChild(img)
     }, 'image/png')
 }
\ No newline at end of file
diff --git a/src/mol-math/geometry/gaussian-density/gpu.ts b/src/mol-math/geometry/gaussian-density/gpu.ts
index a0602983c597aa79be38d628b8b0965a221b7a11..cb779e1ebc04d31bc40eedd421ec77aedee79ca9 100644
--- a/src/mol-math/geometry/gaussian-density/gpu.ts
+++ b/src/mol-math/geometry/gaussian-density/gpu.ts
@@ -60,7 +60,12 @@ export async function GaussianDensityGPU(ctx: RuntimeContext, position: Position
 
     //
 
+    // TODO do in OffscreenCanvas (https://www.chromestatus.com/feature/5681560598609920)?
+    const webgl = getWebGLContext()
+
     const values: GaussianDensityValues = {
+        dWebGL2: ValueCell.create(webgl.isWebGL2),
+
         drawCount: ValueCell.create(n),
         instanceCount: ValueCell.create(1),
 
@@ -81,16 +86,13 @@ export async function GaussianDensityGPU(ctx: RuntimeContext, position: Position
         depthMask: false
     }
 
-    // TODO do in OffscreenCanvas (https://www.chromestatus.com/feature/5681560598609920)
-    const webgl = getWebGLContext()
-
     const renderObject = createGaussianDensityRenderObject(values, state)
     const renderable = createRenderable(webgl, renderObject)
 
     //
 
     // TODO fallback to lower resolution when texture size is not large enough
-    const maxTexSize = 1024 // webgl.maxTextureSize
+    const maxTexSize = webgl.maxTextureSize
     let fboTexDimX = 0
     let fboTexDimY = dim[1]
     let fboTexRows = 1